diff options
Diffstat (limited to 'src/emu')
259 files changed, 45777 insertions, 32028 deletions
diff --git a/src/emu/addrmap.cpp b/src/emu/addrmap.cpp index e1398013670..81e91457e71 100644 --- a/src/emu/addrmap.cpp +++ b/src/emu/addrmap.cpp @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles /*************************************************************************** - addrmap.c + addrmap.cpp Macros and helper functions for handling address map definitions. @@ -19,39 +19,6 @@ #define DETECT_OVERLAPPING_MEMORY (0) -/*------------------------------------------------- - core_i64_hex_format - i64 format printf helper - why isn't fatalerror going through the same - channels as logerror exactly? --------------------------------------------------*/ - -static char *core_i64_hex_format(u64 value, u8 mindigits) -{ - static char buffer[16][64]; - // TODO: this can overflow - e.g. when a lot of unmapped writes are logged - static int index; - char *bufbase = &buffer[index++ % 16][0]; - char *bufptr = bufbase; - s8 curdigit; - - for (curdigit = 15; curdigit >= 0; curdigit--) - { - int nibble = (value >> (curdigit * 4)) & 0xf; - if (nibble != 0 || curdigit < mindigits) - { - mindigits = curdigit; - *bufptr++ = "0123456789ABCDEF"[nibble]; - } - } - if (bufptr == bufbase) - *bufptr++ = '0'; - *bufptr = 0; - - return bufbase; -} - - - //************************************************************************** // ADDRESS MAP ENTRY //************************************************************************** @@ -61,21 +28,73 @@ static char *core_i64_hex_format(u64 value, u8 mindigits) //------------------------------------------------- address_map_entry::address_map_entry(device_t &device, address_map &map, offs_t start, offs_t end) - : m_next(nullptr), - m_map(map), - m_devbase(device), - m_addrstart(start), - m_addrend(end), - m_addrmirror(0), - m_addrmask(0), - m_addrselect(0), - m_mask(0), - m_cswidth(0), - m_share(nullptr), - m_region(nullptr), - m_rgnoffs(0), - m_submap_device(nullptr), - m_memory(nullptr) + : m_next(nullptr) + , m_map(map) + , m_devbase(device) + , m_addrstart(start) + , m_addrend(end) + , m_addrmirror(0) + , m_addrmask(0) + , m_addrselect(0) + , m_mask(0) + , m_cswidth(0) + , m_flags(0) + , m_share(nullptr) + , m_region(nullptr) + , m_rgnoffs(0) + , m_before_time(device) + , m_before_delay(device) + , m_after_delay(device) + , m_rproto8(device) + , m_rproto16(device) + , m_rproto32(device) + , m_rproto64(device) + , m_wproto8(device) + , m_wproto16(device) + , m_wproto32(device) + , m_wproto64(device) + , m_rproto8m(device) + , m_rproto16m(device) + , m_rproto32m(device) + , m_rproto64m(device) + , m_wproto8m(device) + , m_wproto16m(device) + , m_wproto32m(device) + , m_wproto64m(device) + , m_rproto8s(device) + , m_rproto16s(device) + , m_rproto32s(device) + , m_rproto64s(device) + , m_wproto8s(device) + , m_wproto16s(device) + , m_wproto32s(device) + , m_wproto64s(device) + , m_rproto8sm(device) + , m_rproto16sm(device) + , m_rproto32sm(device) + , m_rproto64sm(device) + , m_wproto8sm(device) + , m_wproto16sm(device) + , m_wproto32sm(device) + , m_wproto64sm(device) + , m_rproto8mo(device) + , m_rproto16mo(device) + , m_rproto32mo(device) + , m_rproto64mo(device) + , m_wproto8mo(device) + , m_wproto16mo(device) + , m_wproto32mo(device) + , m_wproto64mo(device) + , m_rproto8smo(device) + , m_rproto16smo(device) + , m_rproto32smo(device) + , m_rproto64smo(device) + , m_wproto8smo(device) + , m_wproto16smo(device) + , m_wproto32smo(device) + , m_wproto64smo(device) + , m_submap_device(nullptr) + , m_memory(nullptr) { } @@ -153,6 +172,15 @@ address_map_entry &address_map_entry::m(device_t *device, address_map_constructo return *this; } +void address_map_entry::view(memory_view &mv) +{ + m_read.m_type = AMH_VIEW; + m_read.m_tag = nullptr; + m_write.m_type = AMH_VIEW; + m_write.m_tag = nullptr; + m_view = &mv; + mv.initialize_from_address_map(m_addrstart, m_addrend, m_map.get_config()); +} //------------------------------------------------- // r/w/rw - handler setters for @@ -686,7 +714,7 @@ bool address_map_entry::unitmask_is_appropriate(u8 width, u64 unitmask, const ch // if map is narrower than 64 bits, check the mask width as well if (m_map.m_databits < 64 && (unitmask >> m_map.m_databits) != 0) { - osd_printf_error("Handler %s specified a mask of %08X%08X, too wide to be used in a %d-bit address map\n", string, (u32)(unitmask >> 32), (u32)unitmask, m_map.m_databits); + osd_printf_error("Handler %s specified a mask of %016X, too wide to be used in a %d-bit address map\n", string, unitmask, m_map.m_databits); return false; } @@ -700,7 +728,7 @@ bool address_map_entry::unitmask_is_appropriate(u8 width, u64 unitmask, const ch count++; else if ((unitmask & singlemask) != 0) { - osd_printf_error("Handler %s specified a mask of %08X%08X; needs to be in even chunks of %X\n", string, (u32)(unitmask >> 32), (u32)unitmask, basemask); + osd_printf_error("Handler %s specified a mask of %016X; needs to be in even chunks of %X\n", string, unitmask, basemask); return false; } singlemask <<= width; @@ -718,7 +746,7 @@ bool address_map_entry::unitmask_is_appropriate(u8 width, u64 unitmask, const ch || (unitmask_wh != 0 && unitmask_wl != 0 && unitmask_wh != unitmask_wl) || (unitmask_dh != 0 && unitmask_dl != 0 && unitmask_dh != unitmask_dl)) { - osd_printf_error("Handler %s specified an asymmetrical mask of %08X%08X\n", string, (u32)(unitmask >> 32), (u32)unitmask); + osd_printf_error("Handler %s specified an asymmetrical mask of %016X\n", string, unitmask); return false; } #endif @@ -739,6 +767,7 @@ bool address_map_entry::unitmask_is_appropriate(u8 width, u64 unitmask, const ch address_map::address_map(device_t &device, int spacenum) : m_spacenum(spacenum), m_device(&device), + m_view(nullptr), m_unmapval(0), m_globalmask(0) { @@ -748,8 +777,8 @@ address_map::address_map(device_t &device, int spacenum) throw emu_fatalerror("No memory interface defined for device '%s'\n", m_device->tag()); // and then the configuration for the current address space - const address_space_config *spaceconfig = memintf->space_config(spacenum); - if (spaceconfig == nullptr) + m_config = memintf->space_config(spacenum); + if (m_config == nullptr) throw emu_fatalerror("No memory address space configuration found for device '%s', space %d\n", m_device->tag(), spacenum); // append the map provided by the owner @@ -761,8 +790,8 @@ address_map::address_map(device_t &device, int spacenum) } // construct the internal device map (last so it takes priority) - if (!spaceconfig->m_internal_map.isnull()) - spaceconfig->m_internal_map(*this); + if (!m_config->m_internal_map.isnull()) + m_config->m_internal_map(*this); } @@ -774,6 +803,8 @@ address_map::address_map(device_t &device, int spacenum) address_map::address_map(device_t &device, address_map_entry *entry) : m_spacenum(AS_PROGRAM), m_device(&device), + m_view(nullptr), + m_config(entry->m_map.m_config), m_unmapval(0), m_globalmask(0) { @@ -788,13 +819,30 @@ address_map::address_map(device_t &device, address_map_entry *entry) // address_map - constructor dynamic device mapping case //---------------------------------------------------------- -address_map::address_map(const address_space &space, offs_t start, offs_t end, u64 unitmask, int cswidth, device_t &device, address_map_constructor submap_delegate) +address_map::address_map(const address_space &space, offs_t start, offs_t end, u64 unitmask, int cswidth, u16 flags, device_t &device, address_map_constructor submap_delegate) : m_spacenum(space.spacenum()), m_device(&device), + m_view(nullptr), + m_config(&space.space_config()), m_unmapval(space.unmap()), m_globalmask(space.addrmask()) { - (*this)(start, end).m(DEVICE_SELF, submap_delegate).umask64(unitmask).cswidth(cswidth); + (*this)(start, end).m(DEVICE_SELF, submap_delegate).umask64(unitmask).cswidth(cswidth).flags(flags); +} + + +//---------------------------------------------------------- +// address_map - constructor memory view mapping case +//---------------------------------------------------------- + +address_map::address_map(memory_view &view) + : m_spacenum(-1), + m_device(&view.m_device), + m_view(&view), + m_config(view.m_config), + m_unmapval(0), + m_globalmask(0) +{ } @@ -825,7 +873,7 @@ void address_map::global_mask(offs_t mask) address_map_entry &address_map::operator()(offs_t start, offs_t end) { - address_map_entry *ptr = global_alloc(address_map_entry(*m_device, *this, start, end)); + address_map_entry *ptr = new address_map_entry(*m_device, *this, start, end); m_entrylist.append(*ptr); return *ptr; } @@ -850,7 +898,7 @@ void address_map::import_submaps(running_machine &machine, device_t &owner, int { mapdevice = owner.subdevice(entry->m_read.m_tag); if (mapdevice == nullptr) - throw emu_fatalerror("Attempted to submap a non-existent device '%s' in space %d of device '%s'\n", owner.subtag(entry->m_read.m_tag).c_str(), m_spacenum, m_device->basetag()); + throw emu_fatalerror("Attempted to submap a non-existent device '%s' in space %d of device '%s'\n", owner.subtag(entry->m_read.m_tag), m_spacenum, m_device->basetag()); } // Grab the submap @@ -933,7 +981,7 @@ void address_map::import_submaps(running_machine &machine, device_t &owner, int subentry_ratio ++; subentry_ratio = data_width / subentry_ratio; if (ratio * subentry_ratio > data_width / 8) - fatalerror("import_submap: In range %x-%x mask %x mirror %x select %x of device %s, the import unitmask of %s combined with an entry unitmask of %s does not fit in %d bits.\n", subentry->m_addrstart, subentry->m_addrend, subentry->m_addrmask, subentry->m_addrmirror, subentry->m_addrselect, entry->m_read.m_tag, core_i64_hex_format(entry->m_mask, data_width/4), core_i64_hex_format(subentry->m_mask, data_width/4), data_width); + fatalerror("import_submap: In range %x-%x mask %x mirror %x select %x of device %s, the import unitmask of %0*x combined with an entry unitmask of %0*x does not fit in %d bits.\n", subentry->m_addrstart, subentry->m_addrend, subentry->m_addrmask, subentry->m_addrmirror, subentry->m_addrselect, entry->m_read.m_tag, data_width/4, entry->m_mask, data_width/4, subentry->m_mask, data_width); // Regenerate the unitmask u64 newmask = 0; @@ -951,6 +999,7 @@ void address_map::import_submaps(running_machine &machine, device_t &owner, int subentry->m_mask = entry->m_mask; subentry->m_cswidth = std::max(subentry->m_cswidth, entry->m_cswidth); + subentry->m_flags = subentry->m_flags | entry->m_flags; if (subentry->m_addrend > max_end) subentry->m_addrend = max_end; @@ -1100,25 +1149,40 @@ void address_map::map_validity_check(validity_checker &valid, int spacenum) cons osd_printf_error("In %s memory range %x-%x, mirror %x touches a select bit (%x)\n", spaceconfig.m_name, entry.m_addrstart, entry.m_addrend, entry.m_addrmirror, entry.m_addrmirror & entry.m_addrselect); // if this is a program space, auto-assign implicit ROM entries - if (entry.m_read.m_type == AMH_ROM && entry.m_region == nullptr) + if (entry.m_read.m_type == AMH_ROM && entry.m_region == nullptr && spacenum == AS_PROGRAM && entry.m_share == nullptr) { entry.m_region = m_device->tag(); entry.m_rgnoffs = entry.m_addrstart; } + // detect conflicts between region and share + if (entry.m_region && entry.m_share) + osd_printf_error("%s space memory map entry %X-%X has both .region() and .share()\n", spaceconfig.m_name, entry.m_addrstart, entry.m_addrend); + + + // Detect inaccesible memory. Region automapping is handled before + if (entry.m_read.m_type == AMH_ROM && entry.m_write.m_type != AMH_RAM && + !entry.m_share && !entry.m_region) + osd_printf_error("%s space memory map entry %X-%X has .rom() but no .region() or .share()\n", spaceconfig.m_name, entry.m_addrstart, entry.m_addrend); + if (entry.m_read.m_type != AMH_RAM && entry.m_write.m_type == AMH_RAM && + !entry.m_share) + osd_printf_error("%s space memory map entry %X-%X has .writeonly() but no .share()\n", spaceconfig.m_name, entry.m_addrstart, entry.m_addrend); + // if this entry references a memory region, validate it - if (entry.m_region != nullptr && entry.m_share == nullptr) + if (entry.m_region != nullptr) { // address map entries that reference regions but are NOPs are pointless - if (entry.m_read.m_type == AMH_NONE && entry.m_write.m_type == AMH_NONE) - osd_printf_error("%s space references memory region %s, but is AM_NOP\n", spaceconfig.m_name, entry.m_region); + if (entry.m_read.m_type != AMH_ROM && entry.m_read.m_type != AMH_RAM) + osd_printf_error("%s space memory map entry %X-%X references memory region %s, but can't read it\n", spaceconfig.m_name, entry.m_addrstart, entry.m_addrend, entry.m_region); + if (entry.m_write.m_type == AMH_RAM) + osd_printf_error("%s space memory map entry %X-%X references memory region %s, but wants to write to it\n", spaceconfig.m_name, entry.m_addrstart, entry.m_addrend, entry.m_region); // make sure we can resolve the full path to the region bool found = false; std::string entry_region = entry.m_devbase.subtag(entry.m_region); // look for the region - for (device_t &dev : device_iterator(m_device->mconfig().root_device())) + for (device_t &dev : device_enumerator(m_device->mconfig().root_device())) { for (romload::region const ®ion : romload::entries(dev.rom_region()).get_regions()) { @@ -1128,6 +1192,39 @@ void address_map::map_validity_check(validity_checker &valid, int spacenum) cons offs_t const length = region.get_length(); if (entry.m_rgnoffs + spaceconfig.addr2byte(entry.m_addrend - entry.m_addrstart + 1) > length) osd_printf_error("%s space memory map entry %X-%X extends beyond region '%s' size (%X)\n", spaceconfig.m_name, entry.m_addrstart, entry.m_addrend, entry.m_region, length); + + device_t *rgndev = dev.subdevice(region.get_tag()); + if (rgndev != m_device) + { + int rgnwidth = region.get_width(); + bool rgnisbe = region.is_bigendian(); + + // memory interface sets region width when tags are the same + device_memory_interface *memintf; + if (rgndev != nullptr && rgndev->interface(memintf)) + { + const address_space_config *rgncfg = memintf->space_config(0); + if (rgncfg != nullptr) + { + rgnwidth = rgncfg->data_width(); + rgnisbe = rgncfg->endianness() == ENDIANNESS_BIG; + } + } + + // verify data width + if (rgnwidth != spaceconfig.data_width()) + osd_printf_error("%s space is %d-bit but references %d-bit region '%s'\n", spaceconfig.m_name, spaceconfig.data_width(), rgnwidth, dev.subtag(region.get_tag())); + + // verify endianness + if (rgnwidth != 8) + { + if (!rgnisbe && spaceconfig.endianness() == ENDIANNESS_BIG) + osd_printf_error("%s space is big-endian but references little-endian region '%s'\n", spaceconfig.m_name, dev.subtag(region.get_tag())); + else if (rgnisbe && spaceconfig.endianness() == ENDIANNESS_LITTLE) + osd_printf_error("%s space is little-endian but references big-endian region '%s'\n", spaceconfig.m_name, dev.subtag(region.get_tag())); + } + } + found = true; } } @@ -1142,71 +1239,71 @@ void address_map::map_validity_check(validity_checker &valid, int spacenum) cons if (entry.m_read.m_type == AMH_DEVICE_DELEGATE || entry.m_read.m_type == AMH_DEVICE_DELEGATE_M || entry.m_read.m_type == AMH_DEVICE_DELEGATE_S || entry.m_read.m_type == AMH_DEVICE_DELEGATE_SM || entry.m_read.m_type == AMH_DEVICE_DELEGATE_MO || entry.m_read.m_type == AMH_DEVICE_DELEGATE_SMO) { // extract the device tag from the proto-delegate - const char *devtag = nullptr; + std::pair<std::reference_wrapper<device_t>, const char *> devtag(entry.m_devbase, nullptr); switch (entry.m_read.m_bits) { case 8: if (entry.m_read.m_type == AMH_DEVICE_DELEGATE) - devtag = entry.m_rproto8.device_name(); + devtag = entry.m_rproto8.finder_target(); else if (entry.m_read.m_type == AMH_DEVICE_DELEGATE_M) - devtag = entry.m_rproto8m.device_name(); + devtag = entry.m_rproto8m.finder_target(); else if (entry.m_read.m_type == AMH_DEVICE_DELEGATE_S) - devtag = entry.m_rproto8s.device_name(); + devtag = entry.m_rproto8s.finder_target(); else if (entry.m_read.m_type == AMH_DEVICE_DELEGATE_SM) - devtag = entry.m_rproto8sm.device_name(); + devtag = entry.m_rproto8sm.finder_target(); else if (entry.m_read.m_type == AMH_DEVICE_DELEGATE_MO) - devtag = entry.m_rproto8mo.device_name(); + devtag = entry.m_rproto8mo.finder_target(); else - devtag = entry.m_rproto8smo.device_name(); + devtag = entry.m_rproto8smo.finder_target(); break; case 16: if (entry.m_read.m_type == AMH_DEVICE_DELEGATE) - devtag = entry.m_rproto16.device_name(); + devtag = entry.m_rproto16.finder_target(); else if (entry.m_read.m_type == AMH_DEVICE_DELEGATE_M) - devtag = entry.m_rproto16m.device_name(); + devtag = entry.m_rproto16m.finder_target(); else if (entry.m_read.m_type == AMH_DEVICE_DELEGATE_S) - devtag = entry.m_rproto16s.device_name(); + devtag = entry.m_rproto16s.finder_target(); else if (entry.m_read.m_type == AMH_DEVICE_DELEGATE_SM) - devtag = entry.m_rproto16sm.device_name(); + devtag = entry.m_rproto16sm.finder_target(); else if (entry.m_read.m_type == AMH_DEVICE_DELEGATE_MO) - devtag = entry.m_rproto16mo.device_name(); + devtag = entry.m_rproto16mo.finder_target(); else - devtag = entry.m_rproto16smo.device_name(); + devtag = entry.m_rproto16smo.finder_target(); break; case 32: if (entry.m_read.m_type == AMH_DEVICE_DELEGATE) - devtag = entry.m_rproto32.device_name(); + devtag = entry.m_rproto32.finder_target(); else if (entry.m_read.m_type == AMH_DEVICE_DELEGATE_M) - devtag = entry.m_rproto32m.device_name(); + devtag = entry.m_rproto32m.finder_target(); else if (entry.m_read.m_type == AMH_DEVICE_DELEGATE_S) - devtag = entry.m_rproto32s.device_name(); + devtag = entry.m_rproto32s.finder_target(); else if (entry.m_read.m_type == AMH_DEVICE_DELEGATE_SM) - devtag = entry.m_rproto32sm.device_name(); + devtag = entry.m_rproto32sm.finder_target(); else if (entry.m_read.m_type == AMH_DEVICE_DELEGATE_MO) - devtag = entry.m_rproto32mo.device_name(); + devtag = entry.m_rproto32mo.finder_target(); else - devtag = entry.m_rproto32smo.device_name(); + devtag = entry.m_rproto32smo.finder_target(); break; case 64: if (entry.m_read.m_type == AMH_DEVICE_DELEGATE) - devtag = entry.m_rproto64.device_name(); + devtag = entry.m_rproto64.finder_target(); else if (entry.m_read.m_type == AMH_DEVICE_DELEGATE_M) - devtag = entry.m_rproto64m.device_name(); + devtag = entry.m_rproto64m.finder_target(); else if (entry.m_read.m_type == AMH_DEVICE_DELEGATE_S) - devtag = entry.m_rproto64s.device_name(); + devtag = entry.m_rproto64s.finder_target(); else if (entry.m_read.m_type == AMH_DEVICE_DELEGATE_SM) - devtag = entry.m_rproto64sm.device_name(); + devtag = entry.m_rproto64sm.finder_target(); else if (entry.m_read.m_type == AMH_DEVICE_DELEGATE_MO) - devtag = entry.m_rproto64mo.device_name(); + devtag = entry.m_rproto64mo.finder_target(); else - devtag = entry.m_rproto64smo.device_name(); + devtag = entry.m_rproto64smo.finder_target(); break; } - if (entry.m_devbase.subdevice(devtag) == nullptr) - osd_printf_error("%s space memory map entry reads from nonexistent device '%s'\n", spaceconfig.m_name, devtag ? devtag : "<unspecified>"); + if (devtag.second && !devtag.first.get().subdevice(devtag.second)) + osd_printf_error("%s space memory map entry reads from nonexistent device '%s'\n", spaceconfig.m_name, devtag.first.get().subtag(devtag.second)); #ifndef MAME_DEBUG // assert will catch this earlier (void)entry.unitmask_is_appropriate(entry.m_read.m_bits, entry.m_mask, entry.m_read.m_name); #endif @@ -1214,71 +1311,71 @@ void address_map::map_validity_check(validity_checker &valid, int spacenum) cons if (entry.m_write.m_type == AMH_DEVICE_DELEGATE || entry.m_read.m_type == AMH_DEVICE_DELEGATE_M || entry.m_write.m_type == AMH_DEVICE_DELEGATE_S || entry.m_write.m_type == AMH_DEVICE_DELEGATE_SM || entry.m_read.m_type == AMH_DEVICE_DELEGATE_MO || entry.m_write.m_type == AMH_DEVICE_DELEGATE_SMO) { // extract the device tag from the proto-delegate - const char *devtag = nullptr; + std::pair<std::reference_wrapper<device_t>, const char *> devtag(entry.m_devbase, nullptr); switch (entry.m_write.m_bits) { case 8: if (entry.m_write.m_type == AMH_DEVICE_DELEGATE) - devtag = entry.m_wproto8.device_name(); + devtag = entry.m_wproto8.finder_target(); else if (entry.m_write.m_type == AMH_DEVICE_DELEGATE_M) - devtag = entry.m_wproto8m.device_name(); + devtag = entry.m_wproto8m.finder_target(); else if (entry.m_write.m_type == AMH_DEVICE_DELEGATE_S) - devtag = entry.m_wproto8s.device_name(); + devtag = entry.m_wproto8s.finder_target(); else if (entry.m_write.m_type == AMH_DEVICE_DELEGATE_SM) - devtag = entry.m_wproto8sm.device_name(); + devtag = entry.m_wproto8sm.finder_target(); else if (entry.m_write.m_type == AMH_DEVICE_DELEGATE_MO) - devtag = entry.m_wproto8mo.device_name(); + devtag = entry.m_wproto8mo.finder_target(); else - devtag = entry.m_wproto8smo.device_name(); + devtag = entry.m_wproto8smo.finder_target(); break; case 16: if (entry.m_write.m_type == AMH_DEVICE_DELEGATE) - devtag = entry.m_wproto16.device_name(); + devtag = entry.m_wproto16.finder_target(); else if (entry.m_write.m_type == AMH_DEVICE_DELEGATE_M) - devtag = entry.m_wproto16m.device_name(); + devtag = entry.m_wproto16m.finder_target(); else if (entry.m_write.m_type == AMH_DEVICE_DELEGATE_S) - devtag = entry.m_wproto16s.device_name(); + devtag = entry.m_wproto16s.finder_target(); else if (entry.m_write.m_type == AMH_DEVICE_DELEGATE_SM) - devtag = entry.m_wproto16sm.device_name(); + devtag = entry.m_wproto16sm.finder_target(); else if (entry.m_write.m_type == AMH_DEVICE_DELEGATE_MO) - devtag = entry.m_wproto16mo.device_name(); + devtag = entry.m_wproto16mo.finder_target(); else - devtag = entry.m_wproto16smo.device_name(); + devtag = entry.m_wproto16smo.finder_target(); break; case 32: if (entry.m_write.m_type == AMH_DEVICE_DELEGATE) - devtag = entry.m_wproto32.device_name(); + devtag = entry.m_wproto32.finder_target(); else if (entry.m_write.m_type == AMH_DEVICE_DELEGATE_M) - devtag = entry.m_wproto32m.device_name(); + devtag = entry.m_wproto32m.finder_target(); else if (entry.m_write.m_type == AMH_DEVICE_DELEGATE_S) - devtag = entry.m_wproto32s.device_name(); + devtag = entry.m_wproto32s.finder_target(); else if (entry.m_write.m_type == AMH_DEVICE_DELEGATE_SM) - devtag = entry.m_wproto32sm.device_name(); + devtag = entry.m_wproto32sm.finder_target(); else if (entry.m_write.m_type == AMH_DEVICE_DELEGATE_MO) - devtag = entry.m_wproto32mo.device_name(); + devtag = entry.m_wproto32mo.finder_target(); else - devtag = entry.m_wproto32smo.device_name(); + devtag = entry.m_wproto32smo.finder_target(); break; case 64: if (entry.m_write.m_type == AMH_DEVICE_DELEGATE) - devtag = entry.m_wproto64.device_name(); + devtag = entry.m_wproto64.finder_target(); else if (entry.m_write.m_type == AMH_DEVICE_DELEGATE_M) - devtag = entry.m_wproto64m.device_name(); + devtag = entry.m_wproto64m.finder_target(); else if (entry.m_write.m_type == AMH_DEVICE_DELEGATE_S) - devtag = entry.m_wproto64s.device_name(); + devtag = entry.m_wproto64s.finder_target(); else if (entry.m_write.m_type == AMH_DEVICE_DELEGATE_SM) - devtag = entry.m_wproto64sm.device_name(); + devtag = entry.m_wproto64sm.finder_target(); else if (entry.m_write.m_type == AMH_DEVICE_DELEGATE_MO) - devtag = entry.m_wproto64mo.device_name(); + devtag = entry.m_wproto64mo.finder_target(); else - devtag = entry.m_wproto64smo.device_name(); + devtag = entry.m_wproto64smo.finder_target(); break; } - if (entry.m_devbase.subdevice(devtag) == nullptr) - osd_printf_error("%s space memory map entry writes to nonexistent device '%s'\n", spaceconfig.m_name, devtag ? devtag : "<unspecified>"); + if (devtag.second && !devtag.first.get().subdevice(devtag.second)) + osd_printf_error("%s space memory map entry writes to nonexistent device '%s'\n", spaceconfig.m_name, devtag.first.get().subtag(devtag.second)); #ifndef MAME_DEBUG // assert will catch this earlier (void)entry.unitmask_is_appropriate(entry.m_write.m_bits, entry.m_mask, entry.m_write.m_name); #endif @@ -1298,3 +1395,8 @@ void address_map::map_validity_check(validity_checker &valid, int spacenum) cons valid.validate_tag(entry.m_share); } } + +const address_space_config &address_map::get_config() const +{ + return *m_config; +} diff --git a/src/emu/addrmap.h b/src/emu/addrmap.h index 5a256c735fe..25ff7d8cec5 100644 --- a/src/emu/addrmap.h +++ b/src/emu/addrmap.h @@ -40,7 +40,8 @@ enum map_handler_type AMH_DEVICE_DELEGATE_SMO, AMH_PORT, AMH_BANK, - AMH_DEVICE_SUBMAP + AMH_DEVICE_SUBMAP, + AMH_VIEW }; @@ -70,19 +71,23 @@ class address_map_entry { friend class address_map; - template <typename T, typename Ret, typename... Params> - struct is_addrmap_method { static constexpr bool value = std::is_constructible<address_map_constructor, Ret (T::*)(Params...), const char *, T*>::value; }; - - template <typename T, typename Ret, typename... Params> - static std::enable_if_t<is_addrmap_method<T, Ret, Params...>::value, address_map_constructor> make_delegate(Ret (T::*func)(Params...), const char *name, T *obj) - { return address_map_constructor(func, name, obj); } + template <typename T, bool Reqd> + static device_t &find_device(const device_finder<T, Reqd> &finder) { + const std::pair<device_t &, const char *> target(finder.finder_target()); + device_t *device(target.first.subdevice(target.second)); + if (!device) + throw emu_fatalerror("Device %s not found in %s\n", target.second, target.first.tag()); + return *device; + } 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 *> make_pointer(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 *> make_pointer(U &obj) - { return &dynamic_cast<T &>(obj); } + static T *make_pointer(U &obj) + { + if constexpr (std::is_convertible_v<std::add_pointer_t<U>, std::add_pointer_t<T> >) + return &downcast<T &>(obj); + else + return &dynamic_cast<T &>(obj); + } 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(); } @@ -116,6 +121,11 @@ public: assert(&target.first == &m_devbase); return share(target.second); } + template<typename _ptrt> address_map_entry &share(const memory_share_creator<_ptrt> &finder) { + const std::pair<device_t &, const char *> target(finder.finder_target()); + assert(&target.first == &m_devbase); + return share(target.second); + } address_map_entry &rom() { m_read.m_type = AMH_ROM; return *this; } address_map_entry &ram() { m_read.m_type = AMH_RAM; m_write.m_type = AMH_RAM; return *this; } @@ -139,16 +149,59 @@ public: // chip select width setting address_map_entry &cswidth(int _cswidth) { m_cswidth = _cswidth; return *this; } + // flags setting + address_map_entry &flags(u16 _flags) { m_flags = _flags; return *this; } + // I/O port configuration address_map_entry &portr(const char *tag) { m_read.m_type = AMH_PORT; m_read.m_tag = tag; return *this; } address_map_entry &portw(const char *tag) { m_write.m_type = AMH_PORT; m_write.m_tag = tag; return *this; } address_map_entry &portrw(const char *tag) { portr(tag); portw(tag); return *this; } + template<bool req> address_map_entry &portr(ioport_finder<req> &finder) { + const std::pair<device_t &, const char *> target(finder.finder_target()); + assert(&target.first == &m_devbase); + m_read.m_type = AMH_PORT; + m_read.m_tag = target.second; + return *this; + } + + template<bool req> address_map_entry &portw(ioport_finder<req> &finder) { + const std::pair<device_t &, const char *> target(finder.finder_target()); + assert(&target.first == &m_devbase); + m_write.m_type = AMH_PORT; + m_write.m_tag = target.second; + return *this; + } + + template<bool req> address_map_entry &portrw(ioport_finder<req> &finder) { + const std::pair<device_t &, const char *> target(finder.finder_target()); + assert(&target.first == &m_devbase); + m_write.m_type = m_read.m_type = AMH_PORT; + m_write.m_tag = m_read.m_tag = target.second; + return *this; + } + // memory bank configuration address_map_entry &bankr(const char *tag) { m_read.m_type = AMH_BANK; m_read.m_tag = tag; return *this; } address_map_entry &bankw(const char *tag) { m_write.m_type = AMH_BANK; m_write.m_tag = tag; return *this; } address_map_entry &bankrw(const char *tag) { bankr(tag); bankw(tag); return *this; } + address_map_entry &bankr(const memory_bank_creator &finder) { + const std::pair<device_t &, const char *> target(finder.finder_target()); + assert(&target.first == &m_devbase); + return bankr(target.second); + } + address_map_entry &bankw(const memory_bank_creator &finder) { + const std::pair<device_t &, const char *> target(finder.finder_target()); + assert(&target.first == &m_devbase); + return bankw(target.second); + } + address_map_entry &bankrw(const memory_bank_creator &finder) { + const std::pair<device_t &, const char *> target(finder.finder_target()); + assert(&target.first == &m_devbase); + return bankrw(target.second); + } + template<bool _reqd> address_map_entry &bankr(const memory_bank_finder<_reqd> &finder) { const std::pair<device_t &, const char *> target(finder.finder_target()); assert(&target.first == &m_devbase); @@ -173,143 +226,199 @@ public: address_map_entry &m(const char *tag, address_map_constructor func); address_map_entry &m(device_t *device, address_map_constructor func); + // view initialization + void view(memory_view &mv); + + + // wait-states, implicit base -> delegate converter + template <typename T> + address_map_entry &before_time(u64 (T::*ws)(offs_t, u64), const char *name) + { m_before_time = ws_time_delegate(*make_pointer<T>(m_devbase), ws, name); return *this; } + + template <typename T> + address_map_entry &before_delay(u32 (T::*ws)(offs_t), const char *name) + { m_before_delay = ws_delay_delegate(*make_pointer<T>(m_devbase), ws, name); return *this; } + + template <typename T> + address_map_entry &after_delay(u32 (T::*ws)(offs_t), const char *name) + { m_after_delay = ws_delay_delegate(*make_pointer<T>(m_devbase), ws, name); return *this; } + + // wait-states, device tag -> delegate converter + template <typename T> + address_map_entry &before_time(const char *tag, u64 (T::*ws)(offs_t, u64), const char *name) + { m_before_time = ws_time_delegate(m_devbase, tag, ws, name); return *this; } + + template <typename T> + address_map_entry &before_delay(const char *tag, u32 (T::*ws)(offs_t), const char *name) + { m_before_delay = ws_delay_delegate(m_devbase, tag, ws, name); return *this; } + + template <typename T> + address_map_entry &after_delay(const char *tag, u32 (T::*ws)(offs_t), const char *name) + { m_after_delay = ws_delay_delegate(m_devbase, tag, ws, name); return *this; } + + // wait-states, device reference -> delegate converter + template <typename T, typename U> + address_map_entry &before_time(T &obj, u64 (U::*ws)(offs_t, u64), const char *name) + { m_before_time = ws_time_delegate(obj, ws, name); return *this; } + + template <typename T, typename U> + address_map_entry &before_delay(T &obj, u32 (U::*ws)(offs_t), const char *name) + { m_before_delay = ws_delay_delegate(obj, ws, name); return *this; } + + template <typename T, typename U> + address_map_entry &after_delay(T &obj, u32 (U::*ws)(offs_t), const char *name) + { m_after_delay = ws_delay_delegate(obj, ws, name); return *this; } + + // wait-states, lambda -> delegate converter + template <typename T> + address_map_entry &before_time(T &&ws, const char *name) + { m_before_time = ws_time_delegate(m_devbase, std::forward<T>(ws), name); return *this; } + + template <typename T> + address_map_entry &before_delay(T &&ws, const char *name) + { m_before_delay = ws_delay_delegate(m_devbase, std::forward<T>(ws), name); return *this; } + + template <typename T> + address_map_entry &after_delay(T &&ws, const char *name) + { m_after_delay = ws_delay_delegate(m_devbase, std::forward<T>(ws), name); return *this; } + // implicit base -> delegate converter template <typename T, typename Ret, typename... Params> address_map_entry &r(Ret (T::*read)(Params...), const char *read_name) - { return r(emu::detail::make_delegate(read, read_name, m_devbase.tag(), make_pointer<T>(m_devbase))); } + { return r(emu::rw_delegate(*make_pointer<T>(m_devbase), read, read_name)); } template <typename T, typename Ret, typename... Params> address_map_entry &w(Ret (T::*write)(Params...), const char *write_name) - { return w(emu::detail::make_delegate(write, write_name, m_devbase.tag(), make_pointer<T>(m_devbase))); } + { return w(emu::rw_delegate(*make_pointer<T>(m_devbase), write, write_name)); } template <typename T, typename RetR, typename... ParamsR, typename U, typename RetW, typename... ParamsW> address_map_entry &rw(RetR (T::*read)(ParamsR...), const char *read_name, RetW (U::*write)(ParamsW...), const char *write_name) - { return r(emu::detail::make_delegate(read, read_name, m_devbase.tag(), make_pointer<T>(m_devbase))).w(emu::detail::make_delegate(write, write_name, m_devbase.tag(), make_pointer<U>(m_devbase))); } + { return r(emu::rw_delegate(*make_pointer<T>(m_devbase), read, read_name)).w(emu::rw_delegate(*make_pointer<U>(m_devbase), write, write_name)); } template <typename T, typename Ret, typename... Params> address_map_entry &m(Ret (T::*map)(Params...), const char *map_name) - { return m(&m_devbase, make_delegate(map, map_name, make_pointer<T>(m_devbase))); } + { return m(&m_devbase, address_map_constructor(map, map_name, make_pointer<T>(m_devbase))); } // device tag -> delegate converter template <typename T, typename Ret, typename... Params> address_map_entry &r(const char *tag, Ret (T::*read)(Params...), const char *read_name) - { return r(emu::detail::make_delegate(read, read_name, tag, nullptr)); } + { return r(emu::rw_delegate(m_devbase, tag, read, read_name)); } template <typename T, typename Ret, typename... Params> address_map_entry &w(const char *tag, Ret (T::*write)(Params...), const char *write_name) - { return w(emu::detail::make_delegate(write, write_name, tag, nullptr)); } + { return w(emu::rw_delegate(m_devbase, tag, write, write_name)); } template <typename T, typename RetR, typename... ParamsR, typename U, typename RetW, typename... ParamsW> address_map_entry &rw(const char *tag, RetR (T::*read)(ParamsR...), const char *read_name, RetW (U::*write)(ParamsW...), const char *write_name) - { return r(emu::detail::make_delegate(read, read_name, tag, nullptr)).w(emu::detail::make_delegate(write, write_name, tag, nullptr)); } + { return r(emu::rw_delegate(m_devbase, tag, read, read_name)).w(emu::rw_delegate(m_devbase, tag, write, write_name)); } template <typename T, typename Ret, typename... Params> address_map_entry &m(const char *tag, Ret (T::*map)(Params...), const char *map_name) - { return m(tag, make_delegate(map, map_name, static_cast<T *>(nullptr))); } + { return m(tag, address_map_constructor(map, map_name, static_cast<T *>(nullptr))); } // device reference -> delegate converter template <typename T, typename U, typename Ret, typename... Params> address_map_entry &r(T &obj, Ret (U::*read)(Params...), const char *read_name) - { return r(emu::detail::make_delegate(read, read_name, get_tag(obj), make_pointer<U>(obj))); } + { return r(emu::rw_delegate(*make_pointer<U>(obj), read, read_name)); } template <typename T, typename U, typename Ret, typename... Params> address_map_entry &w(T &obj, Ret (U::*write)(Params...), const char *write_name) - { return w(emu::detail::make_delegate(write, write_name, get_tag(obj), make_pointer<U>(obj))); } + { return w(emu::rw_delegate(*make_pointer<U>(obj), write, write_name)); } template <typename T, typename U, typename RetR, typename... ParamsR, typename V, typename RetW, typename... ParamsW> address_map_entry &rw(T &obj, RetR (U::*read)(ParamsR...), const char *read_name, RetW (V::*write)(ParamsW...), const char *write_name) - { return r(emu::detail::make_delegate(read, read_name, get_tag(obj), make_pointer<U>(obj))).w(emu::detail::make_delegate(write, write_name, get_tag(obj), make_pointer<V>(obj))); } + { return r(emu::rw_delegate(*make_pointer<U>(obj), read, read_name)).w(emu::rw_delegate(make_pointer<V>(obj), write, write_name)); } template <typename T, typename U, typename Ret, typename... Params> address_map_entry &m(T &obj, Ret (U::*map)(Params...), const char *map_name) - { return m(make_pointer<device_t>(obj), make_delegate(map, map_name, make_pointer<U>(obj))); } + { return m(make_pointer<device_t>(obj), address_map_constructor(map, map_name, make_pointer<U>(obj))); } // device finder -> delegate converter template <typename T, bool Reqd, typename U, typename Ret, typename... Params> - address_map_entry &r(device_finder<T, Reqd> &finder, Ret (U::*read)(Params...), const char *read_name) - { const std::pair<device_t &, const char *> target(finder.finder_target()); device_t &device(*target.first.subdevice(target.second)); return r(emu::detail::make_delegate(read, read_name, device.tag(), make_pointer<U>(device))); } + address_map_entry &r(device_finder<T, Reqd> &finder, Ret (U::*read)(Params...), const char *read_name) { + return r(emu::rw_delegate(finder, read, read_name)); + } template <typename T, bool Reqd, typename U, typename Ret, typename... Params> - address_map_entry &r(const device_finder<T, Reqd> &finder, Ret (U::*read)(Params...), const char *read_name) - { const std::pair<device_t &, const char *> target(finder.finder_target()); device_t &device(*target.first.subdevice(target.second)); return r(emu::detail::make_delegate(read, read_name, device.tag(), make_pointer<U>(device))); } + address_map_entry &r(const device_finder<T, Reqd> &finder, Ret (U::*read)(Params...), const char *read_name) { + return r(emu::rw_delegate(finder, read, read_name)); + } template <typename T, bool Reqd, typename U, typename Ret, typename... Params> - address_map_entry &w(device_finder<T, Reqd> &finder, Ret (U::*write)(Params...), const char *write_name) - { const std::pair<device_t &, const char *> target(finder.finder_target()); device_t &device(*target.first.subdevice(target.second)); return w(emu::detail::make_delegate(write, write_name, device.tag(), make_pointer<U>(device))); } + address_map_entry &w(device_finder<T, Reqd> &finder, Ret (U::*write)(Params...), const char *write_name) { + return w(emu::rw_delegate(finder, write, write_name)); + } template <typename T, bool Reqd, typename U, typename Ret, typename... Params> - address_map_entry &w(const device_finder<T, Reqd> &finder, Ret (U::*write)(Params...), const char *write_name) - { const std::pair<device_t &, const char *> target(finder.finder_target()); device_t &device(*target.first.subdevice(target.second)); return w(emu::detail::make_delegate(write, write_name, device.tag(), make_pointer<U>(device))); } + address_map_entry &w(const device_finder<T, Reqd> &finder, Ret (U::*write)(Params...), const char *write_name) { + return w(emu::rw_delegate(finder, write, write_name)); + } template <typename T, bool Reqd, typename U, typename RetR, typename... ParamsR, typename V, typename RetW, typename... ParamsW> - address_map_entry &rw(device_finder<T, Reqd> &finder, RetR (U::*read)(ParamsR...), const char *read_name, RetW (V::*write)(ParamsW...), const char *write_name) - { const std::pair<device_t &, const char *> target(finder.finder_target()); device_t &device(*target.first.subdevice(target.second)); return r(emu::detail::make_delegate(read, read_name, device.tag(), make_pointer<U>(device))).w(emu::detail::make_delegate(write, write_name, device.tag(), make_pointer<V>(device))); } + address_map_entry &rw(device_finder<T, Reqd> &finder, RetR (U::*read)(ParamsR...), const char *read_name, RetW (V::*write)(ParamsW...), const char *write_name) { + return r(emu::rw_delegate(finder, read, read_name)) + .w(emu::rw_delegate(finder, write, write_name)); + } template <typename T, bool Reqd, typename U, typename RetR, typename... ParamsR, typename V, typename RetW, typename... ParamsW> - address_map_entry &rw(const device_finder<T, Reqd> &finder, RetR (U::*read)(ParamsR...), const char *read_name, RetW (V::*write)(ParamsW...), const char *write_name) - { const std::pair<device_t &, const char *> target(finder.finder_target()); device_t &device(*target.first.subdevice(target.second)); return r(emu::detail::make_delegate(read, read_name, device.tag(), make_pointer<U>(device))).w(emu::detail::make_delegate(write, write_name, device.tag(), make_pointer<V>(device))); } + address_map_entry &rw(const device_finder<T, Reqd> &finder, RetR (U::*read)(ParamsR...), const char *read_name, RetW (V::*write)(ParamsW...), const char *write_name) { + return r(emu::rw_delegate(finder, read, read_name)) + .w(emu::rw_delegate(finder, write, write_name)); + } template <typename T, bool Reqd, typename U, typename Ret, typename... Params> - address_map_entry &m(device_finder<T, Reqd> &finder, Ret (U::*map)(Params...), const char *map_name) - { const std::pair<device_t &, const char *> target(finder.finder_target()); device_t &device(*target.first.subdevice(target.second)); return m(&device, make_delegate(map, map_name, make_pointer<U>(device))); } + address_map_entry &m(device_finder<T, Reqd> &finder, Ret (U::*map)(Params...), const char *map_name) { + device_t &device(find_device(finder)); + return m(&device, address_map_constructor(map, map_name, make_pointer<U>(device))); + } template <typename T, bool Reqd, typename U, typename Ret, typename... Params> - address_map_entry &m(const device_finder<T, Reqd> &finder, Ret (U::*map)(Params...), const char *map_name) - { const std::pair<device_t &, const char *> target(finder.finder_target()); device_t &device(*target.first.subdevice(target.second)); return m(make_delegate(map, map_name, make_pointer<U>(device))); } + address_map_entry &m(const device_finder<T, Reqd> &finder, Ret (U::*map)(Params...), const char *map_name) { + device_t &device(find_device(finder)); + return m(&device, address_map_constructor(map, map_name, make_pointer<U>(device))); + } // lambda -> delegate converter - template<typename _lr> address_map_entry &lr8(const char *name, _lr &&read) { - return r(emu::detail::make_lr8_delegate(read, name)); - } + template <typename T> address_map_entry &lr8(T &&read, const char *name) + { return r(emu::detail::make_lr8_delegate(m_devbase, std::forward<T>(read), name)); } - template<typename _lr> address_map_entry &lr16(const char *name, _lr &&read) { - return r(emu::detail::make_lr16_delegate(read, name)); - } + template <typename T> address_map_entry &lr16(T &&read, const char *name) + { return r(emu::detail::make_lr16_delegate(m_devbase, std::forward<T>(read), name)); } - template<typename _lr> address_map_entry &lr32(const char *name, _lr &&read) { - return r(emu::detail::make_lr32_delegate(read, name)); - } + template <typename T> address_map_entry &lr32(T &&read, const char *name) + { return r(emu::detail::make_lr32_delegate(m_devbase, std::forward<T>(read), name)); } - template<typename _lr> address_map_entry &lr64(const char *name, _lr &&read) { - return r(emu::detail::make_lr64_delegate(read, name)); - } + template <typename T> address_map_entry &lr64(T &&read, const char *name) + { return r(emu::detail::make_lr64_delegate(m_devbase, std::forward<T>(read), name)); } - template<typename _lw> address_map_entry &lw8(const char *name, _lw &&write) { - return w(emu::detail::make_lw8_delegate(write, name)); - } + template <typename T> address_map_entry &lw8(T &&write, const char *name) + { return w(emu::detail::make_lw8_delegate(m_devbase, std::forward<T>(write), name)); } - template<typename _lw> address_map_entry &lw16(const char *name, _lw &&write) { - return w(emu::detail::make_lw16_delegate(write, name)); - } + template <typename T> address_map_entry &lw16(T &&write, const char *name) + { return w(emu::detail::make_lw16_delegate(m_devbase, std::forward<T>(write), name)); } - template<typename _lw> address_map_entry &lw32(const char *name, _lw &&write) { - return w(emu::detail::make_lw32_delegate(write, name)); - } + template <typename T> address_map_entry &lw32(T &&write, const char *name) + { return w(emu::detail::make_lw32_delegate(m_devbase, std::forward<T>(write), name)); } - template<typename _lw> address_map_entry &lw64(const char *name, _lw &&write) { - return w(emu::detail::make_lw64_delegate(write, name)); - } + template <typename T> address_map_entry &lw64(T &&write, const char *name) + { return w(emu::detail::make_lw64_delegate(m_devbase, std::forward<T>(write), name)); } - template<typename _lr, typename _lw> address_map_entry &lrw8(const char *name, _lr &&read, _lw &&write) { - return r(emu::detail::make_lr8_delegate(read, name)).w(emu::detail::make_lw8_delegate(write, name)); - } + template <typename T, typename U> address_map_entry &lrw8(T &&read, const char *read_name, U &&write, const char *write_name) + { return r(emu::detail::make_lr8_delegate(m_devbase, std::forward<T>(read), read_name)).w(emu::detail::make_lw8_delegate(m_devbase, std::forward<U>(write), write_name)); } - template<typename _lr, typename _lw> address_map_entry &lrw16(const char *name, _lr &&read, _lw &&write) { - return r(emu::detail::make_lr16_delegate(read, name)).w(emu::detail::make_lw16_delegate(write, name)); - } + template <typename T, typename U> address_map_entry &lrw16(T &&read, const char *read_name, U &&write, const char *write_name) + { return r(emu::detail::make_lr16_delegate(m_devbase, std::forward<T>(read), read_name)).w(emu::detail::make_lw16_delegate(m_devbase, std::forward<U>(write), write_name)); } - template<typename _lr, typename _lw> address_map_entry &lrw32(const char *name, _lr &&read, _lw &&write) { - return r(emu::detail::make_lr32_delegate(read, name)).w(emu::detail::make_lw32_delegate(write, name)); - } + template <typename T, typename U> address_map_entry &lrw32(T &&read, const char *read_name, U &&write, const char *write_name) + { return r(emu::detail::make_lr32_delegate(m_devbase, std::forward<T>(read), read_name)).w(emu::detail::make_lw32_delegate(m_devbase, std::forward<U>(write), write_name)); } - template<typename _lr, typename _lw> address_map_entry &lrw64(const char *name, _lr &&read, _lw &&write) { - return r(emu::detail::make_lr64_delegate(read, name)).w(emu::detail::make_lw64_delegate(write, name)); - } + template <typename T, typename U> address_map_entry &lrw64(T &&read, const char *read_name, U &&write, const char *write_name) + { return r(emu::detail::make_lr64_delegate(m_devbase, std::forward<T>(read), read_name)).w(emu::detail::make_lw64_delegate(m_devbase, std::forward<U>(write), write_name)); } // public state address_map_entry * m_next; // pointer to the next entry @@ -324,11 +433,15 @@ public: offs_t m_addrselect; // select bits u64 m_mask; // mask for which lanes apply int m_cswidth; // chip select width override + u16 m_flags; // user flags map_handler_data m_read; // data for read handler map_handler_data m_write; // data for write handler const char * m_share; // tag of a shared memory block const char * m_region; // tag of region containing the memory backing this entry offs_t m_rgnoffs; // offset within the region + ws_time_delegate m_before_time; // before-time wait-state + ws_delay_delegate m_before_delay; // before-delay wait-state + ws_delay_delegate m_after_delay; // after-delay wait-state // handlers read8_delegate m_rproto8; // 8-bit read proto-delegate @@ -387,6 +500,7 @@ public: device_t *m_submap_device; address_map_constructor m_submap_delegate; + memory_view *m_view; // information used during processing void * m_memory; // pointer to memory backing this entry @@ -460,15 +574,15 @@ class address_map public: // construction/destruction address_map(device_t &device, int spacenum); + address_map(memory_view &view); address_map(device_t &device, address_map_entry *entry); - address_map(const address_space &space, offs_t start, offs_t end, u64 unitmask, int cswidth, device_t &device, address_map_constructor submap_delegate); + address_map(const address_space &space, offs_t start, offs_t end, u64 unitmask, int cswidth, u16 flags, device_t &device, address_map_constructor submap_delegate); ~address_map(); // setters void global_mask(offs_t mask); void unmap_value_low() { m_unmapval = 0; } void unmap_value_high() { m_unmapval = ~0; } - void unmap_value(u8 value) { m_unmapval = value; } // add a new entry of the given type address_map_entry &operator()(offs_t start, offs_t end); @@ -476,12 +590,15 @@ public: // public data int m_spacenum; // space number of the map device_t * m_device; // associated device + memory_view * m_view; // view, when in one + const address_space_config * m_config; // space configuration u8 m_unmapval; // unmapped memory value offs_t m_globalmask; // global mask simple_list<address_map_entry> m_entrylist; // list of entries void import_submaps(running_machine &machine, device_t &owner, int data_width, endianness_t endian, int addr_shift); void map_validity_check(validity_checker &valid, int spacenum) const; + const address_space_config &get_config() const; }; #endif // MAME_EMU_ADDRMAP_H diff --git a/src/emu/attotime.cpp b/src/emu/attotime.cpp index bda9a5b7a24..030c3193551 100644 --- a/src/emu/attotime.cpp +++ b/src/emu/attotime.cpp @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles /*************************************************************************** - attotime.c + attotime.cpp Support functions for working with attotime data. @@ -40,17 +40,17 @@ attotime &attotime::operator*=(u32 factor) // split attoseconds into upper and lower halves which fit into 32 bits u32 attolo; - u32 attohi = divu_64x32_rem(m_attoseconds, ATTOSECONDS_PER_SECOND_SQRT, &attolo); + u32 attohi = divu_64x32_rem(m_attoseconds, ATTOSECONDS_PER_SECOND_SQRT, attolo); // scale the lower half, then split into high/low parts u64 temp = mulu_32x32(attolo, factor); u32 reslo; - temp = divu_64x32_rem(temp, ATTOSECONDS_PER_SECOND_SQRT, &reslo); + temp = divu_64x32_rem(temp, ATTOSECONDS_PER_SECOND_SQRT, reslo); // scale the upper half, then split into high/low parts temp += mulu_32x32(attohi, factor); u32 reshi; - temp = divu_64x32_rem(temp, ATTOSECONDS_PER_SECOND_SQRT, &reshi); + temp = divu_64x32_rem(temp, ATTOSECONDS_PER_SECOND_SQRT, reshi); // scale the seconds temp += mulu_32x32(m_seconds, factor); @@ -80,19 +80,19 @@ attotime &attotime::operator/=(u32 factor) // split attoseconds into upper and lower halves which fit into 32 bits u32 attolo; - u32 attohi = divu_64x32_rem(m_attoseconds, ATTOSECONDS_PER_SECOND_SQRT, &attolo); + u32 attohi = divu_64x32_rem(m_attoseconds, ATTOSECONDS_PER_SECOND_SQRT, attolo); // divide the seconds and get the remainder u32 remainder; - m_seconds = divu_64x32_rem(m_seconds, factor, &remainder); + m_seconds = divu_64x32_rem(m_seconds, factor, remainder); // combine the upper half of attoseconds with the remainder and divide that u64 temp = s64(attohi) + mulu_32x32(remainder, ATTOSECONDS_PER_SECOND_SQRT); - u32 reshi = divu_64x32_rem(temp, factor, &remainder); + u32 reshi = divu_64x32_rem(temp, factor, remainder); // combine the lower half of attoseconds with the remainder and divide that temp = attolo + mulu_32x32(remainder, ATTOSECONDS_PER_SECOND_SQRT); - u32 reslo = divu_64x32_rem(temp, factor, &remainder); + u32 reslo = divu_64x32_rem(temp, factor, remainder); // round based on the remainder m_attoseconds = (attoseconds_t)reslo + mulu_32x32(reshi, ATTOSECONDS_PER_SECOND_SQRT); @@ -119,11 +119,11 @@ const char *attotime::as_string(int precision) const // special case: never if (*this == never) - sprintf(buffer, "%-*s", precision, "(never)"); + snprintf(buffer, 30, "%-*s", precision, "(never)"); // case 1: we want no precision; seconds only else if (precision == 0) - sprintf(buffer, "%d", m_seconds); + snprintf(buffer, 30, "%d", m_seconds); // case 2: we want 9 or fewer digits of precision else if (precision <= 9) @@ -135,21 +135,38 @@ const char *attotime::as_string(int precision) const upper /= 10; temp++; } - sprintf(buffer, "%d.%0*d", m_seconds, precision, upper); + snprintf(buffer, 30, "%d.%0*d", m_seconds, precision, upper); } // case 3: more than 9 digits of precision else { u32 lower; - u32 upper = divu_64x32_rem(m_attoseconds, ATTOSECONDS_PER_SECOND_SQRT, &lower); + u32 upper = divu_64x32_rem(m_attoseconds, ATTOSECONDS_PER_SECOND_SQRT, lower); int temp = precision; while (temp < 18) { lower /= 10; temp++; } - sprintf(buffer, "%d.%09d%0*d", m_seconds, upper, precision - 9, lower); + snprintf(buffer, 30, "%d.%09d%0*d", m_seconds, upper, precision - 9, lower); } return buffer; } + +//------------------------------------------------- +// to_string - return a human-readable string +// describing an attotime for use in logs +//------------------------------------------------- + +std::string attotime::to_string() const +{ + attotime t = *this; + const char *sign = ""; + if(t.seconds() < 0) { + t = attotime::zero-t; + sign = "-"; + } + int nsec = t.attoseconds() / ATTOSECONDS_PER_NANOSECOND; + return util::string_format("%s%04d.%03d,%03d,%03d", sign, int(t.seconds()), nsec/1000000, (nsec/1000)%1000, nsec % 1000); +} diff --git a/src/emu/attotime.h b/src/emu/attotime.h index c3efeca4ad5..39fcf6e5310 100644 --- a/src/emu/attotime.h +++ b/src/emu/attotime.h @@ -38,9 +38,10 @@ #include "emucore.h" #include "xtal.h" -#include <math.h> -#undef min -#undef max +#include "eminline.h" + +#include <cmath> + //************************************************************************** // CONSTANTS @@ -84,22 +85,22 @@ template <typename T> inline constexpr attoseconds_t ATTOSECONDS_IN_NSEC(T &&x) //************************************************************************** // TYPE DEFINITIONS -//***************************************************************************/ +//************************************************************************** // the attotime structure itself class attotime { public: // construction/destruction - constexpr attotime() : m_seconds(0), m_attoseconds(0) { } + constexpr attotime() noexcept : m_seconds(0), m_attoseconds(0) { } /** Constructs with @p secs seconds and @p attos attoseconds. */ - constexpr attotime(seconds_t secs, attoseconds_t attos) : m_seconds(secs), m_attoseconds(attos) { } + constexpr attotime(seconds_t secs, attoseconds_t attos) noexcept : m_seconds(secs), m_attoseconds(attos) { } - constexpr attotime(const attotime& that) : m_seconds(that.m_seconds), m_attoseconds(that.m_attoseconds) { } + constexpr attotime(const attotime& that) noexcept : m_seconds(that.m_seconds), m_attoseconds(that.m_attoseconds) { } // assignment - attotime& operator=(const attotime& that) + attotime &operator=(const attotime& that) noexcept { this->m_seconds = that.m_seconds; this->m_attoseconds = that.m_attoseconds; @@ -107,36 +108,41 @@ public: } // queries - constexpr bool is_zero() const { return (m_seconds == 0 && m_attoseconds == 0); } + constexpr bool is_zero() const noexcept { return (m_seconds == 0 && m_attoseconds == 0); } /** Test if value is above @ref ATTOTIME_MAX_SECONDS (considered an overflow) */ - constexpr bool is_never() const { return (m_seconds >= ATTOTIME_MAX_SECONDS); } + constexpr bool is_never() const noexcept { return (m_seconds >= ATTOTIME_MAX_SECONDS); } // conversion to other forms - constexpr double as_double() const { return double(m_seconds) + ATTOSECONDS_TO_DOUBLE(m_attoseconds); } - constexpr attoseconds_t as_attoseconds() const; - constexpr double as_hz() const { return m_seconds == 0 ? ATTOSECONDS_TO_HZ(m_attoseconds) : is_never() ? 0.0 : 1.0 / as_double(); } + constexpr double as_double() const noexcept { return double(m_seconds) + ATTOSECONDS_TO_DOUBLE(m_attoseconds); } + constexpr attoseconds_t as_attoseconds() const noexcept; + double as_hz() const noexcept { assert(!is_zero()); return m_seconds == 0 ? ATTOSECONDS_TO_HZ(m_attoseconds) : is_never() ? 0.0 : 1.0 / as_double(); } + double as_khz() const noexcept { assert(!is_zero()); return m_seconds == 0 ? double(ATTOSECONDS_PER_MILLISECOND) / double(m_attoseconds) : is_never() ? 0.0 : 1e-3 / as_double(); } + double as_mhz() const noexcept { assert(!is_zero()); return m_seconds == 0 ? double(ATTOSECONDS_PER_MICROSECOND) / double(m_attoseconds) : is_never() ? 0.0 : 1e-6 / as_double(); } u64 as_ticks(u32 frequency) const; u64 as_ticks(const XTAL &xtal) const { return as_ticks(xtal.value()); } /** Convert to string using at @p precision */ const char *as_string(int precision = 9) const; + /** Convert to string for human readability in logs */ + std::string to_string() const; + /** @return the attoseconds portion. */ - constexpr attoseconds_t attoseconds() const { return m_attoseconds; } + constexpr attoseconds_t attoseconds() const noexcept { return m_attoseconds; } /** @return the seconds portion. */ - constexpr seconds_t seconds() const { return m_seconds; } + constexpr seconds_t seconds() const noexcept { return m_seconds; } static attotime from_double(double _time); static attotime from_ticks(u64 ticks, u32 frequency); static attotime from_ticks(u64 ticks, const XTAL &xtal) { return from_ticks(ticks, xtal.value()); } - /** Create an attotime from a integer count of seconds @seconds */ + /** Create an attotime from an integer count of seconds @p seconds */ static constexpr attotime from_seconds(s32 seconds) { return attotime(seconds, 0); } - /** Create an attotime from a integer count of milliseconds @msec */ + /** Create an attotime from an integer count of milliseconds @p msec */ static constexpr attotime from_msec(s64 msec) { return attotime(msec / 1000, (msec % 1000) * (ATTOSECONDS_PER_SECOND / 1000)); } - /** Create an attotime from a integer count of microseconds @usec */ + /** Create an attotime from an integer count of microseconds @p usec */ static constexpr attotime from_usec(s64 usec) { return attotime(usec / 1000000, (usec % 1000000) * (ATTOSECONDS_PER_SECOND / 1000000)); } - /** Create an attotime from a integer count of nanoseconds @nsec */ + /** Create an attotime from an integer count of nanoseconds @p nsec */ static constexpr attotime from_nsec(s64 nsec) { return attotime(nsec / 1000000000, (nsec % 1000000000) * (ATTOSECONDS_PER_SECOND / 1000000000)); } - /** Create an attotime from at the given frequency @frequency */ + /** Create an attotime from at the given frequency @p frequency */ static attotime from_hz(u32 frequency) { return (frequency > 1) ? attotime(0, HZ_TO_ATTOSECONDS(frequency)) : (frequency == 1) ? attotime(1, 0) : attotime::never; } static attotime from_hz(int frequency) { return (frequency > 0) ? from_hz(u32(frequency)) : attotime::never; } static attotime from_hz(const XTAL &xtal) { return (xtal.dvalue() > 1.0) ? attotime(0, HZ_TO_ATTOSECONDS(xtal)) : from_hz(xtal.dvalue()); } @@ -154,8 +160,8 @@ public: } // math - attotime &operator+=(const attotime &right); - attotime &operator-=(const attotime &right); + attotime &operator+=(const attotime &right) noexcept; + attotime &operator-=(const attotime &right) noexcept; attotime &operator*=(u32 factor); attotime &operator/=(u32 factor); @@ -175,7 +181,7 @@ public: //************************************************************************** /** handle addition between two attotimes */ -inline attotime operator+(const attotime &left, const attotime &right) +inline constexpr attotime operator+(const attotime &left, const attotime &right) noexcept { attotime result; @@ -200,7 +206,7 @@ inline attotime operator+(const attotime &left, const attotime &right) return result; } -inline attotime &attotime::operator+=(const attotime &right) +inline attotime &attotime::operator+=(const attotime &right) noexcept { // if one of the items is never, return never if (this->m_seconds >= ATTOTIME_MAX_SECONDS || right.m_seconds >= ATTOTIME_MAX_SECONDS) @@ -225,7 +231,7 @@ inline attotime &attotime::operator+=(const attotime &right) /** handle subtraction between two attotimes */ -inline attotime operator-(const attotime &left, const attotime &right) +inline constexpr attotime operator-(const attotime &left, const attotime &right) noexcept { attotime result; @@ -246,7 +252,7 @@ inline attotime operator-(const attotime &left, const attotime &right) return result; } -inline attotime &attotime::operator-=(const attotime &right) +inline attotime &attotime::operator-=(const attotime &right) noexcept { // if time1 is never, return never if (this->m_seconds >= ATTOTIME_MAX_SECONDS) @@ -291,39 +297,39 @@ inline attotime operator/(const attotime &left, u32 factor) /** handle comparisons between attotimes */ -inline constexpr bool operator==(const attotime &left, const attotime &right) +inline constexpr bool operator==(const attotime &left, const attotime &right) noexcept { return (left.m_seconds == right.m_seconds && left.m_attoseconds == right.m_attoseconds); } -inline constexpr bool operator!=(const attotime &left, const attotime &right) +inline constexpr bool operator!=(const attotime &left, const attotime &right) noexcept { return (left.m_seconds != right.m_seconds || left.m_attoseconds != right.m_attoseconds); } -inline constexpr bool operator<(const attotime &left, const attotime &right) +inline constexpr bool operator<(const attotime &left, const attotime &right) noexcept { return (left.m_seconds < right.m_seconds || (left.m_seconds == right.m_seconds && left.m_attoseconds < right.m_attoseconds)); } -inline constexpr bool operator<=(const attotime &left, const attotime &right) +inline constexpr bool operator<=(const attotime &left, const attotime &right) noexcept { return (left.m_seconds < right.m_seconds || (left.m_seconds == right.m_seconds && left.m_attoseconds <= right.m_attoseconds)); } -inline constexpr bool operator>(const attotime &left, const attotime &right) +inline constexpr bool operator>(const attotime &left, const attotime &right) noexcept { return (left.m_seconds > right.m_seconds || (left.m_seconds == right.m_seconds && left.m_attoseconds > right.m_attoseconds)); } -inline constexpr bool operator>=(const attotime &left, const attotime &right) +inline constexpr bool operator>=(const attotime &left, const attotime &right) noexcept { return (left.m_seconds > right.m_seconds || (left.m_seconds == right.m_seconds && left.m_attoseconds >= right.m_attoseconds)); } /** Convert to an attoseconds value, clamping to +/- 1 second */ -inline constexpr attoseconds_t attotime::as_attoseconds() const +inline constexpr attoseconds_t attotime::as_attoseconds() const noexcept { return (m_seconds == 0) ? m_attoseconds : // positive values between 0 and 1 second @@ -341,7 +347,7 @@ inline u64 attotime::as_ticks(u32 frequency) const } -/** Create an attotime from a tick count @ticks at the given frequency @frequency */ +/** Create an attotime from a tick count @p ticks at the given frequency @p frequency */ inline attotime attotime::from_ticks(u64 ticks, u32 frequency) { if (frequency > 0) @@ -352,7 +358,7 @@ inline attotime attotime::from_ticks(u64 ticks, u32 frequency) return attotime(0, ticks * attos_per_tick); u32 remainder; - s32 secs = divu_64x32_rem(ticks, frequency, &remainder); + s32 secs = divu_64x32_rem(ticks, frequency, remainder); return attotime(secs, u64(remainder) * attos_per_tick); } else @@ -369,4 +375,4 @@ inline attotime attotime::from_double(double _time) } -#endif // MAME_EMU_ATTOTIME_H +#endif // MAME_EMU_ATTOTIME_H diff --git a/src/emu/audio_effects/aeffect.cpp b/src/emu/audio_effects/aeffect.cpp new file mode 100644 index 00000000000..dcff278099f --- /dev/null +++ b/src/emu/audio_effects/aeffect.cpp @@ -0,0 +1,46 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert + +#include "emu.h" +#include "aeffect.h" +#include "filter.h" +#include "compressor.h" +#include "reverb.h" +#include "eq.h" + +const char *const audio_effect::effect_names[COUNT] = { + "Filters", + "Compressor", + "Reverb", + "Equalizer" +}; + +audio_effect *audio_effect::create(int type, u32 sample_rate, audio_effect *def) +{ + switch(type) { + case FILTER: return new audio_effect_filter (sample_rate, def); + case COMPRESSOR: return new audio_effect_compressor(sample_rate, def); + case REVERB: return new audio_effect_reverb (sample_rate, def); + case EQ: return new audio_effect_eq (sample_rate, def); + } + return nullptr; +} + + +void audio_effect::copy(const emu::detail::output_buffer_flat<sample_t> &src, emu::detail::output_buffer_flat<sample_t> &dest) const +{ + u32 samples = src.available_samples(); + dest.prepare_space(samples); + u32 channels = src.channels(); + for(u32 channel = 0; channel != channels; channel++) { + const sample_t *srcd = src.ptrs(channel, 0); + sample_t *destd = dest.ptrw(channel, 0); + std::copy(srcd, srcd + samples, destd); + } + dest.commit(samples); +} + +u32 audio_effect::history_size() const +{ + return 0; +} diff --git a/src/emu/audio_effects/aeffect.h b/src/emu/audio_effects/aeffect.h new file mode 100644 index 00000000000..72e6279f1e6 --- /dev/null +++ b/src/emu/audio_effects/aeffect.h @@ -0,0 +1,43 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert + +#pragma once + +#ifndef MAME_EMU_AUDIO_EFFECTS_AEFFECT_H +#define MAME_EMU_AUDIO_EFFECTS_AEFFECT_H + +class audio_effect +{ +public: + using sample_t = sound_stream::sample_t; + + enum { + FILTER, + COMPRESSOR, + REVERB, + EQ, + COUNT + }; + + static const char *const effect_names[COUNT]; + + static audio_effect *create(int type, u32 sample_rate, audio_effect *def = nullptr); + + audio_effect(u32 sample_rate, audio_effect *def) : m_default(def), m_sample_rate(sample_rate) {} + virtual ~audio_effect() = default; + + void copy(const emu::detail::output_buffer_flat<sample_t> &src, emu::detail::output_buffer_flat<sample_t> &dest) const; + + virtual int type() const = 0; + virtual u32 history_size() const; + virtual void apply(const emu::detail::output_buffer_flat<sample_t> &src, emu::detail::output_buffer_flat<sample_t> &dest) = 0; + virtual void config_load(util::xml::data_node const *ef_node) = 0; + virtual void config_save(util::xml::data_node *ef_node) const = 0; + virtual void default_changed() = 0; + +protected: + audio_effect *m_default; + u32 m_sample_rate; +}; + +#endif diff --git a/src/emu/audio_effects/compressor.cpp b/src/emu/audio_effects/compressor.cpp new file mode 100644 index 00000000000..06c054a4837 --- /dev/null +++ b/src/emu/audio_effects/compressor.cpp @@ -0,0 +1,28 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert + +#include "emu.h" +#include "compressor.h" +#include "xmlfile.h" + +audio_effect_compressor::audio_effect_compressor(u32 sample_rate, audio_effect *def) : audio_effect(sample_rate, def) +{ +} + + +void audio_effect_compressor::config_load(util::xml::data_node const *ef_node) +{ +} + +void audio_effect_compressor::config_save(util::xml::data_node *ef_node) const +{ +} + +void audio_effect_compressor::default_changed() +{ +} + +void audio_effect_compressor::apply(const emu::detail::output_buffer_flat<sample_t> &src, emu::detail::output_buffer_flat<sample_t> &dest) +{ + copy(src, dest); +} diff --git a/src/emu/audio_effects/compressor.h b/src/emu/audio_effects/compressor.h new file mode 100644 index 00000000000..551212f34e5 --- /dev/null +++ b/src/emu/audio_effects/compressor.h @@ -0,0 +1,24 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert + +#pragma once + +#ifndef MAME_EMU_AUDIO_EFFECTS_COMPRESSOR_H +#define MAME_EMU_AUDIO_EFFECTS_COMPRESSOR_H + +#include "aeffect.h" + +class audio_effect_compressor : public audio_effect +{ +public: + audio_effect_compressor(u32 sample_rate, audio_effect *def); + virtual ~audio_effect_compressor() = default; + + virtual int type() const override { return COMPRESSOR; } + virtual void apply(const emu::detail::output_buffer_flat<sample_t> &src, emu::detail::output_buffer_flat<sample_t> &dest) override; + virtual void config_load(util::xml::data_node const *ef_node) override; + virtual void config_save(util::xml::data_node *ef_node) const override; + virtual void default_changed() override; +}; + +#endif diff --git a/src/emu/audio_effects/eq.cpp b/src/emu/audio_effects/eq.cpp new file mode 100644 index 00000000000..799b2f8c9b5 --- /dev/null +++ b/src/emu/audio_effects/eq.cpp @@ -0,0 +1,331 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert + +#include "emu.h" +#include "eq.h" +#include "xmlfile.h" + +// This effect implements a parametric EQ using peak and shelf filters + +// Formulas taken from (with some fixes): + +// [Zölzer 2011] "DAFX: Digital Audio Effects", Udo Zölzer, Second Edition, Wiley publishing, 2011 (Tables 2.3 and 2.4) +// [Zölzer 2008] "Digital Audio Signal Processing", Udo Zölzer, Second Edition, Wiley publishing, 2008 (Tables 5.3, 5.4 and 5.5) + +audio_effect_eq::audio_effect_eq(u32 sample_rate, audio_effect *def) : audio_effect(sample_rate, def) +{ + // Minimal init to avoid using uninitialized values when reset_* + // recomputes filters + + for(u32 band = 0; band != BANDS; band++) { + m_q[band] = 0.7; + m_f[band] = 1000; + m_db[band] = 0; + } + + reset_mode(); + reset_low_shelf(); + reset_high_shelf(); + for(u32 band = 0; band != BANDS; band++) { + reset_q(band); + reset_f(band); + reset_db(band); + } +} + +void audio_effect_eq::reset_mode() +{ + audio_effect_eq *d = static_cast<audio_effect_eq *>(m_default); + m_isset_mode = false; + m_mode = d ? d->mode() : 1; +} + +void audio_effect_eq::reset_q(u32 band) +{ + audio_effect_eq *d = static_cast<audio_effect_eq *>(m_default); + m_isset_q[band] = false; + m_q[band] = d ? d->q(band) : 0.7; + build_filter(band); +} + +void audio_effect_eq::reset_f(u32 band) +{ + static const u32 defs[BANDS] = { 80, 200, 500, 3200, 8000 }; + audio_effect_eq *d = static_cast<audio_effect_eq *>(m_default); + m_isset_f[band] = false; + m_f[band] = d ? d->f(band) : defs[band]; + build_filter(band); +} + +void audio_effect_eq::reset_db(u32 band) +{ + audio_effect_eq *d = static_cast<audio_effect_eq *>(m_default); + m_isset_db[band] = false; + m_db[band] = d ? d->db(band) : 0; + build_filter(band); +} + +void audio_effect_eq::reset_low_shelf() +{ + audio_effect_eq *d = static_cast<audio_effect_eq *>(m_default); + m_isset_low_shelf = false; + m_low_shelf = d ? d->low_shelf() : true; + build_filter(0); +} + +void audio_effect_eq::reset_high_shelf() +{ + audio_effect_eq *d = static_cast<audio_effect_eq *>(m_default); + m_isset_high_shelf = false; + m_high_shelf = d ? d->high_shelf() : true; + build_filter(BANDS-1); +} + +void audio_effect_eq::config_load(util::xml::data_node const *ef_node) +{ + if(ef_node->has_attribute("mode")) { + m_mode = ef_node->get_attribute_int("mode", 0); + m_isset_mode = true; + } else + reset_mode(); + + if(ef_node->has_attribute("low_shelf")) { + m_low_shelf = ef_node->get_attribute_int("low_shelf", 0); + m_isset_low_shelf = true; + } else + reset_low_shelf(); + + if(ef_node->has_attribute("high_shelf")) { + m_high_shelf = ef_node->get_attribute_int("high_shelf", 0); + m_isset_high_shelf = true; + } else + reset_high_shelf(); + + for(u32 band = 0; band != BANDS; band++) { + if(ef_node->has_attribute(util::string_format("q%d", band+1).c_str())) { + m_q[band] = ef_node->get_attribute_float(util::string_format("q%d", band+1).c_str(), 0); + m_isset_q[band] = true; + } else + reset_q(band); + + if(ef_node->has_attribute(util::string_format("f%d", band+1).c_str())) { + m_f[band] = ef_node->get_attribute_float(util::string_format("f%d", band+1).c_str(), 0); + m_isset_f[band] = true; + } else + reset_f(band); + + if(ef_node->has_attribute(util::string_format("db%d", band+1).c_str())) { + m_db[band] = ef_node->get_attribute_float(util::string_format("db%d", band+1).c_str(), 0); + m_isset_db[band] = true; + } else + reset_db(band); + } +} + +void audio_effect_eq::config_save(util::xml::data_node *ef_node) const +{ + if(m_isset_mode) + ef_node->set_attribute_int("mode", m_mode); + if(m_isset_low_shelf) + ef_node->set_attribute_int("low_shelf", m_low_shelf); + if(m_isset_high_shelf) + ef_node->set_attribute_int("high_shelf", m_high_shelf); + for(u32 band = 0; band != BANDS; band++) { + if(m_isset_q[band]) + ef_node->set_attribute_float(util::string_format("q%d", band+1).c_str(), m_q[band]); + if(m_isset_f[band]) + ef_node->set_attribute_float(util::string_format("f%d", band+1).c_str(), m_f[band]); + if(m_isset_db[band]) + ef_node->set_attribute_float(util::string_format("db%d", band+1).c_str(), m_db[band]); + } +} + +void audio_effect_eq::default_changed() +{ + if(!m_default) + return; + if(!m_isset_mode) + reset_mode(); + if(!m_isset_low_shelf) + reset_low_shelf(); + if(!m_isset_high_shelf) + reset_high_shelf(); + for(u32 band = 0; band != BANDS; band++) { + if(!m_isset_q[band]) + reset_q(band); + if(!m_isset_f[band]) + reset_f(band); + if(!m_isset_db[band]) + reset_db(band); + } +} + +void audio_effect_eq::set_mode(u32 mode) +{ + m_isset_mode = true; + m_mode = mode; +} + +void audio_effect_eq::set_q(u32 band, float q) +{ + m_isset_q[band] = true; + m_q[band] = q; + build_filter(band); +} + +void audio_effect_eq::set_f(u32 band, float f) +{ + m_isset_f[band] = true; + m_f[band] = f; + build_filter(band); +} + +void audio_effect_eq::set_db(u32 band, float db) +{ + m_isset_db[band] = true; + m_db[band] = db; + build_filter(band); +} + +void audio_effect_eq::set_low_shelf(bool active) +{ + m_isset_low_shelf = true; + m_low_shelf = active; + build_filter(0); +} + +void audio_effect_eq::set_high_shelf(bool active) +{ + m_isset_high_shelf = true; + m_high_shelf = active; + build_filter(BANDS-1); +} + +void audio_effect_eq::build_filter(u32 band) +{ + if(band == 0 && m_low_shelf) { + build_low_shelf(band); + return; + } + if(band == BANDS-1 && m_high_shelf) { + build_high_shelf(band); + return; + } + build_peak(band); +} + +void audio_effect_eq::build_low_shelf(u32 band) +{ + auto &fi = m_filter[band]; + if(m_db[band] == 0) { + fi.clear(); + return; + } + + float V = pow(10, abs(m_db[band])/20); + float K = tan(M_PI*m_f[band]/m_sample_rate); + float K2 = K*K; + + if(m_db[band] > 0) { + float d = 1 + sqrt(2)*K + K2; + fi.m_b0 = (1 + sqrt(2*V)*K + V*K2)/d; + fi.m_b1 = 2*(V*K2-1)/d; + fi.m_b2 = (1 - sqrt(2*V)*K + V*K2)/d; + fi.m_a1 = 2*(K2-1)/d; + fi.m_a2 = (1 - sqrt(2)*K + K2)/d; + } else { + float d = 1 + sqrt(2*V)*K + V*K2; + fi.m_b0 = (1 + sqrt(2)*K + K2)/d; + fi.m_b1 = 2*(K2-1)/d; + fi.m_b2 = (1 - sqrt(2)*K + K2)/d; + fi.m_a1 = 2*(V*K2-1)/d; + fi.m_a2 = (1 - sqrt(2*V)*K + V*K2)/d; + } +} + +void audio_effect_eq::build_high_shelf(u32 band) +{ + auto &fi = m_filter[band]; + if(m_db[band] == 0) { + fi.clear(); + return; + } + + float V = pow(10, m_db[band]/20); + float K = tan(M_PI*m_f[band]/m_sample_rate); + float K2 = K*K; + + if(m_db[band] > 0) { + float d = 1 + sqrt(2)*K + K2; + fi.m_b0 = (V + sqrt(2*V)*K + K2)/d; + fi.m_b1 = 2*(K2-V)/d; + fi.m_b2 = (V - sqrt(2*V)*K + K2)/d; + fi.m_a1 = 2*(K2-1)/d; + fi.m_a2 = (1 - sqrt(2)*K + K2)/d; + } else { + float d = 1 + sqrt(2*V)*K + V*K2; + fi.m_b0 = V*(1 + sqrt(2)*K + K2)/d; + fi.m_b1 = 2*V*(K2-1)/d; + fi.m_b2 = V*(1 - sqrt(2)*K + K2)/d; + fi.m_a1 = 2*(V*K2-1)/d; + fi.m_a2 = (1 - sqrt(2*V)*K + V*K2)/d; + } +} + +void audio_effect_eq::build_peak(u32 band) +{ + auto &fi = m_filter[band]; + if(m_db[band] == 0) { + fi.clear(); + return; + } + + float V = pow(10, m_db[band]/20); + float K = tan(M_PI*m_f[band]/m_sample_rate); + float K2 = K*K; + float Q = m_q[band]; + + if(m_db[band] > 0) { + float d = 1 + K/Q + K2; + fi.m_b0 = (1 + V*K/Q + K2)/d; + fi.m_b1 = 2*(K2-1)/d; + fi.m_b2 = (1 - V*K/Q + K2)/d; + fi.m_a1 = fi.m_b1; + fi.m_a2 = (1 - K/Q + K2)/d; + } else { + float d = 1 + K/(V*Q) + K2; + fi.m_b0 = (1 + K/Q + K2)/d; + fi.m_b1 = 2*(K2-1)/d; + fi.m_b2 = (1 - K/Q + K2)/d; + fi.m_a1 = fi.m_b1; + fi.m_a2 = (1 - K/(V*Q) + K2)/d; + } +} + + +void audio_effect_eq::apply(const emu::detail::output_buffer_flat<sample_t> &src, emu::detail::output_buffer_flat<sample_t> &dest) +{ + if(m_mode == 0) { + copy(src, dest); + return; + } + + u32 samples = src.available_samples(); + dest.prepare_space(samples); + u32 channels = src.channels(); + if(m_history.empty()) + m_history.resize(channels); + + for(u32 channel = 0; channel != channels; channel++) { + const sample_t *srcd = src.ptrs(channel, 0); + sample_t *destd = dest.ptrw(channel, 0); + for(u32 sample = 0; sample != samples; sample++) { + m_history[channel][0].push(*srcd++); + for(u32 band = 0; band != BANDS; band++) + m_filter[band].apply(m_history[channel][band], m_history[channel][band+1]); + *destd++ = m_history[channel][BANDS].m_v0; + } + } + + dest.commit(samples); +} diff --git a/src/emu/audio_effects/eq.h b/src/emu/audio_effects/eq.h new file mode 100644 index 00000000000..919d5292837 --- /dev/null +++ b/src/emu/audio_effects/eq.h @@ -0,0 +1,84 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert + +#pragma once + +#ifndef MAME_EMU_AUDIO_EFFECTS_EQ_H +#define MAME_EMU_AUDIO_EFFECTS_EQ_H + +#include "aeffect.h" + +class audio_effect_eq : public audio_effect +{ +public: + enum { BANDS = 5 }; + + audio_effect_eq(u32 sample_rate, audio_effect *def); + virtual ~audio_effect_eq() = default; + + virtual int type() const override { return EQ; } + virtual void apply(const emu::detail::output_buffer_flat<sample_t> &src, emu::detail::output_buffer_flat<sample_t> &dest) override; + virtual void config_load(util::xml::data_node const *ef_node) override; + virtual void config_save(util::xml::data_node *ef_node) const override; + virtual void default_changed() override; + + void set_mode(u32 mode); + void set_q(u32 band, float q); + void set_f(u32 band, float f); + void set_db(u32 band, float db); + void set_low_shelf(bool active); + void set_high_shelf(bool active); + + u32 mode() const { return m_mode; } + float q(u32 band) const { return m_q[band]; } + float f(u32 band) const { return m_f[band]; } + float db(u32 band) const { return m_db[band]; } + bool low_shelf() const { return m_low_shelf; } + bool high_shelf() const { return m_high_shelf; } + + bool isset_mode() const { return m_isset_mode; } + bool isset_q(u32 band) const { return m_isset_q[band]; } + bool isset_f(u32 band) const { return m_isset_f[band]; } + bool isset_db(u32 band) const { return m_isset_db[band]; } + bool isset_low_shelf() const { return m_isset_low_shelf; } + bool isset_high_shelf() const { return m_isset_high_shelf; } + + void reset_mode(); + void reset_q(u32 band); + void reset_f(u32 band); + void reset_db(u32 band); + void reset_low_shelf(); + void reset_high_shelf(); + +private: + struct history { + float m_v0, m_v1, m_v2; + history() { m_v0 = m_v1 = m_v2 = 0; } + void push(float v) { m_v2 = m_v1; m_v1 = m_v0; m_v0 = v; } + }; + + struct filter { + float m_a1, m_a2, m_b0, m_b1, m_b2; + void clear() { m_a1 = 0; m_a2 = 0; m_b0 = 1; m_b1 = 0; m_b2 = 0; } + void apply(history &x, history &y) const { + y.push(m_b0 * x.m_v0 + m_b1 * x.m_v1 + m_b2 * x.m_v2 - m_a1 * y.m_v0 - m_a2 * y.m_v1); + } + }; + + u32 m_mode; + float m_q[BANDS], m_f[BANDS], m_db[BANDS]; + bool m_low_shelf, m_high_shelf; + std::array<filter, BANDS> m_filter; + std::vector<std::array<history, BANDS+1>> m_history; + + bool m_isset_mode, m_isset_low_shelf, m_isset_high_shelf; + bool m_isset_q[BANDS], m_isset_f[BANDS], m_isset_db[BANDS]; + + void build_filter(u32 band); + + void build_low_shelf(u32 band); + void build_high_shelf(u32 band); + void build_peak(u32 band); +}; + +#endif diff --git a/src/emu/audio_effects/filter.cpp b/src/emu/audio_effects/filter.cpp new file mode 100644 index 00000000000..70cf7ddc967 --- /dev/null +++ b/src/emu/audio_effects/filter.cpp @@ -0,0 +1,259 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert + +#include "emu.h" +#include "filter.h" +#include "xmlfile.h" + +// This effect implements a couple of very standard biquad filters, +// one lowpass and one highpass. + +// Formulas taken from: + +// [Zölzer 2011] "DAFX: Digital Audio Effects", Udo Zölzer, Second Edition, Wiley publishing, 2011 (Table 2.2) + + +audio_effect_filter::audio_effect_filter(u32 sample_rate, audio_effect *def) : audio_effect(sample_rate, def) +{ + // Minimal init to avoid using uninitialized values when reset_* + // recomputes filters + m_fl = m_fh = 1000; + m_ql = m_qh = 0.7; + + reset_lowpass_active(); + reset_highpass_active(); + reset_fl(); + reset_fh(); + reset_ql(); + reset_qh(); +} + +void audio_effect_filter::reset_lowpass_active() +{ + audio_effect_filter *d = static_cast<audio_effect_filter *>(m_default); + m_isset_lowpass_active = false; + m_lowpass_active = d ? d->lowpass_active() : false; + build_lowpass(); +} + +void audio_effect_filter::reset_highpass_active() +{ + audio_effect_filter *d = static_cast<audio_effect_filter *>(m_default); + m_isset_highpass_active = false; + m_highpass_active = d ? d->highpass_active() : true; + build_highpass(); +} + +void audio_effect_filter::reset_fl() +{ + audio_effect_filter *d = static_cast<audio_effect_filter *>(m_default); + m_isset_fl = false; + m_fl = d ? d->fl() : 8000; + build_lowpass(); +} + +void audio_effect_filter::reset_ql() +{ + audio_effect_filter *d = static_cast<audio_effect_filter *>(m_default); + m_isset_ql = false; + m_ql = d ? d->ql() : 0.7; + build_lowpass(); +} + +void audio_effect_filter::reset_fh() +{ + audio_effect_filter *d = static_cast<audio_effect_filter *>(m_default); + m_isset_fh = false; + m_fh = d ? d->fh() : 40; + build_highpass(); +} + +void audio_effect_filter::reset_qh() +{ + audio_effect_filter *d = static_cast<audio_effect_filter *>(m_default); + m_isset_qh = false; + m_qh = d ? d->qh() : 0.7; + build_highpass(); +} + +void audio_effect_filter::config_load(util::xml::data_node const *ef_node) +{ + if(ef_node->has_attribute("lowpass_active")) { + m_lowpass_active = ef_node->get_attribute_int("lowpass_active", 0); + m_isset_lowpass_active = true; + } else + reset_lowpass_active(); + + if(ef_node->has_attribute("fl")) { + m_fl = ef_node->get_attribute_float("fl", 0); + m_isset_fl = true; + } else + reset_fl(); + + if(ef_node->has_attribute("ql")) { + m_ql = ef_node->get_attribute_float("ql", 0); + m_isset_ql = true; + } else + reset_ql(); + + if(ef_node->has_attribute("highpass_active")) { + m_highpass_active = ef_node->get_attribute_int("highpass_active", 0); + m_isset_highpass_active = true; + } else + reset_highpass_active(); + + if(ef_node->has_attribute("fh")) { + m_fh = ef_node->get_attribute_float("fh", 0); + m_isset_fh = true; + } else + reset_fh(); + + if(ef_node->has_attribute("qh")) { + m_qh = ef_node->get_attribute_float("qh", 0); + m_isset_qh = true; + } else + reset_qh(); +} + +void audio_effect_filter::config_save(util::xml::data_node *ef_node) const +{ + if(m_isset_lowpass_active) + ef_node->set_attribute_int("lowpass_active", m_lowpass_active); + if(m_isset_fl) + ef_node->set_attribute_float("fl", m_fl); + if(m_isset_ql) + ef_node->set_attribute_float("ql", m_ql); + if(m_isset_highpass_active) + ef_node->set_attribute_int("highpass_active", m_highpass_active); + if(m_isset_fh) + ef_node->set_attribute_float("fh", m_fh); + if(m_isset_qh) + ef_node->set_attribute_float("qh", m_qh); +} + +void audio_effect_filter::default_changed() +{ + if(!m_isset_lowpass_active) + reset_lowpass_active(); + if(!m_isset_highpass_active) + reset_highpass_active(); + if(!m_isset_fl) + reset_fl(); + if(!m_isset_fh) + reset_fh(); + if(!m_isset_ql) + reset_ql(); + if(!m_isset_qh) + reset_qh(); +} + +void audio_effect_filter::apply(const emu::detail::output_buffer_flat<sample_t> &src, emu::detail::output_buffer_flat<sample_t> &dest) +{ + if(!m_lowpass_active && !m_highpass_active) { + copy(src, dest); + return; + } + + u32 samples = src.available_samples(); + dest.prepare_space(samples); + u32 channels = src.channels(); + if(m_history.empty()) + m_history.resize(channels); + + for(u32 channel = 0; channel != channels; channel++) { + const sample_t *srcd = src.ptrs(channel, 0); + sample_t *destd = dest.ptrw(channel, 0); + for(u32 sample = 0; sample != samples; sample++) { + m_history[channel][0].push(*srcd++); + m_filter[0].apply(m_history[channel][0], m_history[channel][1]); + m_filter[1].apply(m_history[channel][1], m_history[channel][2]); + *destd++ = m_history[channel][2].m_v0; + } + } + + dest.commit(samples); + +} + +void audio_effect_filter::set_lowpass_active(bool active) +{ + m_isset_lowpass_active = true; + m_lowpass_active = active; + build_lowpass(); +} + +void audio_effect_filter::set_highpass_active(bool active) +{ + m_isset_highpass_active = true; + m_highpass_active = active; + build_highpass(); +} + +void audio_effect_filter::set_fl(float f) +{ + m_isset_fl = true; + m_fl = f; + build_lowpass(); +} + +void audio_effect_filter::set_fh(float f) +{ + m_isset_fh = true; + m_fh = f; + build_highpass(); +} + +void audio_effect_filter::set_ql(float q) +{ + m_isset_ql = true; + m_ql = q; + build_lowpass(); +} + +void audio_effect_filter::set_qh(float q) +{ + m_isset_qh = true; + m_qh = q; + build_highpass(); +} + +void audio_effect_filter::build_highpass() +{ + auto &fi = m_filter[0]; + if(!m_highpass_active) { + fi.clear(); + return; + } + + float K = tan(M_PI*m_fh/m_sample_rate); + float K2 = K*K; + float Q = m_qh; + + float d = K2*Q + K + Q; + fi.m_b0 = Q/d; + fi.m_b1 = -2*Q/d; + fi.m_b2 = fi.m_b0; + fi.m_a1 = 2*Q*(K2-1)/d; + fi.m_a2 = (K2*Q - K + Q)/d; +} + +void audio_effect_filter::build_lowpass() +{ + auto &fi = m_filter[1]; + if(!m_lowpass_active) { + fi.clear(); + return; + } + + float K = tan(M_PI*m_fl/m_sample_rate); + float K2 = K*K; + float Q = m_ql; + + float d = K2*Q + K + Q; + fi.m_b0 = K2*Q/d; + fi.m_b1 = 2*K2*Q /d; + fi.m_b2 = fi.m_b0; + fi.m_a1 = 2*Q*(K2-1)/d; + fi.m_a2 = (K2*Q - K + Q)/d; +} + diff --git a/src/emu/audio_effects/filter.h b/src/emu/audio_effects/filter.h new file mode 100644 index 00000000000..13463bd6b4b --- /dev/null +++ b/src/emu/audio_effects/filter.h @@ -0,0 +1,78 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert + +#pragma once + +#ifndef MAME_EMU_AUDIO_EFFECTS_FILTER_H +#define MAME_EMU_AUDIO_EFFECTS_FILTER_H + +#include "aeffect.h" + +class audio_effect_filter : public audio_effect +{ +public: + audio_effect_filter(u32 sample_rate, audio_effect *def); + virtual ~audio_effect_filter() = default; + + virtual int type() const override { return FILTER; } + virtual void apply(const emu::detail::output_buffer_flat<sample_t> &src, emu::detail::output_buffer_flat<sample_t> &dest) override; + virtual void config_load(util::xml::data_node const *ef_node) override; + virtual void config_save(util::xml::data_node *ef_node) const override; + virtual void default_changed() override; + + void set_lowpass_active(bool active); + void set_highpass_active(bool active); + void set_fl(float f); + void set_fh(float f); + void set_ql(float q); + void set_qh(float q); + + bool lowpass_active() const { return m_lowpass_active; } + bool highpass_active() const { return m_highpass_active; } + float fl() const { return m_fl; } + float fh() const { return m_fh; } + float ql() const { return m_ql; } + float qh() const { return m_qh; } + + bool isset_lowpass_active() const { return m_isset_lowpass_active; } + bool isset_highpass_active() const { return m_isset_highpass_active; } + bool isset_fl() const { return m_isset_fl; } + bool isset_fh() const { return m_isset_fh; } + bool isset_ql() const { return m_isset_ql; } + bool isset_qh() const { return m_isset_qh; } + + void reset_lowpass_active(); + void reset_highpass_active(); + void reset_fl(); + void reset_fh(); + void reset_ql(); + void reset_qh(); + +private: + struct history { + float m_v0, m_v1, m_v2; + history() { m_v0 = m_v1 = m_v2 = 0; } + void push(float v) { m_v2 = m_v1; m_v1 = m_v0; m_v0 = v; } + }; + + struct filter { + float m_a1, m_a2, m_b0, m_b1, m_b2; + void clear() { m_a1 = 0; m_a2 = 0; m_b0 = 1; m_b1 = 0; m_b2 = 0; } + void apply(history &x, history &y) const { + y.push(m_b0 * x.m_v0 + m_b1 * x.m_v1 + m_b2 * x.m_v2 - m_a1 * y.m_v0 - m_a2 * y.m_v1); + } + }; + + bool m_isset_lowpass_active, m_isset_highpass_active; + bool m_isset_fl, m_isset_fh, m_isset_ql, m_isset_qh; + + bool m_lowpass_active, m_highpass_active; + float m_fl, m_fh, m_ql, m_qh; + std::array<filter, 2> m_filter; + std::vector<std::array<history, 3>> m_history; + + void build_lowpass(); + void build_highpass(); +}; + +#endif diff --git a/src/emu/audio_effects/reverb.cpp b/src/emu/audio_effects/reverb.cpp new file mode 100644 index 00000000000..c7f231a9d92 --- /dev/null +++ b/src/emu/audio_effects/reverb.cpp @@ -0,0 +1,28 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert + +#include "emu.h" +#include "reverb.h" +#include "xmlfile.h" + +audio_effect_reverb::audio_effect_reverb(u32 sample_rate, audio_effect *def) : audio_effect(sample_rate, def) +{ +} + + +void audio_effect_reverb::config_load(util::xml::data_node const *ef_node) +{ +} + +void audio_effect_reverb::config_save(util::xml::data_node *ef_node) const +{ +} + +void audio_effect_reverb::default_changed() +{ +} + +void audio_effect_reverb::apply(const emu::detail::output_buffer_flat<sample_t> &src, emu::detail::output_buffer_flat<sample_t> &dest) +{ + copy(src, dest); +} diff --git a/src/emu/audio_effects/reverb.h b/src/emu/audio_effects/reverb.h new file mode 100644 index 00000000000..36aaabb697b --- /dev/null +++ b/src/emu/audio_effects/reverb.h @@ -0,0 +1,24 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert + +#pragma once + +#ifndef MAME_EMU_AUDIO_EFFECTS_REVERB_H +#define MAME_EMU_AUDIO_EFFECTS_REVERB_H + +#include "aeffect.h" + +class audio_effect_reverb : public audio_effect +{ +public: + audio_effect_reverb(u32 sample_rate, audio_effect *def); + virtual ~audio_effect_reverb() = default; + + virtual int type() const override { return REVERB; } + virtual void apply(const emu::detail::output_buffer_flat<sample_t> &src, emu::detail::output_buffer_flat<sample_t> &dest) override; + virtual void config_load(util::xml::data_node const *ef_node) override; + virtual void config_save(util::xml::data_node *ef_node) const override; + virtual void default_changed() override; +}; + +#endif diff --git a/src/emu/bookkeeping.cpp b/src/emu/bookkeeping.cpp index 169d4d369ff..47e4b2a195a 100644 --- a/src/emu/bookkeeping.cpp +++ b/src/emu/bookkeeping.cpp @@ -2,7 +2,7 @@ // copyright-holders:Nicola Salmoria /********************************************************************* - bookkeeping.c + bookkeeping.cpp Bookkeeping simple machine functions. @@ -11,6 +11,8 @@ #include "emu.h" #include "config.h" +#include "xmlfile.h" + //************************************************************************** // BOOKKEEPING MANAGER @@ -20,11 +22,11 @@ // bookkeeping_manager - constructor //------------------------------------------------- -bookkeeping_manager::bookkeeping_manager(running_machine &machine) - : m_machine(machine), - m_dispensed_tickets(0) +bookkeeping_manager::bookkeeping_manager(running_machine &machine) : + m_machine(machine), + m_dispensed_tickets(0) { - /* reset coin counters */ + // reset coin counters for (int counternum = 0; counternum < COIN_COUNTERS; counternum++) { m_lastcoin[counternum] = 0; @@ -39,7 +41,9 @@ bookkeeping_manager::bookkeeping_manager(running_machine &machine) machine.save().save_item(NAME(m_dispensed_tickets)); // register for configuration - machine.configuration().config_register("counters", config_load_delegate(&bookkeeping_manager::config_load, this), config_save_delegate(&bookkeeping_manager::config_save, this)); + machine.configuration().config_register("counters", + configuration_manager::load_delegate(&bookkeeping_manager::config_load, this), + configuration_manager::save_delegate(&bookkeeping_manager::config_save, this)); } @@ -49,6 +53,16 @@ bookkeeping_manager::bookkeeping_manager(running_machine &machine) ***************************************************************************/ /*------------------------------------------------- + increment_dispensed_tickets - increment the + number of dispensed tickets +-------------------------------------------------*/ + +void bookkeeping_manager::increment_dispensed_tickets(int delta) +{ + m_dispensed_tickets += delta; +} + +/*------------------------------------------------- get_dispensed_tickets - return the number of tickets dispensed -------------------------------------------------*/ @@ -60,13 +74,13 @@ int bookkeeping_manager::get_dispensed_tickets() const /*------------------------------------------------- - increment_dispensed_tickets - increment the - number of dispensed tickets + reset_dispensed_tickets - reset the number of + tickets dispensed -------------------------------------------------*/ -void bookkeeping_manager::increment_dispensed_tickets(int delta) +void bookkeeping_manager::reset_dispensed_tickets() { - m_dispensed_tickets += delta; + m_dispensed_tickets = 0; } @@ -80,36 +94,30 @@ void bookkeeping_manager::increment_dispensed_tickets(int delta) and tickets -------------------------------------------------*/ -void bookkeeping_manager::config_load(config_type cfg_type, util::xml::data_node const *parentnode) +void bookkeeping_manager::config_load(config_type cfg_type, config_level cfg_level, util::xml::data_node const *parentnode) { - util::xml::data_node const *coinnode, *ticketnode; - - /* on init, reset the counters */ + // on init, reset the counters if (cfg_type == config_type::INIT) { memset(m_coin_count, 0, sizeof(m_coin_count)); m_dispensed_tickets = 0; } - /* only care about game-specific data */ - if (cfg_type != config_type::GAME) - return; - - /* might not have any data */ - if (parentnode == nullptr) + // only care about system-specific data + if ((cfg_type != config_type::SYSTEM) || !parentnode) return; - /* iterate over coins nodes */ - for (coinnode = parentnode->get_child("coins"); coinnode; coinnode = coinnode->get_next_sibling("coins")) + // iterate over coins nodes + for (util::xml::data_node const *coinnode = parentnode->get_child("coins"); coinnode; coinnode = coinnode->get_next_sibling("coins")) { int index = coinnode->get_attribute_int("index", -1); if (index >= 0 && index < COIN_COUNTERS) m_coin_count[index] = coinnode->get_attribute_int("number", 0); } - /* get the single tickets node */ - ticketnode = parentnode->get_child("tickets"); - if (ticketnode != nullptr) + // get the single tickets node + util::xml::data_node const *const ticketnode = parentnode->get_child("tickets"); + if (ticketnode) m_dispensed_tickets = ticketnode->get_attribute_int("number", 0); } @@ -121,19 +129,17 @@ void bookkeeping_manager::config_load(config_type cfg_type, util::xml::data_node void bookkeeping_manager::config_save(config_type cfg_type, util::xml::data_node *parentnode) { - int i; - - /* only care about game-specific data */ - if (cfg_type != config_type::GAME) + // only save system-specific data + if (cfg_type != config_type::SYSTEM) return; - /* iterate over coin counters */ - for (i = 0; i < COIN_COUNTERS; i++) + // iterate over coin counters + for (int i = 0; i < COIN_COUNTERS; i++) { if (m_coin_count[i] != 0) { - util::xml::data_node *coinnode = parentnode->add_child("coins", nullptr); - if (coinnode != nullptr) + util::xml::data_node *const coinnode = parentnode->add_child("coins", nullptr); + if (coinnode) { coinnode->set_attribute_int("index", i); coinnode->set_attribute_int("number", m_coin_count[i]); @@ -141,11 +147,11 @@ void bookkeeping_manager::config_save(config_type cfg_type, util::xml::data_node } } - /* output tickets */ + // output tickets if (m_dispensed_tickets != 0) { - util::xml::data_node *tickets = parentnode->add_child("tickets", nullptr); - if (tickets != nullptr) + util::xml::data_node *const tickets = parentnode->add_child("tickets", nullptr); + if (tickets) tickets->set_attribute_int("number", m_dispensed_tickets); } } @@ -157,11 +163,11 @@ void bookkeeping_manager::config_save(config_type cfg_type, util::xml::data_node void bookkeeping_manager::coin_counter_w(int num, int on) { - if (num >= ARRAY_LENGTH(m_coin_count)) + if (num >= std::size(m_coin_count)) return; - /* Count it only if the data has changed from 0 to non-zero */ - if (on && (m_lastcoin[num] == 0)) + // count it only if the data has changed from 0 to non-zero + if (machine().time() > attotime::zero && on && (m_lastcoin[num] == 0)) m_coin_count[num]++; m_lastcoin[num] = on; } @@ -174,19 +180,32 @@ void bookkeeping_manager::coin_counter_w(int num, int on) int bookkeeping_manager::coin_counter_get_count(int num) { - if (num >= ARRAY_LENGTH(m_coin_count)) + if (num >= std::size(m_coin_count)) return 0; return m_coin_count[num]; } /*------------------------------------------------- + coin_counter_reset_count - reset the coin count + for a given coin +-------------------------------------------------*/ + +void bookkeeping_manager::coin_counter_reset_count(int num) +{ + if (num >= std::size(m_coin_count)) + return; + m_coin_count[num] = 0; +} + + +/*------------------------------------------------- coin_lockout_w - locks out one coin input -------------------------------------------------*/ -void bookkeeping_manager::coin_lockout_w(int num,int on) +void bookkeeping_manager::coin_lockout_w(int num, int on) { - if (num >= ARRAY_LENGTH(m_coinlockedout)) + if (num >= std::size(m_coinlockedout)) return; m_coinlockedout[num] = on; } @@ -199,7 +218,7 @@ void bookkeeping_manager::coin_lockout_w(int num,int on) int bookkeeping_manager::coin_lockout_get_state(int num) { - if (num >= ARRAY_LENGTH(m_coinlockedout)) + if (num >= std::size(m_coinlockedout)) return false; return m_coinlockedout[num]; } @@ -212,6 +231,6 @@ int bookkeeping_manager::coin_lockout_get_state(int num) void bookkeeping_manager::coin_lockout_global_w(int on) { - for (int i = 0; i < ARRAY_LENGTH(m_coinlockedout); i++) + for (int i = 0; i < std::size(m_coinlockedout); i++) coin_lockout_w(i, on); } diff --git a/src/emu/bookkeeping.h b/src/emu/bookkeeping.h index 760789fd536..038d924f12f 100644 --- a/src/emu/bookkeeping.h +++ b/src/emu/bookkeeping.h @@ -32,11 +32,14 @@ public: bookkeeping_manager(running_machine &machine); // ----- tickets ----- + // increment the number of dispensed tickets + void increment_dispensed_tickets(int delta); + // return the number of tickets dispensed int get_dispensed_tickets() const; - // increment the number of dispensed tickets - void increment_dispensed_tickets(int delta); + // reset the number of dispensed tickets + void reset_dispensed_tickets(); // ----- coin counters ----- // write to a particular coin counter (clocks on active high edge) @@ -45,6 +48,9 @@ public: // return the coin count for a given coin int coin_counter_get_count(int num); + // reset the coin count for a given coin + void coin_counter_reset_count(int num); + // enable/disable coin lockout for a particular coin void coin_lockout_w(int num, int on); @@ -56,8 +62,9 @@ public: // getters running_machine &machine() const { return m_machine; } + private: - void config_load(config_type cfg_type, util::xml::data_node const *parentnode); + void config_load(config_type cfg_type, config_level cfg_level, util::xml::data_node const *parentnode); void config_save(config_type cfg_type, util::xml::data_node *parentnode); // internal state diff --git a/src/emu/config.cpp b/src/emu/config.cpp index 3574eb2d12e..8e9423415ee 100644 --- a/src/emu/config.cpp +++ b/src/emu/config.cpp @@ -2,33 +2,50 @@ // copyright-holders:Nicola Salmoria, Aaron Giles /*************************************************************************** - config.c + config.cpp Configuration file I/O. + ***************************************************************************/ #include "emu.h" -#include "emuopts.h" -#include "drivenum.h" #include "config.h" + +#include "drivenum.h" +#include "emuopts.h" +#include "fileio.h" + #include "xmlfile.h" +#include <system_error> +#include <utility> + #define DEBUG_CONFIG 0 + //************************************************************************** // CONFIGURATION MANAGER //************************************************************************** -//------------------------------------------------- -// configuration_manager - constructor -//------------------------------------------------- +/************************************* + * + * Construction/destruction + * + *************************************/ + +configuration_manager::configuration_manager(running_machine &machine) : + m_machine(machine), + m_typelist() +{ +} -configuration_manager::configuration_manager(running_machine &machine) - : m_machine(machine) +configuration_manager::~configuration_manager() { } + + /************************************* * * Register to be involved in config @@ -36,14 +53,9 @@ configuration_manager::configuration_manager(running_machine &machine) * *************************************/ -void configuration_manager::config_register(const char* nodename, config_load_delegate load, config_save_delegate save) +void configuration_manager::config_register(std::string_view name, load_delegate &&load, save_delegate &&save) { - config_element element; - element.name = nodename; - element.load = load; - element.save = save; - - m_typelist.push_back(element); + m_typelist.emplace(name, config_handler{ std::move(load), std::move(save) }); } @@ -54,74 +66,58 @@ void configuration_manager::config_register(const char* nodename, config_load_de * *************************************/ -int configuration_manager::load_settings() +bool configuration_manager::load_settings() { - const char *controller = machine().options().ctrlr(); - int loaded = 0; - - /* loop over all registrants and call their init function */ - for (auto type : m_typelist) - type.load(config_type::INIT, nullptr); + // loop over all registrants and call their init function + for (auto const &type : m_typelist) + type.second.load(config_type::INIT, config_level::DEFAULT, nullptr); - /* now load the controller file */ - if (controller[0] != 0) + // now load the controller file + char const *const controller = machine().options().ctrlr(); + if (controller && *controller) { - /* open the config file */ emu_file file(machine().options().ctrlr_path(), OPEN_FLAG_READ); - osd_printf_verbose("Attempting to parse: %s.cfg\n",controller); - osd_file::error filerr = file.open(controller, ".cfg"); - - if (filerr != osd_file::error::NONE) - throw emu_fatalerror("Could not load controller file %s.cfg", controller); - - /* load the XML */ - if (!load_xml(file, config_type::CONTROLLER)) - throw emu_fatalerror("Could not load controller file %s.cfg", controller); + if (!attempt_load(machine().system(), file, std::string(controller) + ".cfg", config_type::CONTROLLER)) + throw emu_fatalerror("Could not load controller configuration file %s.cfg", controller); } - /* next load the defaults file */ + // next load the defaults file emu_file file(machine().options().cfg_directory(), OPEN_FLAG_READ); - osd_file::error filerr = file.open("default.cfg"); - osd_printf_verbose("Attempting to parse: default.cfg\n"); - if (filerr == osd_file::error::NONE) - load_xml(file, config_type::DEFAULT); - - /* finally, load the game-specific file */ - filerr = file.open(machine().basename(), ".cfg"); - osd_printf_verbose("Attempting to parse: %s.cfg\n",machine().basename()); - if (filerr == osd_file::error::NONE) - loaded = load_xml(file, config_type::GAME); - - /* loop over all registrants and call their final function */ - for (auto type : m_typelist) - type.load(config_type::FINAL, nullptr); - - /* if we didn't find a saved config, return 0 so the main core knows that it */ - /* is the first time the game is run and it should display the disclaimer. */ + attempt_load(machine().system(), file, "default.cfg", config_type::DEFAULT); + + // load the system-specific file + bool const loaded = attempt_load(machine().system(), file, machine().basename() + ".cfg", config_type::SYSTEM); + + // loop over all registrants and call their final function + for (auto const &type : m_typelist) + type.second.load(config_type::FINAL, config_level::DEFAULT, nullptr); + + // if we didn't find a saved config, return false so the main core knows that it + // is the first time the system has been run return loaded; } void configuration_manager::save_settings() { - /* loop over all registrants and call their init function */ - for (auto type : m_typelist) - type.save(config_type::INIT, nullptr); + // loop over all registrants and call their init function + for (auto const &type : m_typelist) + type.second.save(config_type::INIT, nullptr); - /* save the defaults file */ + // save the defaults file emu_file file(machine().options().cfg_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); - osd_file::error filerr = file.open("default.cfg"); - if (filerr == osd_file::error::NONE) + std::error_condition filerr = file.open("default.cfg"); + if (!filerr) save_xml(file, config_type::DEFAULT); - /* finally, save the game-specific file */ - filerr = file.open(machine().basename(), ".cfg"); - if (filerr == osd_file::error::NONE) - save_xml(file, config_type::GAME); + // finally, save the system-specific file + filerr = file.open(machine().basename() + ".cfg"); + if (!filerr) + save_xml(file, config_type::SYSTEM); - /* loop over all registrants and call their final function */ - for (auto type : m_typelist) - type.save(config_type::FINAL, nullptr); + // loop over all registrants and call their final function + for (auto const &type : m_typelist) + type.second.save(config_type::FINAL, nullptr); } @@ -132,66 +128,130 @@ void configuration_manager::save_settings() * *************************************/ -int configuration_manager::load_xml(emu_file &file, config_type which_type) +bool configuration_manager::attempt_load(game_driver const &system, emu_file &file, std::string_view name, config_type which_type) +{ + std::error_condition const filerr = file.open(std::string(name)); + if (std::errc::no_such_file_or_directory == filerr) + { + osd_printf_verbose("Configuration file %s not found\n", name); + return false; + } + else if (filerr) + { + osd_printf_warning( + "Error opening configuration file %s (%s:%d %s)\n", + name, + filerr.category().name(), + filerr.value(), + filerr.message()); + return false; + } + else + { + osd_printf_verbose("Attempting to parse: %s\n", name); + return load_xml(system, file, which_type); + } +} + + +bool configuration_manager::load_xml(game_driver const &system, emu_file &file, config_type which_type) { - /* read the file */ + // read the file util::xml::file::ptr const root(util::xml::file::read(file, nullptr)); if (!root) - return 0; + { + osd_printf_warning("Error parsing XML configuration file %s\n", file.filename()); + return false; + } - /* find the config node */ + // find the config node util::xml::data_node const *const confignode = root->get_child("mameconfig"); if (!confignode) - return 0; + { + osd_printf_warning("Could not find root mameconfig element in configuration file %s\n", file.filename()); + return false; + } - /* validate the config data version */ + // validate the config data version int const version = confignode->get_attribute_int("version", 0); if (version != CONFIG_VERSION) - return 0; + { + osd_printf_warning("Configuration file %s has unsupported version %d\n", file.filename(), version); + return false; + } - /* strip off all the path crap from the source filename */ - const char *srcfile = strrchr(machine().system().type.source(), '/'); + // strip off all the path crap from the source filename + char const *srcfile = strrchr(system.type.source(), '/'); if (!srcfile) - srcfile = strrchr(machine().system().type.source(), '\\'); + srcfile = strrchr(system.type.source(), '\\'); if (!srcfile) - srcfile = strrchr(machine().system().type.source(), ':'); + srcfile = strrchr(system.type.source(), ':'); if (!srcfile) - srcfile = machine().system().type.source(); + srcfile = system.type.source(); else srcfile++; - /* loop over all system nodes in the file */ + // loop over all system nodes in the file int count = 0; for (util::xml::data_node const *systemnode = confignode->get_child("system"); systemnode; systemnode = systemnode->get_next_sibling("system")) { - /* look up the name of the system here; skip if none */ - const char *name = systemnode->get_attribute_string("name", ""); + // look up the name of the system here; skip if none + char const *name = systemnode->get_attribute_string("name", ""); - /* based on the file type, determine whether we have a match */ + // based on the file type, determine whether we have a match + config_level level = config_level::DEFAULT; switch (which_type) { - case config_type::GAME: - /* only match on the specific game name */ - if (strcmp(name, machine().system().name) != 0) + case config_type::SYSTEM: + // only match on the specific system name + if (strcmp(name, system.name)) + { + osd_printf_verbose("Ignoring configuration for system %s in system configuration file %s\n", name, file.filename()); continue; + } + level = config_level::SYSTEM; break; case config_type::DEFAULT: - /* only match on default */ - if (strcmp(name, "default") != 0) + // only match on default + if (strcmp(name, "default")) + { + osd_printf_verbose("Ignoring configuration for system %s in default configuration file %s\n", name, file.filename()); continue; + } + level = config_level::DEFAULT; break; case config_type::CONTROLLER: { + // match on: default, system name, source file name, parent name, grandparent name int clone_of; - /* match on: default, game name, source file name, parent name, grandparent name */ - if (strcmp(name, "default") != 0 && - strcmp(name, machine().system().name) != 0 && - strcmp(name, srcfile) != 0 && - ((clone_of = driver_list::clone(machine().system())) == -1 || strcmp(name, driver_list::driver(clone_of).name) != 0) && - (clone_of == -1 || ((clone_of = driver_list::clone(clone_of)) == -1) || strcmp(name, driver_list::driver(clone_of).name) != 0)) + if (!strcmp(name, "default")) + { + osd_printf_verbose("Applying default configuration from controller configuration file %s\n", file.filename()); + level = config_level::DEFAULT; + } + else if (!strcmp(name, system.name)) + { + osd_printf_verbose("Applying configuration for system %s from controller configuration file %s\n", name, file.filename()); + level = config_level::SYSTEM; + } + else if (!strcmp(name, srcfile)) + { + osd_printf_verbose("Applying configuration for source file %s from controller configuration file %s\n", name, file.filename()); + level = config_level::SOURCE; + } + else if ( + ((clone_of = driver_list::clone(system)) != -1 && !strcmp(name, driver_list::driver(clone_of).name)) || + (clone_of != -1 && ((clone_of = driver_list::clone(clone_of)) != -1) && !strcmp(name, driver_list::driver(clone_of).name))) + { + osd_printf_verbose("Applying configuration for parent/BIOS %s from controller configuration file %s\n", name, file.filename()); + level = (driver_list::driver(clone_of).flags & MACHINE_IS_BIOS_ROOT) ? config_level::BIOS : config_level::PARENT; + } + else + { continue; + } break; } @@ -199,21 +259,24 @@ int configuration_manager::load_xml(emu_file &file, config_type which_type) break; } - /* log that we are processing this entry */ + // log that we are processing this entry if (DEBUG_CONFIG) osd_printf_debug("Entry: %s -- processing\n", name); - /* loop over all registrants and call their load function */ - for (auto type : m_typelist) - type.load(which_type, systemnode->get_child(type.name.c_str())); + // loop over all registrants and call their load function + for (auto const &type : m_typelist) + type.second.load(which_type, level, systemnode->get_child(type.first.c_str())); count++; - } - /* error if this isn't a valid game match */ - if (count == 0) - return 0; + // save unhandled settings for default and system types + if (config_type::DEFAULT == which_type) + save_unhandled(m_unhandled_default, *systemnode); + else if (config_type::SYSTEM == which_type) + save_unhandled(m_unhandled_system, *systemnode); + } - return 1; + // error if this isn't a valid match + return count != 0; } @@ -224,43 +287,85 @@ int configuration_manager::load_xml(emu_file &file, config_type which_type) * *************************************/ -int configuration_manager::save_xml(emu_file &file, config_type which_type) +bool configuration_manager::save_xml(emu_file &file, config_type which_type) { + // if we cant't create a root node, bail util::xml::file::ptr root(util::xml::file::create()); - - /* if we don't have a root, bail */ if (!root) - return 0; + return false; - /* create a config node */ + // create a config node util::xml::data_node *const confignode = root->add_child("mameconfig", nullptr); if (!confignode) - return 0; + return false; confignode->set_attribute_int("version", CONFIG_VERSION); - /* create a system node */ + // create a system node util::xml::data_node *const systemnode = confignode->add_child("system", nullptr); if (!systemnode) - return 0; + return false; systemnode->set_attribute("name", (which_type == config_type::DEFAULT) ? "default" : machine().system().name); - /* create the input node and write it out */ - /* loop over all registrants and call their save function */ - for (auto type : m_typelist) + // loop over all registrants and call their save function + util::xml::data_node *curnode = nullptr; + for (auto const &type : m_typelist) { - util::xml::data_node *const curnode = systemnode->add_child(type.name.c_str(), nullptr); + if (!curnode || (type.first != curnode->get_name())) + curnode = systemnode->add_child(type.first.c_str(), nullptr); if (!curnode) - return 0; - type.save(which_type, curnode); + return false; + type.second.save(which_type, curnode); - /* if nothing was added, just nuke the node */ - if (!curnode->get_value() && !curnode->get_first_child()) + // if nothing was added, just nuke the node + if (!curnode->get_value() && !curnode->get_first_child() && !curnode->count_attributes()) + { curnode->delete_node(); + curnode = nullptr; + } } - /* flush the file */ + // restore unhandled settings + if ((config_type::DEFAULT == which_type) && m_unhandled_default) + restore_unhandled(*m_unhandled_default, *systemnode); + else if ((config_type::SYSTEM == which_type) && m_unhandled_system) + restore_unhandled(*m_unhandled_system, *systemnode); + + // write out the file root->write(file); - /* free and get out of here */ - return 1; + // free and get out of here + return true; +} + + + +/************************************* + * + * Preserving unhandled settings + * + *************************************/ + +void configuration_manager::save_unhandled( + std::unique_ptr<util::xml::file> &unhandled, + util::xml::data_node const &systemnode) +{ + for (util::xml::data_node const *curnode = systemnode.get_first_child(); curnode; curnode = curnode->get_next_sibling()) + { + auto const handler = m_typelist.lower_bound(curnode->get_name()); + if ((m_typelist.end() == handler) || (handler->first != curnode->get_name())) + { + if (!unhandled) + unhandled = util::xml::file::create(); + curnode->copy_into(*unhandled); + } + } +} + + +void configuration_manager::restore_unhandled( + util::xml::file const &unhandled, + util::xml::data_node &systemnode) +{ + for (util::xml::data_node const *curnode = unhandled.get_first_child(); curnode; curnode = curnode->get_next_sibling()) + curnode->copy_into(systemnode); } diff --git a/src/emu/config.h b/src/emu/config.h index e9f528a17cc..4d3dd4f3b75 100644 --- a/src/emu/config.h +++ b/src/emu/config.h @@ -5,6 +5,7 @@ config.h Wrappers for handling MAME configuration files + ***************************************************************************/ #ifndef MAME_EMU_CONFIG_H @@ -12,62 +13,70 @@ #pragma once -#include "xmlfile.h" - -/************************************* - * - * Constants - * - *************************************/ +#include <map> +#include <memory> +#include <string> +#include <string_view> -#define CONFIG_VERSION 10 -enum class config_type +enum class config_type : int { - INIT = 0, // opportunity to initialize things first + INIT, // opportunity to initialize things first CONTROLLER, // loading from controller file DEFAULT, // loading from default.cfg - GAME, // loading from game.cfg + SYSTEM, // loading from system.cfg FINAL // opportunity to finish initialization }; -/************************************* - * - * Type definitions - * - *************************************/ - -typedef delegate<void (config_type, util::xml::data_node const *)> config_load_delegate; -typedef delegate<void (config_type, util::xml::data_node *)> config_save_delegate; +enum class config_level : int +{ + DEFAULT, + SOURCE, + BIOS, + PARENT, + SYSTEM +}; -// ======================> configuration_manager class configuration_manager { - struct config_element - { - std::string name; // node name - config_load_delegate load; // load callback - config_save_delegate save; // save callback - }; - public: + typedef delegate<void (config_type, config_level, util::xml::data_node const *)> load_delegate; + typedef delegate<void (config_type, util::xml::data_node *)> save_delegate; + + static inline constexpr int CONFIG_VERSION = 10; + // construction/destruction configuration_manager(running_machine &machine); + ~configuration_manager(); - void config_register(const char* nodename, config_load_delegate load, config_save_delegate save); - int load_settings(); + void config_register(std::string_view name, load_delegate &&load, save_delegate &&save); + + bool load_settings(); void save_settings(); - // getters - running_machine &machine() const { return m_machine; } private: - int load_xml(emu_file &file, config_type which_type); - int save_xml(emu_file &file, config_type which_type); + struct config_handler + { + load_delegate load; + save_delegate save; + }; + + running_machine &machine() const { return m_machine; } + + bool attempt_load(game_driver const &system, emu_file &file, std::string_view name, config_type which_type); + + bool load_xml(game_driver const &system, emu_file &file, config_type which_type); + bool save_xml(emu_file &file, config_type which_type); + + void save_unhandled(std::unique_ptr<util::xml::file> &unhandled, util::xml::data_node const &systemnode); + void restore_unhandled(util::xml::file const &unhandled, util::xml::data_node &systemnode); // internal state - running_machine & m_machine; // reference to our machine - std::vector<config_element> m_typelist; + running_machine &m_machine; + std::multimap<std::string, config_handler> m_typelist; + std::unique_ptr<util::xml::file> m_unhandled_default; + std::unique_ptr<util::xml::file> m_unhandled_system; }; -#endif /* MAME_EMU_CONFIG_H */ +#endif // MAME_EMU_CONFIG_H diff --git a/src/emu/crsshair.cpp b/src/emu/crsshair.cpp index 11bde091d15..d90cc9a7f5e 100644 --- a/src/emu/crsshair.cpp +++ b/src/emu/crsshair.cpp @@ -2,17 +2,23 @@ // copyright-holders:Aaron Giles /*************************************************************************** - crsshair.c + crsshair.cpp Crosshair handling. + ***************************************************************************/ #include "emu.h" +#include "crsshair.h" + +#include "config.h" #include "emuopts.h" +#include "fileio.h" +#include "render.h" #include "rendutil.h" -#include "config.h" +#include "screen.h" + #include "xmlfile.h" -#include "crsshair.h" @@ -118,7 +124,7 @@ render_crosshair::render_crosshair(running_machine &machine, int player) , m_time(0) { // for now, use the main screen - m_screen = screen_device_iterator(machine.root_device()).first(); + m_screen = screen_device_enumerator(machine.root_device()).first(); } @@ -167,32 +173,45 @@ void render_crosshair::set_default_bitmap() void render_crosshair::create_bitmap() { - int x, y; - rgb_t color = m_player < ARRAY_LENGTH(crosshair_colors) ? crosshair_colors[m_player] : rgb_t::white(); + rgb_t color = m_player < std::size(crosshair_colors) ? crosshair_colors[m_player] : rgb_t::white(); // if we have a bitmap and texture for this player, kill it - if (m_bitmap == nullptr) + if (!m_bitmap) { m_bitmap = std::make_unique<bitmap_argb32>(); m_texture = m_machine.render().texture_alloc(render_texture::hq_scale); } + else + { + m_bitmap->reset(); + } emu_file crossfile(m_machine.options().crosshair_path(), OPEN_FLAG_READ); if (!m_name.empty()) { // look for user specified file - std::string filename = m_name + ".png"; - render_load_png(*m_bitmap, crossfile, nullptr, filename.c_str()); + if (!crossfile.open(m_name + ".png")) + { + render_load_png(*m_bitmap, crossfile); + crossfile.close(); + } } else { // look for default cross?.png in crsshair/game dir - std::string filename = string_format("cross%d.png", m_player + 1); - render_load_png(*m_bitmap, crossfile, m_machine.system().name, filename.c_str()); + std::string const filename = string_format("cross%d.png", m_player + 1); + if (!crossfile.open(m_machine.system().name + (PATH_SEPARATOR + filename))) + { + render_load_png(*m_bitmap, crossfile); + crossfile.close(); + } // look for default cross?.png in crsshair dir - if (!m_bitmap->valid()) - render_load_png(*m_bitmap, crossfile, nullptr, filename.c_str()); + if (!m_bitmap->valid() && !crossfile.open(filename)) + { + render_load_png(*m_bitmap, crossfile); + crossfile.close(); + } } /* if that didn't work, use the built-in one */ @@ -203,14 +222,14 @@ void render_crosshair::create_bitmap() m_bitmap->fill(rgb_t(0x00,0xff,0xff,0xff)); /* extract the raw source data to it */ - for (y = 0; y < CROSSHAIR_RAW_SIZE / 2; y++) + for (int y = 0; y < CROSSHAIR_RAW_SIZE / 2; y++) { /* assume it is mirrored vertically */ - u32 *dest0 = &m_bitmap->pix32(y); - u32 *dest1 = &m_bitmap->pix32(CROSSHAIR_RAW_SIZE - 1 - y); + u32 *const dest0 = &m_bitmap->pix(y); + u32 *const dest1 = &m_bitmap->pix(CROSSHAIR_RAW_SIZE - 1 - y); /* extract to two rows simultaneously */ - for (x = 0; x < CROSSHAIR_RAW_SIZE; x++) + for (int x = 0; x < CROSSHAIR_RAW_SIZE; x++) if ((crosshair_raw_top[y * CROSSHAIR_RAW_ROWBYTES + x / 8] << (x % 8)) & 0x80) dest0[x] = dest1[x] = rgb_t(0xff,0x00,0x00,0x00) | color; } @@ -222,6 +241,50 @@ void render_crosshair::create_bitmap() //------------------------------------------------- +// update_position - update the crosshair values +// for the given player +//------------------------------------------------- + +void render_crosshair::update_position() +{ + // read all the lightgun values + bool gotx = false, goty = false; + for (auto const &port : m_machine.ioport().ports()) + for (ioport_field const &field : port.second->fields()) + if (field.player() == m_player && field.crosshair_axis() != CROSSHAIR_AXIS_NONE && field.enabled()) + { + // handle X axis + if (field.crosshair_axis() == CROSSHAIR_AXIS_X) + { + m_x = field.crosshair_read(); + gotx = true; + if (field.crosshair_altaxis() != 0) + { + m_y = field.crosshair_altaxis(); + goty = true; + } + } + + // handle Y axis + else + { + m_y = field.crosshair_read(); + goty = true; + if (field.crosshair_altaxis() != 0) + { + m_x = field.crosshair_altaxis(); + gotx = true; + } + } + + // if we got both, stop + if (gotx && goty) + return; + } +} + + +//------------------------------------------------- // animate - update the crosshair state //------------------------------------------------- @@ -229,7 +292,7 @@ void render_crosshair::animate(u16 auto_time) { // read all the port values if (m_used) - m_machine.ioport().crosshair_position(m_player, m_x, m_y); + update_position(); // auto visibility if (m_mode == CROSSHAIR_VISIBILITY_AUTO) @@ -283,6 +346,7 @@ void render_crosshair::draw(render_container &container, u8 fade) crosshair_manager::crosshair_manager(running_machine &machine) : m_machine(machine) , m_usage(false) + , m_fade(0) , m_animation_counter(0) , m_auto_time(CROSSHAIR_VISIBILITY_AUTOTIME_DEFAULT) { @@ -294,7 +358,7 @@ crosshair_manager::crosshair_manager(running_machine &machine) /* determine who needs crosshairs */ for (auto &port : machine.ioport().ports()) - for (ioport_field &field : port.second->fields()) + for (ioport_field const &field : port.second->fields()) if (field.crosshair_axis() != CROSSHAIR_AXIS_NONE) { int player = field.player(); @@ -311,11 +375,15 @@ crosshair_manager::crosshair_manager(running_machine &machine) /* register callbacks for when we load/save configurations */ if (m_usage) - machine.configuration().config_register("crosshairs", config_load_delegate(&crosshair_manager::config_load, this), config_save_delegate(&crosshair_manager::config_save, this)); + { + machine.configuration().config_register("crosshairs", + configuration_manager::load_delegate(&crosshair_manager::config_load, this), + configuration_manager::save_delegate(&crosshair_manager::config_save, this)); + } /* register the animation callback */ - screen_device *first_screen = screen_device_iterator(machine.root_device()).first(); - if (first_screen != nullptr) + screen_device *first_screen = screen_device_enumerator(machine.root_device()).first(); + if (first_screen) first_screen->register_vblank_callback(vblank_state_delegate(&crosshair_manager::animate, this)); } @@ -382,19 +450,15 @@ void crosshair_manager::render(screen_device &screen) configuration file -------------------------------------------------*/ -void crosshair_manager::config_load(config_type cfg_type, util::xml::data_node const *parentnode) +void crosshair_manager::config_load(config_type cfg_type, config_level cfg_level, util::xml::data_node const *parentnode) { - /* Note: crosshair_load() is only registered if croshairs are used */ - - /* we only care about game files */ - if (cfg_type != config_type::GAME) - return; + // Note: crosshair_load() is only registered if croshairs are used - /* might not have any data */ - if (parentnode == nullptr) + // we only care about system-specific configuration + if ((cfg_type != config_type::SYSTEM) || !parentnode) return; - /* loop and get player crosshair info */ + // loop and get player crosshair info for (util::xml::data_node const *crosshairnode = parentnode->get_child("crosshair"); crosshairnode; crosshairnode = crosshairnode->get_next_sibling("crosshair")) { int const player = crosshairnode->get_attribute_int("player", -1); @@ -411,8 +475,7 @@ void crosshair_manager::config_load(config_type cfg_type, util::xml::data_node c if (mode >= CROSSHAIR_VISIBILITY_OFF && mode <= CROSSHAIR_VISIBILITY_AUTO) { crosshair.set_mode(u8(mode)); - /* set visibility as specified by mode */ - /* auto mode starts with visibility off */ + // set visibility as specified by mode - auto mode starts with visibility off crosshair.set_visible(mode == CROSSHAIR_VISIBILITY_ON); } @@ -422,7 +485,7 @@ void crosshair_manager::config_load(config_type cfg_type, util::xml::data_node c } } - /* get, check, and store auto visibility time */ + // get, check, and store auto visibility time util::xml::data_node const *crosshairnode = parentnode->get_child("autotime"); if (crosshairnode) { @@ -440,10 +503,10 @@ void crosshair_manager::config_load(config_type cfg_type, util::xml::data_node c void crosshair_manager::config_save(config_type cfg_type, util::xml::data_node *parentnode) { - /* Note: crosshair_save() is only registered if crosshairs are used */ + // Note: crosshair_save() is only registered if crosshairs are used - /* we only care about game files */ - if (cfg_type != config_type::GAME) + // we only create system-specific configuration + if (cfg_type != config_type::SYSTEM) return; for (int player = 0; player < MAX_PLAYERS; player++) @@ -452,7 +515,7 @@ void crosshair_manager::config_save(config_type cfg_type, util::xml::data_node * if (crosshair.is_used()) { - /* create a node */ + // create a node util::xml::data_node *const crosshairnode = parentnode->add_child("crosshair", nullptr); if (crosshairnode != nullptr) @@ -474,20 +537,19 @@ void crosshair_manager::config_save(config_type cfg_type, util::xml::data_node * changed = true; } - /* if nothing changed, kill the node */ + // if nothing changed, kill the node if (!changed) crosshairnode->delete_node(); } } } - /* always store autotime so that it stays at the user value if it is needed */ + // always store autotime so that it stays at the user value if it is needed if (m_auto_time != CROSSHAIR_VISIBILITY_AUTOTIME_DEFAULT) { - /* create a node */ + // create a node util::xml::data_node *const crosshairnode = parentnode->add_child("autotime", nullptr); - - if (crosshairnode != nullptr) + if (crosshairnode) crosshairnode->set_attribute_int("val", m_auto_time); } diff --git a/src/emu/crsshair.h b/src/emu/crsshair.h index c733f0afccf..dd927ec3637 100644 --- a/src/emu/crsshair.h +++ b/src/emu/crsshair.h @@ -5,6 +5,7 @@ crsshair.h Crosshair handling. + ***************************************************************************/ #ifndef MAME_EMU_CRSSHAIR_H @@ -73,6 +74,7 @@ public: private: // private helpers void create_bitmap(); + void update_position(); // private state running_machine & m_machine; // reference to our machine @@ -116,7 +118,7 @@ private: void exit(); void animate(screen_device &device, bool vblank_state); - void config_load(config_type cfg_type, util::xml::data_node const *parentnode); + void config_load(config_type cfg_type, config_level cfg_lvl, util::xml::data_node const *parentnode); void config_save(config_type cfg_type, util::xml::data_node *parentnode); // internal state diff --git a/src/emu/debug/debugbuf.cpp b/src/emu/debug/debugbuf.cpp index 4e232ce16a0..616567b398e 100644 --- a/src/emu/debug/debugbuf.cpp +++ b/src/emu/debug/debugbuf.cpp @@ -82,6 +82,8 @@ void debug_disasm_buffer::debug_data_buffer::fill(offs_t lstart, offs_t size) co } } + // FIXME: This buffer tends to hog more memory than necessary for typical disassembly tasks. + // If the PC values supplied are far enough apart, the buffer may suddenly increase in size to a gigabyte or more. if(m_buffer.empty()) { m_lstart = lstart; m_lend = lend; @@ -179,6 +181,7 @@ void debug_disasm_buffer::debug_data_buffer::setup_methods() int shift = space->addr_shift(); int alignment = m_intf.opcode_alignment(); endianness_t endian = space->endianness(); + bool is_octal = space->is_octal(); m_pc_mask = space->logaddrmask(); @@ -208,8 +211,9 @@ void debug_disasm_buffer::debug_data_buffer::setup_methods() u16 *dest = get_ptr<u16>(lstart); for(offs_t lpc = lstart; lpc != lend; lpc = (lpc + 1) & m_pc_mask) { offs_t tpc = m_intf.pc_linear_to_real(lpc); - if (m_space->device().memory().translate(m_space->spacenum(), TRANSLATE_FETCH_DEBUG, tpc)) - *dest++ = m_space->read_word(tpc); + address_space *space; + if (m_space->device().memory().translate(m_space->spacenum(), device_memory_interface::TR_FETCH, tpc, space)) + *dest++ = space->read_word(tpc); else *dest++ = 0; } @@ -219,14 +223,13 @@ void debug_disasm_buffer::debug_data_buffer::setup_methods() m_do_fill = [this](offs_t lstart, offs_t lend) { auto dis = m_space->device().machine().disable_side_effects(); u8 *dest = get_ptr<u8>(lstart); - u32 steps = 0; for(offs_t lpc = lstart; lpc != lend; lpc = (lpc + 1) & m_pc_mask) { offs_t tpc = m_intf.pc_linear_to_real(lpc); - if (m_space->device().memory().translate(m_space->spacenum(), TRANSLATE_FETCH_DEBUG, tpc)) - *dest++ = m_space->read_byte(tpc); + address_space *space; + if (m_space->device().memory().translate(m_space->spacenum(), device_memory_interface::TR_FETCH, tpc, space)) + *dest++ = space->read_byte(tpc); else *dest++ = 0; - steps++; } }; break; @@ -240,8 +243,9 @@ void debug_disasm_buffer::debug_data_buffer::setup_methods() u64 *dest = get_ptr<u64>(lstart); for(offs_t lpc = lstart; lpc != lend; lpc = (lpc + 1) & m_pc_mask) { offs_t tpc = lpc; - if (m_space->device().memory().translate(m_space->spacenum(), TRANSLATE_FETCH_DEBUG, tpc)) - *dest++ = m_space->read_qword(tpc); + address_space *space; + if (m_space->device().memory().translate(m_space->spacenum(), device_memory_interface::TR_FETCH, tpc, space)) + *dest++ = space->read_qword(tpc); else *dest++ = 0; } @@ -254,8 +258,9 @@ void debug_disasm_buffer::debug_data_buffer::setup_methods() u32 *dest = get_ptr<u32>(lstart); for(offs_t lpc = lstart; lpc != lend; lpc = (lpc + 1) & m_pc_mask) { offs_t tpc = lpc; - if (m_space->device().memory().translate(m_space->spacenum(), TRANSLATE_FETCH_DEBUG, tpc)) - *dest++ = m_space->read_dword(tpc); + address_space *space; + if (m_space->device().memory().translate(m_space->spacenum(), device_memory_interface::TR_FETCH, tpc, space)) + *dest++ = space->read_dword(tpc); else *dest++ = 0; } @@ -268,8 +273,9 @@ void debug_disasm_buffer::debug_data_buffer::setup_methods() u16 *dest = get_ptr<u16>(lstart); for(offs_t lpc = lstart; lpc != lend; lpc = (lpc + 1) & m_pc_mask) { offs_t tpc = lpc; - if (m_space->device().memory().translate(m_space->spacenum(), TRANSLATE_FETCH_DEBUG, tpc)) - *dest++ = m_space->read_word(tpc); + address_space *space; + if (m_space->device().memory().translate(m_space->spacenum(), device_memory_interface::TR_FETCH, tpc, space)) + *dest++ = space->read_word(tpc); else *dest++ = 0; } @@ -282,8 +288,9 @@ void debug_disasm_buffer::debug_data_buffer::setup_methods() u8 *dest = get_ptr<u8>(lstart); for(offs_t lpc = lstart; lpc != lend; lpc = (lpc + 1) & m_pc_mask) { offs_t tpc = lpc; - if (m_space->device().memory().translate(m_space->spacenum(), TRANSLATE_FETCH_DEBUG, tpc)) - *dest++ = m_space->read_byte(tpc); + address_space *space; + if (m_space->device().memory().translate(m_space->spacenum(), device_memory_interface::TR_FETCH, tpc, space)) + *dest++ = space->read_byte(tpc); else *dest++ = 0; } @@ -296,8 +303,9 @@ void debug_disasm_buffer::debug_data_buffer::setup_methods() u16 *dest = reinterpret_cast<u16 *>(&m_buffer[0]) + ((lstart - m_lstart) >> 4); for(offs_t lpc = lstart; lpc != lend; lpc = (lpc + 0x10) & m_pc_mask) { offs_t tpc = lpc; - if (m_space->device().memory().translate(m_space->spacenum(), TRANSLATE_FETCH_DEBUG, tpc)) - *dest++ = m_space->read_word(tpc); + address_space *space; + if (m_space->device().memory().translate(m_space->spacenum(), device_memory_interface::TR_FETCH, tpc, space)) + *dest++ = space->read_word(tpc); else *dest++ = 0; } @@ -954,44 +962,80 @@ void debug_disasm_buffer::debug_data_buffer::setup_methods() // Define the data -> string conversion switch(shift) { case -3: - m_data_to_string = [this](offs_t pc, offs_t size) { - std::ostringstream out; - for(offs_t i=0; i != size; i++) { - if(i) - out << ' '; - util::stream_format(out, "%016X", r64(pc)); - pc = m_next_pc_wrap(pc, 1); - } - return out.str(); - }; - break; - - case -2: - switch(alignment) { - case 1: + if(is_octal) m_data_to_string = [this](offs_t pc, offs_t size) { std::ostringstream out; for(offs_t i=0; i != size; i++) { if(i) out << ' '; - util::stream_format(out, "%08X", r32(pc)); + util::stream_format(out, "%022o", r64(pc)); pc = m_next_pc_wrap(pc, 1); } return out.str(); }; - break; - - case 2: + else m_data_to_string = [this](offs_t pc, offs_t size) { std::ostringstream out; - for(offs_t i=0; i != size; i += 2) { + for(offs_t i=0; i != size; i++) { if(i) out << ' '; util::stream_format(out, "%016X", r64(pc)); - pc = m_next_pc_wrap(pc, 2); + pc = m_next_pc_wrap(pc, 1); } return out.str(); }; + break; + + case -2: + switch(alignment) { + case 1: + if(is_octal) + m_data_to_string = [this](offs_t pc, offs_t size) { + std::ostringstream out; + for(offs_t i=0; i != size; i++) { + if(i) + out << ' '; + util::stream_format(out, "%011o", r32(pc)); + pc = m_next_pc_wrap(pc, 1); + } + return out.str(); + }; + else + m_data_to_string = [this](offs_t pc, offs_t size) { + std::ostringstream out; + for(offs_t i=0; i != size; i++) { + if(i) + out << ' '; + util::stream_format(out, "%08X", r32(pc)); + pc = m_next_pc_wrap(pc, 1); + } + return out.str(); + }; + break; + + case 2: + if(is_octal) + m_data_to_string = [this](offs_t pc, offs_t size) { + std::ostringstream out; + for(offs_t i=0; i != size; i += 2) { + if(i) + out << ' '; + util::stream_format(out, "%022o", r64(pc)); + pc = m_next_pc_wrap(pc, 2); + } + return out.str(); + }; + else + m_data_to_string = [this](offs_t pc, offs_t size) { + std::ostringstream out; + for(offs_t i=0; i != size; i += 2) { + if(i) + out << ' '; + util::stream_format(out, "%016X", r64(pc)); + pc = m_next_pc_wrap(pc, 2); + } + return out.str(); + }; break; } break; @@ -999,42 +1043,78 @@ void debug_disasm_buffer::debug_data_buffer::setup_methods() case -1: switch(alignment) { case 1: - m_data_to_string = [this](offs_t pc, offs_t size) { - std::ostringstream out; - for(offs_t i=0; i != size; i++) { - if(i) - out << ' '; - util::stream_format(out, "%04X", r16(pc)); - pc = m_next_pc_wrap(pc, 1); - } - return out.str(); - }; + if(is_octal) + m_data_to_string = [this](offs_t pc, offs_t size) { + std::ostringstream out; + for(offs_t i=0; i != size; i++) { + if(i) + out << ' '; + util::stream_format(out, "%06o", r16(pc)); + pc = m_next_pc_wrap(pc, 1); + } + return out.str(); + }; + else + m_data_to_string = [this](offs_t pc, offs_t size) { + std::ostringstream out; + for(offs_t i=0; i != size; i++) { + if(i) + out << ' '; + util::stream_format(out, "%04X", r16(pc)); + pc = m_next_pc_wrap(pc, 1); + } + return out.str(); + }; break; case 2: - m_data_to_string = [this](offs_t pc, offs_t size) { - std::ostringstream out; - for(offs_t i=0; i != size; i += 2) { - if(i) + if(is_octal) + m_data_to_string = [this](offs_t pc, offs_t size) { + std::ostringstream out; + for(offs_t i=0; i != size; i += 2) { + if(i) + out << ' '; + util::stream_format(out, "%011o", r32(pc)); + pc = m_next_pc_wrap(pc, 2); + } + return out.str(); + }; + else + m_data_to_string = [this](offs_t pc, offs_t size) { + std::ostringstream out; + for(offs_t i=0; i != size; i += 2) { + if(i) out << ' '; - util::stream_format(out, "%08X", r32(pc)); - pc = m_next_pc_wrap(pc, 2); - } - return out.str(); - }; + util::stream_format(out, "%08X", r32(pc)); + pc = m_next_pc_wrap(pc, 2); + } + return out.str(); + }; break; case 4: - m_data_to_string = [this](offs_t pc, offs_t size) { - std::ostringstream out; - for(offs_t i=0; i != size; i += 4) { - if(i) - out << ' '; - util::stream_format(out, "%016X", r64(pc)); - pc = m_next_pc_wrap(pc, 4); - } - return out.str(); - }; + if(is_octal) + m_data_to_string = [this](offs_t pc, offs_t size) { + std::ostringstream out; + for(offs_t i=0; i != size; i += 4) { + if(i) + out << ' '; + util::stream_format(out, "%022o", r64(pc)); + pc = m_next_pc_wrap(pc, 4); + } + return out.str(); + }; + else + m_data_to_string = [this](offs_t pc, offs_t size) { + std::ostringstream out; + for(offs_t i=0; i != size; i += 4) { + if(i) + out << ' '; + util::stream_format(out, "%016X", r64(pc)); + pc = m_next_pc_wrap(pc, 4); + } + return out.str(); + }; break; } break; @@ -1042,70 +1122,130 @@ void debug_disasm_buffer::debug_data_buffer::setup_methods() case 0: switch(alignment) { case 1: - m_data_to_string = [this](offs_t pc, offs_t size) { - std::ostringstream out; - for(offs_t i=0; i != size; i++) { - if(i) - out << ' '; - util::stream_format(out, "%02X", r8(pc)); - pc = m_next_pc_wrap(pc, 1); - } - return out.str(); - }; + if(is_octal) + m_data_to_string = [this](offs_t pc, offs_t size) { + std::ostringstream out; + for(offs_t i=0; i != size; i++) { + if(i) + out << ' '; + util::stream_format(out, "%03o", r8(pc)); + pc = m_next_pc_wrap(pc, 1); + } + return out.str(); + }; + else + m_data_to_string = [this](offs_t pc, offs_t size) { + std::ostringstream out; + for(offs_t i=0; i != size; i++) { + if(i) + out << ' '; + util::stream_format(out, "%02X", r8(pc)); + pc = m_next_pc_wrap(pc, 1); + } + return out.str(); + }; break; case 2: - m_data_to_string = [this](offs_t pc, offs_t size) { - std::ostringstream out; - for(offs_t i=0; i != size; i += 2) { - if(i) - out << ' '; - util::stream_format(out, "%04X", r16(pc)); - pc = m_next_pc_wrap(pc, 2); - } - return out.str(); - }; + if(is_octal) + m_data_to_string = [this](offs_t pc, offs_t size) { + std::ostringstream out; + for(offs_t i=0; i != size; i += 2) { + if(i) + out << ' '; + util::stream_format(out, "%06o", r16(pc)); + pc = m_next_pc_wrap(pc, 2); + } + return out.str(); + }; + else + m_data_to_string = [this](offs_t pc, offs_t size) { + std::ostringstream out; + for(offs_t i=0; i != size; i += 2) { + if(i) + out << ' '; + util::stream_format(out, "%04X", r16(pc)); + pc = m_next_pc_wrap(pc, 2); + } + return out.str(); + }; break; case 4: + if(is_octal) + m_data_to_string = [this](offs_t pc, offs_t size) { + std::ostringstream out; + for(offs_t i=0; i != size; i += 4) { + if(i) + out << ' '; + util::stream_format(out, "%011o", r32(pc)); + pc = m_next_pc_wrap(pc, 4); + } + return out.str(); + }; + else + m_data_to_string = [this](offs_t pc, offs_t size) { + std::ostringstream out; + for(offs_t i=0; i != size; i += 4) { + if(i) + out << ' '; + util::stream_format(out, "%08X", r32(pc)); + pc = m_next_pc_wrap(pc, 4); + } + return out.str(); + }; + break; + + case 8: + if(is_octal) + m_data_to_string = [this](offs_t pc, offs_t size) { + std::ostringstream out; + for(offs_t i=0; i != size; i += 8) { + if(i) + out << ' '; + util::stream_format(out, "%022o", r64(pc)); + pc = m_next_pc_wrap(pc, 8); + } + return out.str(); + }; + else + m_data_to_string = [this](offs_t pc, offs_t size) { + std::ostringstream out; + for(offs_t i=0; i != size; i += 8) { + if(i) + out << ' '; + util::stream_format(out, "%016X", r64(pc)); + pc = m_next_pc_wrap(pc, 8); + } + return out.str(); + }; + break; + } + break; + + case 3: + if(is_octal) m_data_to_string = [this](offs_t pc, offs_t size) { std::ostringstream out; - for(offs_t i=0; i != size; i += 4) { + for(offs_t i=0; i != size; i += 16) { if(i) out << ' '; - util::stream_format(out, "%08X", r32(pc)); - pc = m_next_pc_wrap(pc, 4); + util::stream_format(out, "%06o", r16(pc)); + pc = m_next_pc_wrap(pc, 16); } return out.str(); }; - break; - - case 8: + else m_data_to_string = [this](offs_t pc, offs_t size) { std::ostringstream out; - for(offs_t i=0; i != size; i += 8) { + for(offs_t i=0; i != size; i += 16) { if(i) out << ' '; - util::stream_format(out, "%016X", r64(pc)); - pc = m_next_pc_wrap(pc, 8); + util::stream_format(out, "%04X", r16(pc)); + pc = m_next_pc_wrap(pc, 16); } return out.str(); }; - break; - } - break; - - case 3: - m_data_to_string = [this](offs_t pc, offs_t size) { - std::ostringstream out; - for(offs_t i=0; i != size; i += 16) { - if(i) - out << ' '; - util::stream_format(out, "%04X", r16(pc)); - pc = m_next_pc_wrap(pc, 16); - } - return out.str(); - }; break; } @@ -1257,7 +1397,7 @@ debug_disasm_buffer::debug_disasm_buffer(device_t &device) : // pc to string conversion int aw = pspace.logaddr_width(); bool is_octal = pspace.is_octal(); - if((m_flags & util::disasm_interface::PAGED2LEVEL) == util::disasm_interface::PAGED2LEVEL) { + if((m_flags & util::disasm_interface::PAGED2LEVEL) == util::disasm_interface::PAGED2LEVEL && aw > (m_dintf.page_address_bits() + m_dintf.page2_address_bits())) { int bits1 = m_dintf.page_address_bits(); int bits2 = m_dintf.page2_address_bits(); int bits3 = aw - bits1 - bits2; @@ -1288,7 +1428,7 @@ debug_disasm_buffer::debug_disasm_buffer(device_t &device) : }; } - } else if(m_flags & util::disasm_interface::PAGED) { + } else if((m_flags & util::disasm_interface::PAGED) && aw > m_dintf.page_address_bits()) { int bits1 = m_dintf.page_address_bits(); int bits2 = aw - bits1; offs_t sm1 = (1 << bits1) - 1; diff --git a/src/emu/debug/debugcmd.cpp b/src/emu/debug/debugcmd.cpp index cd9d9dcf743..0b7efa44a93 100644 --- a/src/emu/debug/debugcmd.cpp +++ b/src/emu/debug/debugcmd.cpp @@ -2,27 +2,37 @@ // copyright-holders:Aaron Giles /********************************************************************* - debugcmd.c + debugcmd.cpp Debugger command interface engine. *********************************************************************/ #include "emu.h" -#include "emuopts.h" -#include "debugger.h" #include "debugcmd.h" + +#include "debugbuf.h" #include "debugcon.h" #include "debugcpu.h" -#include "debugbuf.h" -#include "express.h" #include "debughlp.h" #include "debugvw.h" +#include "express.h" +#include "points.h" + +#include "debugger.h" +#include "emuopts.h" +#include "fileio.h" #include "natkeyboard.h" #include "render.h" -#include <ctype.h> +#include "screen.h" +#include "softlist.h" + +#include "corestr.h" + #include <algorithm> +#include <cctype> #include <fstream> +#include <sstream> @@ -43,7 +53,8 @@ const size_t debugger_commands::MAX_GLOBALS = 1000; bool debugger_commands::cheat_address_is_valid(address_space &space, offs_t address) { - return space.device().memory().translate(space.spacenum(), TRANSLATE_READ, address) && (space.get_write_ptr(address) != nullptr); + address_space *tspace; + return space.device().memory().translate(space.spacenum(), device_memory_interface::TR_READ, address, tspace) && (tspace->get_write_ptr(address) != nullptr); } @@ -52,15 +63,15 @@ bool debugger_commands::cheat_address_is_valid(address_space &space, offs_t addr the current cheat width, if signed -------------------------------------------------*/ -u64 debugger_commands::cheat_sign_extend(const cheat_system *cheatsys, u64 value) +inline u64 debugger_commands::cheat_system::sign_extend(u64 value) const { - if (cheatsys->signed_cheat) + if (signed_cheat) { - switch (cheatsys->width) + switch (width) { - case 1: value = s8(value); break; - case 2: value = s16(value); break; - case 4: value = s32(value); break; + case 1: value = s8(value); break; + case 2: value = s16(value); break; + case 4: value = s32(value); break; } } return value; @@ -70,16 +81,15 @@ u64 debugger_commands::cheat_sign_extend(const cheat_system *cheatsys, u64 value cheat_byte_swap - swap a value -------------------------------------------------*/ -u64 debugger_commands::cheat_byte_swap(const cheat_system *cheatsys, u64 value) +inline u64 debugger_commands::cheat_system::byte_swap(u64 value) const { - if (cheatsys->swapped_cheat) + if (swapped_cheat) { - switch (cheatsys->width) + switch (width) { - case 2: value = ((value >> 8) & 0x00ff) | ((value << 8) & 0xff00); break; - case 4: value = ((value >> 24) & 0x000000ff) | ((value >> 8) & 0x0000ff00) | ((value << 8) & 0x00ff0000) | ((value << 24) & 0xff000000); break; - case 8: value = ((value >> 56) & 0x00000000000000ffU) | ((value >> 40) & 0x000000000000ff00U) | ((value >> 24) & 0x0000000000ff0000U) | ((value >> 8) & 0x00000000ff000000U) | - ((value << 8) & 0x000000ff00000000U) | ((value << 24) & 0x0000ff0000000000U) | ((value << 40) & 0x00ff000000000000U) | ((value << 56) & 0xff00000000000000U); break; + case 2: value = swapendian_int16(value); break; + case 4: value = swapendian_int32(value); break; + case 8: value = swapendian_int64(value); break; } } return value; @@ -91,247 +101,296 @@ u64 debugger_commands::cheat_byte_swap(const cheat_system *cheatsys, u64 value) and swapping if necessary -------------------------------------------------*/ -u64 debugger_commands::cheat_read_extended(const cheat_system *cheatsys, address_space &space, offs_t address) +u64 debugger_commands::cheat_system::read_extended(offs_t address) const { - return cheat_sign_extend(cheatsys, cheat_byte_swap(cheatsys, m_cpu.read_memory(space, address, cheatsys->width, true))); + address &= space->logaddrmask(); + u64 value = space->unmap(); + address_space *tspace; + if (space->device().memory().translate(space->spacenum(), device_memory_interface::TR_READ, address, tspace)) + { + switch (width) + { + case 1: value = tspace->read_byte(address); break; + case 2: value = tspace->read_word_unaligned(address); break; + case 4: value = tspace->read_dword_unaligned(address); break; + case 8: value = tspace->read_qword_unaligned(address); break; + } + } + return sign_extend(byte_swap(value)); } debugger_commands::debugger_commands(running_machine& machine, debugger_cpu& cpu, debugger_console& console) : m_machine(machine) - , m_cpu(cpu) , m_console(console) { - m_global_array = std::make_unique<global_entry []>(MAX_GLOBALS); - - symbol_table *symtable = m_cpu.get_global_symtable(); - - /* add a few simple global functions */ using namespace std::placeholders; - symtable->add("min", 2, 2, std::bind(&debugger_commands::execute_min, this, _1, _2, _3)); - symtable->add("max", 2, 2, std::bind(&debugger_commands::execute_max, this, _1, _2, _3)); - symtable->add("if", 3, 3, std::bind(&debugger_commands::execute_if, this, _1, _2, _3)); + m_global_array = std::make_unique<global_entry []>(MAX_GLOBALS); - /* add all single-entry save state globals */ + symbol_table &symtable = cpu.global_symtable(); + + // add a few simple global functions + symtable.add("min", 2, 2, // lower of two values + [] (int params, const u64 *param) -> u64 + { return (std::min)(param[0], param[1]); }); + symtable.add("max", 2, 2, // higher of two values + [] (int params, const u64 *param) -> u64 + { return (std::max)(param[0], param[1]); }); + symtable.add("if", 3, 3, // a ? b : c + [] (int params, const u64 *param) -> u64 + { return param[0] ? param[1] : param[2]; }); + symtable.add("abs", 1, 1, // absolute value of signed number + [] (int params, const u64 *param) -> u64 + { return std::abs(s64(param[0])); }); + symtable.add("bit", 2, 3, // extract bit field + [] (int params, const u64 *param) -> u64 + { return (params == 2) ? BIT(param[0], param[1]) : BIT(param[0], param[1], param[2]); }); + symtable.add("s8", 1, 1, // sign-extend from 8 bits + [] (int params, const u64 *param) -> u64 + { return s64(s8(u8(param[0]))); }); + symtable.add("s16", 1, 1, // sign-extend from 16 bits + [] (int params, const u64 *param) -> u64 + { return s64(s16(u16(param[0]))); }); + symtable.add("s32", 1, 1, // sign-extend from 32 bits + [] (int params, const u64 *param) -> u64 + { return s64(s32(u32(param[0]))); }); + symtable.add("cpunum", std::bind(&debugger_commands::get_cpunum, this)); + + // add all single-entry save state globals for (int itemnum = 0; itemnum < MAX_GLOBALS; itemnum++) { - u32 valsize, valcount; void *base; + u32 valsize, valcount, blockcount, stride; - /* stop when we run out of items */ - const char* name = m_machine.save().indexed_item(itemnum, base, valsize, valcount); - if (name == nullptr) + // stop when we run out of items + const char* name = m_machine.save().indexed_item(itemnum, base, valsize, valcount, blockcount, stride); + if (!name) break; - /* if this is a single-entry global, add it */ - if (valcount == 1 && strstr(name, "/globals/")) + // if this is a single-entry global, add it + if ((valcount == 1) && (blockcount == 1) && strstr(name, "/globals/")) { char symname[100]; - sprintf(symname, ".%s", strrchr(name, '/') + 1); + snprintf(symname, 100, ".%s", strrchr(name, '/') + 1); m_global_array[itemnum].base = base; m_global_array[itemnum].size = valsize; - symtable->add( + symtable.add( symname, - std::bind(&debugger_commands::global_get, this, _1, &m_global_array[itemnum]), - std::bind(&debugger_commands::global_set, this, _1, &m_global_array[itemnum], _2)); - } - } - - /* add all the commands */ - m_console.register_command("help", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_help, this, _1, _2)); - m_console.register_command("print", CMDFLAG_NONE, 0, 1, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_print, this, _1, _2)); - m_console.register_command("printf", CMDFLAG_NONE, 0, 1, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_printf, this, _1, _2)); - m_console.register_command("logerror", CMDFLAG_NONE, 0, 1, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_logerror, this, _1, _2)); - m_console.register_command("tracelog", CMDFLAG_NONE, 0, 1, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_tracelog, this, _1, _2)); - m_console.register_command("tracesym", CMDFLAG_NONE, 0, 1, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_tracesym, this, _1, _2)); - m_console.register_command("quit", CMDFLAG_NONE, 0, 0, 0, std::bind(&debugger_commands::execute_quit, this, _1, _2)); - m_console.register_command("exit", CMDFLAG_NONE, 0, 0, 0, std::bind(&debugger_commands::execute_quit, this, _1, _2)); - m_console.register_command("do", CMDFLAG_NONE, 0, 1, 1, std::bind(&debugger_commands::execute_do, this, _1, _2)); - m_console.register_command("step", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_step, this, _1, _2)); - m_console.register_command("s", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_step, this, _1, _2)); - m_console.register_command("over", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_over, this, _1, _2)); - m_console.register_command("o", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_over, this, _1, _2)); - m_console.register_command("out" , CMDFLAG_NONE, 0, 0, 0, std::bind(&debugger_commands::execute_out, this, _1, _2)); - m_console.register_command("go", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_go, this, _1, _2)); - m_console.register_command("g", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_go, this, _1, _2)); - m_console.register_command("gvblank", CMDFLAG_NONE, 0, 0, 0, std::bind(&debugger_commands::execute_go_vblank, this, _1, _2)); - m_console.register_command("gv", CMDFLAG_NONE, 0, 0, 0, std::bind(&debugger_commands::execute_go_vblank, this, _1, _2)); - m_console.register_command("gint", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_go_interrupt, this, _1, _2)); - m_console.register_command("gi", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_go_interrupt, this, _1, _2)); - m_console.register_command("gex", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_go_exception, this, _1, _2)); - m_console.register_command("ge", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_go_exception, this, _1, _2)); - m_console.register_command("gtime", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_go_time, this, _1, _2)); - m_console.register_command("gt", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_go_time, this, _1, _2)); - m_console.register_command("gp", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_go_privilege, this, _1, _2)); - m_console.register_command("next", CMDFLAG_NONE, 0, 0, 0, std::bind(&debugger_commands::execute_next, this, _1, _2)); - m_console.register_command("n", CMDFLAG_NONE, 0, 0, 0, std::bind(&debugger_commands::execute_next, this, _1, _2)); - m_console.register_command("focus", CMDFLAG_NONE, 0, 1, 1, std::bind(&debugger_commands::execute_focus, this, _1, _2)); - m_console.register_command("ignore", CMDFLAG_NONE, 0, 0, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_ignore, this, _1, _2)); - m_console.register_command("observe", CMDFLAG_NONE, 0, 0, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_observe, this, _1, _2)); - m_console.register_command("suspend", CMDFLAG_NONE, 0, 0, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_suspend, this, _1, _2)); - m_console.register_command("resume", CMDFLAG_NONE, 0, 0, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_resume, this, _1, _2)); - - m_console.register_command("comadd", CMDFLAG_NONE, 0, 1, 2, std::bind(&debugger_commands::execute_comment_add, this, _1, _2)); - m_console.register_command("//", CMDFLAG_NONE, 0, 1, 2, std::bind(&debugger_commands::execute_comment_add, this, _1, _2)); - m_console.register_command("comdelete", CMDFLAG_NONE, 0, 1, 1, std::bind(&debugger_commands::execute_comment_del, this, _1, _2)); - m_console.register_command("comsave", CMDFLAG_NONE, 0, 0, 0, std::bind(&debugger_commands::execute_comment_save, this, _1, _2)); - m_console.register_command("comlist", CMDFLAG_NONE, 0, 0, 0, std::bind(&debugger_commands::execute_comment_list, this, _1, _2)); - m_console.register_command("commit", CMDFLAG_NONE, 0, 1, 2, std::bind(&debugger_commands::execute_comment_commit, this, _1, _2)); - m_console.register_command("/*", CMDFLAG_NONE, 0, 1, 2, std::bind(&debugger_commands::execute_comment_commit, this, _1, _2)); - - m_console.register_command("bpset", CMDFLAG_NONE, 0, 1, 3, std::bind(&debugger_commands::execute_bpset, this, _1, _2)); - m_console.register_command("bp", CMDFLAG_NONE, 0, 1, 3, std::bind(&debugger_commands::execute_bpset, this, _1, _2)); - m_console.register_command("bpclear", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_bpclear, this, _1, _2)); - m_console.register_command("bpdisable", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_bpdisenable, this, _1, _2)); - m_console.register_command("bpenable", CMDFLAG_NONE, 1, 0, 1, std::bind(&debugger_commands::execute_bpdisenable, this, _1, _2)); - m_console.register_command("bplist", CMDFLAG_NONE, 0, 0, 0, std::bind(&debugger_commands::execute_bplist, this, _1, _2)); - - m_console.register_command("wpset", CMDFLAG_NONE, AS_PROGRAM, 3, 5, std::bind(&debugger_commands::execute_wpset, this, _1, _2)); - m_console.register_command("wp", CMDFLAG_NONE, AS_PROGRAM, 3, 5, std::bind(&debugger_commands::execute_wpset, this, _1, _2)); - m_console.register_command("wpdset", CMDFLAG_NONE, AS_DATA, 3, 5, std::bind(&debugger_commands::execute_wpset, this, _1, _2)); - m_console.register_command("wpd", CMDFLAG_NONE, AS_DATA, 3, 5, std::bind(&debugger_commands::execute_wpset, this, _1, _2)); - m_console.register_command("wpiset", CMDFLAG_NONE, AS_IO, 3, 5, std::bind(&debugger_commands::execute_wpset, this, _1, _2)); - m_console.register_command("wpi", CMDFLAG_NONE, AS_IO, 3, 5, std::bind(&debugger_commands::execute_wpset, this, _1, _2)); - m_console.register_command("wposet", CMDFLAG_NONE, AS_OPCODES, 3, 5, std::bind(&debugger_commands::execute_wpset, this, _1, _2)); - m_console.register_command("wpo", CMDFLAG_NONE, AS_OPCODES, 3, 5, std::bind(&debugger_commands::execute_wpset, this, _1, _2)); - m_console.register_command("wpclear", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_wpclear, this, _1, _2)); - m_console.register_command("wpdisable", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_wpdisenable, this, _1, _2)); - m_console.register_command("wpenable", CMDFLAG_NONE, 1, 0, 1, std::bind(&debugger_commands::execute_wpdisenable, this, _1, _2)); - m_console.register_command("wplist", CMDFLAG_NONE, 0, 0, 0, std::bind(&debugger_commands::execute_wplist, this, _1, _2)); - - m_console.register_command("rpset", CMDFLAG_NONE, 0, 1, 2, std::bind(&debugger_commands::execute_rpset, this, _1, _2)); - m_console.register_command("rp", CMDFLAG_NONE, 0, 1, 2, std::bind(&debugger_commands::execute_rpset, this, _1, _2)); - m_console.register_command("rpclear", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_rpclear, this, _1, _2)); - m_console.register_command("rpdisable", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_rpdisenable, this, _1, _2)); - m_console.register_command("rpenable", CMDFLAG_NONE, 1, 0, 1, std::bind(&debugger_commands::execute_rpdisenable, this, _1, _2)); - m_console.register_command("rplist", CMDFLAG_NONE, 0, 0, 0, std::bind(&debugger_commands::execute_rplist, this, _1, _2)); - - m_console.register_command("hotspot", CMDFLAG_NONE, 0, 0, 3, std::bind(&debugger_commands::execute_hotspot, this, _1, _2)); - - m_console.register_command("statesave", CMDFLAG_NONE, 0, 1, 1, std::bind(&debugger_commands::execute_statesave, this, _1, _2)); - m_console.register_command("ss", CMDFLAG_NONE, 0, 1, 1, std::bind(&debugger_commands::execute_statesave, this, _1, _2)); - m_console.register_command("stateload", CMDFLAG_NONE, 0, 1, 1, std::bind(&debugger_commands::execute_stateload, this, _1, _2)); - m_console.register_command("sl", CMDFLAG_NONE, 0, 1, 1, std::bind(&debugger_commands::execute_stateload, this, _1, _2)); - - m_console.register_command("rewind", CMDFLAG_NONE, 0, 0, 0, std::bind(&debugger_commands::execute_rewind, this, _1, _2)); - m_console.register_command("rw", CMDFLAG_NONE, 0, 0, 0, std::bind(&debugger_commands::execute_rewind, this, _1, _2)); - - m_console.register_command("save", CMDFLAG_NONE, AS_PROGRAM, 3, 4, std::bind(&debugger_commands::execute_save, this, _1, _2)); - m_console.register_command("saved", CMDFLAG_NONE, AS_DATA, 3, 4, std::bind(&debugger_commands::execute_save, this, _1, _2)); - m_console.register_command("savei", CMDFLAG_NONE, AS_IO, 3, 4, std::bind(&debugger_commands::execute_save, this, _1, _2)); - m_console.register_command("saveo", CMDFLAG_NONE, AS_OPCODES, 3, 4, std::bind(&debugger_commands::execute_save, this, _1, _2)); - - m_console.register_command("load", CMDFLAG_NONE, AS_PROGRAM, 2, 4, std::bind(&debugger_commands::execute_load, this, _1, _2)); - m_console.register_command("loadd", CMDFLAG_NONE, AS_DATA, 2, 4, std::bind(&debugger_commands::execute_load, this, _1, _2)); - m_console.register_command("loadi", CMDFLAG_NONE, AS_IO, 2, 4, std::bind(&debugger_commands::execute_load, this, _1, _2)); - m_console.register_command("loado", CMDFLAG_NONE, AS_OPCODES, 2, 4, std::bind(&debugger_commands::execute_load, this, _1, _2)); - - m_console.register_command("dump", CMDFLAG_NONE, AS_PROGRAM, 3, 7, std::bind(&debugger_commands::execute_dump, this, _1, _2)); - m_console.register_command("dumpd", CMDFLAG_NONE, AS_DATA, 3, 7, std::bind(&debugger_commands::execute_dump, this, _1, _2)); - m_console.register_command("dumpi", CMDFLAG_NONE, AS_IO, 3, 7, std::bind(&debugger_commands::execute_dump, this, _1, _2)); - m_console.register_command("dumpo", CMDFLAG_NONE, AS_OPCODES, 3, 7, std::bind(&debugger_commands::execute_dump, this, _1, _2)); - - m_console.register_command("cheatinit", CMDFLAG_NONE, 0, 0, 4, std::bind(&debugger_commands::execute_cheatinit, this, _1, _2)); - m_console.register_command("ci", CMDFLAG_NONE, 0, 0, 4, std::bind(&debugger_commands::execute_cheatinit, this, _1, _2)); - - m_console.register_command("cheatrange",CMDFLAG_NONE, 1, 2, 2, std::bind(&debugger_commands::execute_cheatinit, this, _1, _2)); - m_console.register_command("cr", CMDFLAG_NONE, 1, 2, 2, std::bind(&debugger_commands::execute_cheatinit, this, _1, _2)); - - m_console.register_command("cheatnext", CMDFLAG_NONE, 0, 1, 2, std::bind(&debugger_commands::execute_cheatnext, this, _1, _2)); - m_console.register_command("cn", CMDFLAG_NONE, 0, 1, 2, std::bind(&debugger_commands::execute_cheatnext, this, _1, _2)); - m_console.register_command("cheatnextf",CMDFLAG_NONE, 1, 1, 2, std::bind(&debugger_commands::execute_cheatnext, this, _1, _2)); - m_console.register_command("cnf", CMDFLAG_NONE, 1, 1, 2, std::bind(&debugger_commands::execute_cheatnext, this, _1, _2)); - - m_console.register_command("cheatlist", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_cheatlist, this, _1, _2)); - m_console.register_command("cl", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_cheatlist, this, _1, _2)); - - m_console.register_command("cheatundo", CMDFLAG_NONE, 0, 0, 0, std::bind(&debugger_commands::execute_cheatundo, this, _1, _2)); - m_console.register_command("cu", CMDFLAG_NONE, 0, 0, 0, std::bind(&debugger_commands::execute_cheatundo, this, _1, _2)); - - m_console.register_command("f", CMDFLAG_KEEP_QUOTES, AS_PROGRAM, 3, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_find, this, _1, _2)); - m_console.register_command("find", CMDFLAG_KEEP_QUOTES, AS_PROGRAM, 3, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_find, this, _1, _2)); - m_console.register_command("fd", CMDFLAG_KEEP_QUOTES, AS_DATA, 3, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_find, this, _1, _2)); - m_console.register_command("findd", CMDFLAG_KEEP_QUOTES, AS_DATA, 3, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_find, this, _1, _2)); - m_console.register_command("fi", CMDFLAG_KEEP_QUOTES, AS_IO, 3, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_find, this, _1, _2)); - m_console.register_command("findi", CMDFLAG_KEEP_QUOTES, AS_IO, 3, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_find, this, _1, _2)); - m_console.register_command("fo", CMDFLAG_KEEP_QUOTES, AS_OPCODES, 3, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_find, this, _1, _2)); - m_console.register_command("findo", CMDFLAG_KEEP_QUOTES, AS_OPCODES, 3, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_find, this, _1, _2)); - - m_console.register_command("dasm", CMDFLAG_NONE, 0, 3, 5, std::bind(&debugger_commands::execute_dasm, this, _1, _2)); - - m_console.register_command("trace", CMDFLAG_NONE, 0, 1, 4, std::bind(&debugger_commands::execute_trace, this, _1, _2)); - m_console.register_command("traceover", CMDFLAG_NONE, 0, 1, 4, std::bind(&debugger_commands::execute_traceover, this, _1, _2)); - m_console.register_command("traceflush",CMDFLAG_NONE, 0, 0, 0, std::bind(&debugger_commands::execute_traceflush, this, _1, _2)); - - m_console.register_command("history", CMDFLAG_NONE, 0, 0, 2, std::bind(&debugger_commands::execute_history, this, _1, _2)); - m_console.register_command("trackpc", CMDFLAG_NONE, 0, 0, 3, std::bind(&debugger_commands::execute_trackpc, this, _1, _2)); - - m_console.register_command("trackmem", CMDFLAG_NONE, 0, 0, 3, std::bind(&debugger_commands::execute_trackmem, this, _1, _2)); - m_console.register_command("pcatmemp", CMDFLAG_NONE, AS_PROGRAM, 1, 2, std::bind(&debugger_commands::execute_pcatmem, this, _1, _2)); - m_console.register_command("pcatmemd", CMDFLAG_NONE, AS_DATA, 1, 2, std::bind(&debugger_commands::execute_pcatmem, this, _1, _2)); - m_console.register_command("pcatmemi", CMDFLAG_NONE, AS_IO, 1, 2, std::bind(&debugger_commands::execute_pcatmem, this, _1, _2)); - m_console.register_command("pcatmemo", CMDFLAG_NONE, AS_OPCODES, 1, 2, std::bind(&debugger_commands::execute_pcatmem, this, _1, _2)); - - m_console.register_command("snap", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_snap, this, _1, _2)); - - m_console.register_command("source", CMDFLAG_NONE, 0, 1, 1, std::bind(&debugger_commands::execute_source, this, _1, _2)); - - m_console.register_command("map", CMDFLAG_NONE, AS_PROGRAM, 1, 1, std::bind(&debugger_commands::execute_map, this, _1, _2)); - m_console.register_command("mapd", CMDFLAG_NONE, AS_DATA, 1, 1, std::bind(&debugger_commands::execute_map, this, _1, _2)); - m_console.register_command("mapi", CMDFLAG_NONE, AS_IO, 1, 1, std::bind(&debugger_commands::execute_map, this, _1, _2)); - m_console.register_command("mapo", CMDFLAG_NONE, AS_OPCODES, 1, 1, std::bind(&debugger_commands::execute_map, this, _1, _2)); - m_console.register_command("memdump", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_memdump, this, _1, _2)); - - m_console.register_command("symlist", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_symlist, this, _1, _2)); - - m_console.register_command("softreset", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_softreset, this, _1, _2)); - m_console.register_command("hardreset", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_hardreset, this, _1, _2)); - - m_console.register_command("images", CMDFLAG_NONE, 0, 0, 0, std::bind(&debugger_commands::execute_images, this, _1, _2)); - m_console.register_command("mount", CMDFLAG_NONE, 0, 2, 2, std::bind(&debugger_commands::execute_mount, this, _1, _2)); - m_console.register_command("unmount", CMDFLAG_NONE, 0, 1, 1, std::bind(&debugger_commands::execute_unmount, this, _1, _2)); - - m_console.register_command("input", CMDFLAG_NONE, 0, 1, 1, std::bind(&debugger_commands::execute_input, this, _1, _2)); - m_console.register_command("dumpkbd", CMDFLAG_NONE, 0, 0, 1, std::bind(&debugger_commands::execute_dumpkbd, this, _1, _2)); + std::bind(&debugger_commands::global_get, this, &m_global_array[itemnum]), + std::bind(&debugger_commands::global_set, this, &m_global_array[itemnum], _1)); + } + } + + // add all the commands + m_console.register_command("help", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_help, this, _1)); + m_console.register_command("print", CMDFLAG_NONE, 1, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_print, this, _1)); + m_console.register_command("printf", CMDFLAG_NONE, 1, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_printf, this, _1)); + m_console.register_command("logerror", CMDFLAG_NONE, 1, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_logerror, this, _1)); + m_console.register_command("tracelog", CMDFLAG_NONE, 1, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_tracelog, this, _1)); + m_console.register_command("tracesym", CMDFLAG_NONE, 1, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_tracesym, this, _1)); + m_console.register_command("cls", CMDFLAG_NONE, 0, 0, std::bind(&debugger_commands::execute_cls, this, _1)); + m_console.register_command("quit", CMDFLAG_NONE, 0, 0, std::bind(&debugger_commands::execute_quit, this, _1)); + m_console.register_command("exit", CMDFLAG_NONE, 0, 0, std::bind(&debugger_commands::execute_quit, this, _1)); + m_console.register_command("do", CMDFLAG_NONE, 1, 1, std::bind(&debugger_commands::execute_do, this, _1)); + m_console.register_command("step", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_step, this, _1)); + m_console.register_command("s", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_step, this, _1)); + m_console.register_command("over", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_over, this, _1)); + m_console.register_command("o", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_over, this, _1)); + m_console.register_command("out" , CMDFLAG_NONE, 0, 0, std::bind(&debugger_commands::execute_out, this, _1)); + m_console.register_command("go", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_go, this, _1)); + m_console.register_command("g", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_go, this, _1)); + m_console.register_command("gvblank", CMDFLAG_NONE, 0, 0, std::bind(&debugger_commands::execute_go_vblank, this, _1)); + m_console.register_command("gv", CMDFLAG_NONE, 0, 0, std::bind(&debugger_commands::execute_go_vblank, this, _1)); + m_console.register_command("gint", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_go_interrupt, this, _1)); + m_console.register_command("gi", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_go_interrupt, this, _1)); + m_console.register_command("gex", CMDFLAG_NONE, 0, 2, std::bind(&debugger_commands::execute_go_exception, this, _1)); + m_console.register_command("ge", CMDFLAG_NONE, 0, 2, std::bind(&debugger_commands::execute_go_exception, this, _1)); + m_console.register_command("gtime", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_go_time, this, _1)); + m_console.register_command("gt", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_go_time, this, _1)); + m_console.register_command("gp", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_go_privilege, this, _1)); + m_console.register_command("gbt", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_go_branch, this, true, _1)); + m_console.register_command("gbf", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_go_branch, this, false, _1)); + m_console.register_command("gni", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_go_next_instruction, this, _1)); + m_console.register_command("next", CMDFLAG_NONE, 0, 0, std::bind(&debugger_commands::execute_next, this, _1)); + m_console.register_command("n", CMDFLAG_NONE, 0, 0, std::bind(&debugger_commands::execute_next, this, _1)); + m_console.register_command("focus", CMDFLAG_NONE, 1, 1, std::bind(&debugger_commands::execute_focus, this, _1)); + m_console.register_command("ignore", CMDFLAG_NONE, 0, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_ignore, this, _1)); + m_console.register_command("observe", CMDFLAG_NONE, 0, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_observe, this, _1)); + m_console.register_command("suspend", CMDFLAG_NONE, 0, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_suspend, this, _1)); + m_console.register_command("resume", CMDFLAG_NONE, 0, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_resume, this, _1)); + m_console.register_command("cpulist", CMDFLAG_NONE, 0, 0, std::bind(&debugger_commands::execute_cpulist, this, _1)); + m_console.register_command("time", CMDFLAG_NONE, 0, 0, std::bind(&debugger_commands::execute_time, this, _1)); + + m_console.register_command("comadd", CMDFLAG_NONE, 1, 2, std::bind(&debugger_commands::execute_comment_add, this, _1)); + m_console.register_command("//", CMDFLAG_NONE, 1, 2, std::bind(&debugger_commands::execute_comment_add, this, _1)); + m_console.register_command("comdelete", CMDFLAG_NONE, 1, 1, std::bind(&debugger_commands::execute_comment_del, this, _1)); + m_console.register_command("comsave", CMDFLAG_NONE, 0, 0, std::bind(&debugger_commands::execute_comment_save, this, _1)); + m_console.register_command("comlist", CMDFLAG_NONE, 0, 0, std::bind(&debugger_commands::execute_comment_list, this, _1)); + m_console.register_command("commit", CMDFLAG_NONE, 1, 2, std::bind(&debugger_commands::execute_comment_commit, this, _1)); + m_console.register_command("/*", CMDFLAG_NONE, 1, 2, std::bind(&debugger_commands::execute_comment_commit, this, _1)); + + m_console.register_command("bpset", CMDFLAG_NONE, 1, 3, std::bind(&debugger_commands::execute_bpset, this, _1)); + m_console.register_command("bp", CMDFLAG_NONE, 1, 3, std::bind(&debugger_commands::execute_bpset, this, _1)); + m_console.register_command("bpclear", CMDFLAG_NONE, 0, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_bpclear, this, _1)); + m_console.register_command("bpdisable", CMDFLAG_NONE, 0, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_bpdisenable, this, false, _1)); + m_console.register_command("bpenable", CMDFLAG_NONE, 0, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_bpdisenable, this, true, _1)); + m_console.register_command("bplist", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_bplist, this, _1)); + + m_console.register_command("wpset", CMDFLAG_NONE, 3, 5, std::bind(&debugger_commands::execute_wpset, this, -1, _1)); + m_console.register_command("wp", CMDFLAG_NONE, 3, 5, std::bind(&debugger_commands::execute_wpset, this, -1, _1)); + m_console.register_command("wpdset", CMDFLAG_NONE, 3, 5, std::bind(&debugger_commands::execute_wpset, this, AS_DATA, _1)); + m_console.register_command("wpd", CMDFLAG_NONE, 3, 5, std::bind(&debugger_commands::execute_wpset, this, AS_DATA, _1)); + m_console.register_command("wpiset", CMDFLAG_NONE, 3, 5, std::bind(&debugger_commands::execute_wpset, this, AS_IO, _1)); + m_console.register_command("wpi", CMDFLAG_NONE, 3, 5, std::bind(&debugger_commands::execute_wpset, this, AS_IO, _1)); + m_console.register_command("wposet", CMDFLAG_NONE, 3, 5, std::bind(&debugger_commands::execute_wpset, this, AS_OPCODES, _1)); + m_console.register_command("wpo", CMDFLAG_NONE, 3, 5, std::bind(&debugger_commands::execute_wpset, this, AS_OPCODES, _1)); + m_console.register_command("wpclear", CMDFLAG_NONE, 0, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_wpclear, this, _1)); + m_console.register_command("wpdisable", CMDFLAG_NONE, 0, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_wpdisenable, this, false, _1)); + m_console.register_command("wpenable", CMDFLAG_NONE, 0, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_wpdisenable, this, true, _1)); + m_console.register_command("wplist", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_wplist, this, _1)); + + m_console.register_command("rpset", CMDFLAG_NONE, 1, 2, std::bind(&debugger_commands::execute_rpset, this, _1)); + m_console.register_command("rp", CMDFLAG_NONE, 1, 2, std::bind(&debugger_commands::execute_rpset, this, _1)); + m_console.register_command("rpclear", CMDFLAG_NONE, 0, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_rpclear, this, _1)); + m_console.register_command("rpdisable", CMDFLAG_NONE, 0, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_rpdisenable, this, false, _1)); + m_console.register_command("rpenable", CMDFLAG_NONE, 0, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_rpdisenable, this, true, _1)); + m_console.register_command("rplist", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_rplist, this, _1)); + + m_console.register_command("epset", CMDFLAG_NONE, 1, 3, std::bind(&debugger_commands::execute_epset, this, _1)); + m_console.register_command("ep", CMDFLAG_NONE, 1, 3, std::bind(&debugger_commands::execute_epset, this, _1)); + m_console.register_command("epclear", CMDFLAG_NONE, 0, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_epclear, this, _1)); + m_console.register_command("epdisable", CMDFLAG_NONE, 0, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_epdisenable, this, false, _1)); + m_console.register_command("epenable", CMDFLAG_NONE, 0, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_epdisenable, this, true, _1)); + m_console.register_command("eplist", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_eplist, this, _1)); + + m_console.register_command("statesave", CMDFLAG_NONE, 1, 1, std::bind(&debugger_commands::execute_statesave, this, _1)); + m_console.register_command("ss", CMDFLAG_NONE, 1, 1, std::bind(&debugger_commands::execute_statesave, this, _1)); + m_console.register_command("stateload", CMDFLAG_NONE, 1, 1, std::bind(&debugger_commands::execute_stateload, this, _1)); + m_console.register_command("sl", CMDFLAG_NONE, 1, 1, std::bind(&debugger_commands::execute_stateload, this, _1)); + + m_console.register_command("rewind", CMDFLAG_NONE, 0, 0, std::bind(&debugger_commands::execute_rewind, this, _1)); + m_console.register_command("rw", CMDFLAG_NONE, 0, 0, std::bind(&debugger_commands::execute_rewind, this, _1)); + + m_console.register_command("save", CMDFLAG_NONE, 3, 3, std::bind(&debugger_commands::execute_save, this, -1, _1)); + m_console.register_command("saved", CMDFLAG_NONE, 3, 3, std::bind(&debugger_commands::execute_save, this, AS_DATA, _1)); + m_console.register_command("savei", CMDFLAG_NONE, 3, 3, std::bind(&debugger_commands::execute_save, this, AS_IO, _1)); + m_console.register_command("saveo", CMDFLAG_NONE, 3, 3, std::bind(&debugger_commands::execute_save, this, AS_OPCODES, _1)); + m_console.register_command("saver", CMDFLAG_NONE, 4, 4, std::bind(&debugger_commands::execute_saveregion, this, _1)); + + m_console.register_command("load", CMDFLAG_NONE, 2, 3, std::bind(&debugger_commands::execute_load, this, -1, _1)); + m_console.register_command("loadd", CMDFLAG_NONE, 2, 3, std::bind(&debugger_commands::execute_load, this, AS_DATA, _1)); + m_console.register_command("loadi", CMDFLAG_NONE, 2, 3, std::bind(&debugger_commands::execute_load, this, AS_IO, _1)); + m_console.register_command("loado", CMDFLAG_NONE, 2, 3, std::bind(&debugger_commands::execute_load, this, AS_OPCODES, _1)); + m_console.register_command("loadr", CMDFLAG_NONE, 4, 4, std::bind(&debugger_commands::execute_loadregion, this, _1)); + + m_console.register_command("dump", CMDFLAG_NONE, 3, 6, std::bind(&debugger_commands::execute_dump, this, -1, _1)); + m_console.register_command("dumpd", CMDFLAG_NONE, 3, 6, std::bind(&debugger_commands::execute_dump, this, AS_DATA, _1)); + m_console.register_command("dumpi", CMDFLAG_NONE, 3, 6, std::bind(&debugger_commands::execute_dump, this, AS_IO, _1)); + m_console.register_command("dumpo", CMDFLAG_NONE, 3, 6, std::bind(&debugger_commands::execute_dump, this, AS_OPCODES, _1)); + + m_console.register_command("strdump", CMDFLAG_NONE, 3, 4, std::bind(&debugger_commands::execute_strdump, this, -1, _1)); + m_console.register_command("strdumpd", CMDFLAG_NONE, 3, 4, std::bind(&debugger_commands::execute_strdump, this, AS_DATA, _1)); + m_console.register_command("strdumpi", CMDFLAG_NONE, 3, 4, std::bind(&debugger_commands::execute_strdump, this, AS_IO, _1)); + m_console.register_command("strdumpo", CMDFLAG_NONE, 3, 4, std::bind(&debugger_commands::execute_strdump, this, AS_OPCODES, _1)); + + m_console.register_command("cheatinit", CMDFLAG_NONE, 0, 4, std::bind(&debugger_commands::execute_cheatrange, this, true, _1)); + m_console.register_command("ci", CMDFLAG_NONE, 0, 4, std::bind(&debugger_commands::execute_cheatrange, this, true, _1)); + + m_console.register_command("cheatrange",CMDFLAG_NONE, 2, 2, std::bind(&debugger_commands::execute_cheatrange, this, false, _1)); + m_console.register_command("cr", CMDFLAG_NONE, 2, 2, std::bind(&debugger_commands::execute_cheatrange, this, false, _1)); + + m_console.register_command("cheatnext", CMDFLAG_NONE, 1, 2, std::bind(&debugger_commands::execute_cheatnext, this, false, _1)); + m_console.register_command("cn", CMDFLAG_NONE, 1, 2, std::bind(&debugger_commands::execute_cheatnext, this, false, _1)); + m_console.register_command("cheatnextf",CMDFLAG_NONE, 1, 2, std::bind(&debugger_commands::execute_cheatnext, this, true, _1)); + m_console.register_command("cnf", CMDFLAG_NONE, 1, 2, std::bind(&debugger_commands::execute_cheatnext, this, true, _1)); + + m_console.register_command("cheatlist", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_cheatlist, this, _1)); + m_console.register_command("cl", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_cheatlist, this, _1)); + + m_console.register_command("cheatundo", CMDFLAG_NONE, 0, 0, std::bind(&debugger_commands::execute_cheatundo, this, _1)); + m_console.register_command("cu", CMDFLAG_NONE, 0, 0, std::bind(&debugger_commands::execute_cheatundo, this, _1)); + + m_console.register_command("f", CMDFLAG_KEEP_QUOTES, 3, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_find, this, -1, _1)); + m_console.register_command("find", CMDFLAG_KEEP_QUOTES, 3, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_find, this, -1, _1)); + m_console.register_command("fd", CMDFLAG_KEEP_QUOTES, 3, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_find, this, AS_DATA, _1)); + m_console.register_command("findd", CMDFLAG_KEEP_QUOTES, 3, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_find, this, AS_DATA, _1)); + m_console.register_command("fi", CMDFLAG_KEEP_QUOTES, 3, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_find, this, AS_IO, _1)); + m_console.register_command("findi", CMDFLAG_KEEP_QUOTES, 3, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_find, this, AS_IO, _1)); + m_console.register_command("fo", CMDFLAG_KEEP_QUOTES, 3, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_find, this, AS_OPCODES, _1)); + m_console.register_command("findo", CMDFLAG_KEEP_QUOTES, 3, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_find, this, AS_OPCODES, _1)); + + m_console.register_command("fill", CMDFLAG_KEEP_QUOTES, 3, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_fill, this, -1, _1)); + m_console.register_command("filld", CMDFLAG_KEEP_QUOTES, 3, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_fill, this, AS_DATA, _1)); + m_console.register_command("filli", CMDFLAG_KEEP_QUOTES, 3, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_fill, this, AS_IO, _1)); + m_console.register_command("fillo", CMDFLAG_KEEP_QUOTES, 3, MAX_COMMAND_PARAMS, std::bind(&debugger_commands::execute_fill, this, AS_OPCODES, _1)); + + m_console.register_command("dasm", CMDFLAG_NONE, 3, 5, std::bind(&debugger_commands::execute_dasm, this, _1)); + + m_console.register_command("trace", CMDFLAG_NONE, 1, 4, std::bind(&debugger_commands::execute_trace, this, _1, false)); + m_console.register_command("traceover", CMDFLAG_NONE, 1, 4, std::bind(&debugger_commands::execute_trace, this, _1, true)); + m_console.register_command("traceflush",CMDFLAG_NONE, 0, 0, std::bind(&debugger_commands::execute_traceflush, this, _1)); + + m_console.register_command("history", CMDFLAG_NONE, 0, 2, std::bind(&debugger_commands::execute_history, this, _1)); + m_console.register_command("trackpc", CMDFLAG_NONE, 0, 3, std::bind(&debugger_commands::execute_trackpc, this, _1)); + + m_console.register_command("trackmem", CMDFLAG_NONE, 0, 3, std::bind(&debugger_commands::execute_trackmem, this, _1)); + m_console.register_command("pcatmem", CMDFLAG_NONE, 1, 1, std::bind(&debugger_commands::execute_pcatmem, this, -1, _1)); + m_console.register_command("pcatmemd", CMDFLAG_NONE, 1, 1, std::bind(&debugger_commands::execute_pcatmem, this, AS_DATA, _1)); + m_console.register_command("pcatmemi", CMDFLAG_NONE, 1, 1, std::bind(&debugger_commands::execute_pcatmem, this, AS_IO, _1)); + m_console.register_command("pcatmemo", CMDFLAG_NONE, 1, 1, std::bind(&debugger_commands::execute_pcatmem, this, AS_OPCODES, _1)); + + m_console.register_command("snap", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_snap, this, _1)); + + m_console.register_command("source", CMDFLAG_NONE, 1, 1, std::bind(&debugger_commands::execute_source, this, _1)); + + m_console.register_command("map", CMDFLAG_NONE, 1, 1, std::bind(&debugger_commands::execute_map, this, -1, _1)); + m_console.register_command("mapd", CMDFLAG_NONE, 1, 1, std::bind(&debugger_commands::execute_map, this, AS_DATA, _1)); + m_console.register_command("mapi", CMDFLAG_NONE, 1, 1, std::bind(&debugger_commands::execute_map, this, AS_IO, _1)); + m_console.register_command("mapo", CMDFLAG_NONE, 1, 1, std::bind(&debugger_commands::execute_map, this, AS_OPCODES, _1)); + m_console.register_command("memdump", CMDFLAG_NONE, 0, 2, std::bind(&debugger_commands::execute_memdump, this, _1)); + + m_console.register_command("symlist", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_symlist, this, _1)); + + m_console.register_command("softreset", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_softreset, this, _1)); + m_console.register_command("hardreset", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_hardreset, this, _1)); + + m_console.register_command("images", CMDFLAG_NONE, 0, 0, std::bind(&debugger_commands::execute_images, this, _1)); + m_console.register_command("mount", CMDFLAG_NONE, 2, 2, std::bind(&debugger_commands::execute_mount, this, _1)); + m_console.register_command("unmount", CMDFLAG_NONE, 1, 1, std::bind(&debugger_commands::execute_unmount, this, _1)); + + m_console.register_command("input", CMDFLAG_NONE, 1, 1, std::bind(&debugger_commands::execute_input, this, _1)); + m_console.register_command("dumpkbd", CMDFLAG_NONE, 0, 1, std::bind(&debugger_commands::execute_dumpkbd, this, _1)); - /* set up the initial debugscript if specified */ + // set up the initial debugscript if specified const char* name = m_machine.options().debug_script(); if (name[0] != 0) m_console.source_script(name); - m_cheat.cpu[0] = m_cheat.cpu[1] = 0; + m_cheat.space = nullptr; } -/*------------------------------------------------- - execute_min - return the minimum of two values --------------------------------------------------*/ - -u64 debugger_commands::execute_min(symbol_table &table, int params, const u64 *param) -{ - return (param[0] < param[1]) ? param[0] : param[1]; -} +//------------------------------------------------- +// get_cpunum - getter callback for the +// 'cpunum' symbol +//------------------------------------------------- -/*------------------------------------------------- - execute_max - return the maximum of two values --------------------------------------------------*/ - -u64 debugger_commands::execute_max(symbol_table &table, int params, const u64 *param) +u64 debugger_commands::get_cpunum() { - return (param[0] > param[1]) ? param[0] : param[1]; -} - - -/*------------------------------------------------- - execute_if - if (a) return b; else return c; --------------------------------------------------*/ + unsigned index = 0; + for (device_execute_interface &exec : execute_interface_enumerator(m_machine.root_device())) + { + if (m_console.get_visible_cpu() == &exec.device()) + return index; -u64 debugger_commands::execute_if(symbol_table &table, int params, const u64 *param) -{ - return param[0] ? param[1] : param[2]; + // real CPUs should have pcbase + device_state_interface const *state; + if (exec.device().interface(state) && state->state_find_entry(STATE_GENPCBASE)) + ++index; + } + return u64(s64(-1)); } - /*************************************************************************** GLOBAL ACCESSORS ***************************************************************************/ @@ -340,14 +399,14 @@ u64 debugger_commands::execute_if(symbol_table &table, int params, const u64 *pa global_get - symbol table getter for globals -------------------------------------------------*/ -u64 debugger_commands::global_get(symbol_table &table, global_entry *global) +u64 debugger_commands::global_get(global_entry *global) { switch (global->size) { - case 1: return *(u8 *)global->base; - case 2: return *(u16 *)global->base; - case 4: return *(u32 *)global->base; - case 8: return *(u64 *)global->base; + case 1: return *(u8 *)global->base; + case 2: return *(u16 *)global->base; + case 4: return *(u32 *)global->base; + case 8: return *(u64 *)global->base; } return ~0; } @@ -357,210 +416,33 @@ u64 debugger_commands::global_get(symbol_table &table, global_entry *global) global_set - symbol table setter for globals -------------------------------------------------*/ -void debugger_commands::global_set(symbol_table &table, global_entry *global, u64 value) +void debugger_commands::global_set(global_entry *global, u64 value) { switch (global->size) { - case 1: *(u8 *)global->base = value; break; - case 2: *(u16 *)global->base = value; break; - case 4: *(u32 *)global->base = value; break; - case 8: *(u64 *)global->base = value; break; - } -} - - - -/*************************************************************************** - PARAMETER VALIDATION HELPERS -***************************************************************************/ - -/*------------------------------------------------- - validate_number_parameter - validates a - number parameter --------------------------------------------------*/ - -bool debugger_commands::validate_number_parameter(const std::string ¶m, u64 &result) -{ - /* evaluate the expression; success if no error */ - try - { - parsed_expression expression(m_cpu.get_visible_symtable(), param.c_str(), &result); - return true; - } - catch (expression_error &error) - { - /* print an error pointing to the character that caused it */ - m_console.printf("Error in expression: %s\n", param); - m_console.printf(" %*s^", error.offset(), ""); - m_console.printf("%s\n", error.code_string()); - return false; + case 1: *(u8 *)global->base = value; break; + case 2: *(u16 *)global->base = value; break; + case 4: *(u32 *)global->base = value; break; + case 8: *(u64 *)global->base = value; break; } } -/*------------------------------------------------- - validate_boolean_parameter - validates a - boolean parameter --------------------------------------------------*/ - -bool debugger_commands::validate_boolean_parameter(const std::string ¶m, bool &result) -{ - /* nullptr parameter does nothing and returns no error */ - if (param.empty()) - return true; - - /* evaluate the expression; success if no error */ - bool is_true = core_stricmp(param.c_str(), "true") == 0 || param == "1"; - bool is_false = core_stricmp(param.c_str(), "false") == 0 || param == "0"; - - if (!is_true && !is_false) - { - m_console.printf("Invalid boolean '%s'\n", param); - return false; - } - - result = is_true; - - return true; -} - -/*------------------------------------------------- - validate_cpu_parameter - validates a - parameter as a cpu --------------------------------------------------*/ - -bool debugger_commands::validate_cpu_parameter(const char *param, device_t *&result) -{ - /* if no parameter, use the visible CPU */ - if (param == nullptr) - { - result = m_cpu.get_visible_cpu(); - if (!result) - { - m_console.printf("No valid CPU is currently selected\n"); - return false; - } - return true; - } - - /* first look for a tag match */ - result = m_machine.root_device().subdevice(param); - if (result) - return true; - - /* then evaluate as an expression; on an error assume it was a tag */ - u64 cpunum; - try - { - parsed_expression expression(m_cpu.get_visible_symtable(), param, &cpunum); - } - catch (expression_error &) - { - m_console.printf("Unable to find CPU '%s'\n", param); - return false; - } - - // attempt to find by numerical index - int index = 0; - for (device_execute_interface &exec : execute_interface_iterator(m_machine.root_device())) - { - // real CPUs should have pcbase - const device_state_interface *state; - if (exec.device().interface(state) && state->state_find_entry(STATE_GENPCBASE) != nullptr && index++ == cpunum) - { - result = &exec.device(); - return true; - } - } - - /* if out of range, complain */ - m_console.printf("Invalid CPU index %d\n", (int)cpunum); - return false; -} - - -/*------------------------------------------------- - validate_cpu_space_parameter - validates - a parameter as a cpu and retrieves the given - address space --------------------------------------------------*/ - -bool debugger_commands::validate_cpu_space_parameter(const char *param, int spacenum, address_space *&result) -{ - /* first do the standard CPU thing */ - device_t *cpu; - if (!validate_cpu_parameter(param, cpu)) - return false; - - /* fetch the space pointer */ - if (!cpu->memory().has_space(spacenum)) - { - m_console.printf("No matching memory space found for CPU '%s'\n", cpu->tag()); - return false; - } - result = &cpu->memory().space(spacenum); - return true; -} - - -/*------------------------------------------------- - debug_command_parameter_expression - validates - an expression parameter --------------------------------------------------*/ - -bool debugger_commands::debug_command_parameter_expression(const std::string ¶m, parsed_expression &result) -{ - /* parse the expression; success if no error */ - try - { - result.parse(param.c_str()); - return true; - } - catch (expression_error &err) - { - /* output an error */ - m_console.printf("Error in expression: %s\n", param); - m_console.printf(" %*s^", err.offset(), ""); - m_console.printf("%s\n", err.code_string()); - return false; - } -} - - -/*------------------------------------------------- - debug_command_parameter_command - validates a - command parameter --------------------------------------------------*/ - -bool debugger_commands::debug_command_parameter_command(const char *param) -{ - /* nullptr parameter does nothing and returns no error */ - if (param == nullptr) - return true; - - /* validate the comment; success if no error */ - CMDERR err = m_console.validate_command(param); - if (err == CMDERR_NONE) - return true; - - /* output an error */ - m_console.printf("Error in command: %s\n", param); - m_console.printf(" %*s^", CMDERR_ERROR_OFFSET(err), ""); - m_console.printf("%s\n", debugger_console::cmderr_to_string(err)); - return 0; -} +//************************************************************************** +// COMMAND IMPLEMENTATIONS +//************************************************************************** /*------------------------------------------------- execute_help - execute the help command -------------------------------------------------*/ -void debugger_commands::execute_help(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_help(const std::vector<std::string_view> ¶ms) { if (params.size() == 0) - m_console.printf_wrap(80, "%s\n", debug_get_help("")); + m_console.printf_wrap(80, "%s\n", debug_get_help(std::string_view())); else - m_console.printf_wrap(80, "%s\n", debug_get_help(params[0].c_str())); + m_console.printf_wrap(80, "%s\n", debug_get_help(params[0])); } @@ -568,12 +450,12 @@ void debugger_commands::execute_help(int ref, const std::vector<std::string> &pa execute_print - execute the print command -------------------------------------------------*/ -void debugger_commands::execute_print(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_print(const std::vector<std::string_view> ¶ms) { /* validate the other parameters */ u64 values[MAX_COMMAND_PARAMS]; for (int i = 0; i < params.size(); i++) - if (!validate_number_parameter(params[i], values[i])) + if (!m_console.validate_number_parameter(params[i], values[i])) return; /* then print each one */ @@ -587,124 +469,221 @@ void debugger_commands::execute_print(int ref, const std::vector<std::string> &p mini_printf - safe printf to a buffer -------------------------------------------------*/ -int debugger_commands::mini_printf(char *buffer, const char *format, int params, u64 *param) +bool debugger_commands::mini_printf(std::ostream &stream, const std::vector<std::string_view> ¶ms) { - const char *f = format; - char *p = buffer; + std::string_view const format(params[0]); + auto f = format.begin(); - /* parse the string looking for % signs */ - for (;;) + int param = 1; + u64 number; + + // parse the string looking for % signs + while (f != format.end()) { char c = *f++; - if (!c) break; - /* escape sequences */ + // escape sequences if (c == '\\') { + if (f == format.end()) break; c = *f++; - if (!c) break; switch (c) { - case '\\': *p++ = c; break; - case 'n': *p++ = '\n'; break; + case '\\': stream << c; break; + case 'n': stream << '\n'; break; default: break; } continue; } - /* formatting */ + // formatting else if (c == '%') { + bool left_justify = false; + bool zero_fill = false; int width = 0; - int zerofill = 0; + int precision = 0; + + // parse optional left justification flag + if (f != format.end() && *f == '-') + { + left_justify = true; + f++; + } - /* parse out the width */ - for (;;) + // parse optional zero fill flag + if (f != format.end() && *f == '0') { - c = *f++; - if (!c || c < '0' || c > '9') break; - if (c == '0' && width == 0) - zerofill = 1; - width = width * 10 + (c - '0'); + zero_fill = true; + f++; + } + + // parse optional width + while (f != format.end() && isdigit(*f)) + width = width * 10 + (*f++ - '0'); + if (f == format.end()) + break; + + // apply left justification + if (left_justify) + width = -width; + + if ((c = *f++) == '.') + { + // parse optional precision + while (f != format.end() && isdigit(*f)) + precision = precision * 10 + (*f++ - '0'); + + // get the format + if (f != format.end()) + c = *f++; + else + break; } - if (!c) break; - /* get the format */ switch (c) { case '%': - *p++ = c; + stream << c; break; case 'X': + if (param < params.size() && m_console.validate_number_parameter(params[param++], number)) + util::stream_format(stream, zero_fill ? "%0*X" : "%*X", width, number); + else + { + m_console.printf("Not enough parameters for format!\n"); + return false; + } + break; case 'x': - if (params == 0) + if (param < params.size() && m_console.validate_number_parameter(params[param++], number)) + util::stream_format(stream, zero_fill ? "%0*x" : "%*x", width, number); + else { m_console.printf("Not enough parameters for format!\n"); - return 0; + return false; + } + break; + + case 'O': + case 'o': + if (param < params.size() && m_console.validate_number_parameter(params[param++], number)) + util::stream_format(stream, zero_fill ? "%0*o" : "%*o", width, number); + else + { + m_console.printf("Not enough parameters for format!\n"); + return false; } - if (u32(*param >> 32) != 0) - p += sprintf(p, zerofill ? "%0*X" : "%*X", (width <= 8) ? 1 : width - 8, u32(*param >> 32)); - else if (width > 8) - p += sprintf(p, zerofill ? "%0*X" : "%*X", width - 8, 0); - p += sprintf(p, zerofill ? "%0*X" : "%*X", (width < 8) ? width : 8, u32(*param)); - param++; - params--; break; case 'D': case 'd': - if (params == 0) + if (param < params.size() && m_console.validate_number_parameter(params[param++], number)) + util::stream_format(stream, zero_fill ? "%0*d" : "%*d", width, number); + else { m_console.printf("Not enough parameters for format!\n"); - return 0; + return false; } - p += sprintf(p, zerofill ? "%0*d" : "%*d", width, u32(*param)); - param++; - params--; break; + case 'C': case 'c': - if (params == 0) + if (param < params.size() && m_console.validate_number_parameter(params[param++], number)) + stream << char(number); + else { m_console.printf("Not enough parameters for format!\n"); - return 0; + return false; } - p += sprintf(p, "%c", char(*param)); - param++; - params--; break; + case 's': + { + address_space *space; + if (param < params.size() && m_console.validate_target_address_parameter(params[param++], -1, space, number)) + { + address_space *tspace; + std::string s; + + for (u32 address = u32(number), taddress; space->device().memory().translate(space->spacenum(), device_memory_interface::TR_READ, taddress = address, tspace); address++) + { + u8 const data = tspace->read_byte(taddress); + + if (!data) + break; + + s += data; + + if (precision == 1) + break; + else if (precision) + precision--; + } + + util::stream_format(stream, "%*s", width, s); + } + else + { + m_console.printf("Not enough parameters for format!\n"); + return false; + } + } + break; } } - /* normal stuff */ + // normal stuff else - *p++ = c; + stream << c; } - /* NULL-terminate and exit */ - *p = 0; - return 1; + return true; } /*------------------------------------------------- - execute_printf - execute the printf command + execute_index_command - helper for commands + that take multiple indices as arguments -------------------------------------------------*/ -void debugger_commands::execute_printf(int ref, const std::vector<std::string> ¶ms) +template <typename T> +void debugger_commands::execute_index_command(std::vector<std::string_view> const ¶ms, T &&apply, char const *unused_message) { - /* validate the other parameters */ - u64 values[MAX_COMMAND_PARAMS]; - for (int i = 1; i < params.size(); i++) - if (!validate_number_parameter(params[i], values[i])) + std::vector<u64> index(params.size()); + for (int paramnum = 0; paramnum < params.size(); paramnum++) + { + if (!m_console.validate_number_parameter(params[paramnum], index[paramnum])) return; + } + for (device_t &device : device_enumerator(m_machine.root_device())) + { + for (auto param = index.begin(); index.end() != param; ) + { + if (apply(device, *param)) + param = index.erase(param); + else + ++param; + } + } + + for (auto const ¶m : index) + m_console.printf(unused_message, param); +} + + +/*------------------------------------------------- + execute_printf - execute the printf command +-------------------------------------------------*/ + +void debugger_commands::execute_printf(const std::vector<std::string_view> ¶ms) +{ /* then do a printf */ - char buffer[1024]; - if (mini_printf(buffer, params[0].c_str(), params.size() - 1, &values[1])) - m_console.printf("%s\n", buffer); + std::ostringstream buffer; + if (mini_printf(buffer, params)) + m_console.printf("%s\n", std::move(buffer).str()); } @@ -712,18 +691,12 @@ void debugger_commands::execute_printf(int ref, const std::vector<std::string> & execute_logerror - execute the logerror command -------------------------------------------------*/ -void debugger_commands::execute_logerror(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_logerror(const std::vector<std::string_view> ¶ms) { - /* validate the other parameters */ - u64 values[MAX_COMMAND_PARAMS]; - for (int i = 1; i < params.size(); i++) - if (!validate_number_parameter(params[i], values[i])) - return; - /* then do a printf */ - char buffer[1024]; - if (mini_printf(buffer, params[0].c_str(), params.size() - 1, &values[1])) - m_machine.logerror("%s", buffer); + std::ostringstream buffer; + if (mini_printf(buffer, params)) + m_machine.logerror("%s", std::move(buffer).str()); } @@ -731,18 +704,12 @@ void debugger_commands::execute_logerror(int ref, const std::vector<std::string> execute_tracelog - execute the tracelog command -------------------------------------------------*/ -void debugger_commands::execute_tracelog(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_tracelog(const std::vector<std::string_view> ¶ms) { - /* validate the other parameters */ - u64 values[MAX_COMMAND_PARAMS]; - for (int i = 1; i < params.size(); i++) - if (!validate_number_parameter(params[i], values[i])) - return; - /* then do a printf */ - char buffer[1024]; - if (mini_printf(buffer, params[0].c_str(), params.size() - 1, &values[1])) - m_cpu.get_visible_cpu()->debug()->trace_printf("%s", buffer); + std::ostringstream buffer; + if (mini_printf(buffer, params)) + m_console.get_visible_cpu()->debug()->trace_printf("%s", std::move(buffer).str()); } @@ -750,18 +717,17 @@ void debugger_commands::execute_tracelog(int ref, const std::vector<std::string> execute_tracesym - execute the tracesym command -------------------------------------------------*/ -void debugger_commands::execute_tracesym(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_tracesym(const std::vector<std::string_view> ¶ms) { // build a format string appropriate for the parameters and validate them - std::stringstream format; - u64 values[MAX_COMMAND_PARAMS]; + std::ostringstream format; for (int i = 0; i < params.size(); i++) { // find this symbol - symbol_entry *sym = m_cpu.get_visible_symtable()->find(params[i].c_str()); + symbol_entry *const sym = m_console.visible_symtable().find(strmakelower(params[i]).c_str()); if (!sym) { - m_console.printf("Unknown symbol: %s\n", params[i].c_str()); + m_console.printf("Unknown symbol: %s\n", params[i]); return; } @@ -769,16 +735,29 @@ void debugger_commands::execute_tracesym(int ref, const std::vector<std::string> util::stream_format(format, "%s=%s ", params[i], sym->format().empty() ? "%16X" : sym->format()); - - // validate the parameter - if (!validate_number_parameter(params[i], values[i])) - return; } + // build parameters for printf + auto const format_str = std::move(format).str(); // need this to stay put as long as the string_view exists + std::vector<std::string_view> printf_params; + printf_params.reserve(params.size() + 1); + printf_params.emplace_back(format_str); + std::copy(params.begin(), params.end(), std::back_inserter(printf_params)); + // then do a printf - char buffer[1024]; - if (mini_printf(buffer, format.str().c_str(), params.size(), values)) - m_cpu.get_visible_cpu()->debug()->trace_printf("%s", buffer); + std::ostringstream buffer; + if (mini_printf(buffer, printf_params)) + m_console.get_visible_cpu()->debug()->trace_printf("%s", std::move(buffer).str()); +} + + +/*------------------------------------------------- + execute_cls - execute the cls command +-------------------------------------------------*/ + +void debugger_commands::execute_cls(const std::vector<std::string_view> ¶ms) +{ + text_buffer_clear(m_console.get_console_textbuf()); } @@ -786,7 +765,7 @@ void debugger_commands::execute_tracesym(int ref, const std::vector<std::string> execute_quit - execute the quit command -------------------------------------------------*/ -void debugger_commands::execute_quit(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_quit(const std::vector<std::string_view> ¶ms) { osd_printf_warning("Exited via the debugger\n"); m_machine.schedule_exit(); @@ -797,10 +776,10 @@ void debugger_commands::execute_quit(int ref, const std::vector<std::string> &pa execute_do - execute the do command -------------------------------------------------*/ -void debugger_commands::execute_do(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_do(const std::vector<std::string_view> ¶ms) { u64 dummy; - validate_number_parameter(params[0], dummy); + m_console.validate_number_parameter(params[0], dummy); } @@ -808,14 +787,14 @@ void debugger_commands::execute_do(int ref, const std::vector<std::string> ¶ execute_step - execute the step command -------------------------------------------------*/ -void debugger_commands::execute_step(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_step(const std::vector<std::string_view> ¶ms) { /* if we have a parameter, use it */ u64 steps = 1; - if (params.size() > 0 && !validate_number_parameter(params[0], steps)) + if (params.size() > 0 && !m_console.validate_number_parameter(params[0], steps)) return; - m_cpu.get_visible_cpu()->debug()->single_step(steps); + m_console.get_visible_cpu()->debug()->single_step(steps); } @@ -823,14 +802,14 @@ void debugger_commands::execute_step(int ref, const std::vector<std::string> &pa execute_over - execute the over command -------------------------------------------------*/ -void debugger_commands::execute_over(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_over(const std::vector<std::string_view> ¶ms) { /* if we have a parameter, use it */ u64 steps = 1; - if (params.size() > 0 && !validate_number_parameter(params[0], steps)) + if (params.size() > 0 && !m_console.validate_number_parameter(params[0], steps)) return; - m_cpu.get_visible_cpu()->debug()->single_step_over(steps); + m_console.get_visible_cpu()->debug()->single_step_over(steps); } @@ -838,9 +817,9 @@ void debugger_commands::execute_over(int ref, const std::vector<std::string> &pa execute_out - execute the out command -------------------------------------------------*/ -void debugger_commands::execute_out(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_out(const std::vector<std::string_view> ¶ms) { - m_cpu.get_visible_cpu()->debug()->single_step_out(); + m_console.get_visible_cpu()->debug()->single_step_out(); } @@ -848,15 +827,15 @@ void debugger_commands::execute_out(int ref, const std::vector<std::string> &par execute_go - execute the go command -------------------------------------------------*/ -void debugger_commands::execute_go(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_go(const std::vector<std::string_view> ¶ms) { u64 addr = ~0; /* if we have a parameter, use it instead */ - if (params.size() > 0 && !validate_number_parameter(params[0], addr)) + if (params.size() > 0 && !m_console.validate_number_parameter(params[0], addr)) return; - m_cpu.get_visible_cpu()->debug()->go(addr); + m_console.get_visible_cpu()->debug()->go(addr); } @@ -865,9 +844,9 @@ void debugger_commands::execute_go(int ref, const std::vector<std::string> ¶ command -------------------------------------------------*/ -void debugger_commands::execute_go_vblank(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_go_vblank(const std::vector<std::string_view> ¶ms) { - m_cpu.get_visible_cpu()->debug()->go_vblank(); + m_console.get_visible_cpu()->debug()->go_vblank(); } @@ -875,30 +854,34 @@ void debugger_commands::execute_go_vblank(int ref, const std::vector<std::string execute_go_interrupt - execute the goint command -------------------------------------------------*/ -void debugger_commands::execute_go_interrupt(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_go_interrupt(const std::vector<std::string_view> ¶ms) { u64 irqline = -1; /* if we have a parameter, use it instead */ - if (params.size() > 0 && !validate_number_parameter(params[0], irqline)) + if (params.size() > 0 && !m_console.validate_number_parameter(params[0], irqline)) return; - m_cpu.get_visible_cpu()->debug()->go_interrupt(irqline); + m_console.get_visible_cpu()->debug()->go_interrupt(irqline); } /*------------------------------------------------- execute_go_exception - execute the goex command -------------------------------------------------*/ -void debugger_commands::execute_go_exception(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_go_exception(const std::vector<std::string_view> ¶ms) { u64 exception = -1; /* if we have a parameter, use it instead */ - if (params.size() > 0 && !validate_number_parameter(params[0], exception)) + if (params.size() > 0 && !m_console.validate_number_parameter(params[0], exception)) + return; + + parsed_expression condition(m_console.visible_symtable()); + if (params.size() > 1 && !m_console.validate_expression_parameter(params[1], condition)) return; - m_cpu.get_visible_cpu()->debug()->go_exception(exception); + m_console.get_visible_cpu()->debug()->go_exception(exception, condition.is_empty() ? "1" : condition.original_string()); } @@ -906,15 +889,15 @@ void debugger_commands::execute_go_exception(int ref, const std::vector<std::str execute_go_time - execute the gtime command -------------------------------------------------*/ -void debugger_commands::execute_go_time(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_go_time(const std::vector<std::string_view> ¶ms) { u64 milliseconds = -1; /* if we have a parameter, use it instead */ - if (params.size() > 0 && !validate_number_parameter(params[0], milliseconds)) + if (params.size() > 0 && !m_console.validate_number_parameter(params[0], milliseconds)) return; - m_cpu.get_visible_cpu()->debug()->go_milliseconds(milliseconds); + m_console.get_visible_cpu()->debug()->go_milliseconds(milliseconds); } @@ -922,22 +905,78 @@ void debugger_commands::execute_go_time(int ref, const std::vector<std::string> /*------------------------------------------------- execute_go_privilege - execute the gp command -------------------------------------------------*/ -void debugger_commands::execute_go_privilege(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_go_privilege(const std::vector<std::string_view> ¶ms) +{ + parsed_expression condition(m_console.visible_symtable()); + if (params.size() > 0 && !m_console.validate_expression_parameter(params[0], condition)) + return; + + m_console.get_visible_cpu()->debug()->go_privilege((condition.is_empty()) ? "1" : condition.original_string()); +} + + +/*------------------------------------------------- + execute_go_branch - execute gbt or gbf command +-------------------------------------------------*/ + +void debugger_commands::execute_go_branch(bool sense, const std::vector<std::string_view> ¶ms) +{ + parsed_expression condition(m_console.visible_symtable()); + if (params.size() > 0 && !m_console.validate_expression_parameter(params[0], condition)) + return; + + m_console.get_visible_cpu()->debug()->go_branch(sense, (condition.is_empty()) ? "1" : condition.original_string()); +} + + +/*------------------------------------------------- + execute_go_next_instruction - execute gni command +-------------------------------------------------*/ + +void debugger_commands::execute_go_next_instruction(const std::vector<std::string_view> ¶ms) { - parsed_expression condition(&m_cpu.get_visible_cpu()->debug()->symtable()); - if (params.size() > 0 && !debug_command_parameter_expression(params[0], condition)) + u64 count = 1; + static constexpr u64 MAX_COUNT = 512; + + // if we have a parameter, use it instead */ + if (params.size() > 0 && !m_console.validate_number_parameter(params[0], count)) + return; + if (count == 0) + return; + if (count > MAX_COUNT) + { + m_console.printf("Too many instructions (must be %d or fewer)\n", MAX_COUNT); + return; + } + + device_state_interface *stateintf; + device_t *cpu = m_console.get_visible_cpu(); + if (!cpu->interface(stateintf)) + { + m_console.printf("No state interface available for %s\n", cpu->name()); return; + } + u32 pc = stateintf->pcbase(); + + debug_disasm_buffer buffer(*cpu); + while (count-- != 0) + { + // disassemble the current instruction and get the length + u32 result = buffer.disassemble_info(pc); + pc = buffer.next_pc_wrap(pc, result & util::disasm_interface::LENGTHMASK); + } - m_cpu.get_visible_cpu()->debug()->go_privilege((condition.is_empty()) ? "1" : condition.original_string()); + cpu->debug()->go(pc); } + /*------------------------------------------------- execute_next - execute the next command -------------------------------------------------*/ -void debugger_commands::execute_next(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_next(const std::vector<std::string_view> ¶ms) { - m_cpu.get_visible_cpu()->debug()->go_next_device(); + m_console.get_visible_cpu()->debug()->go_next_device(); } @@ -945,18 +984,18 @@ void debugger_commands::execute_next(int ref, const std::vector<std::string> &pa execute_focus - execute the focus command -------------------------------------------------*/ -void debugger_commands::execute_focus(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_focus(const std::vector<std::string_view> ¶ms) { - /* validate params */ + // validate params device_t *cpu; - if (!validate_cpu_parameter(params[0].c_str(), cpu)) + if (!m_console.validate_cpu_parameter(params[0], cpu)) return; - /* first clear the ignore flag on the focused CPU */ + // first clear the ignore flag on the focused CPU cpu->debug()->ignore(false); - /* then loop over CPUs and set the ignore flags on all other CPUs */ - for (device_execute_interface &exec : execute_interface_iterator(m_machine.root_device())) + // then loop over CPUs and set the ignore flags on all other CPUs + for (device_execute_interface &exec : execute_interface_enumerator(m_machine.root_device())) if (&exec.device() != cpu) exec.device().debug()->ignore(true); m_console.printf("Now focused on CPU '%s'\n", cpu->tag()); @@ -967,17 +1006,17 @@ void debugger_commands::execute_focus(int ref, const std::vector<std::string> &p execute_ignore - execute the ignore command -------------------------------------------------*/ -void debugger_commands::execute_ignore(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_ignore(const std::vector<std::string_view> ¶ms) { - /* if there are no parameters, dump the ignore list */ if (params.empty()) { + // if there are no parameters, dump the ignore list std::string buffer; - /* loop over all executable devices */ - for (device_execute_interface &exec : execute_interface_iterator(m_machine.root_device())) - - /* build up a comma-separated list */ + // loop over all executable devices + for (device_execute_interface &exec : execute_interface_enumerator(m_machine.root_device())) + { + // build up a comma-separated list if (!exec.device().debug()->observing()) { if (buffer.empty()) @@ -985,29 +1024,29 @@ void debugger_commands::execute_ignore(int ref, const std::vector<std::string> & else buffer.append(string_format(", '%s'", exec.device().tag())); } + } - /* special message for none */ + // special message for none if (buffer.empty()) buffer = string_format("Not currently ignoring any devices"); - m_console.printf("%s\n", buffer.c_str()); + m_console.printf("%s\n", buffer); } - - /* otherwise clear the ignore flag on all requested CPUs */ else { + // otherwise clear the ignore flag on all requested CPUs device_t *devicelist[MAX_COMMAND_PARAMS]; - /* validate parameters */ + // validate parameters for (int paramnum = 0; paramnum < params.size(); paramnum++) - if (!validate_cpu_parameter(params[paramnum].c_str(), devicelist[paramnum])) + if (!m_console.validate_cpu_parameter(params[paramnum], devicelist[paramnum])) return; - /* set the ignore flags */ + // set the ignore flags for (int paramnum = 0; paramnum < params.size(); paramnum++) { - /* make sure this isn't the last live CPU */ + // make sure this isn't the last live CPU bool gotone = false; - for (device_execute_interface &exec : execute_interface_iterator(m_machine.root_device())) + for (device_execute_interface &exec : execute_interface_enumerator(m_machine.root_device())) if (&exec.device() != devicelist[paramnum] && exec.device().debug()->observing()) { gotone = true; @@ -1030,17 +1069,17 @@ void debugger_commands::execute_ignore(int ref, const std::vector<std::string> & execute_observe - execute the observe command -------------------------------------------------*/ -void debugger_commands::execute_observe(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_observe(const std::vector<std::string_view> ¶ms) { - /* if there are no parameters, dump the ignore list */ if (params.empty()) { + // if there are no parameters, dump the ignore list std::string buffer; - /* loop over all executable devices */ - for (device_execute_interface &exec : execute_interface_iterator(m_machine.root_device())) - - /* build up a comma-separated list */ + // loop over all executable devices + for (device_execute_interface &exec : execute_interface_enumerator(m_machine.root_device())) + { + // build up a comma-separated list if (exec.device().debug()->observing()) { if (buffer.empty()) @@ -1048,24 +1087,24 @@ void debugger_commands::execute_observe(int ref, const std::vector<std::string> else buffer.append(string_format(", '%s'", exec.device().tag())); } + } - /* special message for none */ + // special message for none if (buffer.empty()) buffer = string_format("Not currently observing any devices"); - m_console.printf("%s\n", buffer.c_str()); + m_console.printf("%s\n", buffer); } - - /* otherwise set the ignore flag on all requested CPUs */ else { + // otherwise set the ignore flag on all requested CPUs device_t *devicelist[MAX_COMMAND_PARAMS]; - /* validate parameters */ + // validate parameters for (int paramnum = 0; paramnum < params.size(); paramnum++) - if (!validate_cpu_parameter(params[paramnum].c_str(), devicelist[paramnum])) + if (!m_console.validate_cpu_parameter(params[paramnum], devicelist[paramnum])) return; - /* clear the ignore flags */ + // clear the ignore flags for (int paramnum = 0; paramnum < params.size(); paramnum++) { devicelist[paramnum]->debug()->ignore(false); @@ -1078,17 +1117,17 @@ void debugger_commands::execute_observe(int ref, const std::vector<std::string> execute_suspend - suspend execution on cpu -------------------------------------------------*/ -void debugger_commands::execute_suspend(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_suspend(const std::vector<std::string_view> ¶ms) { - /* if there are no parameters, dump the ignore list */ + // if there are no parameters, dump the ignore list if (params.empty()) { std::string buffer; - /* loop over all executable devices */ - for (device_execute_interface &exec : execute_interface_iterator(m_machine.root_device())) + // loop over all executable devices + for (device_execute_interface &exec : execute_interface_enumerator(m_machine.root_device())) - /* build up a comma-separated list */ + // build up a comma-separated list if (exec.device().debug()->suspended()) { if (buffer.empty()) @@ -1097,25 +1136,25 @@ void debugger_commands::execute_suspend(int ref, const std::vector<std::string> buffer.append(string_format(", '%s'", exec.device().tag())); } - /* special message for none */ + // special message for none if (buffer.empty()) buffer = string_format("No currently suspended devices"); - m_console.printf("%s\n", buffer.c_str()); + m_console.printf("%s\n", buffer); } else { device_t *devicelist[MAX_COMMAND_PARAMS]; - /* validate parameters */ + // validate parameters for (int paramnum = 0; paramnum < params.size(); paramnum++) - if (!validate_cpu_parameter(params[paramnum].c_str(), devicelist[paramnum])) + if (!m_console.validate_cpu_parameter(params[paramnum], devicelist[paramnum])) return; for (int paramnum = 0; paramnum < params.size(); paramnum++) { - /* make sure this isn't the last live CPU */ + // make sure this isn't the last live CPU bool gotone = false; - for (device_execute_interface &exec : execute_interface_iterator(m_machine.root_device())) + for (device_execute_interface &exec : execute_interface_enumerator(m_machine.root_device())) if (&exec.device() != devicelist[paramnum] && !exec.device().debug()->suspended()) { gotone = true; @@ -1137,17 +1176,17 @@ void debugger_commands::execute_suspend(int ref, const std::vector<std::string> execute_resume - Resume execution on CPU -------------------------------------------------*/ -void debugger_commands::execute_resume(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_resume(const std::vector<std::string_view> ¶ms) { - /* if there are no parameters, dump the ignore list */ + // if there are no parameters, dump the ignore list if (params.empty()) { std::string buffer; - /* loop over all executable devices */ - for (device_execute_interface &exec : execute_interface_iterator(m_machine.root_device())) + // loop over all executable devices + for (device_execute_interface &exec : execute_interface_enumerator(m_machine.root_device())) - /* build up a comma-separated list */ + // build up a comma-separated list if (exec.device().debug()->suspended()) { if (buffer.empty()) @@ -1156,18 +1195,18 @@ void debugger_commands::execute_resume(int ref, const std::vector<std::string> & buffer.append(string_format(", '%s'", exec.device().tag())); } - /* special message for none */ + // special message for none if (buffer.empty()) buffer = string_format("No currently suspended devices"); - m_console.printf("%s\n", buffer.c_str()); + m_console.printf("%s\n", buffer); } else { device_t *devicelist[MAX_COMMAND_PARAMS]; - /* validate parameters */ + // validate parameters for (int paramnum = 0; paramnum < params.size(); paramnum++) - if (!validate_cpu_parameter(params[paramnum].c_str(), devicelist[paramnum])) + if (!m_console.validate_cpu_parameter(params[paramnum], devicelist[paramnum])) return; for (int paramnum = 0; paramnum < params.size(); paramnum++) @@ -1178,32 +1217,56 @@ void debugger_commands::execute_resume(int ref, const std::vector<std::string> & } } +//------------------------------------------------- +// execute_cpulist - list all CPUs +//------------------------------------------------- + +void debugger_commands::execute_cpulist(const std::vector<std::string_view> ¶ms) +{ + int index = 0; + for (device_execute_interface &exec : execute_interface_enumerator(m_machine.root_device())) + { + const device_state_interface *state; + if (exec.device().interface(state) && state->state_find_entry(STATE_GENPCBASE) != nullptr) + m_console.printf("[%s%d] %s\n", &exec.device() == m_console.get_visible_cpu() ? "*" : "", index++, exec.device().tag()); + } +} + +//------------------------------------------------- +// execute_time - execute the time command +//------------------------------------------------- + +void debugger_commands::execute_time(const std::vector<std::string_view> ¶ms) +{ + m_console.printf("%s\n", m_machine.time().as_string()); +} + /*------------------------------------------------- execute_comment - add a comment to a line -------------------------------------------------*/ -void debugger_commands::execute_comment_add(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_comment_add(const std::vector<std::string_view> ¶ms) { - device_t *cpu; + // param 1 is the address for the comment u64 address; - - /* param 1 is the address for the comment */ - if (!validate_number_parameter(params[0], address)) + if (!m_console.validate_number_parameter(params[0], address)) return; - /* CPU parameter is implicit */ - if (!validate_cpu_parameter(nullptr, cpu)) + // CPU parameter is implicit + device_t *cpu; + if (!m_console.validate_cpu_parameter(std::string_view(), cpu)) return; - /* make sure param 2 exists */ + // make sure param 2 exists if (params[1].empty()) { m_console.printf("Error : comment text empty\n"); return; } - /* Now try adding the comment */ - cpu->debug()->comment_add(address, params[1].c_str(), 0x00ff0000); + // Now try adding the comment + std::string const text(params[1]); + cpu->debug()->comment_add(address, text.c_str(), 0x00ff0000); cpu->machine().debug_view().update_all(DVT_DISASSEMBLY); } @@ -1212,57 +1275,56 @@ void debugger_commands::execute_comment_add(int ref, const std::vector<std::stri execute_comment_del - remove a comment from an addr --------------------------------------------------------*/ -void debugger_commands::execute_comment_del(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_comment_del(const std::vector<std::string_view> ¶ms) { - device_t *cpu; + // param 1 can either be a command or the address for the comment u64 address; - - /* param 1 can either be a command or the address for the comment */ - if (!validate_number_parameter(params[0], address)) + if (!m_console.validate_number_parameter(params[0], address)) return; - /* CPU parameter is implicit */ - if (!validate_cpu_parameter(nullptr, cpu)) + // CPU parameter is implicit + device_t *cpu; + if (!m_console.validate_cpu_parameter(std::string_view(), cpu)) return; - /* If it's a number, it must be an address */ - /* The bankoff and cbn will be pulled from what's currently active */ + // If it's a number, it must be an address + // The bankoff and cbn will be pulled from what's currently active cpu->debug()->comment_remove(address); cpu->machine().debug_view().update_all(DVT_DISASSEMBLY); } /** - * @fn void execute_comment_list(running_machine &machine, int ref, int params, const char *param[]) + * @fn void execute_comment_list(const std::vector<std::string_view> ¶ms) * @brief Print current list of comments in debugger * * */ -void debugger_commands::execute_comment_list(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_comment_list(const std::vector<std::string_view> ¶ms) { if (!m_machine.debugger().cpu().comment_load(false)) m_console.printf("Error while parsing XML file\n"); } /** - * @fn void execute_comment_commit(running_machine &machine, int ref, int params, const char *param[]) + * @fn void execute_comment_commit(const std::vector<std::string_view> ¶ms) * @brief Add and Save current list of comments in debugger * */ -void debugger_commands::execute_comment_commit(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_comment_commit(const std::vector<std::string_view> ¶ms) { - execute_comment_add(ref, params); - execute_comment_save(ref, params); + execute_comment_add(params); + execute_comment_save(params); } /*------------------------------------------------- execute_comment - add a comment to a line -------------------------------------------------*/ -void debugger_commands::execute_comment_save(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_comment_save(const std::vector<std::string_view> ¶ms) { - if (m_cpu.comment_save()) + if (m_machine.debugger().cpu().comment_save()) m_console.printf("Comment successfully saved\n"); else m_console.printf("Comment not saved\n"); @@ -1270,7 +1332,7 @@ void debugger_commands::execute_comment_save(int ref, const std::vector<std::str // TODO: add color hex editing capabilities for comments, see below for more info /** - * @fn void execute_comment_color(running_machine &machine, int ref, int params, const char *param[]) + * @fn void execute_comment_color(const std::vector<std::string_view> ¶ms) * @brief Modifies comment given at address $xx with given color * Useful for marking comment with a different color scheme (for example by marking start and end of a given function visually). * @param[in] "address,color" First is the comment address in the current context, color can be hexadecimal or shorthanded to common 1bpp RGB names. @@ -1287,32 +1349,40 @@ void debugger_commands::execute_comment_save(int ref, const std::vector<std::str command -------------------------------------------------*/ -void debugger_commands::execute_bpset(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_bpset(const std::vector<std::string_view> ¶ms) { - device_t *cpu; + // param 1 is the address/CPU u64 address; - int bpnum; - const char *action = nullptr; + address_space *space; + if (!m_console.validate_target_address_parameter(params[0], AS_PROGRAM, space, address)) + return; - /* CPU is implicit */ - if (!validate_cpu_parameter(nullptr, cpu)) + device_execute_interface const *execute; + if (!space->device().interface(execute)) + { + m_console.printf("Device %s is not a CPU\n", space->device().name()); return; + } + device_debug *const debug = space->device().debug(); - /* param 1 is the address */ - if (!validate_number_parameter(params[0], address)) + if (space->spacenum() != AS_PROGRAM) + { + m_console.printf("Only program space breakpoints are supported\n"); return; + } - /* param 2 is the condition */ - parsed_expression condition(&cpu->debug()->symtable()); - if (params.size() > 1 && !debug_command_parameter_expression(params[1], condition)) + // param 2 is the condition + parsed_expression condition(debug->symtable()); + if (params.size() > 1 && !m_console.validate_expression_parameter(params[1], condition)) return; - /* param 3 is the action */ - if (params.size() > 2 && !debug_command_parameter_command(action = params[2].c_str())) + // param 3 is the action + std::string_view action; + if (params.size() > 2 && !m_console.validate_command_parameter(action = params[2])) return; - /* set the breakpoint */ - bpnum = cpu->debug()->breakpoint_set(address, (condition.is_empty()) ? nullptr : condition.original_string(), action); + // set the breakpoint + int const bpnum = debug->breakpoint_set(address, condition.is_empty() ? nullptr : condition.original_string(), action); m_console.printf("Breakpoint %X set\n", bpnum); } @@ -1322,31 +1392,26 @@ void debugger_commands::execute_bpset(int ref, const std::vector<std::string> &p clear command -------------------------------------------------*/ -void debugger_commands::execute_bpclear(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_bpclear(const std::vector<std::string_view> ¶ms) { - u64 bpindex; - - /* if 0 parameters, clear all */ - if (params.empty()) + if (params.empty()) // if no parameters, clear all { - for (device_t &device : device_iterator(m_machine.root_device())) + for (device_t &device : device_enumerator(m_machine.root_device())) device.debug()->breakpoint_clear_all(); m_console.printf("Cleared all breakpoints\n"); } - - /* otherwise, clear the specific one */ - else if (!validate_number_parameter(params[0], bpindex)) - return; - else + else // otherwise, clear the specific ones { - bool found = false; - for (device_t &device : device_iterator(m_machine.root_device())) - if (device.debug()->breakpoint_clear(bpindex)) - found = true; - if (found) - m_console.printf("Breakpoint %X cleared\n", u32(bpindex)); - else - m_console.printf("Invalid breakpoint number %X\n", u32(bpindex)); + execute_index_command( + params, + [this] (device_t &device, u64 param) -> bool + { + if (!device.debug()->breakpoint_clear(param)) + return false; + m_console.printf("Breakpoint %X cleared\n", param); + return true; + }, + "Invalid breakpoint number %X\n"); } } @@ -1356,34 +1421,26 @@ void debugger_commands::execute_bpclear(int ref, const std::vector<std::string> disable/enable commands -------------------------------------------------*/ -void debugger_commands::execute_bpdisenable(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_bpdisenable(bool enable, const std::vector<std::string_view> ¶ms) { - u64 bpindex; - - /* if 0 parameters, clear all */ - if (params.empty()) + if (params.empty()) // if no parameters, disable/enable all { - for (device_t &device : device_iterator(m_machine.root_device())) - device.debug()->breakpoint_enable_all(ref); - if (ref == 0) - m_console.printf("Disabled all breakpoints\n"); - else - m_console.printf("Enabled all breakpoints\n"); + for (device_t &device : device_enumerator(m_machine.root_device())) + device.debug()->breakpoint_enable_all(enable); + m_console.printf(enable ? "Enabled all breakpoints\n" : "Disabled all breakpoints\n"); } - - /* otherwise, clear the specific one */ - else if (!validate_number_parameter(params[0], bpindex)) - return; - else + else // otherwise, disable/enable the specific ones { - bool found = false; - for (device_t &device : device_iterator(m_machine.root_device())) - if (device.debug()->breakpoint_enable(bpindex, ref)) - found = true; - if (found) - m_console.printf("Breakpoint %X %s\n", u32(bpindex), ref ? "enabled" : "disabled"); - else - m_console.printf("Invalid breakpoint number %X\n", u32(bpindex)); + execute_index_command( + params, + [this, enable] (device_t &device, u64 param) -> bool + { + if (!device.debug()->breakpoint_enable(param, enable)) + return false; + m_console.printf(enable ? "Breakpoint %X enabled\n" : "Breakpoint %X disabled\n", param); + return true; + }, + "Invalid breakpoint number %X\n"); } } @@ -1393,32 +1450,49 @@ void debugger_commands::execute_bpdisenable(int ref, const std::vector<std::stri command -------------------------------------------------*/ -void debugger_commands::execute_bplist(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_bplist(const std::vector<std::string_view> ¶ms) { int printed = 0; std::string buffer; - - /* loop over all CPUs */ - for (device_t &device : device_iterator(m_machine.root_device())) - if (!device.debug()->breakpoint_list().empty()) - { - m_console.printf("Device '%s' breakpoints:\n", device.tag()); - - /* loop over the breakpoints */ - for (const device_debug::breakpoint &bp : device.debug()->breakpoint_list()) + auto const apply = + [this, &printed, &buffer] (device_t &device) { - buffer = string_format("%c%4X @ %0*X", bp.enabled() ? ' ' : 'D', bp.index(), device.debug()->logaddrchars(), bp.address()); - if (std::string(bp.condition()).compare("1") != 0) - buffer.append(string_format(" if %s", bp.condition())); - if (std::string(bp.action()).compare("") != 0) - buffer.append(string_format(" do %s", bp.action())); - m_console.printf("%s\n", buffer.c_str()); - printed++; - } - } + if (!device.debug()->breakpoint_list().empty()) + { + m_console.printf("Device '%s' breakpoints:\n", device.tag()); - if (printed == 0) - m_console.printf("No breakpoints currently installed\n"); + // loop over the breakpoints + for (const auto &bpp : device.debug()->breakpoint_list()) + { + debug_breakpoint &bp = *bpp.second; + buffer = string_format("%c%4X @ %0*X", bp.enabled() ? ' ' : 'D', bp.index(), device.debug()->logaddrchars(), bp.address()); + if (std::string(bp.condition()).compare("1") != 0) + buffer.append(string_format(" if %s", bp.condition())); + if (std::string(bp.action()).compare("") != 0) + buffer.append(string_format(" do %s", bp.action())); + m_console.printf("%s\n", buffer); + printed++; + } + } + }; + + if (!params.empty()) + { + device_t *cpu; + if (!m_console.validate_cpu_parameter(params[0], cpu)) + return; + apply(*cpu); + if (!printed) + m_console.printf("No breakpoints currently installed for CPU %s\n", cpu->tag()); + } + else + { + // loop over all CPUs + for (device_t &device : device_enumerator(m_machine.root_device())) + apply(device); + if (!printed) + m_console.printf("No breakpoints currently installed\n"); + } } @@ -1427,50 +1501,57 @@ void debugger_commands::execute_bplist(int ref, const std::vector<std::string> & command -------------------------------------------------*/ -void debugger_commands::execute_wpset(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_wpset(int spacenum, const std::vector<std::string_view> ¶ms) { - address_space *space; - const char *action = nullptr; u64 address, length; - read_or_write type; - int wpnum; + address_space *space; - /* CPU is implicit */ - if (!validate_cpu_space_parameter(nullptr, ref, space)) + // param 1 is the address/CPU + if (!m_console.validate_target_address_parameter(params[0], spacenum, space, address)) return; - /* param 1 is the address */ - if (!validate_number_parameter(params[0], address)) + device_execute_interface const *execute; + if (!space->device().interface(execute)) + { + m_console.printf("Device %s is not a CPU\n", space->device().name()); return; + } + device_debug *const debug = space->device().debug(); - /* param 2 is the length */ - if (!validate_number_parameter(params[1], length)) + // param 2 is the length + if (!m_console.validate_number_parameter(params[1], length)) return; - /* param 3 is the type */ - if (params[2] == "r") - type = read_or_write::READ; - else if (params[2] == "w") - type = read_or_write::WRITE; - else if (params[2] == "rw" || params[2] == "wr") - type = read_or_write::READWRITE; - else + // param 3 is the type + read_or_write type; { - m_console.printf("Invalid watchpoint type: expected r, w, or rw\n"); - return; + using util::streqlower; + using namespace std::literals; + if (streqlower(params[2], "r"sv)) + type = read_or_write::READ; + else if (streqlower(params[2], "w"sv)) + type = read_or_write::WRITE; + else if (streqlower(params[2], "rw"sv) || streqlower(params[2], "wr"sv)) + type = read_or_write::READWRITE; + else + { + m_console.printf("Invalid watchpoint type: expected r, w, or rw\n"); + return; + } } - /* param 4 is the condition */ - parsed_expression condition(&space->device().debug()->symtable()); - if (params.size() > 3 && !debug_command_parameter_expression(params[3], condition)) + // param 4 is the condition + parsed_expression condition(debug->symtable()); + if (params.size() > 3 && !m_console.validate_expression_parameter(params[3], condition)) return; - /* param 5 is the action */ - if (params.size() > 4 && !debug_command_parameter_command(action = params[4].c_str())) + // param 5 is the action + std::string_view action; + if (params.size() > 4 && !m_console.validate_command_parameter(action = params[4])) return; - /* set the watchpoint */ - wpnum = space->device().debug()->watchpoint_set(*space, type, address, length, (condition.is_empty()) ? nullptr : condition.original_string(), action); + // set the watchpoint + int const wpnum = debug->watchpoint_set(*space, type, address, length, (condition.is_empty()) ? nullptr : condition.original_string(), action); m_console.printf("Watchpoint %X set\n", wpnum); } @@ -1480,31 +1561,26 @@ void debugger_commands::execute_wpset(int ref, const std::vector<std::string> &p clear command -------------------------------------------------*/ -void debugger_commands::execute_wpclear(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_wpclear(const std::vector<std::string_view> ¶ms) { - u64 wpindex; - - /* if 0 parameters, clear all */ - if (params.empty()) + if (params.empty()) // if no parameters, clear all { - for (device_t &device : device_iterator(m_machine.root_device())) + for (device_t &device : device_enumerator(m_machine.root_device())) device.debug()->watchpoint_clear_all(); m_console.printf("Cleared all watchpoints\n"); } - - /* otherwise, clear the specific one */ - else if (!validate_number_parameter(params[0], wpindex)) - return; - else + else // otherwise, clear the specific ones { - bool found = false; - for (device_t &device : device_iterator(m_machine.root_device())) - if (device.debug()->watchpoint_clear(wpindex)) - found = true; - if (found) - m_console.printf("Watchpoint %X cleared\n", u32(wpindex)); - else - m_console.printf("Invalid watchpoint number %X\n", u32(wpindex)); + execute_index_command( + params, + [this] (device_t &device, u64 param) -> bool + { + if (!device.debug()->watchpoint_clear(param)) + return false; + m_console.printf("Watchpoint %X cleared\n", param); + return true; + }, + "Invalid watchpoint number %X\n"); } } @@ -1514,34 +1590,26 @@ void debugger_commands::execute_wpclear(int ref, const std::vector<std::string> disable/enable commands -------------------------------------------------*/ -void debugger_commands::execute_wpdisenable(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_wpdisenable(bool enable, const std::vector<std::string_view> ¶ms) { - u64 wpindex; - - /* if 0 parameters, clear all */ - if (params.empty()) + if (params.empty()) // if no parameters, disable/enable all { - for (device_t &device : device_iterator(m_machine.root_device())) - device.debug()->watchpoint_enable_all(ref); - if (ref == 0) - m_console.printf("Disabled all watchpoints\n"); - else - m_console.printf("Enabled all watchpoints\n"); + for (device_t &device : device_enumerator(m_machine.root_device())) + device.debug()->watchpoint_enable_all(enable); + m_console.printf(enable ? "Enabled all watchpoints\n" : "Disabled all watchpoints\n"); } - - /* otherwise, clear the specific one */ - else if (!validate_number_parameter(params[0], wpindex)) - return; - else + else // otherwise, disable/enable the specific ones { - bool found = false; - for (device_t &device : device_iterator(m_machine.root_device())) - if (device.debug()->watchpoint_enable(wpindex, ref)) - found = true; - if (found) - m_console.printf("Watchpoint %X %s\n", u32(wpindex), ref ? "enabled" : "disabled"); - else - m_console.printf("Invalid watchpoint number %X\n", u32(wpindex)); + execute_index_command( + params, + [this, enable] (device_t &device, u64 param) -> bool + { + if (!device.debug()->watchpoint_enable(param, enable)) + return false; + m_console.printf(enable ? "Watchpoint %X enabled\n" : "Watchpoint %X disabled\n", param); + return true; + }, + "Invalid watchpoint number %X\n"); } } @@ -1551,39 +1619,61 @@ void debugger_commands::execute_wpdisenable(int ref, const std::vector<std::stri command -------------------------------------------------*/ -void debugger_commands::execute_wplist(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_wplist(const std::vector<std::string_view> ¶ms) { int printed = 0; std::string buffer; - - /* loop over all CPUs */ - for (device_t &device : device_iterator(m_machine.root_device())) - for (int spacenum = 0; spacenum < device.debug()->watchpoint_space_count(); ++spacenum) - if (!device.debug()->watchpoint_vector(spacenum).empty()) + auto const apply = + [this, &printed, &buffer] (device_t &device) { - static const char *const types[] = { "unkn ", "read ", "write", "r/w " }; + for (int spacenum = 0; spacenum < device.debug()->watchpoint_space_count(); ++spacenum) + { + if (!device.debug()->watchpoint_vector(spacenum).empty()) + { + static const char *const types[] = { "unkn ", "read ", "write", "r/w " }; - m_console.printf("Device '%s' %s space watchpoints:\n", device.tag(), - device.debug()->watchpoint_vector(spacenum).front()->space().name()); + m_console.printf( + "Device '%s' %s space watchpoints:\n", + device.tag(), + device.debug()->watchpoint_vector(spacenum).front()->space().name()); - /* loop over the watchpoints */ - for (const auto &wp : device.debug()->watchpoint_vector(spacenum)) - { - buffer = string_format("%c%4X @ %0*X-%0*X %s", wp->enabled() ? ' ' : 'D', wp->index(), - wp->space().addrchars(), wp->address(), - wp->space().addrchars(), wp->address() + wp->length() - 1, - types[int(wp->type())]); - if (std::string(wp->condition()).compare("1") != 0) - buffer.append(string_format(" if %s", wp->condition())); - if (std::string(wp->action()).compare("") != 0) - buffer.append(string_format(" do %s", wp->action())); - m_console.printf("%s\n", buffer.c_str()); - printed++; + // loop over the watchpoints + for (const auto &wp : device.debug()->watchpoint_vector(spacenum)) + { + buffer = string_format( + "%c%4X @ %0*X-%0*X %s", + wp->enabled() ? ' ' : 'D', wp->index(), + wp->space().addrchars(), wp->address(), + wp->space().addrchars(), wp->address() + wp->length() - 1, + types[int(wp->type())]); + if (std::string(wp->condition()).compare("1") != 0) + buffer.append(string_format(" if %s", wp->condition())); + if (std::string(wp->action()).compare("") != 0) + buffer.append(string_format(" do %s", wp->action())); + m_console.printf("%s\n", buffer); + printed++; + } + } } - } + }; - if (printed == 0) - m_console.printf("No watchpoints currently installed\n"); + if (!params.empty()) + { + device_t *cpu; + if (!m_console.validate_cpu_parameter(params[0], cpu)) + return; + apply(*cpu); + if (!printed) + m_console.printf("No watchpoints currently installed for CPU %s\n", cpu->tag()); + } + else + { + // loop over all CPUs + for (device_t &device : device_enumerator(m_machine.root_device())) + apply(device); + if (!printed) + m_console.printf("No watchpoints currently installed\n"); + } } @@ -1592,28 +1682,26 @@ void debugger_commands::execute_wplist(int ref, const std::vector<std::string> & command -------------------------------------------------*/ -void debugger_commands::execute_rpset(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_rpset(const std::vector<std::string_view> ¶ms) { + // CPU is implicit device_t *cpu; - const char *action = nullptr; - int bpnum; - - /* CPU is implicit */ - if (!validate_cpu_parameter(nullptr, cpu)) + if (!m_console.validate_cpu_parameter(std::string_view(), cpu)) return; - /* param 1 is the condition */ - parsed_expression condition(&cpu->debug()->symtable()); - if (params.size() > 0 && !debug_command_parameter_expression(params[0], condition)) + // param 1 is the condition + parsed_expression condition(cpu->debug()->symtable()); + if (params.size() > 0 && !m_console.validate_expression_parameter(params[0], condition)) return; - /* param 2 is the action */ - if (params.size() > 1 && !debug_command_parameter_command(action = params[1].c_str())) + // param 2 is the action + std::string_view action; + if (params.size() > 1 && !m_console.validate_command_parameter(action = params[1])) return; - /* set the breakpoint */ - bpnum = cpu->debug()->registerpoint_set(condition.original_string(), action); - m_console.printf("Registerpoint %X set\n", bpnum); + // set the registerpoint + int const rpnum = cpu->debug()->registerpoint_set(condition.original_string(), action); + m_console.printf("Registerpoint %X set\n", rpnum); } @@ -1622,31 +1710,26 @@ void debugger_commands::execute_rpset(int ref, const std::vector<std::string> &p clear command -------------------------------------------------*/ -void debugger_commands::execute_rpclear(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_rpclear(const std::vector<std::string_view> ¶ms) { - u64 rpindex; - - /* if 0 parameters, clear all */ - if (params.empty()) + if (params.empty()) // if no parameters, clear all { - for (device_t &device : device_iterator(m_machine.root_device())) + for (device_t &device : device_enumerator(m_machine.root_device())) device.debug()->registerpoint_clear_all(); m_console.printf("Cleared all registerpoints\n"); } - - /* otherwise, clear the specific one */ - else if (!validate_number_parameter(params[0], rpindex)) - return; - else + else // otherwise, clear the specific ones { - bool found = false; - for (device_t &device : device_iterator(m_machine.root_device())) - if (device.debug()->registerpoint_clear(rpindex)) - found = true; - if (found) - m_console.printf("Registerpoint %X cleared\n", u32(rpindex)); - else - m_console.printf("Invalid registerpoint number %X\n", u32(rpindex)); + execute_index_command( + params, + [this] (device_t &device, u64 param) -> bool + { + if (!device.debug()->registerpoint_clear(param)) + return false; + m_console.printf("Registerpoint %X cleared\n", param); + return true; + }, + "Invalid registerpoint number %X\n"); } } @@ -1656,110 +1739,217 @@ void debugger_commands::execute_rpclear(int ref, const std::vector<std::string> disable/enable commands -------------------------------------------------*/ -void debugger_commands::execute_rpdisenable(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_rpdisenable(bool enable, const std::vector<std::string_view> ¶ms) { - u64 rpindex; - - /* if 0 parameters, clear all */ - if (params.empty()) + if (params.empty()) // if no parameters, disable/enable all { - for (device_t &device : device_iterator(m_machine.root_device())) - device.debug()->registerpoint_enable_all(ref); - if (ref == 0) - m_console.printf("Disabled all registerpoints\n"); - else - m_console.printf("Enabled all registeroints\n"); + for (device_t &device : device_enumerator(m_machine.root_device())) + device.debug()->registerpoint_enable_all(enable); + m_console.printf(enable ? "Enabled all registerpoints\n" : "Disabled all registerpoints\n"); + } + else // otherwise, disable/enable the specific ones + { + execute_index_command( + params, + [this, enable] (device_t &device, u64 param) -> bool + { + if (!device.debug()->registerpoint_enable(param, enable)) + return false; + m_console.printf(enable ? "Registerpoint %X enabled\n" : "Breakpoint %X disabled\n", param); + return true; + }, + "Invalid registerpoint number %X\n"); } +} + - /* otherwise, clear the specific one */ - else if (!validate_number_parameter(params[0], rpindex)) +//------------------------------------------------- +// execute_epset - execute the exception point +// set command +//------------------------------------------------- + +void debugger_commands::execute_epset(const std::vector<std::string_view> ¶ms) +{ + // CPU is implicit + device_t *cpu; + if (!m_console.validate_cpu_parameter(std::string_view(), cpu)) return; - else + + // param 1 is the exception type + u64 type; + if (!m_console.validate_number_parameter(params[0], type)) + return; + + // param 2 is the condition + parsed_expression condition(cpu->debug()->symtable()); + if (params.size() > 1 && !m_console.validate_expression_parameter(params[1], condition)) + return; + + // param 3 is the action + std::string_view action; + if (params.size() > 2 && !m_console.validate_command_parameter(action = params[2])) + return; + + // set the exception point + int epnum = cpu->debug()->exceptionpoint_set(type, (condition.is_empty()) ? nullptr : condition.original_string(), action); + m_console.printf("Exception point %X set\n", epnum); +} + + +//------------------------------------------------- +// execute_epclear - execute the exception point +// clear command +//------------------------------------------------- + +void debugger_commands::execute_epclear(const std::vector<std::string_view> ¶ms) +{ + if (params.empty()) // if no parameters, clear all { - bool found = false; - for (device_t &device : device_iterator(m_machine.root_device())) - if (device.debug()->registerpoint_enable(rpindex, ref)) - found = true; - if (found) - m_console.printf("Registerpoint %X %s\n", u32(rpindex), ref ? "enabled" : "disabled"); - else - m_console.printf("Invalid registerpoint number %X\n", u32(rpindex)); + for (device_t &device : device_enumerator(m_machine.root_device())) + device.debug()->exceptionpoint_clear_all(); + m_console.printf("Cleared all exception points\n"); + } + else // otherwise, clear the specific ones + { + execute_index_command( + params, + [this] (device_t &device, u64 param) -> bool + { + if (!device.debug()->exceptionpoint_clear(param)) + return false; + m_console.printf("Exception point %X cleared\n", param); + return true; + }, + "Invalid exception point number %X\n"); } } -/*------------------------------------------------- - execute_rplist - execute the registerpoint list - command --------------------------------------------------*/ +//------------------------------------------------- +// execute_epdisenable - execute the exception +// point disable/enable commands +//------------------------------------------------- -void debugger_commands::execute_rplist(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_epdisenable(bool enable, const std::vector<std::string_view> ¶ms) { - int printed = 0; - std::string buffer; + if (params.empty()) // if no parameters, disable/enable all + { + for (device_t &device : device_enumerator(m_machine.root_device())) + device.debug()->exceptionpoint_enable_all(enable); + m_console.printf(enable ? "Enabled all exception points\n" : "Disabled all exception points\n"); + } + else // otherwise, disable/enable the specific ones + { + execute_index_command( + params, + [this, enable] (device_t &device, u64 param) -> bool + { + if (!device.debug()->exceptionpoint_enable(param, enable)) + return false; + m_console.printf(enable ? "Exception point %X enabled\n" : "Exception point %X disabled\n", param); + return true; + }, + "Invalid exception point number %X\n"); + } +} - /* loop over all CPUs */ - for (device_t &device : device_iterator(m_machine.root_device())) - if (!device.debug()->registerpoint_list().empty()) - { - m_console.printf("Device '%s' registerpoints:\n", device.tag()); - /* loop over the breakpoints */ - for (const device_debug::registerpoint &rp : device.debug()->registerpoint_list()) +//------------------------------------------------- +// execute_eplist - execute the exception point +// list command +//------------------------------------------------- + +void debugger_commands::execute_eplist(const std::vector<std::string_view> ¶ms) +{ + int printed = 0; + std::string buffer; + auto const apply = + [this, &printed, &buffer] (device_t &device) { - buffer = string_format("%c%4X if %s", rp.enabled() ? ' ' : 'D', rp.index(), rp.condition()); - if (rp.action() != nullptr) - buffer.append(string_format(" do %s", rp.action())); - m_console.printf("%s\n", buffer.c_str()); - printed++; - } - } + if (!device.debug()->exceptionpoint_list().empty()) + { + m_console.printf("Device '%s' exception points:\n", device.tag()); + + // loop over the exception points + for (const auto &epp : device.debug()->exceptionpoint_list()) + { + debug_exceptionpoint &ep = *epp.second; + buffer = string_format("%c%4X : %X", ep.enabled() ? ' ' : 'D', ep.index(), ep.type()); + if (std::string(ep.condition()).compare("1") != 0) + buffer.append(string_format(" if %s", ep.condition())); + if (!ep.action().empty()) + buffer.append(string_format(" do %s", ep.action())); + m_console.printf("%s\n", buffer); + printed++; + } + } + }; - if (printed == 0) - m_console.printf("No registerpoints currently installed\n"); + if (!params.empty()) + { + device_t *cpu; + if (!m_console.validate_cpu_parameter(params[0], cpu)) + return; + apply(*cpu); + if (!printed) + m_console.printf("No exception points currently installed for CPU %s\n", cpu->tag()); + } + else + { + // loop over all CPUs + for (device_t &device : device_enumerator(m_machine.root_device())) + apply(device); + if (!printed) + m_console.printf("No exception points currently installed\n"); + } } /*------------------------------------------------- - execute_hotspot - execute the hotspot + execute_rplist - execute the registerpoint list command -------------------------------------------------*/ -void debugger_commands::execute_hotspot(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_rplist(const std::vector<std::string_view> ¶ms) { - /* if no params, and there are live hotspots, clear them */ - if (params.empty()) - { - bool cleared = false; - - /* loop over CPUs and find live spots */ - for (device_t &device : device_iterator(m_machine.root_device())) - if (device.debug()->hotspot_tracking_enabled()) + int printed = 0; + std::string buffer; + auto const apply = + [this, &printed, &buffer] (device_t &device) { - device.debug()->hotspot_track(0, 0); - m_console.printf("Cleared hotspot tracking on CPU '%s'\n", device.tag()); - cleared = true; - } + if (!device.debug()->registerpoint_list().empty()) + { + m_console.printf("Device '%s' registerpoints:\n", device.tag()); - /* if we cleared, we're done */ - if (cleared) + // loop over the registerpoints + for (const auto &rp : device.debug()->registerpoint_list()) + { + buffer = string_format("%c%4X if %s", rp.enabled() ? ' ' : 'D', rp.index(), rp.condition()); + if (!rp.action().empty()) + buffer.append(string_format(" do %s", rp.action())); + m_console.printf("%s\n", buffer); + printed++; + } + } + }; + + if (!params.empty()) + { + device_t *cpu; + if (!m_console.validate_cpu_parameter(params[0], cpu)) return; + apply(*cpu); + if (!printed) + m_console.printf("No registerpoints currently installed for CPU %s\n", cpu->tag()); + } + else + { + // loop over all CPUs + for (device_t &device : device_enumerator(m_machine.root_device())) + apply(device); + if (!printed) + m_console.printf("No registerpoints currently installed\n"); } - - /* extract parameters */ - device_t *device = nullptr; - if (!validate_cpu_parameter(!params.empty() ? params[0].c_str() : nullptr, device)) - return; - u64 count = 64; - if (params.size() > 1 && !validate_number_parameter(params[1], count)) - return; - u64 threshhold = 250; - if (params.size() > 2 && !validate_number_parameter(params[2], threshhold)) - return; - - /* attempt to install */ - device->debug()->hotspot_track(count, threshhold); - m_console.printf("Now tracking hotspots on CPU '%s' using %d slots with a threshold of %d\n", device->tag(), (int)count, (int)threshhold); } @@ -1767,10 +1957,9 @@ void debugger_commands::execute_hotspot(int ref, const std::vector<std::string> execute_statesave - execute the statesave command -------------------------------------------------*/ -void debugger_commands::execute_statesave(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_statesave(const std::vector<std::string_view> ¶ms) { - const std::string &filename(params[0]); - m_machine.immediate_save(filename.c_str()); + m_machine.immediate_save(params[0]); m_console.printf("State save attempted. Please refer to window message popup for results.\n"); } @@ -1779,13 +1968,12 @@ void debugger_commands::execute_statesave(int ref, const std::vector<std::string execute_stateload - execute the stateload command -------------------------------------------------*/ -void debugger_commands::execute_stateload(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_stateload(const std::vector<std::string_view> ¶ms) { - const std::string &filename(params[0]); - m_machine.immediate_load(filename.c_str()); + m_machine.immediate_load(params[0]); // clear all PC & memory tracks - for (device_t &device : device_iterator(m_machine.root_device())) + for (device_t &device : device_enumerator(m_machine.root_device())) { device.debug()->track_pc_data_clear(); device.debug()->track_mem_data_clear(); @@ -1798,12 +1986,12 @@ void debugger_commands::execute_stateload(int ref, const std::vector<std::string execute_rewind - execute the rewind command -------------------------------------------------*/ -void debugger_commands::execute_rewind(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_rewind(const std::vector<std::string_view> ¶ms) { bool success = m_machine.rewind_step(); if (success) // clear all PC & memory tracks - for (device_t &device : device_iterator(m_machine.root_device())) + for (device_t &device : device_enumerator(m_machine.root_device())) { device.debug()->track_pc_data_clear(); device.debug()->track_mem_data_clear(); @@ -1817,34 +2005,32 @@ void debugger_commands::execute_rewind(int ref, const std::vector<std::string> & execute_save - execute the save command -------------------------------------------------*/ -void debugger_commands::execute_save(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_save(int spacenum, const std::vector<std::string_view> ¶ms) { u64 offset, endoffset, length; address_space *space; - FILE *f; - /* validate parameters */ - if (!validate_number_parameter(params[1], offset)) - return; - if (!validate_number_parameter(params[2], length)) + // validate parameters + if (!m_console.validate_target_address_parameter(params[1], spacenum, space, offset)) return; - if (!validate_cpu_space_parameter(params.size() > 3 ? params[3].c_str() : nullptr, ref, space)) + if (!m_console.validate_number_parameter(params[2], length)) return; - /* determine the addresses to write */ + // determine the addresses to write endoffset = (offset + length - 1) & space->addrmask(); offset = offset & space->addrmask(); - endoffset ++; + endoffset++; - /* open the file */ - f = fopen(params[0].c_str(), "wb"); + // open the file + std::string const filename(params[0]); + FILE *const f = fopen(filename.c_str(), "wb"); if (!f) { - m_console.printf("Error opening file '%s'\n", params[0].c_str()); + m_console.printf("Error opening file '%s'\n", params[0]); return; } - /* now write the data out */ + // now write the data out auto dis = space->device().machine().disable_side_effects(); switch (space->addr_shift()) { @@ -1852,8 +2038,9 @@ void debugger_commands::execute_save(int ref, const std::vector<std::string> &pa for (u64 i = offset; i != endoffset; i++) { offs_t curaddr = i; - u64 data = space->device().memory().translate(space->spacenum(), TRANSLATE_READ_DEBUG, curaddr) ? - space->read_qword(curaddr) : space->unmap(); + address_space *tspace; + u64 data = space->device().memory().translate(space->spacenum(), device_memory_interface::TR_READ, curaddr, tspace) ? + tspace->read_qword(curaddr) : space->unmap(); fwrite(&data, 8, 1, f); } break; @@ -1861,8 +2048,9 @@ void debugger_commands::execute_save(int ref, const std::vector<std::string> &pa for (u64 i = offset; i != endoffset; i++) { offs_t curaddr = i; - u32 data = space->device().memory().translate(space->spacenum(), TRANSLATE_READ_DEBUG, curaddr) ? - space->read_dword(curaddr) : space->unmap(); + address_space *tspace; + u32 data = space->device().memory().translate(space->spacenum(), device_memory_interface::TR_READ, curaddr, tspace) ? + tspace->read_dword(curaddr) : space->unmap(); fwrite(&data, 4, 1, f); } break; @@ -1870,8 +2058,9 @@ void debugger_commands::execute_save(int ref, const std::vector<std::string> &pa for (u64 i = offset; i != endoffset; i++) { offs_t curaddr = i; - u16 data = space->device().memory().translate(space->spacenum(), TRANSLATE_READ_DEBUG, curaddr) ? - space->read_word(curaddr) : space->unmap(); + address_space *tspace; + u16 data = space->device().memory().translate(space->spacenum(), device_memory_interface::TR_READ, curaddr, tspace) ? + tspace->read_word(curaddr) : space->unmap(); fwrite(&data, 2, 1, f); } break; @@ -1879,8 +2068,9 @@ void debugger_commands::execute_save(int ref, const std::vector<std::string> &pa for (u64 i = offset; i != endoffset; i++) { offs_t curaddr = i; - u8 data = space->device().memory().translate(space->spacenum(), TRANSLATE_READ_DEBUG, curaddr) ? - space->read_byte(curaddr) : space->unmap(); + address_space *tspace; + u8 data = space->device().memory().translate(space->spacenum(), device_memory_interface::TR_READ, curaddr, tspace) ? + tspace->read_byte(curaddr) : space->unmap(); fwrite(&data, 1, 1, f); } break; @@ -1890,14 +2080,55 @@ void debugger_commands::execute_save(int ref, const std::vector<std::string> &pa for (u64 i = offset; i != endoffset; i+=16) { offs_t curaddr = i; - u16 data = space->device().memory().translate(space->spacenum(), TRANSLATE_READ_DEBUG, curaddr) ? - space->read_word(curaddr) : space->unmap(); + address_space *tspace; + u16 data = space->device().memory().translate(space->spacenum(), device_memory_interface::TR_READ, curaddr, tspace) ? + tspace->read_word(curaddr) : space->unmap(); fwrite(&data, 2, 1, f); } break; } - /* close the file */ + // close the file + fclose(f); + m_console.printf("Data saved successfully\n"); +} + + +/*------------------------------------------------- + execute_saveregion - execute the save command on region memory +-------------------------------------------------*/ + +void debugger_commands::execute_saveregion(const std::vector<std::string_view> ¶ms) +{ + u64 offset, length; + memory_region *region; + + // validate parameters + if (!m_console.validate_number_parameter(params[1], offset)) + return; + if (!m_console.validate_number_parameter(params[2], length)) + return; + if (!m_console.validate_memory_region_parameter(params[3], region)) + return; + + if (offset >= region->bytes()) + { + m_console.printf("Invalid offset\n"); + return; + } + if ((length <= 0) || ((length + offset) >= region->bytes())) + length = region->bytes() - offset; + + /* open the file */ + std::string const filename(params[0]); + FILE *f = fopen(filename.c_str(), "wb"); + if (!f) + { + m_console.printf("Error opening file '%s'\n", params[0]); + return; + } + fwrite(region->base() + offset, 1, length, f); + fclose(f); m_console.printf("Data saved successfully\n"); } @@ -1907,25 +2138,24 @@ void debugger_commands::execute_save(int ref, const std::vector<std::string> &pa execute_load - execute the load command -------------------------------------------------*/ -void debugger_commands::execute_load(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_load(int spacenum, const std::vector<std::string_view> ¶ms) { u64 offset, endoffset, length = 0; address_space *space; // validate parameters - if (!validate_number_parameter(params[1], offset)) - return; - if (params.size() > 2 && !validate_number_parameter(params[2], length)) + if (!m_console.validate_target_address_parameter(params[1], spacenum, space, offset)) return; - if (!validate_cpu_space_parameter((params.size() > 3) ? params[3].c_str() : nullptr, ref, space)) + if (params.size() > 2 && !m_console.validate_number_parameter(params[2], length)) return; // open the file std::ifstream f; - f.open(params[0], std::ifstream::in | std::ifstream::binary); + std::string const fname(params[0]); + f.open(fname, std::ifstream::in | std::ifstream::binary); if (f.fail()) { - m_console.printf("Error opening file '%s'\n", params[0].c_str()); + m_console.printf("Error opening file '%s'\n", params[0]); return; } @@ -1945,7 +2175,9 @@ void debugger_commands::execute_load(int ref, const std::vector<std::string> &pa endoffset = (offset + length - 1) & space->addrmask(); offset = offset & space->addrmask(); u64 i = 0; + // now read the data in, ignore endoffset and load entire file if length has been set to zero (offset-1) + auto dis = space->device().machine().disable_side_effects(); switch (space->addr_shift()) { case -3: @@ -1954,8 +2186,9 @@ void debugger_commands::execute_load(int ref, const std::vector<std::string> &pa offs_t curaddr = i; u64 data; f.read((char *)&data, 8); - if (f && space->device().memory().translate(space->spacenum(), TRANSLATE_WRITE_DEBUG, curaddr)) - space->write_qword(curaddr, data); + address_space *tspace; + if (f && space->device().memory().translate(space->spacenum(), device_memory_interface::TR_WRITE, curaddr, tspace)) + tspace->write_qword(curaddr, data); } break; case -2: @@ -1964,8 +2197,9 @@ void debugger_commands::execute_load(int ref, const std::vector<std::string> &pa offs_t curaddr = i; u32 data; f.read((char *)&data, 4); - if (f && space->device().memory().translate(space->spacenum(), TRANSLATE_WRITE_DEBUG, curaddr)) - space->write_dword(curaddr, data); + address_space *tspace; + if (f && space->device().memory().translate(space->spacenum(), device_memory_interface::TR_WRITE, curaddr, tspace)) + tspace->write_dword(curaddr, data); } break; case -1: @@ -1974,8 +2208,9 @@ void debugger_commands::execute_load(int ref, const std::vector<std::string> &pa offs_t curaddr = i; u16 data; f.read((char *)&data, 2); - if (f && space->device().memory().translate(space->spacenum(), TRANSLATE_WRITE_DEBUG, curaddr)) - space->write_word(curaddr, data); + address_space *tspace; + if (f && space->device().memory().translate(space->spacenum(), device_memory_interface::TR_WRITE, curaddr, tspace)) + tspace->write_word(curaddr, data); } break; case 0: @@ -1984,8 +2219,9 @@ void debugger_commands::execute_load(int ref, const std::vector<std::string> &pa offs_t curaddr = i; u8 data; f.read((char *)&data, 1); - if (f && space->device().memory().translate(space->spacenum(), TRANSLATE_WRITE_DEBUG, curaddr)) - space->write_byte(curaddr, data); + address_space *tspace; + if (f && space->device().memory().translate(space->spacenum(), device_memory_interface::TR_WRITE, curaddr, tspace)) + tspace->write_byte(curaddr, data); } break; case 3: @@ -1996,8 +2232,9 @@ void debugger_commands::execute_load(int ref, const std::vector<std::string> &pa offs_t curaddr = i; u16 data; f.read((char *)&data, 2); - if (f && space->device().memory().translate(space->spacenum(), TRANSLATE_WRITE_DEBUG, curaddr)) - space->write_word(curaddr, data); + address_space *tspace; + if (f && space->device().memory().translate(space->spacenum(), device_memory_interface::TR_WRITE, curaddr, tspace)) + tspace->write_word(curaddr, data); } break; } @@ -2012,40 +2249,86 @@ void debugger_commands::execute_load(int ref, const std::vector<std::string> &pa /*------------------------------------------------- + execute_loadregion - execute the load command on region memory +-------------------------------------------------*/ + +void debugger_commands::execute_loadregion(const std::vector<std::string_view> ¶ms) +{ + u64 offset, length; + memory_region *region; + + // validate parameters + if (!m_console.validate_number_parameter(params[1], offset)) + return; + if (!m_console.validate_number_parameter(params[2], length)) + return; + if (!m_console.validate_memory_region_parameter(params[3], region)) + return; + + if (offset >= region->bytes()) + { + m_console.printf("Invalid offset\n"); + return; + } + if ((length <= 0) || ((length + offset) >= region->bytes())) + length = region->bytes() - offset; + + // open the file + std::string filename(params[0]); + FILE *const f = fopen(filename.c_str(), "rb"); + if (!f) + { + m_console.printf("Error opening file '%s'\n", params[0]); + return; + } + + fseek(f, 0L, SEEK_END); + u64 size = ftell(f); + rewind(f); + + // check file size + if (length >= size) + length = size; + + fread(region->base() + offset, 1, length, f); + + fclose(f); + m_console.printf("Data loaded successfully to memory : 0x%X to 0x%X\n", offset, offset + length - 1); +} + + +/*------------------------------------------------- execute_dump - execute the dump command -------------------------------------------------*/ -void debugger_commands::execute_dump(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_dump(int spacenum, const std::vector<std::string_view> ¶ms) { - /* validate parameters */ + // validate parameters + address_space *space; u64 offset; - if (!validate_number_parameter(params[1], offset)) + if (!m_console.validate_target_address_parameter(params[1], spacenum, space, offset)) return; u64 length; - if (!validate_number_parameter(params[2], length)) + if (!m_console.validate_number_parameter(params[2], length)) return; u64 width = 0; - if (params.size() > 3 && !validate_number_parameter(params[3], width)) - return; - - u64 ascii = 1; - if (params.size() > 4 && !validate_number_parameter(params[4], ascii)) + if (params.size() > 3 && !m_console.validate_number_parameter(params[3], width)) return; - address_space *space; - if (!validate_cpu_space_parameter((params.size() > 6) ? params[6].c_str() : nullptr, ref, space)) + bool ascii = true; + if (params.size() > 4 && !m_console.validate_boolean_parameter(params[4], ascii)) return; u64 rowsize = space->byte_to_address(16); - if (params.size() > 5 && !validate_number_parameter(params[5], rowsize)) + if (params.size() > 5 && !m_console.validate_number_parameter(params[5], rowsize)) return; int shift = space->addr_shift(); - u64 granularity = shift > 0 ? 2 : 1 << -shift; + u64 granularity = shift >= 0 ? 1 : 1 << -shift; - /* further validation */ + // further validation if (width == 0) width = space->data_width() / 8; if (width < space->address_to_byte(1)) @@ -2069,15 +2352,16 @@ void debugger_commands::execute_dump(int ref, const std::vector<std::string> &pa u64 endoffset = (offset + length - 1) & space->addrmask(); offset = offset & space->addrmask(); - /* open the file */ - FILE* f = fopen(params[0].c_str(), "w"); + // open the file + std::string filename(params[0]); + FILE *const f = fopen(filename.c_str(), "w"); if (!f) { - m_console.printf("Error opening file '%s'\n", params[0].c_str()); + m_console.printf("Error opening file '%s'\n", params[0]); return; } - /* now write the data out */ + // now write the data out util::ovectorstream output; output.reserve(200); @@ -2091,30 +2375,31 @@ void debugger_commands::execute_dump(int ref, const std::vector<std::string> &pa output.clear(); output.rdbuf()->clear(); - /* print the address */ + // print the address util::stream_format(output, "%0*X: ", space->logaddrchars(), i); - /* print the bytes */ + // print the bytes for (u64 j = 0; j < rowsize; j += delta) { if (i + j <= endoffset) { offs_t curaddr = i + j; - if (space->device().memory().translate(space->spacenum(), TRANSLATE_READ_DEBUG, curaddr)) + address_space *tspace; + if (space->device().memory().translate(space->spacenum(), device_memory_interface::TR_READ, curaddr, tspace)) { switch (width) { case 8: - util::stream_format(output, " %016X", space->read_qword_unaligned(i+j)); + util::stream_format(output, " %016X", tspace->read_qword_unaligned(i+j)); break; case 4: - util::stream_format(output, " %08X", space->read_dword_unaligned(i+j)); + util::stream_format(output, " %08X", tspace->read_dword_unaligned(i+j)); break; case 2: - util::stream_format(output, " %04X", space->read_word_unaligned(i+j)); + util::stream_format(output, " %04X", tspace->read_word_unaligned(i+j)); break; case 1: - util::stream_format(output, " %02X", space->read_byte(i+j)); + util::stream_format(output, " %02X", tspace->read_byte(i+j)); break; } } @@ -2127,29 +2412,30 @@ void debugger_commands::execute_dump(int ref, const std::vector<std::string> &pa util::stream_format(output, " %*s", width * 2, ""); } - /* print the ASCII */ + // print the ASCII if (ascii) { util::stream_format(output, " "); for (u64 j = 0; j < rowsize && (i + j) <= endoffset; j += delta) { offs_t curaddr = i + j; - if (space->device().memory().translate(space->spacenum(), TRANSLATE_READ_DEBUG, curaddr)) + address_space *tspace; + if (space->device().memory().translate(space->spacenum(), device_memory_interface::TR_READ, curaddr, tspace)) { u64 data = 0; switch (width) { case 8: - data = space->read_qword_unaligned(i+j); + data = tspace->read_qword_unaligned(i+j); break; case 4: - data = space->read_dword_unaligned(i+j); + data = tspace->read_dword_unaligned(i+j); break; case 2: - data = space->read_word_unaligned(i+j); + data = tspace->read_word_unaligned(i+j); break; case 1: - data = space->read_byte(i+j); + data = tspace->read_byte(i+j); break; } for (unsigned int b = 0; b != width; b++) { @@ -2164,167 +2450,361 @@ void debugger_commands::execute_dump(int ref, const std::vector<std::string> &pa } } - /* output the result */ + // output the result auto const &text = output.vec(); fprintf(f, "%.*s\n", int(unsigned(text.size())), &text[0]); } - /* close the file */ + // close the file fclose(f); m_console.printf("Data dumped successfully\n"); } -/*------------------------------------------------- - execute_cheatinit - initialize the cheat system --------------------------------------------------*/ +//------------------------------------------------- +// execute_strdump - execute the strdump command +//------------------------------------------------- -void debugger_commands::execute_cheatinit(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_strdump(int spacenum, const std::vector<std::string_view> ¶ms) { - u64 offset, length = 0, real_length = 0; - address_space *space; - u32 active_cheat = 0; - u64 curaddr; - u8 i, region_count = 0; + // validate parameters + u64 offset; + if (!m_console.validate_number_parameter(params[1], offset)) + return; - cheat_region_map cheat_region[100]; + u64 length; + if (!m_console.validate_number_parameter(params[2], length)) + return; - memset(cheat_region, 0, sizeof(cheat_region)); + u64 term = 0; + if (params.size() > 3 && !m_console.validate_number_parameter(params[3], term)) + return; - /* validate parameters */ - if (!validate_cpu_space_parameter((params.size() > 3) ? params[3].c_str() : nullptr, AS_PROGRAM, space)) + address_space *space; + if (!m_console.validate_device_space_parameter((params.size() > 4) ? params[4] : std::string_view(), spacenum, space)) return; - if (ref == 0) + // further validation + if (term >= 0x100 && term != u64(-0x80)) { - m_cheat.width = 1; - m_cheat.signed_cheat = false; - m_cheat.swapped_cheat = false; - if (!params.empty()) + m_console.printf("Invalid termination character\n"); + return; + } + + // open the file + std::string filename(params[0]); + FILE *f = fopen(filename.c_str(), "w"); + if (!f) + { + m_console.printf("Error opening file '%s'\n", params[0]); + return; + } + + const int shift = space->addr_shift(); + const unsigned delta = (shift >= 0) ? (1 << shift) : 1; + const unsigned width = (shift >= 0) ? 1 : (1 << -shift); + const bool be = space->endianness() == ENDIANNESS_BIG; + + offset = offset & space->addrmask(); + if (shift > 0) + length >>= shift; + + // now write the data out + util::ovectorstream output; + output.reserve(200); + + auto dis = space->device().machine().disable_side_effects(); + + bool terminated = true; + while (length-- != 0) + { + if (terminated) { - char *srtpnt = (char*)params[0].c_str(); + terminated = false; + output.clear(); + output.rdbuf()->clear(); - if (*srtpnt == 's') - m_cheat.signed_cheat = true; - else if (*srtpnt == 'u') - m_cheat.signed_cheat = false; - else + // print the address + util::stream_format(output, "%0*X: \"", space->logaddrchars(), offset); + } + + // get the character data + u64 data = 0; + offs_t curaddr = offset; + address_space *tspace; + if (space->device().memory().translate(space->spacenum(), device_memory_interface::TR_READ, curaddr, tspace)) + { + switch (width) { - m_console.printf("Invalid sign: expected s or u\n"); - return; + case 1: + data = tspace->read_byte(curaddr); + break; + + case 2: + data = tspace->read_word(curaddr); + if (be) + data = swapendian_int16(data); + break; + + case 4: + data = tspace->read_dword(curaddr); + if (be) + data = swapendian_int32(data); + break; + + case 8: + data = tspace->read_qword(curaddr); + if (be) + data = swapendian_int64(data); + break; } + } - if (*(++srtpnt) == 'b') - m_cheat.width = 1; - else if (*srtpnt == 'w') - m_cheat.width = 2; - else if (*srtpnt == 'd') - m_cheat.width = 4; - else if (*srtpnt == 'q') - m_cheat.width = 8; - else + // print the characters + for (int n = 0; n < width; n++) + { + // check for termination within word + if (terminated) { - m_console.printf("Invalid width: expected b, w, d or q\n"); - return; + terminated = false; + + // output the result + auto const &text = output.vec(); + fprintf(f, "%.*s\"\n", int(unsigned(text.size())), &text[0]); + output.clear(); + output.rdbuf()->clear(); + + // print the address + util::stream_format(output, "%0*X.%d: \"", space->logaddrchars(), offset, n); + } + + u8 ch = data & 0xff; + data >>= 8; + + // check for termination + if (term == u64(-0x80)) + { + if (BIT(ch, 7)) + { + terminated = true; + ch &= 0x7f; + } + } + else if (ch == term) + { + terminated = true; + continue; } - if (*(++srtpnt) == 's') - m_cheat.swapped_cheat = true; + // check for non-ASCII characters + if (ch < 0x20 || ch >= 0x7f) + { + // use special or octal escape + if (ch >= 0x07 && ch <= 0x0d) + util::stream_format(output, "\\%c", "abtnvfr"[ch - 0x07]); + else + util::stream_format(output, "\\%03o", ch); + } else - m_cheat.swapped_cheat = false; + { + if (ch == '"' || ch == '\\') + output << '\\'; + output << char(ch); + } } + + if (terminated) + { + // output the result + auto const &text = output.vec(); + fprintf(f, "%.*s\"\n", int(unsigned(text.size())), &text[0]); + output.clear(); + output.rdbuf()->clear(); + } + + offset += delta; } - /* initialize entire memory by default */ - if (params.size() <= 1) + if (!terminated) { - for (address_map_entry &entry : space->map()->m_entrylist) + // output the result + auto const &text = output.vec(); + fprintf(f, "%.*s\"\\\n", int(unsigned(text.size())), &text[0]); + } + + // close the file + fclose(f); + m_console.printf("Data dumped successfully\n"); +} + + +/*------------------------------------------------- + execute_cheatrange - add a range to search for + cheats +-------------------------------------------------*/ + +void debugger_commands::execute_cheatrange(bool init, const std::vector<std::string_view> ¶ms) +{ + address_space *space = m_cheat.space; + if (!space && !init) + { + m_console.printf("Use cheatinit before cheatrange\n"); + return; + } + + u8 width = (space || !init) ? m_cheat.width : 1; + bool signed_cheat = (space || !init) ? m_cheat.signed_cheat : false; + bool swapped_cheat = (space || !init) ? m_cheat.swapped_cheat : false; + if (init) + { + // first argument is sign/size/swap flags + if (!params.empty()) { - cheat_region[region_count].offset = entry.m_addrstart & space->addrmask(); - cheat_region[region_count].endoffset = entry.m_addrend & space->addrmask(); - cheat_region[region_count].share = entry.m_share; - cheat_region[region_count].disabled = (entry.m_write.m_type == AMH_RAM) ? false : true; + std::string_view const &srtpnt = params[0]; + if (!srtpnt.empty()) + { + width = 1; + signed_cheat = false; + swapped_cheat = false; + } + + if (srtpnt.length() >= 1) + { + char const sspec = std::tolower((unsigned char)srtpnt[0]); + if (sspec == 's') + signed_cheat = true; + else if (sspec == 'u') + signed_cheat = false; + else + { + m_console.printf("Invalid sign: expected s or u\n"); + return; + } + } - /* disable double share regions */ - if (entry.m_share != nullptr) - for (i = 0; i < region_count; i++) - if (cheat_region[i].share != nullptr) - if (strcmp(cheat_region[i].share, entry.m_share) == 0) - cheat_region[region_count].disabled = true; + if (srtpnt.length() >= 2) + { + char const wspec = std::tolower((unsigned char)srtpnt[1]); + if (wspec == 'b') + width = 1; + else if (wspec == 'w') + width = 2; + else if (wspec == 'd') + width = 4; + else if (wspec == 'q') + width = 8; + else + { + m_console.printf("Invalid width: expected b, w, d or q\n"); + return; + } + } - region_count++; + if (srtpnt.length() >= 3) + { + if (std::tolower((unsigned char)srtpnt[2]) == 's') + swapped_cheat = true; + else + { + m_console.printf("Invalid swap: expected s\n"); + return; + } + } } + + // fourth argument is device/space + if (!m_console.validate_device_space_parameter((params.size() > 3) ? params[3] : std::string_view(), -1, space)) + return; } - else + + cheat_region_map cheat_region[100]; // FIXME: magic number + unsigned region_count = 0; + if (params.size() >= (init ? 3 : 2)) { - /* validate parameters */ - if (!validate_number_parameter(params[(ref == 0) ? 1 : 0], offset)) + // validate parameters + u64 offset, length; + if (!m_console.validate_number_parameter(params[init ? 1 : 0], offset)) return; - if (!validate_number_parameter(params[(ref == 0) ? 2 : 1], length)) + if (!m_console.validate_number_parameter(params[init ? 2 : 1], length)) return; - /* force region to the specified range */ + // force region to the specified range cheat_region[region_count].offset = offset & space->addrmask(); cheat_region[region_count].endoffset = (offset + length - 1) & space->addrmask(); cheat_region[region_count].share = nullptr; cheat_region[region_count].disabled = false; region_count++; } + else + { + // initialize to entire memory by default + for (address_map_entry &entry : space->map()->m_entrylist) + { + cheat_region[region_count].offset = entry.m_addrstart & space->addrmask(); + cheat_region[region_count].endoffset = entry.m_addrend & space->addrmask(); + cheat_region[region_count].share = entry.m_share; + cheat_region[region_count].disabled = entry.m_write.m_type != AMH_RAM; + + // disable duplicate share regions + if (entry.m_share) + for (unsigned i = 0; i < region_count; i++) + if (cheat_region[i].share && !strcmp(cheat_region[i].share, entry.m_share)) + cheat_region[region_count].disabled = true; + + if (!cheat_region[region_count].disabled) + region_count++; + } + } - /* determine the writable extent of each region in total */ - for (i = 0; i < region_count; i++) - if (!cheat_region[i].disabled) - for (curaddr = cheat_region[i].offset; curaddr <= cheat_region[i].endoffset; curaddr += m_cheat.width) - if (cheat_address_is_valid(*space, curaddr)) - real_length++; + // determine the writable extent of each region in total + u64 real_length = 0; + for (unsigned i = 0; i < region_count; i++) + for (u64 curaddr = cheat_region[i].offset; curaddr <= cheat_region[i].endoffset; curaddr += width) + if (cheat_address_is_valid(*space, curaddr)) + real_length++; - if (real_length == 0) + if (!real_length) { m_console.printf("No writable bytes found in this area\n"); return; } - if (ref == 0) + size_t active_cheat = 0; + if (init) { - /* initialize new cheat system */ - m_cheat.cheatmap.resize(real_length); + // initialize new cheat system + m_cheat.space = space; + m_cheat.width = width; m_cheat.undo = 0; - m_cheat.cpu[0] = params.size() > 3 ? params[3][0] : '0'; + m_cheat.signed_cheat = signed_cheat; + m_cheat.swapped_cheat = swapped_cheat; } else { - /* add range to cheat system */ - if (m_cheat.cpu[0] == 0) - { - m_console.printf("Use cheatinit before cheatrange\n"); - return; - } - - if (!validate_cpu_space_parameter(m_cheat.cpu, AS_PROGRAM, space)) - return; - active_cheat = m_cheat.cheatmap.size(); - m_cheat.cheatmap.resize(m_cheat.cheatmap.size() + real_length); } + m_cheat.cheatmap.resize(active_cheat + real_length); - /* initialize cheatmap in the selected space */ - for (i = 0; i < region_count; i++) - if (!cheat_region[i].disabled) - for (curaddr = cheat_region[i].offset; curaddr <= cheat_region[i].endoffset; curaddr += m_cheat.width) - if (cheat_address_is_valid(*space, curaddr)) - { - m_cheat.cheatmap[active_cheat].previous_value = cheat_read_extended(&m_cheat, *space, curaddr); - m_cheat.cheatmap[active_cheat].first_value = m_cheat.cheatmap[active_cheat].previous_value; - m_cheat.cheatmap[active_cheat].offset = curaddr; - m_cheat.cheatmap[active_cheat].state = 1; - m_cheat.cheatmap[active_cheat].undo = 0; - active_cheat++; - } + // initialize cheatmap in the selected space + for (unsigned i = 0; i < region_count; i++) + for (u64 curaddr = cheat_region[i].offset; curaddr <= cheat_region[i].endoffset; curaddr += width) + if (cheat_address_is_valid(*space, curaddr)) + { + m_cheat.cheatmap[active_cheat].previous_value = m_cheat.read_extended(curaddr); + m_cheat.cheatmap[active_cheat].first_value = m_cheat.cheatmap[active_cheat].previous_value; + m_cheat.cheatmap[active_cheat].offset = curaddr; + m_cheat.cheatmap[active_cheat].state = 1; + m_cheat.cheatmap[active_cheat].undo = 0; + active_cheat++; + } - /* give a detailed init message to avoid searches being mistakingly carried out on the wrong CPU */ - device_t *cpu = nullptr; - validate_cpu_parameter(m_cheat.cpu, cpu); - m_console.printf("%u cheat initialized for CPU index %s ( aka %s )\n", active_cheat, m_cheat.cpu, cpu->tag()); + // give a detailed init message to avoid searches being mistakenly carried out on the wrong CPU + m_console.printf( + "%u cheat locations initialized for %s '%s' %s space\n", + active_cheat, + space->device().type().fullname(), + space->device().tag(), + space->name()); } @@ -2332,14 +2812,8 @@ void debugger_commands::execute_cheatinit(int ref, const std::vector<std::string execute_cheatnext - execute the search -------------------------------------------------*/ -void debugger_commands::execute_cheatnext(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_cheatnext(bool initial, const std::vector<std::string_view> ¶ms) { - address_space *space; - u64 cheatindex; - u32 active_cheat = 0; - u8 condition; - u64 comp_value = 0; - enum { CHEAT_ALL = 0, @@ -2358,54 +2832,61 @@ void debugger_commands::execute_cheatnext(int ref, const std::vector<std::string CHEAT_CHANGEDBY }; - if (m_cheat.cpu[0] == 0) + address_space *const space = m_cheat.space; + if (!space) { m_console.printf("Use cheatinit before cheatnext\n"); return; } - if (!validate_cpu_space_parameter(m_cheat.cpu, AS_PROGRAM, space)) - return; - - if (params.size() > 1 && !validate_number_parameter(params[1], comp_value)) + u64 comp_value = 0; + if (params.size() > 1 && !m_console.validate_number_parameter(params[1], comp_value)) return; - comp_value = cheat_sign_extend(&m_cheat, comp_value); + comp_value = m_cheat.sign_extend(comp_value); - /* decode condition */ - if (params[0] == "all") - condition = CHEAT_ALL; - else if (params[0] == "equal" || params[0] == "eq") - condition = (params.size() > 1) ? CHEAT_EQUALTO : CHEAT_EQUAL; - else if (params[0] == "notequal" || params[0] == "ne") - condition = (params.size() > 1) ? CHEAT_NOTEQUALTO : CHEAT_NOTEQUAL; - else if (params[0] == "decrease" || params[0] == "de" || params[0] == "-") - condition = (params.size() > 1) ? CHEAT_DECREASEOF : CHEAT_DECREASE; - else if (params[0] == "increase" || params[0] == "in" || params[0] == "+") - condition = (params.size() > 1) ? CHEAT_INCREASEOF : CHEAT_INCREASE; - else if (params[0] == "decreaseorequal" || params[0] == "deeq") - condition = CHEAT_DECREASE_OR_EQUAL; - else if (params[0] == "increaseorequal" || params[0] == "ineq") - condition = CHEAT_INCREASE_OR_EQUAL; - else if (params[0] == "smallerof" || params[0] == "lt" || params[0] == "<") - condition = CHEAT_SMALLEROF; - else if (params[0] == "greaterof" || params[0] == "gt" || params[0] == ">") - condition = CHEAT_GREATEROF; - else if (params[0] == "changedby" || params[0] == "ch" || params[0] == "~") - condition = CHEAT_CHANGEDBY; - else + // decode condition + u8 condition; { - m_console.printf("Invalid condition type\n"); - return; + using util::streqlower; + using namespace std::literals; + if (streqlower(params[0], "all"sv)) + condition = CHEAT_ALL; + else if (streqlower(params[0], "equal"sv) || streqlower(params[0], "eq"sv)) + condition = (params.size() > 1) ? CHEAT_EQUALTO : CHEAT_EQUAL; + else if (streqlower(params[0], "notequal"sv) || streqlower(params[0], "ne"sv)) + condition = (params.size() > 1) ? CHEAT_NOTEQUALTO : CHEAT_NOTEQUAL; + else if (streqlower(params[0], "decrease"sv) || streqlower(params[0], "de"sv) || params[0] == "-"sv) + condition = (params.size() > 1) ? CHEAT_DECREASEOF : CHEAT_DECREASE; + else if (streqlower(params[0], "increase"sv) || streqlower(params[0], "in"sv) || params[0] == "+"sv) + condition = (params.size() > 1) ? CHEAT_INCREASEOF : CHEAT_INCREASE; + else if (streqlower(params[0], "decreaseorequal"sv) || streqlower(params[0], "deeq"sv)) + condition = CHEAT_DECREASE_OR_EQUAL; + else if (streqlower(params[0], "increaseorequal"sv) || streqlower(params[0], "ineq"sv)) + condition = CHEAT_INCREASE_OR_EQUAL; + else if (streqlower(params[0], "smallerof"sv) || streqlower(params[0], "lt"sv) || params[0] == "<"sv) + condition = CHEAT_SMALLEROF; + else if (streqlower(params[0], "greaterof"sv) || streqlower(params[0], "gt"sv) || params[0] == ">"sv) + condition = CHEAT_GREATEROF; + else if (streqlower(params[0], "changedby"sv) || streqlower(params[0], "ch"sv) || params[0] == "~"sv) + condition = CHEAT_CHANGEDBY; + else + { + m_console.printf("Invalid condition type\n"); + return; + } } m_cheat.undo++; - /* execute the search */ - for (cheatindex = 0; cheatindex < m_cheat.cheatmap.size(); cheatindex += 1) + // execute the search + u32 active_cheat = 0; + for (u64 cheatindex = 0; cheatindex < m_cheat.cheatmap.size(); cheatindex += 1) if (m_cheat.cheatmap[cheatindex].state == 1) { - u64 cheat_value = cheat_read_extended(&m_cheat, *space, m_cheat.cheatmap[cheatindex].offset); - u64 comp_byte = (ref == 0) ? m_cheat.cheatmap[cheatindex].previous_value : m_cheat.cheatmap[cheatindex].first_value; + u64 cheat_value = m_cheat.read_extended(m_cheat.cheatmap[cheatindex].offset); + u64 comp_byte = initial + ? m_cheat.cheatmap[cheatindex].first_value + : m_cheat.cheatmap[cheatindex].previous_value; u8 disable_byte = false; switch (condition) @@ -2494,12 +2975,12 @@ void debugger_commands::execute_cheatnext(int ref, const std::vector<std::string else active_cheat++; - /* update previous value */ + // update previous value m_cheat.cheatmap[cheatindex].previous_value = cheat_value; } if (active_cheat <= 5) - execute_cheatlist(0, std::vector<std::string>()); + execute_cheatlist(std::vector<std::string_view>()); m_console.printf("%u cheats found\n", active_cheat); } @@ -2509,51 +2990,79 @@ void debugger_commands::execute_cheatnext(int ref, const std::vector<std::string execute_cheatlist - show a list of active cheat -------------------------------------------------*/ -void debugger_commands::execute_cheatlist(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_cheatlist(const std::vector<std::string_view> ¶ms) { - char spaceletter, sizeletter; - address_space *space; - device_t *cpu; - u32 active_cheat = 0; - u64 cheatindex; - u64 sizemask; - FILE *f = nullptr; - - if (!validate_cpu_space_parameter(m_cheat.cpu, AS_PROGRAM, space)) - return; - - if (!validate_cpu_parameter(m_cheat.cpu, cpu)) + address_space *const space = m_cheat.space; + if (!space) + { + m_console.printf("Use cheatinit before cheatlist\n"); return; + } + FILE *f = nullptr; if (params.size() > 0) - f = fopen(params[0].c_str(), "w"); + { + std::string filename(params[0]); + f = fopen(filename.c_str(), "w"); + if (!f) + { + m_console.printf("Error opening file '%s'\n", params[0]); + return; + } + } + // get device/space syntax for memory access + std::string tag(space->device().tag()); + std::string spaceletter; switch (space->spacenum()) { - default: - case AS_PROGRAM: spaceletter = 'p'; break; - case AS_DATA: spaceletter = 'd'; break; - case AS_IO: spaceletter = 'i'; break; - case AS_OPCODES: spaceletter = 'o'; break; + default: + tag.append(1, ':'); + tag.append(space->name()); + break; + case AS_PROGRAM: + spaceletter = "p"; + break; + case AS_DATA: + spaceletter = "d"; + break; + case AS_IO: + spaceletter = "i"; + break; + case AS_OPCODES: + spaceletter = "3"; + break; } + // get size syntax for memory access and formatting values + bool const octal = space->is_octal(); + int const addrchars = octal + ? ((2 + space->logaddr_width()) / 3) + : ((3 + space->logaddr_width()) / 4); + int const datachars = octal + ? ((2 + (m_cheat.width * 8)) / 3) + : ((3 + (m_cheat.width * 8)) / 4); + u64 const sizemask = util::make_bitmask<u64>(m_cheat.width * 8); + char sizeletter; switch (m_cheat.width) { - default: - case 1: sizeletter = 'b'; sizemask = 0xffU; break; - case 2: sizeletter = 'w'; sizemask = 0xffffU; break; - case 4: sizeletter = 'd'; sizemask = 0xffffffffU; break; - case 8: sizeletter = 'q'; sizemask = 0xffffffffffffffffU; break; + default: + case 1: sizeletter = 'b'; break; + case 2: sizeletter = 'w'; break; + case 4: sizeletter = 'd'; break; + case 8: sizeletter = 'q'; break; } - /* write the cheat list */ + // write the cheat list + u32 active_cheat = 0; util::ovectorstream output; - for (cheatindex = 0; cheatindex < m_cheat.cheatmap.size(); cheatindex += 1) + for (u64 cheatindex = 0; cheatindex < m_cheat.cheatmap.size(); cheatindex += 1) { if (m_cheat.cheatmap[cheatindex].state == 1) { - u64 value = cheat_byte_swap(&m_cheat, cheat_read_extended(&m_cheat, *space, m_cheat.cheatmap[cheatindex].offset)) & sizemask; - offs_t address = space->byte_to_address(m_cheat.cheatmap[cheatindex].offset); + u64 const value = m_cheat.byte_swap(m_cheat.read_extended(m_cheat.cheatmap[cheatindex].offset)) & sizemask; + u64 const first_value = m_cheat.byte_swap(m_cheat.cheatmap[cheatindex].first_value) & sizemask; + offs_t const address = space->byte_to_address(m_cheat.cheatmap[cheatindex].offset); if (!params.empty()) { @@ -2562,23 +3071,31 @@ void debugger_commands::execute_cheatlist(int ref, const std::vector<std::string output.rdbuf()->clear(); stream_format( output, - " <cheat desc=\"Possibility %d : %0*X (%0*X)\">\n" - " <script state=\"run\">\n" - " <action>%s.p%c%c@%0*X=%0*X</action>\n" - " </script>\n" - " </cheat>\n\n", - active_cheat, space->logaddrchars(), address, m_cheat.width * 2, value, - cpu->tag(), spaceletter, sizeletter, space->logaddrchars(), address, m_cheat.width * 2, cheat_byte_swap(&m_cheat, m_cheat.cheatmap[cheatindex].first_value) & sizemask); + octal ? + " <cheat desc=\"Possibility %d: 0%0*o (0%0*o)\">\n" + " <script state=\"run\">\n" + " <action>%s.%s%c@0o%0*o=0o%0*o</action>\n" + " </script>\n" + " </cheat>\n\n" : + " <cheat desc=\"Possibility %d: %0*X (%0*X)\">\n" + " <script state=\"run\">\n" + " <action>%s.%s%c@0x%0*X=0x%0*X</action>\n" + " </script>\n" + " </cheat>\n\n", + active_cheat, addrchars, address, datachars, value, + tag, spaceletter, sizeletter, addrchars, address, datachars, first_value); auto const &text(output.vec()); fprintf(f, "%.*s", int(unsigned(text.size())), &text[0]); } else { m_console.printf( - "Address=%0*X Start=%0*X Current=%0*X\n", - space->logaddrchars(), address, - m_cheat.width * 2, cheat_byte_swap(&m_cheat, m_cheat.cheatmap[cheatindex].first_value) & sizemask, - m_cheat.width * 2, value); + octal + ? "Address=0%0*o Start=0%0*o Current=0%0*o\n" + : "Address=%0*X Start=%0*X Current=%0*X\n", + addrchars, address, + datachars, first_value, + datachars, value); } } } @@ -2591,14 +3108,12 @@ void debugger_commands::execute_cheatlist(int ref, const std::vector<std::string execute_cheatundo - undo the last search -------------------------------------------------*/ -void debugger_commands::execute_cheatundo(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_cheatundo(const std::vector<std::string_view> ¶ms) { - u64 cheatindex; - u32 undo_count = 0; - if (m_cheat.undo > 0) { - for (cheatindex = 0; cheatindex < m_cheat.cheatmap.size(); cheatindex += 1) + u64 undo_count = 0; + for (u64 cheatindex = 0; cheatindex < m_cheat.cheatmap.size(); cheatindex += 1) { if (m_cheat.cheatmap[cheatindex].undo == m_cheat.undo) { @@ -2612,7 +3127,9 @@ void debugger_commands::execute_cheatundo(int ref, const std::vector<std::string m_console.printf("%u cheat reactivated\n", undo_count); } else + { m_console.printf("Maximum undo reached\n"); + } } @@ -2620,89 +3137,119 @@ void debugger_commands::execute_cheatundo(int ref, const std::vector<std::string execute_find - execute the find command -------------------------------------------------*/ -void debugger_commands::execute_find(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_find(int spacenum, const std::vector<std::string_view> ¶ms) { - u64 offset, endoffset, length; + u64 offset, length; address_space *space; - u64 data_to_find[256]; - u8 data_size[256]; - int cur_data_size; - int data_count = 0; - int found = 0; - int j; - /* validate parameters */ - if (!validate_number_parameter(params[0], offset)) - return; - if (!validate_number_parameter(params[1], length)) + // validate parameters + if (!m_console.validate_target_address_parameter(params[0], spacenum, space, offset)) return; - if (!validate_cpu_space_parameter(nullptr, ref, space)) + if (!m_console.validate_number_parameter(params[1], length)) return; - /* further validation */ - endoffset = space->address_to_byte_end((offset + length - 1) & space->addrmask()); + // further validation + u64 const endoffset = space->address_to_byte_end((offset + length - 1) & space->addrmask()); offset = space->address_to_byte(offset & space->addrmask()); - cur_data_size = space->addr_shift() > 0 ? 2 : 1 << -space->addr_shift(); + int cur_data_size = (space->addr_shift() > 0) ? 2 : (1 << -space->addr_shift()); if (cur_data_size == 0) cur_data_size = 1; - /* parse the data parameters */ + // parse the data parameters + u64 data_to_find[256]; + u8 data_size[256]; + int data_count = 0; for (int i = 2; i < params.size(); i++) { - const char *pdata = params[i].c_str(); - size_t pdatalen = strlen(pdata) - 1; + std::string_view pdata = params[i]; - /* check for a string */ - if (pdata[0] == '"' && pdata[pdatalen] == '"') + if (!pdata.empty() && pdata.front() == '"' && pdata.back() == '"') // check for a string { - for (j = 1; j < pdatalen; j++) + auto const pdatalen = params[i].length() - 1; + for (int j = 1; j < pdatalen; j++) { data_to_find[data_count] = pdata[j]; data_size[data_count++] = 1; } } - - /* otherwise, validate as a number */ - else + else // otherwise, validate as a number { - /* check for a 'b','w','d',or 'q' prefix */ + // check for a 'b','w','d',or 'q' prefix data_size[data_count] = cur_data_size; - if (tolower(u8(pdata[0])) == 'b' && pdata[1] == '.') { data_size[data_count] = cur_data_size = 1; pdata += 2; } - if (tolower(u8(pdata[0])) == 'w' && pdata[1] == '.') { data_size[data_count] = cur_data_size = 2; pdata += 2; } - if (tolower(u8(pdata[0])) == 'd' && pdata[1] == '.') { data_size[data_count] = cur_data_size = 4; pdata += 2; } - if (tolower(u8(pdata[0])) == 'q' && pdata[1] == '.') { data_size[data_count] = cur_data_size = 8; pdata += 2; } + if (pdata.length() >= 2) + { + if (tolower(u8(pdata[0])) == 'b' && pdata[1] == '.') { data_size[data_count] = cur_data_size = 1; pdata.remove_prefix(2); } + if (tolower(u8(pdata[0])) == 'w' && pdata[1] == '.') { data_size[data_count] = cur_data_size = 2; pdata.remove_prefix(2); } + if (tolower(u8(pdata[0])) == 'd' && pdata[1] == '.') { data_size[data_count] = cur_data_size = 4; pdata.remove_prefix(2); } + if (tolower(u8(pdata[0])) == 'q' && pdata[1] == '.') { data_size[data_count] = cur_data_size = 8; pdata.remove_prefix(2); } + } - /* look for a wildcard */ - if (!strcmp(pdata, "?")) + // look for a wildcard + if (pdata == "?") data_size[data_count++] |= 0x10; - /* otherwise, validate as a number */ - else if (!validate_number_parameter(pdata, data_to_find[data_count++])) + // otherwise, validate as a number + else if (!m_console.validate_number_parameter(pdata, data_to_find[data_count++])) return; } } - /* now search */ + // now search + device_memory_interface &memory = space->device().memory(); + auto dis = space->device().machine().disable_side_effects(); + int found = 0; for (u64 i = offset; i <= endoffset; i += data_size[0]) { int suboffset = 0; - int match = 1; + bool match = true; - /* find the entire string */ - for (j = 0; j < data_count && match; j++) + // find the entire string + for (int j = 0; j < data_count && match; j++) { + offs_t address = space->byte_to_address(i + suboffset); + address_space *tspace; switch (data_size[j]) { - case 1: match = (u8(m_cpu.read_byte(*space, space->byte_to_address(i + suboffset), true)) == u8(data_to_find[j])); break; - case 2: match = (u16(m_cpu.read_word(*space, space->byte_to_address(i + suboffset), true)) == u16(data_to_find[j])); break; - case 4: match = (u32(m_cpu.read_dword(*space, space->byte_to_address(i + suboffset), true)) == u32(data_to_find[j])); break; - case 8: match = (u64(m_cpu.read_qword(*space, space->byte_to_address(i + suboffset), true)) == u64(data_to_find[j])); break; - default: /* all other cases are wildcards */ break; + case 1: + address &= space->logaddrmask(); + if (memory.translate(space->spacenum(), device_memory_interface::TR_READ, address, tspace)) + match = tspace->read_byte(address) == u8(data_to_find[j]); + else + match = false; + break; + + case 2: + address &= space->logaddrmask(); + if (memory.translate(space->spacenum(), device_memory_interface::TR_READ, address, tspace)) + match = tspace->read_word_unaligned(address) == u16(data_to_find[j]); + else + match = false; + break; + + case 4: + address &= space->logaddrmask(); + if (memory.translate(space->spacenum(), device_memory_interface::TR_READ, address, tspace)) + match = tspace->read_dword_unaligned(address) == u32(data_to_find[j]); + else + match = false; + break; + + case 8: + address &= space->logaddrmask(); + if (memory.translate(space->spacenum(), device_memory_interface::TR_READ, address, tspace)) + match = tspace->read_qword_unaligned(address) == u64(data_to_find[j]); + else + match = false; + break; + + default: + // all other cases are wildcards + break; } suboffset += data_size[j] & 0x0f; } - /* did we find it? */ + // did we find it? if (match) { found++; @@ -2710,32 +3257,142 @@ void debugger_commands::execute_find(int ref, const std::vector<std::string> &pa } } - /* print something if not found */ + // print something if not found if (found == 0) m_console.printf("Not found\n"); } +//------------------------------------------------- +// execute_fill - execute the fill command +//------------------------------------------------- + +void debugger_commands::execute_fill(int spacenum, const std::vector<std::string_view> ¶ms) +{ + u64 offset, length; + address_space *space; + + // validate parameters + if (!m_console.validate_target_address_parameter(params[0], spacenum, space, offset)) + return; + if (!m_console.validate_number_parameter(params[1], length)) + return; + + // further validation + offset = space->address_to_byte(offset & space->addrmask()); + int cur_data_size = (space->addr_shift() > 0) ? 2 : (1 << -space->addr_shift()); + if (cur_data_size == 0) + cur_data_size = 1; + + // parse the data parameters + u64 fill_data[256]; + u8 fill_data_size[256]; + int data_count = 0; + for (int i = 2; i < params.size(); i++) + { + std::string_view pdata = params[i]; + + // check for a string + if (!pdata.empty() && pdata.front() == '"' && pdata.back() == '"') + { + auto const pdatalen = pdata.length() - 1; + for (int j = 1; j < pdatalen; j++) + { + fill_data[data_count] = pdata[j]; + fill_data_size[data_count++] = 1; + } + } + + // otherwise, validate as a number + else + { + // check for a 'b','w','d',or 'q' prefix + fill_data_size[data_count] = cur_data_size; + if (pdata.length() >= 2) + { + if (tolower(u8(pdata[0])) == 'b' && pdata[1] == '.') { fill_data_size[data_count] = cur_data_size = 1; pdata.remove_prefix(2); } + if (tolower(u8(pdata[0])) == 'w' && pdata[1] == '.') { fill_data_size[data_count] = cur_data_size = 2; pdata.remove_prefix(2); } + if (tolower(u8(pdata[0])) == 'd' && pdata[1] == '.') { fill_data_size[data_count] = cur_data_size = 4; pdata.remove_prefix(2); } + if (tolower(u8(pdata[0])) == 'q' && pdata[1] == '.') { fill_data_size[data_count] = cur_data_size = 8; pdata.remove_prefix(2); } + } + + // validate as a number + if (!m_console.validate_number_parameter(pdata, fill_data[data_count++])) + return; + } + } + if (data_count == 0) + return; + + // now fill memory + device_memory_interface &memory = space->device().memory(); + auto dis = space->device().machine().disable_side_effects(); + u64 count = space->address_to_byte(length); + while (count != 0) + { + // write the entire string + for (int j = 0; j < data_count; j++) + { + offs_t address = space->byte_to_address(offset) & space->logaddrmask(); + address_space *tspace; + if (!memory.translate(space->spacenum(), device_memory_interface::TR_WRITE, address, tspace)) + { + m_console.printf("Fill aborted due to page fault at %0*X\n", space->logaddrchars(), space->byte_to_address(offset) & space->logaddrmask()); + length = 0; + break; + } + switch (fill_data_size[j]) + { + case 1: + tspace->write_byte(address, fill_data[j]); + break; + + case 2: + tspace->write_word_unaligned(address, fill_data[j]); + break; + + case 4: + tspace->write_dword_unaligned(address, fill_data[j]); + break; + + case 8: + tspace->read_qword_unaligned(address, fill_data[j]); + break; + } + offset += fill_data_size[j]; + if (count <= fill_data_size[j]) + { + count = 0; + break; + } + else + count -= fill_data_size[j]; + } + } +} + + /*------------------------------------------------- execute_dasm - execute the dasm command -------------------------------------------------*/ -void debugger_commands::execute_dasm(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_dasm(const std::vector<std::string_view> ¶ms) { - u64 offset, length, bytes = 1; + u64 offset, length; + bool bytes = true; address_space *space; - /* validate parameters */ - if (!validate_number_parameter(params[1], offset)) + // validate parameters + if (!m_console.validate_number_parameter(params[1], offset)) return; - if (!validate_number_parameter(params[2], length)) + if (!m_console.validate_number_parameter(params[2], length)) return; - if (params.size() > 3 && !validate_number_parameter(params[3], bytes)) + if (params.size() > 3 && !m_console.validate_boolean_parameter(params[3], bytes)) return; - if (!validate_cpu_space_parameter(params.size() > 4 ? params[4].c_str() : nullptr, AS_PROGRAM, space)) + if (!m_console.validate_device_space_parameter(params.size() > 4 ? params[4] : std::string_view(), AS_PROGRAM, space)) return; - /* determine the width of the bytes */ + // determine the width of the bytes device_disasm_interface *dasmintf; if (!space->device().interface(dasmintf)) { @@ -2743,7 +3400,7 @@ void debugger_commands::execute_dasm(int ref, const std::vector<std::string> &pa return; } - /* build the data, check the maximum size of the opcodes and disasm */ + // build the data, check the maximum size of the opcodes and disasm std::vector<offs_t> pcs; std::vector<std::string> instructions; std::vector<std::string> tpc; @@ -2766,11 +3423,11 @@ void debugger_commands::execute_dasm(int ref, const std::vector<std::string> &pa topcodes.emplace_back(buffer.data_to_string(offset, size, true)); int osize = topcodes.back().size(); - if(osize > max_opcodes_size) + if (osize > max_opcodes_size) max_opcodes_size = osize; int dsize = instructions.back().size(); - if(dsize > max_disasm_size) + if (dsize > max_disasm_size) max_disasm_size = dsize; i += size; @@ -2778,7 +3435,8 @@ void debugger_commands::execute_dasm(int ref, const std::vector<std::string> &pa } /* write the data */ - std::ofstream f(params[0]); + std::string fname(params[0]); + std::ofstream f(fname); if (!f.good()) { m_console.printf("Error opening file '%s'\n", params[0]); @@ -2787,7 +3445,7 @@ void debugger_commands::execute_dasm(int ref, const std::vector<std::string> &pa if (bytes) { - for(unsigned int i=0; i != pcs.size(); i++) + for (unsigned int i=0; i != pcs.size(); i++) { const char *comment = space->device().debug()->comment_text(pcs[i]); if (comment) @@ -2798,7 +3456,7 @@ void debugger_commands::execute_dasm(int ref, const std::vector<std::string> &pa } else { - for(unsigned int i=0; i != pcs.size(); i++) + for (unsigned int i=0; i != pcs.size(); i++) { const char *comment = space->device().debug()->comment_text(pcs[i]); if (comment) @@ -2813,104 +3471,88 @@ void debugger_commands::execute_dasm(int ref, const std::vector<std::string> &pa /*------------------------------------------------- - execute_trace_internal - functionality for + execute_trace - functionality for trace over and trace info -------------------------------------------------*/ -void debugger_commands::execute_trace_internal(int ref, const std::vector<std::string> ¶ms, bool trace_over) +void debugger_commands::execute_trace(const std::vector<std::string_view> ¶ms, bool trace_over) { - const char *action = nullptr; + std::string_view action; bool detect_loops = true; bool logerror = false; - device_t *cpu; - FILE *f = nullptr; - const char *mode; - std::string filename = params[0]; + std::string filename(params[0]); - /* replace macros */ + // replace macros strreplace(filename, "{game}", m_machine.basename()); - /* validate parameters */ - if (!validate_cpu_parameter(params.size() > 1 ? params[1].c_str() : nullptr, cpu)) + // validate parameters + device_t *cpu; + if (!m_console.validate_cpu_parameter(params.size() > 1 ? params[1] : std::string_view(), cpu)) return; if (params.size() > 2) { std::stringstream stream; - stream.str(params[2]); + stream.str(std::string(params[2])); std::string flag; while (std::getline(stream, flag, '|')) { - if (!core_stricmp(flag.c_str(), "noloop")) + using namespace std::literals; + if (util::streqlower(flag, "noloop"sv)) detect_loops = false; - else if (!core_stricmp(flag.c_str(), "logerror")) + else if (util::streqlower(flag, "logerror"sv)) logerror = true; else { - m_console.printf("Invalid flag '%s'\n", flag.c_str()); + m_console.printf("Invalid flag '%s'\n", flag); return; } } } - if (!debug_command_parameter_command(action = (params.size() > 3) ? params[3].c_str() : nullptr)) + if (params.size() > 3 && !m_console.validate_command_parameter(action = params[3])) return; - /* open the file */ - if (core_stricmp(filename.c_str(), "off") != 0) + // open the file + std::unique_ptr<std::ofstream> f; + using namespace std::literals; + if (!util::streqlower(filename, "off"sv)) { - mode = "w"; + std::ios_base::openmode mode; - /* opening for append? */ + // opening for append? if ((filename[0] == '>') && (filename[1] == '>')) { - mode = "a"; + mode = std::ios_base::in | std::ios_base::out | std::ios_base::ate; filename = filename.substr(2); } + else + mode = std::ios_base::out | std::ios_base::trunc; - f = fopen(filename.c_str(), mode); - if (!f) + f = std::make_unique<std::ofstream>(filename.c_str(), mode); + if (f->fail()) { - m_console.printf("Error opening file '%s'\n", params[0].c_str()); + m_console.printf("Error opening file '%s'\n", params[0]); return; } } - /* do it */ - cpu->debug()->trace(f, trace_over, detect_loops, logerror, action); - if (f) - m_console.printf("Tracing CPU '%s' to file %s\n", cpu->tag(), filename.c_str()); + // do it + bool const on(f); + cpu->debug()->trace(std::move(f), trace_over, detect_loops, logerror, action); + if (on) + m_console.printf("Tracing CPU '%s' to file %s\n", cpu->tag(), filename); else m_console.printf("Stopped tracing on CPU '%s'\n", cpu->tag()); } /*------------------------------------------------- - execute_trace - execute the trace command --------------------------------------------------*/ - -void debugger_commands::execute_trace(int ref, const std::vector<std::string> ¶ms) -{ - execute_trace_internal(ref, params, false); -} - - -/*------------------------------------------------- - execute_traceover - execute the trace over command --------------------------------------------------*/ - -void debugger_commands::execute_traceover(int ref, const std::vector<std::string> ¶ms) -{ - execute_trace_internal(ref, params, true); -} - - -/*------------------------------------------------- execute_traceflush - execute the trace flush command -------------------------------------------------*/ -void debugger_commands::execute_traceflush(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_traceflush(const std::vector<std::string_view> ¶ms) { - m_cpu.flush_traces(); + m_machine.debugger().cpu().flush_traces(); } @@ -2918,43 +3560,46 @@ void debugger_commands::execute_traceflush(int ref, const std::vector<std::strin execute_history - execute the history command -------------------------------------------------*/ -void debugger_commands::execute_history(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_history(const std::vector<std::string_view> ¶ms) { - /* validate parameters */ - address_space *space; - if (!validate_cpu_space_parameter(!params.empty() ? params[0].c_str() : nullptr, AS_PROGRAM, space)) + // validate parameters + device_t *device; + if (!m_console.validate_cpu_parameter(!params.empty() ? params[0] : std::string_view(), device)) return; u64 count = device_debug::HISTORY_SIZE; - if (params.size() > 1 && !validate_number_parameter(params[1], count)) + if (params.size() > 1 && !m_console.validate_number_parameter(params[1], count)) return; - /* further validation */ + // further validation if (count > device_debug::HISTORY_SIZE) count = device_debug::HISTORY_SIZE; - device_debug *debug = space->device().debug(); + device_debug *const debug = device->debug(); - /* loop over lines */ device_disasm_interface *dasmintf; - if (!space->device().interface(dasmintf)) + if (!device->interface(dasmintf)) { - m_console.printf("No disassembler available for %s\n", space->device().name()); + m_console.printf("No disassembler available for device %s\n", device->name()); return; } - debug_disasm_buffer buffer(space->device()); - - for (int index = 0; index < (int) count; index++) + // loop over lines + std::string instruction; + for (int index = int(unsigned(count)); index > 0; index--) { - offs_t pc = debug->history_pc(-index); - std::string instruction; - offs_t next_offset; - offs_t size; - u32 info; - buffer.disassemble(pc, instruction, next_offset, size, info); - - m_console.printf("%s: %s\n", buffer.pc_to_string(pc), instruction); + auto const pc = debug->history_pc(1 - index); + if (pc.second) + { + debug_disasm_buffer buffer(*device); + offs_t next_offset; + offs_t size; + u32 info; + instruction.clear(); + buffer.disassemble(pc.first, instruction, next_offset, size, info); + + m_console.printf("%s: %s\n", buffer.pc_to_string(pc.first), instruction); + } } } @@ -2963,30 +3608,37 @@ void debugger_commands::execute_history(int ref, const std::vector<std::string> execute_trackpc - execute the trackpc command -------------------------------------------------*/ -void debugger_commands::execute_trackpc(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_trackpc(const std::vector<std::string_view> ¶ms) { // Gather the on/off switch (if present) bool turnOn = true; - if (params.size() > 0 && !validate_boolean_parameter(params[0], turnOn)) + if (params.size() > 0 && !m_console.validate_boolean_parameter(params[0], turnOn)) return; // Gather the cpu id (if present) device_t *cpu = nullptr; - if (!validate_cpu_parameter((params.size() > 1) ? params[1].c_str() : nullptr, cpu)) + if (!m_console.validate_cpu_parameter((params.size() > 1) ? params[1] : std::string_view(), cpu)) return; + const device_state_interface *state; + if (!cpu->interface(state)) + { + m_console.printf("Device has no PC to be tracked\n"); + return; + } + // Should we clear the existing data? bool clear = false; - if (params.size() > 2 && !validate_boolean_parameter(params[2], clear)) + if (params.size() > 2 && !m_console.validate_boolean_parameter(params[2], clear)) return; cpu->debug()->set_track_pc((bool)turnOn); if (turnOn) { // Insert current pc - if (m_cpu.get_visible_cpu() == cpu) + if (m_console.get_visible_cpu() == cpu) { - const offs_t pc = cpu->state().pcbase(); + const offs_t pc = state->pcbase(); cpu->debug()->set_track_pc_visited(pc); } m_console.printf("PC tracking enabled\n"); @@ -3005,26 +3657,29 @@ void debugger_commands::execute_trackpc(int ref, const std::vector<std::string> execute_trackmem - execute the trackmem command -------------------------------------------------*/ -void debugger_commands::execute_trackmem(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_trackmem(const std::vector<std::string_view> ¶ms) { // Gather the on/off switch (if present) bool turnOn = true; - if (params.size() > 0 && !validate_boolean_parameter(params[0], turnOn)) + if (params.size() > 0 && !m_console.validate_boolean_parameter(params[0], turnOn)) return; // Gather the cpu id (if present) + std::string_view cpuparam; + if (params.size() > 1) + cpuparam = params[1]; device_t *cpu = nullptr; - if (!validate_cpu_parameter((params.size() > 1) ? params[1].c_str() : nullptr, cpu)) + if (!m_console.validate_cpu_parameter(cpuparam, cpu)) return; // Should we clear the existing data? bool clear = false; - if (params.size() > 2 && !validate_boolean_parameter(params[2], clear)) + if (params.size() > 2 && !m_console.validate_boolean_parameter(params[2], clear)) return; // Get the address space for the given cpu address_space *space; - if (!validate_cpu_space_parameter((params.size() > 1) ? params[1].c_str() : nullptr, AS_PROGRAM, space)) + if (!m_console.validate_device_space_parameter(cpuparam, AS_PROGRAM, space)) return; // Inform the CPU it's time to start tracking memory writes @@ -3040,30 +3695,47 @@ void debugger_commands::execute_trackmem(int ref, const std::vector<std::string> execute_pcatmem - execute the pcatmem command -------------------------------------------------*/ -void debugger_commands::execute_pcatmem(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_pcatmem(int spacenum, const std::vector<std::string_view> ¶ms) { - // Gather the required address parameter + // Gather the required target address/space parameter u64 address; - if (!validate_number_parameter(params[0], address)) - return; - - // Gather the cpu id (if present) - device_t *cpu = nullptr; - if (!validate_cpu_parameter((params.size() > 1) ? params[1].c_str() : nullptr, cpu)) + address_space *space; + if (!m_console.validate_target_address_parameter(params[0], spacenum, space, address)) return; - // Get the address space for the given cpu - address_space *space; - if (!validate_cpu_space_parameter((params.size() > 1) ? params[1].c_str() : nullptr, ref, space)) + // Translate the address + offs_t a = address & space->logaddrmask(); + address_space *tspace; + if (!space->device().memory().translate(space->spacenum(), device_memory_interface::TR_READ, a, tspace)) + { + m_console.printf("Address translation failed\n"); return; + } // Get the value of memory at the address - const int native_data_width = space->data_width() / 8; - const u64 data = m_cpu.read_memory(*space, space->address_to_byte(address), native_data_width, true); + u64 data = space->unmap(); + auto dis = space->device().machine().disable_side_effects(); + switch (space->data_width()) + { + case 8: + data = tspace->read_byte(a); + break; + + case 16: + data = tspace->read_word_unaligned(a); + break; + + case 32: + data = tspace->read_dword_unaligned(a); + break; + + case 64: + data = tspace->read_qword_unaligned(a); + break; + } // Recover the pc & print - const int space_num = (int)ref; - const offs_t result = space->device().debug()->track_mem_pc_from_space_address_data(space_num, address, data); + const offs_t result = space->device().debug()->track_mem_pc_from_space_address_data(space->spacenum(), address, data); if (result != (offs_t)(-1)) m_console.printf("%02x\n", result); else @@ -3075,7 +3747,7 @@ void debugger_commands::execute_pcatmem(int ref, const std::vector<std::string> execute_snap - execute the snapshot command -------------------------------------------------*/ -void debugger_commands::execute_snap(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_snap(const std::vector<std::string_view> ¶ms) { /* if no params, use the default behavior */ if (params.empty()) @@ -3087,10 +3759,11 @@ void debugger_commands::execute_snap(int ref, const std::vector<std::string> &pa /* otherwise, we have to open the file ourselves */ else { - const char *filename = params[0].c_str(); - int scrnum = (params.size() > 1) ? atoi(params[1].c_str()) : 0; + u64 scrnum = 0; + if (params.size() > 1 && !m_console.validate_number_parameter(params[1], scrnum)) + return; - screen_device_iterator iter(m_machine.root_device()); + screen_device_enumerator iter(m_machine.root_device()); screen_device *screen = iter.byindex(scrnum); if ((screen == nullptr) || !m_machine.render().is_live(*screen)) @@ -3099,20 +3772,20 @@ void debugger_commands::execute_snap(int ref, const std::vector<std::string> &pa return; } - std::string fname(filename); + std::string fname(params[0]); if (fname.find(".png") == -1) fname.append(".png"); emu_file file(m_machine.options().snapshot_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); - osd_file::error filerr = file.open(fname.c_str()); + std::error_condition filerr = file.open(std::move(fname)); - if (filerr != osd_file::error::NONE) + if (filerr) { - m_console.printf("Error creating file '%s'\n", filename); + m_console.printf("Error creating file '%s' (%s:%d %s)\n", params[0], filerr.category().name(), filerr.value(), filerr.message()); return; } screen->machine().video().save_snapshot(screen, file); - m_console.printf("Saved screen #%d snapshot as '%s'\n", scrnum, filename); + m_console.printf("Saved screen #%d snapshot as '%s'\n", scrnum, params[0]); } } @@ -3121,9 +3794,10 @@ void debugger_commands::execute_snap(int ref, const std::vector<std::string> &pa execute_source - execute the source command -------------------------------------------------*/ -void debugger_commands::execute_source(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_source(const std::vector<std::string_view> ¶ms) { - m_console.source_script(params[0].c_str()); + std::string filename(params[0]); + m_console.source_script(filename.c_str()); } @@ -3131,34 +3805,29 @@ void debugger_commands::execute_source(int ref, const std::vector<std::string> & execute_map - execute the map command -------------------------------------------------*/ -void debugger_commands::execute_map(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_map(int spacenum, const std::vector<std::string_view> ¶ms) { - address_space *space; - offs_t taddress; + // validate parameters u64 address; - int intention; - - /* validate parameters */ - if (!validate_number_parameter(params[0], address)) - return; - - /* CPU is implicit */ - if (!validate_cpu_space_parameter(nullptr, ref, space)) + address_space *space; + if (!m_console.validate_target_address_parameter(params[0], spacenum, space, address)) return; + address &= space->logaddrmask(); - /* do the translation first */ - for (intention = TRANSLATE_READ_DEBUG; intention <= TRANSLATE_FETCH_DEBUG; intention++) + // do the translation first + for (int intention = device_memory_interface::TR_READ; intention <= device_memory_interface::TR_FETCH; intention++) { static const char *const intnames[] = { "Read", "Write", "Fetch" }; - taddress = address & space->addrmask(); - if (space->device().memory().translate(space->spacenum(), intention, taddress)) + offs_t taddress = address; + address_space *tspace; + if (space->device().memory().translate(space->spacenum(), intention, taddress, tspace)) { - std::string mapname = space->get_handler_string((intention == TRANSLATE_WRITE_DEBUG) ? read_or_write::WRITE : read_or_write::READ, taddress); + std::string mapname = tspace->get_handler_string((intention == device_memory_interface::TR_WRITE) ? read_or_write::WRITE : read_or_write::READ, taddress); m_console.printf( - "%7s: %0*X logical == %0*X physical -> %s\n", + "%7s: %0*X logical %s == %0*X physical %s -> %s\n", intnames[intention & 3], - space->logaddrchars(), address, - space->addrchars(), taddress, + space->logaddrchars(), address, space->name(), + tspace->addrchars(), taddress, tspace->name(), mapname); } else @@ -3171,20 +3840,29 @@ void debugger_commands::execute_map(int ref, const std::vector<std::string> &par execute_memdump - execute the memdump command -------------------------------------------------*/ -void debugger_commands::execute_memdump(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_memdump(const std::vector<std::string_view> ¶ms) { - FILE *file; - const char *filename; + device_t *root = &m_machine.root_device(); + if ((params.size() >= 2) && !m_console.validate_device_parameter(params[1], root)) + return; - filename = params.empty() ? "memdump.log" : params[0].c_str(); + using namespace std::literals; + std::string filename = params.empty() ? "memdump.log"s : std::string(params[0]); + FILE *const file = fopen(filename.c_str(), "w"); + if (!file) + { + m_console.printf("Error opening file %s\n", filename); + return; + } - m_console.printf("Dumping memory to %s\n", filename); + m_console.printf("Dumping memory maps to %s\n", filename); - file = fopen(filename, "w"); - if (file) + try { - memory_interface_iterator iter(m_machine.root_device()); - for (device_memory_interface &memory : iter) { + memory_interface_enumerator iter(*root); + std::vector<memory_entry> entries[2]; + for (device_memory_interface &memory : iter) + { for (int space = 0; space != memory.max_space_count(); space++) if (memory.has_space(space)) { @@ -3192,25 +3870,36 @@ void debugger_commands::execute_memdump(int ref, const std::vector<std::string> bool octal = sp.is_octal(); int nc = octal ? (sp.addr_width() + 2) / 3 : (sp.addr_width() + 3) / 4; - std::vector<memory_entry> entries[2]; sp.dump_maps(entries[0], entries[1]); for (int mode = 0; mode < 2; mode ++) { - fprintf(file, " device %s space %s %s:\n", memory.device().tag(), sp.name(), mode ? "write" : "read"); + fprintf(file, " %s '%s' space %s %s:\n", memory.device().type().fullname(), memory.device().tag(), sp.name(), mode ? "write" : "read"); for (memory_entry &entry : entries[mode]) { if (octal) - fprintf(file, "%0*o - %0*o", nc, entry.start, nc, entry.end); + fprintf(file, "%0*o - %0*o:", nc, entry.start, nc, entry.end); else - fprintf(file, "%0*x - %0*x", nc, entry.start, nc, entry.end); - fprintf(file, ": %s\n", entry.entry->name().c_str()); + fprintf(file, "%0*x - %0*x:", nc, entry.start, nc, entry.end); + for (const auto &c : entry.context) + if (c.disabled) + fprintf(file, " %s[off]", c.view->name().c_str()); + else + fprintf(file, " %s[%d]", c.view->name().c_str(), c.slot); + fprintf(file, " %s\n", entry.entry->name().c_str()); } fprintf(file, "\n"); } + entries[0].clear(); + entries[1].clear(); } } fclose(file); } + catch (...) + { + fclose(file); + throw; + } } @@ -3218,53 +3907,56 @@ void debugger_commands::execute_memdump(int ref, const std::vector<std::string> execute_symlist - execute the symlist command -------------------------------------------------*/ -void debugger_commands::execute_symlist(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_symlist(const std::vector<std::string_view> ¶ms) { - device_t *cpu = nullptr; const char *namelist[1000]; symbol_table *symtable; - int symnum, count = 0; + int count = 0; if (!params.empty()) { - /* validate parameters */ - if (!validate_cpu_parameter(params[0].c_str(), cpu)) + // validate parameters + device_t *cpu; + if (!m_console.validate_cpu_parameter(params[0], cpu)) return; symtable = &cpu->debug()->symtable(); m_console.printf("CPU '%s' symbols:\n", cpu->tag()); } else { - symtable = m_cpu.get_global_symtable(); + symtable = &m_machine.debugger().cpu().global_symtable(); m_console.printf("Global symbols:\n"); } - /* gather names for all symbols */ + // gather names for all symbols for (auto &entry : symtable->entries()) { - /* only display "register" type symbols */ + // only display "register" type symbols if (!entry.second->is_function()) { namelist[count++] = entry.second->name(); - if (count >= ARRAY_LENGTH(namelist)) + if (count >= std::size(namelist)) break; } } - /* sort the symbols */ + // sort the symbols if (count > 1) - std::sort(&namelist[0], &namelist[count], [](const char *item1, const char *item2) { - return strcmp(item1, item2) < 0; - }); + { + std::sort( + &namelist[0], + &namelist[count], + [] (const char *item1, const char *item2) { return strcmp(item1, item2) < 0; }); + } - /* iterate over symbols and print out relevant ones */ - for (symnum = 0; symnum < count; symnum++) + // iterate over symbols and print out relevant ones + for (int symnum = 0; symnum < count; symnum++) { - const symbol_entry *entry = symtable->find(namelist[symnum]); + symbol_entry const *const entry = symtable->find(namelist[symnum]); assert(entry != nullptr); u64 value = entry->value(); - /* only display "register" type symbols */ + // only display "register" type symbols m_console.printf("%s = %X", namelist[symnum], value); if (!entry->is_lval()) m_console.printf(" (read-only)"); @@ -3277,7 +3969,7 @@ void debugger_commands::execute_symlist(int ref, const std::vector<std::string> execute_softreset - execute the softreset command -------------------------------------------------*/ -void debugger_commands::execute_softreset(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_softreset(const std::vector<std::string_view> ¶ms) { m_machine.schedule_soft_reset(); } @@ -3287,7 +3979,7 @@ void debugger_commands::execute_softreset(int ref, const std::vector<std::string execute_hardreset - execute the hardreset command -------------------------------------------------*/ -void debugger_commands::execute_hardreset(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_hardreset(const std::vector<std::string_view> ¶ms) { m_machine.schedule_hard_reset(); } @@ -3297,57 +3989,84 @@ void debugger_commands::execute_hardreset(int ref, const std::vector<std::string mounted files -------------------------------------------------*/ -void debugger_commands::execute_images(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_images(const std::vector<std::string_view> ¶ms) { - image_interface_iterator iter(m_machine.root_device()); + image_interface_enumerator iter(m_machine.root_device()); for (device_image_interface &img : iter) - m_console.printf("%s: %s\n", img.brief_instance_name(), img.exists() ? img.filename() : "[empty slot]"); - if (iter.first() == nullptr) - m_console.printf("No image devices in this driver\n"); + { + if (!img.exists()) + { + m_console.printf("%s: [no media]\n", img.brief_instance_name()); + } + else if (img.loaded_through_softlist()) + { + m_console.printf("%s: %s:%s:%s\n", + img.brief_instance_name(), + img.software_list_name(), + img.software_entry()->shortname(), + img.part_entry()->name()); + } + else + { + m_console.printf("%s: %s\n", img.brief_instance_name(), img.filename()); + } + } + if (!iter.first()) + m_console.printf("No image devices present\n"); } /*------------------------------------------------- execute_mount - execute the image mount command -------------------------------------------------*/ -void debugger_commands::execute_mount(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_mount(const std::vector<std::string_view> ¶ms) { - bool done = false; - for (device_image_interface &img : image_interface_iterator(m_machine.root_device())) + for (device_image_interface &img : image_interface_enumerator(m_machine.root_device())) { - if (img.brief_instance_name() == params[0]) + if ((img.instance_name() == params[0]) || (img.brief_instance_name() == params[0])) { - if (img.load(params[1]) != image_init_result::PASS) - m_console.printf("Unable to mount file %s on %s\n", params[1], params[0]); - else + auto [err, msg] = img.load(params[1]); + if (!err) + { m_console.printf("File %s mounted on %s\n", params[1], params[0]); - done = true; - break; + } + else + { + m_console.printf( + "Unable to mount file %s on %s: %s\n", + params[1], + params[0], + !msg.empty() ? msg : err.message()); + } + return; } } - if (!done) - m_console.printf("There is no image device :%s\n", params[0].c_str()); + m_console.printf("No image instance %s\n", params[0]); } /*------------------------------------------------- execute_unmount - execute the image unmount command -------------------------------------------------*/ -void debugger_commands::execute_unmount(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_unmount(const std::vector<std::string_view> ¶ms) { - bool done = false; - for (device_image_interface &img : image_interface_iterator(m_machine.root_device())) + for (device_image_interface &img : image_interface_enumerator(m_machine.root_device())) { - if (img.brief_instance_name() == params[0]) + if ((img.instance_name() == params[0]) || (img.brief_instance_name() == params[0])) { - img.unload(); - m_console.printf("Unmounted file from : %s\n", params[0]); - done = true; - break; + if (img.exists()) + { + img.unload(); + m_console.printf("Unmounted media from %s\n", params[0]); + } + else + { + m_console.printf("No media mounted on %s\n", params[0]); + } + return; } } - if (!done) - m_console.printf("There is no image device :%s\n", params[0]); + m_console.printf("No image instance %s\n", params[0]); } @@ -3356,9 +4075,9 @@ void debugger_commands::execute_unmount(int ref, const std::vector<std::string> natural keyboard input -------------------------------------------------*/ -void debugger_commands::execute_input(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_input(const std::vector<std::string_view> ¶ms) { - m_machine.ioport().natkeyboard().post_coded(params[0].c_str()); + m_machine.natkeyboard().post_coded(params[0]); } @@ -3367,15 +4086,15 @@ void debugger_commands::execute_input(int ref, const std::vector<std::string> &p keyboard codes -------------------------------------------------*/ -void debugger_commands::execute_dumpkbd(int ref, const std::vector<std::string> ¶ms) +void debugger_commands::execute_dumpkbd(const std::vector<std::string_view> ¶ms) { // was there a file specified? - const char *filename = !params.empty() ? params[0].c_str() : nullptr; + std::string filename = !params.empty() ? std::string(params[0]) : std::string(); FILE *file = nullptr; - if (filename != nullptr) + if (!filename.empty()) { // if so, open it - file = fopen(filename, "w"); + file = fopen(filename.c_str(), "w"); if (file == nullptr) { m_console.printf("Cannot open \"%s\"\n", filename); @@ -3384,13 +4103,13 @@ void debugger_commands::execute_dumpkbd(int ref, const std::vector<std::string> } // loop through all codes - std::string buffer = m_machine.ioport().natkeyboard().dump(); + std::string buffer = m_machine.natkeyboard().dump(); // and output it as appropriate if (file != nullptr) fprintf(file, "%s\n", buffer.c_str()); else - m_console.printf("%s\n", buffer.c_str()); + m_console.printf("%s\n", buffer); // cleanup if (file != nullptr) diff --git a/src/emu/debug/debugcmd.h b/src/emu/debug/debugcmd.h index 862ad93a45a..cf38c6d903a 100644 --- a/src/emu/debug/debugcmd.h +++ b/src/emu/debug/debugcmd.h @@ -13,26 +13,13 @@ #pragma once -#include "debugcpu.h" -#include "debugcon.h" +#include <string_view> class debugger_commands { public: - debugger_commands(running_machine& machine, debugger_cpu& cpu, debugger_console& console); - - /* validates a parameter as a boolean value */ - bool validate_boolean_parameter(const std::string ¶m, bool &result); - - /* validates a parameter as a numeric value */ - bool validate_number_parameter(const std::string ¶m, u64 &result); - - /* validates a parameter as a cpu */ - bool validate_cpu_parameter(const char *param, device_t *&result); - - /* validates a parameter as a cpu and retrieves the given address space */ - bool validate_cpu_space_parameter(const char *param, int spacenum, address_space *&result); + debugger_commands(running_machine &machine, debugger_cpu &cpu, debugger_console &console); private: struct global_entry @@ -56,117 +43,122 @@ private: // TODO [RH 31 May 2016]: Move this cheat stuff into its own class struct cheat_system { - char cpu[2]; + address_space *space; u8 width; - std::vector<cheat_map> cheatmap; - u8 undo; u8 signed_cheat; u8 swapped_cheat; - }; + std::vector<cheat_map> cheatmap; + u8 undo; + u64 sign_extend(u64 value) const; + u64 byte_swap(u64 value) const; + u64 read_extended(offs_t address) const; + }; struct cheat_region_map { - u64 offset; - u64 endoffset; - const char *share; - u8 disabled; + u64 offset = 0U; + u64 endoffset = 0U; + const char *share = nullptr; + u8 disabled = 0U; }; - bool debug_command_parameter_expression(const std::string ¶m, parsed_expression &result); - bool debug_command_parameter_command(const char *param); - bool cheat_address_is_valid(address_space &space, offs_t address); - u64 cheat_sign_extend(const cheat_system *cheatsys, u64 value); - u64 cheat_byte_swap(const cheat_system *cheatsys, u64 value); - u64 cheat_read_extended(const cheat_system *cheatsys, address_space &space, offs_t address); - - u64 execute_min(symbol_table &table, int params, const u64 *param); - u64 execute_max(symbol_table &table, int params, const u64 *param); - u64 execute_if(symbol_table &table, int params, const u64 *param); - - u64 global_get(symbol_table &table, global_entry *global); - void global_set(symbol_table &table, global_entry *global, u64 value); - - int mini_printf(char *buffer, const char *format, int params, u64 *param); - - void execute_trace_internal(int ref, const std::vector<std::string> ¶ms, bool trace_over); - - void execute_help(int ref, const std::vector<std::string> ¶ms); - void execute_print(int ref, const std::vector<std::string> ¶ms); - void execute_printf(int ref, const std::vector<std::string> ¶ms); - void execute_logerror(int ref, const std::vector<std::string> ¶ms); - void execute_tracelog(int ref, const std::vector<std::string> ¶ms); - void execute_tracesym(int ref, const std::vector<std::string> ¶ms); - void execute_quit(int ref, const std::vector<std::string> ¶ms); - void execute_do(int ref, const std::vector<std::string> ¶ms); - void execute_step(int ref, const std::vector<std::string> ¶ms); - void execute_over(int ref, const std::vector<std::string> ¶ms); - void execute_out(int ref, const std::vector<std::string> ¶ms); - void execute_go(int ref, const std::vector<std::string> ¶ms); - void execute_go_vblank(int ref, const std::vector<std::string> ¶ms); - void execute_go_interrupt(int ref, const std::vector<std::string> ¶ms); - void execute_go_exception(int ref, const std::vector<std::string> ¶ms); - void execute_go_time(int ref, const std::vector<std::string> ¶ms); - void execute_go_privilege(int ref, const std::vector<std::string> ¶ms); - void execute_focus(int ref, const std::vector<std::string> ¶ms); - void execute_ignore(int ref, const std::vector<std::string> ¶ms); - void execute_observe(int ref, const std::vector<std::string> ¶ms); - void execute_suspend(int ref, const std::vector<std::string> ¶ms); - void execute_resume(int ref, const std::vector<std::string> ¶ms); - void execute_next(int ref, const std::vector<std::string> ¶ms); - void execute_comment_add(int ref, const std::vector<std::string> ¶ms); - void execute_comment_del(int ref, const std::vector<std::string> ¶ms); - void execute_comment_save(int ref, const std::vector<std::string> ¶ms); - void execute_comment_list(int ref, const std::vector<std::string> ¶ms); - void execute_comment_commit(int ref, const std::vector<std::string> ¶ms); - void execute_bpset(int ref, const std::vector<std::string> ¶ms); - void execute_bpclear(int ref, const std::vector<std::string> ¶ms); - void execute_bpdisenable(int ref, const std::vector<std::string> ¶ms); - void execute_bplist(int ref, const std::vector<std::string> ¶ms); - void execute_wpset(int ref, const std::vector<std::string> ¶ms); - void execute_wpclear(int ref, const std::vector<std::string> ¶ms); - void execute_wpdisenable(int ref, const std::vector<std::string> ¶ms); - void execute_wplist(int ref, const std::vector<std::string> ¶ms); - void execute_rpset(int ref, const std::vector<std::string> ¶ms); - void execute_rpclear(int ref, const std::vector<std::string> ¶ms); - void execute_rpdisenable(int ref, const std::vector<std::string> ¶ms); - void execute_rplist(int ref, const std::vector<std::string> ¶ms); - void execute_hotspot(int ref, const std::vector<std::string> ¶ms); - void execute_statesave(int ref, const std::vector<std::string> ¶ms); - void execute_stateload(int ref, const std::vector<std::string> ¶ms); - void execute_rewind(int ref, const std::vector<std::string> ¶ms); - void execute_save(int ref, const std::vector<std::string> ¶ms); - void execute_load(int ref, const std::vector<std::string> ¶ms); - void execute_dump(int ref, const std::vector<std::string> ¶ms); - void execute_cheatinit(int ref, const std::vector<std::string> ¶ms); - void execute_cheatnext(int ref, const std::vector<std::string> ¶ms); - void execute_cheatlist(int ref, const std::vector<std::string> ¶ms); - void execute_cheatundo(int ref, const std::vector<std::string> ¶ms); - void execute_dasm(int ref, const std::vector<std::string> ¶ms); - void execute_find(int ref, const std::vector<std::string> ¶ms); - void execute_trace(int ref, const std::vector<std::string> ¶ms); - void execute_traceover(int ref, const std::vector<std::string> ¶ms); - void execute_traceflush(int ref, const std::vector<std::string> ¶ms); - void execute_history(int ref, const std::vector<std::string> ¶ms); - void execute_trackpc(int ref, const std::vector<std::string> ¶ms); - void execute_trackmem(int ref, const std::vector<std::string> ¶ms); - void execute_pcatmem(int ref, const std::vector<std::string> ¶ms); - void execute_snap(int ref, const std::vector<std::string> ¶ms); - void execute_source(int ref, const std::vector<std::string> ¶ms); - void execute_map(int ref, const std::vector<std::string> ¶ms); - void execute_memdump(int ref, const std::vector<std::string> ¶ms); - void execute_symlist(int ref, const std::vector<std::string> ¶ms); - void execute_softreset(int ref, const std::vector<std::string> ¶ms); - void execute_hardreset(int ref, const std::vector<std::string> ¶ms); - void execute_images(int ref, const std::vector<std::string> ¶ms); - void execute_mount(int ref, const std::vector<std::string> ¶ms); - void execute_unmount(int ref, const std::vector<std::string> ¶ms); - void execute_input(int ref, const std::vector<std::string> ¶ms); - void execute_dumpkbd(int ref, const std::vector<std::string> ¶ms); + + u64 get_cpunum(); + + u64 global_get(global_entry *global); + void global_set(global_entry *global, u64 value); + + bool mini_printf(std::ostream &stream, const std::vector<std::string_view> ¶ms); + template <typename T> + void execute_index_command(std::vector<std::string_view> const ¶ms, T &&apply, char const *unused_message); + + void execute_help(const std::vector<std::string_view> ¶ms); + void execute_print(const std::vector<std::string_view> ¶ms); + void execute_printf(const std::vector<std::string_view> ¶ms); + void execute_logerror(const std::vector<std::string_view> ¶ms); + void execute_tracelog(const std::vector<std::string_view> ¶ms); + void execute_tracesym(const std::vector<std::string_view> ¶ms); + void execute_cls(const std::vector<std::string_view> ¶ms); + void execute_quit(const std::vector<std::string_view> ¶ms); + void execute_do(const std::vector<std::string_view> ¶ms); + void execute_step(const std::vector<std::string_view> ¶ms); + void execute_over(const std::vector<std::string_view> ¶ms); + void execute_out(const std::vector<std::string_view> ¶ms); + void execute_go(const std::vector<std::string_view> ¶ms); + void execute_go_vblank(const std::vector<std::string_view> ¶ms); + void execute_go_interrupt(const std::vector<std::string_view> ¶ms); + void execute_go_exception(const std::vector<std::string_view> ¶ms); + void execute_go_time(const std::vector<std::string_view> ¶ms); + void execute_go_privilege(const std::vector<std::string_view> ¶ms); + void execute_go_branch(bool sense, const std::vector<std::string_view> ¶ms); + void execute_go_next_instruction(const std::vector<std::string_view> ¶ms); + void execute_focus(const std::vector<std::string_view> ¶ms); + void execute_ignore(const std::vector<std::string_view> ¶ms); + void execute_observe(const std::vector<std::string_view> ¶ms); + void execute_suspend(const std::vector<std::string_view> ¶ms); + void execute_resume(const std::vector<std::string_view> ¶ms); + void execute_next(const std::vector<std::string_view> ¶ms); + void execute_cpulist(const std::vector<std::string_view> ¶ms); + void execute_time(const std::vector<std::string_view> ¶ms); + void execute_comment_add(const std::vector<std::string_view> ¶ms); + void execute_comment_del(const std::vector<std::string_view> ¶ms); + void execute_comment_save(const std::vector<std::string_view> ¶ms); + void execute_comment_list(const std::vector<std::string_view> ¶ms); + void execute_comment_commit(const std::vector<std::string_view> ¶ms); + void execute_bpset(const std::vector<std::string_view> ¶ms); + void execute_bpclear(const std::vector<std::string_view> ¶ms); + void execute_bpdisenable(bool enable, const std::vector<std::string_view> ¶ms); + void execute_bplist(const std::vector<std::string_view> ¶ms); + void execute_wpset(int spacenum, const std::vector<std::string_view> ¶ms); + void execute_wpclear(const std::vector<std::string_view> ¶ms); + void execute_wpdisenable(bool enable, const std::vector<std::string_view> ¶ms); + void execute_wplist(const std::vector<std::string_view> ¶ms); + void execute_rpset(const std::vector<std::string_view> ¶ms); + void execute_rpclear(const std::vector<std::string_view> ¶ms); + void execute_rpdisenable(bool enable, const std::vector<std::string_view> ¶ms); + void execute_rplist(const std::vector<std::string_view> ¶ms); + void execute_epset(const std::vector<std::string_view> ¶ms); + void execute_epclear(const std::vector<std::string_view> ¶ms); + void execute_epdisenable(bool enable, const std::vector<std::string_view> ¶ms); + void execute_eplist(const std::vector<std::string_view> ¶ms); + void execute_statesave(const std::vector<std::string_view> ¶ms); + void execute_stateload(const std::vector<std::string_view> ¶ms); + void execute_rewind(const std::vector<std::string_view> ¶ms); + void execute_save(int spacenum, const std::vector<std::string_view> ¶ms); + void execute_saveregion(const std::vector<std::string_view> ¶ms); + void execute_load(int spacenum, const std::vector<std::string_view> ¶ms); + void execute_loadregion(const std::vector<std::string_view> ¶ms); + void execute_dump(int spacenum, const std::vector<std::string_view> ¶ms); + void execute_strdump(int spacenum, const std::vector<std::string_view> ¶ms); + void execute_cheatrange(bool init, const std::vector<std::string_view> ¶ms); + void execute_cheatnext(bool initial, const std::vector<std::string_view> ¶ms); + void execute_cheatlist(const std::vector<std::string_view> ¶ms); + void execute_cheatundo(const std::vector<std::string_view> ¶ms); + void execute_dasm(const std::vector<std::string_view> ¶ms); + void execute_find(int spacenum, const std::vector<std::string_view> ¶ms); + void execute_fill(int spacenum, const std::vector<std::string_view> ¶ms); + void execute_trace(const std::vector<std::string_view> ¶ms, bool trace_over); + void execute_traceflush(const std::vector<std::string_view> ¶ms); + void execute_history(const std::vector<std::string_view> ¶ms); + void execute_trackpc(const std::vector<std::string_view> ¶ms); + void execute_trackmem(const std::vector<std::string_view> ¶ms); + void execute_pcatmem(int spacenum, const std::vector<std::string_view> ¶ms); + void execute_snap(const std::vector<std::string_view> ¶ms); + void execute_source(const std::vector<std::string_view> ¶ms); + void execute_map(int spacenum, const std::vector<std::string_view> ¶ms); + void execute_memdump(const std::vector<std::string_view> ¶ms); + void execute_symlist(const std::vector<std::string_view> ¶ms); + void execute_softreset(const std::vector<std::string_view> ¶ms); + void execute_hardreset(const std::vector<std::string_view> ¶ms); + void execute_images(const std::vector<std::string_view> ¶ms); + void execute_mount(const std::vector<std::string_view> ¶ms); + void execute_unmount(const std::vector<std::string_view> ¶ms); + void execute_input(const std::vector<std::string_view> ¶ms); + void execute_dumpkbd(const std::vector<std::string_view> ¶ms); running_machine& m_machine; - debugger_cpu& m_cpu; debugger_console& m_console; std::unique_ptr<global_entry []> m_global_array; diff --git a/src/emu/debug/debugcon.cpp b/src/emu/debug/debugcon.cpp index 8faf4f0de02..ec5bc154751 100644 --- a/src/emu/debug/debugcon.cpp +++ b/src/emu/debug/debugcon.cpp @@ -1,8 +1,8 @@ // license:BSD-3-Clause -// copyright-holders:Aaron Giles +// copyright-holders:Aaron Giles, Vas Crabb /********************************************************************* - debugcon.c + debugcon.cpp Debugger console engine. @@ -10,12 +10,21 @@ #include "emu.h" #include "debugcon.h" + #include "debugcpu.h" #include "debugvw.h" #include "textbuf.h" + #include "debugger.h" -#include <ctype.h> +#include "fileio.h" +#include "main.h" + +#include "corestr.h" + +#include <cctype> #include <fstream> +#include <iterator> + /*************************************************************************** CONSTANTS @@ -35,9 +44,10 @@ debugger_console::debugger_console(running_machine &machine) : m_machine(machine) + , m_visiblecpu(nullptr) , m_console_textbuf(nullptr) , m_errorlog_textbuf(nullptr) - , m_commandlist(nullptr) + , m_logfile(nullptr) { /* allocate text buffers */ m_console_textbuf = text_buffer_alloc(CONSOLE_BUF_SIZE, CONSOLE_MAX_LINES); @@ -48,6 +58,9 @@ debugger_console::debugger_console(running_machine &machine) if (!m_errorlog_textbuf) return; + /* due to initialization order, @machine is holding our debug.log handle */ + m_logfile = machine.steal_debuglogfile(); + /* print the opening lines */ printf("%s debugger version %s\n", emulator_info::get_appname(), emulator_info::get_build_version()); printf("Currently targeting %s (%s)\n", m_machine.system().name, m_machine.system().type.fullname()); @@ -60,31 +73,41 @@ debugger_console::debugger_console(running_machine &machine) m_machine.add_logerror_callback(std::bind(&debugger_console::errorlog_write_line, this, _1)); /* register our own custom-command help */ - register_command("helpcustom", CMDFLAG_NONE, 0, 0, 0, std::bind(&debugger_console::execute_help_custom, this, _1, _2)); + register_command("helpcustom", CMDFLAG_NONE, 0, 0, std::bind(&debugger_console::execute_help_custom, this, _1)); + register_command("condump", CMDFLAG_NONE, 1, 1, std::bind(&debugger_console::execute_condump, this, _1)); + + /* first CPU is visible by default */ + for (device_t &device : device_enumerator(m_machine.root_device())) + { + auto *cpu = dynamic_cast<cpu_device *>(&device); + if (cpu) + { + m_visiblecpu = cpu; + break; + } + } } +debugger_console::~debugger_console() +{ +} -/*------------------------------------------------- - exit - frees the console system --------------------------------------------------*/ + +//------------------------------------------------- +// exit - frees the console system +//------------------------------------------------- void debugger_console::exit() { - /* free allocated memory */ - if (m_console_textbuf) - { - text_buffer_free(m_console_textbuf); - } - m_console_textbuf = nullptr; + // free allocated memory + m_console_textbuf.reset(); + m_errorlog_textbuf.reset(); - if (m_errorlog_textbuf) - { - text_buffer_free(m_errorlog_textbuf); - } - m_errorlog_textbuf = nullptr; + // free the command list + m_commandlist.clear(); - /* free the command list */ - m_commandlist = nullptr; + // close the logfile, if any + m_logfile.reset(); } @@ -95,192 +118,230 @@ void debugger_console::exit() ***************************************************************************/ -/*------------------------------------------------------------ - execute_help_custom - execute the helpcustom command -------------------------------------------------------------*/ +inline bool debugger_console::debug_command::compare::operator()(const debug_command &a, const debug_command &b) const +{ + return a.command < b.command; +} + +inline bool debugger_console::debug_command::compare::operator()(const char *a, const debug_command &b) const +{ + return strcmp(a, b.command.c_str()) < 0; +} -void debugger_console::execute_help_custom(int ref, const std::vector<std::string> ¶ms) +inline bool debugger_console::debug_command::compare::operator()(const debug_command &a, const char *b) const { - debug_command *cmd = m_commandlist; - char buf[64]; - while (cmd) + return strcmp(a.command.c_str(), b) < 0; +} + + +debugger_console::debug_command::debug_command(std::string_view _command, u32 _flags, int _minparams, int _maxparams, std::function<void (const std::vector<std::string_view> &)> &&_handler) + : command(_command), params(nullptr), help(nullptr), handler(std::move(_handler)), flags(_flags), minparams(_minparams), maxparams(_maxparams) +{ +} + + +//------------------------------------------------------------ +// execute_help_custom - execute the helpcustom command +//------------------------------------------------------------ + +void debugger_console::execute_help_custom(const std::vector<std::string_view> ¶ms) +{ + for (const debug_command &cmd : m_commandlist) { - if (cmd->flags & CMDFLAG_CUSTOM_HELP) + if (cmd.flags & CMDFLAG_CUSTOM_HELP) { - snprintf(buf, 63, "%s help", cmd->command); - buf[63] = 0; - char *temp_params[1] = { buf }; - internal_execute_command(true, 1, &temp_params[0]); + std::string buf = cmd.command + " help"; + std::vector<std::string_view> temp_params = { buf }; + internal_execute_command(true, temp_params); } - cmd = cmd->next; } } +/*------------------------------------------------------------ + execute_condump - execute the condump command +------------------------------------------------------------*/ -/*------------------------------------------------- - trim_parameter - executes a - command --------------------------------------------------*/ +void debugger_console::execute_condump(const std::vector<std::string_view>& params) +{ + std::string filename(params[0]); + const char* mode; + + /* replace macros */ + strreplace(filename, "{game}", m_machine.basename()); + + mode = "w"; + /* opening for append? */ + if (filename.length() >= 2 && filename[0] == '>' && filename[1] == '>') + { + mode = "a"; + filename = filename.substr(2); + } + + FILE* f = fopen(filename.c_str(), mode); + if (!f) + { + printf("Error opening file '%s'\n", filename); + return; + } + + for (std::string_view line_info : text_buffer_lines(*m_console_textbuf)) + { + fwrite(line_info.data(), sizeof(char), line_info.length(), f); + fputc('\n', f); + } -void debugger_console::trim_parameter(char **paramptr, bool keep_quotes) + fclose(f); + printf("Wrote console contents to '%s'\n", filename); +} + +//------------------------------------------------- +// visible_symtable - return the locally-visible +// symbol table +//------------------------------------------------- + +symbol_table &debugger_console::visible_symtable() const +{ + return m_visiblecpu->debug()->symtable(); +} + + + +//------------------------------------------------- +// trim_parameter - trim spaces and quotes around +// a command parameter +//------------------------------------------------- + +std::string_view debugger_console::trim_parameter(std::string_view param, bool keep_quotes) { - char *param = *paramptr; - size_t len = strlen(param); + std::string_view::size_type len = param.length(); bool repeat; - /* loop until all adornments are gone */ + // loop until all adornments are gone do { repeat = false; - /* check for begin/end quotes */ + // check for begin/end quotes if (len >= 2 && param[0] == '"' && param[len - 1] == '"') { if (!keep_quotes) { - param[len - 1] = 0; - param++; + param = param.substr(1, len - 2); len -= 2; } } - /* check for start/end braces */ + // check for start/end braces else if (len >= 2 && param[0] == '{' && param[len - 1] == '}') { - param[len - 1] = 0; - param++; + param = param.substr(1, len - 2); len -= 2; repeat = true; } - /* check for leading spaces */ + // check for leading spaces else if (len >= 1 && param[0] == ' ') { - param++; + param.remove_prefix(1); len--; repeat = true; } - /* check for trailing spaces */ + // check for trailing spaces else if (len >= 1 && param[len - 1] == ' ') { - param[len - 1] = 0; + param.remove_suffix(1); len--; repeat = true; } } while (repeat); - *paramptr = param; + return param; } -/*------------------------------------------------- - internal_execute_command - executes a - command --------------------------------------------------*/ +//------------------------------------------------- +// internal_execute_command - executes a +// command +//------------------------------------------------- -CMDERR debugger_console::internal_execute_command(bool execute, int params, char **param) +CMDERR debugger_console::internal_execute_command(bool execute, std::vector<std::string_view> ¶ms) { - debug_command *cmd, *found = nullptr; - int i, foundcount = 0; - char *p, *command; - size_t len; + // no params is an error + if (params.empty()) + return CMDERR::none(); + + // the first parameter has the command and the real first parameter; separate them + std::string_view command_param = params[0]; + std::string_view::size_type pos = 0; + while (pos < command_param.length() && !isspace(u8(command_param[pos]))) + pos++; + const std::string command(strmakelower(command_param.substr(0, pos))); + while (pos < command_param.length() && isspace(u8(command_param[pos]))) + pos++; + if (pos == command_param.length() && params.size() == 1) + params.clear(); + else + params[0].remove_prefix(pos); - /* no params is an error */ - if (params == 0) - return CMDERR_NONE; + // search the command list + auto const found = m_commandlist.lower_bound(command.c_str()); - /* the first parameter has the command and the real first parameter; separate them */ - for (p = param[0]; *p && isspace(u8(*p)); p++) { } - for (command = p; *p && !isspace(u8(*p)); p++) { } - if (*p != 0) + // error if not found + if (m_commandlist.end() == found || std::string_view(command) != std::string_view(found->command).substr(0, command.length())) + return CMDERR::unknown_command(0); + if (found->command.length() > command.length()) { - *p++ = 0; - for ( ; *p && isspace(u8(*p)); p++) { } - if (*p != 0) - param[0] = p; - else - params = 0; + auto const next = std::next(found); + if (m_commandlist.end() != next && std::string_view(command) == std::string_view(next->command).substr(0, command.length())) + return CMDERR::ambiguous_command(0); } - else - { - params = 0; - param[0] = nullptr; - } - - /* search the command list */ - len = strlen(command); - for (cmd = m_commandlist; cmd != nullptr; cmd = cmd->next) - if (!strncmp(command, cmd->command, len)) - { - foundcount++; - found = cmd; - if (strlen(cmd->command) == len) - { - foundcount = 1; - break; - } - } - /* error if not found */ - if (!found) - return MAKE_CMDERR_UNKNOWN_COMMAND(0); - if (foundcount > 1) - return MAKE_CMDERR_AMBIGUOUS_COMMAND(0); + // now go back and trim quotes and braces and any spaces they reveal + for (std::string_view ¶m : params) + param = trim_parameter(param, found->flags & CMDFLAG_KEEP_QUOTES); - /* NULL-terminate and trim space around all the parameters */ - for (i = 1; i < params; i++) - *param[i]++ = 0; + // see if we have the right number of parameters + if (params.size() < found->minparams) + return CMDERR::not_enough_params(0); + if (params.size() > found->maxparams) + return CMDERR::too_many_params(0); - /* now go back and trim quotes and braces and any spaces they reveal*/ - for (i = 0; i < params; i++) - trim_parameter(¶m[i], found->flags & CMDFLAG_KEEP_QUOTES); - - /* see if we have the right number of parameters */ - if (params < found->minparams) - return MAKE_CMDERR_NOT_ENOUGH_PARAMS(0); - if (params > found->maxparams) - return MAKE_CMDERR_TOO_MANY_PARAMS(0); - - /* execute the handler */ + // execute the handler if (execute) - { - std::vector<std::string> params_vec(param, param + params); - found->handler(found->ref, params_vec); - } - return CMDERR_NONE; + found->handler(params); + return CMDERR::none(); } -/*------------------------------------------------- - internal_parse_command - parses a command - and either executes or just validates it --------------------------------------------------*/ +//------------------------------------------------- +// internal_parse_command - parses a command +// and either executes or just validates it +//------------------------------------------------- -CMDERR debugger_console::internal_parse_command(const std::string &original_command, bool execute) +CMDERR debugger_console::internal_parse_command(std::string_view command, bool execute) { - char command[MAX_COMMAND_LENGTH], parens[MAX_COMMAND_LENGTH]; - char *params[MAX_COMMAND_PARAMS] = { nullptr }; - CMDERR result; - char *command_start; - char *p, c = 0; - - /* make a copy of the command */ - strcpy(command, original_command.c_str()); + std::string_view::size_type pos = 0; + std::string_view::size_type len = command.length(); - /* loop over all semicolon-separated stuff */ - for (p = command; *p != 0; ) + while (pos < len) { - int paramcount = 0, parendex = 0; + std::string parens; + std::vector<std::string_view> params; bool foundend = false, instring = false, isexpr = false; - /* find a semicolon or the end */ - for (params[paramcount++] = p; !foundend; p++) + // skip leading spaces + while (pos < len && isspace(u8(command[pos]))) + pos++; + std::string_view::size_type startpos = pos; + + // find a semicolon or the end + for (params.push_back(command.substr(pos)); !foundend && pos < len; pos++) { - c = *p; + char c = command[pos]; if (instring) { - if (c == '"' && p[-1] != '\\') + if (c == '"' && command[pos - 1] != '\\') instring = false; } else @@ -290,90 +351,84 @@ CMDERR debugger_console::internal_parse_command(const std::string &original_comm case '"': instring = true; break; case '(': case '[': - case '{': parens[parendex++] = c; break; - case ')': if (parendex == 0 || parens[--parendex] != '(') return MAKE_CMDERR_UNBALANCED_PARENS(p - command); break; - case ']': if (parendex == 0 || parens[--parendex] != '[') return MAKE_CMDERR_UNBALANCED_PARENS(p - command); break; - case '}': if (parendex == 0 || parens[--parendex] != '{') return MAKE_CMDERR_UNBALANCED_PARENS(p - command); break; - case ',': if (parendex == 0) params[paramcount++] = p; break; - case ';': if (parendex == 0) foundend = true; break; - case '-': if (parendex == 0 && paramcount == 1 && p[1] == '-') isexpr = true; *p = c; break; - case '+': if (parendex == 0 && paramcount == 1 && p[1] == '+') isexpr = true; *p = c; break; - case '=': if (parendex == 0 && paramcount == 1) isexpr = true; *p = c; break; - case 0: foundend = true; break; - default: *p = tolower(u8(c)); break; + case '{': parens.push_back(c); break; + case ')': if (parens.empty() || parens.back() != '(') return CMDERR::unbalanced_parens(pos); parens.pop_back(); break; + case ']': if (parens.empty() || parens.back() != '[') return CMDERR::unbalanced_parens(pos); parens.pop_back(); break; + case '}': if (parens.empty() || parens.back() != '{') return CMDERR::unbalanced_parens(pos); parens.pop_back(); break; + case ',': if (parens.empty()) { params.back().remove_suffix(len - pos); params.push_back(command.substr(pos + 1)); } break; + case ';': if (parens.empty()) { params.back().remove_suffix(len - pos); foundend = true; } break; + case '-': if (parens.empty() && params.size() == 1 && pos > 0 && command[pos - 1] == '-') isexpr = true; break; + case '+': if (parens.empty() && params.size() == 1 && pos > 0 && command[pos - 1] == '+') isexpr = true; break; + case '=': if (parens.empty() && params.size() == 1) isexpr = true; break; + default: break; } } } - /* check for unbalanced parentheses or quotes */ + // check for unbalanced parentheses or quotes if (instring) - return MAKE_CMDERR_UNBALANCED_QUOTES(p - command); - if (parendex != 0) - return MAKE_CMDERR_UNBALANCED_PARENS(p - command); - - /* NULL-terminate if we ended in a semicolon */ - p--; - if (c == ';') *p++ = 0; + return CMDERR::unbalanced_quotes(pos); + if (!parens.empty()) + return CMDERR::unbalanced_parens(pos); - /* process the command */ - command_start = params[0]; + // process the command + std::string_view command_or_expr = params[0]; - /* allow for "do" commands */ - if (tolower(u8(command_start[0])) == 'd' && tolower(u8(command_start[1])) == 'o' && isspace(u8(command_start[2]))) + // allow for "do" commands + if (command_or_expr.length() > 3 && tolower(u8(command_or_expr[0])) == 'd' && tolower(u8(command_or_expr[1])) == 'o' && isspace(u8(command_or_expr[2]))) { isexpr = true; - command_start += 3; + command_or_expr.remove_prefix(3); } - /* if it smells like an assignment expression, treat it as such */ - if (isexpr && paramcount == 1) + // if it smells like an assignment expression, treat it as such + if (isexpr && params.size() == 1) { try { - u64 expresult; - parsed_expression expression(m_machine.debugger().cpu().get_visible_symtable(), command_start, &expresult); + parsed_expression expr(visible_symtable(), command_or_expr); + if (execute) + expr.execute(); } catch (expression_error &err) { - return MAKE_CMDERR_EXPRESSION_ERROR(err); + return CMDERR::expression_error(err); } } else { - result = internal_execute_command(execute, paramcount, ¶ms[0]); - if (result != CMDERR_NONE) - return MAKE_CMDERR(CMDERR_ERROR_CLASS(result), command_start - command); + const CMDERR result = internal_execute_command(execute, params); + if (result.error_class() != CMDERR::NONE) + return CMDERR(result.error_class(), startpos); } } - return CMDERR_NONE; + return CMDERR::none(); } -/*------------------------------------------------- - execute_command - execute a command string --------------------------------------------------*/ +//------------------------------------------------- +// execute_command - execute a command string +//------------------------------------------------- -CMDERR debugger_console::execute_command(const std::string &command, bool echo) +CMDERR debugger_console::execute_command(std::string_view command, bool echo) { - CMDERR result; - - /* echo if requested */ + // echo if requested if (echo) - printf(">%s\n", command.c_str()); + printf(">%s\n", command); - /* parse and execute */ - result = internal_parse_command(command, true); + // parse and execute + const CMDERR result = internal_parse_command(command, true); - /* display errors */ - if (result != CMDERR_NONE) + // display errors + if (result.error_class() != CMDERR::NONE) { if (!echo) - printf(">%s\n", command.c_str()); - printf(" %*s^\n", CMDERR_ERROR_OFFSET(result), ""); - printf("%s\n", cmderr_to_string(result).c_str()); + printf(">%s\n", command); + printf(" %*s^\n", result.error_offset(), ""); + printf("%s\n", cmderr_to_string(result)); } - /* update all views */ + // update all views if (echo) { m_machine.debug_view().update_all(); @@ -383,11 +438,11 @@ CMDERR debugger_console::execute_command(const std::string &command, bool echo) } -/*------------------------------------------------- - validate_command - validate a command string --------------------------------------------------*/ +//------------------------------------------------- +// validate_command - validate a command string +//------------------------------------------------- -CMDERR debugger_console::validate_command(const char *command) +CMDERR debugger_console::validate_command(std::string_view command) { return internal_parse_command(command, false); } @@ -397,26 +452,16 @@ CMDERR debugger_console::validate_command(const char *command) register_command - register a command handler -------------------------------------------------*/ -void debugger_console::register_command(const char *command, u32 flags, int ref, int minparams, int maxparams, std::function<void(int, const std::vector<std::string> &)> handler) +void debugger_console::register_command(std::string_view command, u32 flags, int minparams, int maxparams, std::function<void (const std::vector<std::string_view> &)> &&handler) { if (m_machine.phase() != machine_phase::INIT) throw emu_fatalerror("Can only call debugger_console::register_command() at init time!"); if (!(m_machine.debug_flags & DEBUG_FLAG_ENABLED)) throw emu_fatalerror("Cannot call debugger_console::register_command() when debugger is not running"); - debug_command *cmd = auto_alloc_clear(m_machine, <debug_command>()); - - /* fill in the command */ - strcpy(cmd->command, command); - cmd->flags = flags; - cmd->ref = ref; - cmd->minparams = minparams; - cmd->maxparams = maxparams; - cmd->handler = handler; - - /* link it */ - cmd->next = m_commandlist; - m_commandlist = cmd; + auto const ins = m_commandlist.emplace(command, flags, minparams, maxparams, std::move(handler)); + if (!ins.second) + osd_printf_error("error: Duplicate debugger command %s registered\n", command); } @@ -469,7 +514,7 @@ void debugger_console::process_source_file() buf.resize(pos); // strip whitespace - strtrimrightspace(buf); + buf = strtrimrightspace(buf); // execute the command if (!buf.empty()) @@ -499,22 +544,510 @@ void debugger_console::process_source_file() std::string debugger_console::cmderr_to_string(CMDERR error) { - int offset = CMDERR_ERROR_OFFSET(error); - switch (CMDERR_ERROR_CLASS(error)) + const int offset = error.error_offset(); + switch (error.error_class()) { - case CMDERR_UNKNOWN_COMMAND: return "unknown command"; - case CMDERR_AMBIGUOUS_COMMAND: return "ambiguous command"; - case CMDERR_UNBALANCED_PARENS: return "unbalanced parentheses"; - case CMDERR_UNBALANCED_QUOTES: return "unbalanced quotes"; - case CMDERR_NOT_ENOUGH_PARAMS: return "not enough parameters for command"; - case CMDERR_TOO_MANY_PARAMS: return "too many parameters for command"; - case CMDERR_EXPRESSION_ERROR: return string_format("error in assignment expression: %s", + case CMDERR::UNKNOWN_COMMAND: return "unknown command"; + case CMDERR::AMBIGUOUS_COMMAND: return "ambiguous command"; + case CMDERR::UNBALANCED_PARENS: return "unbalanced parentheses"; + case CMDERR::UNBALANCED_QUOTES: return "unbalanced quotes"; + case CMDERR::NOT_ENOUGH_PARAMS: return "not enough parameters for command"; + case CMDERR::TOO_MANY_PARAMS: return "too many parameters for command"; + case CMDERR::EXPRESSION_ERROR: return string_format("error in assignment expression: %s", expression_error(static_cast<expression_error::error_code>(offset)).code_string()); default: return "unknown error"; } } +//************************************************************************** +// PARAMETER VALIDATION HELPERS +//************************************************************************** + +namespace { + +template <typename T> +inline std::string_view::size_type find_delimiter(std::string_view str, T &&is_delim) +{ + unsigned parens = 0; + for (std::string_view::size_type i = 0; str.length() > i; ++i) + { + if (str[i] == '(') + { + ++parens; + } + else if (parens) + { + if (str[i] == ')') + --parens; + } + else if (is_delim(str[i])) + { + return i; + } + } + return std::string_view::npos; +} + +} // anonymous namespace + + +/// \brief Validate parameter as a Boolean value +/// +/// Validates a parameter as a Boolean value. Fixed strings and +/// expressions evaluating to numeric values are recognised. The result +/// is unchanged for an empty string. +/// \param [in] param The parameter string. +/// \param [in,out] result The default value on entry, and the value of +/// the parameter interpreted as a Boolean on success. Unchanged if +/// the parameter is an empty string. +/// \return true if the parameter is a valid Boolean value or an empty +/// string, or false otherwise. +bool debugger_console::validate_boolean_parameter(std::string_view param, bool &result) +{ + // nullptr parameter does nothing and returns no error + if (param.empty()) + return true; + + // evaluate the expression; success if no error + using namespace std::literals; + bool const is_true = util::streqlower(param, "true"sv); + bool const is_false = util::streqlower(param, "false"sv); + + if (is_true || is_false) + { + result = is_true; + return true; + } + + // try to evaluate as a number + u64 val; + if (!validate_number_parameter(param, val)) + return false; + + result = val != 0; + return true; +} + + +/// \brief Validate parameter as a numeric value +/// +/// Parses the parameter as an expression and evaluates it as a number. +/// \param [in] param The parameter string. +/// \param [out] result The numeric value of the expression on success. +/// Unchanged on failure. +/// \return true if the parameter is a valid expression that evaluates +/// to a numeric value, or false otherwise. +bool debugger_console::validate_number_parameter(std::string_view param, u64 &result) +{ + // evaluate the expression; success if no error + try + { + result = parsed_expression(visible_symtable(), param).execute(); + return true; + } + catch (expression_error const &error) + { + // print an error pointing to the character that caused it + printf("Error in expression: %s\n", param); + printf(" %*s^", error.offset(), ""); + printf("%s\n", error.code_string()); + return false; + } +} + + +/// \brief Validate parameter as a device +/// +/// Validates a parameter as a device identifier and retrieves the +/// device on success. A string corresponding to the tag of a device +/// refers to that device; an empty string refers to the current CPU +/// with debugger focus; any other string is parsed as an expression +/// and treated as an index of a device implementing +/// #device_execute_interface and #device_state_interface, and exposing +/// a generic PC base value. +/// \param [in] param The parameter string. +/// \param [out] result A pointer to the device on success, or unchanged +/// on failure. +/// \return true if the parameter refers to a device in the current +/// system, or false otherwise. +bool debugger_console::validate_device_parameter(std::string_view param, device_t *&result) +{ + // if no parameter, use the visible CPU + if (param.empty()) + { + device_t *const current = m_visiblecpu; + if (current) + { + result = current; + return true; + } + else + { + printf("No valid CPU is currently selected\n"); + return false; + } + } + + // next look for a tag match + std::string_view relative = param; + device_t &base = get_device_search_base(relative); + device_t *device = base.subdevice(strmakelower(relative)); + if (device) + { + result = device; + return true; + } + + // then evaluate as an expression; on an error assume it was a tag + u64 cpunum; + try + { + cpunum = parsed_expression(visible_symtable(), param).execute(); + } + catch (expression_error &) + { + printf("Unable to find device '%s'\n", param); + return false; + } + + // attempt to find by numerical index + device = get_cpu_by_index(cpunum); + if (device) + { + result = device; + return true; + } + else + { + // if out of range, complain + printf("Invalid CPU index %u\n", cpunum); + return false; + } +} + + +/// \brief Validate a parameter as a CPU +/// +/// Validates a parameter as a CPU identifier. Uses the same rules as +/// #validate_device_parameter to identify devices, but additionally +/// checks that the device is a "CPU" for the debugger's purposes. +/// \param [in] The parameter string. +/// \param [out] result The device on success, or unchanged on failure. +/// \return true if the parameter refers to a CPU-like device in the +/// current system, or false otherwise. +bool debugger_console::validate_cpu_parameter(std::string_view param, device_t *&result) +{ + // first do the standard device thing + device_t *device; + if (!validate_device_parameter(param, device)) + return false; + + // check that it's a "CPU" for the debugger's purposes + device_execute_interface const *execute; + if (device->interface(execute)) + { + result = device; + return true; + } + + printf("Device %s is not a CPU\n", device->name()); + return false; +} + + +/// \brief Validate a parameter as an address space identifier +/// +/// Validates a parameter as an address space identifier. Uses the same +/// rules as #validate_device_parameter to identify devices. If the +/// default address space number is negative, the first address space +/// exposed by the device will be used as the default. +/// \param [in] The parameter string. +/// \param [in] spacenum The default address space index. If negative, +/// the first address space exposed by the device (i.e. the address +/// space with the lowest index) will be used as the default. +/// \param [out] result The addresfs space on success, or unchanged on +/// failure. +/// \return true if the parameter refers to an address space in the +/// current system, or false otherwise. +bool debugger_console::validate_device_space_parameter(std::string_view param, int spacenum, address_space *&result) +{ + device_t *device; + std::string spacename; + if (param.empty()) + { + // if no parameter, use the visible CPU + device = m_visiblecpu; + if (!device) + { + printf("No valid CPU is currently selected\n"); + return false; + } + } + else + { + // look for a tag match on the whole parameter value + std::string_view relative = param; + device_t &base = get_device_search_base(relative); + device = base.subdevice(strmakelower(relative)); + + // if that failed, treat the last component as an address space + if (!device) + { + auto const delimiter = relative.find_last_of(":^"); + bool const found = std::string_view::npos != delimiter; + if (!found || (':' == relative[delimiter])) + { + spacename = strmakelower(relative.substr(found ? (delimiter + 1) : 0)); + relative = relative.substr(0, !found ? 0 : !delimiter ? 1 : delimiter); + if (!relative.empty()) + device = base.subdevice(strmakelower(relative)); + else if (m_visiblecpu) + device = m_visiblecpu; + else + device = &m_machine.root_device(); + } + } + } + + // if still no device found, evaluate as an expression + if (!device) + { + u64 cpunum; + try + { + cpunum = parsed_expression(visible_symtable(), param).execute(); + } + catch (expression_error const &) + { + // parsing failed - assume it was a tag + printf("Unable to find device '%s'\n", param); + return false; + } + + // attempt to find by numerical index + device = get_cpu_by_index(cpunum); + if (!device) + { + // if out of range, complain + printf("Invalid CPU index %u\n", cpunum); + return false; + } + } + + // ensure the device implements the memory interface + device_memory_interface *memory; + if (!device->interface(memory)) + { + printf("No memory interface found for device %s\n", device->name()); + return false; + } + + // fall back to supplied default space if appropriate + if (spacename.empty() && (0 <= spacenum)) + { + if (memory->has_space(spacenum)) + { + result = &memory->space(spacenum); + return true; + } + else + { + printf("No matching memory space found for device '%s'\n", device->tag()); + return false; + } + } + + // otherwise find the specified space or fall back to the first populated space + for (int i = 0; memory->max_space_count() > i; ++i) + { + if (memory->has_space(i) && (spacename.empty() || (memory->space(i).name() == spacename))) + { + result = &memory->space(i); + return true; + } + } + + // report appropriate error message + if (spacename.empty()) + printf("No memory spaces found for device '%s'\n", device->tag()); + else + printf("Memory space '%s' not found found for device '%s'\n", spacename, device->tag()); + return false; +} + + +/// \brief Validate a parameter as a target address +/// +/// Validates a parameter as an numeric expression to use as an address +/// optionally followed by a colon and a device identifier. If the +/// device identifier is not presnt, the current CPU with debugger focus +/// is assumed. See #validate_device_parameter for information on how +/// device parametersare interpreted. +/// \param [in] The parameter string. +/// \param [in] spacenum The default address space index. If negative, +/// the first address space exposed by the device (i.e. the address +/// space with the lowest index) will be used as the default. +/// \param [out] space The address space on success, or unchanged on +/// failure. +/// \param [out] addr The address on success, or unchanged on failure. +/// \return true if the address is a valid expression evaluating to a +/// number and the address space is found, or false otherwise. +bool debugger_console::validate_target_address_parameter(std::string_view param, int spacenum, address_space *&space, u64 &addr) +{ + // check for the device delimiter + std::string_view::size_type const devdelim = find_delimiter(param, [] (char ch) { return ':' == ch; }); + std::string_view device; + if (devdelim != std::string::npos) + device = param.substr(devdelim + 1); + + // parse the address first + u64 addrval; + if (!validate_number_parameter(param.substr(0, devdelim), addrval)) + return false; + + // find the address space + if (!validate_device_space_parameter(device, spacenum, space)) + return false; + + // set the address now that we have the space + addr = addrval; + return true; +} + + +/// \brief Validate a parameter as a memory region +/// +/// Validates a parameter as a memory region tag and retrieves the +/// specified memory region. +/// \param [in] The parameter string. +/// \param [out] result The memory region on success, or unchanged on +/// failure. +/// \return true if the parameter refers to a memory region in the +/// current system, or false otherwise. +bool debugger_console::validate_memory_region_parameter(std::string_view param, memory_region *&result) +{ + auto const ®ions = m_machine.memory().regions(); + std::string_view relative = param; + device_t &base = get_device_search_base(relative); + auto const iter = regions.find(base.subtag(strmakelower(relative))); + if (regions.end() != iter) + { + result = iter->second.get(); + return true; + } + else + { + printf("No matching memory region found for '%s'\n", param); + return false; + } +} + + +/// \brief Get search base for device or address space parameter +/// +/// Handles prefix prefixes used to indicate that a device tag should be +/// interpreted relative to the selected CPU. Removes the recognised +/// prefixes from the parameter value. +/// \param [in,out] param The parameter string. Recognised prefixes +/// affecting the search base are removed, leaving a tag relative to +/// the base device. +/// \return A reference to the base device that the tag should be +/// interpreted relative to. +device_t &debugger_console::get_device_search_base(std::string_view ¶m) const +{ + if (!param.empty()) + { + // handle ".:" or ".^" prefix for tag relative to current CPU if any + if (('.' == param[0]) && ((param.size() == 1) || (':' == param[1]) || ('^' == param[1]))) + { + param.remove_prefix(((param.size() > 1) && (':' == param[1])) ? 2 : 1); + device_t *const current = m_visiblecpu; + return current ? *current : m_machine.root_device(); + } + + // a sibling path makes most sense relative to current CPU + if ('^' == param[0]) + { + device_t *const current = m_visiblecpu; + return current ? *current : m_machine.root_device(); + } + } + + // default to root device + return m_machine.root_device(); +} + + +/// \brief Get CPU by index +/// +/// Looks up a CPU by the number the debugger assigns it based on its +/// position in the device tree relative to other CPUs. +/// \param [in] cpunum Zero-based index of the CPU to find. +/// \return A pointer to the CPU if found, or \c nullptr if no CPU has +/// the specified index. +device_t *debugger_console::get_cpu_by_index(u64 cpunum) const +{ + unsigned index = 0; + for (device_execute_interface &exec : execute_interface_enumerator(m_machine.root_device())) + { + // real CPUs should have pcbase + device_state_interface const *state; + if (exec.device().interface(state) && state->state_find_entry(STATE_GENPCBASE)) + { + if (index++ == cpunum) + { + return &exec.device(); + } + } + } + return nullptr; +} + + +//------------------------------------------------- +// validate_expression_parameter - validates +// an expression parameter +//-----------------------------------------------*/ + +bool debugger_console::validate_expression_parameter(std::string_view param, parsed_expression &result) +{ + try + { + // parse the expression; success if no error + result.parse(param); + return true; + } + catch (expression_error const &err) + { + // output an error + printf("Error in expression: %s\n", param); + printf(" %*s^", err.offset(), ""); + printf("%s\n", err.code_string()); + return false; + } +} + + +//------------------------------------------------- +// validate_command_parameter - validates a +// command parameter +//------------------------------------------------- + +bool debugger_console::validate_command_parameter(std::string_view param) +{ + // validate the comment; success if no error + CMDERR err = validate_command(param); + if (err.error_class() == CMDERR::NONE) + return true; + + // output an error + printf("Error in command: %s\n", param); + printf(" %*s^", err.error_offset(), ""); + printf("%s\n", cmderr_to_string(err)); + return false; +} + /*************************************************************************** @@ -522,62 +1055,86 @@ std::string debugger_console::cmderr_to_string(CMDERR error) ***************************************************************************/ -/*------------------------------------------------- - vprintf - vprintfs the given arguments using - the format to the debug console --------------------------------------------------*/ -void debugger_console::vprintf(util::format_argument_pack<std::ostream> const &args) +//------------------------------------------------- +// print_core - write preformatted text +// to the debug console +//------------------------------------------------- + +void debugger_console::print_core(std::string_view text) +{ + text_buffer_print(*m_console_textbuf, text); + if (m_logfile) + m_logfile->write(text.data(), text.length()); +} + +//------------------------------------------------- +// print_core_wrap - write preformatted text +// to the debug console, with wrapping +//------------------------------------------------- + +void debugger_console::print_core_wrap(std::string_view text, int wrapcol) { - text_buffer_print(m_console_textbuf, util::string_format(args).c_str()); + // FIXME: look into honoring wrapcol for the logfile + text_buffer_print_wrap(*m_console_textbuf, text, wrapcol); + if (m_logfile) + m_logfile->write(text.data(), text.length()); +} - /* force an update of any console views */ +//------------------------------------------------- +// vprintf - vprintfs the given arguments using +// the format to the debug console +//------------------------------------------------- + +void debugger_console::vprintf(util::format_argument_pack<char> const &args) +{ + print_core(util::string_format(args)); + + // force an update of any console views m_machine.debug_view().update_all(DVT_CONSOLE); } -void debugger_console::vprintf(util::format_argument_pack<std::ostream> &&args) +void debugger_console::vprintf(util::format_argument_pack<char> &&args) { - text_buffer_print(m_console_textbuf, util::string_format(std::move(args)).c_str()); + print_core(util::string_format(std::move(args))); - /* force an update of any console views */ + // force an update of any console views m_machine.debug_view().update_all(DVT_CONSOLE); } -/*------------------------------------------------- - vprintf_wrap - vprintfs the given arguments - using the format to the debug console --------------------------------------------------*/ +//------------------------------------------------- +// vprintf_wrap - vprintfs the given arguments +// using the format to the debug console +//------------------------------------------------- -void debugger_console::vprintf_wrap(int wrapcol, util::format_argument_pack<std::ostream> const &args) +void debugger_console::vprintf_wrap(int wrapcol, util::format_argument_pack<char> const &args) { - text_buffer_print_wrap(m_console_textbuf, util::string_format(args).c_str(), wrapcol); + print_core_wrap(util::string_format(args), wrapcol); - /* force an update of any console views */ + // force an update of any console views m_machine.debug_view().update_all(DVT_CONSOLE); } -void debugger_console::vprintf_wrap(int wrapcol, util::format_argument_pack<std::ostream> &&args) +void debugger_console::vprintf_wrap(int wrapcol, util::format_argument_pack<char> &&args) { - text_buffer_print_wrap(m_console_textbuf, util::string_format(std::move(args)).c_str(), wrapcol); + print_core_wrap(util::string_format(std::move(args)), wrapcol); - /* force an update of any console views */ + // force an update of any console views m_machine.debug_view().update_all(DVT_CONSOLE); } -/*------------------------------------------------- - errorlog_write_line - writes a line to the - errorlog ring buffer --------------------------------------------------*/ +//------------------------------------------------- +// errorlog_write_line - writes a line to the +// errorlog ring buffer +//------------------------------------------------- void debugger_console::errorlog_write_line(const char *line) { if (m_errorlog_textbuf) - { - text_buffer_print(m_errorlog_textbuf, line); - } + text_buffer_print(*m_errorlog_textbuf, line); - /* force an update of any log views */ + // force an update of any log views m_machine.debug_view().update_all(DVT_LOG); } diff --git a/src/emu/debug/debugcon.h b/src/emu/debug/debugcon.h index 289123591f0..37b2e235348 100644 --- a/src/emu/debug/debugcon.h +++ b/src/emu/debug/debugcon.h @@ -16,86 +16,85 @@ #include "textbuf.h" #include <functional> +#include <set> /*************************************************************************** CONSTANTS ***************************************************************************/ -#define MAX_COMMAND_LENGTH 4096 -#define MAX_COMMAND_PARAMS 128 +constexpr int MAX_COMMAND_PARAMS = 128; -/* flags for command parsing */ -#define CMDFLAG_NONE (0x0000) -#define CMDFLAG_KEEP_QUOTES (0x0001) -#define CMDFLAG_CUSTOM_HELP (0x0002) - -/* values for the error code in a command error */ -#define CMDERR_NONE (0) -#define CMDERR_UNKNOWN_COMMAND (1) -#define CMDERR_AMBIGUOUS_COMMAND (2) -#define CMDERR_UNBALANCED_PARENS (3) -#define CMDERR_UNBALANCED_QUOTES (4) -#define CMDERR_NOT_ENOUGH_PARAMS (5) -#define CMDERR_TOO_MANY_PARAMS (6) -#define CMDERR_EXPRESSION_ERROR (7) - -/* parameter separator macros */ -#define CMDPARAM_SEPARATOR "\0" -#define CMDPARAM_TERMINATOR "\0\0" +// flags for command parsing +constexpr u32 CMDFLAG_NONE = 0x0000; +constexpr u32 CMDFLAG_KEEP_QUOTES = 0x0001; +constexpr u32 CMDFLAG_CUSTOM_HELP = 0x0002; /*************************************************************************** - MACROS + TYPE DEFINITIONS ***************************************************************************/ -/* command error assembly/disassembly macros */ -#define CMDERR_ERROR_CLASS(x) ((x) >> 16) -#define CMDERR_ERROR_OFFSET(x) ((x) & 0xffff) -#define MAKE_CMDERR(a,b) (((a) << 16) | ((b) & 0xffff)) - -/* macros to assemble specific error conditions */ -#define MAKE_CMDERR_UNKNOWN_COMMAND(x) MAKE_CMDERR(CMDERR_UNKNOWN_COMMAND, (x)) -#define MAKE_CMDERR_AMBIGUOUS_COMMAND(x) MAKE_CMDERR(CMDERR_AMBIGUOUS_COMMAND, (x)) -#define MAKE_CMDERR_UNBALANCED_PARENS(x) MAKE_CMDERR(CMDERR_UNBALANCED_PARENS, (x)) -#define MAKE_CMDERR_UNBALANCED_QUOTES(x) MAKE_CMDERR(CMDERR_UNBALANCED_QUOTES, (x)) -#define MAKE_CMDERR_NOT_ENOUGH_PARAMS(x) MAKE_CMDERR(CMDERR_NOT_ENOUGH_PARAMS, (x)) -#define MAKE_CMDERR_TOO_MANY_PARAMS(x) MAKE_CMDERR(CMDERR_TOO_MANY_PARAMS, (x)) -#define MAKE_CMDERR_EXPRESSION_ERROR(x) MAKE_CMDERR(CMDERR_EXPRESSION_ERROR, (x)) - +// CMDERR is an error code for command evaluation +class CMDERR +{ +public: + // values for the error code in a command error + static constexpr u16 NONE = 0; + static constexpr u16 UNKNOWN_COMMAND = 1; + static constexpr u16 AMBIGUOUS_COMMAND = 2; + static constexpr u16 UNBALANCED_PARENS = 3; + static constexpr u16 UNBALANCED_QUOTES = 4; + static constexpr u16 NOT_ENOUGH_PARAMS = 5; + static constexpr u16 TOO_MANY_PARAMS = 6; + static constexpr u16 EXPRESSION_ERROR = 7; + + // command error assembly/disassembly + constexpr CMDERR(u16 c, u16 o) : m_error_class(c), m_error_offset(o) { } + constexpr u16 error_class() const { return m_error_class; } + constexpr u16 error_offset() const { return m_error_offset; } + + // assemble specific error conditions + static constexpr CMDERR none() { return CMDERR(NONE, 0); } + static constexpr CMDERR unknown_command(u16 x) { return CMDERR(UNKNOWN_COMMAND, x); } + static constexpr CMDERR ambiguous_command(u16 x) { return CMDERR(AMBIGUOUS_COMMAND, x); } + static constexpr CMDERR unbalanced_parens(u16 x) { return CMDERR(UNBALANCED_PARENS, x); } + static constexpr CMDERR unbalanced_quotes(u16 x) { return CMDERR(UNBALANCED_QUOTES, x); } + static constexpr CMDERR not_enough_params(u16 x) { return CMDERR(NOT_ENOUGH_PARAMS, x); } + static constexpr CMDERR too_many_params(u16 x) { return CMDERR(TOO_MANY_PARAMS, x); } + static constexpr CMDERR expression_error(u16 x) { return CMDERR(EXPRESSION_ERROR, x); } -/*************************************************************************** - TYPE DEFINITIONS -***************************************************************************/ +private: + u16 m_error_class, m_error_offset; +}; -/* CMDERR is an error code for command evaluation */ -typedef u32 CMDERR; class debugger_console { public: debugger_console(running_machine &machine); + ~debugger_console(); - /* command handling */ - CMDERR execute_command(const std::string &command, bool echo); - CMDERR validate_command(const char *command); - void register_command(const char *command, u32 flags, int ref, int minparams, int maxparams, std::function<void(int, const std::vector<std::string> &)> handler); + // command handling + CMDERR execute_command(std::string_view command, bool echo); + CMDERR validate_command(std::string_view command); + void register_command(std::string_view command, u32 flags, int minparams, int maxparams, std::function<void (const std::vector<std::string_view> &)> &&handler); void source_script(const char *file); void process_source_file(); - /* console management */ - void vprintf(util::format_argument_pack<std::ostream> const &args); - void vprintf(util::format_argument_pack<std::ostream> &&args); - void vprintf_wrap(int wrapcol, util::format_argument_pack<std::ostream> const &args); - void vprintf_wrap(int wrapcol, util::format_argument_pack<std::ostream> &&args); - text_buffer * get_console_textbuf() const { return m_console_textbuf; } + // console management + void vprintf(util::format_argument_pack<char> const &args); + void vprintf(util::format_argument_pack<char> &&args); + void vprintf_wrap(int wrapcol, util::format_argument_pack<char> const &args); + void vprintf_wrap(int wrapcol, util::format_argument_pack<char> &&args); + text_buffer &get_console_textbuf() const { return *m_console_textbuf; } - /* errorlog management */ - void errorlog_write_line(const char *line); - text_buffer * get_errorlog_textbuf() const { return m_errorlog_textbuf; } + // errorlog management + void errorlog_write_line(const char *line); + text_buffer &get_errorlog_textbuf() const { return *m_errorlog_textbuf; } - /* convenience templates */ + // convenience templates template <typename Format, typename... Params> inline void printf(Format &&fmt, Params &&...args) { @@ -107,38 +106,88 @@ public: vprintf_wrap(wrapcol, util::make_format_argument_pack(std::forward<Format>(fmt), std::forward<Params>(args)...)); } + device_t *get_visible_cpu() const { return m_visiblecpu; } + void set_visible_cpu(device_t *visiblecpu) { m_visiblecpu = visiblecpu; } + symbol_table &visible_symtable() const; + static std::string cmderr_to_string(CMDERR error); + // validates a parameter as a boolean value + bool validate_boolean_parameter(std::string_view param, bool &result); + + // validates a parameter as a numeric value + bool validate_number_parameter(std::string_view param, u64 &result); + + // validates a parameter as a device + bool validate_device_parameter(std::string_view param, device_t *&result); + + // validates a parameter as a CPU + bool validate_cpu_parameter(std::string_view param, device_t *&result); + + // validates a parameter as an address space identifier + bool validate_device_space_parameter(std::string_view param, int spacenum, address_space *&result); + + // validates a parameter as a target address and retrieves the given address space and address + bool validate_target_address_parameter(std::string_view param, int spacenum, address_space *&space, u64 &addr); + + // validates a parameter as a memory region name and retrieves the given region + bool validate_memory_region_parameter(std::string_view param, memory_region *&result); + + // validates a parameter as a debugger expression + bool validate_expression_parameter(std::string_view param, parsed_expression &result); + + // validates a parameter as a debugger command + bool validate_command_parameter(std::string_view param); + private: void exit(); - void execute_help_custom(int ref, const std::vector<std::string> ¶ms); + void execute_help_custom(const std::vector<std::string_view> ¶ms); + void execute_condump(const std::vector<std::string_view>& params); + + [[nodiscard]] static std::string_view trim_parameter(std::string_view param, bool keep_quotes); + CMDERR internal_execute_command(bool execute, std::vector<std::string_view> ¶ms); + CMDERR internal_parse_command(std::string_view command, bool execute); + + void print_core(std::string_view text); // core text output + void print_core_wrap(std::string_view text, int wrapcol); // core text output - void trim_parameter(char **paramptr, bool keep_quotes); - CMDERR internal_execute_command(bool execute, int params, char **param); - CMDERR internal_parse_command(const std::string &original_command, bool execute); + device_t &get_device_search_base(std::string_view ¶m) const; + device_t *get_cpu_by_index(u64 cpunum) const; struct debug_command { - debug_command * next; - char command[32]; + debug_command(std::string_view _command, u32 _flags, int _minparams, int _maxparams, std::function<void (const std::vector<std::string_view> &)> &&_handler); + + struct compare + { + using is_transparent = void; + bool operator()(const debug_command &a, const debug_command &b) const; + bool operator()(const char *a, const debug_command &b) const; + bool operator()(const debug_command &a, const char *b) const; + }; + + std::string command; const char * params; const char * help; - std::function<void(int, const std::vector<std::string> &)> handler; + std::function<void (const std::vector<std::string_view> &)> handler; u32 flags; - int ref; int minparams; int maxparams; }; running_machine &m_machine; - text_buffer *m_console_textbuf; - text_buffer *m_errorlog_textbuf; + // visible CPU device (the one that commands should apply to) + device_t *m_visiblecpu; + + text_buffer_ptr m_console_textbuf; + text_buffer_ptr m_errorlog_textbuf; - debug_command *m_commandlist; + std::set<debug_command, debug_command::compare> m_commandlist; std::unique_ptr<std::istream> m_source_file; // script source file + std::unique_ptr<emu_file> m_logfile; // logfile for debug console output }; #endif // MAME_EMU_DEBUG_DEBUGCON_H diff --git a/src/emu/debug/debugcpu.cpp b/src/emu/debug/debugcpu.cpp index 01e0a91647b..968966b1b74 100644 --- a/src/emu/debug/debugcpu.cpp +++ b/src/emu/debug/debugcpu.cpp @@ -13,15 +13,18 @@ #include "debugbuf.h" #include "express.h" +#include "points.h" #include "debugcon.h" #include "debugvw.h" #include "debugger.h" #include "emuopts.h" +#include "fileio.h" +#include "main.h" #include "screen.h" #include "uiinput.h" -#include "coreutil.h" +#include "corestr.h" #include "osdepend.h" #include "xmlfile.h" @@ -36,7 +39,6 @@ const size_t debugger_cpu::NUM_TEMP_VARIABLES = 10; debugger_cpu::debugger_cpu(running_machine &machine) : m_machine(machine) , m_livecpu(nullptr) - , m_visiblecpu(nullptr) , m_breakcpu(nullptr) , m_symtable(nullptr) , m_vblank_occurred(false) @@ -45,45 +47,45 @@ debugger_cpu::debugger_cpu(running_machine &machine) , m_bpindex(1) , m_wpindex(1) , m_rpindex(1) + , m_epindex(1) , m_wpdata(0) , m_wpaddr(0) + , m_wpsize(0) , m_last_periodic_update_time(0) , m_comments_loaded(false) { m_tempvar = make_unique_clear<u64[]>(NUM_TEMP_VARIABLES); /* create a global symbol table */ - m_symtable = std::make_unique<symbol_table>(&m_machine); + m_symtable = std::make_unique<symbol_table>(machine); + m_symtable->set_memory_modified_func([this]() { set_memory_modified(true); }); - // configure our base memory accessors - configure_memory(*m_symtable); - - /* add "wpaddr", "wpdata", "cycles", "cpunum", "logunmap" to the global symbol table */ + /* add "wpaddr", "wpdata", "wpsize" to the global symbol table */ m_symtable->add("wpaddr", symbol_table::READ_ONLY, &m_wpaddr); m_symtable->add("wpdata", symbol_table::READ_ONLY, &m_wpdata); + m_symtable->add("wpsize", symbol_table::READ_ONLY, &m_wpsize); - using namespace std::placeholders; - m_symtable->add("cpunum", std::bind(&debugger_cpu::get_cpunum, this, _1)); - - screen_device_iterator screen_iterator = screen_device_iterator(m_machine.root_device()); - screen_device_iterator::auto_iterator iter = screen_iterator.begin(); - const uint32_t count = (uint32_t)screen_iterator.count(); + screen_device_enumerator screen_enumerator = screen_device_enumerator(m_machine.root_device()); + screen_device_enumerator::iterator iter = screen_enumerator.begin(); + const uint32_t count = (uint32_t)screen_enumerator.count(); if (count == 1) { - m_symtable->add("beamx", std::bind(&debugger_cpu::get_beamx, this, _1, iter.current())); - m_symtable->add("beamy", std::bind(&debugger_cpu::get_beamy, this, _1, iter.current())); - m_symtable->add("frame", std::bind(&debugger_cpu::get_frame, this, _1, iter.current())); - iter.current()->register_vblank_callback(vblank_state_delegate(&debugger_cpu::on_vblank, this)); + screen_device &screen = *iter.current(); + m_symtable->add("beamx", [&screen]() { return screen.hpos(); }); + m_symtable->add("beamy", [&screen]() { return screen.vpos(); }); + m_symtable->add("frame", [&screen]() { return screen.frame_number(); }); + screen.register_vblank_callback(vblank_state_delegate(&debugger_cpu::on_vblank, this)); } else if (count > 1) { for (uint32_t i = 0; i < count; i++, iter++) { - m_symtable->add(string_format("beamx%d", i).c_str(), std::bind(&debugger_cpu::get_beamx, this, _1, iter.current())); - m_symtable->add(string_format("beamy%d", i).c_str(), std::bind(&debugger_cpu::get_beamy, this, _1, iter.current())); - m_symtable->add(string_format("frame%d", i).c_str(), std::bind(&debugger_cpu::get_frame, this, _1, iter.current())); - iter.current()->register_vblank_callback(vblank_state_delegate(&debugger_cpu::on_vblank, this)); + screen_device &screen = *iter.current(); + m_symtable->add(string_format("beamx%d", i).c_str(), [&screen]() { return screen.hpos(); }); + m_symtable->add(string_format("beamy%d", i).c_str(), [&screen]() { return screen.vpos(); }); + m_symtable->add(string_format("frame%d", i).c_str(), [&screen]() { return screen.frame_number(); }); + screen.register_vblank_callback(vblank_state_delegate(&debugger_cpu::on_vblank, this)); } } @@ -91,31 +93,11 @@ debugger_cpu::debugger_cpu(running_machine &machine) for (int regnum = 0; regnum < NUM_TEMP_VARIABLES; regnum++) { char symname[10]; - sprintf(symname, "temp%d", regnum); + snprintf(symname, 10, "temp%d", regnum); m_symtable->add(symname, symbol_table::READ_WRITE, &m_tempvar[regnum]); } - - /* first CPU is visible by default */ - for (device_t &device : device_iterator(m_machine.root_device())) - { - auto *cpu = dynamic_cast<cpu_device *>(&device); - if (cpu != nullptr) - { - m_visiblecpu = cpu; - break; - } - } } -void debugger_cpu::configure_memory(symbol_table &table) -{ - using namespace std::placeholders; - table.configure_memory( - &m_machine, - std::bind(&debugger_cpu::expression_validate, this, _1, _2, _3), - std::bind(&debugger_cpu::expression_read_memory, this, _1, _2, _3, _4, _5, _6), - std::bind(&debugger_cpu::expression_write_memory, this, _1, _2, _3, _4, _5, _6, _7)); -} /*------------------------------------------------- flush_traces - flushes all traces; this is @@ -127,29 +109,13 @@ void debugger_cpu::flush_traces() { /* this can be called on exit even when no debugging is enabled, so make sure the devdebug is valid before proceeding */ - for (device_t &device : device_iterator(m_machine.root_device())) + for (device_t &device : device_enumerator(m_machine.root_device())) if (device.debug() != nullptr) device.debug()->trace_flush(); } -/*************************************************************************** - SYMBOL TABLE INTERFACES -***************************************************************************/ - -/*------------------------------------------------- - get_visible_symtable - return the - locally-visible symbol table --------------------------------------------------*/ - -symbol_table* debugger_cpu::get_visible_symtable() -{ - return &m_visiblecpu->debug()->symtable(); -} - - - //************************************************************************** // MEMORY AND DISASSEMBLY HELPERS //************************************************************************** @@ -185,7 +151,7 @@ bool debugger_cpu::comment_save() // for each device bool found_comments = false; - for (device_t &device : device_iterator(m_machine.root_device())) + for (device_t &device : device_enumerator(m_machine.root_device())) if (device.debug() && device.debug()->comment_count() > 0) { // create a node for this device @@ -204,8 +170,8 @@ bool debugger_cpu::comment_save() if (found_comments) { emu_file file(m_machine.options().comment_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); - osd_file::error filerr = file.open(m_machine.basename(), ".cmt"); - if (filerr == osd_file::error::NONE) + std::error_condition const filerr = file.open(m_machine.basename() + ".cmt"); + if (!filerr) { root->write(file); comments_saved = true; @@ -230,10 +196,10 @@ bool debugger_cpu::comment_load(bool is_inline) { // open the file emu_file file(m_machine.options().comment_directory(), OPEN_FLAG_READ); - osd_file::error filerr = file.open(m_machine.basename(), ".cmt"); + std::error_condition const filerr = file.open(m_machine.basename() + ".cmt"); // if an error, just return false - if (filerr != osd_file::error::NONE) + if (filerr) return false; // wrap in a try/catch to handle errors @@ -288,294 +254,6 @@ bool debugger_cpu::comment_load(bool is_inline) /*************************************************************************** - DEBUGGER MEMORY ACCESSORS -***************************************************************************/ - -/*------------------------------------------------- - read_byte - return a byte from the specified - memory space --------------------------------------------------*/ - -u8 debugger_cpu::read_byte(address_space &space, offs_t address, bool apply_translation) -{ - device_memory_interface &memory = space.device().memory(); - - if (apply_translation) - { - /* mask against the logical byte mask */ - address &= space.logaddrmask(); - - /* translate if necessary; if not mapped, return 0xff */ - if (!memory.translate(space.spacenum(), TRANSLATE_READ_DEBUG, address)) - return 0xff; - } - - /* otherwise, call the byte reading function for the translated address */ - return space.read_byte(address); -} - - -/*------------------------------------------------- - read_word - return a word from the specified - memory space --------------------------------------------------*/ - -u16 debugger_cpu::read_word(address_space &space, offs_t address, bool apply_translation) -{ - device_memory_interface &memory = space.device().memory(); - - if (apply_translation) - { - /* mask against the logical byte mask */ - address &= space.logaddrmask(); - - /* translate if necessary; if not mapped, return 0xffff */ - if (!memory.translate(space.spacenum(), TRANSLATE_READ_DEBUG, address)) - return 0xffff; - } - - /* otherwise, call the byte reading function for the translated address */ - return space.read_word_unaligned(address); -} - - -/*------------------------------------------------- - read_dword - return a dword from the specified - memory space --------------------------------------------------*/ - -u32 debugger_cpu::read_dword(address_space &space, offs_t address, bool apply_translation) -{ - device_memory_interface &memory = space.device().memory(); - - if (apply_translation) - { - /* mask against the logical byte mask */ - address &= space.logaddrmask(); - - /* translate if necessary; if not mapped, return 0xffffffff */ - if (!memory.translate(space.spacenum(), TRANSLATE_READ_DEBUG, address)) - return 0xffffffff; - } - - /* otherwise, call the byte reading function for the translated address */ - return space.read_dword_unaligned(address); -} - - -/*------------------------------------------------- - read_qword - return a qword from the specified - memory space --------------------------------------------------*/ - -u64 debugger_cpu::read_qword(address_space &space, offs_t address, bool apply_translation) -{ - device_memory_interface &memory = space.device().memory(); - - /* translate if necessary; if not mapped, return 0xffffffffffffffff */ - if (apply_translation) - { - /* mask against the logical byte mask */ - address &= space.logaddrmask(); - - /* translate if necessary; if not mapped, return 0xffffffff */ - if (!memory.translate(space.spacenum(), TRANSLATE_READ_DEBUG, address)) - return ~u64(0); - } - - /* otherwise, call the byte reading function for the translated address */ - return space.read_qword_unaligned(address); -} - - -/*------------------------------------------------- - read_memory - return 1,2,4 or 8 bytes - from the specified memory space --------------------------------------------------*/ - -u64 debugger_cpu::read_memory(address_space &space, offs_t address, int size, bool apply_translation) -{ - u64 result = ~u64(0) >> (64 - 8*size); - switch (size) - { - case 1: result = read_byte(space, address, apply_translation); break; - case 2: result = read_word(space, address, apply_translation); break; - case 4: result = read_dword(space, address, apply_translation); break; - case 8: result = read_qword(space, address, apply_translation); break; - } - return result; -} - - -/*------------------------------------------------- - write_byte - write a byte to the specified - memory space --------------------------------------------------*/ - -void debugger_cpu::write_byte(address_space &space, offs_t address, u8 data, bool apply_translation) -{ - device_memory_interface &memory = space.device().memory(); - - if (apply_translation) - { - /* mask against the logical byte mask */ - address &= space.logaddrmask(); - - /* translate if necessary; if not mapped, we're done */ - if (!memory.translate(space.spacenum(), TRANSLATE_WRITE_DEBUG, address)) - return; - } - - /* otherwise, call the byte reading function for the translated address */ - space.write_byte(address, data); - - m_memory_modified = true; -} - - -/*------------------------------------------------- - write_word - write a word to the specified - memory space --------------------------------------------------*/ - -void debugger_cpu::write_word(address_space &space, offs_t address, u16 data, bool apply_translation) -{ - device_memory_interface &memory = space.device().memory(); - - if (apply_translation) - { - /* mask against the logical byte mask */ - address &= space.logaddrmask(); - - /* translate if necessary; if not mapped, we're done */ - if (!memory.translate(space.spacenum(), TRANSLATE_WRITE_DEBUG, address)) - return; - } - - /* otherwise, call the byte reading function for the translated address */ - space.write_word_unaligned(address, data); - - m_memory_modified = true; -} - - -/*------------------------------------------------- - write_dword - write a dword to the specified - memory space --------------------------------------------------*/ - -void debugger_cpu::write_dword(address_space &space, offs_t address, u32 data, bool apply_translation) -{ - device_memory_interface &memory = space.device().memory(); - - if (apply_translation) - { - /* mask against the logical byte mask */ - address &= space.logaddrmask(); - - /* translate if necessary; if not mapped, we're done */ - if (!memory.translate(space.spacenum(), TRANSLATE_WRITE_DEBUG, address)) - return; - } - - /* otherwise, call the byte reading function for the translated address */ - space.write_dword_unaligned(address, data); - - m_memory_modified = true; -} - - -/*------------------------------------------------- - write_qword - write a qword to the specified - memory space --------------------------------------------------*/ - -void debugger_cpu::write_qword(address_space &space, offs_t address, u64 data, bool apply_translation) -{ - device_memory_interface &memory = space.device().memory(); - - if (apply_translation) - { - /* mask against the logical byte mask */ - address &= space.logaddrmask(); - - /* translate if necessary; if not mapped, we're done */ - if (!memory.translate(space.spacenum(), TRANSLATE_WRITE_DEBUG, address)) - return; - } - - /* otherwise, call the byte reading function for the translated address */ - space.write_qword_unaligned(address, data); - - m_memory_modified = true; -} - - -/*------------------------------------------------- - write_memory - write 1,2,4 or 8 bytes to the - specified memory space --------------------------------------------------*/ - -void debugger_cpu::write_memory(address_space &space, offs_t address, u64 data, int size, bool apply_translation) -{ - switch (size) - { - case 1: write_byte(space, address, data, apply_translation); break; - case 2: write_word(space, address, data, apply_translation); break; - case 4: write_dword(space, address, data, apply_translation); break; - case 8: write_qword(space, address, data, apply_translation); break; - } -} - - -/*------------------------------------------------- - read_opcode - read 1,2,4 or 8 bytes at the - given offset from opcode space --------------------------------------------------*/ - -u64 debugger_cpu::read_opcode(address_space &space, offs_t address, int size) -{ - device_memory_interface &memory = space.device().memory(); - - u64 result = ~u64(0) & (~u64(0) >> (64 - 8*size)); - - /* keep in logical range */ - address &= space.logaddrmask(); - - /* translate to physical first */ - if (!memory.translate(space.spacenum(), TRANSLATE_FETCH_DEBUG, address)) - return result; - - /* keep in physical range */ - address &= space.addrmask(); - - /* switch off the size and handle unaligned accesses */ - switch (size) - { - case 1: - result = space.read_byte(address); - break; - - case 2: - result = space.read_word_unaligned(address); - break; - - case 4: - result = space.read_dword_unaligned(address); - break; - - case 6: - case 8: - result = space.read_qword_unaligned(address); - break; - } - - return result; -} - - - -/*************************************************************************** INTERNAL HELPERS ***************************************************************************/ @@ -599,572 +277,15 @@ void debugger_cpu::on_vblank(screen_device &device, bool vblank_state) void debugger_cpu::reset_transient_flags() { /* loop over CPUs and reset the transient flags */ - for (device_t &device : device_iterator(m_machine.root_device())) + for (device_t &device : device_enumerator(m_machine.root_device())) device.debug()->reset_transient_flag(); m_stop_when_not_device = nullptr; } - -/*************************************************************************** - EXPRESSION HANDLERS -***************************************************************************/ - -/*------------------------------------------------- - expression_get_device - return a device - based on a case insensitive tag search --------------------------------------------------*/ - -device_t* debugger_cpu::expression_get_device(const char *tag) -{ - // convert to lowercase then lookup the name (tags are enforced to be all lower case) - std::string fullname(tag); - strmakelower(fullname); - return m_machine.root_device().subdevice(fullname.c_str()); -} - - -/*------------------------------------------------- - expression_read_memory - read 1,2,4 or 8 bytes - at the given offset in the given address - space --------------------------------------------------*/ - -u64 debugger_cpu::expression_read_memory(void *param, const char *name, expression_space spacenum, u32 address, int size, bool disable_se) -{ - switch (spacenum) - { - case EXPSPACE_PROGRAM_LOGICAL: - case EXPSPACE_DATA_LOGICAL: - case EXPSPACE_IO_LOGICAL: - case EXPSPACE_SPACE3_LOGICAL: - { - device_t *device = nullptr; - device_memory_interface *memory; - - if (name != nullptr) - device = expression_get_device(name); - if (device == nullptr || !device->interface(memory)) - { - device = get_visible_cpu(); - memory = &device->memory(); - } - if (memory->has_space(AS_PROGRAM + (spacenum - EXPSPACE_PROGRAM_LOGICAL))) - { - address_space &space = memory->space(AS_PROGRAM + (spacenum - EXPSPACE_PROGRAM_LOGICAL)); - auto dis = m_machine.disable_side_effects(disable_se); - return read_memory(space, address, size, true); - } - break; - } - - case EXPSPACE_PROGRAM_PHYSICAL: - case EXPSPACE_DATA_PHYSICAL: - case EXPSPACE_IO_PHYSICAL: - case EXPSPACE_SPACE3_PHYSICAL: - { - device_t *device = nullptr; - device_memory_interface *memory; - - if (name != nullptr) - device = expression_get_device(name); - if (device == nullptr || !device->interface(memory)) - { - device = get_visible_cpu(); - memory = &device->memory(); - } - if (memory->has_space(AS_PROGRAM + (spacenum - EXPSPACE_PROGRAM_PHYSICAL))) - { - address_space &space = memory->space(AS_PROGRAM + (spacenum - EXPSPACE_PROGRAM_PHYSICAL)); - auto dis = m_machine.disable_side_effects(disable_se); - return read_memory(space, address, size, false); - } - break; - } - - case EXPSPACE_RAMWRITE: - { - device_t *device = nullptr; - device_memory_interface *memory; - - if (name != nullptr) - device = expression_get_device(name); - if (device == nullptr || !device->interface(memory)) - { - device = get_visible_cpu(); - memory = &device->memory(); - } - auto dis = m_machine.disable_side_effects(disable_se); - return expression_read_program_direct(memory->space(AS_PROGRAM), (spacenum == EXPSPACE_OPCODE), address, size); - break; - } - - case EXPSPACE_OPCODE: - { - device_t *device = nullptr; - device_memory_interface *memory; - - if (name != nullptr) - device = expression_get_device(name); - if (device == nullptr || !device->interface(memory)) - { - device = get_visible_cpu(); - memory = &device->memory(); - } - auto dis = m_machine.disable_side_effects(disable_se); - return expression_read_program_direct(memory->space(AS_OPCODES), (spacenum == EXPSPACE_OPCODE), address, size); - break; - } - - case EXPSPACE_REGION: - if (name == nullptr) - break; - return expression_read_memory_region(name, address, size); - break; - - default: - break; - } - - return 0; -} - - -/*------------------------------------------------- - expression_read_program_direct - read memory - directly from an opcode or RAM pointer --------------------------------------------------*/ - -u64 debugger_cpu::expression_read_program_direct(address_space &space, int opcode, offs_t address, int size) -{ - u8 *base; - - /* adjust the address into a byte address, but not if being called recursively */ - if ((opcode & 2) == 0) - address = space.address_to_byte(address); - - /* call ourself recursively until we are byte-sized */ - if (size > 1) - { - int halfsize = size / 2; - - /* read each half, from lower address to upper address */ - u64 r0 = expression_read_program_direct(space, opcode | 2, address + 0, halfsize); - u64 r1 = expression_read_program_direct(space, opcode | 2, address + halfsize, halfsize); - - /* assemble based on the target endianness */ - if (space.endianness() == ENDIANNESS_LITTLE) - return r0 | (r1 << (8 * halfsize)); - else - return r1 | (r0 << (8 * halfsize)); - } - - /* handle the byte-sized final requests */ - else - { - /* lowmask specified which address bits are within the databus width */ - offs_t lowmask = space.data_width() / 8 - 1; - - /* get the base of memory, aligned to the address minus the lowbits */ - base = (u8 *)space.get_read_ptr(address & ~lowmask); - - /* if we have a valid base, return the appropriate byte */ - if (base != nullptr) - { - if (space.endianness() == ENDIANNESS_LITTLE) - return base[BYTE8_XOR_LE(address) & lowmask]; - else - return base[BYTE8_XOR_BE(address) & lowmask]; - } - } - - return 0; -} - - -/*------------------------------------------------- - expression_read_memory_region - read memory - from a memory region --------------------------------------------------*/ - -u64 debugger_cpu::expression_read_memory_region(const char *rgntag, offs_t address, int size) -{ - memory_region *region = m_machine.root_device().memregion(rgntag); - u64 result = ~u64(0) >> (64 - 8*size); - - /* make sure we get a valid base before proceeding */ - if (region != nullptr) - { - /* call ourself recursively until we are byte-sized */ - if (size > 1) - { - int halfsize = size / 2; - u64 r0, r1; - - /* read each half, from lower address to upper address */ - r0 = expression_read_memory_region(rgntag, address + 0, halfsize); - r1 = expression_read_memory_region(rgntag, address + halfsize, halfsize); - - /* assemble based on the target endianness */ - if (region->endianness() == ENDIANNESS_LITTLE) - result = r0 | (r1 << (8 * halfsize)); - else - result = r1 | (r0 << (8 * halfsize)); - } - - /* only process if we're within range */ - else if (address < region->bytes()) - { - /* lowmask specified which address bits are within the databus width */ - u32 lowmask = region->bytewidth() - 1; - u8 *base = region->base() + (address & ~lowmask); - - /* if we have a valid base, return the appropriate byte */ - if (region->endianness() == ENDIANNESS_LITTLE) - result = base[BYTE8_XOR_LE(address) & lowmask]; - else - result = base[BYTE8_XOR_BE(address) & lowmask]; - } - } - return result; -} - - -/*------------------------------------------------- - expression_write_memory - write 1,2,4 or 8 - bytes at the given offset in the given address - space --------------------------------------------------*/ - -void debugger_cpu::expression_write_memory(void *param, const char *name, expression_space spacenum, u32 address, int size, u64 data, bool disable_se) -{ - device_t *device = nullptr; - device_memory_interface *memory; - - switch (spacenum) - { - case EXPSPACE_PROGRAM_LOGICAL: - case EXPSPACE_DATA_LOGICAL: - case EXPSPACE_IO_LOGICAL: - case EXPSPACE_SPACE3_LOGICAL: - if (name != nullptr) - device = expression_get_device(name); - if (device == nullptr || !device->interface(memory)) - { - device = get_visible_cpu(); - memory = &device->memory(); - } - if (memory->has_space(AS_PROGRAM + (spacenum - EXPSPACE_PROGRAM_LOGICAL))) - { - address_space &space = memory->space(AS_PROGRAM + (spacenum - EXPSPACE_PROGRAM_LOGICAL)); - auto dis = m_machine.disable_side_effects(disable_se); - write_memory(space, address, data, size, true); - } - break; - - case EXPSPACE_PROGRAM_PHYSICAL: - case EXPSPACE_DATA_PHYSICAL: - case EXPSPACE_IO_PHYSICAL: - case EXPSPACE_SPACE3_PHYSICAL: - if (name != nullptr) - device = expression_get_device(name); - if (device == nullptr || !device->interface(memory)) - { - device = get_visible_cpu(); - memory = &device->memory(); - } - if (memory->has_space(AS_PROGRAM + (spacenum - EXPSPACE_PROGRAM_PHYSICAL))) - { - address_space &space = memory->space(AS_PROGRAM + (spacenum - EXPSPACE_PROGRAM_PHYSICAL)); - auto dis = m_machine.disable_side_effects(disable_se); - write_memory(space, address, data, size, false); - } - break; - - case EXPSPACE_RAMWRITE: { - if (name != nullptr) - device = expression_get_device(name); - if (device == nullptr || !device->interface(memory)) - { - device = get_visible_cpu(); - memory = &device->memory(); - } - auto dis = m_machine.disable_side_effects(disable_se); - expression_write_program_direct(memory->space(AS_PROGRAM), (spacenum == EXPSPACE_OPCODE), address, size, data); - break; - } - - case EXPSPACE_OPCODE: { - if (name != nullptr) - device = expression_get_device(name); - if (device == nullptr || !device->interface(memory)) - { - device = get_visible_cpu(); - memory = &device->memory(); - } - auto dis = m_machine.disable_side_effects(disable_se); - expression_write_program_direct(memory->space(AS_OPCODES), (spacenum == EXPSPACE_OPCODE), address, size, data); - break; - } - - case EXPSPACE_REGION: - if (name == nullptr) - break; - expression_write_memory_region(name, address, size, data); - break; - - default: - break; - } -} - - -/*------------------------------------------------- - expression_write_program_direct - write memory - directly to an opcode or RAM pointer --------------------------------------------------*/ - -void debugger_cpu::expression_write_program_direct(address_space &space, int opcode, offs_t address, int size, u64 data) -{ - /* adjust the address into a byte address, but not if being called recursively */ - if ((opcode & 2) == 0) - address = space.address_to_byte(address); - - /* call ourself recursively until we are byte-sized */ - if (size > 1) - { - int halfsize = size / 2; - - /* break apart based on the target endianness */ - u64 halfmask = ~u64(0) >> (64 - 8 * halfsize); - u64 r0, r1; - if (space.endianness() == ENDIANNESS_LITTLE) - { - r0 = data & halfmask; - r1 = (data >> (8 * halfsize)) & halfmask; - } - else - { - r0 = (data >> (8 * halfsize)) & halfmask; - r1 = data & halfmask; - } - - /* write each half, from lower address to upper address */ - expression_write_program_direct(space, opcode | 2, address + 0, halfsize, r0); - expression_write_program_direct(space, opcode | 2, address + halfsize, halfsize, r1); - } - - /* handle the byte-sized final case */ - else - { - /* lowmask specified which address bits are within the databus width */ - offs_t lowmask = space.data_width() / 8 - 1; - - /* get the base of memory, aligned to the address minus the lowbits */ - u8 *base = (u8 *)space.get_read_ptr(address & ~lowmask); - - /* if we have a valid base, write the appropriate byte */ - if (base != nullptr) - { - if (space.endianness() == ENDIANNESS_LITTLE) - base[BYTE8_XOR_LE(address) & lowmask] = data; - else - base[BYTE8_XOR_BE(address) & lowmask] = data; - m_memory_modified = true; - } - } -} - - -/*------------------------------------------------- - expression_write_memory_region - write memory - from a memory region --------------------------------------------------*/ - -void debugger_cpu::expression_write_memory_region(const char *rgntag, offs_t address, int size, u64 data) -{ - memory_region *region = m_machine.root_device().memregion(rgntag); - - /* make sure we get a valid base before proceeding */ - if (region != nullptr) - { - /* call ourself recursively until we are byte-sized */ - if (size > 1) - { - int halfsize = size / 2; - - /* break apart based on the target endianness */ - u64 halfmask = ~u64(0) >> (64 - 8 * halfsize); - u64 r0, r1; - if (region->endianness() == ENDIANNESS_LITTLE) - { - r0 = data & halfmask; - r1 = (data >> (8 * halfsize)) & halfmask; - } - else - { - r0 = (data >> (8 * halfsize)) & halfmask; - r1 = data & halfmask; - } - - /* write each half, from lower address to upper address */ - expression_write_memory_region(rgntag, address + 0, halfsize, r0); - expression_write_memory_region(rgntag, address + halfsize, halfsize, r1); - } - - /* only process if we're within range */ - else if (address < region->bytes()) - { - /* lowmask specified which address bits are within the databus width */ - u32 lowmask = region->bytewidth() - 1; - u8 *base = region->base() + (address & ~lowmask); - - /* if we have a valid base, set the appropriate byte */ - if (region->endianness() == ENDIANNESS_LITTLE) - { - base[BYTE8_XOR_LE(address) & lowmask] = data; - } - else - { - base[BYTE8_XOR_BE(address) & lowmask] = data; - } - m_memory_modified = true; - } - } -} - - -/*------------------------------------------------- - expression_validate - validate that the - provided expression references an - appropriate name --------------------------------------------------*/ - -expression_error::error_code debugger_cpu::expression_validate(void *param, const char *name, expression_space space) -{ - device_t *device = nullptr; - device_memory_interface *memory; - - switch (space) - { - case EXPSPACE_PROGRAM_LOGICAL: - case EXPSPACE_DATA_LOGICAL: - case EXPSPACE_IO_LOGICAL: - case EXPSPACE_SPACE3_LOGICAL: - if (name) - { - device = expression_get_device(name); - if (device == nullptr) - return expression_error::INVALID_MEMORY_NAME; - } - if (!device) - device = get_visible_cpu(); - if (!device->interface(memory) || !memory->has_space(AS_PROGRAM + (space - EXPSPACE_PROGRAM_LOGICAL))) - return expression_error::NO_SUCH_MEMORY_SPACE; - break; - - case EXPSPACE_PROGRAM_PHYSICAL: - case EXPSPACE_DATA_PHYSICAL: - case EXPSPACE_IO_PHYSICAL: - case EXPSPACE_SPACE3_PHYSICAL: - if (name) - { - device = expression_get_device(name); - if (device == nullptr) - return expression_error::INVALID_MEMORY_NAME; - } - if (!device) - device = get_visible_cpu(); - if (!device->interface(memory) || !memory->has_space(AS_PROGRAM + (space - EXPSPACE_PROGRAM_PHYSICAL))) - return expression_error::NO_SUCH_MEMORY_SPACE; - break; - - case EXPSPACE_RAMWRITE: - if (name) - { - device = expression_get_device(name); - if (device == nullptr || !device->interface(memory)) - return expression_error::INVALID_MEMORY_NAME; - } - if (!device) - device = get_visible_cpu(); - if (!device->interface(memory) || !memory->has_space(AS_PROGRAM)) - return expression_error::NO_SUCH_MEMORY_SPACE; - break; - - case EXPSPACE_OPCODE: - if (name) - { - device = expression_get_device(name); - if (device == nullptr || !device->interface(memory)) - return expression_error::INVALID_MEMORY_NAME; - } - if (!device) - device = get_visible_cpu(); - if (!device->interface(memory) || !memory->has_space(AS_OPCODES)) - return expression_error::NO_SUCH_MEMORY_SPACE; - break; - - case EXPSPACE_REGION: - if (!name) - return expression_error::MISSING_MEMORY_NAME; - if (!m_machine.root_device().memregion(name) || !m_machine.root_device().memregion(name)->base()) - return expression_error::INVALID_MEMORY_NAME; - break; - - default: - return expression_error::NO_SUCH_MEMORY_SPACE; - } - return expression_error::NONE; -} - - - -/*************************************************************************** - VARIABLE GETTERS/SETTERS -***************************************************************************/ - -/*------------------------------------------------- - get_beamx - get beam horizontal position --------------------------------------------------*/ - -u64 debugger_cpu::get_beamx(symbol_table &table, screen_device *screen) -{ - return (screen != nullptr) ? screen->hpos() : 0; -} - - -/*------------------------------------------------- - get_beamy - get beam vertical position --------------------------------------------------*/ - -u64 debugger_cpu::get_beamy(symbol_table &table, screen_device *screen) -{ - return (screen != nullptr) ? screen->vpos() : 0; -} - - -/*------------------------------------------------- - get_frame - get current frame number --------------------------------------------------*/ - -u64 debugger_cpu::get_frame(symbol_table &table, screen_device *screen) -{ - return (screen != nullptr) ? screen->frame_number() : 0; -} - - -/*------------------------------------------------- - get_cpunum - getter callback for the - 'cpunum' symbol --------------------------------------------------*/ - -u64 debugger_cpu::get_cpunum(symbol_table &table) -{ - execute_interface_iterator iter(m_machine.root_device()); - return iter.indexof(m_visiblecpu->execute()); -} - +//************************************************************************** +// EXECUTION HOOKS +//************************************************************************** void debugger_cpu::start_hook(device_t *device, bool stop_on_vblank) { @@ -1172,8 +293,15 @@ void debugger_cpu::start_hook(device_t *device, bool stop_on_vblank) assert(m_livecpu == nullptr); m_livecpu = device; + // can't stop on a device without a state interface + if (m_execution_state == exec_state::STOPPED && dynamic_cast<device_state_interface *>(device) == nullptr) + { + if (m_stop_when_not_device == nullptr) + m_stop_when_not_device = device; + m_execution_state = exec_state::RUNNING; + } // if we're a new device, stop now - if (m_stop_when_not_device != nullptr && m_stop_when_not_device != device) + else if (m_stop_when_not_device != nullptr && m_stop_when_not_device != device && device->debug()->observing()) { m_stop_when_not_device = nullptr; m_execution_state = exec_state::STOPPED; @@ -1183,13 +311,14 @@ void debugger_cpu::start_hook(device_t *device, bool stop_on_vblank) // if we're running, do some periodic updating if (m_execution_state != exec_state::STOPPED) { - if (device == m_visiblecpu && osd_ticks() > m_last_periodic_update_time + osd_ticks_per_second() / 4) + device_t *visiblecpu = m_machine.debugger().console().get_visible_cpu(); + if (device == visiblecpu && osd_ticks() > m_last_periodic_update_time + osd_ticks_per_second() / 4) { // check for periodic updates m_machine.debug_view().update_all(); m_machine.debug_view().flush_osd_updates(); m_last_periodic_update_time = osd_ticks(); } - else if (device == m_breakcpu) + if (device == m_breakcpu) { // check for pending breaks m_execution_state = exec_state::STOPPED; m_breakcpu = nullptr; @@ -1209,7 +338,10 @@ void debugger_cpu::start_hook(device_t *device, bool stop_on_vblank) } // check for debug keypresses if (m_machine.ui_input().pressed(IPT_UI_DEBUG_BREAK)) - m_visiblecpu->debug()->halt_on_next_instruction("User-initiated break\n"); + { + visiblecpu->debug()->ignore(false); + visiblecpu->debug()->halt_on_next_instruction("User-initiated break\n"); + } } } @@ -1217,6 +349,13 @@ void debugger_cpu::stop_hook(device_t *device) { assert(m_livecpu == device); + // if we are supposed to be stopped at this point (most likely because of a watchpoint), keep going until this CPU is live again + if (m_execution_state == exec_state::STOPPED) + { + m_breakcpu = device; + m_execution_state = exec_state::RUNNING; + } + // clear the live CPU m_livecpu = nullptr; } @@ -1248,7 +387,7 @@ void debugger_cpu::go_vblank() m_execution_state = exec_state::RUNNING; } -void debugger_cpu::halt_on_next_instruction(device_t *device, util::format_argument_pack<std::ostream> &&args) +void debugger_cpu::halt_on_next_instruction(device_t *device, util::format_argument_pack<char> &&args) { // if something is pending on this CPU already, ignore this request if (device == m_breakcpu) @@ -1270,6 +409,70 @@ void debugger_cpu::halt_on_next_instruction(device_t *device, util::format_argum } } + +//------------------------------------------------- +// wait_for_debugger - pause during execution to +// allow debugging +//------------------------------------------------- + +void debugger_cpu::wait_for_debugger(device_t &device) +{ + assert(is_stopped()); + assert(within_instruction_hook()); + + bool firststop = true; + + // load comments if we haven't yet + ensure_comments_loaded(); + + // reset any transient state + reset_transient_flags(); + set_break_cpu(nullptr); + + // remember the last visible CPU in the debugger + m_machine.debugger().console().set_visible_cpu(&device); + + // update all views + m_machine.debug_view().update_all(); + m_machine.debugger().refresh_display(); + + // wait for the debugger; during this time, disable sound output + m_machine.sound().debugger_mute(true); + while (is_stopped()) + { + // flush any pending updates before waiting again + m_machine.debug_view().flush_osd_updates(); + + emulator_info::periodic_check(); + + // clear the memory modified flag and wait + set_memory_modified(false); + if (m_machine.debug_flags & DEBUG_FLAG_OSD_ENABLED) + m_machine.osd().wait_for_debugger(device, firststop); + firststop = false; + + // if something modified memory, update the screen + if (memory_modified()) + { + m_machine.debug_view().update_all(DVT_DISASSEMBLY); + m_machine.debug_view().update_all(DVT_STATE); + m_machine.debugger().refresh_display(); + } + + // check for commands in the source file + m_machine.debugger().console().process_source_file(); + + // if an event got scheduled, resume + if (m_machine.scheduled_event_pending()) + set_execution_running(); + } + m_machine.sound().debugger_mute(false); + + // remember the last visible CPU in the debugger + m_machine.debugger().console().set_visible_cpu(&device); +} + + //************************************************************************** // DEVICE DEBUG //************************************************************************** @@ -1285,10 +488,10 @@ device_debug::device_debug(device_t &device) , m_state(nullptr) , m_disasm(nullptr) , m_flags(0) - , m_symtable(&device, device.machine().debugger().cpu().get_global_symtable()) - , m_instrhook(nullptr) + , m_symtable(std::make_unique<symbol_table>(device.machine(), &device.machine().debugger().cpu().global_symtable(), &device)) , m_stepaddr(0) , m_stepsleft(0) + , m_delay_steps(0) , m_stopaddr(0) , m_stoptime(attotime::zero) , m_stopirq(0) @@ -1296,13 +499,15 @@ device_debug::device_debug(device_t &device) , m_endexectime(attotime::zero) , m_total_cycles(0) , m_last_total_cycles(0) + , m_was_waiting(true) , m_pc_history_index(0) + , m_pc_history_valid(0) , m_bplist() , m_rplist() + , m_eplist() , m_triggered_breakpoint(nullptr) , m_triggered_watchpoint(nullptr) , m_trace(nullptr) - , m_hotspot_threshhold(0) , m_track_pc_set() , m_track_pc(false) , m_comment_set() @@ -1321,15 +526,14 @@ device_debug::device_debug(device_t &device) // set up notifiers and clear the passthrough handlers if (m_memory) { int count = m_memory->max_space_count(); - m_phr.resize(count, nullptr); - m_phw.resize(count, nullptr); + m_phw.resize(count); for (int i=0; i != count; i++) if (m_memory->has_space(i)) { address_space &space = m_memory->space(i); - m_notifiers.push_back(space.add_change_notifier([this, &space](read_or_write mode) { reinstall(space, mode); })); + m_notifiers.emplace_back(space.add_change_notifier([this, &space] (read_or_write mode) { reinstall(space, mode); })); } else - m_notifiers.push_back(-1); + m_notifiers.emplace_back(); } // set up state-related stuff @@ -1338,49 +542,48 @@ device_debug::device_debug(device_t &device) // add global symbol for cycles and totalcycles if (m_exec != nullptr) { - m_symtable.add("cycles", get_cycles); - m_symtable.add("totalcycles", get_totalcycles); - m_symtable.add("lastinstructioncycles", get_lastinstructioncycles); + m_symtable->add("cycles", [this]() { return m_exec->cycles_remaining(); }); + m_symtable->add("totalcycles", symbol_table::READ_ONLY, &m_total_cycles); + m_symtable->add("lastinstructioncycles", [this]() { return m_total_cycles - m_last_total_cycles; }); } // add entries to enable/disable unmap reporting for each space if (m_memory != nullptr) { if (m_memory->has_space(AS_PROGRAM)) - m_symtable.add( + m_symtable->add( "logunmap", - [&space = m_memory->space(AS_PROGRAM)] (symbol_table &table) { return space.log_unmap(); }, - [&space = m_memory->space(AS_PROGRAM)] (symbol_table &table, u64 value) { return space.set_log_unmap(bool(value)); }); + [&space = m_memory->space(AS_PROGRAM)] () { return space.log_unmap(); }, + [&space = m_memory->space(AS_PROGRAM)] (u64 value) { return space.set_log_unmap(bool(value)); }); if (m_memory->has_space(AS_DATA)) - m_symtable.add( + m_symtable->add( "logunmap", - [&space = m_memory->space(AS_DATA)] (symbol_table &table) { return space.log_unmap(); }, - [&space = m_memory->space(AS_DATA)] (symbol_table &table, u64 value) { return space.set_log_unmap(bool(value)); }); + [&space = m_memory->space(AS_DATA)] () { return space.log_unmap(); }, + [&space = m_memory->space(AS_DATA)] (u64 value) { return space.set_log_unmap(bool(value)); }); if (m_memory->has_space(AS_IO)) - m_symtable.add( + m_symtable->add( "logunmap", - [&space = m_memory->space(AS_IO)] (symbol_table &table) { return space.log_unmap(); }, - [&space = m_memory->space(AS_IO)] (symbol_table &table, u64 value) { return space.set_log_unmap(bool(value)); }); + [&space = m_memory->space(AS_IO)] () { return space.log_unmap(); }, + [&space = m_memory->space(AS_IO)] (u64 value) { return space.set_log_unmap(bool(value)); }); if (m_memory->has_space(AS_OPCODES)) - m_symtable.add( + m_symtable->add( "logunmap", - [&space = m_memory->space(AS_OPCODES)] (symbol_table &table) { return space.log_unmap(); }, - [&space = m_memory->space(AS_OPCODES)] (symbol_table &table, u64 value) { return space.set_log_unmap(bool(value)); }); + [&space = m_memory->space(AS_OPCODES)] () { return space.log_unmap(); }, + [&space = m_memory->space(AS_OPCODES)] (u64 value) { return space.set_log_unmap(bool(value)); }); } // add all registers into it - std::string tempstr; for (const auto &entry : m_state->state_entries()) { // TODO: floating point registers if (!entry->is_float()) { using namespace std::placeholders; - strmakelower(tempstr.assign(entry->symbol())); - m_symtable.add( + std::string tempstr(strmakelower(entry->symbol())); + m_symtable->add( tempstr.c_str(), - std::bind(&device_debug::get_state, _1, entry->index()), - entry->writeable() ? std::bind(&device_debug::set_state, _1, entry->index(), _2) : symbol_table::setter_func(nullptr), + std::bind(&device_state_entry::value, entry.get()), + entry->writeable() ? std::bind(&device_state_entry::set_value, entry.get(), _1) : symbol_table::setter_func(nullptr), entry->format_string()); } } @@ -1392,8 +595,8 @@ device_debug::device_debug(device_t &device) m_flags = DEBUG_FLAG_OBSERVING | DEBUG_FLAG_HISTORY; // if no curpc, add one - if (m_state && !m_symtable.find("curpc")) - m_symtable.add("curpc", get_current_pc); + if (m_state && !m_symtable->find("curpc")) + m_symtable->add("curpc", std::bind(&device_state_interface::pcbase, m_state)); } // set up trace @@ -1412,11 +615,12 @@ device_debug::~device_debug() breakpoint_clear_all(); watchpoint_clear_all(); registerpoint_clear_all(); + exceptionpoint_clear_all(); } void device_debug::write_tracking(address_space &space, offs_t address, u64 data) { - dasm_memory_access const newAccess(space.spacenum(), address, data, history_pc(0)); + dasm_memory_access const newAccess(space.spacenum(), address, data, m_state->pcbase()); std::pair<std::set<dasm_memory_access>::iterator, bool> trackedAccess = m_track_mem_set.insert(newAccess); if (!trackedAccess.second) trackedAccess.first->m_pc = newAccess.m_pc; @@ -1425,30 +629,16 @@ void device_debug::write_tracking(address_space &space, offs_t address, u64 data void device_debug::reinstall(address_space &space, read_or_write mode) { int id = space.spacenum(); - if (u32(mode) & u32(read_or_write::READ)) - { - if (m_phr[id]) - m_phr[id]->remove(); - if (!m_hotspots.empty()) - switch (space.data_width()) - { - case 8: m_phr[id] = space.install_read_tap(0, space.addrmask(), "hotspot", [this, &space](offs_t address, u8 &, u8 ) { hotspot_check(space, address); }, m_phr[id]); break; - case 16: m_phr[id] = space.install_read_tap(0, space.addrmask(), "hotspot", [this, &space](offs_t address, u16 &, u16) { hotspot_check(space, address); }, m_phr[id]); break; - case 32: m_phr[id] = space.install_read_tap(0, space.addrmask(), "hotspot", [this, &space](offs_t address, u32 &, u32) { hotspot_check(space, address); }, m_phr[id]); break; - case 64: m_phr[id] = space.install_read_tap(0, space.addrmask(), "hotspot", [this, &space](offs_t address, u64 &, u64) { hotspot_check(space, address); }, m_phr[id]); break; - } - } if (u32(mode) & u32(read_or_write::WRITE)) { - if (m_phw[id]) - m_phw[id]->remove(); + m_phw[id].remove(); if (m_track_mem) switch (space.data_width()) { - case 8: m_phw[id] = space.install_read_tap(0, space.addrmask(), "track_mem", [this, &space](offs_t address, u8 &data, u8 ) { write_tracking(space, address, data); }, m_phw[id]); break; - case 16: m_phw[id] = space.install_read_tap(0, space.addrmask(), "track_mem", [this, &space](offs_t address, u16 &data, u16) { write_tracking(space, address, data); }, m_phw[id]); break; - case 32: m_phw[id] = space.install_read_tap(0, space.addrmask(), "track_mem", [this, &space](offs_t address, u32 &data, u32) { write_tracking(space, address, data); }, m_phw[id]); break; - case 64: m_phw[id] = space.install_read_tap(0, space.addrmask(), "track_mem", [this, &space](offs_t address, u64 &data, u64) { write_tracking(space, address, data); }, m_phw[id]); break; + case 8: m_phw[id] = space.install_write_tap(0, space.addrmask(), "track_mem", [this, &space] (offs_t address, u8 &data, u8 ) { write_tracking(space, address, data); }, &m_phw[id]); break; + case 16: m_phw[id] = space.install_write_tap(0, space.addrmask(), "track_mem", [this, &space] (offs_t address, u16 &data, u16) { write_tracking(space, address, data); }, &m_phw[id]); break; + case 32: m_phw[id] = space.install_write_tap(0, space.addrmask(), "track_mem", [this, &space] (offs_t address, u32 &data, u32) { write_tracking(space, address, data); }, &m_phw[id]); break; + case 64: m_phw[id] = space.install_write_tap(0, space.addrmask(), "track_mem", [this, &space] (offs_t address, u64 &data, u64) { write_tracking(space, address, data); }, &m_phw[id]); break; } } } @@ -1462,6 +652,21 @@ void device_debug::reinstall_all(read_or_write mode) } //------------------------------------------------- +// set_track_mem - start or stop tracking memory +// writes +//------------------------------------------------- + +void device_debug::set_track_mem(bool value) +{ + if (m_track_mem != value) + { + m_track_mem = value; + reinstall_all(read_or_write::WRITE); + } +} + + +//------------------------------------------------- // start_hook - the scheduler calls this hook // before beginning execution for the given device //------------------------------------------------- @@ -1496,15 +701,53 @@ void device_debug::stop_hook() // acknowledged //------------------------------------------------- -void device_debug::interrupt_hook(int irqline) +void device_debug::interrupt_hook(int irqline, offs_t pc) { + // CPU is presumably no longer waiting if it acknowledges an interrupt + if (m_was_waiting) + { + m_was_waiting = false; + compute_debug_flags(); + } + // see if this matches a pending interrupt request if ((m_flags & DEBUG_FLAG_STOP_INTERRUPT) != 0 && (m_stopirq == -1 || m_stopirq == irqline)) { m_device.machine().debugger().cpu().set_execution_stopped(); - m_device.machine().debugger().console().printf("Stopped on interrupt (CPU '%s', IRQ %d)\n", m_device.tag(), irqline); + const address_space &space = m_memory->space(AS_PROGRAM); + if (space.is_octal()) + m_device.machine().debugger().console().printf("Stopped on interrupt (CPU '%s', IRQ %d, PC=%0*o)\n", m_device.tag(), irqline, (space.logaddr_width() + 2) / 3, pc); + else + m_device.machine().debugger().console().printf("Stopped on interrupt (CPU '%s', IRQ %d, PC=%0*X)\n", m_device.tag(), irqline, space.logaddrchars(), pc); compute_debug_flags(); } + + if (m_trace != nullptr) + m_trace->interrupt_update(irqline, pc); + + if ((m_flags & (DEBUG_FLAG_STEPPING_OVER | DEBUG_FLAG_STEPPING_OUT | DEBUG_FLAG_STEPPING_BRANCH)) != 0) + { + if ((m_flags & DEBUG_FLAG_CALL_IN_PROGRESS) == 0) + { + if ((m_flags & DEBUG_FLAG_TEST_IN_PROGRESS) != 0) + { + if ((m_stepaddr == pc && (m_flags & DEBUG_FLAG_STEPPING_BRANCH_FALSE) != 0) || + (m_stepaddr != pc && m_delay_steps == 1 && (m_flags & (DEBUG_FLAG_STEPPING_OUT | DEBUG_FLAG_STEPPING_BRANCH_TRUE)) != 0)) + { + // step over the interrupt and then call it finished + m_flags = (m_flags & ~(DEBUG_FLAG_TEST_IN_PROGRESS | DEBUG_FLAG_STEPPING_ANY)) | DEBUG_FLAG_STEPPING_OVER; + m_stepsleft = 1; + } + } + + // remember the interrupt return address + m_flags |= DEBUG_FLAG_CALL_IN_PROGRESS; + m_stepaddr = pc; + } + + m_flags &= ~DEBUG_FLAG_TEST_IN_PROGRESS; + m_delay_steps = 0; + } } @@ -1518,9 +761,54 @@ void device_debug::exception_hook(int exception) // see if this matches an exception breakpoint if ((m_flags & DEBUG_FLAG_STOP_EXCEPTION) != 0 && (m_stopexception == -1 || m_stopexception == exception)) { - m_device.machine().debugger().cpu().set_execution_stopped(); - m_device.machine().debugger().console().printf("Stopped on exception (CPU '%s', exception %d)\n", m_device.tag(), exception); - compute_debug_flags(); + bool matched = true; + if (m_exception_condition && !m_exception_condition->is_empty()) + { + try + { + matched = m_exception_condition->execute(); + } + catch (expression_error &) + { + return; + } + } + + if (matched) + { + m_device.machine().debugger().cpu().set_execution_stopped(); + m_device.machine().debugger().console().printf("Stopped on exception (CPU '%s', exception %X, PC=%s)\n", m_device.tag(), exception, m_state->state_string(STATE_GENPC)); + compute_debug_flags(); + } + } + + // see if any exception points match + if (!m_eplist.empty()) + { + auto epitp = m_eplist.equal_range(exception); + for (auto epit = epitp.first; epit != epitp.second; ++epit) + { + debug_exceptionpoint &ep = *epit->second; + if (ep.hit(exception)) + { + // halt in the debugger by default + debugger_cpu &debugcpu = m_device.machine().debugger().cpu(); + debugcpu.set_execution_stopped(); + + // if we hit, evaluate the action + if (!ep.m_action.empty()) + m_device.machine().debugger().console().execute_command(ep.m_action, false); + + // print a notification, unless the action made us go again + if (debugcpu.is_stopped()) + { + debugcpu.set_execution_stopped(); + m_device.machine().debugger().console().printf("Stopped at exception point %X (CPU '%s', PC=%s)\n", ep.m_index, m_device.tag(), m_state->state_string(STATE_GENPC)); + compute_debug_flags(); + } + break; + } + } } } @@ -1532,18 +820,18 @@ void device_debug::exception_hook(int exception) void device_debug::privilege_hook() { - bool matched = 1; - if ((m_flags & DEBUG_FLAG_STOP_PRIVILEGE) != 0) { - if (m_privilege_condition && !m_privilege_condition->is_empty()) + bool matched = true; + if (m_stop_condition && !m_stop_condition->is_empty()) { try { - matched = m_privilege_condition->execute(); + matched = m_stop_condition->execute(); } - catch (...) + catch (expression_error &) { + return; } } @@ -1556,6 +844,7 @@ void device_debug::privilege_hook() } } + //------------------------------------------------- // instruction_hook - called by the CPU cores // before executing each instruction @@ -1564,17 +853,25 @@ void device_debug::privilege_hook() void device_debug::instruction_hook(offs_t curpc) { running_machine &machine = m_device.machine(); - debugger_cpu& debugcpu = machine.debugger().cpu(); + debugger_cpu &debugcpu = machine.debugger().cpu(); // note that we are in the debugger code debugcpu.set_within_instruction(true); // update the history - m_pc_history[m_pc_history_index++ % HISTORY_SIZE] = curpc; + m_pc_history[m_pc_history_index] = curpc; + m_pc_history_index = (m_pc_history_index + 1) % std::size(m_pc_history); + if (std::size(m_pc_history) > m_pc_history_valid) + ++m_pc_history_valid; // update total cycles m_last_total_cycles = m_total_cycles; m_total_cycles = m_exec->total_cycles(); + if (m_was_waiting) + { + m_was_waiting = false; + compute_debug_flags(); + } // are we tracking our recent pc visits? if (m_track_pc) @@ -1587,26 +884,56 @@ void device_debug::instruction_hook(offs_t curpc) if (m_trace != nullptr) m_trace->update(curpc); - // per-instruction hook? - if (!debugcpu.is_stopped() && (m_flags & DEBUG_FLAG_HOOKED) != 0 && (*m_instrhook)(m_device, curpc)) - debugcpu.set_execution_stopped(); - // handle single stepping if (!debugcpu.is_stopped() && (m_flags & DEBUG_FLAG_STEPPING_ANY) != 0) { + bool do_step = true; + if ((m_flags & (DEBUG_FLAG_CALL_IN_PROGRESS | DEBUG_FLAG_TEST_IN_PROGRESS)) != 0) + { + if (curpc == m_stepaddr) + { + if ((~m_flags & (DEBUG_FLAG_TEST_IN_PROGRESS | DEBUG_FLAG_STEPPING_BRANCH_FALSE)) == 0) + { + debugcpu.set_execution_stopped(); + do_step = false; + } + + // reset the breakpoint + m_flags &= ~(DEBUG_FLAG_CALL_IN_PROGRESS | DEBUG_FLAG_TEST_IN_PROGRESS); + m_delay_steps = 0; + } + else if (m_delay_steps != 0) + { + m_delay_steps--; + if (m_delay_steps == 0) + { + // branch taken or subroutine entered (TODO: interleaved multithreading can falsely trigger this) + if ((m_flags & DEBUG_FLAG_TEST_IN_PROGRESS) != 0 && (m_flags & (DEBUG_FLAG_STEPPING_OUT | DEBUG_FLAG_STEPPING_BRANCH_TRUE)) != 0) + { + debugcpu.set_execution_stopped(); + do_step = false; + } + if ((m_flags & DEBUG_FLAG_CALL_IN_PROGRESS) != 0) + do_step = false; + m_flags &= ~DEBUG_FLAG_TEST_IN_PROGRESS; + } + } + else + do_step = false; + } + // is this an actual step? - if (m_stepaddr == ~0 || curpc == m_stepaddr) + if (do_step) { - // decrement the count and reset the breakpoint + // decrement the count m_stepsleft--; - m_stepaddr = ~0; // if we hit 0, stop if (m_stepsleft == 0) debugcpu.set_execution_stopped(); // update every 100 steps until we are within 200 of the end - else if ((m_flags & DEBUG_FLAG_STEPPING_OUT) == 0 && (m_stepsleft < 200 || m_stepsleft % 100 == 0)) + else if ((m_flags & (DEBUG_FLAG_STEPPING_OUT | DEBUG_FLAG_STEPPING_BRANCH_TRUE | DEBUG_FLAG_STEPPING_BRANCH_FALSE)) == 0 && (m_stepsleft < 200 || m_stepsleft % 100 == 0)) { machine.debug_view().update_all(); machine.debug_view().flush_osd_updates(); @@ -1616,7 +943,7 @@ void device_debug::instruction_hook(offs_t curpc) } // handle breakpoints - if (!debugcpu.is_stopped() && (m_flags & (DEBUG_FLAG_STOP_TIME | DEBUG_FLAG_STOP_PC | DEBUG_FLAG_LIVE_BP)) != 0) + if (!debugcpu.is_stopped() && (m_flags & (DEBUG_FLAG_STOP_TIME | DEBUG_FLAG_STOP_PC | DEBUG_FLAG_LIVE_BP | DEBUG_FLAG_LIVE_RP)) != 0) { // see if we hit a target time if ((m_flags & DEBUG_FLAG_STOP_TIME) != 0 && machine.time() >= m_stoptime) @@ -1628,72 +955,76 @@ void device_debug::instruction_hook(offs_t curpc) // check the temp running breakpoint and break if we hit it else if ((m_flags & DEBUG_FLAG_STOP_PC) != 0 && m_stopaddr == curpc) { - machine.debugger().console().printf("Stopped at temporary breakpoint %X on CPU '%s'\n", m_stopaddr, m_device.tag()); + if (is_octal()) + machine.debugger().console().printf("Stopped at temporary breakpoint %o on CPU '%s'\n", m_stopaddr, m_device.tag()); + else + machine.debugger().console().printf("Stopped at temporary breakpoint %X on CPU '%s'\n", m_stopaddr, m_device.tag()); debugcpu.set_execution_stopped(); } // check for execution breakpoints - else if ((m_flags & DEBUG_FLAG_LIVE_BP) != 0) - breakpoint_check(curpc); + else + { + if ((m_flags & DEBUG_FLAG_LIVE_BP) != 0) + breakpoint_check(curpc); + if ((m_flags & DEBUG_FLAG_LIVE_RP) != 0) + registerpoint_check(); + } } // if we are supposed to halt, do it now if (debugcpu.is_stopped()) - { - bool firststop = true; - - // load comments if we haven't yet - debugcpu.ensure_comments_loaded(); - - // reset any transient state - debugcpu.reset_transient_flags(); - debugcpu.set_break_cpu(nullptr); + debugcpu.wait_for_debugger(m_device); - // remember the last visible CPU in the debugger - debugcpu.set_visible_cpu(&m_device); + // handle step out/over on the instruction we are about to execute + if ((m_flags & (DEBUG_FLAG_STEPPING_OVER | DEBUG_FLAG_STEPPING_OUT | DEBUG_FLAG_STEPPING_BRANCH)) != 0 && (m_flags & (DEBUG_FLAG_CALL_IN_PROGRESS | DEBUG_FLAG_TEST_IN_PROGRESS)) == 0) + prepare_for_step_overout(m_state->pcbase()); - // update all views - machine.debug_view().update_all(); - machine.debugger().refresh_display(); + // no longer in debugger code + debugcpu.set_within_instruction(false); +} - // wait for the debugger; during this time, disable sound output - m_device.machine().sound().debugger_mute(true); - while (debugcpu.is_stopped()) - { - // flush any pending updates before waiting again - machine.debug_view().flush_osd_updates(); - emulator_info::periodic_check(); +//------------------------------------------------- +// wait_hook - called by the CPU cores while +// waiting indefinitely for some kind of event +//------------------------------------------------- - // clear the memory modified flag and wait - debugcpu.set_memory_modified(false); - if (machine.debug_flags & DEBUG_FLAG_OSD_ENABLED) - machine.osd().wait_for_debugger(m_device, firststop); - firststop = false; +void device_debug::wait_hook() +{ + running_machine &machine = m_device.machine(); + debugger_cpu &debugcpu = machine.debugger().cpu(); - // if something modified memory, update the screen - if (debugcpu.memory_modified()) - { - machine.debug_view().update_all(DVT_DISASSEMBLY); - machine.debugger().refresh_display(); - } + // note that we are in the debugger code + debugcpu.set_within_instruction(true); - // check for commands in the source file - machine.debugger().console().process_source_file(); + // update total cycles + m_last_total_cycles = m_total_cycles; + m_total_cycles = m_exec->total_cycles(); - // if an event got scheduled, resume - if (machine.scheduled_event_pending()) - debugcpu.set_execution_running(); + // handle registerpoints (but not breakpoints) + if (!debugcpu.is_stopped() && (m_flags & (DEBUG_FLAG_STOP_TIME | DEBUG_FLAG_LIVE_RP)) != 0) + { + // see if we hit a target time + if ((m_flags & DEBUG_FLAG_STOP_TIME) != 0 && machine.time() >= m_stoptime) + { + machine.debugger().console().printf("Stopped at time interval %.1g\n", machine.time().as_double()); + debugcpu.set_execution_stopped(); } - m_device.machine().sound().debugger_mute(false); - - // remember the last visible CPU in the debugger - debugcpu.set_visible_cpu(&m_device); + else if ((m_flags & DEBUG_FLAG_LIVE_RP) != 0) + registerpoint_check(); } - // handle step out/over on the instruction we are about to execute - if ((m_flags & (DEBUG_FLAG_STEPPING_OVER | DEBUG_FLAG_STEPPING_OUT)) != 0 && m_stepaddr == ~0) - prepare_for_step_overout(m_state->pcbase()); + // if we are supposed to halt, do it now + if (debugcpu.is_stopped()) + { + if (!m_was_waiting) + { + machine.debugger().console().printf("CPU waiting after PC=%s\n", m_state->state_string(STATE_GENPCBASE)); + m_was_waiting = true; + } + debugcpu.wait_for_debugger(m_device); + } // no longer in debugger code debugcpu.set_within_instruction(false); @@ -1701,22 +1032,6 @@ void device_debug::instruction_hook(offs_t curpc) //------------------------------------------------- -// set_instruction_hook - set a hook to be -// called on each instruction for a given device -//------------------------------------------------- - -void device_debug::set_instruction_hook(debug_instruction_hook_func hook) -{ - // set the hook and also the CPU's flag for fast knowledge of the hook - m_instrhook = hook; - if (hook != nullptr) - m_flags |= DEBUG_FLAG_HOOKED; - else - m_flags &= ~DEBUG_FLAG_HOOKED; -} - - -//------------------------------------------------- // ignore - ignore/observe a given device //------------------------------------------------- @@ -1771,8 +1086,9 @@ void device_debug::single_step(int numsteps) m_device.machine().rewind_capture(); m_stepsleft = numsteps; - m_stepaddr = ~0; + m_delay_steps = 0; m_flags |= DEBUG_FLAG_STEPPING; + m_flags &= ~(DEBUG_FLAG_CALL_IN_PROGRESS | DEBUG_FLAG_TEST_IN_PROGRESS); m_device.machine().debugger().cpu().set_execution_running(); } @@ -1788,8 +1104,9 @@ void device_debug::single_step_over(int numsteps) m_device.machine().rewind_capture(); m_stepsleft = numsteps; - m_stepaddr = ~0; + m_delay_steps = 0; m_flags |= DEBUG_FLAG_STEPPING_OVER; + m_flags &= ~(DEBUG_FLAG_CALL_IN_PROGRESS | DEBUG_FLAG_TEST_IN_PROGRESS); m_device.machine().debugger().cpu().set_execution_running(); } @@ -1804,9 +1121,11 @@ void device_debug::single_step_out() assert(m_exec != nullptr); m_device.machine().rewind_capture(); + m_stop_condition.reset(); m_stepsleft = 100; - m_stepaddr = ~0; + m_delay_steps = 0; m_flags |= DEBUG_FLAG_STEPPING_OUT; + m_flags &= ~(DEBUG_FLAG_CALL_IN_PROGRESS | DEBUG_FLAG_TEST_IN_PROGRESS); m_device.machine().debugger().cpu().set_execution_running(); } @@ -1866,12 +1185,13 @@ void device_debug::go_next_device() // exception fires on the visible CPU //------------------------------------------------- -void device_debug::go_exception(int exception) +void device_debug::go_exception(int exception, const char *condition) { assert(m_exec != nullptr); m_device.machine().rewind_invalidate(); m_stopexception = exception; + m_exception_condition = std::make_unique<parsed_expression>(*m_symtable, condition); m_flags |= DEBUG_FLAG_STOP_EXCEPTION; m_device.machine().debugger().cpu().set_execution_running(); } @@ -1902,20 +1222,38 @@ void device_debug::go_privilege(const char *condition) { assert(m_exec != nullptr); m_device.machine().rewind_invalidate(); - m_privilege_condition = std::make_unique<parsed_expression>(&m_symtable, condition); + m_stop_condition = std::make_unique<parsed_expression>(*m_symtable, condition); m_flags |= DEBUG_FLAG_STOP_PRIVILEGE; m_device.machine().debugger().cpu().set_execution_running(); } //------------------------------------------------- +// go_branch - execute until branch taken or +// not taken +//------------------------------------------------- + +void device_debug::go_branch(bool sense, const char *condition) +{ + assert(m_exec != nullptr); + m_device.machine().rewind_invalidate(); + m_stop_condition = std::make_unique<parsed_expression>(*m_symtable, condition); + m_stepsleft = 100; + m_delay_steps = 0; + m_flags |= sense ? DEBUG_FLAG_STEPPING_BRANCH_TRUE : DEBUG_FLAG_STEPPING_BRANCH_FALSE; + m_flags &= ~(DEBUG_FLAG_CALL_IN_PROGRESS | DEBUG_FLAG_TEST_IN_PROGRESS); + m_device.machine().debugger().cpu().set_execution_running(); +} + + +//------------------------------------------------- // halt_on_next_instruction_impl - halt in the // debugger on the next instruction, internal // implementation which is necessary solely due // to templates in C++ being janky as all get out //------------------------------------------------- -void device_debug::halt_on_next_instruction_impl(util::format_argument_pack<std::ostream> &&args) +void device_debug::halt_on_next_instruction_impl(util::format_argument_pack<char> &&args) { assert(m_exec != nullptr); m_device.machine().debugger().cpu().halt_on_next_instruction(&m_device, std::move(args)); @@ -1926,11 +1264,11 @@ void device_debug::halt_on_next_instruction_impl(util::format_argument_pack<std: // given address, or nullptr if none exists there //------------------------------------------------- -const device_debug::breakpoint *device_debug::breakpoint_find(offs_t address) const +const debug_breakpoint *device_debug::breakpoint_find(offs_t address) const { - for (const breakpoint &bp : m_bplist) - if (bp.address() == address) - return &bp; + auto bpitp = m_bplist.equal_range(address); + if (bpitp.first != bpitp.second) + return bpitp.first->second.get(); return nullptr; } @@ -1940,15 +1278,15 @@ const device_debug::breakpoint *device_debug::breakpoint_find(offs_t address) co // returning its index //------------------------------------------------- -int device_debug::breakpoint_set(offs_t address, const char *condition, const char *action) +int device_debug::breakpoint_set(offs_t address, const char *condition, std::string_view action) { // allocate a new one and hook it into our list u32 id = m_device.machine().debugger().cpu().get_breakpoint_index(); - m_bplist.emplace_front(this, m_symtable, id, address, condition, action); + m_bplist.emplace(address, std::make_unique<debug_breakpoint>(this, *m_symtable, id, address, condition, action)); // update the flags and return the index breakpoint_update_flags(); - return m_bplist.front().m_index; + return id; } @@ -1960,10 +1298,10 @@ int device_debug::breakpoint_set(offs_t address, const char *condition, const ch bool device_debug::breakpoint_clear(int index) { // scan the list to see if we own this breakpoint - for (auto bbp = m_bplist.before_begin(); std::next(bbp) != m_bplist.end(); ++bbp) - if (std::next(bbp)->m_index == index) + for (auto bpit = m_bplist.begin(); bpit != m_bplist.end(); ++bpit) + if (bpit->second->m_index == index) { - m_bplist.erase_after(bbp); + m_bplist.erase(bpit); breakpoint_update_flags(); return true; } @@ -1993,13 +1331,16 @@ void device_debug::breakpoint_clear_all() bool device_debug::breakpoint_enable(int index, bool enable) { // scan the list to see if we own this breakpoint - for (breakpoint &bp : m_bplist) + for (auto &bpp : m_bplist) + { + debug_breakpoint &bp = *bpp.second; if (bp.m_index == index) { bp.m_enabled = enable; breakpoint_update_flags(); return true; } + } // we don't own it, return false return false; @@ -2014,8 +1355,8 @@ bool device_debug::breakpoint_enable(int index, bool enable) void device_debug::breakpoint_enable_all(bool enable) { // apply the enable to all breakpoints we own - for (breakpoint &bp : m_bplist) - breakpoint_enable(bp.index(), enable); + for (auto &bpp : m_bplist) + breakpoint_enable(bpp.second->index(), enable); } @@ -2024,14 +1365,14 @@ void device_debug::breakpoint_enable_all(bool enable) // returning its index //------------------------------------------------- -int device_debug::watchpoint_set(address_space &space, read_or_write type, offs_t address, offs_t length, const char *condition, const char *action) +int device_debug::watchpoint_set(address_space &space, read_or_write type, offs_t address, offs_t length, const char *condition, std::string_view action) { if (space.spacenum() >= int(m_wplist.size())) m_wplist.resize(space.spacenum()+1); // allocate a new one u32 id = m_device.machine().debugger().cpu().get_watchpoint_index(); - m_wplist[space.spacenum()].emplace_back(std::make_unique<watchpoint>(this, m_symtable, id, space, type, address, length, condition, action)); + m_wplist[space.spacenum()].emplace_back(std::make_unique<debug_watchpoint>(this, *m_symtable, id, space, type, address, length, condition, action)); return id; } @@ -2111,11 +1452,11 @@ void device_debug::watchpoint_enable_all(bool enable) // returning its index //------------------------------------------------- -int device_debug::registerpoint_set(const char *condition, const char *action) +int device_debug::registerpoint_set(const char *condition, std::string_view action) { // allocate a new one u32 id = m_device.machine().debugger().cpu().get_registerpoint_index(); - m_rplist.emplace_front(m_symtable, id, condition, action); + m_rplist.emplace_front(*m_symtable, id, condition, action); // update the flags and return the index breakpoint_update_flags(); @@ -2164,7 +1505,7 @@ void device_debug::registerpoint_clear_all() bool device_debug::registerpoint_enable(int index, bool enable) { // scan the list to see if we own this conditionpoint - for (registerpoint &rp : m_rplist) + for (debug_registerpoint &rp : m_rplist) if (rp.m_index == index) { rp.m_enabled = enable; @@ -2185,32 +1526,93 @@ bool device_debug::registerpoint_enable(int index, bool enable) void device_debug::registerpoint_enable_all(bool enable) { // apply the enable to all registerpoints we own - for (registerpoint &rp : m_rplist) + for (debug_registerpoint &rp : m_rplist) registerpoint_enable(rp.index(), enable); } //------------------------------------------------- -// hotspot_track - enable/disable tracking of -// hotspots +// exceptionpoint_set - set a new exception +// point, returning its index //------------------------------------------------- -void device_debug::hotspot_track(int numspots, int threshhold) +int device_debug::exceptionpoint_set(int exception, const char *condition, std::string_view action) { - // if we already have tracking enabled, kill it - m_hotspots.clear(); + // allocate a new one and hook it into our list + u32 id = m_device.machine().debugger().cpu().get_exceptionpoint_index(); + m_eplist.emplace(exception, std::make_unique<debug_exceptionpoint>(this, *m_symtable, id, exception, condition, action)); + + // return the index + return id; +} + + +//------------------------------------------------- +// exceptionpoint_clear - clear an exception +// point by index, returning true if we found it +//------------------------------------------------- + +bool device_debug::exceptionpoint_clear(int index) +{ + // scan the list to see if we own this breakpoint + for (auto epit = m_eplist.begin(); epit != m_eplist.end(); ++epit) + if (epit->second->m_index == index) + { + m_eplist.erase(epit); + return true; + } + + // we don't own it, return false + return false; +} - // only start tracking if we have a non-zero count - if (numspots > 0) - { - // allocate memory for hotspots - m_hotspots.resize(numspots); - memset(&m_hotspots[0], 0xff, numspots*sizeof(m_hotspots[0])); - // fill in the info - m_hotspot_threshhold = threshhold; +//------------------------------------------------- +// exceptionpoint_clear_all - clear all exception +// points +//------------------------------------------------- + +void device_debug::exceptionpoint_clear_all() +{ + // clear the list + m_eplist.clear(); +} + + +//------------------------------------------------- +// exceptionpoint_enable - enable/disable an +// exception point by index, returning true if we +// found it +//------------------------------------------------- + +bool device_debug::exceptionpoint_enable(int index, bool enable) +{ + // scan the list to see if we own this exception point + for (auto &epp : m_eplist) + { + debug_exceptionpoint &ep = *epp.second; + if (ep.m_index == index) + { + ep.m_enabled = enable; + return true; + } } - reinstall_all(read_or_write::READ); + + // we don't own it, return false + return false; +} + + +//------------------------------------------------- +// exceptionpoint_enable_all - enable/disable all +// exception points +//------------------------------------------------- + +void device_debug::exceptionpoint_enable_all(bool enable) +{ + // apply the enable to all exception points we own + for (auto &epp : m_eplist) + exceptionpoint_enable(epp.second->index(), enable); } @@ -2219,13 +1621,28 @@ void device_debug::hotspot_track(int numspots, int threshhold) // history //------------------------------------------------- -offs_t device_debug::history_pc(int index) const +std::pair<offs_t, bool> device_debug::history_pc(int index) const { - if (index > 0) - index = 0; - if (index <= -HISTORY_SIZE) - index = -HISTORY_SIZE + 1; - return m_pc_history[(m_pc_history_index + ARRAY_LENGTH(m_pc_history) - 1 + index) % ARRAY_LENGTH(m_pc_history)]; + if ((index <= 0) && (-index < m_pc_history_valid)) + { + int const i = (m_pc_history_index + std::size(m_pc_history) - 1 + index) % std::size(m_pc_history); + return std::make_pair(m_pc_history[i], true); + } + else + { + return std::make_pair(offs_t(0), false); + } +} + + +//------------------------------------------------- +// set_track_pc - turn visited PC tracking on or +// off +//------------------------------------------------- + +void device_debug::set_track_pc(bool value) +{ + m_track_pc = value; } @@ -2233,10 +1650,9 @@ offs_t device_debug::history_pc(int index) const // track_pc_visited - returns a boolean stating // if this PC has been visited or not. CRC32 is // done in this function on currently active CPU. -// TODO: Take a CPU context as input //------------------------------------------------- -bool device_debug::track_pc_visited(const offs_t& pc) const +bool device_debug::track_pc_visited(offs_t pc) const { if (m_track_pc_set.empty()) return false; @@ -2247,10 +1663,9 @@ bool device_debug::track_pc_visited(const offs_t& pc) const //------------------------------------------------- // set_track_pc_visited - set this pc as visited. -// TODO: Take a CPU context as input //------------------------------------------------- -void device_debug::set_track_pc_visited(const offs_t& pc) +void device_debug::set_track_pc_visited(offs_t pc) { const u32 crc = compute_opcode_crc32(pc); m_track_pc_set.insert(dasm_pc_tag(pc, crc)); @@ -2336,7 +1751,7 @@ bool device_debug::comment_export(util::xml::data_node &curnode) // iterate through the comments for (const auto & elem : m_comment_set) { - util::xml::data_node *datanode = curnode.add_child("comment", util::xml::normalize_string(elem.m_text.c_str())); + util::xml::data_node *datanode = curnode.add_child("comment", elem.m_text.c_str()); if (datanode == nullptr) return false; datanode->set_attribute_int("address", elem.m_address); @@ -2389,7 +1804,7 @@ u32 device_debug::compute_opcode_crc32(offs_t pc) const buffer.data_get(pc, dasmresult & util::disasm_interface::LENGTHMASK, true, opbuf); // return a CRC of the exact count of opcode bytes - return core_crc32(0, &opbuf[0], opbuf.size()); + return util::crc32_creator::simple(&opbuf[0], opbuf.size()); } @@ -2397,31 +1812,14 @@ u32 device_debug::compute_opcode_crc32(offs_t pc) const // trace - trace execution of a given device //------------------------------------------------- -void device_debug::trace(FILE *file, bool trace_over, bool detect_loops, bool logerror, const char *action) +void device_debug::trace(std::unique_ptr<std::ostream> &&file, bool trace_over, bool detect_loops, bool logerror, std::string_view action) { // delete any existing tracers m_trace = nullptr; // if we have a new file, make a new tracer if (file != nullptr) - m_trace = std::make_unique<tracer>(*this, *file, trace_over, detect_loops, logerror, action); -} - - -//------------------------------------------------- -// trace_printf - output data into the given -// device's tracefile, if tracing -//------------------------------------------------- - -void device_debug::trace_printf(const char *fmt, ...) -{ - if (m_trace != nullptr) - { - va_list va; - va_start(va, fmt); - m_trace->vprintf(fmt, va); - va_end(va); - } + m_trace = std::make_unique<tracer>(*this, std::move(file), trace_over, detect_loops, logerror, action); } @@ -2433,7 +1831,7 @@ void device_debug::trace_printf(const char *fmt, ...) void device_debug::compute_debug_flags() { running_machine &machine = m_device.machine(); - debugger_cpu& debugcpu = machine.debugger().cpu(); + debugger_cpu &debugcpu = machine.debugger().cpu(); // clear out global flags by default, keep DEBUG_FLAG_OSD_ENABLED machine.debug_flags &= DEBUG_FLAG_OSD_ENABLED; @@ -2449,7 +1847,7 @@ void device_debug::compute_debug_flags() // if we're tracking history, or we're hooked, or stepping, or stopping at a breakpoint // make sure we call the hook - if ((m_flags & (DEBUG_FLAG_HISTORY | DEBUG_FLAG_HOOKED | DEBUG_FLAG_STEPPING_ANY | DEBUG_FLAG_STOP_PC | DEBUG_FLAG_LIVE_BP)) != 0) + if ((m_flags & (DEBUG_FLAG_HISTORY | DEBUG_FLAG_STEPPING_ANY | DEBUG_FLAG_STOP_PC | DEBUG_FLAG_LIVE_BP | DEBUG_FLAG_LIVE_RP)) != 0) machine.debug_flags |= DEBUG_FLAG_CALL_HOOK; // also call if we are tracing @@ -2459,6 +1857,10 @@ void device_debug::compute_debug_flags() // if we are stopping at a particular time and that time is within the current timeslice, we need to be called if ((m_flags & DEBUG_FLAG_STOP_TIME) && m_endexectime <= m_stoptime) machine.debug_flags |= DEBUG_FLAG_CALL_HOOK; + + // if we were waiting, call if only to clear + if (m_was_waiting) + machine.debug_flags |= DEBUG_FLAG_CALL_HOOK; } @@ -2473,12 +1875,32 @@ void device_debug::prepare_for_step_overout(offs_t pc) // disassemble the current instruction and get the flags u32 dasmresult = buffer.disassemble_info(pc); + if ((dasmresult & util::disasm_interface::SUPPORTED) == 0) + return; + + bool step_out = (m_flags & DEBUG_FLAG_STEPPING_OUT) != 0 && (dasmresult & util::disasm_interface::STEP_OUT) != 0; + bool test_cond = (dasmresult & util::disasm_interface::STEP_COND) != 0 && ((m_flags & (DEBUG_FLAG_STEPPING_BRANCH_TRUE | DEBUG_FLAG_STEPPING_BRANCH_FALSE)) != 0 || step_out); + if (test_cond && m_stop_condition && !m_stop_condition->is_empty()) + { + try + { + test_cond = m_stop_condition->execute(); + } + catch (expression_error &) + { + test_cond = false; + } + } // if flags are supported and it's a call-style opcode, set a temp breakpoint after that instruction - if ((dasmresult & util::disasm_interface::SUPPORTED) != 0 && (dasmresult & util::disasm_interface::STEP_OVER) != 0) + // (TODO: this completely fails for subroutines that consume inline operands or use alternate returns) + if ((dasmresult & util::disasm_interface::STEP_OVER) != 0 || test_cond) { int extraskip = (dasmresult & util::disasm_interface::OVERINSTMASK) >> util::disasm_interface::OVERINSTSHIFT; pc = buffer.next_pc_wrap(pc, dasmresult & util::disasm_interface::LENGTHMASK); + m_delay_steps = extraskip + 1; + if (m_stepsleft < m_delay_steps) + m_stepsleft = m_delay_steps; // if we need to skip additional instructions, advance as requested while (extraskip-- > 0) { @@ -2486,15 +1908,27 @@ void device_debug::prepare_for_step_overout(offs_t pc) pc = buffer.next_pc_wrap(pc, result & util::disasm_interface::LENGTHMASK); } m_stepaddr = pc; + if ((dasmresult & util::disasm_interface::STEP_OVER) != 0) + m_flags |= DEBUG_FLAG_CALL_IN_PROGRESS; + if (test_cond) + m_flags |= DEBUG_FLAG_TEST_IN_PROGRESS; } // if we're stepping out and this isn't a step out instruction, reset the steps until stop to a high number - if ((m_flags & DEBUG_FLAG_STEPPING_OUT) != 0) + if ((m_flags & (DEBUG_FLAG_STEPPING_OUT | DEBUG_FLAG_STEPPING_BRANCH_TRUE | DEBUG_FLAG_STEPPING_BRANCH_FALSE)) != 0) { - if ((dasmresult & util::disasm_interface::SUPPORTED) != 0 && (dasmresult & util::disasm_interface::STEP_OUT) == 0) + // make sure to also reset the number of steps for conditionals that may be single-instruction loops + if (test_cond || !step_out) m_stepsleft = 100; else - m_stepsleft = 1; + { + // add extra instructions for delay slots + int extraskip = (dasmresult & util::disasm_interface::OVERINSTMASK) >> util::disasm_interface::OVERINSTSHIFT; + m_stepsleft = extraskip + 1; + + // take the last few steps normally + m_flags = (m_flags | DEBUG_FLAG_STEPPING) & ~DEBUG_FLAG_STEPPING_OUT; + } } } @@ -2507,23 +1941,21 @@ void device_debug::prepare_for_step_overout(offs_t pc) void device_debug::breakpoint_update_flags() { // see if there are any enabled breakpoints - m_flags &= ~DEBUG_FLAG_LIVE_BP; - for (breakpoint &bp : m_bplist) - if (bp.m_enabled) + m_flags &= ~(DEBUG_FLAG_LIVE_BP | DEBUG_FLAG_LIVE_RP); + for (auto &bpp : m_bplist) + if (bpp.second->m_enabled) { m_flags |= DEBUG_FLAG_LIVE_BP; break; } - if ( ! ( m_flags & DEBUG_FLAG_LIVE_BP ) ) + // see if there are any enabled registerpoints + for (debug_registerpoint &rp : m_rplist) { - // see if there are any enabled registerpoints - for (registerpoint &rp : m_rplist) + if (rp.m_enabled) { - if (rp.m_enabled) - { - m_flags |= DEBUG_FLAG_LIVE_BP; - } + m_flags |= DEBUG_FLAG_LIVE_RP; + break; } } @@ -2540,12 +1972,15 @@ void device_debug::breakpoint_update_flags() void device_debug::breakpoint_check(offs_t pc) { - debugger_cpu& debugcpu = m_device.machine().debugger().cpu(); - // see if we match - for (breakpoint &bp : m_bplist) + auto bpitp = m_bplist.equal_range(pc); + for (auto bpit = bpitp.first; bpit != bpitp.second; ++bpit) + { + debug_breakpoint &bp = *bpit->second; if (bp.hit(pc)) { + debugger_cpu &debugcpu = m_device.machine().debugger().cpu(); + // halt in the debugger by default debugcpu.set_execution_stopped(); @@ -2561,12 +1996,24 @@ void device_debug::breakpoint_check(offs_t pc) } break; } + } +} + +//------------------------------------------------- +// registerpoint_check - check the registerpoints +// for a given device +//------------------------------------------------- + +void device_debug::registerpoint_check() +{ // see if we have any matching registerpoints - for (registerpoint &rp : m_rplist) + for (debug_registerpoint &rp : m_rplist) { if (rp.hit()) { + debugger_cpu &debugcpu = m_device.machine().debugger().cpu(); + // halt in the debugger by default debugcpu.set_execution_stopped(); @@ -2587,546 +2034,6 @@ void device_debug::breakpoint_check(offs_t pc) } -//------------------------------------------------- -// watchpoint_check - check the watchpoints -// for a given CPU and address space -//------------------------------------------------- - -//------------------------------------------------- -// hotspot_check - check for hotspots on a -// memory read access -//------------------------------------------------- - -void device_debug::hotspot_check(address_space &space, offs_t address) -{ - offs_t curpc = m_device.state().pcbase(); - - // see if we have a match in our list - unsigned int hotindex; - for (hotindex = 0; hotindex < m_hotspots.size(); hotindex++) - if (m_hotspots[hotindex].m_access == address && m_hotspots[hotindex].m_pc == curpc && m_hotspots[hotindex].m_space == &space) - break; - - // if we didn't find any, make a new entry - if (hotindex == m_hotspots.size()) - { - // if the bottom of the list is over the threshold, print it - hotspot_entry &spot = m_hotspots[m_hotspots.size() - 1]; - if (spot.m_count > m_hotspot_threshhold) - m_device.machine().debugger().console().printf("Hotspot @ %s %08X (PC=%08X) hit %d times (fell off bottom)\n", space.name(), spot.m_access, spot.m_pc, spot.m_count); - - // move everything else down and insert this one at the top - memmove(&m_hotspots[1], &m_hotspots[0], sizeof(m_hotspots[0]) * (m_hotspots.size() - 1)); - m_hotspots[0].m_access = address; - m_hotspots[0].m_pc = curpc; - m_hotspots[0].m_space = &space; - m_hotspots[0].m_count = 1; - } - - // if we did find one, increase the count and move it to the top - else - { - m_hotspots[hotindex].m_count++; - if (hotindex != 0) - { - hotspot_entry temp = m_hotspots[hotindex]; - memmove(&m_hotspots[1], &m_hotspots[0], sizeof(m_hotspots[0]) * hotindex); - m_hotspots[0] = temp; - } - } -} - -//------------------------------------------------- -// get_current_pc - getter callback for a device's -// current instruction pointer -//------------------------------------------------- - -u64 device_debug::get_current_pc(symbol_table &table) -{ - device_t *device = reinterpret_cast<device_t *>(table.globalref()); - return device->state().pcbase(); -} - - -//------------------------------------------------- -// get_cycles - getter callback for the -// 'cycles' symbol -//------------------------------------------------- - -u64 device_debug::get_cycles(symbol_table &table) -{ - device_t *device = reinterpret_cast<device_t *>(table.globalref()); - return device->debug()->m_exec->cycles_remaining(); -} - - -//------------------------------------------------- -// get_totalcycles - getter callback for the -// 'totalcycles' symbol -//------------------------------------------------- - -u64 device_debug::get_totalcycles(symbol_table &table) -{ - device_t *device = reinterpret_cast<device_t *>(table.globalref()); - return device->debug()->m_total_cycles; -} - - -//------------------------------------------------- -// get_lastinstructioncycles - getter callback for the -// 'lastinstructioncycles' symbol -//------------------------------------------------- - -u64 device_debug::get_lastinstructioncycles(symbol_table &table) -{ - device_t *device = reinterpret_cast<device_t *>(table.globalref()); - device_debug *debug = device->debug(); - return debug->m_total_cycles - debug->m_last_total_cycles; -} - - -//------------------------------------------------- -// get_state - getter callback for a device's -// state symbols -//------------------------------------------------- - -u64 device_debug::get_state(symbol_table &table, int index) -{ - device_t *device = reinterpret_cast<device_t *>(table.globalref()); - return device->debug()->m_state->state_int(index); -} - - -//------------------------------------------------- -// set_state - setter callback for a device's -// state symbols -//------------------------------------------------- - -void device_debug::set_state(symbol_table &table, int index, u64 value) -{ - device_t *device = reinterpret_cast<device_t *>(table.globalref()); - device->debug()->m_state->set_state_int(index, value); -} - - - -//************************************************************************** -// DEBUG BREAKPOINT -//************************************************************************** - -//------------------------------------------------- -// breakpoint - constructor -//------------------------------------------------- - -device_debug::breakpoint::breakpoint(device_debug* debugInterface, - symbol_table &symbols, - int index, - offs_t address, - const char *condition, - const char *action) - : m_debugInterface(debugInterface), - m_index(index), - m_enabled(true), - m_address(address), - m_condition(&symbols, (condition != nullptr) ? condition : "1"), - m_action((action != nullptr) ? action : "") -{ -} - - -//------------------------------------------------- -// hit - detect a hit -//------------------------------------------------- - -bool device_debug::breakpoint::hit(offs_t pc) -{ - // don't hit if disabled - if (!m_enabled) - return false; - - // must match our address - if (m_address != pc) - return false; - - // must satisfy the condition - if (!m_condition.is_empty()) - { - try - { - return (m_condition.execute() != 0); - } - catch (expression_error &) - { - return false; - } - } - - return true; -} - - - -//************************************************************************** -// DEBUG WATCHPOINT -//************************************************************************** - -//------------------------------------------------- -// watchpoint - constructor -//------------------------------------------------- - -device_debug::watchpoint::watchpoint(device_debug* debugInterface, - symbol_table &symbols, - int index, - address_space &space, - read_or_write type, - offs_t address, - offs_t length, - const char *condition, - const char *action) - : m_debugInterface(debugInterface), - m_phr(nullptr), - m_phw(nullptr), - m_space(space), - m_index(index), - m_enabled(true), - m_type(type), - m_address(address & space.addrmask()), - m_length(length), - m_condition(&symbols, (condition != nullptr) ? condition : "1"), - m_action((action != nullptr) ? action : ""), - m_installing(false) -{ - std::fill(std::begin(m_start_address), std::end(m_start_address), 0); - std::fill(std::begin(m_end_address), std::end(m_end_address), 0); - std::fill(std::begin(m_masks), std::end(m_masks), 0); - - int ashift = m_space.addr_shift(); - endianness_t endian = m_space.endianness(); - offs_t subamask = m_space.alignment() - 1; - offs_t unit_size = ashift <= 0 ? 8 << -ashift : 8 >> ashift; - offs_t start = m_address; - offs_t end = (m_address + m_length - 1) & space.addrmask(); - if (end < start) - end = space.addrmask(); - offs_t rstart = start & ~subamask; - offs_t rend = end | subamask; - u64 smask, mmask, emask; - smask = mmask = emask = make_bitmask<u64>(m_space.data_width()); - if (start != rstart) - { - if (endian == ENDIANNESS_LITTLE) - smask &= ~make_bitmask<u64>((start - rstart) * unit_size); - else - smask &= make_bitmask<u64>((rstart + subamask + 1 - start) * unit_size); - } - if (end != rend) - { - if (endian == ENDIANNESS_LITTLE) - emask &= make_bitmask<u64>((subamask + 1 + end - rend) * unit_size); - else - emask &= ~make_bitmask<u64>((rend - end) * unit_size); - } - - if (rend == (rstart | subamask) || smask == emask) - { - m_start_address[0] = rstart; - m_end_address[0] = rend; - m_masks[0] = smask & emask; - } - else - { - int idx = 0; - if (smask != mmask) - { - m_start_address[idx] = rstart; - m_end_address[idx] = rstart | subamask; - m_masks[idx] = smask; - idx++; - rstart += subamask + 1; - } - if (mmask == emask) - { - m_start_address[idx] = rstart; - m_end_address[idx] = rend; - m_masks[idx] = emask; - } - else - { - if (rstart < rend - subamask) - { - m_start_address[idx] = rstart; - m_end_address[idx] = rend - subamask - 1; - m_masks[idx] = mmask; - idx++; - } - m_start_address[idx] = rend - subamask; - m_end_address[idx] = rend; - m_masks[idx] = emask; - } - } - - install(read_or_write::READWRITE); - m_notifier = m_space.add_change_notifier([this](read_or_write mode) { - if (m_enabled) - { - install(mode); - } - }); -} - -device_debug::watchpoint::~watchpoint() -{ - m_space.remove_change_notifier(m_notifier); - if (m_phr) - m_phr->remove(); - if (m_phw) - m_phw->remove(); -} - -void device_debug::watchpoint::setEnabled(bool value) -{ - if (m_enabled != value) - { - m_enabled = value; - if (m_enabled) - install(read_or_write::READWRITE); - else - { - m_installing = true; - if(m_phr) - m_phr->remove(); - if(m_phw) - m_phw->remove(); - m_installing = false; - } - } -} - -void device_debug::watchpoint::install(read_or_write mode) -{ - if (m_installing) - return; - m_installing = true; - if ((u32(mode) & u32(read_or_write::READ)) && m_phr) - m_phr->remove(); - if ((u32(mode) & u32(read_or_write::WRITE)) && m_phw) - m_phw->remove(); - std::string name = util::string_format("wp@%x", m_address); - switch (m_space.data_width()) - { - case 8: - if (u32(m_type) & u32(mode) & u32(read_or_write::READ)) - m_phr = m_space.install_read_tap(m_start_address[0], m_end_address[0], name, - [this](offs_t offset, u8 &data, u8 mem_mask) { - triggered(read_or_write::READ, offset, data, mem_mask); - }, m_phr); - if (u32(m_type) & u32(mode) & u32(read_or_write::WRITE)) - m_phw = m_space.install_write_tap(m_start_address[0], m_end_address[0], name, - [this](offs_t offset, u8 &data, u8 mem_mask) { - triggered(read_or_write::WRITE, offset, data, mem_mask); - }, m_phw); - break; - - case 16: - for (int i=0; i != 3; i++) - if (m_masks[i]) - { - u16 mask = m_masks[i]; - if (u32(m_type) & u32(mode) & u32(read_or_write::READ)) - m_phr = m_space.install_read_tap(m_start_address[i], m_end_address[i], name, - [this, mask](offs_t offset, u16 &data, u16 mem_mask) { - if (mem_mask & mask) - triggered(read_or_write::READ, offset, data, mem_mask); - }, m_phr); - if (u32(m_type) & u32(mode) & u32(read_or_write::WRITE)) - m_phw = m_space.install_write_tap(m_start_address[i], m_end_address[i], name, - [this, mask](offs_t offset, u16 &data, u16 mem_mask) { - if (mem_mask & mask) - triggered(read_or_write::WRITE, offset, data, mem_mask); - }, m_phw); - } - break; - - case 32: - for (int i=0; i != 3; i++) - if (m_masks[i]) - { - u32 mask = m_masks[i]; - if (u32(m_type) & u32(mode) & u32(read_or_write::READ)) - m_phr = m_space.install_read_tap(m_start_address[i], m_end_address[i], name, - [this, mask](offs_t offset, u32 &data, u32 mem_mask) { - if (mem_mask & mask) - triggered(read_or_write::READ, offset, data, mem_mask); - }, m_phr); - if (u32(m_type) & u32(mode) & u32(read_or_write::WRITE)) - m_phw = m_space.install_write_tap(m_start_address[i], m_end_address[i], name, - [this, mask](offs_t offset, u32 &data, u32 mem_mask) { - if (mem_mask & mask) - triggered(read_or_write::WRITE, offset, data, mem_mask); - }, m_phw); - } - break; - - case 64: - for (int i=0; i != 3; i++) - if (m_masks[i]) - { - u64 mask = m_masks[i]; - if (u32(m_type) & u32(mode) & u32(read_or_write::READ)) - m_phr = m_space.install_read_tap(m_start_address[i], m_end_address[i], name, - [this, mask](offs_t offset, u64 &data, u64 mem_mask) { - if (mem_mask & mask) - triggered(read_or_write::READ, offset, data, mem_mask); - }, m_phr); - if (u32(m_type) & u32(mode) & u32(read_or_write::WRITE)) - m_phw = m_space.install_write_tap(m_start_address[i], m_end_address[i], name, - [this, mask](offs_t offset, u64 &data, u64 mem_mask) { - if (mem_mask & mask) - triggered(read_or_write::WRITE, offset, data, mem_mask); - }, m_phw); - } - break; - } - m_installing = false; -} - -void device_debug::watchpoint::triggered(read_or_write type, offs_t address, u64 data, u64 mem_mask) -{ - auto &machine = m_debugInterface->m_device.machine(); - auto &debug = machine.debugger(); - - // if we're within debugger code, don't trigger - if (debug.cpu().within_instruction_hook() || machine.side_effects_disabled()) - return; - - // adjust address, size & value_to_write based on mem_mask. - offs_t size = 0; - int ashift = m_space.addr_shift(); - offs_t unit_size = ashift <= 0 ? 8 << -ashift : 8 >> ashift; - u64 unit_mask = make_bitmask<u64>(unit_size); - - offs_t address_offset = 0; - - if(!mem_mask) - mem_mask = 0xff; - - while (!(mem_mask & unit_mask)) - { - address_offset++; - data >>= unit_size; - mem_mask >>= unit_size; - } - - while (mem_mask) - { - size++; - mem_mask >>= unit_size; - } - - data &= make_bitmask<u64>(size * unit_size); - - if (m_space.endianness() == ENDIANNESS_LITTLE) - address += address_offset; - else - address += m_space.alignment() - size - address_offset; - - // stash the value that will be written or has just been read - debug.cpu().set_wpinfo(address, data); - - // protect against recursion - debug.cpu().set_within_instruction(true); - - // must satisfy the condition - if (!m_condition.is_empty()) - { - try - { - if (!m_condition.execute()) - { - debug.cpu().set_within_instruction(false); - return; - } - } - catch (expression_error &) - { - debug.cpu().set_within_instruction(false); - return; - } - } - - // halt in the debugger by default - debug.cpu().set_execution_stopped(); - - // evaluate the action - if (!m_action.empty()) - debug.console().execute_command(m_action, false); - - // print a notification, unless the action made us go again - if (debug.cpu().is_stopped()) - { - offs_t pc = m_space.device().state().pcbase(); - std::string buffer; - - buffer = string_format(type == read_or_write::READ ? - "Stopped at watchpoint %X reading %0*X from %08X (PC=%X)" : - "Stopped at watchpoint %X writing %0*X to %08X (PC=%X)", - m_index, - size * unit_size / 4, - data, - address, - pc); - debug.console().printf("%s\n", buffer); - m_debugInterface->compute_debug_flags(); - m_debugInterface->set_triggered_watchpoint(this); - } - - debug.cpu().set_within_instruction(false); -} - -//************************************************************************** -// DEBUG REGISTERPOINT -//************************************************************************** - -//------------------------------------------------- -// registerpoint - constructor -//------------------------------------------------- - -device_debug::registerpoint::registerpoint(symbol_table &symbols, int index, const char *condition, const char *action) - : m_index(index), - m_enabled(true), - m_condition(&symbols, (condition != nullptr) ? condition : "1"), - m_action((action != nullptr) ? action : "") -{ -} - - -//------------------------------------------------- -// hit - detect a hit -//------------------------------------------------- - -bool device_debug::registerpoint::hit() -{ - // don't hit if disabled - if (!m_enabled) - return false; - - // must satisfy the condition - if (!m_condition.is_empty()) - { - try - { - return (m_condition.execute() != 0); - } - catch (expression_error &) - { - return false; - } - } - - return true; -} - - //************************************************************************** // TRACER @@ -3136,10 +2043,10 @@ bool device_debug::registerpoint::hit() // tracer - constructor //------------------------------------------------- -device_debug::tracer::tracer(device_debug &debug, FILE &file, bool trace_over, bool detect_loops, bool logerror, const char *action) +device_debug::tracer::tracer(device_debug &debug, std::unique_ptr<std::ostream> &&file, bool trace_over, bool detect_loops, bool logerror, std::string_view action) : m_debug(debug) - , m_file(file) - , m_action((action != nullptr) ? action : "") + , m_file(std::move(file)) + , m_action(action) , m_detect_loops(detect_loops) , m_logerror(logerror) , m_loops(0) @@ -3158,7 +2065,7 @@ device_debug::tracer::tracer(device_debug &debug, FILE &file, bool trace_over, b device_debug::tracer::~tracer() { // make sure we close the file if we can - fclose(&m_file); + m_file.reset(); } @@ -3194,7 +2101,7 @@ void device_debug::tracer::update(offs_t pc) // if we just finished looping, indicate as much if (m_loops != 0) - fprintf(&m_file, "\n (loops for %d instructions)\n\n", m_loops); + util::stream_format(*m_file, "\n (loops for %d instructions)\n\n", m_loops); m_loops = 0; } @@ -3209,7 +2116,7 @@ void device_debug::tracer::update(offs_t pc) buffer.disassemble(pc, instruction, next_pc, size, dasmresult); // output the result - fprintf(&m_file, "%s: %s\n", buffer.pc_to_string(pc).c_str(), instruction.c_str()); + util::stream_format(*m_file, "%s: %s\n", buffer.pc_to_string(pc), instruction); // do we need to step the trace over this instruction? if (m_trace_over && (dasmresult & util::disasm_interface::SUPPORTED) != 0 && (dasmresult & util::disasm_interface::STEP_OVER) != 0) @@ -3227,7 +2134,33 @@ void device_debug::tracer::update(offs_t pc) // log this PC m_nextdex = (m_nextdex + 1) % TRACE_LOOPS; m_history[m_nextdex] = pc; - fflush(&m_file); + m_file->flush(); +} + + +//------------------------------------------------- +// interrupt_update - log interrupt to tracefile +//------------------------------------------------- + +void device_debug::tracer::interrupt_update(int irqline, offs_t pc) +{ + if (m_trace_over) + { + if (m_trace_over_target != ~0) + return; + m_trace_over_target = pc; + } + + // if we just finished looping, indicate as much + *m_file << "\n"; + if (m_detect_loops && m_loops != 0) + { + util::stream_format(*m_file, " (loops for %d instructions)\n", m_loops); + m_loops = 0; + } + + util::stream_format(*m_file, " (interrupted at %s, IRQ %d)\n\n", debug_disasm_buffer(m_debug.device()).pc_to_string(pc), irqline); + m_file->flush(); } @@ -3235,11 +2168,11 @@ void device_debug::tracer::update(offs_t pc) // vprintf - generic print to the trace file //------------------------------------------------- -void device_debug::tracer::vprintf(const char *format, va_list va) +void device_debug::tracer::vprintf(util::format_argument_pack<char> const &args) { // pass through to the file - vfprintf(&m_file, format, va); - fflush(&m_file); + util::stream_format(*m_file, args); + m_file->flush(); } @@ -3250,7 +2183,7 @@ void device_debug::tracer::vprintf(const char *format, va_list va) void device_debug::tracer::flush() { - fflush(&m_file); + m_file->flush(); } diff --git a/src/emu/debug/debugcpu.h b/src/emu/debug/debugcpu.h index 26653eb2baa..631d4c788d9 100644 --- a/src/emu/debug/debugcpu.h +++ b/src/emu/debug/debugcpu.h @@ -13,9 +13,8 @@ #pragma once -#include "express.h" - #include <set> +#include <utility> //************************************************************************** @@ -30,9 +29,6 @@ constexpr int COMMENT_VERSION = 1; // TYPE DEFINITIONS //************************************************************************** -typedef int (*debug_instruction_hook_func)(device_t &device, offs_t curpc); - - // ======================> device_debug // [TODO] This whole thing is terrible. @@ -40,133 +36,12 @@ typedef int (*debug_instruction_hook_func)(device_t &device, offs_t curpc); class device_debug { public: - // breakpoint class - class breakpoint - { - friend class device_debug; - - public: - // construction/destruction - breakpoint( - device_debug* debugInterface, - symbol_table &symbols, - int index, - offs_t address, - const char *condition = nullptr, - const char *action = nullptr); - - // getters - const device_debug *debugInterface() const { return m_debugInterface; } - int index() const { return m_index; } - bool enabled() const { return m_enabled; } - offs_t address() const { return m_address; } - const char *condition() const { return m_condition.original_string(); } - const char *action() const { return m_action.c_str(); } - - // setters - void setEnabled(bool value) { m_enabled = value; } - - private: - // internals - bool hit(offs_t pc); - const device_debug * m_debugInterface; // the interface we were created from - int m_index; // user reported index - bool m_enabled; // enabled? - offs_t m_address; // execution address - parsed_expression m_condition; // condition - std::string m_action; // action - }; - - // watchpoint class - class watchpoint - { - friend class device_debug; - - public: - // construction/destruction - watchpoint( - device_debug* debugInterface, - symbol_table &symbols, - int index, - address_space &space, - read_or_write type, - offs_t address, - offs_t length, - const char *condition = nullptr, - const char *action = nullptr); - ~watchpoint(); - - // getters - const device_debug *debugInterface() const { return m_debugInterface; } - address_space &space() const { return m_space; } - int index() const { return m_index; } - read_or_write type() const { return m_type; } - bool enabled() const { return m_enabled; } - offs_t address() const { return m_address; } - offs_t length() const { return m_length; } - const char *condition() const { return m_condition.original_string(); } - const std::string &action() const { return m_action; } - - // setters - void setEnabled(bool value); - - // internals - bool hit(int type, offs_t address, int size); - - private: - device_debug * m_debugInterface; // the interface we were created from - memory_passthrough_handler *m_phr; // passthrough handler reference, read access - memory_passthrough_handler *m_phw; // passthrough handler reference, write access - address_space & m_space; // address space - int m_index; // user reported index - bool m_enabled; // enabled? - read_or_write m_type; // type (read/write) - offs_t m_address; // start address - offs_t m_length; // length of watch area - parsed_expression m_condition; // condition - std::string m_action; // action - int m_notifier; // address map change notifier id - - offs_t m_start_address[3]; // the start addresses of the checks to install - offs_t m_end_address[3]; // the end addresses - u64 m_masks[3]; // the access masks - bool m_installing; // prevent recursive multiple installs - void install(read_or_write mode); - void triggered(read_or_write type, offs_t address, u64 data, u64 mem_mask); - }; - - // registerpoint class - class registerpoint - { - friend class device_debug; - - public: - // construction/destruction - registerpoint(symbol_table &symbols, int index, const char *condition, const char *action = nullptr); - - // getters - int index() const { return m_index; } - bool enabled() const { return m_enabled; } - const char *condition() const { return m_condition.original_string(); } - const char *action() const { return m_action.c_str(); } - - private: - // internals - bool hit(); - - int m_index; // user reported index - bool m_enabled; // enabled? - parsed_expression m_condition; // condition - std::string m_action; // action - }; - -public: // construction/destruction device_debug(device_t &device); ~device_debug(); // getters - symbol_table &symtable() { return m_symtable; } + symbol_table &symtable() { return *m_symtable; } // commonly-used pass-throughs int logaddrchars() const { return (m_memory != nullptr && m_memory->has_space(AS_PROGRAM)) ? m_memory->space(AS_PROGRAM).logaddrchars() : 8; } @@ -176,13 +51,11 @@ public: // hooks used by the rest of the system void start_hook(const attotime &endtime); void stop_hook(); - void interrupt_hook(int irqline); + void interrupt_hook(int irqline, offs_t pc); void exception_hook(int exception); void privilege_hook(); void instruction_hook(offs_t curpc); - - // hooks into our operations - void set_instruction_hook(debug_instruction_hook_func hook); + void wait_hook(); // debugger focus void ignore(bool ignore = true); @@ -201,9 +74,10 @@ public: void go(offs_t targetpc = ~0); void go_vblank(); void go_interrupt(int irqline = -1); - void go_exception(int exception); + void go_exception(int exception, const char *condition); void go_milliseconds(u64 milliseconds); void go_privilege(const char *condition); + void go_branch(bool sense, const char *condition); void go_next_device(); template <typename Format, typename... Params> @@ -213,37 +87,41 @@ public: } // breakpoints - const std::forward_list<breakpoint> &breakpoint_list() const { return m_bplist; } - const breakpoint *breakpoint_find(offs_t address) const; - int breakpoint_set(offs_t address, const char *condition = nullptr, const char *action = nullptr); + const auto &breakpoint_list() const { return m_bplist; } + const debug_breakpoint *breakpoint_find(offs_t address) const; + int breakpoint_set(offs_t address, const char *condition = nullptr, std::string_view action = {}); bool breakpoint_clear(int index); void breakpoint_clear_all(); bool breakpoint_enable(int index, bool enable = true); void breakpoint_enable_all(bool enable = true); - breakpoint *triggered_breakpoint(void) { breakpoint *ret = m_triggered_breakpoint; m_triggered_breakpoint = nullptr; return ret; } + debug_breakpoint *triggered_breakpoint() { debug_breakpoint *ret = m_triggered_breakpoint; m_triggered_breakpoint = nullptr; return ret; } // watchpoints int watchpoint_space_count() const { return m_wplist.size(); } - const std::vector<std::unique_ptr<watchpoint>> &watchpoint_vector(int spacenum) const { return m_wplist[spacenum]; } - int watchpoint_set(address_space &space, read_or_write type, offs_t address, offs_t length, const char *condition, const char *action); + const std::vector<std::unique_ptr<debug_watchpoint>> &watchpoint_vector(int spacenum) const { return m_wplist[spacenum]; } + int watchpoint_set(address_space &space, read_or_write type, offs_t address, offs_t length, const char *condition = nullptr, std::string_view action = {}); bool watchpoint_clear(int wpnum); void watchpoint_clear_all(); bool watchpoint_enable(int index, bool enable = true); void watchpoint_enable_all(bool enable = true); - void set_triggered_watchpoint(watchpoint *wp) { m_triggered_watchpoint = wp; } - watchpoint *triggered_watchpoint(void) { watchpoint *ret = m_triggered_watchpoint; m_triggered_watchpoint = nullptr; return ret; } + void set_triggered_watchpoint(debug_watchpoint *wp) { m_triggered_watchpoint = wp; } + debug_watchpoint *triggered_watchpoint() { debug_watchpoint *ret = m_triggered_watchpoint; m_triggered_watchpoint = nullptr; return ret; } // registerpoints - const std::forward_list<registerpoint> ®isterpoint_list() const { return m_rplist; } - int registerpoint_set(const char *condition, const char *action = nullptr); + const std::forward_list<debug_registerpoint> ®isterpoint_list() const { return m_rplist; } + int registerpoint_set(const char *condition, std::string_view action = {}); bool registerpoint_clear(int index); void registerpoint_clear_all(); bool registerpoint_enable(int index, bool enable = true); - void registerpoint_enable_all(bool enable = true ); + void registerpoint_enable_all(bool enable = true); - // hotspots - bool hotspot_tracking_enabled() const { return !m_hotspots.empty(); } - void hotspot_track(int numspots, int threshhold); + // exception points + const auto &exceptionpoint_list() const { return m_eplist; } + int exceptionpoint_set(int exception, const char *condition = nullptr, std::string_view action = {}); + bool exceptionpoint_clear(int index); + void exceptionpoint_clear_all(); + bool exceptionpoint_enable(int index, bool enable = true); + void exceptionpoint_enable_all(bool enable = true); // comments void comment_add(offs_t address, const char *comment, rgb_t color); @@ -256,24 +134,28 @@ public: u32 compute_opcode_crc32(offs_t pc) const; // history - offs_t history_pc(int index) const; + std::pair<offs_t, bool> history_pc(int index) const; // pc tracking - void set_track_pc(bool value) { m_track_pc = value; } - bool track_pc_visited(const offs_t& pc) const; - void set_track_pc_visited(const offs_t& pc); + void set_track_pc(bool value); + bool track_pc_visited(offs_t pc) const; + void set_track_pc_visited(offs_t pc); void track_pc_data_clear() { m_track_pc_set.clear(); } // memory tracking - void set_track_mem(bool value) { m_track_mem = value; } + void set_track_mem(bool value); offs_t track_mem_pc_from_space_address_data(const int& space, const offs_t& address, const u64& data) const; void track_mem_data_clear() { m_track_mem_set.clear(); } // tracing - void trace(FILE *file, bool trace_over, bool detect_loops, bool logerror, const char *action); - void trace_printf(const char *fmt, ...) ATTR_PRINTF(2,3); + void trace(std::unique_ptr<std::ostream> &&file, bool trace_over, bool detect_loops, bool logerror, std::string_view action); + template <typename Format, typename... Params> void trace_printf(Format &&fmt, Params &&...args) + { + if (m_trace != nullptr) + m_trace->vprintf(util::make_format_argument_pack(std::forward<Format>(fmt), std::forward<Params>(args)...)); + } void trace_flush() { if (m_trace != nullptr) m_trace->flush(); } void reset_transient_flag() { m_flags &= ~DEBUG_FLAG_TRANSIENT; } @@ -284,7 +166,7 @@ public: void compute_debug_flags(); private: - void halt_on_next_instruction_impl(util::format_argument_pack<std::ostream> &&args); + void halt_on_next_instruction_impl(util::format_argument_pack<char> &&args); // internal helpers void prepare_for_step_overout(offs_t pc); @@ -293,19 +175,11 @@ private: // breakpoint and watchpoint helpers void breakpoint_update_flags(); void breakpoint_check(offs_t pc); - void hotspot_check(address_space &space, offs_t address); + void registerpoint_check(); void reinstall_all(read_or_write mode); void reinstall(address_space &space, read_or_write mode); void write_tracking(address_space &space, offs_t address, u64 data); - // symbol get/set callbacks - static u64 get_current_pc(symbol_table &table); - static u64 get_cycles(symbol_table &table); - static u64 get_totalcycles(symbol_table &table); - static u64 get_lastinstructioncycles(symbol_table &table); - static u64 get_state(symbol_table &table, int index); - static void set_state(symbol_table &table, int index, u64 value); - // basic device information device_t & m_device; // device we are attached to device_execute_interface * m_exec; // execute interface, if present @@ -315,44 +189,49 @@ private: // global state u32 m_flags; // debugging flags for this CPU - symbol_table m_symtable; // symbol table for expression evaluation - debug_instruction_hook_func m_instrhook; // per-instruction callback hook + std::unique_ptr<symbol_table> m_symtable; // symbol table for expression evaluation // stepping information - offs_t m_stepaddr; // step target address for DEBUG_FLAG_STEPPING_OVER + offs_t m_stepaddr; // step target address for DEBUG_FLAG_STEPPING_OVER or DEBUG_FLAG_STEPPING_BRANCH int m_stepsleft; // number of steps left until done + int m_delay_steps; // number of steps until target address check // execution information offs_t m_stopaddr; // stop address for DEBUG_FLAG_STOP_PC attotime m_stoptime; // stop time for DEBUG_FLAG_STOP_TIME int m_stopirq; // stop IRQ number for DEBUG_FLAG_STOP_INTERRUPT int m_stopexception; // stop exception number for DEBUG_FLAG_STOP_EXCEPTION - std::unique_ptr<parsed_expression> m_privilege_condition; // expression to evaluate on privilege change + std::unique_ptr<parsed_expression> m_stop_condition; // expression to evaluate on privilege change + std::unique_ptr<parsed_expression> m_exception_condition; // expression to evaluate on exception hit attotime m_endexectime; // ending time of the current execution u64 m_total_cycles; // current total cycles u64 m_last_total_cycles; // last total cycles + bool m_was_waiting; // true if no instruction executed since last wait // history offs_t m_pc_history[HISTORY_SIZE]; // history of recent PCs u32 m_pc_history_index; // current history index + u32 m_pc_history_valid; // number of valid PC history entries // breakpoints and watchpoints - std::forward_list<breakpoint> m_bplist; // list of breakpoints - std::vector<std::vector<std::unique_ptr<watchpoint>>> m_wplist; // watchpoint lists for each address space - std::forward_list<registerpoint> m_rplist; // list of registerpoints + std::multimap<offs_t, std::unique_ptr<debug_breakpoint>> m_bplist; // list of breakpoints + std::vector<std::vector<std::unique_ptr<debug_watchpoint>>> m_wplist; // watchpoint lists for each address space + std::forward_list<debug_registerpoint> m_rplist; // list of registerpoints + std::multimap<offs_t, std::unique_ptr<debug_exceptionpoint>> m_eplist; // list of exception points - breakpoint * m_triggered_breakpoint; // latest breakpoint that was triggered - watchpoint * m_triggered_watchpoint; // latest watchpoint that was triggered + debug_breakpoint * m_triggered_breakpoint; // latest breakpoint that was triggered + debug_watchpoint * m_triggered_watchpoint; // latest watchpoint that was triggered // tracing class tracer { public: - tracer(device_debug &debug, FILE &file, bool trace_over, bool detect_loops, bool logerror, const char *action); + tracer(device_debug &debug, std::unique_ptr<std::ostream> &&file, bool trace_over, bool detect_loops, bool logerror, std::string_view action); ~tracer(); void update(offs_t pc); - void vprintf(const char *format, va_list va); + void interrupt_update(int irqline, offs_t pc); + void vprintf(util::format_argument_pack<char> const &args); void flush(); bool logerror() const { return m_logerror; } @@ -360,7 +239,7 @@ private: static const int TRACE_LOOPS = 64; device_debug & m_debug; // reference to our owner - FILE & m_file; // tracing file for this CPU + std::unique_ptr<std::ostream> m_file; // tracing file for this CPU std::string m_action; // action to perform during a trace offs_t m_history[TRACE_LOOPS]; // history of recent PCs bool m_detect_loops; // whether or not we should detect loops @@ -372,22 +251,10 @@ private: // (0 = not tracing over, // ~0 = not currently tracing over) }; - std::unique_ptr<tracer> m_trace; // tracer state + std::unique_ptr<tracer> m_trace; // tracer state - // hotspots - struct hotspot_entry - { - offs_t m_access; // access address - offs_t m_pc; // PC of the access - address_space * m_space; // space where the access occurred - u32 m_count; // number of hits - }; - std::vector<hotspot_entry> m_hotspots; // hotspot list - int m_hotspot_threshhold; // threshhold for the number of hits to print - - std::vector<memory_passthrough_handler *> m_phr; // passthrough handler reference for each space, read mode - std::vector<memory_passthrough_handler *> m_phw; // passthrough handler reference for each space, write mode - std::vector<int> m_notifiers; // notifiers for each space + std::vector<memory_passthrough_handler> m_phw; // passthrough handler reference for each space, write mode + std::vector<util::notifier_subscription> m_notifiers; // notifiers for each space // pc tracking class dasm_pc_tag @@ -455,7 +322,6 @@ private: static constexpr u32 DEBUG_FLAG_HISTORY = 0x00000002; // tracking this CPU's history static constexpr u32 DEBUG_FLAG_TRACING = 0x00000004; // tracing this CPU static constexpr u32 DEBUG_FLAG_TRACING_OVER = 0x00000008; // tracing this CPU with step over behavior - static constexpr u32 DEBUG_FLAG_HOOKED = 0x00000010; // per-instruction callback hook static constexpr u32 DEBUG_FLAG_STEPPING = 0x00000020; // CPU is single stepping static constexpr u32 DEBUG_FLAG_STEPPING_OVER = 0x00000040; // CPU is stepping over a function static constexpr u32 DEBUG_FLAG_STEPPING_OUT = 0x00000080; // CPU is stepping out of a function @@ -465,14 +331,20 @@ private: static constexpr u32 DEBUG_FLAG_STOP_VBLANK = 0x00001000; // there is a pending stop on the next VBLANK static constexpr u32 DEBUG_FLAG_STOP_TIME = 0x00002000; // there is a pending stop at cpu->stoptime static constexpr u32 DEBUG_FLAG_SUSPENDED = 0x00004000; // CPU currently suspended - static constexpr u32 DEBUG_FLAG_LIVE_BP = 0x00010000; // there are live breakpoints for this CPU + static constexpr u32 DEBUG_FLAG_LIVE_BP = 0x00008000; // there are live breakpoints for this CPU + static constexpr u32 DEBUG_FLAG_LIVE_RP = 0x00010000; // there are live registerpoints for this CPU static constexpr u32 DEBUG_FLAG_STOP_PRIVILEGE = 0x00020000; // run until execution level changes + static constexpr u32 DEBUG_FLAG_STEPPING_BRANCH_TRUE = 0x0040000; // run until true branch + static constexpr u32 DEBUG_FLAG_STEPPING_BRANCH_FALSE = 0x0080000; // run until false branch + static constexpr u32 DEBUG_FLAG_CALL_IN_PROGRESS = 0x01000000; // CPU is in the middle of a subroutine call + static constexpr u32 DEBUG_FLAG_TEST_IN_PROGRESS = 0x02000000; // CPU is performing a conditional test and branch - static constexpr u32 DEBUG_FLAG_STEPPING_ANY = DEBUG_FLAG_STEPPING | DEBUG_FLAG_STEPPING_OVER | DEBUG_FLAG_STEPPING_OUT; + static constexpr u32 DEBUG_FLAG_STEPPING_BRANCH = DEBUG_FLAG_STEPPING_BRANCH_TRUE | DEBUG_FLAG_STEPPING_BRANCH_FALSE; + static constexpr u32 DEBUG_FLAG_STEPPING_ANY = DEBUG_FLAG_STEPPING | DEBUG_FLAG_STEPPING_OVER | DEBUG_FLAG_STEPPING_OUT | DEBUG_FLAG_STEPPING_BRANCH; static constexpr u32 DEBUG_FLAG_TRACING_ANY = DEBUG_FLAG_TRACING | DEBUG_FLAG_TRACING_OVER; static constexpr u32 DEBUG_FLAG_TRANSIENT = DEBUG_FLAG_STEPPING_ANY | DEBUG_FLAG_STOP_PC | DEBUG_FLAG_STOP_INTERRUPT | DEBUG_FLAG_STOP_EXCEPTION | DEBUG_FLAG_STOP_VBLANK | - DEBUG_FLAG_STOP_TIME | DEBUG_FLAG_STOP_PRIVILEGE; + DEBUG_FLAG_STOP_TIME | DEBUG_FLAG_STOP_PRIVILEGE | DEBUG_FLAG_CALL_IN_PROGRESS | DEBUG_FLAG_TEST_IN_PROGRESS; }; //************************************************************************** @@ -491,14 +363,9 @@ public: /* flushes all traces; this is useful if a trace is going on when we fatalerror */ void flush_traces(); - void configure_memory(symbol_table &table); - /* ----- debugging status & information ----- */ - /* return the visible CPU device (the one that commands should apply to) */ - device_t *get_visible_cpu() { return m_visiblecpu; } - /* return true if the current execution state is stopped */ bool is_stopped() const { return m_execution_state == exec_state::STOPPED; } bool is_running() const { return m_execution_state == exec_state::RUNNING; } @@ -507,10 +374,7 @@ public: /* ----- symbol table interfaces ----- */ /* return the global symbol table */ - symbol_table *get_global_symtable() { return m_symtable.get(); } - - /* return the locally-visible symbol table */ - symbol_table *get_visible_symtable(); + symbol_table &global_symtable() { return *m_symtable; } /* ----- debugger comment helpers ----- */ @@ -522,41 +386,6 @@ public: bool comment_load(bool is_inline); - /* ----- debugger memory accessors ----- */ - - /* return a byte from the specified memory space */ - u8 read_byte(address_space &space, offs_t address, bool apply_translation); - - /* return a word from the specified memory space */ - u16 read_word(address_space &space, offs_t address, bool apply_translation); - - /* return a dword from the specified memory space */ - u32 read_dword(address_space &space, offs_t address, bool apply_translation); - - /* return a qword from the specified memory space */ - u64 read_qword(address_space &space, offs_t address, bool apply_translation); - - /* return 1,2,4 or 8 bytes from the specified memory space */ - u64 read_memory(address_space &space, offs_t address, int size, bool apply_translation); - - /* write a byte to the specified memory space */ - void write_byte(address_space &space, offs_t address, u8 data, bool apply_translation); - - /* write a word to the specified memory space */ - void write_word(address_space &space, offs_t address, u16 data, bool apply_translation); - - /* write a dword to the specified memory space */ - void write_dword(address_space &space, offs_t address, u32 data, bool apply_translation); - - /* write a qword to the specified memory space */ - void write_qword(address_space &space, offs_t address, u64 data, bool apply_translation); - - /* write 1,2,4 or 8 bytes to the specified memory space */ - void write_memory(address_space &space, offs_t address, u64 data, int size, bool apply_translation); - - /* read 1,2,4 or 8 bytes at the given offset from opcode space */ - u64 read_opcode(address_space &space, offs_t offset, int size); - // getters bool within_instruction_hook() const { return m_within_instruction_hook; } bool memory_modified() const { return m_memory_modified; } @@ -565,15 +394,15 @@ public: u32 get_breakpoint_index() { return m_bpindex++; } u32 get_watchpoint_index() { return m_wpindex++; } u32 get_registerpoint_index() { return m_rpindex++; } + u32 get_exceptionpoint_index() { return m_epindex++; } // setters - void set_visible_cpu(device_t * visiblecpu) { m_visiblecpu = visiblecpu; } void set_break_cpu(device_t * breakcpu) { m_breakcpu = breakcpu; } void set_within_instruction(bool within_instruction) { m_within_instruction_hook = within_instruction; } void set_memory_modified(bool memory_modified) { m_memory_modified = memory_modified; } void set_execution_stopped() { m_execution_state = exec_state::STOPPED; } void set_execution_running() { m_execution_state = exec_state::RUNNING; } - void set_wpinfo(offs_t address, u64 data) { m_wpaddr = address; m_wpdata = data; } + void set_wpinfo(offs_t address, u64 data, offs_t size) { m_wpaddr = address; m_wpdata = data; m_wpsize = size; } // device_debug helpers // [TODO] [RH]: Look into this more later, can possibly merge these two classes @@ -581,36 +410,20 @@ public: void stop_hook(device_t *device); void go_next_device(device_t *device); void go_vblank(); - void halt_on_next_instruction(device_t *device, util::format_argument_pack<std::ostream> &&args); + void halt_on_next_instruction(device_t *device, util::format_argument_pack<char> &&args); void ensure_comments_loaded(); void reset_transient_flags(); + void wait_for_debugger(device_t &device); private: static const size_t NUM_TEMP_VARIABLES; - /* expression handlers */ - u64 expression_read_memory(void *param, const char *name, expression_space space, u32 address, int size, bool disable_se); - u64 expression_read_program_direct(address_space &space, int opcode, offs_t address, int size); - u64 expression_read_memory_region(const char *rgntag, offs_t address, int size); - void expression_write_memory(void *param, const char *name, expression_space space, u32 address, int size, u64 data, bool disable_se); - void expression_write_program_direct(address_space &space, int opcode, offs_t address, int size, u64 data); - void expression_write_memory_region(const char *rgntag, offs_t address, int size, u64 data); - expression_error::error_code expression_validate(void *param, const char *name, expression_space space); - device_t* expression_get_device(const char *tag); - - /* variable getters/setters */ - u64 get_cpunum(symbol_table &table); - u64 get_beamx(symbol_table &table, screen_device *screen); - u64 get_beamy(symbol_table &table, screen_device *screen); - u64 get_frame(symbol_table &table, screen_device *screen); - - /* internal helpers */ + // internal helpers void on_vblank(screen_device &device, bool vblank_state); running_machine& m_machine; device_t * m_livecpu; - device_t * m_visiblecpu; device_t * m_breakcpu; std::unique_ptr<symbol_table> m_symtable; // global symbol table @@ -625,9 +438,11 @@ private: u32 m_bpindex; u32 m_wpindex; u32 m_rpindex; + u32 m_epindex; u64 m_wpdata; u64 m_wpaddr; + u64 m_wpsize; std::unique_ptr<u64[]> m_tempvar; osd_ticks_t m_last_periodic_update_time; diff --git a/src/emu/debug/debughlp.cpp b/src/emu/debug/debughlp.cpp index d737c0376aa..8e5fa1b9c64 100644 --- a/src/emu/debug/debughlp.cpp +++ b/src/emu/debug/debughlp.cpp @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles /********************************************************************* - debughlp.c + debughlp.cpp Debugger help engine. @@ -10,48 +10,28 @@ #include "emu.h" #include "debughlp.h" -#include <ctype.h> +#include "corestr.h" +#include <cstdio> +#include <iterator> +#include <map> -/*************************************************************************** - CONSTANTS -***************************************************************************/ - -#define CONSOLE_HISTORY (10000) -#define CONSOLE_LINE_CHARS (100) +namespace { /*************************************************************************** - TYPE DEFINITIONS + TABLE OF HELP ***************************************************************************/ struct help_item { - const char * tag; - const char * help; + char const *tag; + char const *help; }; - - -/*************************************************************************** - LOCAL VARIABLES -***************************************************************************/ - - - -/*************************************************************************** - FUNCTION PROTOTYPES -***************************************************************************/ - - - -/*************************************************************************** - TABLE OF HELP -***************************************************************************/ - -static const help_item static_help_list[] = +const help_item f_static_help_list[] = { { "", @@ -66,6 +46,7 @@ static const help_item static_help_list[] = " Breakpoints\n" " Watchpoints\n" " Registerpoints\n" + " Exceptionpoints\n" " Expressions\n" " Comments\n" " Cheats\n" @@ -88,18 +69,21 @@ static const help_item static_help_list[] = " logerror <format>[,<item>[,...]] -- outputs one or more <item>s to the error.log\n" " tracelog <format>[,<item>[,...]] -- outputs one or more <item>s to the trace file using <format>\n" " tracesym <item>[,...]] -- outputs one or more <item>s to the trace file\n" - " history [<CPU>,<length>] -- outputs a brief history of visited opcodes\n" - " trackpc [<bool>,<CPU>,<bool>] -- visually track visited opcodes [boolean to turn on and off, for the given CPU, clear]\n" - " trackmem [<bool>,<bool>] -- record which PC writes to each memory address [boolean to turn on and off, clear]\n" - " pcatmemp <address>[,<CPU>] -- query which PC wrote to a given program memory address for the current CPU\n" - " pcatmemd <address>[,<CPU>] -- query which PC wrote to a given data memory address for the current CPU\n" - " pcatmemi <address>[,<CPU>] -- query which PC wrote to a given I/O memory address for the current CPU\n" - " (Note: you can also query this info by right clicking in a memory window\n" + " history [<CPU>,[<length>]] -- outputs a brief history of visited opcodes\n" + " trackpc [<bool>,[<CPU>,[<bool>]]] -- visually track visited opcodes [boolean to turn on and off, for CPU, clear]\n" + " trackmem [<bool>,[<CPU>,[<bool>]]] -- record which PC writes to each memory address [boolean to turn on and off, for CPU, clear]\n" + " pcatmem <address>[:<space>] -- query which PC wrote to a given memory address\n" + " pcatmemd <address>[:<space>] -- query which PC wrote to a given data memory address\n" + " pcatmemi <address>[:<space>] -- query which PC wrote to a given I/O memory address\n" + " pcatmemo <address>[:<space>] -- query which PC wrote to a given opcode memory address\n" + " (Note: you can also query this info by right-clicking in a memory window)\n" " rewind[rw] -- go back in time by loading the most recent rewind state" " statesave[ss] <filename> -- save a state file for the current driver\n" " stateload[sl] <filename> -- load a state file for the current driver\n" " snap [<filename>] -- save a screen snapshot.\n" " source <filename> -- reads commands from <filename> and executes them one by one\n" + " time -- prints current machine time to the console\n" + " cls -- clears the console text buffer\n" " quit -- exits MAME and the debugger\n" }, { @@ -109,23 +93,36 @@ static const help_item static_help_list[] = "Type help <command> for further details on each command\n" "\n" " dasm <filename>,<address>,<length>[,<opcodes>[,<CPU>]] -- disassemble to the given file\n" - " f[ind] <address>,<length>[,<data>[,...]] -- search program memory for data\n" + " f[ind] <address>,<length>[,<data>[,...]] -- search memory for data\n" " f[ind]d <address>,<length>[,<data>[,...]] -- search data memory for data\n" " f[ind]i <address>,<length>[,<data>[,...]] -- search I/O memory for data\n" - " dump <filename>,<address>,<length>[,<size>[,<ascii>[,<CPU>]]] -- dump program memory as text\n" - " dumpd <filename>,<address>,<length>[,<size>[,<ascii>[,<CPU>]]] -- dump data memory as text\n" - " dumpi <filename>,<address>,<length>[,<size>[,<ascii>[,<CPU>]]] -- dump I/O memory as text\n" - " dumpo <filename>,<address>,<length>[,<size>[,<ascii>[,<CPU>]]] -- dump opcodes memory as text\n" - " save <filename>,<address>,<length>[,<CPU>] -- save binary program memory to the given file\n" - " saved <filename>,<address>,<length>[,<CPU>] -- save binary data memory to the given file\n" - " savei <filename>,<address>,<length>[,<CPU>] -- save binary I/O memory to the given file\n" - " load <filename>,<address>[,<length>,<CPU>] -- load binary program memory from the given file\n" - " loadd <filename>,<address>[,<length>,<CPU>] -- load binary data memory from the given file\n" - " loadi <filename>,<address>[,<length>,<CPU>] -- load binary I/O memory from the given file\n" - " map <address> -- map logical program address to physical address and bank\n" - " mapd <address> -- map logical data address to physical address and bank\n" - " mapi <address> -- map logical I/O address to physical address and bank\n" - " memdump [<filename>] -- dump the current memory map to <filename>\n" + " fill <address>,<length>[,<data>[,...]] -- fill memory with data\n" + " filld <address>[:<space>],<length>[,<data>[,...]] -- fill data memory with data\n" + " filli <address>[:<space>],<length>[,<data>[,...][ -- fill I/O memory with data\n" + " fillo <address>[:<space>],<length>[,<data>[,...][ -- fill opcode memory with data\n" + " dump <filename>,<address>[:<space>],<length>[,<group>[,<ascii>[,<rowsize>]]] -- dump memory as text\n" + " dumpd <filename>,<address>[:<space>],<length>[,<group>[,<ascii>[,<rowsize>]]] -- dump data memory as text\n" + " dumpi <filename>,<address>[:<space>],<length>[,<group>[,<ascii>[,<rowsize>]]] -- dump I/O memory as text\n" + " dumpo <filename>,<address>[:<space>],<length>[,<group>[,<ascii>[,<rowsize>]]] -- dump opcodes memory as text\n" + " strdump <filename>,<address>[:<space>],<length>[,<term>] -- dump ASCII strings from memory\n" + " strdumpd <filename>,<address>[:<space>],<length>[,<term>] -- dump ASCII strings from data memory\n" + " strdumpi <filename>,<address>[:<space>],<length>[,<term>] -- dump ASCII strings from I/O memory\n" + " strdumpo <filename>,<address>[:<space>],<length>[,<term>] -- dump ASCII strings from opcodes memory\n" + " save <filename>,<address>[:<space>],<length> -- save binary memory to the given file\n" + " saved <filename>,<address>[:<space>],<length> -- save binary data memory to the given file\n" + " savei <filename>,<address>[:<space>],<length> -- save binary I/O memory to the given file\n" + " saveo <filename>,<address>[:<space>],<length> -- save binary opcode memory to the given file\n" + " saver <filename>,<address>[:<space>],<length>,<region> -- save binary memory region to the given file\n" + " load <filename>,<address>[:<space>][,<length>] -- load binary memory from the given file\n" + " loadd <filename>,<address>[:<space>][,<length>] -- load binary data memory from the given file\n" + " loadi <filename>,<address>[:<space>][,<length>] -- load binary I/O memory from the given file\n" + " loado <filename>,<address>[:<space>][,<length>] -- load binary opcode memory from the given file\n" + " loadr <filename>,<address>[:<space>],<length>,<region> -- load binary memory region from the given file\n" + " map <address>[:<space>] -- map logical address to physical address and bank\n" + " mapd <address>[:<space>] -- map logical data address to physical address and bank\n" + " mapi <address>[:<space>] -- map logical I/O address to physical address and bank\n" + " mapo <address>[:<space>] -- map logical opcode address to physical address and bank\n" + " memdump [<filename>,[<root>]] -- dump current memory maps to <filename>\n" }, { "execution", @@ -137,8 +134,12 @@ static const help_item static_help_list[] = " o[ver] [<count>=1] -- single steps over <count> instructions (F10)\n" " out -- single steps until the current subroutine/exception handler is exited (Shift-F11)\n" " g[o] [<address>] -- resumes execution, sets temp breakpoint at <address> (F5)\n" - " ge[x] [<exception>] -- resumes execution, setting temp breakpoint if <exception> is raised\n" + " gbf [<condition>] -- resumes execution until next false branch\n" + " gbt [<condition>] -- resumes execution until next true branch\n" + " gn[i] [<count>] -- resumes execution, sets temp breakpoint <count> instructions ahead\n" + " ge[x] [<exception>[,<condition>]] -- resumes execution, setting temp breakpoint if <exception> is raised\n" " gi[nt] [<irqline>] -- resumes execution, setting temp breakpoint if <irqline> is taken (F7)\n" + " gp [<condition>] -- resumes execution, setting temp breakpoint if privilege level changes\n" " gt[ime] <milliseconds> -- resumes execution until the given delay has elapsed\n" " gv[blank] -- resumes execution, setting temp breakpoint on the next VBLANK (F8)\n" " n[ext] -- executes until the next CPU switch (F6)\n" @@ -147,6 +148,7 @@ static const help_item static_help_list[] = " observe [<CPU>[,<CPU>[,...]]] -- resumes debugging on <CPU>\n" " suspend [<CPU>[,<CPU>[,...]]] -- suspends execution on <CPU>\n" " resume [<CPU>[,<CPU>[,...]]] -- resumes execution on <CPU>\n" + " cpulist -- list all CPUs\n" " trace {<filename>|OFF}[,<CPU>[,<detectloops>[,<action>]]] -- trace the given CPU to a file (defaults to active CPU)\n" " traceover {<filename>|OFF}[,<CPU>[,<detectloops>[,<action>]]] -- trace the given CPU to a file, but skip subroutines (defaults to active CPU)\n" " traceflush -- flushes all open trace files\n" @@ -157,11 +159,11 @@ static const help_item static_help_list[] = "Breakpoint Commands\n" "Type help <command> for further details on each command\n" "\n" - " bp[set] <address>[,<condition>[,<action>]] -- sets breakpoint at <address>\n" - " bpclear [<bpnum>] -- clears a given breakpoint or all if no <bpnum> specified\n" - " bpdisable [<bpnum>] -- disables a given breakpoint or all if no <bpnum> specified\n" - " bpenable [<bpnum>] -- enables a given breakpoint or all if no <bpnum> specified\n" - " bplist -- lists all the breakpoints\n" + " bp[set] <address>[:<CPU>][,<condition>[,<action>]] -- sets breakpoint at <address>\n" + " bpclear [<bpnum>[,...]] -- clears given breakpoints or all if no <bpnum> specified\n" + " bpdisable [<bpnum>[,...]] -- disables given breakpoints or all if no <bpnum> specified\n" + " bpenable [<bpnum>[,...]] -- enables given breakpoints or all if no <bpnum> specified\n" + " bplist [<CPU>] -- lists all the breakpoints\n" }, { "watchpoints", @@ -169,14 +171,14 @@ static const help_item static_help_list[] = "Watchpoint Commands\n" "Type help <command> for further details on each command\n" "\n" - " wp[set] <address>,<length>,<type>[,<condition>[,<action>]] -- sets program space watchpoint\n" - " wpd[set] <address>,<length>,<type>[,<condition>[,<action>]] -- sets data space watchpoint\n" - " wpi[set] <address>,<length>,<type>[,<condition>[,<action>]] -- sets I/O space watchpoint\n" - " wpclear [<wpnum>] -- clears a given watchpoint or all if no <wpnum> specified\n" - " wpdisable [<wpnum>] -- disables a given watchpoint or all if no <wpnum> specified\n" - " wpenable [<wpnum>] -- enables a given watchpoint or all if no <wpnum> specified\n" - " wplist -- lists all the watchpoints\n" - " hotspot [<CPU>,[<depth>[,<hits>]]] -- attempt to find hotspots\n" + " wp[set] <address>[:<space>],<length>,<type>[,<condition>[,<action>]] -- sets watchpoint\n" + " wpd[set] <address>[:<space>],<length>,<type>[,<condition>[,<action>]] -- sets data space watchpoint\n" + " wpi[set] <address>[:<space>],<length>,<type>[,<condition>[,<action>]] -- sets I/O space watchpoint\n" + " wpo[set] <address>[:<space>],<length>,<type>[,<condition>[,<action>]] -- sets opcode space watchpoint\n" + " wpclear [<wpnum>[,...]] -- clears given watchpoints or all if no <wpnum> specified\n" + " wpdisable [<wpnum>[,...]] -- disables given watchpoints or all if no <wpnum> specified\n" + " wpenable [<wpnum>[,...]] -- enables given watchpoints or all if no <wpnum> specified\n" + " wplist [<CPU>] -- lists all the watchpoints\n" }, { "registerpoints", @@ -184,11 +186,23 @@ static const help_item static_help_list[] = "Registerpoint Commands\n" "Type help <command> for further details on each command\n" "\n" - " rp[set] {<condition>}[,<action>] -- sets a registerpoint to trigger on <condition>\n" - " rpclear [<rpnum>] -- clears a given registerpoint or all if no <rpnum> specified\n" - " rpdisable [<rpnum>] -- disabled a given registerpoint or all if no <rpnum> specified\n" - " rpenable [<rpnum>] -- enables a given registerpoint or all if no <rpnum> specified\n" - " rplist -- lists all the registerpoints\n" + " rp[set] <condition>[,<action>] -- sets a registerpoint to trigger on <condition>\n" + " rpclear [<rpnum>[,...]] -- clears given registerpoints or all if no <rpnum> specified\n" + " rpdisable [<rpnum>[,...]] -- disabled given registerpoints or all if no <rpnum> specified\n" + " rpenable [<rpnum>[,...]] -- enables given registerpoints or all if no <rpnum> specified\n" + " rplist [<CPU>] -- lists all the registerpoints\n" + }, + { + "exceptionpoints", + "\n" + "Exceptionpoint Commands\n" + "Type help <command> for further details on each command\n" + "\n" + " ep[set] <type>[,<condition>[,<action>]] -- sets exceptionpoint on <type>\n" + " epclear [<epnum>] -- clears a given exceptionpoint or all if no <epnum> specified\n" + " epdisable [<epnum>] -- disabled a given exceptionpoint or all if no <epnum> specified\n" + " epenable [<epnum>] -- enables a given exceptionpoint or all if no <epnum> specified\n" + " eplist -- lists all the exceptionpoints\n" }, { "expressions", @@ -250,9 +264,9 @@ static const help_item static_help_list[] = "Cheat Commands\n" "Type help <command> for further details on each command\n" "\n" - " cheatinit [<address>,<length>[,<CPU>]] -- initialize the cheat search to the selected memory area\n" - " cheatrange <address>,<length> -- add to the cheat search the selected memory area\n" - " cheatnext <condition>[,<comparisonvalue>] -- continue cheat search comparing with the last value\n" + " cheatinit [[<sign>[<width>[<swap>]]],[<address>,<length>[,<space>]]] -- initialize the cheat search to the selected memory area\n" + " cheatrange <address>,<length> -- add selected memory area to the cheat search\n" + " cheatnext <condition>[,<comparisonvalue>] -- continue cheat search comparing with the previous value\n" " cheatnextf <condition>[,<comparisonvalue>] -- continue cheat search comparing with the first value\n" " cheatlist [<filename>] -- show the list of cheat search matches or save them to <filename>\n" " cheatundo -- undo the last cheat search (state only)\n" @@ -263,9 +277,9 @@ static const help_item static_help_list[] = "Image Commands\n" "Type help <command> for further details on each command\n" "\n" - " images -- lists all image devices and mounted files\n" - " mount <device>,<filename> -- mounts file to named device\n" - " unmount <device> -- unmounts file from named device\n" + " images -- lists all image devices and mounted mounted media\n" + " mount <instance>,<filename> -- mounts file to specified device\n" + " unmount <instance> -- unmounts media from specified device\n" }, { "do", @@ -345,16 +359,20 @@ static const help_item static_help_list[] = "The printf command performs a C-style printf to the debugger console. Only a very limited set of " "formatting options are available:\n" "\n" - " %[0][<n>]d -- prints <item> as a decimal value with optional digit count and zero-fill\n" - " %[0][<n>]x -- prints <item> as a hexadecimal value with optional digit count and zero-fill\n" + " %c -- 8-bit character\n" + " %[-][0][<n>]d -- decimal number with optional left justification, zero fill and minimum width\n" + " %[-][0][<n>]o -- octal number with optional left justification, zero fill and minimum width\n" + " %[-][0][<n>]x -- lowercase hexadecimal number with optional left justification, zero fill and minimum width\n" + " %[-][0][<n>]X -- uppercase hexadecimal number with optional left justification, zero fill and minimum width\n" + " %[-][<n>][.[<n>]]s -- null-terminated string of 8-bit characters with optional left justification, minimum and maximum width\n" "\n" - "All remaining formatting options are ignored. Use %% together to output a % character. Multiple " - "lines can be printed by embedding a \\n in the text.\n" + "All remaining formatting options are ignored. Use %% to output a % character. Multiple lines can be " + "printed by embedding a \\n in the text.\n" "\n" "Examples:\n" "\n" "printf \"PC=%04X\",pc\n" - " Prints PC=<pcval> where <pcval> is displayed in hexadecimal with 4 digits with zero-fill.\n" + " Prints PC=<pcval> where <pcval> is displayed in uppercase hexadecimal with 4 digits and zero fill.\n" "\n" "printf \"A=%d, B=%d\\nC=%d\",a,b,a+b\n" " Prints A=<aval>, B=<bval> on one line, and C=<a+bval> on a second line.\n" @@ -365,18 +383,12 @@ static const help_item static_help_list[] = " logerror <format>[,<item>[,...]]\n" "\n" "The logerror command performs a C-style printf to the error log. Only a very limited set of " - "formatting options are available:\n" - "\n" - " %[0][<n>]d -- logs <item> as a decimal value with optional digit count and zero-fill\n" - " %[0][<n>]x -- logs <item> as a hexadecimal value with optional digit count and zero-fill\n" - "\n" - "All remaining formatting options are ignored. Use %% together to output a % character. Multiple " - "lines can be printed by embedding a \\n in the text.\n" + "formatting options are available. See the 'printf' help for details.\n" "\n" "Examples:\n" "\n" - "logerror \"PC=%04X\",pc\n" - " Logs PC=<pcval> where <pcval> is displayed in hexadecimal with 4 digits with zero-fill.\n" + "logerror \"PC=%04x\",pc\n" + " Logs PC=<pcval> where <pcval> is displayed in lowercase hexadecimal with 4 digits and zero fill.\n" "\n" "logerror \"A=%d, B=%d\\nC=%d\",a,b,a+b\n" " Logs A=<aval>, B=<bval> on one line, and C=<a+bval> on a second line.\n" @@ -395,31 +407,54 @@ static const help_item static_help_list[] = "tracelog \"PC=%04X\",pc\n" " Outputs PC=<pcval> where <pcval> is displayed in hexadecimal with 4 digits with zero-fill.\n" "\n" - "printf \"A=%d, B=%d\\nC=%d\",a,b,a+b\n" + "tracelog \"A=%d, B=%d\\nC=%d\",a,b,a+b\n" " Outputs A=<aval>, B=<bval> on one line, and C=<a+bval> on a second line.\n" }, { "tracesym", "\n" " tracesym <item>[,...]\n" - "\n" - "The tracesym command prints the specified symbols and routes the output to the currently open trace " - "file (see the 'trace' command for details). If no file is currently open, tracesym does nothing. " - "\n" - "Examples:\n" - "\n" - "tracelog pc\n" - " Outputs PC=<pcval> where <pcval> is displayed in the default format.\n" + "\n" + "The tracesym command prints the specified symbols and routes the output to the currently open trace " + "file (see the 'trace' command for details). If no file is currently open, tracesym does nothing. " + "\n" + "Examples:\n" + "\n" + "tracesym pc\n" + " Outputs PC=<pcval> where <pcval> is displayed in the default format.\n" + }, + { + "history", + "\n" + " history [<CPU>,[<length>]]\n" + "\n" + "The history command displays recently visited PC addresses, and the disassembly of the " + "instructions at those addresses. If present, the first argument is a CPU selector " + "(either a tag or a CPU number); if no CPU is specified, the current CPU is assumed. " + "The second argument, if present, limits the maximum number of addresses shown. " + "Addresses are shown in order from least to most recently visited.\n" + "\n" + "Examples:\n" + "\n" + "history ,5\n" + " Displays up to five most recently visited PC addresses for the current CPU.\n" + "\n" + "history 3\n" + " Displays recently visited PC addresses for CPU 3.\n" + "\n" + "history audiocpu,1\n" + " Displays the most recently visited PC addresses for the CPU ':audiocpu'.\n" }, { "trackpc", "\n" - " trackpc [<bool>,<CPU>,<bool>]\n" + " trackpc [<bool>,[<CPU>,[<bool>]]]\n" "\n" - "The trackpc command displays which program counters have already been visited in all disassembler " - "windows. The first boolean argument toggles the process on and off. The second argument is a " - "CPU selector; if no CPU is specified, the current CPU is automatically selected. The third argument " - "is a boolean denoting if the existing data should be cleared or not.\n" + "The trackpc command displays which program counters have already been visited in all " + "disassembler views. The first Boolean argument toggles the process on and off. The " + "second argument is a CPU selector (either a tag or a debugger CPU number); if no CPU is " + "specified, the current CPU is assumed. The third argument is a Boolean indicating " + "whether the existing data should be cleared.\n" "\n" "Examples:\n" "\n" @@ -432,38 +467,53 @@ static const help_item static_help_list[] = { "trackmem", "\n" - " trackmem [<bool>,<CPU>,<bool>]\n" + " trackmem [<bool>,[<CPU>,[<bool>]]]\n" "\n" "The trackmem command logs the PC at each time a memory address is written to. " - "The first boolean argument toggles the process on and off. The second argument is a CPU " - "selector; if no CPU is specified, the current CPU is automatically selected. The third argument " - " is a boolean denoting if the existing data should be cleared or not. Please refer to the " - "pcatmem command for information on how to retrieve this data. Also, right clicking in " - "a memory window will display the logged PC for the given address.\n" + "The first Boolean argument toggles the process on and off. The second argument is a CPU " + "selector (either a tag or a debugger CPU number); if no CPU is specified, the current CPU " + "is assumed. The third argument is a Boolean indicating whether the existing data should " + "be cleared. Please refer to the 'pcatmem' command for information on how to retrieve this " + "data. Also, right-clicking in a memory view will display the logged PC for the given " + "address.\n" "\n" "Examples:\n" "\n" "trackmem\n" - " Begin tracking the current CPU's pc.\n" + " Begin tracking memory writes for the current CPU.\n" "\n" "trackmem 1, 0, 1\n" - " Continue tracking memory writes on CPU 0, but clear existing track info.\n" + " Continue tracking memory writes on CPU 0, but clear existing tracking data.\n" }, { "pcatmem", "\n" - " pcatmem(p/d/i) <address>[,<CPU>]\n" + " pcatmem[{d|i|o}] <address>[:<space>]\n" "\n" - "The pcatmem command returns which PC wrote to a given memory address for the current CPU. " - "The first argument is the requested address. The second argument is a CPU selector; if no " - "CPU is specified, the current CPU is automatically selected. Right clicking in a memory window " - "will also display the logged PC for the given address.\n" + "The pcatmem command returns which PC value at the time the specified address was most " + "recently written. The argument is the requested address, optionally followed by a colon " + "and a CPU and/or address space. The CPU may be specified as a tag or debugger CPU number; " + "if no CPU is specified, the CPU currently visible in the debugger is assumed. If an " + "address space is not specified, the command suffix sets the address space: 'pcatmem' " + "defaults to the first space exposed by the device, 'pcatmemd' defaults to the data space, " + "'pcatmemi' defaults to the I/O space, and 'pcatmemo' defaults to the opcodes space.\n" + "\n" + "Right-clicking in a memory view will also display the logged PC for the given address.\n" "\n" "Examples:\n" "\n" "pcatmem 400000\n" - " Print which PC wrote this CPU's memory location 0x400000.\n" + " Print which PC wrote to this CPU's program space at location 0x400000.\n" + "\n" + "pcatmem 3bc:io\n" + " Print which PC wrote this CPU's memory io space at location 0x3bc.\n" + "\n" + "pcatmem 1400:audiocpu\n" + " Print which PC wrote the CPU :audiocpu's memory program space at location 0x1400.\n" }, + { "pcatmemd", "#pcatmem" }, + { "pcatmemi", "#pcatmem" }, + { "pcatmemo", "#pcatmem" }, { "rewind[rw]", "\n" @@ -510,22 +560,25 @@ static const help_item static_help_list[] = { "snap", "\n" - " snap [[<filename>], <scrnum>]\n" + " snap [<filename>[,<scrnum>]]\n" + "\n" + "Takes a snapshot of the emulated video display and saves it to the configured snapshot " + "directory. If a filename is specified, a single screenshot for the specified screen is " + "saved using the specified filename (or the first emulated screen in the system if a screen " + "is not specified). If a file name is not specified, the configured snapshot view and file " + "name pattern are used.\n" "\n" - "The snap command takes a snapshot of the current video display and saves it to the configured " - "snapshot directory. If <filename> is specified explicitly, a single screenshot for <scrnum> is " - "saved under the requested filename. If <filename> is omitted, all screens are saved using the " - "same default rules as the \"save snapshot\" key in MAME proper.\n" + "If a file name is specified, the .png extension is automatically appended. The screen " + "number is specified as a zero-based index.\n" "\n" "Examples:\n" "\n" "snap\n" - " Takes a snapshot of the current video screen and saves to the next non-conflicting filename " - " in the configured snapshot directory.\n" + " Takes a snapshot using the configured snapshot view and file name options.\n" "\n" "snap shinobi\n" - " Takes a snapshot of the current video screen and saves it as 'shinobi.png' in the configured " - " snapshot directory.\n" + " Takes a snapshot of the first emulated video screen and saves it as 'shinobi.png' in the " + " configured snapshot directory.\n" }, { "source", @@ -541,6 +594,13 @@ static const help_item static_help_list[] = " Reads in debugger commands from break_and_trace.cmd and executes them.\n" }, { + "time", + "\n" + " time\n" + "\n" + "The time command prints the current machine time to the console.\n" + }, + { "quit", "\n" " quit\n" @@ -572,50 +632,91 @@ static const help_item static_help_list[] = { "find", "\n" - " f[ind][{d|i}] <address>,<length>[,<data>[,...]]\n" - "\n" - "The find/findd/findi commands search through memory for the specified sequence of data. " - "'find' will search program space memory, while 'findd' will search data space memory " - "and 'findi' will search I/O space memory. <address> indicates the address to begin searching, " - "and <length> indicates how much memory to search. <data> can either be a quoted string " - "or a numeric value or expression or the wildcard character '?'. Strings by default imply a " - "byte-sized search; non-string data is searched by default in the native word size of the CPU. " - "To override the search size for non-strings, you can prefix the value with b. to force byte- " - "sized search, w. for word-sized search, d. for dword-sized, and q. for qword-sized. Overrides " - "are remembered, so if you want to search for a series of words, you need only to prefix the " - "first value with a w. Note also that you can intermix sizes in order to perform more complex " - "searches. The entire range <address> through <address>+<length>-1 inclusive will be searched " - "for the sequence, and all occurrences will be displayed.\n" + " f[ind][{d|i|o}] <address>[:<space>],<length>[,<data>[,...]]\n" + "\n" + "The find commands search through memory for the specified sequence of data. The <address> " + "is the address to begin searching from, optionally followed by a device and/or address " + "space; the <length> specifies how much memory to search. The device may be specified as a " + "tag or a debugger CPU number; if no device is specified, the CPU currently visible in the " + "debugger is assumed. If an address space is not specified, the command suffix sets the " + "address space: 'find' defaults to the first address space exposed by the device, 'findd' " + "defaults to the data space, 'findi' defaults to the I/O space, and 'findo' defaults to the " + "opcodes space.\n" + "\n" + "The <data> can either be a quoted string or a numeric value or expression or the wildcard " + "character '?'. By default, strings imply a byte-sized search; by default non-string data " + "is searched using the native word size of the address space. To override the search size " + "for non-string data, you can prefix values with b. to force byte-sized search, w. for " + "word-sized search, d. for dword-sized search, and q. for qword-sized search. Overrides " + "propagate to subsequent values, so if you want to search for a sequence of words, you need " + "only prefix the first value with a w. Also note that you can intermix sizes to perform " + "more complex searches. The entire range <address> through <address>+<length>-1, " + "inclusive, will be searched for the sequence, and all occurrences will be displayed.\n" "\n" "Examples:\n" "\n" "find 0,10000,\"HIGH SCORE\",0\n" - " Searches the address range 0-ffff in the current CPU for the string \"HIGH SCORE\" followed " - "by a 0 byte.\n" + " Searches the address range 0-ffff in the current CPU for the string \"HIGH SCORE\" " + "followed by a 0 byte.\n" "\n" - "findd 3000,1000,w.abcd,4567\n" - " Searches the data memory address range 3000-3fff for the word-sized value abcd followed by " - "the word-sized value 4567.\n" + "find 300:tms9918a,100,w.abcd,4567\n" + " Searches the address range 300-3ff in the first address space exposed by the device " + "':tms9918a' for the word-sized value abcd followed by the word-sized value 4567.\n" "\n" "find 0,8000,\"AAR\",d.0,\"BEN\",w.0\n" " Searches the address range 0000-7fff for the string \"AAR\" followed by a dword-sized 0 " "followed by the string \"BEN\", followed by a word-sized 0.\n" }, + { "findd", "#find" }, + { "findi", "#find" }, + { "findo", "#find" }, { - "dump", + "fill", "\n" - " dump[{d|i}] <filename>,<address>,<length>[,<size>[,<ascii>[,<CPU>]]]\n" + " fill[{d|i|o}] <address>[:<space>],<length>[,<data>[,...]]\n" "\n" - "The dump/dumpd/dumpi/dumpo commands dump memory to the text file specified in the <filename> " - "parameter. 'dump' will dump program space memory, while 'dumpd' will dump data space memory, " - "'dumpi' will dump I/O space memory and 'dumpo' will dump opcodes memory. <address> indicates " - "the address of the start of dumping, and <length> indicates how much memory to dump. The range " - "<address> through <address>+<length>-1 inclusive will be output to the file. By default, the data " - "will be output in byte format, unless the underlying address space is word/dword/qword-only. " - "You can override this by specifying the <size> parameter, which can be used to group the data in " - "1, 2, 4 or 8-byte chunks. The optional <ascii> parameter can be used to enable (1) or disable (0) " - "the output of ASCII characters to the right of each line; by default, this is enabled. Finally, " - "you can dump memory from another CPU by specifying the <CPU> parameter.\n" + "The fill commands overwrite a block of memory with copies of the supplied data sequence. " + "The <address> specifies the address to begin writing at, optionally followed by a device " + "and/or address space; the <length> specifies how much memory to fill. The device may be " + "specified as a tag or a debugger CPU number; if no device is specified, the CPU currently " + "visible in the debugger is assumed. If an address space is not specified, the command " + "suffix sets the address space: 'fill' defaults to the first address space exposed by the " + "device, 'filld' defaults to the data space, 'filli' defaults to the I/O space, and 'fillo' " + "defaults to the opcodes space.\n" + "\n" + "The <data> can either be a quoted string or a numeric value or expression. By default, " + "non-string data is written using the native word size of the address space. To override " + "the data size for non-string data, you can prefix values with b. to force byte-sized fill, " + "w. for word-sized fill, d. for dword-sized fill, and q. for qword-sized fill. Overrides " + "propagate to subsequent values, so if you want to fill with a series of words, you need " + "only prefix the first value with a w. Also note that you can intermix sizes to perform " + "more complex fills. The fill operation may be truncated if a page fault occurs or if part " + "of the sequence or string would fall beyond <address>+<length>-1.\n" + }, + { "filld", "#fill" }, + { "filli", "#fill" }, + { "fillo", "#fill" }, + { + "dump", + "\n" + " dump[{d|i|o}] <filename>,<address>[:<space>],<length>[,<group>[,<ascii>[,<rowsize>]]]\n" + "\n" + "The dump commands dump memory to the text file specified in the <filename> parameter. The " + "<address> specifies the address to start dumping from, optionally followed by a device " + "and/or address space; the <length> specifies how much memory to dump. The device may be " + "specified as a tag or a debugger CPU number; if no device is specified, the CPU currently " + "visible in the debugger is assumed. If an address space is not specified, the command " + "suffix sets the address space: 'dump' defaults to the first address space exposed by the " + "device, 'dumpd' defaults to the data space, 'dumpi' defaults to the I/O space, and 'dumpo' " + "defaults to the opcodes space.\n" + "\n" + "The range <address> through <address>+<length>-1, inclusive, will be output to the file. " + "By default, the data will be output using the native word size of the address space. You " + "can override this by specifying the <group> parameter, which can be used to group the data " + "in 1-, 2-, 4- or 8-byte chunks. The optional <ascii> parameter is a Boolean value used to " + "enable or disable output of ASCII characters on the right of each line (enabled by " + "default). The optional <rowsize> parameter specifies the amount of data on each line in " + "address units (defaults to 16 bytes).\n" "\n" "Examples:\n" "\n" @@ -623,43 +724,111 @@ static const help_item static_help_list[] = " Dumps addresses 0-ffff in the current CPU in 1-byte chunks, including ASCII data, to " "the file 'venture.dmp'.\n" "\n" - "dumpd harddriv.dmp,3000,1000,4,0,3\n" + "dumpd harddriv.dmp,3000:3,1000,4,0\n" " Dumps data memory addresses 3000-3fff from CPU #3 in 4-byte chunks, with no ASCII data, " "to the file 'harddriv.dmp'.\n" + "\n" + "dump vram.dmp,0:sms_vdp:videoram,4000,1,0,8\n" + " Dumps 'videoram' space addresses 0000-3fff from the device ':sms_vdp' in 1-byte chunks, " + "with no ASCII data, and 8 bytes per line, to the file 'vram.dmp'.\n" }, + { "dumpd", "#dump" }, + { "dumpi", "#dump" }, + { "dumpo", "#dump" }, + { + "strdump", + "\n" + " strdump[{d|i|o}] <filename>,<address>[:<space>],<length>[,<term>]\n" + "\n" + "The strdump commands dump memory to the text file specified in the <filename> parameter. " + "The <address> specifies the address to start dumping from, optionally followed by a device " + "and/or address space; the <length> specifies how much memory to dump. The device may be " + "specified as a tag or a debugger CPU number; if no device is specified, the CPU currently " + "visible in the debugger is assumed. If an address space is not specified, the command " + "suffix sets the address space: 'strdump' defaults to the first address space exposed by " + "the device, 'strdumpd' defaults to the data space, 'strdumpi' defaults to the I/O space, " + "and 'strdumpo' defaults to the opcodes space.\n" + "\n" + "By default, the data will be interpreted as a series of NUL-terminated strings, the dump " + "will have one string per line, and C-style escapes will be used for non-ASCII characters. " + "The optional <term> parameter can be used to specify a different string terminator " + "character.\n" + }, + { "strdumpd", "#strdump" }, + { "strdumpi", "#strdump" }, + { "strdumpo", "#strdump" }, { "save", "\n" - " save[{d|i}] <filename>,<address>,<length>[,<CPU>]\n" + " save[{d|i|o}] <filename>,<address>[:<space>],<length>\n" "\n" - "The save/saved/savei commands save raw memory to the binary file specified in the <filename> " - "parameter. 'save' will save program space memory, while 'saved' will save data space memory " - "and 'savei' will save I/O space memory. <address> indicates the address of the start of saving, " - "and <length> indicates how much memory to save. The range <address> through <address>+<length>-1 " - "inclusive will be output to the file. You can also save memory from another CPU by specifying the " - "<CPU> parameter.\n" + "The save commands save raw memory to the binary file specified in the <filename> " + "parameter. The <address> specifies the address to start saving from, optionally followed " + "by a device and/or address space; the <length> specifies how much memory to save. The " + "device may be specified as a tag or a debugger CPU number; if no device is specified, the " + "CPU currently visible in the debugger is assumed. If an address space is not specified, " + "the command suffix sets the address space: 'save' defaults to the first address space " + "exposed by the device, 'saved' defaults to the data space, 'savei' defaults to the I/O " + "space, and 'saveo' defaults to the opcodes space.\n" + "\n" + "The range <address> through <address>+<length>-1, inclusive, will be output to the file.\n" "\n" "Examples:\n" "\n" "save venture.bin,0,10000\n" " Saves addresses 0-ffff in the current CPU to the binary file 'venture.bin'.\n" "\n" - "saved harddriv.bin,3000,1000,3\n" + "saved harddriv.bin,3000:3,1000\n" " Saves data memory addresses 3000-3fff from CPU #3 to the binary file 'harddriv.bin'.\n" + "\n" + "save vram.bin,0:sms_vdp:videoram,4000\n" + " Saves 'videoram' space addresses 0000-3fff from the device ':sms_vdp' to the binary file " + "'vram.bin'.\n" + }, + { "saved", "#save" }, + { "savei", "#save" }, + { "saveo", "#save" }, + { + "saver", + "\n" + " saver <filename>,<address>,<length>,<region>\n" + "\n" + "The saver command saves the raw content of memory region <region> to the binary file " + "specified in the <filename> parameter. The <address> specifies the address to start " + "saving from, and the <length> specifies how much memory to save. The range <address> " + "through <address>+<length>-1, inclusive, will be output to the file.\n" + "\n" + "Examples:\n" + "\n" + "saver data.bin,200,100,:monitor\n" + " Saves ':monitor' region addresses 200-2ff to the binary file 'data.bin'.\n" + "\n" + "saver cpurom.bin,1000,400,.\n" + " Saves addresses 1000-13ff from the memory region with the same tag as the visible CPU to " + "the binary file 'cpurom.bin'.\n" }, { "load", "\n" - " load[{d|i}] <filename>,<address>[,<length>,<CPU>]\n" + " load[{d|i|o}] <filename>,<address>[:<space>][,<length>]\n" "\n" - "The load/loadd/loadi commands load raw memory from the binary file specified in the <filename> " - "parameter. 'load' will load program space memory, while 'loadd' will load data space memory " - "and 'loadi' will load I/O space memory. <address> indicates the address of the start of saving, " - "and <length> indicates how much memory to load. The range <address> through <address>+<length>-1 " - "inclusive will be read in from the file. If you specify <length> = 0 or a length greater than the " - "total length of the file it will load the entire contents of the file and no more. You can also load " - "memory from another CPU by specifying the <CPU> parameter.\n" - "NOTE: This will only actually write memory that is possible to overwrite in the Memory Window\n" + "The load commands load raw memory from the binary file specified in the <filename> " + "parameter. The <address> specifies the address to start loading to, optionally followed " + "by a device and/or address space; the <length> specifies how much memory to load. The " + "device may be specified as a tag or a debugger CPU number; if no device is specified, the " + "CPU currently visible in the debugger is assumed. If an address space is not specified, " + "the command suffix sets the address space: 'load' defaults to the first address space " + "exposed by the device, 'loadd' defaults to the data space, 'loadi' defaults to the I/O " + "space, and 'loado' defaults to the opcodes space.\n" + "\n" + "The range <address> through <address>+<length>-1, inclusive, will be read in from the " + "file. If the <length> is omitted, if it is zero, or if it is greater than the total " + "length of the file, the entire contents of the file will be loaded but no more.\n" + "\n" + "NOTE: this has the same effect as writing to the address space from a debugger memory " + "view, or using memory accessors in debugger expressions. Read-only memory will not be " + "overwritten, and writing to I/O addresses may have effects beyond setting register " + "values.\n" "\n" "Examples:\n" "\n" @@ -668,6 +837,34 @@ static const help_item static_help_list[] = "\n" "loadd harddriv.bin,3000,1000,3\n" " Loads data memory addresses 3000-3fff from CPU #3 from the binary file 'harddriv.bin'.\n" + "\n" + "load vram.bin,0:sms_vdp:videoram\n" + " Loads 'videoram' space starting at address 0000 from the device ':sms_vdp' with the " + "entire content of the binary file 'vram.bin'.\n" + }, + { "loadd", "#load" }, + { "loadi", "#load" }, + { "loado", "#load" }, + { + "loadr", + "\n" + " loadr <filename>,<address>,<length>,<region>\n" + "\n" + "The loadr command loads raw memory in the memory region <region> from the binary file " + "specified by the <filename> parameter. The <address> specifies the address to start " + "loading to, and the <length> specifies how much memory to load. The range <address> " + "through <address>+<length>-1, inclusive, will be read in from the file. If the <length> " + "is zero, or is greater than the total length of the file, the entire contents of the file " + "will be loaded but no more.\n" + "\n" + "Examples:\n" + "\n" + "loadr data.bin,200,100,:monitor\n" + " Loads ':monitor' region addresses 200-2ff from the binary file 'data.bin'.\n" + "\n" + "loadr cpurom.bin,1000,400,.\n" + " Loads addresses 1000-13ff in the memory region with the same tag as the visible CPU from " + "the binary file 'cpurom.bin'.\n" }, { "step", @@ -722,7 +919,7 @@ static const help_item static_help_list[] = "Note that the step out functionality may not be implemented on all CPU types. If it is not " "implemented, then 'out' will behave exactly like 'step'.\n" "\n" - "Examples:\n" + "Example:\n" "\n" "out\n" " Steps until the current subroutine or exception handler returns.\n" @@ -746,41 +943,132 @@ static const help_item static_help_list[] = " Resume execution, stopping at address 1234 unless something else stops us first.\n" }, { - "gvblank", + "gbf", "\n" - " gv[blank]\n" + " gbf [<condition>]\n" "\n" - "The gvblank command resumes execution of the current code. Control will not be returned to " - "the debugger until a breakpoint or watchpoint is hit, or until the next VBLANK occurs in the " - "emulator.\n" + "The gbf command resumes execution of the current code. Control will not be returned to the " + "debugger until a conditional branch or skip instruction tests false and falls through to the " + "next instruction, or the instruction that follows its delay slot. The optional conditional " + "expression, if provided, will be evaluated at (not after) each conditional branch; execution " + "will not halt regardless of consequent program flow unless its result is true (non-zero).\n" "\n" "Examples:\n" "\n" - "gv\n" - " Resume execution until the next break/watchpoint or until the next VBLANK.\n" + "gbf\n" + " Resume execution until the next break/watchpoint or until the next false branch.\n" + "\n" + "gbf {pc != 1234}\n" + " Resume execution until the next false branch, disregarding the instruction at address 1234.\n" + }, + { + "gbt", + "\n" + " gbt [<condition>]\n" + "\n" + "The gbt command resumes execution of the current code. Control will not be returned to the " + "debugger until a conditional branch or skip instruction tests true and program flow transfers " + "to any instruction other than the next one following the delay slot (if any). The optional " + "conditional expression, if provided, will be evaluated at (not after) each conditional " + "branch; execution will not halt regardless of consequent program flow unless its result is " + "true (non-zero).\n" + "\n" + "Examples:\n" + "\n" + "gbt\n" + " Resume execution until the next break/watchpoint or until the next true branch.\n" + "\n" + "gbt {pc != 1234}\n" + " Resume execution until the next true branch, disregarding the instruction at address 1234.\n" + }, + { + "gni", + "\n" + " gn[i] [<count>]\n" + "\n" + "The gni command resumes execution of the current code. Control will not be returned to the " + "debugger until a breakpoint or watchpoint is hit, or until you manually break in using the " + "assigned key. Before executing, the gni command sets a temporary unconditional breakpoint " + "<count> instructions sequentially past the current one, which is automatically removed when " + "hit. If <count> is omitted, its default value is 1. If <count> is specified as zero, the " + "command does nothing. <count> is not permitted to exceed 512 decimal." + "\n" + "Examples:\n" + "\n" + "gni\n" + " Resume execution, stopping at the address of the next instruction unless something else " + "stops us first.\n" + "\n" + "gni 2\n" + " Resume execution, stopping at two instructions past the current one.\n" + }, + { + "gex", + "\n" + " ge[x] [<exception>,[<condition>]]\n" + "\n" + "The gex command resumes execution of the current code. Control will not be returned to " + "the debugger until a breakpoint or watchpoint is hit, or until an exception condition " + "is raised on the current CPU. You can specify <exception> if you wish to stop execution " + "only on a particular exception condition occurring. If <exception> is omitted, then any " + "exception condition will stop execution. The optional <condition> parameter lets you " + "specify an expression that will be evaluated each time the specified exception condition " + "is raised. If the result of the expression is true (non-zero), the exception will halt " + "execution; otherwise, execution will continue with no notification.\n" + "\n" + "Examples:\n" + "\n" + "gex\n" + " Resume execution until the next break/watchpoint or until any exception condition is " + "raised on the current CPU.\n" + "\n" + "ge 2\n" + " Resume execution until the next break/watchpoint or until exception condition 2 is " + "raised on the current CPU.\n" }, { "gint", "\n" " gi[nt] [<irqline>]\n" "\n" - "The gint command resumes execution of the current code. Control will not be returned to the " - "debugger until a breakpoint or watchpoint is hit, or until an IRQ is asserted and acknowledged " - "on the current CPU. You can specify <irqline> if you wish to stop execution only on a particular " - "IRQ line being asserted and acknowledged. If <irqline> is omitted, then any IRQ line will stop " - "execution.\n" + "The gint command resumes execution of the current code. Control will not be returned to " + "the debugger until a breakpoint or watchpoint is hit, or until an IRQ is asserted and " + "acknowledged on the current CPU. You can specify <irqline> if you wish to stop execution " + "only on a particular IRQ line being asserted and acknowledged. If <irqline> is omitted, " + "then any IRQ line will stop execution.\n" "\n" "Examples:\n" "\n" "gi\n" - " Resume execution until the next break/watchpoint or until any IRQ is asserted and acknowledged " - "on the current CPU.\n" + " Resume execution until the next break/watchpoint or until any IRQ is asserted and " + "acknowledged on the current CPU.\n" "\n" "gint 4\n" " Resume execution until the next break/watchpoint or until IRQ line 4 is asserted and " "acknowledged on the current CPU.\n" }, { + "gp", + "\n" + " gp [<condition>]\n" + "\n" + "The gp command resumes execution of the current code. Control will not be returned to " + "the debugger until a breakpoint or watchpoint is hit, or the privilege level of the current " + "CPU changes. The optional <condition> parameter lets you specify an expression that will " + "be evaluated each time the privilege level changes. If the expression evaluates to true " + "(non-zero), execution will halt; otherwise, execution will continue with no notification.\n" + "\n" + "Examples:\n" + "\n" + "gp\n" + " Resume execution until the next break/watchpoint or the privilege level of the current " + "CPU changes.\n" + "\n" + "gp {pc != 1234}\n" + " Resume execution until the next break/watchpoint or the privilege level of the current " + "CPU changes, disregarding the instruction at address 1234.\n" + }, + { "gtime", "\n" " gt[ime] <milliseconds>\n" @@ -794,13 +1082,32 @@ static const help_item static_help_list[] = " Resume execution for ten seconds\n" }, { + "gvblank", + "\n" + " gv[blank]\n" + "\n" + "The gvblank command resumes execution of the current code. Control will not be returned to " + "the debugger until a breakpoint or watchpoint is hit, or until the beginning of the " + " vertical blanking interval for an emulated screen.\n" + "\n" + "Example:\n" + "\n" + "gv\n" + " Resume execution until the next break/watchpoint or until the next VBLANK.\n" + }, + { "next", "\n" " n[ext]\n" "\n" "The next command resumes execution and continues executing until the next time a different " - "CPU is scheduled. Note that if you have used 'ignore' to ignore certain CPUs, you will not " - "stop until a non-'ignore'd CPU is scheduled.\n" + "CPU is scheduled. Note that if you have used 'focus' or 'ignore' to ignore certain CPUs, " + "execution will continue until a non-'ignore'd CPU is scheduled.\n" + "\n" + "Example:\n" + "\n" + "n\n" + " Resume execution, stopping when a different CPU that is not ignored is scheduled.\n" }, { "focus", @@ -814,21 +1121,25 @@ static const help_item static_help_list[] = "\n" "focus 1\n" " Focus exclusively CPU #1 while ignoring all other CPUs when using the debugger.\n" + "\n" + "focus audiopcb:melodycpu\n" + " Focus exclusively on the CPU ':audiopcb:melodycpu'.\n" }, { "ignore", "\n" " ignore [<CPU>[,<CPU>[,...]]]\n" "\n" - "Ignores the specified <CPU> in the debugger. This means that you won't ever see execution " - "on that CPU, nor will you be able to set breakpoints on that CPU. To undo this change use " - "the 'observe' command. You can specify multiple <CPU>s in a single command. Note also that " - "you are not permitted to ignore all CPUs; at least one must be active at all times.\n" + "Ignores the specified CPUs in the debugger. CPUs can be specified by tag or debugger CPU " + "number. The debugger never shows execution for ignored CPUs, and breakpoints or " + "watchpoints on ignored CPUs have no effect. If no CPUs are specified, currently ignored " + "CPUs will be listed. Use the 'observe' command to stop ignoring a CPU. Note that you " + "cannot ignore all CPUs; at least CPU must be observed at all times.\n" "\n" "Examples:\n" "\n" - "ignore 1\n" - " Ignore CPU #1 when using the debugger.\n" + "ignore audiocpu\n" + " Ignore the CPU ':audiocpu' when using the debugger.\n" "\n" "ignore 2,3,4\n" " Ignore CPU #2, #3 and #4 when using the debugger.\n" @@ -846,8 +1157,8 @@ static const help_item static_help_list[] = "\n" "Examples:\n" "\n" - "observe 1\n" - " Stop ignoring CPU #1 when using the debugger.\n" + "observe audiocpu\n" + " Stop ignoring the CPU ':audiocpu' when using the debugger.\n" "\n" "observe 2,3,4\n" " Stop ignoring CPU #2, #3 and #4 when using the debugger.\n" @@ -858,106 +1169,120 @@ static const help_item static_help_list[] = { "trace", "\n" - " trace {<filename>|OFF}[,<CPU>[,[noloop|logerror][,<action>]]]\n" - "\n" - "Starts or stops tracing of the execution of the specified <CPU>. If <CPU> is omitted, " - "the currently active CPU is specified. When enabling tracing, specify the filename in the " - "<filename> parameter. To disable tracing, substitute the keyword 'off' for <filename>. " - "<detectloops> should be either true or false. If 'noloop' is omitted, the trace " - "will have loops detected and condensed to a single line. If 'noloop' is specified, the trace " - "will contain every opcode as it is executed. If 'logerror' is specified, logerror output " - "will augment the trace. If you " - "wish to log additional information on each trace, you can append an <action> parameter which " - "is a command that is executed before each trace is logged. Generally, this is used to include " - "a 'tracelog' command. Note that you may need to embed the action within braces { } in order " - "to prevent commas and semicolons from being interpreted as applying to the trace command " - "itself.\n" + " trace {<filename>|off}[,<CPU>[,[noloop|logerror][,<action>]]]\n" + "\n" + "Starts or stops tracing of the execution of the specified <CPU>, or the currently visible " + "CPU if no CPU is specified. To enable tracing, specify the trace log file name in the " + "<filename> parameter. To disable tracing, use the keyword 'off' for <filename> " + "parameter. If the **<filename>** begins with two right angle brackets (>>), it is treated " + "as a directive to open the file for appending rather than overwriting.\n" + "\n" + "The optional third parameter is a flags field. The supported flags are 'noloop' and " + "'logerror'. Multiple flags must be separated by | (pipe) characters. By default, loops " + "are detected and condensed to a single line. If the 'noloop' flag is specified, loops " + "will not be detected and every instruction will be logged as executed. If the 'logerror' " + "flag is specified, error log output will be included in the trace log.\n" + "\n" + "The optional <action> parameter is a debugger command to execute before each trace message " + "is logged. Generally, this will include a 'tracelog' or 'tracesym' command to include " + "additional information in the trace log. Note that you may need to embed the action " + "within braces { } in order to prevent commas and semicolons from being interpreted as " + "applying to the trace command itself.\n" "\n" "Examples:\n" "\n" "trace joust.tr\n" - " Begin tracing the currently active CPU, logging output to joust.tr.\n" + " Begin tracing the execution of the currently visible CPU, logging output to joust.tr.\n" "\n" - "trace dribling.tr,0\n" - " Begin tracing the execution of CPU #0, logging output to dribling.tr.\n" + "trace dribling.tr,maincpu\n" + " Begin tracing the execution of the CPU ':maincpu', logging output to dribling.tr.\n" "\n" - "trace starswep.tr,0,noloop\n" - " Begin tracing the execution of CPU #0, logging output to starswep.tr, with loop detection disabled.\n" + "trace starswep.tr,,noloop\n" + " Begin tracing the execution of the currently visible CPU, logging output to starswep.tr, " + "with loop detection disabled.\n" "\n" - "trace starswep.tr,0,logerror\n" - " Begin tracing the execution of CPU #0, logging output (along with logerror output) to starswep.tr.\n" + "trace starswep.tr,1,logerror\n" + " Begin tracing the execution of CPU #1, logging output (along with logerror output) to " + "starswep.tr.\n" "\n" "trace starswep.tr,0,logerror|noloop\n" - " Begin tracing the execution of CPU #0, logging output (along with logerror output) to starswep.tr, with loop detection disabled.\n" + " Begin tracing the execution of CPU #0, logging output (along with logerror output) to " + "starswep.tr, with loop detection disabled.\n" "\n" "trace >>pigskin.tr\n" - " Begin tracing the currently active CPU, appending log output to pigskin.tr.\n" + " Begin tracing execution of the currently visible CPU, appending log output to " + "pigskin.tr.\n" "\n" "trace off,0\n" " Turn off tracing on CPU #0.\n" "\n" - "trace asteroid.tr,0,,{tracelog \"A=%02X \",a}\n" - " Begin tracing the execution of CPU #0, logging output to asteroid.tr. Before each line, " - "output A=<aval> to the tracelog.\n" + "trace asteroid.tr,,,{tracelog \"A=%02X \",a}\n" + " Begin tracing the execution of the currently visible CPU, logging output to asteroid.tr. " + "Before each line, output A=<aval> to the trace log.\n" }, { "traceover", "\n" - " traceover {<filename>|OFF}[,<CPU>[,<detectloops>[,<action>]]]\n" - "\n" - "Starts or stops tracing of the execution of the specified <CPU>. When tracing reaches " - "a subroutine or call, tracing will skip over the subroutine. The same algorithm is used as is " - "used in the step over command. This means that traceover will not work properly when calls " - "are recusive or the return address is not immediately following the call instruction. If " - "<detectloops> should be either true or false. If <detectloops> is true or omitted, the trace " - "will have loops detected and condensed to a single line. If it is false, the trace will contain " - "every opcode as it is executed. If <CPU> is omitted, the currently active CPU is specified. When " - "enabling tracing, specify the filename in the <filename> parameter. To disable tracing, substitute " - "the keyword 'off' for <filename>. If you wish to log additional information on each trace, you can " - "append an <action> parameter which is a command that is executed before each trace is logged. " - "Generally, this is used to include a 'tracelog' command. Note that you may need to embed the " - "action within braces { } in order to prevent commas and semicolons from being interpreted as " - "applying to the trace command itself.\n" + " traceover {<filename>|off}[,<CPU>[,[noloop|logerror][,<action>]]]\n" + "\n" + "Starts or stops tracing for execution of the specified **<CPU>**, or the currently visible " + "CPU if no CPU is specified. When a subroutine call is encountered, tracing will skip over " + "the subroutine. The same algorithm is used as is used in the step over command. It will " + "not work properly with recursive functions, or if the return address does not immediately " + "follow the call instruction.\n" + "\n" + "This command accepts the same parameters as the 'trace' command. Please refer to the " + "corresponding section for a detailed description of options and more examples.\n" "\n" "Examples:\n" "\n" "traceover joust.tr\n" - " Begin tracing the currently active CPU, logging output to joust.tr.\n" + " Begin tracing the execution of the currently visible CPU, logging output to joust.tr.\n" "\n" - "traceover dribling.tr,0\n" - " Begin tracing the execution of CPU #0, logging output to dribling.tr.\n" + "traceover dribling.tr,maincpu\n" + " Begin tracing the execution of the CPU ':maincpu', logging output to dribling.tr.\n" "\n" - "traceover starswep.tr,0,false\n" - " Begin tracing the execution of CPU #0, logging output to starswep.tr, with loop detection disabled.\n" + "traceover starswep.tr,,noloop\n" + " Begin tracing the execution of the currently visible CPU, logging output to starswep.tr, " + "with loop detection disabled.\n" "\n" "traceover off,0\n" " Turn off tracing on CPU #0.\n" "\n" - "traceover asteroid.tr,0,true,{tracelog \"A=%02X \",a}\n" - " Begin tracing the execution of CPU #0, logging output to asteroid.tr. Before each line, " - "output A=<aval> to the tracelog.\n" + "traceover asteroid.tr,,,{tracelog \"A=%02X \",a}\n" + " Begin tracing the execution of the currently visible CPU, logging output to " + "asteroid.tr. Before each line, output A=<aval> to the trace log.\n" }, { "traceflush", "\n" " traceflush\n" "\n" - "Flushes all open trace files.\n" + "Flushes all open trace log files to disk.\n" + "\n" + "Example:\n" + "\n" + "traceflush\n" + " Flush trace log files.\n" }, { "bpset", "\n" - " bp[set] <address>[,<condition>[,<action>]]\n" + " bp[set] <address>[:<CPU>][,<condition>[,<action>]]\n" "\n" - "Sets a new execution breakpoint at the specified <address>. The optional <condition> " - "parameter lets you specify an expression that will be evaluated each time the breakpoint is " - "hit. If the result of the expression is true (non-zero), the breakpoint will actually halt " - "execution; otherwise, execution will continue with no notification. The optional <action> " - "parameter provides a command that is executed whenever the breakpoint is hit and the " - "<condition> is true. Note that you may need to embed the action within braces { } in order " - "to prevent commas and semicolons from being interpreted as applying to the bpset command " - "itself. Each breakpoint that is set is assigned an index which can be used in other " - "breakpoint commands to reference this breakpoint.\n" + "Sets a new execution breakpoint at the specified <address>. The <address> may optionally " + "be followed by a colon and a tag or debugger CPU number to specify a CPU explicitly. If " + "no CPU is specified, the CPU currently visible in the debugger is assumed. The optional " + "<condition> parameter lets you specify an expression that will be evaluated each time the " + "breakpoint is hit. If the result of the expression is true (non-zero), the breakpoint " + "will halt execution; otherwise, execution will continue with no notification. The " + "optional <action> parameter provides a command that is executed whenever the breakpoint is " + "hit and the <condition> is true. Note that you may need to embed the action within braces " + "{ } in order to prevent commas and semicolons from being interpreted as applying to the " + "'bpset' command itself.\n" + "\n" + "Each breakpoint that is set is assigned an index which can be used to refer to it in other " + "breakpoint commands.\n" "\n" "Examples:\n" "\n" @@ -968,31 +1293,32 @@ static const help_item static_help_list[] = " Set a breakpoint that will halt execution whenever the PC is equal to 23456 AND the " "expression (a0 == 0 && a1 == 0) is true.\n" "\n" - "bp 3456,1,{printf \"A0=%08X\\n\",a0; g}\n" - " Set a breakpoint that will halt execution whenever the PC is equal to 3456. When " - "this happens, print A0=<a0val> and continue executing.\n" + "bp 3456:audiocpu,1,{ printf \"A0=%08X\\n\",a0 ; g }\n" + " Set a breakpoint on the CPU ':audiocpu' that will halt execution whenever the PC is " + "equal to 3456. When this happens, print A0=<a0val> and continue executing.\n" "\n" - "bp 45678,a0==100,{a0 = ff; g}\n" - " Set a breakpoint that will halt execution whenever the PC is equal to 45678 AND the " - "expression (a0 == 100) is true. When that happens, set a0 to ff and resume execution.\n" + "bp 45678:2,a0==100,{ a0 = ff ; g }\n" + " Set a breakpoint on CPU #2 that will halt execution whenever the PC is equal to 45678 " + "and the expression (a0 == 100) is true. When that happens, set a0 to ff and resume " + "execution.\n" "\n" - "temp0 = 0; bp 567890,++temp0 >= 10\n" - " Set a breakpoint that will halt execution whenever the PC is equal to 567890 AND the " - "expression (++temp0 >= 10) is true. This effectively breaks only after the breakpoint " + "temp0 = 0 ; bp 567890,++temp0 >= 10\n" + " Set a breakpoint that will halt execution whenever the PC is equal to 567890 and the " + "expression (++temp0 >= 10) is true. This effectively breaks only after the breakpoint " "has been hit 16 times.\n" }, { "bpclear", "\n" - " bpclear [<bpnum>]\n" + " bpclear [<bpnum>[,...]]\n" "\n" - "The bpclear command clears a breakpoint. If <bpnum> is specified, only the requested " - "breakpoint is cleared, otherwise all breakpoints are cleared.\n" + "The bpclear command clears breakpoints. If <bpnum> is specified, only the requested " + "breakpoints are cleared; otherwise all breakpoints are cleared.\n" "\n" "Examples:\n" "\n" "bpclear 3\n" - " Clear breakpoint index 3.\n" + " Clear the breakpoint with index 3.\n" "\n" "bpclear\n" " Clear all breakpoints.\n" @@ -1000,16 +1326,16 @@ static const help_item static_help_list[] = { "bpdisable", "\n" - " bpdisable [<bpnum>]\n" + " bpdisable [<bpnum>,[...]]\n" "\n" - "The bpdisable command disables a breakpoint. If <bpnum> is specified, only the requested " - "breakpoint is disabled, otherwise all breakpoints are disabled. Note that disabling a " + "The bpdisable command disables breakpoints. If <bpnum> is specified, only the requested " + "breakpoints are disabled; otherwise all breakpoints are disabled. Note that disabling a " "breakpoint does not delete it, it just temporarily marks the breakpoint as inactive.\n" "\n" "Examples:\n" "\n" "bpdisable 3\n" - " Disable breakpoint index 3.\n" + " Disable the breakpoint with index 3.\n" "\n" "bpdisable\n" " Disable all breakpoints.\n" @@ -1017,15 +1343,15 @@ static const help_item static_help_list[] = { "bpenable", "\n" - " bpenable [<bpnum>]\n" + " bpenable [<bpnum>,[...]]\n" "\n" - "The bpenable command enables a breakpoint. If <bpnum> is specified, only the requested " - "breakpoint is enabled, otherwise all breakpoints are enabled.\n" + "The bpenable command enable breakpoints. If <bpnum> is specified, only the requested " + "breakpoints enabled; otherwise all breakpoints are enabled.\n" "\n" "Examples:\n" "\n" "bpenable 3\n" - " Enable breakpoint index 3.\n" + " Enable the breakpoint with index 3.\n" "\n" "bpenable\n" " Enable all breakpoints.\n" @@ -1033,35 +1359,55 @@ static const help_item static_help_list[] = { "bplist", "\n" - " bplist\n" + " bplist [<CPU>]\n" "\n" - "The bplist command lists all the current breakpoints, along with their index and any " - "conditions or actions attached to them.\n" - }, - { - "wpset", + "The bplist list current breakpoints, along with their indices and any associated " + "conditions or actions. If no <CPU> is specified, breakpoints for all CPUs in the system " + "will be listed; if a <CPU> is specified, only breakpoints for that CPU will be listed. " + "The <CPU> can be specified by tag or by debugger CPU number.\n" "\n" - " wp[{d|i}][set] <address>,<length>,<type>[,<condition>[,<action>]]\n" + "Examples:\n" + "\n" + "bplist\n" + " List all breakpoints.\n" "\n" - "Sets a new watchpoint starting at the specified <address> and extending for <length>. The " - "inclusive range of the watchpoint is <address> through <address> + <length> - 1. The 'wpset' " - "command sets a watchpoint on program memory; the 'wpdset' command sets a watchpoint on data " - "memory; and the 'wpiset' sets a watchpoint on I/O memory. The <type> parameter specifies " - "which sort of accesses to trap on. It can be one of three values: 'r' for a read watchpoint " - "'w' for a write watchpoint, and 'rw' for a read/write watchpoint.\n" + "bplist .\n" + " List all breakpoints for the visible CPU.\n" "\n" - "The optional <condition> parameter lets you specify an expression that will be evaluated each " - "time the watchpoint is hit. If the result of the expression is true (non-zero), the watchpoint " - "will actually halt execution; otherwise, execution will continue with no notification. The " - "optional <action> parameter provides a command that is executed whenever the watchpoint is hit " - "and the <condition> is true. Note that you may need to embed the action within braces { } in " - "order to prevent commas and semicolons from being interpreted as applying to the wpset command " - "itself. Each watchpoint that is set is assigned an index which can be used in other " - "watchpoint commands to reference this watchpoint.\n" + "bplist maincpu\n" + " List all breakpoints for the CPU ':maincpu'.\n" + }, + { + "wpset", "\n" - "In order to help <condition> expressions, two variables are available. For all watchpoints, " - "the variable 'wpaddr' is set to the address that actually triggered the watchpoint. For write " - "watchpoints, the variable 'wpdata' is set to the data that is being written.\n" + " wp[{d|i|o}][set] <address>[:<space>],<length>,<type>[,<condition>[,<action>]]\n" + "\n" + "Sets a new watchpoint starting at the specified <address> and extending for <length>. The " + "inclusive range of the watchpoint is <address> through <address>+<length>-1. The " + "<address> may optionally be followed by a CPU and/or address space. The CPU may be " + "specified as a tag or a debugger CPU number; if no CPU is specified, the CPU currently " + "visible in the debugger is assumed. If an address space is not specified, the command " + "suffix sets the address space: 'wpset' defaults to the first address space exposed by the " + "CPU, 'wpdset' defaults to the data space, 'wpiset' defaults to the I/O space, and 'wposet' " + "defaults to the opcodes space. The <type> parameter specifies the access types to trap " + "on - it can be one of three values: 'r' for read accesses, 'w' for write accesses, or 'rw' " + "for both read and write accesses.\n" + "\n" + "The optional <condition> parameter lets you specify an expression that will be evaluated " + "each time the watchpoint is triggered. If the result of the expression is true " + "(non-zero), the watchpoint will halt execution; otherwise, execution will continue with no " + "notification. The optional <action> parameter provides a command that is executed " + "whenever the watchpoint is triggered and the <condition> is true. Note that you may need " + "to embed the action within braces { } in order to prevent commas and semicolons from being " + "interpreted as applying to the wpset command itself.\n" + "\n" + "Each watchpoint that is set is assigned an index which can be used to refer to it in other " + "watchpoint commands\n" + "\n" + "To make <condition> expressions more useful, two variables are available: for all " + "watchpoints, the variable 'wpaddr' is set to the access address that triggered the " + "watchpoint; for write watchpoints, the variable 'wpdata' is set to the data being " + "written.\n" "\n" "Examples:\n" "\n" @@ -1069,31 +1415,35 @@ static const help_item static_help_list[] = " Set a watchpoint that will halt execution whenever a read or write occurs in the address " "range 1234-1239 inclusive.\n" "\n" - "wp 23456,a,w,wpdata == 1\n" + "wp 23456:data,a,w,wpdata == 1\n" " Set a watchpoint that will halt execution whenever a write occurs in the address range " - "23456-2345f AND the data written is equal to 1.\n" + "23456-2345f of the data space and the data written is equal to 1.\n" "\n" - "wp 3456,20,r,1,{printf \"Read @ %08X\\n\",wpaddr; g}\n" - " Set a watchpoint that will halt execution whenever a read occurs in the address range " - "3456-3475. When this happens, print Read @ <wpaddr> and continue executing.\n" + "wp 3456:maincpu,20,r,1,{ printf \"Read @ %08X\\n\",wpaddr ; g }\n" + " Set a watchpoint on the CPU ':maincpu' that will halt execution whenever a read occurs " + "in the address range 3456-3475. When this happens, print Read @ <wpaddr> and continue " + "execution.\n" "\n" - "temp0 = 0; wp 45678,1,w,wpdata==f0,{temp0++; g}\n" - " Set a watchpoint that will halt execution whenever a write occurs to the address 45678 AND " - "the value being written is equal to f0. When that happens, increment the variable temp0 and " - "resume execution.\n" + "temp0 = 0 ; wp 45678,1,w,wpdata==f0,{ temp0++ ; g }\n" + " Set a watchpoint that will halt execution whenever a write occurs to the address 45678 " + "and the value being written is equal to f0. When that happens, increment the variable " + "temp0 and continue execution.\n" }, + { "wpdset", "#wpset" }, + { "wpiset", "#wpset" }, + { "wposet", "#wpset" }, { "wpclear", "\n" - " wpclear [<wpnum>]\n" + " wpclear [<wpnum>[,...]]\n" "\n" - "The wpclear command clears a watchpoint. If <wpnum> is specified, only the requested " - "watchpoint is cleared, otherwise all watchpoints are cleared.\n" + "The wpclear command clears watchpoints. If <wpnum> is specified, only the requested " + "watchpoints are cleared; otherwise all watchpoints are cleared.\n" "\n" "Examples:\n" "\n" "wpclear 3\n" - " Clear watchpoint index 3.\n" + " Clear the watchpoint with index 3.\n" "\n" "wpclear\n" " Clear all watchpoints.\n" @@ -1101,16 +1451,16 @@ static const help_item static_help_list[] = { "wpdisable", "\n" - " wpdisable [<wpnum>]\n" + " wpdisable [<wpnum>[,...]]\n" "\n" - "The wpdisable command disables a watchpoint. If <wpnum> is specified, only the requested " - "watchpoint is disabled, otherwise all watchpoints are disabled. Note that disabling a " + "The wpdisable command disables watchpoints. If <wpnum> is specified, only the requested " + "watchpoints are disabled; otherwise all watchpoints are disabled. Note that disabling a " "watchpoint does not delete it, it just temporarily marks the watchpoint as inactive.\n" "\n" "Examples:\n" "\n" "wpdisable 3\n" - " Disable watchpoint index 3.\n" + " Disable the watchpoint with index 3.\n" "\n" "wpdisable\n" " Disable all watchpoints.\n" @@ -1118,15 +1468,15 @@ static const help_item static_help_list[] = { "wpenable", "\n" - " wpenable [<wpnum>]\n" + " wpenable [<wpnum>[,...]]\n" "\n" - "The wpenable command enables a watchpoint. If <wpnum> is specified, only the requested " - "watchpoint is enabled, otherwise all watchpoints are enabled.\n" + "The wpenable command enables watchpoints. If <wpnum> is specified, only the requested " + "watchpoints are enabled; otherwise all watchpoints are enabled.\n" "\n" "Examples:\n" "\n" "wpenable 3\n" - " Enable watchpoint index 3.\n" + " Enable the watchpoint with index 3.\n" "\n" "wpenable\n" " Enable all watchpoints.\n" @@ -1134,64 +1484,47 @@ static const help_item static_help_list[] = { "wplist", "\n" - " wplist\n" + " wplist [<CPU>]\n" "\n" - "The wplist command lists all the current watchpoints, along with their index and any " - "conditions or actions attached to them.\n" - }, - { - "hotspot", - "\n" - " hotspot [<CPU>,[<depth>[,<hits>]]]\n" - "\n" - "The hotspot command attempts to help locate hotspots in the code where speedup opportunities " - "might be present. <CPU>, which defaults to the currently active CPU, specified which " - "processor's memory to track. <depth>, which defaults to 64, controls the depth of the search " - "buffer. The search buffer tracks the last <depth> memory reads from unique PCs. The <hits> " - "parameter, which defaults to 250, specifies the minimum number of hits to report.\n" - "\n" - "The basic theory of operation is like this: each memory read is trapped by the debugger and " - "logged in the search buffer according to the address which was read and the PC that executed " - "the opcode. If the search buffer already contains a matching entry, that entry's count is " - "incremented and the entry is moved to the top of the list. If the search buffer does not " - "contain a matching entry, the entry from the bottom of the list is removed, and a new entry " - "is created at the top with an initial count of 1. Entries which fall off the bottom are " - "examined and if their count is larger than <hits>, they are reported to the debugger " - "console.\n" + "The wplist list current watchpoints, along with their indices and any associated " + "conditions or actions. If no <CPU> is specified, watchpoints for all CPUs in the system " + "will be listed; if a <CPU> is specified, only watchpoints for that CPU will be listed. " + "The <CPU> can be specified by tag or by debugger CPU number.\n" "\n" "Examples:\n" "\n" - "hotspot 0,10\n" - " Looks for hotspots on CPU 0 using a search buffer of 16 entries, reporting any entries which " - "end up with 250 or more hits.\n" + "wplist\n" + " List all watchpoints.\n" + "\n" + "wplist .\n" + " List all watchpoints for the visible CPU.\n" "\n" - "hotspot 1,40,#1000\n" - " Looks for hotspots on CPU 1 using a search buffer of 64 entries, reporting any entries which " - "end up with 1000 or more hits.\n" + "wplist maincpu\n" + " List all watchpoints for the CPU ':maincpu'.\n" }, { "rpset", "\n" - " rp[set] {<condition>}[,<action>]]\n" + " rp[set] <condition>[,<action>]\n" "\n" - "Sets a new registerpoint which will be triggered when <condition> is met. The condition must " - "be specified between curly braces to prevent the condition from being evaluated as an " - "assignment.\n" + "Sets a new registerpoint which will be triggered when <condition> is true (evaluates to a " + "non-zero value). The condition must be embedded in braces { } to prevent it from being " + "interpreted as an assignment.\n" "\n" - "The optional <action> parameter provides a command that is executed whenever the registerpoint " - "is hit. Note that you may need to embed the action within braces { } in " - "order to prevent commas and semicolons from being interpreted as applying to the rpset command " - "itself. Each registerpoint that is set is assigned an index which can be used in other " - "registerpoint commands to reference this registerpoint.\n" + "The optional <action> parameter provides a command that is executed whenever the " + "registerpoint is hit. Note that you may need to embed the action within braces { } in " + "order to prevent commas and semicolons from being interpreted as applying to the rpset " + "command itself. Each registerpoint that is set is assigned an index which can be used in " + "other registerpoint commands to reference this registerpoint.\n" "\n" "Examples:\n" "\n" - "rp {PC==0150}\n" - " Set a registerpoint that will halt execution whenever the PC register equals 0x150.\n" + "rp {PC==150}\n" + " Set a registerpoint that will halt execution whenever the PC register equals 150.\n" "\n" - "temp0=0; rp {PC==0150},{temp0++; g}\n" + "temp0=0; rp {PC==150},{temp0++; g}\n" " Set a registerpoint that will increment the variable temp0 whenever the PC register " - "equals 0x0150.\n" + "equals 150.\n" "\n" "rp {temp0==5}\n" " Set a registerpoint that will halt execution whenever the temp0 variable equals 5.\n" @@ -1199,15 +1532,15 @@ static const help_item static_help_list[] = { "rpclear", "\n" - " rpclear [<rpnum>]\n" + " rpclear [<rpnum>[,...]]\n" "\n" - "The rpclear command clears a registerpoint. If <rpnum> is specified, only the requested " - "registerpoint is cleared, otherwise all registerpoints are cleared.\n" + "The rpclear command clears registerpoints. If <rpnum> is specified, only the requested " + "registerpoints are cleared, otherwise all registerpoints are cleared.\n" "\n" "Examples:\n" "\n" "rpclear 3\n" - " Clear registerpoint index 3.\n" + " Clear the registerpoint with index 3.\n" "\n" "rpclear\n" " Clear all registerpoints.\n" @@ -1215,16 +1548,17 @@ static const help_item static_help_list[] = { "rpdisable", "\n" - " rpdisable [<rpnum>]\n" + " rpdisable [<rpnum>[,...]]\n" "\n" - "The rpdisable command disables a registerpoint. If <rpnum> is specified, only the requested " - "registerpoint is disabled, otherwise all registerpoints are disabled. Note that disabling a " - "registerpoint does not delete it, it just temporarily marks the registerpoint as inactive.\n" + "The rpdisable command disables registerpoints. If <rpnum> is specified, only the " + "requested registerpoints are disabled, otherwise all registerpoints are disabled. Note " + "that disabling a registerpoint does not delete it, it just temporarily marks the " + "registerpoint as inactive.\n" "\n" "Examples:\n" "\n" "rpdisable 3\n" - " Disable registerpoint index 3.\n" + " Disable the registerpoint with index 3.\n" "\n" "rpdisable\n" " Disable all registerpoints.\n" @@ -1232,15 +1566,15 @@ static const help_item static_help_list[] = { "rpenable", "\n" - " rpenable [<rpnum>]\n" + " rpenable [<rpnum>[,...]]\n" "\n" - "The rpenable command enables a registerpoint. If <rpnum> is specified, only the requested " - "registerpoint is enabled, otherwise all registerpoints are enabled.\n" + "The rpenable command enables registerpoints. If <rpnum> is specified, only the requested " + "registerpoints are enabled, otherwise all registerpoints are enabled.\n" "\n" "Examples:\n" "\n" "rpenable 3\n" - " Enable registerpoint index 3.\n" + " Enable the registerpoint with index 3.\n" "\n" "rpenable\n" " Enable all registerpoints.\n" @@ -1254,33 +1588,141 @@ static const help_item static_help_list[] = "actions attached to them.\n" }, { + "epset", + "\n" + " ep[set] <type>[,<condition>[,<action>]]\n" + "\n" + "Sets a new exceptionpoint for exceptions of type <type> on the currently visible CPU. " + "The optional <condition> parameter lets you specify an expression that will be evaluated " + "each time the exceptionpoint is hit. If the result of the expression is true (non-zero), " + "the exceptionpoint will actually halt execution at the start of the exception handler; " + "otherwise, execution will continue with no notification. The optional <action> parameter " + "provides a command that is executed whenever the exceptionpoint is hit and the " + "<condition> is true. Note that you may need to embed the action within braces { } in order " + "to prevent commas and semicolons from being interpreted as applying to the epset command " + "itself.\n" + "\n" + "The numbering of exceptions depends upon the CPU type. Causes of exceptions may include " + "internally or externally vectored interrupts, errors occurring within instructions and " + "system calls.\n" + "\n" + "Each exceptionpoint that is set is assigned an index which can be used in other " + "exceptionpoint commands to reference this exceptionpoint.\n" + "\n" + "Examples:\n" + "\n" + "ep 2\n" + " Set an exception that will halt execution whenever the visible CPU raises exception " + "number 2.\n" + }, + { + "epclear", + "\n" + " epclear [<epnum>[,...]]\n" + "\n" + "The epclear command clears exceptionpoints. If <epnum> is specified, only the requested " + "exceptionpoints are cleared, otherwise all exceptionpoints are cleared.\n" + "\n" + "Examples:\n" + "\n" + "epclear 3\n" + " Clear exceptionpoint index 3.\n" + "\n" + "epclear\n" + " Clear all exceptionpoints.\n" + }, + { + "epdisable", + "\n" + " epdisable [<epnum>[,...]]\n" + "\n" + "The epdisable command disables exceptionpoints. If <epnum> is specified, only the requested " + "exceptionpoints are disabled, otherwise all exceptionpoints are disabled. Note that " + "disabling an exceptionpoint does not delete it, it just temporarily marks the " + "exceptionpoint as inactive.\n" + "\n" + "Examples:\n" + "\n" + "epdisable 3\n" + " Disable exceptionpoint index 3.\n" + "\n" + "epdisable\n" + " Disable all exceptionpoints.\n" + }, + { + "epenable", + "\n" + " epenable [<epnum>[,...]]\n" + "\n" + "The epenable command enables exceptionpoints. If <epnum> is specified, only the " + "requested exceptionpoints are enabled, otherwise all exceptionpoints are enabled.\n" + "\n" + "Examples:\n" + "\n" + "epenable 3\n" + " Enable exceptionpoint index 3.\n" + "\n" + "epenable\n" + " Enable all exceptionpoints.\n" + }, + { + "eplist", + "\n" + " eplist\n" + "\n" + "The eplist command lists all the current exceptionpoints, along with their index and " + "any conditions or actions attached to them.\n" + }, + { "map", "\n" - " map[{d|i}] <address>\n" + " map[{d|i|o}] <address>[:<space>]\n" "\n" - "The map/mapd/mapi commands map a logical address in memory to the correct physical address, as " - "well as specifying the bank. 'map' will map program space memory, while 'mapd' will map data space " - "memory and 'mapi' will map I/O space memory.\n" + "The map commands map a logical memory address to the corresponding physical address, as " + "well as reporting the handler name. The address may optionally be followed by a colon " + "and device and/or address space. The device may be specified as a tag or a debugger CPU " + "number; if no device is specified, the CPU currently visible in the debugger is assumed. " + "If an address space is not specified, the command suffix sets the address space: 'map' " + "defaults to the first address space exposed by the device, 'mapd' defaults to the data " + "space, 'mapi' defaults to the I/O space, and 'mapo' defaults to the opcodes space.\n" "\n" - "Example:\n" + "Examples:\n" "\n" "map 152d0\n" - " Gives physical address and bank for logical address 152d0 in program memory\n" + " Gives physical address and handler name for logical address 152d0 in program memory for " + "the currently visible CPU.\n" + "\n" + "map 107:sms_vdp\n" + " Gives physical address and handler name for logical address 107 in the first address " + "space for the device ':sms_vdp'.\n" }, + { "mapd", "#map" }, + { "mapi", "#map" }, + { "mapo", "#map" }, { "memdump", "\n" - " memdump [<filename>]\n" + " memdump [<filename>,[<device>]]\n" "\n" - "Dumps the current memory map to <filename>. If <filename> is omitted, then dumps to memdump.log" + "Dumps the current memory maps to the file specified by <filename>, or memdump.log if " + "omitted. If <device> is specified, only memory maps for the part of the device tree " + "rooted at this device will be dumped. Devices may be specified using tags or CPU " + "numbers.\n" "\n" "Examples:\n" "\n" "memdump mylog.log\n" - " Dumps memory to mylog.log.\n" + " Dumps memory maps for all devices in the system to the file mylog.log.\n" "\n" "memdump\n" - " Dumps memory to memdump.log.\n" + " Dumps memory maps for all devices in the system to the file memdump.log.\n" + "\n" + "memdump audiomaps.log,audiopcb\n" + " Dumps memory maps for device ':audiopcb' and all its child devices to the file " + "audiomaps.log.\n" + "\n" + "memdump mylog.log,1\n" + " Dumps memory maps for the CPU 1 and all its child devices to the file mylog.log.\n" }, { "comlist", @@ -1311,6 +1753,7 @@ static const help_item static_help_list[] = " Adds the comment 'undocumented opcode!' to the code at address 0x10\n" "\n" }, + { "//", "#comadd" }, { "commit", "\n" @@ -1322,10 +1765,10 @@ static const help_item static_help_list[] = "Examples:\n" "\n" "commit 0, hello world.\n" - " Adds the comment 'hello world.' to the code at address 0x0\n" + " Adds the comment 'hello world.' to the code at address 0x0 and saves comments\n" "\n" "/* 10, undocumented opcode!\n" - " Adds the comment 'undocumented opcode!' to the code at address 0x10\n" + " Adds the comment 'undocumented opcode!' to the code at address 0x10 and saves comments\n" "\n" }, { @@ -1358,240 +1801,322 @@ static const help_item static_help_list[] = { "cheatinit", "\n" - " cheatinit [<sign><width><swap>,[<address>,<length>[,<CPU>]]]\n" + " cheatinit [[<sign>[<width>[<swap>]]],[<address>,<length>[,<space>]]]\n" "\n" - "The cheatinit command initializes the cheat search to the selected memory area.\n" - "If no parameter is specified the cheat search is initialized to all changeable memory of the main CPU.\n" - "<sign> can be s(signed) or u(unsigned)\n" - "<width> can be b(8 bit), w(16 bit), d(32 bit) or q(64 bit)\n" - "<swap> append s for swapped search\n" + "The cheatinit command initializes the cheat search to writable RAM areas in the specified " + "address space. The device may be specified as a tag or a debugger CPU number; if no " + "device is specified, the CPU currently visible in the debugger is assumed. If an address " + "space is not specified, the first address space exposed by the device is used.\n" + "\n" + "The first argument specifies the data format to search for:\n" + " <sign>: 's' (signed), 'u' (unsigned)\n" + " <width>: 'b' (8-bit), 'w' (16-bit), 'd' (32-bit), 'q' (64-bit)\n" + " <swap>: 's' (reverse byte order)\n" + "If the first argument is omitted or empty, the format from the previous cheat search is " + "used, or unsigned 8-bit for the first cheat search.\n" + "\n" + "The <address> specifies the address to start searching from, and the <length> specifies " + "how much memory to search. If specified, writable RAM in the range <address> through " + "<address>+<length>-1, inclusive, will be searched; otherwise, all writable RAM in the " + "address space will be searched.\n" "\n" "Examples:\n" "\n" "cheatinit ub,0x1000,0x10\n" - " Initialize the cheat search from 0x1000 to 0x1010 of the first CPU.\n" + " Initialize the cheat search for unsigned 8-bit values in addresses 0x1000-0x100f in the " + "program space of the visible CPU.\n" "\n" "cheatinit sw,0x2000,0x1000,1\n" - " Initialize the cheat search with width of 2 byte in signed mode from 0x2000 to 0x3000 of the second CPU.\n" + " Initialize the cheat search for signed 16-bit values in addresses 0x2000-0x3000 in the " + "program space of CPU #1.\n" "\n" "cheatinit uds,0x0000,0x1000\n" - " Initialize the cheat search with width of 4 byte swapped from 0x0000 to 0x1000.\n" + " Initialize the cheat search for unsigned 32-bit values with reverse byte order in " + "addresses 0x0000-0x0fff of the program space of the visible CPU.\n" }, { "cheatrange", "\n" " cheatrange <address>,<length>\n" "\n" - "The cheatrange command adds the selected memory area to the cheat search.\n" - "Before using cheatrange it is necessary to initialize the cheat search with cheatinit.\n" + "The cheatrange command adds writable RAM areas in the range <address> through " + "through <address>+<length>-1, inclusive, to the cheat search. Before using cheatrange, " + "the cheatinit command must be used to initialize the cheat search.\n" "\n" "Examples:\n" "\n" "cheatrange 0x1000,0x10\n" - " Add the bytes from 0x1000 to 0x1010 to the cheat search.\n" + " Add addresses 0x1000-0x100f to the cheat search.\n" }, { "cheatnext", "\n" " cheatnext <condition>[,<comparisonvalue>]\n" "\n" - "The cheatnext command will make comparisons with the last search matches.\n" + "The cheatnext command makes comparisons with the previous search matches.\n" "Possible <condition>:\n" " all\n" - " no <comparisonvalue> needed.\n" - " use to update the last value without changing the current matches.\n" - " equal [eq]\n" - " without <comparisonvalue> search for all bytes that are equal to the last search.\n" - " with <comparisonvalue> search for all bytes that are equal to the <comparisonvalue>.\n" - " notequal [ne]\n" - " without <comparisonvalue> search for all bytes that are not equal to the last search.\n" - " with <comparisonvalue> search for all bytes that are not equal to the <comparisonvalue>.\n" - " decrease [de, +]\n" - " without <comparisonvalue> search for all bytes that have decreased since the last search.\n" - " with <comparisonvalue> search for all bytes that have decreased by the <comparisonvalue> since the last search.\n" - " increase [in, -]\n" - " without <comparisonvalue> search for all bytes that have increased since the last search.\n" - " with <comparisonvalue> search for all bytes that have increased by the <comparisonvalue> since the last search.\n" - " decreaseorequal [deeq]\n" - " no <comparisonvalue> needed.\n" - " search for all bytes that have decreased or have same value since the last search.\n" - " increaseorequal [ineq]\n" - " no <comparisonvalue> needed.\n" - " search for all bytes that have decreased or have same value since the last search.\n" - " smallerof [lt]\n" - " without <comparisonvalue> this condition is invalid\n" - " with <comparisonvalue> search for all bytes that are smaller than the <comparisonvalue>.\n" - " greaterof [gt]\n" - " without <comparisonvalue> this condition is invalid\n" - " with <comparisonvalue> search for all bytes that are larger than the <comparisonvalue>.\n" - " changedby [ch, ~]\n" - " without <comparisonvalue> this condition is invalid\n" - " with <comparisonvalue> search for all bytes that have changed by the <comparisonvalue> since the last search.\n" + " Use to update the last value without changing the current matches.\n" + " The <comparisonvalue> is not used.\n" + " equal (eq)\n" + " Without <comparisonvalue>**, search for values that are equal to the previous " + "search.\n" + " With <comparisonvalue>, search for values that are equal to the <comparisonvalue>.\n" + " notequal (ne)\n" + " Without <comparisonvalue>, search for values that are not equal to the previous " + "search.\n" + " With <comparisonvalue>, search for values that are not equal to the <comparisonvalue>.\n" + " decrease (de, -)\n" + " Without <comparisonvalue>, search for values that have decreased since the previous " + "search.\n" + " With <comparisonvalue>, search for values that have decreased by the <comparisonvalue> " + "since the previous search.\n" + " increase (in, +)\n" + " Without <comparisonvalue>, search for values that have increased since the previous " + "search.\n" + " With <comparisonvalue>, search for values that have increased by the <comparisonvalue> " + "since the previous search.\n" + " decreaseorequal (deeq)\n" + " Search for values that have decreased or are unchanged since the previous search.\n" + " The <comparisonvalue> is not used.\n" + " increaseorequal (ineq)\n" + " Search for values that have increased or are unchanged since the previous search.\n" + " The <comparisonvalue> is not used.\n" + " smallerof (lt, <)\n" + " Search for values that are less than the <comparisonvalue>.\n" + " The <comparisonvalue> is required.\n" + " greaterof (gt, >)\n" + " Search for values that are greater than the <comparisonvalue>.\n" + " The <comparisonvalue> is required.\n" + " changedby (ch, ~)\n" + " Search for values that have changed by the <comparisonvalue> since the previous " + "search.\n" + " The <comparisonvalue> is required.\n" "\n" "Examples:\n" "\n" "cheatnext increase\n" - " search for all bytes that have increased since the last search.\n" + " Search for all values that have increased since the previous search.\n" "\n" - "cheatnext decrease, 1\n" - " search for all bytes that have decreased by 1 since the last search.\n" + "cheatnext decrease,1\n" + " Search for all values that have decreased by 1 since the previous search.\n" }, { "cheatnextf", "\n" " cheatnextf <condition>[,<comparisonvalue>]\n" "\n" - "The cheatnextf command will make comparisons with the initial search.\n" + "The cheatnextf command makes comparisons with the initial search matches.\n" "Possible <condition>:\n" " all\n" - " no <comparisonvalue> needed.\n" - " use to update the last value without changing the current matches.\n" - " equal [eq]\n" - " without <comparisonvalue> search for all bytes that are equal to the initial search.\n" - " with <comparisonvalue> search for all bytes that are equal to the <comparisonvalue>.\n" - " notequal [ne]\n" - " without <comparisonvalue> search for all bytes that are not equal to the initial search.\n" - " with <comparisonvalue> search for all bytes that are not equal to the <comparisonvalue>.\n" - " decrease [de, +]\n" - " without <comparisonvalue> search for all bytes that have decreased since the initial search.\n" - " with <comparisonvalue> search for all bytes that have decreased by the <comparisonvalue> since the initial search.\n" - " increase [in, -]\n" - " without <comparisonvalue> search for all bytes that have increased since the initial search.\n" - " with <comparisonvalue> search for all bytes that have increased by the <comparisonvalue> since the initial search.\n" - " decreaseorequal [deeq]\n" - " no <comparisonvalue> needed.\n" - " search for all bytes that have decreased or have same value since the initial search.\n" - " increaseorequal [ineq]\n" - " no <comparisonvalue> needed.\n" - " search for all bytes that have decreased or have same value since the initial search.\n" - " smallerof [lt]\n" - " without <comparisonvalue> this condition is invalid.\n" - " with <comparisonvalue> search for all bytes that are smaller than the <comparisonvalue>.\n" - " greaterof [gt]\n" - " without <comparisonvalue> this condition is invalid.\n" - " with <comparisonvalue> search for all bytes that are larger than the <comparisonvalue>.\n" - " changedby [ch, ~]\n" - " without <comparisonvalue> this condition is invalid\n" - " with <comparisonvalue> search for all bytes that have changed by the <comparisonvalue> since the initial search.\n" + " Use to update the last value without changing the current matches.\n" + " The <comparisonvalue> is not used.\n" + " equal (eq)\n" + " Without <comparisonvalue>**, search for values that are equal to the initial search.\n" + " With <comparisonvalue>, search for values that are equal to the <comparisonvalue>.\n" + " notequal (ne)\n" + " Without <comparisonvalue>, search for values that are not equal to the initial search.\n" + " With <comparisonvalue>, search for values that are not equal to the <comparisonvalue>.\n" + " decrease (de, -)\n" + " Without <comparisonvalue>, search for values that have decreased since the initial " + "search.\n" + " With <comparisonvalue>, search for values that have decreased by the <comparisonvalue> " + "since the initial search.\n" + " increase (in, +)\n" + " Without <comparisonvalue>, search for values that have increased since the initial " + "search.\n" + " With <comparisonvalue>, search for values that have increased by the <comparisonvalue> " + "since the initial search.\n" + " decreaseorequal (deeq)\n" + " Search for values that have decreased or are unchanged since the initial search.\n" + " The <comparisonvalue> is not used.\n" + " increaseorequal (ineq)\n" + " Search for values that have increased or are unchanged since the initial search.\n" + " The <comparisonvalue> is not used.\n" + " smallerof (lt, <)\n" + " Search for values that are less than the <comparisonvalue>.\n" + " The <comparisonvalue> is required.\n" + " greaterof (gt, >)\n" + " Search for values that are greater than the <comparisonvalue>.\n" + " The <comparisonvalue> is required.\n" + " changedby (ch, ~)\n" + " Search for values that have changed by the <comparisonvalue> since the initial search.\n" + " The <comparisonvalue> is required.\n" "\n" "Examples:\n" "\n" "cheatnextf increase\n" - " search for all bytes that have increased since the initial search.\n" + " Search for all values that have increased since the initial search.\n" "\n" - "cheatnextf decrease, 1\n" - " search for all bytes that have decreased by 1 since the initial search.\n" + "cheatnextf decrease,1\n" + " Search for all values that have decreased by 1 since the initial search.\n" }, { "cheatlist", "\n" " cheatlist [<filename>]\n" "\n" - "Without <filename> show the list of matches in the debug console.\n" - "With <filename> save the list of matches in basic xml format to <filename>.\n" + "Without <filename>, show the current cheat matches in the debugger console; with " + "<filename>, save the current cheat matches in basic XML format to the specified file.\n" "\n" "Examples:\n" "\n" "cheatlist\n" - " Show the current matches in the debug console.\n" - "cheatlist cheat.txt\n" - " Save the current matches to cheat.txt.\n" + " Show the current matches in the console.\n" + "cheatlist cheat.xml\n" + " Save the current matches to cheat.xml in XML format.\n" }, { "cheatundo", "\n" " cheatundo\n" "\n" - "Undo the results of the last search.\n" - "The undo command has no effect on the last value.\n" + "Undo filtering of cheat candidates by the most recent cheatnext or cheatnextf command.\n" + "Note that the previous values ARE NOT rolled back.\n" "\n" "Examples:\n" "\n" "cheatundo\n" - " Undo the last search (state only).\n" + " Restore cheat candidates filtered out by the most recent cheatnext or cheatnextf " + "command.\n" }, { "images", "\n" " images\n" "\n" - "Used to display list of available image devices.\n" + "Lists the instance names for media images devices in the system and the currently mounted " + "media images, if any. Brief instance names, as allowed for command line media options, " + "are listed.\n" "\n" "Examples:\n" "\n" "images\n" - " Show list of devices and mounted files for current driver.\n" + " Lists image device instance names and mounted media.\n" }, { "mount", "\n" - " mount <device>,<filename>\n" + " mount <instance>,<filename>\n" "\n" - "Mount <filename> to image <device>.\n" - "<filename> can be softlist item or full path to file.\n" + "Mounts a file on a media device. The device may be specified by its instance name or " + "brief instance name, as allowed for command line media options.\n" + "\n" + "Some media devices allow software list items to be mounted using this command by supplying " + "the short name of the software list item in place of a filename for the <filename> " + "parameter.\n" "\n" "Examples:\n" "\n" - "mount cart,aladdin\n" - " Mounts softlist item aladdin on cart device.\n" + "mount flop1,os1xutls.td0\n" + " Mount the file 'os1xutls.td0' on the media device with instance name 'flop1'.\n" + "mount cart,10yard\n" + " Mount the software list item with short name '10yard' on the media device with instance " + "name 'cart'.\n" }, { "unmount", "\n" - " unmount <device>\n" + " unmount <instance>\n" "\n" - "Unmounts file from image <device>.\n" + "Unmounts the mounted media image (if any) from a device. The device may be specified by " + "its instance name or brief instance name, as allowed for command line media options.\n" "\n" "Examples:\n" "\n" "unmount cart\n" - " Unmounts any file mounted on device named cart.\n" + " Unmounts any media image mounted on the device with instance name 'cart'.\n" } }; /*************************************************************************** - CODE + HELP_MANAGER ***************************************************************************/ -const char *debug_get_help(const char *tag) +class help_manager { - static char ambig_message[1024]; - const help_item *found = nullptr; - int i, msglen, foundcount = 0; - int taglen = (int)strlen(tag); - char tagcopy[256]; +private: + using help_map = std::map<std::string_view, char const *>; - /* make a lowercase copy of the tag */ - for (i = 0; i <= taglen; i++) - tagcopy[i] = tolower(u8(tag[i])); + help_map m_help_list; + help_item const *m_uncached_help = std::begin(f_static_help_list); + + util::ovectorstream m_message_buffer; + + help_manager() = default; + +public: + std::string_view find(std::string_view tag) + { + // find a cached exact match if possible + std::string const lower = strmakelower(tag); + auto const found = m_help_list.find(lower); + if (m_help_list.end() != found) + return found->second; + + // cache more entries while searching for an exact match + while (std::end(f_static_help_list) != m_uncached_help) + { + help_map::iterator ins; + if (*m_uncached_help->help == '#') + { + auto const xref = m_help_list.find(&m_uncached_help->help[1]); + assert(m_help_list.end() != xref); + ins = m_help_list.emplace(m_uncached_help->tag, xref->second).first; + } + else + { + ins = m_help_list.emplace(m_uncached_help->tag, m_uncached_help->help).first; + } + ++m_uncached_help; + if (lower == ins->first) + return ins->second; + } - /* find a match */ - for (i = 0; i < ARRAY_LENGTH(static_help_list); i++) - if (!strncmp(static_help_list[i].tag, tagcopy, taglen)) + // find a partial match + auto candidate = m_help_list.lower_bound(lower); + if ((m_help_list.end() != candidate) && (candidate->first.substr(0, lower.length()) == lower)) { - foundcount++; - found = &static_help_list[i]; - if (strlen(found->tag) == taglen) + // if only one partial match, take it + auto const next = std::next(candidate); + if ((m_help_list.end() == next) || (next->first.substr(0, lower.length()) != lower)) + return candidate->second; + + m_message_buffer.clear(); + m_message_buffer.reserve(1024); + m_message_buffer.seekp(0, util::ovectorstream::beg); + m_message_buffer << "Ambiguous help request, did you mean:\n"; + do { - foundcount = 1; - break; + util::stream_format(m_message_buffer, " help %.*s?\n", int(candidate->first.length()), &candidate->first[0]); + ++candidate; } + while ((m_help_list.end() != candidate) && (candidate->first.substr(0, lower.length()) == lower)); + return util::buf_to_string_view(m_message_buffer); } - /* only a single match makes sense */ - if (foundcount == 1) - return found->help; + // take the first help entry if no matches at all + return f_static_help_list[0].help; + } - /* if not found, return the first entry */ - if (foundcount == 0) - return static_help_list[0].help; + static help_manager &instance() + { + static help_manager s_instance; + return s_instance; + } +}; - /* otherwise, indicate ambiguous help */ - msglen = sprintf(ambig_message, "Ambiguous help request, did you mean:\n"); - for (i = 0; i < ARRAY_LENGTH(static_help_list); i++) - if (!strncmp(static_help_list[i].tag, tagcopy, taglen)) - msglen += sprintf(&ambig_message[msglen], " help %s?\n", static_help_list[i].tag); - return ambig_message; +} // anonymous namespace + + + +/*************************************************************************** + PUBLIC INTERFACE +***************************************************************************/ + +std::string_view debug_get_help(std::string_view tag) +{ + return help_manager::instance().find(tag); } diff --git a/src/emu/debug/debughlp.h b/src/emu/debug/debughlp.h index 30cf6d5820f..9efe12acaca 100644 --- a/src/emu/debug/debughlp.h +++ b/src/emu/debug/debughlp.h @@ -13,12 +13,14 @@ #pragma once +#include <string_view> + /*************************************************************************** FUNCTION PROTOTYPES ***************************************************************************/ -/* help management */ -const char * debug_get_help(const char *tag); +// help management +std::string_view debug_get_help(std::string_view tag); #endif // MAME_EMU_DEBUG_DEBUGHLP_H diff --git a/src/emu/debug/debugvw.cpp b/src/emu/debug/debugvw.cpp index f351896480e..7412ddd2f60 100644 --- a/src/emu/debug/debugvw.cpp +++ b/src/emu/debug/debugvw.cpp @@ -2,24 +2,29 @@ // copyright-holders:Aaron Giles /********************************************************************* - debugvw.c + debugvw.cpp Debugger view engine. ***************************************************************************/ #include "emu.h" -#include "express.h" #include "debugvw.h" -#include "dvtext.h" -#include "dvstate.h" + +#include "debugcpu.h" +#include "dvbpoints.h" #include "dvdisasm.h" +#include "dvepoints.h" #include "dvmemory.h" -#include "dvbpoints.h" +#include "dvrpoints.h" +#include "dvstate.h" +#include "dvtext.h" #include "dvwpoints.h" -#include "debugcpu.h" +#include "express.h" + #include "debugger.h" -#include <ctype.h> + +#include <cctype> @@ -31,9 +36,8 @@ // debug_view_source - constructor //------------------------------------------------- -debug_view_source::debug_view_source(const char *name, device_t *device) - : m_next(nullptr), - m_name(name), +debug_view_source::debug_view_source(std::string &&name, device_t *device) + : m_name(std::move(name)), m_device(device) { } @@ -232,10 +236,10 @@ void debug_view::set_source(const debug_view_source &source) const debug_view_source *debug_view::source_for_device(device_t *device) const { - for (debug_view_source &source : m_source_list) - if (device == source.device()) - return &source; - return m_source_list.first(); + for (auto &source : m_source_list) + if (device == source->device()) + return source.get(); + return first_source(); } @@ -251,6 +255,7 @@ void debug_view::adjust_visible_x_for_cursor() m_topleft.x = m_cursor.x; else if (m_cursor.x >= m_topleft.x + m_visible.x - 1) m_topleft.x = m_cursor.x - m_visible.x + 2; + m_topleft.x = (std::max)((std::min)(m_topleft.x, m_total.x - m_visible.x), 0); } @@ -266,6 +271,7 @@ void debug_view::adjust_visible_y_for_cursor() m_topleft.y = m_cursor.y; else if (m_cursor.y >= m_topleft.y + m_visible.y - 1) m_topleft.y = m_cursor.y - m_visible.y + 2; + m_topleft.y = (std::max)((std::min)(m_topleft.y, m_total.y - m_visible.y), 0); } @@ -327,7 +333,7 @@ debug_view_manager::~debug_view_manager() { debug_view *oldhead = m_viewlist; m_viewlist = oldhead->m_next; - global_free(oldhead); + delete oldhead; } } @@ -341,25 +347,31 @@ debug_view *debug_view_manager::alloc_view(debug_view_type type, debug_view_osd_ switch (type) { case DVT_CONSOLE: - return append(global_alloc(debug_view_console(machine(), osdupdate, osdprivate))); + return append(new debug_view_console(machine(), osdupdate, osdprivate)); case DVT_STATE: - return append(global_alloc(debug_view_state(machine(), osdupdate, osdprivate))); + return append(new debug_view_state(machine(), osdupdate, osdprivate)); case DVT_DISASSEMBLY: - return append(global_alloc(debug_view_disasm(machine(), osdupdate, osdprivate))); + return append(new debug_view_disasm(machine(), osdupdate, osdprivate)); case DVT_MEMORY: - return append(global_alloc(debug_view_memory(machine(), osdupdate, osdprivate))); + return append(new debug_view_memory(machine(), osdupdate, osdprivate)); case DVT_LOG: - return append(global_alloc(debug_view_log(machine(), osdupdate, osdprivate))); + return append(new debug_view_log(machine(), osdupdate, osdprivate)); case DVT_BREAK_POINTS: - return append(global_alloc(debug_view_breakpoints(machine(), osdupdate, osdprivate))); + return append(new debug_view_breakpoints(machine(), osdupdate, osdprivate)); case DVT_WATCH_POINTS: - return append(global_alloc(debug_view_watchpoints(machine(), osdupdate, osdprivate))); + return append(new debug_view_watchpoints(machine(), osdupdate, osdprivate)); + + case DVT_REGISTER_POINTS: + return append(new debug_view_registerpoints(machine(), osdupdate, osdprivate)); + + case DVT_EXCEPTION_POINTS: + return append(new debug_view_exceptionpoints(machine(), osdupdate, osdprivate)); default: fatalerror("Attempt to create invalid debug view type %d\n", type); @@ -379,7 +391,7 @@ void debug_view_manager::free_view(debug_view &view) if (*viewptr == &view) { *viewptr = view.m_next; - global_free(&view); + delete &view; break; } } @@ -450,7 +462,7 @@ debug_view_expression::debug_view_expression(running_machine &machine) : m_machine(machine) , m_dirty(true) , m_result(0) - , m_parsed(machine.debugger().cpu().get_global_symtable()) + , m_parsed(machine.debugger().cpu().global_symtable()) , m_string("0") { } @@ -472,7 +484,10 @@ debug_view_expression::~debug_view_expression() void debug_view_expression::set_context(symbol_table *context) { - m_parsed.set_symbols((context != nullptr) ? context : m_machine.debugger().cpu().get_global_symtable()); + if (context != nullptr) + m_parsed.set_symbols(*context); + else + m_parsed.set_symbols(m_machine.debugger().cpu().global_symtable()); m_dirty = true; } @@ -492,11 +507,11 @@ bool debug_view_expression::recompute() std::string oldstring(m_parsed.original_string()); try { - m_parsed.parse(m_string.c_str()); + m_parsed.parse(m_string); } catch (expression_error &) { - m_parsed.parse(oldstring.c_str()); + m_parsed.parse(oldstring); } } diff --git a/src/emu/debug/debugvw.h b/src/emu/debug/debugvw.h index 374af7f384e..64bd5dac10a 100644 --- a/src/emu/debug/debugvw.h +++ b/src/emu/debug/debugvw.h @@ -13,6 +13,12 @@ #include "express.h" +#include <algorithm> +#include <iterator> +#include <memory> +#include <string> +#include <vector> + //************************************************************************** // CONSTANTS @@ -28,7 +34,9 @@ enum debug_view_type DVT_MEMORY, DVT_LOG, DVT_BREAK_POINTS, - DVT_WATCH_POINTS + DVT_WATCH_POINTS, + DVT_REGISTER_POINTS, + DVT_EXCEPTION_POINTS }; @@ -107,23 +115,19 @@ class debug_view_source { DISABLE_COPYING(debug_view_source); - friend class simple_list<debug_view_source>; - public: // construction/destruction - debug_view_source(const char *name, device_t *device = nullptr); + debug_view_source(std::string &&name, device_t *device = nullptr); virtual ~debug_view_source(); // getters const char *name() const { return m_name.c_str(); } - debug_view_source *next() const { return m_next; } device_t *device() const { return m_device; } private: // internal state - debug_view_source * m_next; // link to next item - std::string m_name; // name of the source item - device_t * m_device; // associated device (if applicable) + std::string const m_name; // name of the source item + device_t *const m_device; // associated device (if applicable) }; @@ -149,9 +153,16 @@ public: debug_view_xy cursor_position() { flush_updates(); return m_cursor; } bool cursor_supported() { flush_updates(); return m_supports_cursor; } bool cursor_visible() { flush_updates(); return m_cursor_visible; } + size_t source_count() const { return m_source_list.size(); } const debug_view_source *source() const { return m_source; } - const debug_view_source *first_source() const { return m_source_list.first(); } - const simple_list<debug_view_source> &source_list() const { return m_source_list; } + const debug_view_source *source(unsigned i) const { return (m_source_list.size() > i) ? m_source_list[i].get() : nullptr; } + const debug_view_source *first_source() const { return m_source_list.empty() ? nullptr : m_source_list[0].get(); } + auto source_index(const debug_view_source &source) const + { + const auto it(std::find_if(m_source_list.begin(), m_source_list.end(), [&source] (const auto &x) { return x.get() == &source; })); + return (m_source_list.end() != it) ? std::distance(m_source_list.begin(), it) : -1; + } + const std::vector<std::unique_ptr<const debug_view_source> > &source_list() const { return m_source_list; } // setters void set_visible_size(debug_view_xy size); @@ -188,7 +199,7 @@ protected: debug_view * m_next; // link to the next view debug_view_type m_type; // type of view const debug_view_source *m_source; // currently selected data source - simple_list<debug_view_source> m_source_list; // list of available data sources + std::vector<std::unique_ptr<const debug_view_source> > m_source_list; // list of available data sources // OSD data debug_view_osd_update_func m_osdupdate; // callback for the update @@ -258,12 +269,13 @@ public: u64 last_value() const { return m_result; } u64 value() { recompute(); return m_result; } const char *string() const { return m_string.c_str(); } - symbol_table *context() const { return m_parsed.symbols(); } + symbol_table &context() const { return m_parsed.symbols(); } // setters void mark_dirty() { m_dirty = true; } template <typename... Params> void set_string(Params &&... args) { m_string.assign(std::forward<Params>(args)...); m_dirty = true; } void set_context(symbol_table *context); + void set_default_base(int base) { m_parsed.set_default_base(base); } private: // internal helpers diff --git a/src/emu/debug/dvbpoints.cpp b/src/emu/debug/dvbpoints.cpp index d86ce1150cc..0290f8719dc 100644 --- a/src/emu/debug/dvbpoints.cpp +++ b/src/emu/debug/dvbpoints.cpp @@ -1,79 +1,81 @@ // license:BSD-3-Clause -// copyright-holders:Aaron Giles +// copyright-holders:Andrew Gardner, Vas Crabb /********************************************************************* - dvbpoints.c + dvbpoints.cpp Breakpoint debugger view. ***************************************************************************/ #include "emu.h" -#include "debugger.h" #include "dvbpoints.h" +#include "debugcpu.h" +#include "points.h" + #include <algorithm> #include <iomanip> // Sorting functors for the qsort function -static bool cIndexAscending(const device_debug::breakpoint *a, const device_debug::breakpoint *b) +static bool cIndexAscending(const debug_breakpoint *a, const debug_breakpoint *b) { return a->index() < b->index(); } -static bool cIndexDescending(const device_debug::breakpoint *a, const device_debug::breakpoint *b) +static bool cIndexDescending(const debug_breakpoint *a, const debug_breakpoint *b) { return cIndexAscending(b, a); } -static bool cEnabledAscending(const device_debug::breakpoint *a, const device_debug::breakpoint *b) +static bool cEnabledAscending(const debug_breakpoint *a, const debug_breakpoint *b) { return !a->enabled() && b->enabled(); } -static bool cEnabledDescending(const device_debug::breakpoint *a, const device_debug::breakpoint *b) +static bool cEnabledDescending(const debug_breakpoint *a, const debug_breakpoint *b) { return cEnabledAscending(b, a); } -static bool cCpuAscending(const device_debug::breakpoint *a, const device_debug::breakpoint *b) +static bool cCpuAscending(const debug_breakpoint *a, const debug_breakpoint *b) { return strcmp(a->debugInterface()->device().tag(), b->debugInterface()->device().tag()) < 0; } -static bool cCpuDescending(const device_debug::breakpoint *a, const device_debug::breakpoint *b) +static bool cCpuDescending(const debug_breakpoint *a, const debug_breakpoint *b) { return cCpuAscending(b, a); } -static bool cAddressAscending(const device_debug::breakpoint *a, const device_debug::breakpoint *b) +static bool cAddressAscending(const debug_breakpoint *a, const debug_breakpoint *b) { return a->address() < b->address(); } -static bool cAddressDescending(const device_debug::breakpoint *a, const device_debug::breakpoint *b) +static bool cAddressDescending(const debug_breakpoint *a, const debug_breakpoint *b) { return cAddressAscending(b, a); } -static bool cConditionAscending(const device_debug::breakpoint *a, const device_debug::breakpoint *b) +static bool cConditionAscending(const debug_breakpoint *a, const debug_breakpoint *b) { return strcmp(a->condition(), b->condition()) < 0; } -static bool cConditionDescending(const device_debug::breakpoint *a, const device_debug::breakpoint *b) +static bool cConditionDescending(const debug_breakpoint *a, const debug_breakpoint *b) { return cConditionAscending(b, a); } -static bool cActionAscending(const device_debug::breakpoint *a, const device_debug::breakpoint *b) +static bool cActionAscending(const debug_breakpoint *a, const debug_breakpoint *b) { - return strcmp(a->action(), b->action()) < 0; + return a->action() < b->action(); } -static bool cActionDescending(const device_debug::breakpoint *a, const device_debug::breakpoint *b) +static bool cActionDescending(const debug_breakpoint *a, const debug_breakpoint *b) { return cActionAscending(b, a); } @@ -96,7 +98,7 @@ debug_view_breakpoints::debug_view_breakpoints(running_machine &machine, debug_v { // fail if no available sources enumerate_sources(); - if (m_source_list.count() == 0) + if (m_source_list.empty()) throw std::bad_alloc(); } @@ -118,18 +120,20 @@ debug_view_breakpoints::~debug_view_breakpoints() void debug_view_breakpoints::enumerate_sources() { // start with an empty list - m_source_list.reset(); + m_source_list.clear(); // iterate over devices with disassembly interfaces - for (device_disasm_interface &dasm : disasm_interface_iterator(machine().root_device())) + for (device_disasm_interface &dasm : disasm_interface_enumerator(machine().root_device())) { - std::string name; - name = string_format("%s '%s'", dasm.device().name(), dasm.device().tag()); - m_source_list.append(*global_alloc(debug_view_source(name.c_str(), &dasm.device()))); + m_source_list.emplace_back( + std::make_unique<debug_view_source>( + util::string_format("%s '%s'", dasm.device().name(), dasm.device().tag()), + &dasm.device())); } // reset the source to a known good entry - set_source(*m_source_list.first()); + if (!m_source_list.empty()) + set_source(*m_source_list[0]); } @@ -167,7 +171,7 @@ void debug_view_breakpoints::view_click(const int button, const debug_view_xy& p return; // Enable / disable - const_cast<device_debug::breakpoint &>(*m_buffer[bpIndex]).setEnabled(!m_buffer[bpIndex]->enabled()); + const_cast<debug_breakpoint &>(*m_buffer[bpIndex]).setEnabled(!m_buffer[bpIndex]->enabled()); machine().debug_view().update_all(DVT_DISASSEMBLY); } @@ -189,12 +193,12 @@ void debug_view_breakpoints::pad_ostream_to_length(std::ostream& str, int len) void debug_view_breakpoints::gather_breakpoints() { m_buffer.resize(0); - for (const debug_view_source &source : m_source_list) + for (auto &source : m_source_list) { // Collect - device_debug &debugInterface = *source.device()->debug(); - for (const device_debug::breakpoint &bp : debugInterface.breakpoint_list()) - m_buffer.push_back(&bp); + device_debug &debugInterface = *source->device()->debug(); + for (const auto &bpp : debugInterface.breakpoint_list()) + m_buffer.push_back(bpp.second.get()); } // And now for the sort @@ -214,7 +218,7 @@ void debug_view_breakpoints::view_update() gather_breakpoints(); // Set the view region so the scroll bars update - m_total.x = tableBreaks[ARRAY_LENGTH(tableBreaks) - 1]; + m_total.x = tableBreaks[std::size(tableBreaks) - 1]; m_total.y = m_buffer.size() + 1; if (m_total.y < 10) m_total.y = 10; @@ -222,7 +226,7 @@ void debug_view_breakpoints::view_update() // Draw debug_view_char *dest = &m_viewdata[0]; util::ovectorstream linebuf; - linebuf.reserve(ARRAY_LENGTH(tableBreaks) - 1); + linebuf.reserve(std::size(tableBreaks) - 1); // Header if (m_visible.y > 0) @@ -268,7 +272,7 @@ void debug_view_breakpoints::view_update() int bpi = row + m_topleft.y - 1; if ((bpi < m_buffer.size()) && (bpi >= 0)) { - const device_debug::breakpoint *const bp = m_buffer[bpi]; + const debug_breakpoint *const bp = m_buffer[bpi]; linebuf.clear(); linebuf.rdbuf()->clear(); diff --git a/src/emu/debug/dvbpoints.h b/src/emu/debug/dvbpoints.h index 2cba6cd3081..be291fa66d8 100644 --- a/src/emu/debug/dvbpoints.h +++ b/src/emu/debug/dvbpoints.h @@ -1,5 +1,5 @@ // license:BSD-3-Clause -// copyright-holders:Aaron Giles +// copyright-holders:Andrew Gardner, Vas Crabb /********************************************************************* dvbpoints.h @@ -7,19 +7,14 @@ Breakpoint debugger view. ***************************************************************************/ - #ifndef MAME_EMU_DEBUG_DVBPOINTS_H #define MAME_EMU_DEBUG_DVBPOINTS_H #pragma once #include "debugvw.h" -#include "debugcpu.h" - -//************************************************************************** -// CONSTANTS -//************************************************************************** +#include <vector> //************************************************************************** @@ -46,10 +41,9 @@ private: void pad_ostream_to_length(std::ostream& str, int len); void gather_breakpoints(); - // internal state - bool (*m_sortType)(const device_debug::breakpoint *, const device_debug::breakpoint *); - std::vector<const device_debug::breakpoint *> m_buffer; + bool (*m_sortType)(const debug_breakpoint *, const debug_breakpoint *); + std::vector<const debug_breakpoint *> m_buffer; }; #endif // MAME_EMU_DEBUG_DVBPOINTS_H diff --git a/src/emu/debug/dvdisasm.cpp b/src/emu/debug/dvdisasm.cpp index 667542483a4..17cc766ef94 100644 --- a/src/emu/debug/dvdisasm.cpp +++ b/src/emu/debug/dvdisasm.cpp @@ -2,17 +2,18 @@ // copyright-holders:Aaron Giles, Olivier Galibert /********************************************************************* - dvdisasm.c + dvdisasm.cpp Disassembly debugger view. ***************************************************************************/ #include "emu.h" -#include "debugvw.h" #include "dvdisasm.h" + +#include "debugbuf.h" #include "debugcpu.h" -#include "debugger.h" + //************************************************************************** // DEBUG VIEW DISASM SOURCE @@ -22,11 +23,15 @@ // debug_view_disasm_source - constructor //------------------------------------------------- -debug_view_disasm_source::debug_view_disasm_source(const char *name, device_t &device) - : debug_view_source(name, &device), +debug_view_disasm_source::debug_view_disasm_source(std::string &&name, device_t &device) + : debug_view_source(std::move(name), &device), m_space(device.memory().space(AS_PROGRAM)), - m_decrypted_space(device.memory().has_space(AS_OPCODES) ? device.memory().space(AS_OPCODES) : device.memory().space(AS_PROGRAM)) + m_decrypted_space(device.memory().has_space(AS_OPCODES) ? device.memory().space(AS_OPCODES) : device.memory().space(AS_PROGRAM)), + m_pcbase(nullptr) { + const device_state_interface *state; + if (device.interface(state)) + m_pcbase = state->state_find_entry(STATE_GENPCBASE); } @@ -35,9 +40,6 @@ debug_view_disasm_source::debug_view_disasm_source(const char *name, device_t &d // DEBUG VIEW DISASM //************************************************************************** -const int debug_view_disasm::DEFAULT_DASM_LINES, debug_view_disasm::DEFAULT_DASM_WIDTH, debug_view_disasm::DASM_MAX_BYTES; - - //------------------------------------------------- // debug_view_disasm - constructor //------------------------------------------------- @@ -52,16 +54,16 @@ debug_view_disasm::debug_view_disasm(running_machine &machine, debug_view_osd_up { // fail if no available sources enumerate_sources(); - if(m_source_list.count() == 0) + if(m_source_list.empty()) throw std::bad_alloc(); // count the number of comments - int total_comments = 0; - for(const debug_view_source &source : m_source_list) - { - const debug_view_disasm_source &dasmsource = downcast<const debug_view_disasm_source &>(source); - total_comments += dasmsource.device()->debug()->comment_count(); - } + //int total_comments = 0; + //for(auto &source : m_source_list) + //{ + //const debug_view_disasm_source &dasmsource = downcast<const debug_view_disasm_source &>(*source); + //total_comments += dasmsource.device()->debug()->comment_count(); + //} // configure the view m_total.y = DEFAULT_DASM_LINES; @@ -86,19 +88,23 @@ debug_view_disasm::~debug_view_disasm() void debug_view_disasm::enumerate_sources() { // start with an empty list - m_source_list.reset(); + m_source_list.clear(); // iterate over devices with disassembly interfaces - std::string name; - for(device_disasm_interface &dasm : disasm_interface_iterator(machine().root_device())) + for (device_disasm_interface &dasm : disasm_interface_enumerator(machine().root_device())) { - name = string_format("%s '%s'", dasm.device().name(), dasm.device().tag()); - if(dasm.device().memory().space_config(AS_PROGRAM)!=nullptr) - m_source_list.append(*global_alloc(debug_view_disasm_source(name.c_str(), dasm.device()))); + if (dasm.device().memory().space_config(AS_PROGRAM)) + { + m_source_list.emplace_back( + std::make_unique<debug_view_disasm_source>( + util::string_format("%s '%s'", dasm.device().name(), dasm.device().tag()), + dasm.device())); + } } // reset the source to a known good entry - set_source(*m_source_list.first()); + if (!m_source_list.empty()) + set_source(*m_source_list[0]); } @@ -113,7 +119,11 @@ void debug_view_disasm::view_notify(debug_view_notification type) adjust_visible_y_for_cursor(); else if(type == VIEW_NOTIFY_SOURCE_CHANGED) - m_expression.set_context(&downcast<const debug_view_disasm_source *>(m_source)->device()->debug()->symtable()); + { + const debug_view_disasm_source &source = downcast<const debug_view_disasm_source &>(*m_source); + m_expression.set_context(&source.device()->debug()->symtable()); + m_expression.set_default_base(source.space().is_octal() ? 8 : 16); + } } @@ -159,7 +169,7 @@ void debug_view_disasm::view_char(int chval) case DCH_HOME: // set the active column to the PC { const debug_view_disasm_source &source = downcast<const debug_view_disasm_source &>(*m_source); - offs_t pc = source.device()->state().pcbase() & source.m_space.logaddrmask(); + offs_t pc = source.pcbase(); // figure out which row the pc is on for(unsigned int curline = 0; curline < m_dasm.size(); curline++) @@ -225,6 +235,7 @@ void debug_view_disasm::generate_from_address(debug_disasm_buffer &buffer, offs_ m_dasm.emplace_back(address, size, dasm); address = next_address; } + m_recompute = false; } bool debug_view_disasm::generate_with_pc(debug_disasm_buffer &buffer, offs_t pc) @@ -242,6 +253,7 @@ bool debug_view_disasm::generate_with_pc(debug_disasm_buffer &buffer, offs_t pc) backwards_offset = 64 << shift; m_dasm.clear(); + m_recompute = false; offs_t address = (pc - m_backwards_steps*backwards_offset) & source.m_space.logaddrmask(); // Handle wrap at 0 if(address > pc) @@ -316,7 +328,7 @@ void debug_view_disasm::generate_dasm(debug_disasm_buffer &buffer, offs_t pc) return; } - if(address_position(pc) != -1) { + if(!m_recompute && address_position(pc) != -1) { generate_from_address(buffer, m_dasm[0].m_address); int pos = address_position(pc); if(pos != -1) { @@ -352,13 +364,7 @@ void debug_view_disasm::complete_information(const debug_view_disasm_source &sou dasm.m_is_pc = adr == pc; - dasm.m_is_bp = false; - for(const device_debug::breakpoint &bp : source.device()->debug()->breakpoint_list()) - if(adr == (bp.address() & source.m_space.logaddrmask())) { - dasm.m_is_bp = true; - break; - } - + dasm.m_is_bp = source.device()->debug()->breakpoint_find(adr) != nullptr; dasm.m_is_visited = source.device()->debug()->track_pc_visited(adr); const char *comment = source.device()->debug()->comment_text(adr); @@ -376,7 +382,7 @@ void debug_view_disasm::view_update() { const debug_view_disasm_source &source = downcast<const debug_view_disasm_source &>(*m_source); debug_disasm_buffer buffer(*source.device()); - offs_t pc = source.device()->state().pcbase() & source.m_space.logaddrmask(); + offs_t pc = source.pcbase(); generate_dasm(buffer, pc); @@ -389,25 +395,25 @@ void debug_view_disasm::view_update() // print - print a string in the disassembly view //------------------------------------------------- -void debug_view_disasm::print(int row, std::string text, int start, int end, u8 attrib) +void debug_view_disasm::print(u32 row, std::string text, s32 start, s32 end, u8 attrib) { - int view_end = end - m_topleft.x; + s32 view_end = end - m_topleft.x; if(view_end < 0) return; - int string_0 = start - m_topleft.x; + s32 string_0 = start - m_topleft.x; if(string_0 >= m_visible.x) return; - int view_start = string_0 > 0 ? string_0 : 0; + s32 view_start = string_0 > 0 ? string_0 : 0; debug_view_char *dest = &m_viewdata[row * m_visible.x + view_start]; if(view_end >= m_visible.x) view_end = m_visible.x; - for(int pos = view_start; pos < view_end; pos++) { - int spos = pos - string_0; - if(spos >= int(text.size())) + for(s32 pos = view_start; pos < view_end; pos++) { + s32 spos = pos - string_0; + if(spos >= s32(text.size())) *dest++ = { ' ', attrib }; else *dest++ = { u8(text[spos]), attrib }; @@ -422,13 +428,15 @@ void debug_view_disasm::print(int row, std::string text, int start, int end, u8 void debug_view_disasm::redraw() { // determine how many characters we need for an address and set the divider - int m_divider1 = 1 + m_dasm[0].m_tadr.size() + 1; + s32 divider1 = 1 + m_dasm[0].m_tadr.size() + 1; // assume a fixed number of characters for the disassembly - int m_divider2 = m_divider1 + 1 + m_dasm_width + 1; + s32 divider2 = divider1 + 1 + m_dasm_width + 1; // set the width of the third column to max comment length - m_total.x = m_divider2 + 1 + 50; // DEBUG_COMMENT_MAX_LINE_LENGTH + m_total.x = divider2 + 1 + 50; // DEBUG_COMMENT_MAX_LINE_LENGTH + + const s32 max_visible_col = m_topleft.x + m_visible.x; // loop over visible rows for(u32 row = 0; row < m_visible.y; row++) @@ -455,22 +463,22 @@ void debug_view_disasm::redraw() if(m_dasm[effrow].m_is_visited) attrib |= DCA_VISITED; - print(row, ' ' + m_dasm[effrow].m_tadr, 0, m_divider1, attrib | DCA_ANCILLARY); - print(row, ' ' + m_dasm[effrow].m_dasm, m_divider1, m_divider2, attrib); + print(row, ' ' + m_dasm[effrow].m_tadr, 0, divider1, attrib | DCA_ANCILLARY); + print(row, ' ' + m_dasm[effrow].m_dasm, divider1, divider2, attrib); if(m_right_column == DASM_RIGHTCOL_RAW || m_right_column == DASM_RIGHTCOL_ENCRYPTED) { std::string text = ' ' +(m_right_column == DASM_RIGHTCOL_RAW ? m_dasm[effrow].m_topcodes : m_dasm[effrow].m_tparams); - print(row, text, m_divider2, m_visible.x, attrib | DCA_ANCILLARY); - if(int(text.size()) > m_visible.x - m_divider2) { - int base = m_total.x - 3; - if(base < m_divider2) - base = m_divider2; - print(row, "...", base, m_visible.x, attrib | DCA_ANCILLARY); + print(row, text, divider2, max_visible_col, attrib | DCA_ANCILLARY); + if(s32(text.size()) > max_visible_col - divider2) { + s32 base = max_visible_col - 3; + if(base < divider2) + base = divider2; + print(row, "...", base, max_visible_col, attrib | DCA_ANCILLARY); } } else if(!m_dasm[effrow].m_comment.empty()) - print(row, " // " + m_dasm[effrow].m_comment, m_divider2, m_visible.x, attrib | DCA_COMMENT | DCA_ANCILLARY); + print(row, " // " + m_dasm[effrow].m_comment, divider2, max_visible_col, attrib | DCA_COMMENT | DCA_ANCILLARY); else - print(row, "", m_divider2, m_visible.x, attrib | DCA_COMMENT | DCA_ANCILLARY); + print(row, "", divider2, max_visible_col, attrib | DCA_COMMENT | DCA_ANCILLARY); } } } @@ -511,7 +519,7 @@ void debug_view_disasm::set_right_column(disasm_right_column contents) { begin_update(); m_right_column = contents; - m_recompute = m_update_pending = true; + m_update_pending = true; end_update(); } @@ -568,7 +576,7 @@ void debug_view_disasm::set_selected_address(offs_t address) void debug_view_disasm::set_source(const debug_view_source &source) { if(&source != m_source) { + m_recompute = true; debug_view::set_source(source); - m_dasm.clear(); } } diff --git a/src/emu/debug/dvdisasm.h b/src/emu/debug/dvdisasm.h index ef9e4209f1b..ef4c5e9cf87 100644 --- a/src/emu/debug/dvdisasm.h +++ b/src/emu/debug/dvdisasm.h @@ -14,9 +14,6 @@ #pragma once #include "debugvw.h" -#include "debugbuf.h" - -#include "vecstream.h" //************************************************************************** @@ -38,22 +35,27 @@ enum disasm_right_column // TYPE DEFINITIONS //************************************************************************** +// forward declaration +class debug_disasm_buffer; + // a disassembly view_source class debug_view_disasm_source : public debug_view_source { friend class debug_view_disasm; +public: // construction/destruction - debug_view_disasm_source(const char *name, device_t &device); + debug_view_disasm_source(std::string &&name, device_t &device); -public: // getters address_space &space() const { return m_space; } + offs_t pcbase() const { return m_pcbase != nullptr ? m_pcbase->value() & m_space.logaddrmask() : 0; } private: // internal state address_space & m_space; // address space to display address_space & m_decrypted_space; // address space to display for decrypted opcodes + const device_state_entry *m_pcbase; }; @@ -117,7 +119,7 @@ private: void complete_information(const debug_view_disasm_source &source, debug_disasm_buffer &buffer, offs_t pc); void enumerate_sources(); - void print(int row, std::string text, int start, int end, u8 attrib); + void print(u32 row, std::string text, s32 start, s32 end, u8 attrib); void redraw(); // internal state diff --git a/src/emu/debug/dvepoints.cpp b/src/emu/debug/dvepoints.cpp new file mode 100644 index 00000000000..2ebc53a6609 --- /dev/null +++ b/src/emu/debug/dvepoints.cpp @@ -0,0 +1,314 @@ +// license:BSD-3-Clause +// copyright-holders:Andrew Gardner, Vas Crabb +/********************************************************************* + + dvepoints.cpp + + Exceptionpoint debugger view. + +***************************************************************************/ + +#include "emu.h" +#include "dvepoints.h" + +#include "debugcpu.h" +#include "points.h" + +#include <algorithm> +#include <iomanip> + + + +// Sorting functors for the qsort function +static bool cIndexAscending(const debug_exceptionpoint *a, const debug_exceptionpoint *b) +{ + return a->index() < b->index(); +} + +static bool cIndexDescending(const debug_exceptionpoint *a, const debug_exceptionpoint *b) +{ + return cIndexAscending(b, a); +} + +static bool cEnabledAscending(const debug_exceptionpoint *a, const debug_exceptionpoint *b) +{ + return !a->enabled() && b->enabled(); +} + +static bool cEnabledDescending(const debug_exceptionpoint *a, const debug_exceptionpoint *b) +{ + return cEnabledAscending(b, a); +} + +static bool cCpuAscending(const debug_exceptionpoint *a, const debug_exceptionpoint *b) +{ + return strcmp(a->debugInterface()->device().tag(), b->debugInterface()->device().tag()) < 0; +} + +static bool cCpuDescending(const debug_exceptionpoint *a, const debug_exceptionpoint *b) +{ + return cCpuAscending(b, a); +} + +static bool cTypeAscending(const debug_exceptionpoint *a, const debug_exceptionpoint *b) +{ + return a->type() < b->type(); +} + +static bool cTypeDescending(const debug_exceptionpoint *a, const debug_exceptionpoint *b) +{ + return cTypeAscending(b, a); +} + +static bool cConditionAscending(const debug_exceptionpoint *a, const debug_exceptionpoint *b) +{ + return strcmp(a->condition(), b->condition()) < 0; +} + +static bool cConditionDescending(const debug_exceptionpoint *a, const debug_exceptionpoint *b) +{ + return cConditionAscending(b, a); +} + +static bool cActionAscending(const debug_exceptionpoint *a, const debug_exceptionpoint *b) +{ + return a->action() < b->action(); +} + +static bool cActionDescending(const debug_exceptionpoint *a, const debug_exceptionpoint *b) +{ + return cActionAscending(b, a); +} + + +//************************************************************************** +// DEBUG VIEW BREAK POINTS +//************************************************************************** + +static const int tableBreaks[] = { 5, 9, 31, 45, 63, 80 }; + + +//------------------------------------------------- +// debug_view_exceptionpoints - constructor +//------------------------------------------------- + +debug_view_exceptionpoints::debug_view_exceptionpoints(running_machine &machine, debug_view_osd_update_func osdupdate, void *osdprivate) + : debug_view(machine, DVT_EXCEPTION_POINTS, osdupdate, osdprivate) + , m_sortType(cIndexAscending) +{ + // fail if no available sources + enumerate_sources(); + if (m_source_list.empty()) + throw std::bad_alloc(); +} + + +//------------------------------------------------- +// ~debug_view_exceptionpoints - destructor +//------------------------------------------------- + +debug_view_exceptionpoints::~debug_view_exceptionpoints() +{ +} + + +//------------------------------------------------- +// enumerate_sources - enumerate all possible +// sources for a disassembly view +//------------------------------------------------- + +void debug_view_exceptionpoints::enumerate_sources() +{ + // start with an empty list + m_source_list.clear(); + + // iterate over devices with disassembly interfaces + for (device_disasm_interface &dasm : disasm_interface_enumerator(machine().root_device())) + { + m_source_list.emplace_back( + std::make_unique<debug_view_source>( + util::string_format("%s '%s'", dasm.device().name(), dasm.device().tag()), + &dasm.device())); + } + + // reset the source to a known good entry + if (!m_source_list.empty()) + set_source(*m_source_list[0]); +} + + +//------------------------------------------------- +// view_click - handle a mouse click within the +// current view +//------------------------------------------------- + +void debug_view_exceptionpoints::view_click(const int button, const debug_view_xy& pos) +{ + bool clickedTopRow = (m_topleft.y == pos.y); + + if (clickedTopRow) + { + if (pos.x < tableBreaks[0]) + m_sortType = (m_sortType == &cIndexAscending) ? &cIndexDescending : &cIndexAscending; + else if (pos.x < tableBreaks[1]) + m_sortType = (m_sortType == &cEnabledAscending) ? &cEnabledDescending : &cEnabledAscending; + else if (pos.x < tableBreaks[2]) + m_sortType = (m_sortType == &cCpuAscending) ? &cCpuDescending : &cCpuAscending; + else if (pos.x < tableBreaks[3]) + m_sortType = (m_sortType == &cTypeAscending) ? &cTypeDescending : &cTypeAscending; + else if (pos.x < tableBreaks[4]) + m_sortType = (m_sortType == &cConditionAscending) ? &cConditionDescending : &cConditionAscending; + else if (pos.x < tableBreaks[5]) + m_sortType = (m_sortType == &cActionAscending) ? &cActionDescending : &cActionAscending; + } + else + { + // Gather a sorted list of all the exceptionpoints for all the CPUs + gather_exceptionpoints(); + + int epIndex = pos.y - 1; + if ((epIndex >= m_buffer.size()) || (epIndex < 0)) + return; + + // Enable / disable + const_cast<debug_exceptionpoint &>(*m_buffer[epIndex]).setEnabled(!m_buffer[epIndex]->enabled()); + + machine().debug_view().update_all(DVT_DISASSEMBLY); + } + + begin_update(); + m_update_pending = true; + end_update(); +} + + +void debug_view_exceptionpoints::pad_ostream_to_length(std::ostream& str, int len) +{ + auto const current = str.tellp(); + if (current < decltype(current)(len)) + str << std::setw(decltype(current)(len) - current) << ""; +} + + +void debug_view_exceptionpoints::gather_exceptionpoints() +{ + m_buffer.resize(0); + for (auto &source : m_source_list) + { + // Collect + device_debug &debugInterface = *source->device()->debug(); + for (const auto &epp : debugInterface.exceptionpoint_list()) + m_buffer.push_back(epp.second.get()); + } + + // And now for the sort + if (!m_buffer.empty()) + std::stable_sort(m_buffer.begin(), m_buffer.end(), m_sortType); +} + + +//------------------------------------------------- +// view_update - update the contents of the +// exceptionpoints view +//------------------------------------------------- + +void debug_view_exceptionpoints::view_update() +{ + // Gather a list of all the exceptionpoints for all the CPUs + gather_exceptionpoints(); + + // Set the view region so the scroll bars update + m_total.x = tableBreaks[std::size(tableBreaks) - 1]; + m_total.y = m_buffer.size() + 1; + if (m_total.y < 10) + m_total.y = 10; + + // Draw + debug_view_char *dest = &m_viewdata[0]; + util::ovectorstream linebuf; + linebuf.reserve(std::size(tableBreaks) - 1); + + // Header + if (m_visible.y > 0) + { + linebuf.clear(); + linebuf.rdbuf()->clear(); + linebuf << "ID"; + if (m_sortType == &cIndexAscending) linebuf.put('\\'); + else if (m_sortType == &cIndexDescending) linebuf.put('/'); + pad_ostream_to_length(linebuf, tableBreaks[0]); + linebuf << "En"; + if (m_sortType == &cEnabledAscending) linebuf.put('\\'); + else if (m_sortType == &cEnabledDescending) linebuf.put('/'); + pad_ostream_to_length(linebuf, tableBreaks[1]); + linebuf << "CPU"; + if (m_sortType == &cCpuAscending) linebuf.put('\\'); + else if (m_sortType == &cCpuDescending) linebuf.put('/'); + pad_ostream_to_length(linebuf, tableBreaks[2]); + linebuf << "Type"; + if (m_sortType == &cTypeAscending) linebuf.put('\\'); + else if (m_sortType == &cTypeDescending) linebuf.put('/'); + pad_ostream_to_length(linebuf, tableBreaks[3]); + linebuf << "Condition"; + if (m_sortType == &cConditionAscending) linebuf.put('\\'); + else if (m_sortType == &cConditionDescending) linebuf.put('/'); + pad_ostream_to_length(linebuf, tableBreaks[4]); + linebuf << "Action"; + if (m_sortType == &cActionAscending) linebuf.put('\\'); + else if (m_sortType == &cActionDescending) linebuf.put('/'); + pad_ostream_to_length(linebuf, tableBreaks[5]); + + auto const &text(linebuf.vec()); + for (u32 i = m_topleft.x; i < (m_topleft.x + m_visible.x); i++, dest++) + { + dest->byte = (i < text.size()) ? text[i] : ' '; + dest->attrib = DCA_ANCILLARY; + } + } + + for (int row = 1; row < m_visible.y; row++) + { + // Breakpoints + int epi = row + m_topleft.y - 1; + if ((epi < m_buffer.size()) && (epi >= 0)) + { + const debug_exceptionpoint *const ep = m_buffer[epi]; + + linebuf.clear(); + linebuf.rdbuf()->clear(); + util::stream_format(linebuf, "%2X", ep->index()); + pad_ostream_to_length(linebuf, tableBreaks[0]); + linebuf.put(ep->enabled() ? 'X' : 'O'); + pad_ostream_to_length(linebuf, tableBreaks[1]); + linebuf << ep->debugInterface()->device().tag(); + pad_ostream_to_length(linebuf, tableBreaks[2]); + util::stream_format(linebuf, "%X", ep->type()); + pad_ostream_to_length(linebuf, tableBreaks[3]); + if (strcmp(ep->condition(), "1")) + linebuf << ep->condition(); + pad_ostream_to_length(linebuf, tableBreaks[4]); + linebuf << ep->action(); + pad_ostream_to_length(linebuf, tableBreaks[5]); + + auto const &text(linebuf.vec()); + for (u32 i = m_topleft.x; i < (m_topleft.x + m_visible.x); i++, dest++) + { + dest->byte = (i < text.size()) ? text[i] : ' '; + dest->attrib = DCA_NORMAL; + + // Color disabled exceptionpoints red + if ((i >= tableBreaks[0]) && (i < tableBreaks[1]) && !ep->enabled()) + dest->attrib |= DCA_CHANGED; + } + } + else + { + // Fill the remaining vertical space + for (u32 i = m_topleft.x; i < (m_topleft.x + m_visible.x); i++, dest++) + { + dest->byte = ' '; + dest->attrib = DCA_NORMAL; + } + } + } +} diff --git a/src/emu/debug/dvepoints.h b/src/emu/debug/dvepoints.h new file mode 100644 index 00000000000..3b042218e2a --- /dev/null +++ b/src/emu/debug/dvepoints.h @@ -0,0 +1,49 @@ +// license:BSD-3-Clause +// copyright-holders:Andrew Gardner, Vas Crabb +/********************************************************************* + + dvepoints.h + + Exceptionpoint debugger view. + +***************************************************************************/ +#ifndef MAME_EMU_DEBUG_DVEPOINTS_H +#define MAME_EMU_DEBUG_DVEPOINTS_H + +#pragma once + +#include "debugvw.h" + +#include <vector> + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// debug view for exceptionpoints +class debug_view_exceptionpoints : public debug_view +{ + friend class debug_view_manager; + + // construction/destruction + debug_view_exceptionpoints(running_machine &machine, debug_view_osd_update_func osdupdate, void *osdprivate); + virtual ~debug_view_exceptionpoints(); + +protected: + // view overrides + virtual void view_update() override; + virtual void view_click(const int button, const debug_view_xy& pos) override; + +private: + // internal helpers + void enumerate_sources(); + void pad_ostream_to_length(std::ostream& str, int len); + void gather_exceptionpoints(); + + // internal state + bool (*m_sortType)(const debug_exceptionpoint *, const debug_exceptionpoint *); + std::vector<const debug_exceptionpoint *> m_buffer; +}; + +#endif // MAME_EMU_DEBUG_DVEPOINTS_H diff --git a/src/emu/debug/dvmemory.cpp b/src/emu/debug/dvmemory.cpp index 947c387800b..7c10407822b 100644 --- a/src/emu/debug/dvmemory.cpp +++ b/src/emu/debug/dvmemory.cpp @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles /********************************************************************* - dvmemory.c + dvmemory.cpp Memory debugger view. @@ -12,35 +12,51 @@ #include "dvmemory.h" #include "debugcpu.h" -#include "debugger.h" #include <algorithm> -#include <ctype.h> +#include <cctype> #include <tuple> //************************************************************************** +// HELPER FUNCTIONS +//************************************************************************** + +namespace { + +constexpr u8 sanitise_character(u8 ch) +{ + // assume ISO-8859-1 (low 256 Unicode codepoints) - tab, soft hyphen, C0 and C1 cause problems + return ('\t' == ch) ? ' ' : (0xadU == ch) ? '-' : ((' ' > ch) || (('~' < ch) && (0xa0U > ch))) ? '.' : ch; +} + +} // anonymous namespace + +//************************************************************************** // GLOBAL VARIABLES //************************************************************************** -const debug_view_memory::memory_view_pos debug_view_memory::s_memory_pos_table[12] = +const debug_view_memory::memory_view_pos debug_view_memory::s_memory_pos_table[16] = { - /* 0 bytes per chunk: */ { 0, { 0 } }, - /* 1 byte per chunk: 00 11 22 33 44 55 66 77 */ { 3, { 0x04, 0x00, 0x80 } }, - /* 2 bytes per chunk: 0011 2233 4455 6677 */ { 6, { 0x8c, 0x0c, 0x08, 0x04, 0x00, 0x80 } }, - /* 3 bytes per chunk: */ { 0, { 0 } }, - /* 4 bytes per chunk: 00112233 44556677 */ { 12, { 0x9c, 0x9c, 0x1c, 0x18, 0x14, 0x10, 0x0c, 0x08, 0x04, 0x00, 0x80, 0x80 } }, - /* 5 bytes per chunk: */ { 0, { 0 } }, - /* 6 bytes per chunk: */ { 0, { 0 } }, - /* 7 bytes per chunk: */ { 0, { 0 } }, - /* 8 bytes per chunk: 0011223344556677 */ { 24, { 0xbc, 0xbc, 0xbc, 0xbc, 0x3c, 0x38, 0x34, 0x30, 0x2c, 0x28, 0x24, 0x20, 0x1c, 0x18, 0x14, 0x10, 0x0c, 0x08, 0x04, 0x00, 0x80, 0x80, 0x80, 0x80 } }, - /* 32 bit floating point: */ { 16, { 0 } }, - /* 64 bit floating point: */ { 32, { 0 } }, - /* 80 bit floating point: */ { 32, { 0 } }, + /* 0 bytes per chunk: */ { 0, 0, { 0 } }, + /* 1 byte per chunk: 00 11 22 33 44 55 66 77 */ { 1, 3, { 0x04, 0x00, 0x80 } }, + /* 2 bytes per chunk: 0011 2233 4455 6677 */ { 2, 6, { 0x8c, 0x0c, 0x08, 0x04, 0x00, 0x80 } }, + /* 3 bytes per chunk: */ { 0, 0, { 0 } }, + /* 4 bytes per chunk: 00112233 44556677 */ { 4, 12, { 0x9c, 0x9c, 0x1c, 0x18, 0x14, 0x10, 0x0c, 0x08, 0x04, 0x00, 0x80, 0x80 } }, + /* 5 bytes per chunk: */ { 0, 0, { 0 } }, + /* 6 bytes per chunk: */ { 0, 0, { 0 } }, + /* 7 bytes per chunk: */ { 0, 0, { 0 } }, + /* 8 bytes per chunk: 0011223344556677 */ { 8, 24, { 0xbc, 0xbc, 0xbc, 0xbc, 0x3c, 0x38, 0x34, 0x30, 0x2c, 0x28, 0x24, 0x20, 0x1c, 0x18, 0x14, 0x10, 0x0c, 0x08, 0x04, 0x00, 0x80, 0x80, 0x80, 0x80 } }, + /* 32 bit floating point: */ { 4, 16, { 0 } }, + /* 64 bit floating point: */ { 8, 32, { 0 } }, + /* 80 bit floating point: */ { 10, 32, { 0 } }, + /* 8 bit octal: */ { 1, 4, { 0x06, 0x03, 0x00, 0x80 } }, + /* 16 bit octal: */ { 2, 8, { 0x8f, 0x0f, 0x0c, 0x09, 0x06, 0x03, 0x00, 0x80 } }, + /* 32 bit octal: */ { 4, 15, { 0x9e, 0x9e, 0x1e, 0x1b, 0x18, 0x15, 0x12, 0x0f, 0x0c, 0x09, 0x06, 0x03, 0x00, 0x80, 0x80 } }, + /* 64 bit octal: */ { 8, 28, { 0xbf, 0xbf, 0xbf, 0x3f, 0x3c, 0x39, 0x36, 0x33, 0x30, 0x2d, 0x2a, 0x27, 0x24, 0x21, 0x1e, 0x1b, 0x18, 0x15, 0x12, 0x0f, 0x0c, 0x09, 0x06, 0x03, 0x00, 0x80, 0x80, 0x80 } }, }; - //************************************************************************** // DEBUG VIEW MEMORY SOURCE //************************************************************************** @@ -49,39 +65,45 @@ const debug_view_memory::memory_view_pos debug_view_memory::s_memory_pos_table[1 // debug_view_memory_source - constructors //------------------------------------------------- -debug_view_memory_source::debug_view_memory_source(const char *name, address_space &space) - : debug_view_source(name, &space.device()), - m_space(&space), - m_memintf(dynamic_cast<device_memory_interface *>(&space.device())), - m_base(nullptr), - m_length(0), - m_offsetxor(0), - m_endianness(space.endianness()), - m_prefsize(space.data_width() / 8) +debug_view_memory_source::debug_view_memory_source(std::string &&name, address_space &space) + : debug_view_source(std::move(name), &space.device()) + , m_space(&space) + , m_memintf(dynamic_cast<device_memory_interface *>(&space.device())) + , m_base(nullptr) + , m_blocklength(0) + , m_numblocks(0) + , m_blockstride(0) + , m_offsetxor(0) + , m_endianness(space.endianness()) + , m_prefsize(space.data_width() / 8) { } -debug_view_memory_source::debug_view_memory_source(const char *name, memory_region ®ion) - : debug_view_source(name), - m_space(nullptr), - m_memintf(nullptr), - m_base(region.base()), - m_length(region.bytes()), - m_offsetxor(ENDIAN_VALUE_NE_NNE(region.endianness(), 0, region.bytewidth() - 1)), - m_endianness(region.endianness()), - m_prefsize(std::min<u8>(region.bytewidth(), 8)) +debug_view_memory_source::debug_view_memory_source(std::string &&name, memory_region ®ion) + : debug_view_source(std::move(name)) + , m_space(nullptr) + , m_memintf(nullptr) + , m_base(region.base()) + , m_blocklength(region.bytes()) + , m_numblocks(1) + , m_blockstride(0) + , m_offsetxor(region.endianness() == ENDIANNESS_NATIVE ? 0 : region.bytewidth() - 1) + , m_endianness(region.endianness()) + , m_prefsize(std::min<u8>(region.bytewidth(), 8)) { } -debug_view_memory_source::debug_view_memory_source(const char *name, void *base, int element_size, int num_elements) - : debug_view_source(name), - m_space(nullptr), - m_memintf(nullptr), - m_base(base), - m_length(element_size * num_elements), - m_offsetxor(0), - m_endianness(ENDIANNESS_NATIVE), - m_prefsize(std::min(element_size, 8)) +debug_view_memory_source::debug_view_memory_source(std::string &&name, void *base, int element_size, int num_elements, int num_blocks, int block_stride) + : debug_view_source(std::move(name)) + , m_space(nullptr) + , m_memintf(nullptr) + , m_base(base) + , m_blocklength(element_size * num_elements) + , m_numblocks(num_blocks) + , m_blockstride(block_stride) + , m_offsetxor(0) + , m_endianness(ENDIANNESS_NATIVE) + , m_prefsize(std::min(element_size, 8)) { } @@ -101,11 +123,13 @@ debug_view_memory::debug_view_memory(running_machine &machine, debug_view_osd_up m_chunks_per_row(16), m_bytes_per_chunk(1), m_steps_per_chunk(1), - m_data_format(1), + m_data_format(data_format::HEX_8BIT), m_reverse_view(false), m_ascii_view(true), m_no_translation(false), m_edit_enabled(true), + m_shift_bits(4), + m_address_radix(16), m_maxaddr(0), m_bytes_per_row(16), m_byte_offset(0) @@ -119,7 +143,7 @@ debug_view_memory::debug_view_memory(running_machine &machine, debug_view_osd_up // fail if no available sources enumerate_sources(); - if (m_source_list.count() == 0) + if (m_source_list.empty()) throw std::bad_alloc(); // configure the view @@ -135,49 +159,56 @@ debug_view_memory::debug_view_memory(running_machine &machine, debug_view_osd_up void debug_view_memory::enumerate_sources() { // start with an empty list - m_source_list.reset(); - std::string name; + m_source_list.clear(); + m_source_list.reserve(machine().save().registration_count()); // first add all the devices' address spaces - for (device_memory_interface &memintf : memory_interface_iterator(machine().root_device())) + for (device_memory_interface &memintf : memory_interface_enumerator(machine().root_device())) + { for (int spacenum = 0; spacenum < memintf.max_space_count(); ++spacenum) + { if (memintf.has_space(spacenum)) { - address_space &space = memintf.space(spacenum); - name = string_format("%s '%s' %s space memory", memintf.device().name(), memintf.device().tag(), space.name()); - m_source_list.append(*global_alloc(debug_view_memory_source(name.c_str(), space))); + address_space &space(memintf.space(spacenum)); + m_source_list.emplace_back( + std::make_unique<debug_view_memory_source>( + util::string_format("%s '%s' %s space memory", memintf.device().name(), memintf.device().tag(), space.name()), + space)); } + } + } // then add all the memory regions for (auto ®ion : machine().memory().regions()) { - name = string_format("Region '%s'", region.second->name()); - m_source_list.append(*global_alloc(debug_view_memory_source(name.c_str(), *region.second.get()))); + m_source_list.emplace_back( + std::make_unique<debug_view_memory_source>( + util::string_format("Region '%s'", region.second->name()), + *region.second.get())); } - // finally add all global array symbols in alphabetical order - std::vector<std::tuple<std::string, void *, u32, u32> > itemnames; - itemnames.reserve(machine().save().registration_count()); - + // finally add all global array symbols in ASCII order + std::string name; + std::size_t const firstsave = m_source_list.size(); for (int itemnum = 0; itemnum < machine().save().registration_count(); itemnum++) { - u32 valsize, valcount; + u32 valsize, valcount, blockcount, stride; void *base; - std::string name_string(machine().save().indexed_item(itemnum, base, valsize, valcount)); + name = machine().save().indexed_item(itemnum, base, valsize, valcount, blockcount, stride); // add pretty much anything that's not a timer (we may wish to cull other items later) // also, don't trim the front of the name, it's important to know which VIA6522 we're looking at, e.g. - if (strncmp(name_string.c_str(), "timer/", 6)) - itemnames.emplace_back(std::move(name_string), base, valsize, valcount); + if (strncmp(name.c_str(), "timer/", 6)) + m_source_list.emplace_back(std::make_unique<debug_view_memory_source>(std::move(name), base, valsize, valcount, blockcount, stride)); } - - std::sort(itemnames.begin(), itemnames.end(), [] (auto const &x, auto const &y) { return std::get<0>(x) < std::get<0>(y); }); - - for (auto const &item : itemnames) - m_source_list.append(*global_alloc(debug_view_memory_source(std::get<0>(item).c_str(), std::get<1>(item), std::get<2>(item), std::get<3>(item)))); + std::sort( + std::next(m_source_list.begin(), firstsave), + m_source_list.end(), + [] (auto const &x, auto const &y) { return 0 > std::strcmp(x->name(), y->name()); }); // reset the source to a known good entry - set_source(*m_source_list.first()); + if (!m_source_list.empty()) + set_source(*m_source_list[0]); } @@ -201,12 +232,33 @@ void debug_view_memory::view_notify(debug_view_notification type) m_bytes_per_chunk = source.m_prefsize; if (m_bytes_per_chunk > 8) m_bytes_per_chunk = 8; - m_data_format = m_bytes_per_chunk; + bool octal = source.m_space != nullptr && source.m_space->is_octal(); + switch (m_bytes_per_chunk) + { + case 1: + m_data_format = octal ? data_format::OCTAL_8BIT : data_format::HEX_8BIT; + break; + + case 2: + m_data_format = octal ? data_format::OCTAL_16BIT : data_format::HEX_16BIT; + break; + + case 4: + m_data_format = octal ? data_format::OCTAL_32BIT : data_format::HEX_32BIT; + break; + + case 8: + m_data_format = octal ? data_format::OCTAL_64BIT : data_format::HEX_64BIT; + break; + } + m_shift_bits = octal ? 3 : 4; m_steps_per_chunk = source.m_space ? source.m_space->byte_to_address(m_bytes_per_chunk) : m_bytes_per_chunk; if (source.m_space != nullptr) m_expression.set_context(&source.m_space->device().debug()->symtable()); else m_expression.set_context(nullptr); + m_address_radix = octal ? 8 : 16; + m_expression.set_default_base(m_address_radix); } } @@ -244,6 +296,122 @@ static inline float u64_to_double(u64 value) } //------------------------------------------------- +// generate_row - read one row of data and make a +// text representation of the chunks +//------------------------------------------------- + +void debug_view_memory::generate_row(debug_view_char *destmin, debug_view_char *destmax, debug_view_char *destrow, offs_t address) +{ + // get positional data + const memory_view_pos &posdata = get_posdata(m_data_format); + int spacing = posdata.m_spacing; + + // generate the address + char addrtext[20]; + snprintf(addrtext, 20, m_addrformat.c_str(), address); + debug_view_char *dest = destrow + m_section[0].m_pos + 1; + for (int ch = 0; addrtext[ch] != 0 && ch < m_section[0].m_width - 1; ch++, dest++) + if (dest >= destmin && dest < destmax) + dest->byte = addrtext[ch]; + + // generate the data and the ASCII string + std::string chunkascii; + if (m_shift_bits != 0) + { + for (int chunknum = 0; chunknum < m_chunks_per_row; chunknum++) + { + u64 chunkdata; + bool ismapped = read_chunk(address, chunknum, chunkdata); + + int chunkindex = m_reverse_view ? (m_chunks_per_row - 1 - chunknum) : chunknum; + dest = destrow + m_section[1].m_pos + 1 + chunkindex * spacing; + for (int ch = 0; ch < spacing; ch++, dest++) + if (dest >= destmin && dest < destmax) + { + u8 shift = posdata.m_shift[ch]; + if (shift < 64) + dest->byte = ismapped ? "0123456789ABCDEF"[BIT(chunkdata, shift, m_shift_bits)] : '*'; + } + + for (int i = 0; i < m_bytes_per_chunk; i++) + { + u8 chval = chunkdata >> (8 * (m_bytes_per_chunk - i - 1)); + chunkascii += char(ismapped ? sanitise_character(chval) : '.'); + } + } + } + else + { + for (int chunknum = 0; chunknum < m_chunks_per_row; chunknum++) + { + char valuetext[64]; + u64 chunkdata = 0; + extFloat80_t chunkdata80 = { 0, 0 }; + bool ismapped; + + if (m_data_format != data_format::FLOAT_80BIT) + ismapped = read(m_bytes_per_chunk, address + chunknum * m_steps_per_chunk, chunkdata); + else + ismapped = read(m_bytes_per_chunk, address + chunknum * m_steps_per_chunk, chunkdata80); + + if (ismapped) + switch (m_data_format) + { + case data_format::FLOAT_32BIT: + snprintf(valuetext, 64, "%.8g", u32_to_float(u32(chunkdata))); + break; + case data_format::FLOAT_64BIT: + snprintf(valuetext, 64, "%.24g", u64_to_double(chunkdata)); + break; + case data_format::FLOAT_80BIT: + { + float64_t f64 = extF80M_to_f64(&chunkdata80); + snprintf(valuetext, 64, "%.24g", u64_to_double(f64.v)); + break; + } + default: + break; + } + else + { + valuetext[0] = '*'; + valuetext[1] = 0; + } + + int ch; + int chunkindex = m_reverse_view ? (m_chunks_per_row - 1 - chunknum) : chunknum; + dest = destrow + m_section[1].m_pos + 1 + chunkindex * spacing; + // first copy the text + for (ch = 0; (ch < spacing) && (valuetext[ch] != 0); ch++, dest++) + if (dest >= destmin && dest < destmax) + dest->byte = valuetext[ch]; + // then fill with spaces + for (; ch < spacing; ch++, dest++) + if (dest >= destmin && dest < destmax) + dest->byte = ' '; + + for (int i = 0; i < m_bytes_per_chunk; i++) + { + u8 chval = chunkdata >> (8 * (m_bytes_per_chunk - i - 1)); + chunkascii += char(ismapped ? sanitise_character(chval) : '.'); + } + } + } + + // generate the ASCII data, but follow the chunks + if (m_section[2].m_width > 0) + { + dest = destrow + m_section[2].m_pos + 1; + for (size_t i = 0; i != chunkascii.size(); i++) + { + if (dest >= destmin && dest < destmax) + dest->byte = chunkascii[i]; + dest++; + } + } +} + +//------------------------------------------------- // view_update - update the contents of the // memory view //------------------------------------------------- @@ -256,9 +424,6 @@ void debug_view_memory::view_update() if (needs_recompute()) recompute(); - // get positional data - const memory_view_pos &posdata = s_memory_pos_table[m_data_format]; - // loop over visible rows for (u32 row = 0; row < m_visible.y; row++) { @@ -268,10 +433,9 @@ void debug_view_memory::view_update() u32 effrow = m_topleft.y + row; // reset the line of data; section 1 is normal, others are ancillary, cursor is selected - debug_view_char *dest = destmin; - for (int ch = 0; ch < m_visible.x; ch++, dest++) + u32 effcol = m_topleft.x; + for (debug_view_char *dest = destmin; dest != destmax; dest++, effcol++) { - u32 effcol = m_topleft.x + ch; dest->byte = ' '; dest->attrib = DCA_ANCILLARY; if (m_section[1].contains(effcol)) @@ -287,96 +451,7 @@ void debug_view_memory::view_update() { offs_t addrbyte = m_byte_offset + effrow * m_bytes_per_row; offs_t address = (source.m_space != nullptr) ? source.m_space->byte_to_address(addrbyte) : addrbyte; - char addrtext[20]; - - // generate the address - sprintf(addrtext, m_addrformat.c_str(), address); - dest = destrow + m_section[0].m_pos + 1; - for (int ch = 0; addrtext[ch] != 0 && ch < m_section[0].m_width - 1; ch++, dest++) - if (dest >= destmin && dest < destmax) - dest->byte = addrtext[ch]; - - // generate the data and the ascii string - std::string chunkascii; - for (int chunknum = 0; chunknum < m_chunks_per_row; chunknum++) - { - int chunkindex = m_reverse_view ? (m_chunks_per_row - 1 - chunknum) : chunknum; - int spacing = posdata.m_spacing; - - if (m_data_format <= 8) { - u64 chunkdata; - bool ismapped = read_chunk(address, chunknum, chunkdata); - dest = destrow + m_section[1].m_pos + 1 + chunkindex * spacing; - for (int ch = 0; ch < posdata.m_spacing; ch++, dest++) - if (dest >= destmin && dest < destmax) - { - u8 shift = posdata.m_shift[ch]; - if (shift < 64) - dest->byte = ismapped ? "0123456789ABCDEF"[(chunkdata >> shift) & 0x0f] : '*'; - } - for (int i=0; i < m_bytes_per_chunk; i++) { - u8 chval = chunkdata >> (8 * (m_bytes_per_chunk - i - 1)); - chunkascii += char((ismapped && isprint(chval)) ? chval : '.'); - } - } - else { - int ch; - char valuetext[64]; - u64 chunkdata = 0; - floatx80 chunkdata80 = { 0, 0 }; - bool ismapped; - - if (m_data_format != 11) - ismapped = read(m_bytes_per_chunk, address + chunknum * m_steps_per_chunk, chunkdata); - else - ismapped = read(m_bytes_per_chunk, address + chunknum * m_steps_per_chunk, chunkdata80); - - if (ismapped) - switch (m_data_format) - { - case 9: - sprintf(valuetext, "%.8g", u32_to_float(u32(chunkdata))); - break; - case 10: - sprintf(valuetext, "%.24g", u64_to_double(chunkdata)); - break; - case 11: - float64 f64 = floatx80_to_float64(chunkdata80); - sprintf(valuetext, "%.24g", u64_to_double(f64)); - break; - } - else { - valuetext[0] = '*'; - valuetext[1] = 0; - } - - dest = destrow + m_section[1].m_pos + 1 + chunkindex * spacing; - // first copy the text - for (ch = 0; (ch < spacing) && (valuetext[ch] != 0); ch++, dest++) - if (dest >= destmin && dest < destmax) - dest->byte = valuetext[ch]; - // then fill with spaces - for (; ch < spacing; ch++, dest++) - if (dest >= destmin && dest < destmax) - dest->byte = ' '; - - for (int i=0; i < m_bytes_per_chunk; i++) { - u8 chval = chunkdata >> (8 * (m_bytes_per_chunk - i - 1)); - chunkascii += char((ismapped && isprint(chval)) ? chval : '.'); - } - } - } - - // generate the ASCII data, but follow the chunks - if (m_section[2].m_width > 0) - { - dest = destrow + m_section[2].m_pos + 1; - for (size_t i = 0; i != chunkascii.size(); i++) { - if (dest >= destmin && dest < destmax) - dest->byte = chunkascii[i]; - dest++; - } - } + generate_row(destmin, destmax, destrow, address); } } } @@ -429,12 +504,12 @@ void debug_view_memory::view_char(int chval) case DCH_HOME: pos.m_address -= pos.m_address % m_bytes_per_row; - pos.m_shift = (m_bytes_per_chunk * 8) - 4; + pos.m_shift = get_posdata(m_data_format).m_shift[0] & 0x7f; break; case DCH_CTRLHOME: pos.m_address = m_byte_offset; - pos.m_shift = (m_bytes_per_chunk * 8) - 4; + pos.m_shift = get_posdata(m_data_format).m_shift[0] & 0x7f; break; case DCH_END: @@ -458,43 +533,35 @@ void debug_view_memory::view_char(int chval) break; default: - { - static const char hexvals[] = "0123456789abcdef"; - char *hexchar = (char *)strchr(hexvals, tolower(chval)); - if (hexchar == nullptr) - break; - - const debug_view_memory_source &source = downcast<const debug_view_memory_source &>(*m_source); - offs_t address = (source.m_space != nullptr) ? source.m_space->byte_to_address(pos.m_address) : pos.m_address; - u64 data; - bool ismapped = read(m_bytes_per_chunk, address, data); - if (!ismapped) - break; + { + static const char hexvals[] = "0123456789abcdef"; + char *hexchar = (char *)strchr(hexvals, tolower(chval)); + if (hexchar == nullptr || (m_shift_bits == 3 && chval >= '8')) + break; - data &= ~(u64(0x0f) << pos.m_shift); - data |= u64(hexchar - hexvals) << pos.m_shift; - write(m_bytes_per_chunk, address, data); + if (!write_digit(pos.m_address, pos.m_shift, hexchar - hexvals)) + break; // TODO: alert OSD? + } // fall through to the right-arrow press - } - + [[fallthrough]]; case DCH_RIGHT: - if (pos.m_shift == 0 && pos.m_address != m_maxaddr) + if (pos.m_shift != 0) + pos.m_shift -= m_shift_bits; + else if (pos.m_address != m_maxaddr) { - pos.m_shift = m_bytes_per_chunk * 8 - 4; + pos.m_shift = get_posdata(m_data_format).m_shift[0] & 0x7f; pos.m_address += m_bytes_per_chunk; } - else - pos.m_shift -= 4; break; case DCH_LEFT: - if (pos.m_shift == m_bytes_per_chunk * 8 - 4 && pos.m_address != m_byte_offset) + if (pos.m_shift != (get_posdata(m_data_format).m_shift[0] & 0x7f)) + pos.m_shift += m_shift_bits; + else if (pos.m_address != m_byte_offset) { pos.m_shift = 0; pos.m_address -= m_bytes_per_chunk; } - else - pos.m_shift += 4; break; } @@ -551,19 +618,42 @@ void debug_view_memory::recompute() { m_maxaddr = m_no_translation ? source.m_space->addrmask() : source.m_space->logaddrmask(); maxbyte = source.m_space->address_to_byte_end(m_maxaddr); - addrchars = m_no_translation ? source.m_space->addrchars() : source.m_space->logaddrchars(); + if (m_address_radix == 8) + addrchars = ((m_no_translation ? source.m_space->addr_width() : source.m_space->logaddr_width()) + 2) / 3; + else + addrchars = m_no_translation ? source.m_space->addrchars() : source.m_space->logaddrchars(); } else { - maxbyte = m_maxaddr = source.m_length - 1; - addrchars = string_format("%X", m_maxaddr).size(); + maxbyte = m_maxaddr = (source.m_blocklength * source.m_numblocks) - 1; + if (m_address_radix == 8) + addrchars = string_format("%o", m_maxaddr).size(); + else + addrchars = string_format("%X", m_maxaddr).size(); } // generate an 8-byte aligned format for the address - if (!m_reverse_view) - m_addrformat = string_format("%*s%%0%dX", 8 - addrchars, "", addrchars); - else - m_addrformat = string_format("%%0%dX%*s", addrchars, 8 - addrchars, ""); + switch (m_address_radix) + { + case 8: + if (!m_reverse_view) + m_addrformat = string_format("%*s%%0%do", 11 - addrchars, "", addrchars); + else + m_addrformat = string_format("%%0%do%*s", addrchars, 11 - addrchars, ""); + break; + + case 10: + // omit leading zeros for decimal addresses + m_addrformat = m_reverse_view ? "%-10d" : "%10d"; + break; + + case 16: + if (!m_reverse_view) + m_addrformat = string_format("%*s%%0%dX", 8 - addrchars, "", addrchars); + else + m_addrformat = string_format("%%0%dX%*s", addrchars, 8 - addrchars, ""); + break; + } // if we are viewing a space with a minimum chunk size, clamp the bytes per chunk // BAD @@ -588,14 +678,21 @@ void debug_view_memory::recompute() m_byte_offset = val % m_bytes_per_row; // compute the section widths - m_section[0].m_width = 1 + 8 + 1; - if (m_data_format <= 8) - m_section[1].m_width = 1 + 3 * m_bytes_per_row + 1; - else { - const memory_view_pos &posdata = s_memory_pos_table[m_data_format]; + switch (m_address_radix) + { + case 8: + m_section[0].m_width = 1 + 11 + 1; + break; - m_section[1].m_width = 1 + posdata.m_spacing * m_chunks_per_row + 1; + case 10: + m_section[0].m_width = 1 + 10 + 1; + break; + + case 16: + m_section[0].m_width = 1 + 8 + 1; + break; } + m_section[1].m_width = 1 + get_posdata(m_data_format).m_spacing * m_chunks_per_row + 1; m_section[2].m_width = m_ascii_view ? (1 + m_bytes_per_row + 1) : 0; // compute the section positions @@ -661,11 +758,11 @@ debug_view_memory::cursor_pos debug_view_memory::get_cursor_pos(const debug_view { // start with the base address for this row cursor_pos pos; - const memory_view_pos &posdata = s_memory_pos_table[m_data_format]; + const memory_view_pos &posdata = get_posdata(m_data_format); pos.m_address = m_byte_offset + cursor.y * m_bytes_per_chunk * m_chunks_per_row; // determine the X position within the middle section, clamping as necessary - if (m_data_format <= 8) { + if (posdata.m_shift[0] != 0) { int xposition = cursor.x - m_section[1].m_pos - 1; if (xposition < 0) xposition = 0; @@ -712,7 +809,7 @@ debug_view_memory::cursor_pos debug_view_memory::get_cursor_pos(const debug_view void debug_view_memory::set_cursor_pos(cursor_pos pos) { - const memory_view_pos &posdata = s_memory_pos_table[m_data_format]; + const memory_view_pos &posdata = get_posdata(m_data_format); // offset the address by the byte offset if (pos.m_address < m_byte_offset) @@ -727,7 +824,7 @@ void debug_view_memory::set_cursor_pos(cursor_pos pos) if (m_reverse_view) chunknum = m_chunks_per_row - 1 - chunknum; - if (m_data_format <= 8) { + if (posdata.m_shift[0] != 0) { // scan within the chunk to find the shift for (m_cursor.x = 0; m_cursor.x < posdata.m_spacing; m_cursor.x++) if (posdata.m_shift[m_cursor.x] == pos.m_shift) @@ -764,22 +861,17 @@ bool debug_view_memory::read(u8 size, offs_t offs, u64 &data) auto dis = machine().disable_side_effects(); bool ismapped = offs <= m_maxaddr; + address_space *tspace; if (ismapped && !m_no_translation) { offs_t dummyaddr = offs; - ismapped = source.m_memintf->translate(source.m_space->spacenum(), TRANSLATE_READ_DEBUG, dummyaddr); + ismapped = source.m_memintf->translate(source.m_space->spacenum(), device_memory_interface::TR_READ, dummyaddr, tspace); } + else + tspace = source.m_space; data = ~u64(0); if (ismapped) - { - switch (size) - { - case 1: data = machine().debugger().cpu().read_byte(*source.m_space, offs, !m_no_translation); break; - case 2: data = machine().debugger().cpu().read_word(*source.m_space, offs, !m_no_translation); break; - case 4: data = machine().debugger().cpu().read_dword(*source.m_space, offs, !m_no_translation); break; - case 8: data = machine().debugger().cpu().read_qword(*source.m_space, offs, !m_no_translation); break; - } - } + data = m_expression.context().read_memory(*tspace, offs, size, !m_no_translation); return ismapped; } @@ -801,9 +893,9 @@ bool debug_view_memory::read(u8 size, offs_t offs, u64 &data) // all 0xff if out of bounds offs ^= source.m_offsetxor; - if (offs >= source.m_length) + if (offs >= (source.m_blocklength * source.m_numblocks)) return false; - data = *((u8 *)source.m_base + offs); + data = *(reinterpret_cast<const u8 *>(source.m_base) + (offs / source.m_blocklength * source.m_blockstride) + (offs % source.m_blocklength)); return true; } @@ -812,21 +904,21 @@ bool debug_view_memory::read(u8 size, offs_t offs, u64 &data) // read - read a 80 bit value //------------------------------------------------- -bool debug_view_memory::read(u8 size, offs_t offs, floatx80 &data) +bool debug_view_memory::read(u8 size, offs_t offs, extFloat80_t &data) { u64 t; bool mappedhi, mappedlo; const debug_view_memory_source &source = downcast<const debug_view_memory_source &>(*m_source); if (source.m_endianness == ENDIANNESS_LITTLE) { - mappedlo = read(8, offs, data.low); + mappedlo = read(8, offs, data.signif); mappedhi = read(2, offs+8, t); - data.high = (bits16)t; + data.signExp = u16(t); } else { mappedhi = read(2, offs, t); - data.high = (bits16)t; - mappedlo = read(8, offs + 2, data.low); + data.signExp = u16(t); + mappedlo = read(8, offs + 2, data.signif); } return mappedhi && mappedlo; @@ -870,14 +962,7 @@ void debug_view_memory::write(u8 size, offs_t offs, u64 data) if (source.m_space) { auto dis = machine().disable_side_effects(); - - switch (size) - { - case 1: machine().debugger().cpu().write_byte(*source.m_space, offs, data, !m_no_translation); break; - case 2: machine().debugger().cpu().write_word(*source.m_space, offs, data, !m_no_translation); break; - case 4: machine().debugger().cpu().write_dword(*source.m_space, offs, data, !m_no_translation); break; - case 8: machine().debugger().cpu().write_qword(*source.m_space, offs, data, !m_no_translation); break; - } + m_expression.context().write_memory(*source.m_space, offs, data, size, !m_no_translation); return; } @@ -900,18 +985,44 @@ void debug_view_memory::write(u8 size, offs_t offs, u64 data) // ignore if out of bounds offs ^= source.m_offsetxor; - if (offs >= source.m_length) + if (offs >= (source.m_blocklength * source.m_numblocks)) return; - *((u8 *)source.m_base + offs) = data; + *(reinterpret_cast<u8 *>(source.m_base) + (offs / source.m_blocklength * source.m_blockstride) + (offs % source.m_blocklength)) = data; +} + + +//------------------------------------------------- +// write_digit - write one hex or octal digit +// at the given address and bit position +//------------------------------------------------- + +bool debug_view_memory::write_digit(offs_t offs, u8 pos, u8 digit) +{ + const debug_view_memory_source &source = downcast<const debug_view_memory_source &>(*m_source); + offs_t address = (source.m_space != nullptr) ? source.m_space->byte_to_address(offs) : offs; + u64 data; + bool ismapped = read(m_bytes_per_chunk, address, data); + if (!ismapped) + return false; -// hack for FD1094 editing -#ifdef FD1094_HACK - if (source.m_base == machine().root_device().memregion("user2")) + // clamp to chunk size + if (m_bytes_per_chunk * 8 < pos + m_shift_bits) { - extern void fd1094_regenerate_key(running_machine &machine); - fd1094_regenerate_key(machine()); + assert(m_bytes_per_chunk * 8 > pos); + digit &= util::make_bitmask<u8>(m_bytes_per_chunk * 8 - pos); } -#endif + + u64 write_data = (data & ~(util::make_bitmask<u64>(m_shift_bits) << pos)) | (u64(digit) << pos); + write(m_bytes_per_chunk, address, write_data); + + // verify that data reads back as it was written + if (source.m_space != nullptr) + { + read(m_bytes_per_chunk, address, data); + return data == write_data; + } + else + return true; } @@ -948,15 +1059,15 @@ void debug_view_memory::set_chunks_per_row(u32 rowchunks) //------------------------------------------------- // set_data_format - specify what kind of values -// are shown, 1-8 8-64 bits, 9 32bit floating point +// are shown //------------------------------------------------- -void debug_view_memory::set_data_format(int format) +void debug_view_memory::set_data_format(data_format format) { cursor_pos pos; // should never be - if ((format <= 0) || (format > 11)) + if (!is_valid_format(format)) return; // no need to change if (format == m_data_format) @@ -964,47 +1075,42 @@ void debug_view_memory::set_data_format(int format) pos = begin_update_and_get_cursor_pos(); const debug_view_memory_source &source = downcast<const debug_view_memory_source &>(*m_source); - if ((format <= 8) && (m_data_format <= 8)) { - + if (is_hex_format(format) && is_hex_format(m_data_format)) { pos.m_address += (pos.m_shift / 8) ^ ((source.m_endianness == ENDIANNESS_LITTLE) ? 0 : (m_bytes_per_chunk - 1)); pos.m_shift %= 8; - m_bytes_per_chunk = format; + m_bytes_per_chunk = get_posdata(format).m_bytes; m_steps_per_chunk = source.m_space ? source.m_space->byte_to_address(m_bytes_per_chunk) : m_bytes_per_chunk; - m_chunks_per_row = m_bytes_per_row / format; + m_chunks_per_row = m_bytes_per_row / m_bytes_per_chunk; if (m_chunks_per_row < 1) m_chunks_per_row = 1; pos.m_shift += 8 * ((pos.m_address % m_bytes_per_chunk) ^ ((source.m_endianness == ENDIANNESS_LITTLE) ? 0 : (m_bytes_per_chunk - 1))); pos.m_address -= pos.m_address % m_bytes_per_chunk; } else { - if (format <= 8) { + if (is_hex_format(format)) { m_supports_cursor = true; m_edit_enabled = true; - - m_bytes_per_chunk = format; + m_shift_bits = 4; + } + else if (is_octal_format(format)) { + m_supports_cursor = true; + m_edit_enabled = true; + m_shift_bits = 3; } else { m_supports_cursor = false; m_edit_enabled = false; m_cursor_visible = false; - - switch (format) - { - case 9: - m_bytes_per_chunk = 4; - break; - case 10: - m_bytes_per_chunk = 8; - break; - case 11: - m_bytes_per_chunk = 10; - break; - } + m_shift_bits = 0; } + + m_bytes_per_chunk = get_posdata(format).m_bytes; m_chunks_per_row = m_bytes_per_row / m_bytes_per_chunk; + if (m_chunks_per_row < 1) + m_chunks_per_row = 1; m_steps_per_chunk = source.m_space ? source.m_space->byte_to_address(m_bytes_per_chunk) : m_bytes_per_chunk; - pos.m_shift = 0; + pos.m_shift = get_posdata(format).m_shift[0] & 0x7f; pos.m_address -= pos.m_address % m_bytes_per_chunk; } m_recompute = m_update_pending = true; @@ -1053,3 +1159,22 @@ void debug_view_memory::set_physical(bool physical) m_recompute = m_update_pending = true; end_update_and_set_cursor_pos(pos); } + + +//------------------------------------------------- +// set_address_radix - specify whether the memory +// view should display addresses in base 8, base +// 10 or base 16 +//------------------------------------------------- + +void debug_view_memory::set_address_radix(int radix) +{ + if (radix != 8 && radix != 10 && radix != 16) + return; + + cursor_pos pos = begin_update_and_get_cursor_pos(); + m_address_radix = radix; + m_expression.set_default_base(radix); + m_recompute = m_update_pending = true; + end_update_and_set_cursor_pos(pos); +} diff --git a/src/emu/debug/dvmemory.h b/src/emu/debug/dvmemory.h index a9341afcf76..6f1ffab4e9e 100644 --- a/src/emu/debug/dvmemory.h +++ b/src/emu/debug/dvmemory.h @@ -15,8 +15,7 @@ #include "debugvw.h" -#include "softfloat/mamesf.h" -#include "softfloat/softfloat.h" +#include "softfloat3/source/include/softfloat.h" //************************************************************************** @@ -28,21 +27,23 @@ class debug_view_memory_source : public debug_view_source { friend class debug_view_memory; - debug_view_memory_source(const char *name, address_space &space); - debug_view_memory_source(const char *name, memory_region ®ion); - debug_view_memory_source(const char *name, void *base, int element_size, int num_elements); - public: + debug_view_memory_source(std::string &&name, address_space &space); + debug_view_memory_source(std::string &&name, memory_region ®ion); + debug_view_memory_source(std::string &&name, void *base, int element_size, int num_elements, int num_blocks, int block_stride); + address_space *space() const { return m_space; } private: - address_space *m_space; // address space we reference (if any) + address_space *m_space; // address space we reference (if any) device_memory_interface *m_memintf; // pointer to the memory interface of the device - void * m_base; // pointer to memory base - offs_t m_length; // length of memory - offs_t m_offsetxor; // XOR to apply to offsets - endianness_t m_endianness; // endianness of memory - u8 m_prefsize; // preferred bytes per chunk + void * m_base; // pointer to memory base + offs_t m_blocklength; // length of each block of memory + offs_t m_numblocks; // number of blocks of memory + offs_t m_blockstride; // stride between blocks of memory + offs_t m_offsetxor; // XOR to apply to offsets + endianness_t m_endianness; // endianness of memory + u8 m_prefsize; // preferred bytes per chunk }; @@ -54,23 +55,43 @@ class debug_view_memory : public debug_view // construction/destruction debug_view_memory(running_machine &machine, debug_view_osd_update_func osdupdate, void *osdprivate); + struct memory_view_pos; + public: + enum class data_format + { + HEX_8BIT = 1, + HEX_16BIT = 2, + HEX_32BIT = 4, + HEX_64BIT = 8, + FLOAT_32BIT = 9, + FLOAT_64BIT = 10, + FLOAT_80BIT = 11, + OCTAL_8BIT = 12, + OCTAL_16BIT = 13, + OCTAL_32BIT = 14, + OCTAL_64BIT = 15 + }; + static bool is_valid_format(data_format format) { return int(format) >= 0 && int(format) < std::size(s_memory_pos_table) && get_posdata(format).m_bytes != 0; } + // getters const char *expression() const { return m_expression.string(); } - int get_data_format() { flush_updates(); return m_data_format; } + data_format get_data_format() { flush_updates(); return m_data_format; } u32 chunks_per_row() { flush_updates(); return m_chunks_per_row; } bool reverse() const { return m_reverse_view; } bool ascii() const { return m_ascii_view; } bool physical() const { return m_no_translation; } + int address_radix() const { return m_address_radix; } offs_t addressAtCursorPosition(const debug_view_xy& pos) { return get_cursor_pos(pos).m_address; } // setters void set_expression(const std::string &expression); void set_chunks_per_row(u32 rowchunks); - void set_data_format(int format); // 1-8 current values 9 32bit floating point + void set_data_format(data_format format); void set_reverse(bool reverse); - void set_ascii(bool reverse); + void set_ascii(bool ascii); void set_physical(bool physical); + void set_address_radix(int radix); protected: // view overrides @@ -87,6 +108,11 @@ private: u8 m_shift; }; + // data format helpers + static bool is_hex_format(data_format format) { return int(format) <= 8; } + static bool is_octal_format(data_format format) { return int(format) >= 12; } + static const memory_view_pos &get_posdata(data_format format) { return s_memory_pos_table[int(format)]; } + // internal helpers void enumerate_sources(); void recompute(); @@ -101,19 +127,23 @@ private: // memory access bool read(u8 size, offs_t offs, u64 &data); void write(u8 size, offs_t offs, u64 data); - bool read(u8 size, offs_t offs, floatx80 &data); + bool write_digit(offs_t offs, u8 pos, u8 digit); + bool read(u8 size, offs_t offs, extFloat80_t &data); bool read_chunk(offs_t address, int chunknum, u64 &chunkdata); + void generate_row(debug_view_char *destmin, debug_view_char *destmax, debug_view_char *destrow, offs_t address); // internal state debug_view_expression m_expression; // expression describing the start address u32 m_chunks_per_row; // number of chunks displayed per line u8 m_bytes_per_chunk; // bytes per chunk u8 m_steps_per_chunk; // bytes per chunk - int m_data_format; // 1-8 current values 9 32bit floating point + data_format m_data_format; // 1-8 current values 9 32bit floating point bool m_reverse_view; // reverse-endian view? bool m_ascii_view; // display ASCII characters? bool m_no_translation; // don't run addresses through the cpu translation hook bool m_edit_enabled; // can modify contents ? + u8 m_shift_bits; // number of bits for each character/cursor position + u8 m_address_radix; // numerical radix for address column and expressions offs_t m_maxaddr; // (derived) maximum address to display u32 m_bytes_per_row; // (derived) number of bytes displayed per line u32 m_byte_offset; // (derived) offset of starting visible byte @@ -129,10 +159,11 @@ private: struct memory_view_pos { - u8 m_spacing; /* spacing between each entry */ - u8 m_shift[24]; /* shift for each character */ + u8 m_bytes; // bytes per entry + u8 m_spacing; // spacing between each entry + u8 m_shift[28]; // shift for each character }; - static const memory_view_pos s_memory_pos_table[12]; // table for rendering at different data formats + static const memory_view_pos s_memory_pos_table[16]; // table for rendering at different data formats // constants static constexpr int MEM_MAX_LINE_WIDTH = 1024; diff --git a/src/emu/debug/dvrpoints.cpp b/src/emu/debug/dvrpoints.cpp new file mode 100644 index 00000000000..a36f42bd1d0 --- /dev/null +++ b/src/emu/debug/dvrpoints.cpp @@ -0,0 +1,298 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +/********************************************************************* + + dvrpoints.cpp + + Registerpoint debugger view. + +***************************************************************************/ + +#include "emu.h" +#include "dvrpoints.h" + +#include "debugcpu.h" +#include "points.h" + +#include <algorithm> +#include <iomanip> + + +namespace { + +bool cIndexAscending(std::pair<device_t *, debug_registerpoint const *> const &a, std::pair<device_t *, debug_registerpoint const *> const &b) +{ + return a.second->index() < b.second->index(); +} + +bool cIndexDescending(std::pair<device_t *, debug_registerpoint const *> const &a, std::pair<device_t *, debug_registerpoint const *> const &b) +{ + return a.second->index() > b.second->index(); +} + +bool cEnabledAscending(std::pair<device_t *, debug_registerpoint const *> const &a, std::pair<device_t *, debug_registerpoint const *> const &b) +{ + return !a.second->enabled() && b.second->enabled(); +} + +bool cEnabledDescending(std::pair<device_t *, debug_registerpoint const *> const &a, std::pair<device_t *, debug_registerpoint const *> const &b) +{ + return cEnabledAscending(b, a); +} + +bool cCpuAscending(std::pair<device_t *, debug_registerpoint const *> const &a, std::pair<device_t *, debug_registerpoint const *> const &b) +{ + return strcmp(a.first->tag(), b.first->tag()) < 0; +} + +bool cCpuDescending(std::pair<device_t *, debug_registerpoint const *> const &a, std::pair<device_t *, debug_registerpoint const *> const &b) +{ + return cCpuAscending(b, a); +} + +bool cConditionAscending(std::pair<device_t *, debug_registerpoint const *> const &a, std::pair<device_t *, debug_registerpoint const *> const &b) +{ + return strcmp(a.second->condition(), b.second->condition()) < 0; +} + +bool cConditionDescending(std::pair<device_t *, debug_registerpoint const *> const &a, std::pair<device_t *, debug_registerpoint const *> const &b) +{ + return cConditionAscending(b, a); +} + +bool cActionAscending(std::pair<device_t *, debug_registerpoint const *> const &a, std::pair<device_t *, debug_registerpoint const *> const &b) +{ + return a.second->action() < b.second->action(); +} + +bool cActionDescending(std::pair<device_t *, debug_registerpoint const *> const &a, std::pair<device_t *, debug_registerpoint const *> const &b) +{ + return cActionAscending(b, a); +} + + +constexpr int TABLE_BREAKS[] = { 5, 9, 31, 49, 66 }; + +} // anonymous namespace + + +//************************************************************************** +// DEBUG VIEW REGISTER POINTS +//************************************************************************** + + +//------------------------------------------------- +// debug_view_registerpoints - constructor +//------------------------------------------------- + +debug_view_registerpoints::debug_view_registerpoints(running_machine &machine, debug_view_osd_update_func osdupdate, void *osdprivate) + : debug_view(machine, DVT_REGISTER_POINTS, osdupdate, osdprivate) + , m_sort_type(cIndexAscending) +{ + // fail if no available sources + enumerate_sources(); + if (m_source_list.empty()) + throw std::bad_alloc(); +} + + +//------------------------------------------------- +// ~debug_view_registerpoints - destructor +//------------------------------------------------- + +debug_view_registerpoints::~debug_view_registerpoints() +{ +} + + +//------------------------------------------------- +// enumerate_sources - enumerate all possible +// sources for a disassembly view +//------------------------------------------------- + +void debug_view_registerpoints::enumerate_sources() +{ + // start with an empty list + m_source_list.clear(); + + // iterate over devices with disassembly interfaces + for (device_disasm_interface &dasm : disasm_interface_enumerator(machine().root_device())) + { + m_source_list.emplace_back( + std::make_unique<debug_view_source>( + util::string_format("%s '%s'", dasm.device().name(), dasm.device().tag()), + &dasm.device())); + } + + // reset the source to a known good entry + if (!m_source_list.empty()) + set_source(*m_source_list[0]); +} + + +//------------------------------------------------- +// view_click - handle a mouse click within the +// current view +//------------------------------------------------- + +void debug_view_registerpoints::view_click(const int button, const debug_view_xy& pos) +{ + bool clickedTopRow = (m_topleft.y == pos.y); + + if (clickedTopRow) + { + if (pos.x < TABLE_BREAKS[0]) + m_sort_type = (m_sort_type == &cIndexAscending) ? &cIndexDescending : &cIndexAscending; + else if (pos.x < TABLE_BREAKS[1]) + m_sort_type = (m_sort_type == &cEnabledAscending) ? &cEnabledDescending : &cEnabledAscending; + else if (pos.x < TABLE_BREAKS[2]) + m_sort_type = (m_sort_type == &cCpuAscending) ? &cCpuDescending : &cCpuAscending; + else if (pos.x < TABLE_BREAKS[3]) + m_sort_type = (m_sort_type == &cConditionAscending) ? &cConditionDescending : &cConditionAscending; + else if (pos.x < TABLE_BREAKS[4]) + m_sort_type = (m_sort_type == &cActionAscending) ? &cActionDescending : &cActionAscending; + } + else + { + // Gather a sorted list of all the breakpoints for all the CPUs + gather_registerpoints(); + + int rpIndex = pos.y - 1; + if ((rpIndex >= m_buffer.size()) || (rpIndex < 0)) + return; + + // Enable / disable + m_buffer[rpIndex].first->debug()->registerpoint_enable( + m_buffer[rpIndex].second->index(), + !m_buffer[rpIndex].second->enabled()); + } + + begin_update(); + m_update_pending = true; + end_update(); +} + + +void debug_view_registerpoints::pad_ostream_to_length(std::ostream& str, int len) +{ + auto const current = str.tellp(); + if (current < decltype(current)(len)) + str << std::setw(decltype(current)(len) - current) << ""; +} + + +void debug_view_registerpoints::gather_registerpoints() +{ + m_buffer.resize(0); + for (auto &source : m_source_list) + { + // Collect + device_debug &debugInterface = *source->device()->debug(); + for (const auto &rp : debugInterface.registerpoint_list()) + m_buffer.emplace_back(source->device(), &rp); + } + + // And now for the sort + if (!m_buffer.empty()) + std::stable_sort(m_buffer.begin(), m_buffer.end(), m_sort_type); +} + + +//------------------------------------------------- +// view_update - update the contents of the +// registerpoints view +//------------------------------------------------- + +void debug_view_registerpoints::view_update() +{ + // Gather a list of all the registerpoints for all the CPUs + gather_registerpoints(); + + // Set the view region so the scroll bars update + m_total.x = TABLE_BREAKS[std::size(TABLE_BREAKS) - 1]; + m_total.y = m_buffer.size() + 1; + if (m_total.y < 10) + m_total.y = 10; + + // Draw + debug_view_char *dest = &m_viewdata[0]; + util::ovectorstream linebuf; + linebuf.reserve(std::size(TABLE_BREAKS) - 1); + + // Header + if (m_visible.y > 0) + { + linebuf.clear(); + linebuf.rdbuf()->clear(); + linebuf << "ID"; + if (m_sort_type == &cIndexAscending) linebuf.put('\\'); + else if (m_sort_type == &cIndexDescending) linebuf.put('/'); + pad_ostream_to_length(linebuf, TABLE_BREAKS[0]); + linebuf << "En"; + if (m_sort_type == &cEnabledAscending) linebuf.put('\\'); + else if (m_sort_type == &cEnabledDescending) linebuf.put('/'); + pad_ostream_to_length(linebuf, TABLE_BREAKS[1]); + linebuf << "CPU"; + if (m_sort_type == &cCpuAscending) linebuf.put('\\'); + else if (m_sort_type == &cCpuDescending) linebuf.put('/'); + pad_ostream_to_length(linebuf, TABLE_BREAKS[2]); + linebuf << "Condition"; + if (m_sort_type == &cConditionAscending) linebuf.put('\\'); + else if (m_sort_type == &cConditionDescending) linebuf.put('/'); + pad_ostream_to_length(linebuf, TABLE_BREAKS[3]); + linebuf << "Action"; + if (m_sort_type == &cActionAscending) linebuf.put('\\'); + else if (m_sort_type == &cActionDescending) linebuf.put('/'); + pad_ostream_to_length(linebuf, TABLE_BREAKS[4]); + + auto const &text(linebuf.vec()); + for (u32 i = m_topleft.x; i < (m_topleft.x + m_visible.x); i++, dest++) + { + dest->byte = (i < text.size()) ? text[i] : ' '; + dest->attrib = DCA_ANCILLARY; + } + } + + for (int row = 1; row < m_visible.y; row++) + { + // Breakpoints + int rpi = row + m_topleft.y - 1; + if ((rpi < m_buffer.size()) && (rpi >= 0)) + { + point_pair const &rpp = m_buffer[rpi]; + + linebuf.clear(); + linebuf.rdbuf()->clear(); + util::stream_format(linebuf, "%2X", rpp.second->index()); + pad_ostream_to_length(linebuf, TABLE_BREAKS[0]); + linebuf.put(rpp.second->enabled() ? 'X' : 'O'); + pad_ostream_to_length(linebuf, TABLE_BREAKS[1]); + linebuf << rpp.first->tag(); + pad_ostream_to_length(linebuf, TABLE_BREAKS[2]); + linebuf << rpp.second->condition(); + pad_ostream_to_length(linebuf, TABLE_BREAKS[3]); + linebuf << rpp.second->action(); + pad_ostream_to_length(linebuf, TABLE_BREAKS[4]); + + auto const &text(linebuf.vec()); + for (u32 i = m_topleft.x; i < (m_topleft.x + m_visible.x); i++, dest++) + { + dest->byte = (i < text.size()) ? text[i] : ' '; + dest->attrib = DCA_NORMAL; + + // Color disabled breakpoints red + if ((i >= TABLE_BREAKS[0]) && (i < TABLE_BREAKS[1]) && !rpp.second->enabled()) + dest->attrib |= DCA_CHANGED; + } + } + else + { + // Fill the remaining vertical space + for (u32 i = m_topleft.x; i < (m_topleft.x + m_visible.x); i++, dest++) + { + dest->byte = ' '; + dest->attrib = DCA_NORMAL; + } + } + } +} diff --git a/src/emu/debug/dvrpoints.h b/src/emu/debug/dvrpoints.h new file mode 100644 index 00000000000..31b821d7fae --- /dev/null +++ b/src/emu/debug/dvrpoints.h @@ -0,0 +1,52 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +/********************************************************************* + + dvrpoints.h + + Registerpoint debugger view. + +***************************************************************************/ +#ifndef MAME_EMU_DEBUG_DVRPOINTS_H +#define MAME_EMU_DEBUG_DVRPOINTS_H + +#pragma once + +#include "debugvw.h" + +#include <utility> +#include <vector> + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// debug view for breakpoints +class debug_view_registerpoints : public debug_view +{ + friend class debug_view_manager; + + // construction/destruction + debug_view_registerpoints(running_machine &machine, debug_view_osd_update_func osdupdate, void *osdprivate); + virtual ~debug_view_registerpoints(); + +protected: + // view overrides + virtual void view_update() override; + virtual void view_click(int button, debug_view_xy const &pos) override; + +private: + using point_pair = std::pair<device_t *, debug_registerpoint const *>; + + // internal helpers + void enumerate_sources(); + void pad_ostream_to_length(std::ostream& str, int len); + void gather_registerpoints(); + + // internal state + bool (*m_sort_type)(point_pair const &, point_pair const &); + std::vector<point_pair> m_buffer; +}; + +#endif // MAME_EMU_DEBUG_DVBPOINTS_H diff --git a/src/emu/debug/dvstate.cpp b/src/emu/debug/dvstate.cpp index e0004f9bb60..931ae51111b 100644 --- a/src/emu/debug/dvstate.cpp +++ b/src/emu/debug/dvstate.cpp @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles /********************************************************************* - dvstate.c + dvstate.cpp State debugger view. @@ -16,13 +16,6 @@ #include "screen.h" -const int debug_view_state::REG_DIVIDER; -const int debug_view_state::REG_CYCLES; -const int debug_view_state::REG_BEAMX; -const int debug_view_state::REG_BEAMY; -const int debug_view_state::REG_FRAME; - - //************************************************************************** // DEBUG VIEW STATE SOURCE //************************************************************************** @@ -31,8 +24,8 @@ const int debug_view_state::REG_FRAME; // debug_view_state_source - constructor //------------------------------------------------- -debug_view_state_source::debug_view_state_source(const char *name, device_t &device) - : debug_view_source(name, &device) +debug_view_state_source::debug_view_state_source(std::string &&name, device_t &device) + : debug_view_source(std::move(name), &device) , m_stateintf(dynamic_cast<device_state_interface *>(&device)) , m_execintf(dynamic_cast<device_execute_interface *>(&device)) { @@ -55,7 +48,7 @@ debug_view_state::debug_view_state(running_machine &machine, debug_view_osd_upda { // fail if no available sources enumerate_sources(); - if (m_source_list.count() == 0) + if (m_source_list.empty()) throw std::bad_alloc(); } @@ -78,18 +71,20 @@ debug_view_state::~debug_view_state() void debug_view_state::enumerate_sources() { // start with an empty list - m_source_list.reset(); + m_source_list.clear(); // iterate over devices that have state interfaces - std::string name; - for (device_state_interface &state : state_interface_iterator(machine().root_device())) + for (device_state_interface &state : state_interface_enumerator(machine().root_device())) { - name = string_format("%s '%s'", state.device().name(), state.device().tag()); - m_source_list.append(*global_alloc(debug_view_state_source(name.c_str(), state.device()))); + m_source_list.emplace_back( + std::make_unique<debug_view_state_source>( + util::string_format("%s '%s'", state.device().name(), state.device().tag()), + state.device())); } // reset the source to a known good entry - set_source(*m_source_list.first()); + if (!m_source_list.empty()) + set_source(*m_source_list[0]); } @@ -119,8 +114,8 @@ void debug_view_state::recompute() // add a cycles entry: cycles:99999999 m_state_list.emplace_back(REG_CYCLES, "cycles", 8); - screen_device_iterator screen_iterator = screen_device_iterator(machine().root_device()); - screen_device_iterator::auto_iterator iter = screen_iterator.begin(); + screen_device_enumerator screen_iterator(machine().root_device()); + screen_device_enumerator::iterator iter = screen_iterator.begin(); const int screen_count = screen_iterator.count(); if (screen_count == 1) @@ -145,7 +140,9 @@ void debug_view_state::recompute() } // add a flags entry: flags:xxxxxxxx - m_state_list.emplace_back(STATE_GENFLAGS, "flags", source.m_stateintf->state_string_max_length(STATE_GENFLAGS)); + const device_state_entry *flags = source.m_stateintf->state_find_entry(STATE_GENFLAGS); + if (flags != nullptr) + m_state_list.emplace_back(STATE_GENFLAGS, "flags", flags->max_length()); // add a divider entry m_state_list.emplace_back(REG_DIVIDER, "", 0); @@ -156,7 +153,7 @@ void debug_view_state::recompute() if (entry->divider()) m_state_list.emplace_back(REG_DIVIDER, "", 0); else if (entry->visible()) - m_state_list.emplace_back(entry->index(), entry->symbol(), source.m_stateintf->state_string_max_length(entry->index())); + m_state_list.emplace_back(entry->index(), entry->symbol(), entry->max_length()); } // count the entries and determine the maximum tag and value sizes @@ -239,25 +236,28 @@ void debug_view_state::view_update() case REG_BEAMX_S0: case REG_BEAMX_S1: case REG_BEAMX_S2: case REG_BEAMX_S3: case REG_BEAMX_S4: case REG_BEAMX_S5: case REG_BEAMX_S6: case REG_BEAMX_S7: - curitem.update(screen_device_iterator(machine().root_device()).byindex(-(curitem.index() - REG_BEAMX_S0))->hpos(), cycles_changed); + curitem.update(screen_device_enumerator(machine().root_device()).byindex(-(curitem.index() - REG_BEAMX_S0))->hpos(), cycles_changed); valstr = string_format("%4d", curitem.value()); break; case REG_BEAMY_S0: case REG_BEAMY_S1: case REG_BEAMY_S2: case REG_BEAMY_S3: case REG_BEAMY_S4: case REG_BEAMY_S5: case REG_BEAMY_S6: case REG_BEAMY_S7: - curitem.update(screen_device_iterator(machine().root_device()).byindex(-(curitem.index() - REG_BEAMY_S0))->vpos(), cycles_changed); + curitem.update(screen_device_enumerator(machine().root_device()).byindex(-(curitem.index() - REG_BEAMY_S0))->vpos(), cycles_changed); valstr = string_format("%4d", curitem.value()); break; case REG_FRAME_S0: case REG_FRAME_S1: case REG_FRAME_S2: case REG_FRAME_S3: case REG_FRAME_S4: case REG_FRAME_S5: case REG_FRAME_S6: case REG_FRAME_S7: - curitem.update(screen_device_iterator(machine().root_device()).byindex(-(curitem.index() - REG_FRAME_S0))->frame_number(), cycles_changed); + curitem.update(screen_device_enumerator(machine().root_device()).byindex(-(curitem.index() - REG_FRAME_S0))->frame_number(), cycles_changed); valstr = string_format("%-6d", curitem.value()); break; default: curitem.update(source.m_stateintf->state_int(curitem.index()), cycles_changed); valstr = source.m_stateintf->state_string(curitem.index()); + // state_string may not always provide the maximum number of characters with some formats + valstr.resize(curitem.value_length(), ' '); + break; } // if this row is visible, add it to the buffer diff --git a/src/emu/debug/dvstate.h b/src/emu/debug/dvstate.h index 8239a53f5db..63c0eb38822 100644 --- a/src/emu/debug/dvstate.h +++ b/src/emu/debug/dvstate.h @@ -25,8 +25,10 @@ class debug_view_state_source : public debug_view_source { friend class debug_view_state; +public: // construction/destruction - debug_view_state_source(const char *name, device_t &device); + debug_view_state_source(std::string &&name, device_t &device); + private: // internal state device_state_interface *m_stateintf; // state interface diff --git a/src/emu/debug/dvtext.cpp b/src/emu/debug/dvtext.cpp index e55a6b278e8..6a4d2893562 100644 --- a/src/emu/debug/dvtext.cpp +++ b/src/emu/debug/dvtext.cpp @@ -2,17 +2,19 @@ // copyright-holders:Aaron Giles /********************************************************************* - dvtext.c + dvtext.cpp Debugger simple text-based views. ***************************************************************************/ #include "emu.h" -#include "debugvw.h" #include "dvtext.h" + #include "debugcon.h" #include "debugger.h" +#include "debugvw.h" + //************************************************************************** // DEBUG VIEW TEXTBUF @@ -22,11 +24,16 @@ // debug_view_textbuf - constructor //------------------------------------------------- -debug_view_textbuf::debug_view_textbuf(running_machine &machine, debug_view_type type, debug_view_osd_update_func osdupdate, void *osdprivate, text_buffer &textbuf) - : debug_view(machine, type, osdupdate, osdprivate), - m_textbuf(textbuf), - m_at_bottom(true), - m_topseq(0) +debug_view_textbuf::debug_view_textbuf( + running_machine &machine, + debug_view_type type, + debug_view_osd_update_func osdupdate, + void *osdprivate, + text_buffer &textbuf) + : debug_view(machine, type, osdupdate, osdprivate) + , m_textbuf(textbuf) + , m_at_bottom(true) + , m_topseq(0) { } @@ -47,7 +54,7 @@ debug_view_textbuf::~debug_view_textbuf() void debug_view_textbuf::clear() { begin_update(); - text_buffer_clear(&m_textbuf); + text_buffer_clear(m_textbuf); m_at_bottom = true; m_topseq = 0; end_update(); @@ -61,8 +68,8 @@ void debug_view_textbuf::clear() void debug_view_textbuf::view_update() { // update the console info - m_total.x = text_buffer_max_width(&m_textbuf); - m_total.y = text_buffer_num_lines(&m_textbuf); + m_total.x = text_buffer_max_width(m_textbuf); + m_total.y = text_buffer_num_lines(m_textbuf); if (m_total.x < 80) m_total.x = 80; @@ -71,24 +78,24 @@ void debug_view_textbuf::view_update() if (!m_at_bottom) { curseq = m_topseq; - if (!text_buffer_get_seqnum_line(&m_textbuf, curseq)) + if (!text_buffer_get_seqnum_line(m_textbuf, curseq)) m_at_bottom = true; } if (m_at_bottom) { - curseq = text_buffer_line_index_to_seqnum(&m_textbuf, m_total.y - 1); + curseq = text_buffer_line_index_to_seqnum(m_textbuf, m_total.y - 1); if (m_total.y < m_visible.y) curseq -= m_total.y - 1; else curseq -= m_visible.y - 1; } - m_topleft.y = curseq - text_buffer_line_index_to_seqnum(&m_textbuf, 0); + m_topleft.y = curseq - text_buffer_line_index_to_seqnum(m_textbuf, 0); // loop over visible rows debug_view_char *dest = &m_viewdata[0]; for (u32 row = 0; row < m_visible.y; row++) { - const char *line = text_buffer_get_seqnum_line(&m_textbuf, curseq++); + const char *line = text_buffer_get_seqnum_line(m_textbuf, curseq++); u32 col = 0; // if this visible row is valid, add it to the buffer @@ -133,7 +140,7 @@ void debug_view_textbuf::view_notify(debug_view_notification type) /* otherwise, track the seqence number of the top line */ if (!m_at_bottom) - m_topseq = text_buffer_line_index_to_seqnum(&m_textbuf, m_topleft.y); + m_topseq = text_buffer_line_index_to_seqnum(m_textbuf, m_topleft.y); } } @@ -148,7 +155,7 @@ void debug_view_textbuf::view_notify(debug_view_notification type) //------------------------------------------------- debug_view_console::debug_view_console(running_machine &machine, debug_view_osd_update_func osdupdate, void *osdprivate) - : debug_view_textbuf(machine, DVT_CONSOLE, osdupdate, osdprivate, *machine.debugger().console().get_console_textbuf()) + : debug_view_textbuf(machine, DVT_CONSOLE, osdupdate, osdprivate, machine.debugger().console().get_console_textbuf()) { } @@ -163,6 +170,6 @@ debug_view_console::debug_view_console(running_machine &machine, debug_view_osd_ //------------------------------------------------- debug_view_log::debug_view_log(running_machine &machine, debug_view_osd_update_func osdupdate, void *osdprivate) - : debug_view_textbuf(machine, DVT_LOG, osdupdate, osdprivate, *machine.debugger().console().get_errorlog_textbuf()) + : debug_view_textbuf(machine, DVT_LOG, osdupdate, osdprivate, machine.debugger().console().get_errorlog_textbuf()) { } diff --git a/src/emu/debug/dvtext.h b/src/emu/debug/dvtext.h index 14a323794db..e6b65c137b3 100644 --- a/src/emu/debug/dvtext.h +++ b/src/emu/debug/dvtext.h @@ -31,7 +31,12 @@ public: protected: // construction/destruction - debug_view_textbuf(running_machine &machine, debug_view_type type, debug_view_osd_update_func osdupdate, void *osdprivate, text_buffer &textbuf); + debug_view_textbuf( + running_machine &machine, + debug_view_type type, + debug_view_osd_update_func osdupdate, + void *osdprivate, + text_buffer &textbuf); virtual ~debug_view_textbuf(); protected: @@ -41,9 +46,9 @@ protected: private: // internal state - text_buffer & m_textbuf; /* pointer to the text buffer */ - bool m_at_bottom; /* are we tracking new stuff being added? */ - u32 m_topseq; /* sequence number of the top line */ + text_buffer & m_textbuf; // pointer to the text buffer + bool m_at_bottom; // are we tracking new stuff being added? + u32 m_topseq; // sequence number of the top line }; diff --git a/src/emu/debug/dvwpoints.cpp b/src/emu/debug/dvwpoints.cpp index b0584b5d61c..116904232a1 100644 --- a/src/emu/debug/dvwpoints.cpp +++ b/src/emu/debug/dvwpoints.cpp @@ -1,8 +1,8 @@ // license:BSD-3-Clause -// copyright-holders:Aaron Giles +// copyright-holders:Andrew Gardner, Vas Crabb /********************************************************************* - dvwpoints.c + dvwpoints.cpp Watchpoint debugger view. @@ -11,87 +11,90 @@ #include "emu.h" #include "dvwpoints.h" +#include "debugcpu.h" +#include "points.h" + #include <algorithm> #include <iomanip> -static bool cIndexAscending(const device_debug::watchpoint *a, const device_debug::watchpoint *b) +static bool cIndexAscending(const debug_watchpoint *a, const debug_watchpoint *b) { return a->index() < b->index(); } -static bool cIndexDescending(const device_debug::watchpoint *a, const device_debug::watchpoint *b) +static bool cIndexDescending(const debug_watchpoint *a, const debug_watchpoint *b) { return cIndexAscending(b, a); } -static bool cEnabledAscending(const device_debug::watchpoint *a, const device_debug::watchpoint *b) +static bool cEnabledAscending(const debug_watchpoint *a, const debug_watchpoint *b) { return !a->enabled() && b->enabled(); } -static bool cEnabledDescending(const device_debug::watchpoint *a, const device_debug::watchpoint *b) +static bool cEnabledDescending(const debug_watchpoint *a, const debug_watchpoint *b) { return cEnabledAscending(b, a); } -static bool cCpuAscending(const device_debug::watchpoint *a, const device_debug::watchpoint *b) +static bool cCpuAscending(const debug_watchpoint *a, const debug_watchpoint *b) { return strcmp(a->debugInterface()->device().tag(), b->debugInterface()->device().tag()) < 0; } -static bool cCpuDescending(const device_debug::watchpoint *a, const device_debug::watchpoint *b) +static bool cCpuDescending(const debug_watchpoint *a, const debug_watchpoint *b) { return cCpuAscending(b, a); } -static bool cSpaceAscending(const device_debug::watchpoint *a, const device_debug::watchpoint *b) +static bool cSpaceAscending(const debug_watchpoint *a, const debug_watchpoint *b) { return strcmp(a->space().name(), b->space().name()) < 0; } -static bool cSpaceDescending(const device_debug::watchpoint *a, const device_debug::watchpoint *b) +static bool cSpaceDescending(const debug_watchpoint *a, const debug_watchpoint *b) { return cSpaceAscending(b, a); } -static bool cAddressAscending(const device_debug::watchpoint *a, const device_debug::watchpoint *b) +static bool cAddressAscending(const debug_watchpoint *a, const debug_watchpoint *b) { return a->address() < b->address(); } -static bool cAddressDescending(const device_debug::watchpoint *a, const device_debug::watchpoint *b) +static bool cAddressDescending(const debug_watchpoint *a, const debug_watchpoint *b) { return cAddressAscending(b, a); } -static bool cTypeAscending(const device_debug::watchpoint *a, const device_debug::watchpoint *b) +static bool cTypeAscending(const debug_watchpoint *a, const debug_watchpoint *b) { return int(a->type()) < int(b->type()); } -static bool cTypeDescending(const device_debug::watchpoint *a, const device_debug::watchpoint *b) +static bool cTypeDescending(const debug_watchpoint *a, const debug_watchpoint *b) { return cTypeAscending(b, a); } -static bool cConditionAscending(const device_debug::watchpoint *a, const device_debug::watchpoint *b) +static bool cConditionAscending(const debug_watchpoint *a, const debug_watchpoint *b) { return strcmp(a->condition(), b->condition()) < 0; } -static bool cConditionDescending(const device_debug::watchpoint *a, const device_debug::watchpoint *b) +static bool cConditionDescending(const debug_watchpoint *a, const debug_watchpoint *b) { return cConditionAscending(b, a); } -static bool cActionAscending(const device_debug::watchpoint *a, const device_debug::watchpoint *b) +static bool cActionAscending(const debug_watchpoint *a, const debug_watchpoint *b) { return a->action() < b->action(); } -static bool cActionDescending(const device_debug::watchpoint *a, const device_debug::watchpoint *b) +static bool cActionDescending(const debug_watchpoint *a, const debug_watchpoint *b) { return cActionAscending(b, a); } @@ -113,7 +116,7 @@ debug_view_watchpoints::debug_view_watchpoints(running_machine &machine, debug_v { // fail if no available sources enumerate_sources(); - if (m_source_list.count() == 0) + if (m_source_list.empty()) throw std::bad_alloc(); } @@ -135,18 +138,20 @@ debug_view_watchpoints::~debug_view_watchpoints() void debug_view_watchpoints::enumerate_sources() { // start with an empty list - m_source_list.reset(); + m_source_list.clear(); // iterate over devices with disassembly interfaces - for (device_disasm_interface &dasm : disasm_interface_iterator(machine().root_device())) + for (device_disasm_interface &dasm : disasm_interface_enumerator(machine().root_device())) { - std::string name; - name = string_format("%s '%s'", dasm.device().name(), dasm.device().tag()); - m_source_list.append(*global_alloc(debug_view_source(name.c_str(), &dasm.device()))); + m_source_list.emplace_back( + std::make_unique<debug_view_source>( + util::string_format("%s '%s'", dasm.device().name(), dasm.device().tag()), + &dasm.device())); } // reset the source to a known good entry - set_source(*m_source_list.first()); + if (!m_source_list.empty()) + set_source(*m_source_list[0]); } @@ -208,10 +213,10 @@ void debug_view_watchpoints::pad_ostream_to_length(std::ostream& str, int len) void debug_view_watchpoints::gather_watchpoints() { m_buffer.resize(0); - for (const debug_view_source &source : m_source_list) + for (auto &source : m_source_list) { // Collect - device_debug &debugInterface = *source.device()->debug(); + device_debug &debugInterface = *source->device()->debug(); for (int spacenum = 0; spacenum < debugInterface.watchpoint_space_count(); ++spacenum) { for (const auto &wp : debugInterface.watchpoint_vector(spacenum)) @@ -236,7 +241,7 @@ void debug_view_watchpoints::view_update() gather_watchpoints(); // Set the view region so the scroll bars update - m_total.x = tableBreaks[ARRAY_LENGTH(tableBreaks) - 1]; + m_total.x = tableBreaks[std::size(tableBreaks) - 1]; m_total.y = m_buffer.size() + 1; if (m_total.y < 10) m_total.y = 10; @@ -244,7 +249,7 @@ void debug_view_watchpoints::view_update() // Draw debug_view_char *dest = &m_viewdata[0]; util::ovectorstream linebuf; - linebuf.reserve(ARRAY_LENGTH(tableBreaks) - 1); + linebuf.reserve(std::size(tableBreaks) - 1); // Header if (m_visible.y > 0) @@ -299,7 +304,7 @@ void debug_view_watchpoints::view_update() if ((wpi < m_buffer.size()) && wpi >= 0) { static char const *const types[] = { "unkn ", "read ", "write", "r/w " }; - device_debug::watchpoint *const wp = m_buffer[wpi]; + debug_watchpoint *const wp = m_buffer[wpi]; linebuf.clear(); linebuf.rdbuf()->clear(); diff --git a/src/emu/debug/dvwpoints.h b/src/emu/debug/dvwpoints.h index 26b7c676e0e..df1145742b6 100644 --- a/src/emu/debug/dvwpoints.h +++ b/src/emu/debug/dvwpoints.h @@ -1,5 +1,5 @@ // license:BSD-3-Clause -// copyright-holders:Aaron Giles +// copyright-holders:Andrew Gardner, Vas Crabb /********************************************************************* dvwpoints.h @@ -7,14 +7,12 @@ Watchpoint debugger view. ***************************************************************************/ - #ifndef MAME_EMU_DEBUG_DVWPOINTS_H #define MAME_EMU_DEBUG_DVWPOINTS_H #pragma once #include "debugvw.h" -#include "debugcpu.h" //************************************************************************** @@ -43,8 +41,8 @@ private: // internal state - bool (*m_sortType)(const device_debug::watchpoint *, const device_debug::watchpoint *); - std::vector<device_debug::watchpoint *> m_buffer; + bool (*m_sortType)(const debug_watchpoint *, const debug_watchpoint *); + std::vector<debug_watchpoint *> m_buffer; }; #endif // MAME_EMU_DEBUG_DVWPOINTS_H diff --git a/src/emu/debug/express.cpp b/src/emu/debug/express.cpp index bcd9269db30..9ab75b7b655 100644 --- a/src/emu/debug/express.cpp +++ b/src/emu/debug/express.cpp @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles /*************************************************************************** - express.c + express.cpp Generic expressions engine. @@ -31,7 +31,9 @@ #include "emu.h" #include "express.h" -#include <ctype.h> + +#include "corestr.h" +#include <cctype> @@ -39,19 +41,17 @@ DEBUGGING ***************************************************************************/ -#define DEBUG_TOKENS 0 +#define LOG_OUTPUT_FUNC osd_printf_info +#define VERBOSE 0 +#include "logmacro.h" +namespace { /*************************************************************************** CONSTANTS ***************************************************************************/ -#ifndef DEFAULT_BASE -#define DEFAULT_BASE 16 // hex unless otherwise specified -#endif - - // token.value values if token.is_operator() enum { @@ -104,7 +104,7 @@ enum //************************************************************************** -// TYPE DEFINITIONS +// REGISTER SYMBOL ENTRY //************************************************************************** // a symbol entry representing a register, with read/write callbacks @@ -117,8 +117,8 @@ public: integer_symbol_entry(symbol_table &table, const char *name, symbol_table::getter_func getter, symbol_table::setter_func setter, const std::string &format); // symbol access - virtual bool is_lval() const override; - virtual u64 value() const override; + virtual bool is_lval() const override { return m_setter != nullptr; } + virtual u64 value() const override { return m_getter(); } virtual void set_value(u64 newvalue) override; private: @@ -129,6 +129,61 @@ private: }; +//------------------------------------------------- +// integer_symbol_entry - constructor +//------------------------------------------------- + +integer_symbol_entry::integer_symbol_entry(symbol_table &table, const char *name, symbol_table::read_write rw, u64 *ptr) + : symbol_entry(table, SMT_INTEGER, name, ""), + m_getter(ptr + ? symbol_table::getter_func([ptr] () { return *ptr; }) + : symbol_table::getter_func([this] () { return m_value; })), + m_setter((rw == symbol_table::READ_ONLY) + ? symbol_table::setter_func(nullptr) + : ptr + ? symbol_table::setter_func([ptr] (u64 value) { *ptr = value; }) + : symbol_table::setter_func([this] (u64 value) { m_value = value; })), + m_value(0) +{ +} + + +integer_symbol_entry::integer_symbol_entry(symbol_table &table, const char *name, u64 constval) + : symbol_entry(table, SMT_INTEGER, name, ""), + m_getter([this] () { return m_value; }), + m_setter(nullptr), + m_value(constval) +{ +} + + +integer_symbol_entry::integer_symbol_entry(symbol_table &table, const char *name, symbol_table::getter_func getter, symbol_table::setter_func setter, const std::string &format) + : symbol_entry(table, SMT_INTEGER, name, format), + m_getter(std::move(getter)), + m_setter(std::move(setter)), + m_value(0) +{ +} + + +//------------------------------------------------- +// set_value - set the value of this symbol +//------------------------------------------------- + +void integer_symbol_entry::set_value(u64 newvalue) +{ + if (m_setter != nullptr) + m_setter(newvalue); + else + throw emu_fatalerror("Symbol '%s' is read-only", m_name); +} + + + +//************************************************************************** +// FUNCTION SYMBOL ENTRY +//************************************************************************** + // a symbol entry representing a function class function_symbol_entry : public symbol_entry { @@ -136,8 +191,12 @@ public: // construction/destruction function_symbol_entry(symbol_table &table, const char *name, int minparams, int maxparams, symbol_table::execute_func execute); + // getters + u16 minparams() const { return m_minparams; } + u16 maxparams() const { return m_maxparams; } + // symbol access - virtual bool is_lval() const override; + virtual bool is_lval() const override { return false; } virtual u64 value() const override; virtual void set_value(u64 newvalue) override; @@ -152,6 +211,82 @@ private: }; +//------------------------------------------------- +// function_symbol_entry - constructor +//------------------------------------------------- + +function_symbol_entry::function_symbol_entry(symbol_table &table, const char *name, int minparams, int maxparams, symbol_table::execute_func execute) + : symbol_entry(table, SMT_FUNCTION, name, ""), + m_minparams(minparams), + m_maxparams(maxparams), + m_execute(std::move(execute)) +{ +} + + +//------------------------------------------------- +// value - return the value of this symbol +//------------------------------------------------- + +u64 function_symbol_entry::value() const +{ + throw emu_fatalerror("Symbol '%s' is a function and cannot be used in this context", m_name); +} + + +//------------------------------------------------- +// set_value - set the value of this symbol +//------------------------------------------------- + +void function_symbol_entry::set_value(u64 newvalue) +{ + throw emu_fatalerror("Symbol '%s' is a function and cannot be written", m_name); +} + + +//------------------------------------------------- +// execute - execute the function +//------------------------------------------------- + +u64 function_symbol_entry::execute(int numparams, const u64 *paramlist) +{ + if (numparams < m_minparams) + throw emu_fatalerror("Function '%s' requires at least %d parameters", m_name, m_minparams); + if (numparams > m_maxparams) + throw emu_fatalerror("Function '%s' accepts no more than %d parameters", m_name, m_maxparams); + return m_execute(numparams, paramlist); +} + + + +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +inline std::pair<device_t &, char const *> get_device_search(running_machine &machine, device_memory_interface *memintf, char const *tag) +{ + if (tag) + { + if (('.' == tag[0]) && (!tag[1] || (':' == tag[1]) || ('^' == tag[1]))) + return std::pair<device_t &, char const *>(memintf ? memintf->device() : machine.root_device(), tag + ((':' == tag[1]) ? 2 : 1)); + else if (('^' == tag[0]) && memintf) + return std::pair<device_t &, char const *>(memintf->device(), tag); + else + return std::pair<device_t &, char const *>(machine.root_device(), tag); + } + else if (memintf) + { + return std::pair<device_t &, char const *>(memintf->device(), ""); + } + else + { + return std::pair<device_t &, char const *>(machine.root_device(), ""); + } +} + +} // anonymous namespace + + //************************************************************************** // EXPRESSION ERROR @@ -162,7 +297,7 @@ private: // given expression error //------------------------------------------------- -const char *expression_error::code_string() const +std::string expression_error::code_string() const { switch (m_code) { @@ -178,6 +313,8 @@ const char *expression_error::code_string() const case DIVIDE_BY_ZERO: return "divide by zero"; case OUT_OF_MEMORY: return "out of memory"; case INVALID_PARAM_COUNT: return "invalid number of parameters"; + case TOO_FEW_PARAMS: return util::string_format("too few parameters (at least %d required)", m_num); + case TOO_MANY_PARAMS: return util::string_format("too many parameters (no more than %d accepted)", m_num); case UNBALANCED_QUOTES: return "unbalanced quotes"; case TOO_MANY_STRINGS: return "too many strings"; case INVALID_MEMORY_SIZE: return "invalid memory size (b/w/d/q expected)"; @@ -200,8 +337,7 @@ const char *expression_error::code_string() const //------------------------------------------------- symbol_entry::symbol_entry(symbol_table &table, symbol_type type, const char *name, const std::string &format) - : m_next(nullptr), - m_table(table), + : m_table(table), m_type(type), m_name(name), m_format(format) @@ -220,312 +356,652 @@ symbol_entry::~symbol_entry() //************************************************************************** -// REGISTER SYMBOL ENTRY +// SYMBOL TABLE //************************************************************************** //------------------------------------------------- -// integer_symbol_entry - constructor +// symbol_table - constructor //------------------------------------------------- -integer_symbol_entry::integer_symbol_entry(symbol_table &table, const char *name, symbol_table::read_write rw, u64 *ptr) - : symbol_entry(table, SMT_INTEGER, name, ""), - m_getter(ptr - ? symbol_table::getter_func([ptr] (symbol_table &table) { return *ptr; }) - : symbol_table::getter_func([this] (symbol_table &table) { return m_value; })), - m_setter((rw == symbol_table::READ_ONLY) - ? symbol_table::setter_func(nullptr) - : ptr - ? symbol_table::setter_func([ptr] (symbol_table &table, u64 value) { *ptr = value; }) - : symbol_table::setter_func([this] (symbol_table &table, u64 value) { m_value = value; })), - m_value(0) -{ -} - - -integer_symbol_entry::integer_symbol_entry(symbol_table &table, const char *name, u64 constval) - : symbol_entry(table, SMT_INTEGER, name, ""), - m_getter([this] (symbol_table &table) { return m_value; }), - m_setter(nullptr), - m_value(constval) -{ -} - - -integer_symbol_entry::integer_symbol_entry(symbol_table &table, const char *name, symbol_table::getter_func getter, symbol_table::setter_func setter, const std::string &format) - : symbol_entry(table, SMT_INTEGER, name, format), - m_getter(getter), - m_setter(setter), - m_value(0) +symbol_table::symbol_table(running_machine &machine, symbol_table *parent, device_t *device) + : m_machine(machine) + , m_parent(parent) + , m_memintf(dynamic_cast<device_memory_interface *>(device)) + , m_memory_modified(nullptr) { } //------------------------------------------------- -// is_lval - is this symbol allowable as an lval? +// set_memory_modified_func - install notifier +// for when memory is modified in debugger //------------------------------------------------- -bool integer_symbol_entry::is_lval() const +void symbol_table::set_memory_modified_func(memory_modified_func modified) { - return (m_setter != nullptr); + m_memory_modified = std::move(modified); } //------------------------------------------------- -// value - return the value of this symbol +// add - add a new u64 pointer symbol //------------------------------------------------- -u64 integer_symbol_entry::value() const +symbol_entry &symbol_table::add(const char *name, read_write rw, u64 *ptr) { - return m_getter(m_table); + m_symlist.erase(name); + return *m_symlist.emplace(name, std::make_unique<integer_symbol_entry>(*this, name, rw, ptr)).first->second; } //------------------------------------------------- -// set_value - set the value of this symbol +// add - add a new value symbol //------------------------------------------------- -void integer_symbol_entry::set_value(u64 newvalue) +symbol_entry &symbol_table::add(const char *name, u64 value) { - if (m_setter != nullptr) - m_setter(m_table, newvalue); - else - throw emu_fatalerror("Symbol '%s' is read-only", m_name.c_str()); + m_symlist.erase(name); + return *m_symlist.emplace(name, std::make_unique<integer_symbol_entry>(*this, name, value)).first->second; } - -//************************************************************************** -// FUNCTION SYMBOL ENTRY -//************************************************************************** - //------------------------------------------------- -// function_symbol_entry - constructor +// add - add a new register symbol //------------------------------------------------- -function_symbol_entry::function_symbol_entry(symbol_table &table, const char *name, int minparams, int maxparams, symbol_table::execute_func execute) - : symbol_entry(table, SMT_FUNCTION, name, ""), - m_minparams(minparams), - m_maxparams(maxparams), - m_execute(execute) +symbol_entry &symbol_table::add(const char *name, getter_func getter, setter_func setter, const std::string &format_string) { + m_symlist.erase(name); + return *m_symlist.emplace(name, std::make_unique<integer_symbol_entry>(*this, name, getter, setter, format_string)).first->second; } //------------------------------------------------- -// is_lval - is this symbol allowable as an lval? +// add - add a new function symbol //------------------------------------------------- -bool function_symbol_entry::is_lval() const +symbol_entry &symbol_table::add(const char *name, int minparams, int maxparams, execute_func execute) { - return false; + m_symlist.erase(name); + return *m_symlist.emplace(name, std::make_unique<function_symbol_entry>(*this, name, minparams, maxparams, execute)).first->second; } //------------------------------------------------- -// value - return the value of this symbol +// find_deep - do a deep search for a symbol, +// looking in the parent if needed //------------------------------------------------- -u64 function_symbol_entry::value() const +symbol_entry *symbol_table::find_deep(const char *symbol) { - throw emu_fatalerror("Symbol '%s' is a function and cannot be used in this context", m_name.c_str()); + // walk up the table hierarchy to find the owner + for (symbol_table *symtable = this; symtable != nullptr; symtable = symtable->m_parent) + { + symbol_entry *entry = symtable->find(symbol); + if (entry != nullptr) + return entry; + } + return nullptr; } //------------------------------------------------- -// set_value - set the value of this symbol +// value - return the value of a symbol //------------------------------------------------- -void function_symbol_entry::set_value(u64 newvalue) +u64 symbol_table::value(const char *symbol) { - throw emu_fatalerror("Symbol '%s' is a function and cannot be written", m_name.c_str()); + symbol_entry *entry = find_deep(symbol); + return (entry != nullptr) ? entry->value() : 0; } //------------------------------------------------- -// execute - execute the function +// set_value - set the value of a symbol //------------------------------------------------- -u64 function_symbol_entry::execute(int numparams, const u64 *paramlist) +void symbol_table::set_value(const char *symbol, u64 value) { - if (numparams < m_minparams) - throw emu_fatalerror("Function '%s' requires at least %d parameters", m_name.c_str(), m_minparams); - if (numparams > m_maxparams) - throw emu_fatalerror("Function '%s' accepts no more than %d parameters", m_name.c_str(), m_maxparams); - return m_execute(m_table, numparams, paramlist); + symbol_entry *entry = find_deep(symbol); + if (entry != nullptr) + entry->set_value(value); } //************************************************************************** -// SYMBOL TABLE +// EXPRESSION MEMORY HANDLERS //************************************************************************** //------------------------------------------------- -// symbol_table - constructor +// read_memory - return 1,2,4 or 8 bytes +// from the specified memory space //------------------------------------------------- -symbol_table::symbol_table(void *globalref, symbol_table *parent) - : m_parent(parent), - m_globalref(globalref), - m_memory_param(nullptr), - m_memory_valid(nullptr), - m_memory_read(nullptr), - m_memory_write(nullptr) +u64 symbol_table::read_memory(address_space &space, offs_t address, int size, bool apply_translation) { -} + u64 result = ~u64(0) >> (64 - 8*size); + address_space *tspace = &space; -//------------------------------------------------- -// add - add a new u64 pointer symbol -//------------------------------------------------- + if (apply_translation) + { + // mask against the logical byte mask + address &= space.logaddrmask(); -void symbol_table::configure_memory(void *param, valid_func valid, read_func read, write_func write) -{ - m_memory_param = param; - m_memory_valid = valid; - m_memory_read = read; - m_memory_write = write; + // translate if necessary; if not mapped, return 0xffffffffffffffff + if (!space.device().memory().translate(space.spacenum(), device_memory_interface::TR_READ, address, tspace)) + return result; + } + + // otherwise, call the reading function for the translated address + switch (size) + { + case 1: result = tspace->read_byte(address); break; + case 2: result = tspace->read_word_unaligned(address); break; + case 4: result = tspace->read_dword_unaligned(address); break; + case 8: result = tspace->read_qword_unaligned(address); break; + } + return result; } //------------------------------------------------- -// add - add a new u64 pointer symbol +// write_memory - write 1,2,4 or 8 bytes to the +// specified memory space //------------------------------------------------- -void symbol_table::add(const char *name, read_write rw, u64 *ptr) +void symbol_table::write_memory(address_space &space, offs_t address, u64 data, int size, bool apply_translation) { - m_symlist.erase(name); - m_symlist.emplace(name, std::make_unique<integer_symbol_entry>(*this, name, rw, ptr)); + address_space *tspace = &space; + + if (apply_translation) + { + // mask against the logical byte mask + address &= space.logaddrmask(); + + // translate if necessary; if not mapped, we're done + if (!space.device().memory().translate(space.spacenum(), device_memory_interface::TR_WRITE, address, tspace)) + return; + } + + // otherwise, call the writing function for the translated address + switch (size) + { + case 1: tspace->write_byte(address, data); break; + case 2: tspace->write_word_unaligned(address, data); break; + case 4: tspace->write_dword_unaligned(address, data); break; + case 8: tspace->write_qword_unaligned(address, data); break; + } + + notify_memory_modified(); } //------------------------------------------------- -// add - add a new value symbol +// expression_get_space - return a space +// based on a case insensitive tag search //------------------------------------------------- -void symbol_table::add(const char *name, u64 value) +expression_error symbol_table::expression_get_space(const char *tag, int &spacenum, device_memory_interface *&memory) { - m_symlist.erase(name); - m_symlist.emplace(name, std::make_unique<integer_symbol_entry>(*this, name, value)); + device_t *device = nullptr; + std::string spacename; + if (tag) + { + // convert to lowercase then lookup the name (tags are enforced to be all lower case) + auto base = get_device_search(m_machine, m_memintf, tag); + device = base.first.subdevice(strmakelower(base.second)); + + // if that failed, treat the last component as an address space + if (!device) + { + std::string_view t = base.second; + auto const delimiter = t.find_last_of(":^"); + bool const found = std::string_view::npos != delimiter; + if (!found || (':' == t[delimiter])) + { + spacename = strmakelower(t.substr(found ? (delimiter + 1) : 0)); + t = t.substr(0, !found ? 0 : !delimiter ? 1 : delimiter); + if (!t.empty()) + device = base.first.subdevice(strmakelower(t)); + else + device = m_memintf ? &m_memintf->device() : &m_machine.root_device(); + } + } + } + else if (m_memintf) + { + device = &m_memintf->device(); + } + + // if still no device, report error + if (!device) + { + memory = nullptr; + return expression_error::INVALID_MEMORY_NAME; + } + + // ensure device has memory interface, and check for space if search not required + if (!device->interface(memory) || (spacename.empty() && (0 <= spacenum) && !memory->has_space(spacenum))) + { + memory = nullptr; + return expression_error::NO_SUCH_MEMORY_SPACE; + } + + // search not required + if (spacename.empty() && (0 <= spacenum)) + return expression_error::NONE; + + // find space by name or take first populated space if required + for (int i = 0; memory->max_space_count() > i; ++i) + { + if (memory->has_space(i) && (spacename.empty() || (memory->space(i).name() == spacename))) + { + spacenum = i; + return expression_error::NONE; + } + } + + // space not found + memory = nullptr; + return expression_error::NO_SUCH_MEMORY_SPACE; } //------------------------------------------------- -// add - add a new register symbol +// notify_memory_modified - notify that memory +// has been changed //------------------------------------------------- -void symbol_table::add(const char *name, getter_func getter, setter_func setter, const std::string &format_string) +void symbol_table::notify_memory_modified() { - m_symlist.erase(name); - m_symlist.emplace(name, std::make_unique<integer_symbol_entry>(*this, name, getter, setter, format_string)); + // walk up the table hierarchy to find the owner + for (symbol_table *symtable = this; symtable != nullptr; symtable = symtable->m_parent) + if (symtable->m_memory_modified) + symtable->m_memory_modified(); } //------------------------------------------------- -// add - add a new function symbol +// memory_value - read 1,2,4 or 8 bytes at the +// given offset in the given address space //------------------------------------------------- -void symbol_table::add(const char *name, int minparams, int maxparams, execute_func execute) +u64 symbol_table::memory_value(const char *name, expression_space spacenum, u32 address, int size, bool disable_se) { - m_symlist.erase(name); - m_symlist.emplace(name, std::make_unique<function_symbol_entry>(*this, name, minparams, maxparams, execute)); + device_memory_interface *memory = m_memintf; + + bool logical = true; + int space = -1; + switch (spacenum) + { + case EXPSPACE_PROGRAM_PHYSICAL: + case EXPSPACE_DATA_PHYSICAL: + case EXPSPACE_IO_PHYSICAL: + case EXPSPACE_OPCODE_PHYSICAL: + spacenum = expression_space(spacenum - (EXPSPACE_PROGRAM_PHYSICAL - EXPSPACE_PROGRAM_LOGICAL)); + logical = false; + [[fallthrough]]; + case EXPSPACE_PROGRAM_LOGICAL: + case EXPSPACE_DATA_LOGICAL: + case EXPSPACE_IO_LOGICAL: + case EXPSPACE_OPCODE_LOGICAL: + space = AS_PROGRAM + (spacenum - EXPSPACE_PROGRAM_LOGICAL); + expression_get_space(name, space, memory); + if (memory) + { + auto dis = m_machine.disable_side_effects(disable_se); + return read_memory(memory->space(space), address, size, logical); + } + break; + + case EXPSPACE_PRGDIRECT: + case EXPSPACE_OPDIRECT: + space = (spacenum == EXPSPACE_OPDIRECT) ? AS_OPCODES : AS_PROGRAM; + expression_get_space(name, space, memory); + if (memory) + { + auto dis = m_machine.disable_side_effects(disable_se); + return read_program_direct(memory->space(space), (spacenum == EXPSPACE_OPDIRECT) ? 1 : 0, address, size); + } + break; + + case EXPSPACE_REGION: + if (name) + return read_memory_region(name, address, size); + break; + + default: + break; + } + + return 0; } //------------------------------------------------- -// find_deep - do a deep search for a symbol, -// looking in the parent if needed +// read_program_direct - read memory directly +// from an opcode or RAM pointer //------------------------------------------------- -symbol_entry *symbol_table::find_deep(const char *symbol) +u64 symbol_table::read_program_direct(address_space &space, int opcode, offs_t address, int size) { - // walk up the table hierarchy to find the owner - for (symbol_table *symtable = this; symtable != nullptr; symtable = symtable->m_parent) + u8 *base; + + // adjust the address into a byte address, but not if being called recursively + if ((opcode & 2) == 0) + address = space.address_to_byte(address); + + // call ourself recursively until we are byte-sized + if (size > 1) { - symbol_entry *entry = symtable->find(symbol); - if (entry != nullptr) - return entry; + int halfsize = size / 2; + + // read each half, from lower address to upper address + u64 r0 = read_program_direct(space, opcode | 2, address + 0, halfsize); + u64 r1 = read_program_direct(space, opcode | 2, address + halfsize, halfsize); + + // assemble based on the target endianness + if (space.endianness() == ENDIANNESS_LITTLE) + return r0 | (r1 << (8 * halfsize)); + else + return r1 | (r0 << (8 * halfsize)); } - return nullptr; + + // handle the byte-sized final requests + else + { + // lowmask specified which address bits are within the databus width + offs_t lowmask = space.data_width() / 8 - 1; + + // get the base of memory, aligned to the address minus the lowbits + base = (u8 *)space.get_read_ptr(address & ~lowmask); + + // if we have a valid base, return the appropriate byte + if (base != nullptr) + { + if (space.endianness() == ENDIANNESS_LITTLE) + return base[BYTE8_XOR_LE(address) & lowmask]; + else + return base[BYTE8_XOR_BE(address) & lowmask]; + } + } + + return 0; } //------------------------------------------------- -// value - return the value of a symbol +// read_memory_region - read memory from a +// memory region //------------------------------------------------- -u64 symbol_table::value(const char *symbol) +u64 symbol_table::read_memory_region(const char *rgntag, offs_t address, int size) { - symbol_entry *entry = find_deep(symbol); - return (entry != nullptr) ? entry->value() : 0; + auto search = get_device_search(m_machine, m_memintf, rgntag); + memory_region *const region = search.first.memregion(search.second); + u64 result = ~u64(0) >> (64 - 8*size); + + // make sure we get a valid base before proceeding + if (region) + { + // call ourself recursively until we are byte-sized + if (size > 1) + { + int halfsize = size / 2; + u64 r0, r1; + + // read each half, from lower address to upper address + r0 = read_memory_region(rgntag, address + 0, halfsize); + r1 = read_memory_region(rgntag, address + halfsize, halfsize); + + // assemble based on the target endianness + if (region->endianness() == ENDIANNESS_LITTLE) + result = r0 | (r1 << (8 * halfsize)); + else + result = r1 | (r0 << (8 * halfsize)); + } + + // only process if we're within range + else if (address < region->bytes()) + { + // lowmask specified which address bits are within the databus width + u32 lowmask = region->bytewidth() - 1; + u8 *base = region->base() + (address & ~lowmask); + + // if we have a valid base, return the appropriate byte + if (region->endianness() == ENDIANNESS_LITTLE) + result = base[BYTE8_XOR_LE(address) & lowmask]; + else + result = base[BYTE8_XOR_BE(address) & lowmask]; + } + } + return result; } //------------------------------------------------- -// set_value - set the value of a symbol +// set_memory_value - write 1,2,4 or 8 bytes at +// the given offset in the given address space //------------------------------------------------- -void symbol_table::set_value(const char *symbol, u64 value) +void symbol_table::set_memory_value(const char *name, expression_space spacenum, u32 address, int size, u64 data, bool disable_se) { - symbol_entry *entry = find_deep(symbol); - if (entry != nullptr) - entry->set_value(value); + device_memory_interface *memory = m_memintf; + + bool logical = true; + int space = -1; + switch (spacenum) + { + case EXPSPACE_PROGRAM_PHYSICAL: + case EXPSPACE_DATA_PHYSICAL: + case EXPSPACE_IO_PHYSICAL: + case EXPSPACE_OPCODE_PHYSICAL: + spacenum = expression_space(spacenum - (EXPSPACE_PROGRAM_PHYSICAL - EXPSPACE_PROGRAM_LOGICAL)); + logical = false; + [[fallthrough]]; + case EXPSPACE_PROGRAM_LOGICAL: + case EXPSPACE_DATA_LOGICAL: + case EXPSPACE_IO_LOGICAL: + case EXPSPACE_OPCODE_LOGICAL: + space = AS_PROGRAM + (spacenum - EXPSPACE_PROGRAM_LOGICAL); + expression_get_space(name, space, memory); + if (memory) + { + auto dis = m_machine.disable_side_effects(disable_se); + write_memory(memory->space(space), address, data, size, logical); + } + break; + + case EXPSPACE_PRGDIRECT: + case EXPSPACE_OPDIRECT: + space = (spacenum == EXPSPACE_OPDIRECT) ? AS_OPCODES : AS_PROGRAM; + expression_get_space(name, space, memory); + if (memory) + { + auto dis = m_machine.disable_side_effects(disable_se); + write_program_direct(memory->space(space), (spacenum == EXPSPACE_OPDIRECT) ? 1 : 0, address, size, data); + } + break; + + case EXPSPACE_REGION: + if (name) + write_memory_region(name, address, size, data); + break; + + default: + break; + } } //------------------------------------------------- -// memory_valid - return true if the given -// memory name/space/offset combination is valid +// write_program_direct - write memory directly +// to an opcode or RAM pointer //------------------------------------------------- -expression_error::error_code symbol_table::memory_valid(const char *name, expression_space space) +void symbol_table::write_program_direct(address_space &space, int opcode, offs_t address, int size, u64 data) { - // walk up the table hierarchy to find the owner - for (symbol_table *symtable = this; symtable != nullptr; symtable = symtable->m_parent) - if (symtable->m_memory_valid != nullptr) + // adjust the address into a byte address, but not if being called recursively + if ((opcode & 2) == 0) + address = space.address_to_byte(address); + + // call ourself recursively until we are byte-sized + if (size > 1) + { + int halfsize = size / 2; + + // break apart based on the target endianness + u64 halfmask = ~u64(0) >> (64 - 8 * halfsize); + u64 r0, r1; + if (space.endianness() == ENDIANNESS_LITTLE) { - expression_error::error_code err = symtable->m_memory_valid(symtable->m_memory_param, name, space); - if (err != expression_error::NO_SUCH_MEMORY_SPACE) - return err; + r0 = data & halfmask; + r1 = (data >> (8 * halfsize)) & halfmask; } - return expression_error::NO_SUCH_MEMORY_SPACE; + else + { + r0 = (data >> (8 * halfsize)) & halfmask; + r1 = data & halfmask; + } + + // write each half, from lower address to upper address + write_program_direct(space, opcode | 2, address + 0, halfsize, r0); + write_program_direct(space, opcode | 2, address + halfsize, halfsize, r1); + } + + // handle the byte-sized final case + else + { + // lowmask specified which address bits are within the databus width + offs_t lowmask = space.data_width() / 8 - 1; + + // get the base of memory, aligned to the address minus the lowbits + u8 *base = (u8 *)space.get_read_ptr(address & ~lowmask); + + // if we have a valid base, write the appropriate byte + if (base != nullptr) + { + if (space.endianness() == ENDIANNESS_LITTLE) + base[BYTE8_XOR_LE(address) & lowmask] = data; + else + base[BYTE8_XOR_BE(address) & lowmask] = data; + notify_memory_modified(); + } + } } //------------------------------------------------- -// memory_value - return a value read from memory +// write_memory_region - write memory to a +// memory region //------------------------------------------------- -u64 symbol_table::memory_value(const char *name, expression_space space, u32 offset, int size, bool disable_se) +void symbol_table::write_memory_region(const char *rgntag, offs_t address, int size, u64 data) { - // walk up the table hierarchy to find the owner - for (symbol_table *symtable = this; symtable != nullptr; symtable = symtable->m_parent) - if (symtable->m_memory_valid != nullptr) + auto search = get_device_search(m_machine, m_memintf, rgntag); + memory_region *const region = search.first.memregion(search.second); + + // make sure we get a valid base before proceeding + if (region) + { + // call ourself recursively until we are byte-sized + if (size > 1) { - expression_error::error_code err = symtable->m_memory_valid(symtable->m_memory_param, name, space); - if (err != expression_error::NO_SUCH_MEMORY_SPACE && symtable->m_memory_read != nullptr) - return symtable->m_memory_read(symtable->m_memory_param, name, space, offset, size, disable_se); - return 0; + int halfsize = size / 2; + + // break apart based on the target endianness + u64 halfmask = ~u64(0) >> (64 - 8 * halfsize); + u64 r0, r1; + if (region->endianness() == ENDIANNESS_LITTLE) + { + r0 = data & halfmask; + r1 = (data >> (8 * halfsize)) & halfmask; + } + else + { + r0 = (data >> (8 * halfsize)) & halfmask; + r1 = data & halfmask; + } + + // write each half, from lower address to upper address + write_memory_region(rgntag, address + 0, halfsize, r0); + write_memory_region(rgntag, address + halfsize, halfsize, r1); } - return 0; + + // only process if we're within range + else if (address < region->bytes()) + { + // lowmask specified which address bits are within the databus width + u32 lowmask = region->bytewidth() - 1; + u8 *base = region->base() + (address & ~lowmask); + + // if we have a valid base, set the appropriate byte + if (region->endianness() == ENDIANNESS_LITTLE) + { + base[BYTE8_XOR_LE(address) & lowmask] = data; + } + else + { + base[BYTE8_XOR_BE(address) & lowmask] = data; + } + notify_memory_modified(); + } + } } //------------------------------------------------- -// set_memory_value - write a value to memory +// memory_valid - return true if the given +// memory name/space/offset combination is valid //------------------------------------------------- -void symbol_table::set_memory_value(const char *name, expression_space space, u32 offset, int size, u64 value, bool disable_se) +expression_error::error_code symbol_table::memory_valid(const char *name, expression_space space) { - // walk up the table hierarchy to find the owner - for (symbol_table *symtable = this; symtable != nullptr; symtable = symtable->m_parent) - if (symtable->m_memory_valid != nullptr) + device_memory_interface *memory = m_memintf; + + int spaceno = -1; + switch (space) + { + case EXPSPACE_PROGRAM_LOGICAL: + case EXPSPACE_DATA_LOGICAL: + case EXPSPACE_IO_LOGICAL: + case EXPSPACE_OPCODE_LOGICAL: + spaceno = AS_PROGRAM + (space - EXPSPACE_PROGRAM_LOGICAL); + return expression_get_space(name, spaceno, memory); + + case EXPSPACE_PROGRAM_PHYSICAL: + case EXPSPACE_DATA_PHYSICAL: + case EXPSPACE_IO_PHYSICAL: + case EXPSPACE_OPCODE_PHYSICAL: + spaceno = AS_PROGRAM + (space - EXPSPACE_PROGRAM_PHYSICAL); + return expression_get_space(name, spaceno, memory); + + case EXPSPACE_PRGDIRECT: + case EXPSPACE_OPDIRECT: + spaceno = (space == EXPSPACE_OPDIRECT) ? AS_OPCODES : AS_PROGRAM; + return expression_get_space(name, spaceno, memory); + + case EXPSPACE_REGION: + if (!name) { - expression_error::error_code err = symtable->m_memory_valid(symtable->m_memory_param, name, space); - if (err != expression_error::NO_SUCH_MEMORY_SPACE && symtable->m_memory_write != nullptr) - symtable->m_memory_write(symtable->m_memory_param, name, space, offset, size, value, disable_se); - return; + return expression_error::MISSING_MEMORY_NAME; } + else + { + auto search = get_device_search(m_machine, m_memintf, name); + memory_region *const region = search.first.memregion(search.second); + if (!region || !region->base()) + return expression_error::INVALID_MEMORY_NAME; + } + break; + + default: + return expression_error::NO_SUCH_MEMORY_SPACE; + } + + return expression_error::NONE; } @@ -538,16 +1014,33 @@ void symbol_table::set_memory_value(const char *name, expression_space space, u3 // parsed_expression - constructor //------------------------------------------------- -parsed_expression::parsed_expression(symbol_table *symtable, const char *expression, u64 *result) +parsed_expression::parsed_expression(symbol_table &symtable) : m_symtable(symtable) + , m_default_base(16) { - // if we got an expression parse it - if (expression != nullptr) - parse(expression); +} + +parsed_expression::parsed_expression(symbol_table &symtable, std::string_view expression, int default_base) + : m_symtable(symtable) + , m_default_base(default_base) +{ + assert(default_base == 8 || default_base == 10 || default_base == 16); + + parse(expression); +} + - // if we get a result pointer, execute it - if (result != nullptr) - *result = execute(); +//------------------------------------------------- +// parsed_expression - copy constructor +//------------------------------------------------- + +parsed_expression::parsed_expression(const parsed_expression &src) + : m_symtable(src.m_symtable) + , m_default_base(src.m_default_base) + , m_original_string(src.m_original_string) +{ + if (!m_original_string.empty()) + parse_string_into_tokens(); } @@ -555,12 +1048,12 @@ parsed_expression::parsed_expression(symbol_table *symtable, const char *express // parse - parse an expression into tokens //------------------------------------------------- -void parsed_expression::parse(const char *expression) +void parsed_expression::parse(std::string_view expression) { // copy the string and reset our parsing state m_original_string.assign(expression); - m_tokenlist.reset(); - m_stringlist.reset(); + m_tokenlist.clear(); + m_stringlist.clear(); // first parse the tokens into the token array in order parse_string_into_tokens(); @@ -577,6 +1070,7 @@ void parsed_expression::parse(const char *expression) void parsed_expression::copy(const parsed_expression &src) { m_symtable = src.m_symtable; + m_default_base = src.m_default_base; m_original_string.assign(src.m_original_string); if (!m_original_string.empty()) parse_string_into_tokens(); @@ -588,89 +1082,72 @@ void parsed_expression::copy(const parsed_expression &src) // human readable token representation //------------------------------------------------- -void parsed_expression::print_tokens(FILE *out) +void parsed_expression::print_tokens() { -#if DEBUG_TOKENS - osd_printf_debug("----\n"); - for (parse_token *token = m_tokens.first(); token != nullptr; token = token->next()) + LOG("----\n"); + for (parse_token &token : m_tokenlist) { - switch (token->type) + if (token.is_number()) + LOG("NUMBER: %016X\n", token.value()); + else if (token.is_string()) + LOG("STRING: ""%s""\n", token.string()); + else if (token.is_symbol()) + LOG("SYMBOL: %s%s%s\n", token.symbol().name(), token.symbol().is_function() ? "()" : "", token.symbol().is_lval() ? " &" : ""); + else if (token.is_operator()) { - default: - case parse_token::INVALID: - fprintf(out, "INVALID\n"); - break; - - case parse_token::END: - fprintf(out, "END\n"); - break; - - case parse_token::NUMBER: - fprintf(out, "NUMBER: %08X%08X\n", (u32)(token->value.i >> 32), u32(token->value.i)); - break; - - case parse_token::STRING: - fprintf(out, "STRING: ""%s""\n", token->string); - break; - - case parse_token::SYMBOL: - fprintf(out, "SYMBOL: %08X%08X\n", u32(token->value.i >> 32), u32(token->value.i)); - break; - - case parse_token::OPERATOR: - switch (token->value.i) - { - case TVL_LPAREN: fprintf(out, "(\n"); break; - case TVL_RPAREN: fprintf(out, ")\n"); break; - case TVL_PLUSPLUS: fprintf(out, "++ (unspecified)\n"); break; - case TVL_MINUSMINUS: fprintf(out, "-- (unspecified)\n"); break; - case TVL_PREINCREMENT: fprintf(out, "++ (prefix)\n"); break; - case TVL_PREDECREMENT: fprintf(out, "-- (prefix)\n"); break; - case TVL_POSTINCREMENT: fprintf(out, "++ (postfix)\n"); break; - case TVL_POSTDECREMENT: fprintf(out, "-- (postfix)\n"); break; - case TVL_COMPLEMENT: fprintf(out, "!\n"); break; - case TVL_NOT: fprintf(out, "~\n"); break; - case TVL_UPLUS: fprintf(out, "+ (unary)\n"); break; - case TVL_UMINUS: fprintf(out, "- (unary)\n"); break; - case TVL_MULTIPLY: fprintf(out, "*\n"); break; - case TVL_DIVIDE: fprintf(out, "/\n"); break; - case TVL_MODULO: fprintf(out, "%%\n"); break; - case TVL_ADD: fprintf(out, "+\n"); break; - case TVL_SUBTRACT: fprintf(out, "-\n"); break; - case TVL_LSHIFT: fprintf(out, "<<\n"); break; - case TVL_RSHIFT: fprintf(out, ">>\n"); break; - case TVL_LESS: fprintf(out, "<\n"); break; - case TVL_LESSOREQUAL: fprintf(out, "<=\n"); break; - case TVL_GREATER: fprintf(out, ">\n"); break; - case TVL_GREATEROREQUAL:fprintf(out, ">=\n"); break; - case TVL_EQUAL: fprintf(out, "==\n"); break; - case TVL_NOTEQUAL: fprintf(out, "!=\n"); break; - case TVL_BAND: fprintf(out, "&\n"); break; - case TVL_BXOR: fprintf(out, "^\n"); break; - case TVL_BOR: fprintf(out, "|\n"); break; - case TVL_LAND: fprintf(out, "&&\n"); break; - case TVL_LOR: fprintf(out, "||\n"); break; - case TVL_ASSIGN: fprintf(out, "=\n"); break; - case TVL_ASSIGNMULTIPLY:fprintf(out, "*=\n"); break; - case TVL_ASSIGNDIVIDE: fprintf(out, "/=\n"); break; - case TVL_ASSIGNMODULO: fprintf(out, "%%=\n"); break; - case TVL_ASSIGNADD: fprintf(out, "+=\n"); break; - case TVL_ASSIGNSUBTRACT:fprintf(out, "-=\n"); break; - case TVL_ASSIGNLSHIFT: fprintf(out, "<<=\n"); break; - case TVL_ASSIGNRSHIFT: fprintf(out, ">>=\n"); break; - case TVL_ASSIGNBAND: fprintf(out, "&=\n"); break; - case TVL_ASSIGNBXOR: fprintf(out, "^=\n"); break; - case TVL_ASSIGNBOR: fprintf(out, "|=\n"); break; - case TVL_COMMA: fprintf(out, ",\n"); break; - case TVL_MEMORYAT: fprintf(out, token.memory_size_effect() ? "mem!\n" : "mem@\n");break; - case TVL_EXECUTEFUNC: fprintf(out, "execute\n"); break; - default: fprintf(out, "INVALID OPERATOR\n"); break; - } - break; + switch (token.optype()) + { + case TVL_LPAREN: LOG("(\n"); break; + case TVL_RPAREN: LOG(")\n"); break; + case TVL_PLUSPLUS: LOG("++ (unspecified)\n"); break; + case TVL_MINUSMINUS: LOG("-- (unspecified)\n"); break; + case TVL_PREINCREMENT: LOG("++ (prefix)\n"); break; + case TVL_PREDECREMENT: LOG("-- (prefix)\n"); break; + case TVL_POSTINCREMENT: LOG("++ (postfix)\n"); break; + case TVL_POSTDECREMENT: LOG("-- (postfix)\n"); break; + case TVL_COMPLEMENT: LOG("!\n"); break; + case TVL_NOT: LOG("~\n"); break; + case TVL_UPLUS: LOG("+ (unary)\n"); break; + case TVL_UMINUS: LOG("- (unary)\n"); break; + case TVL_MULTIPLY: LOG("*\n"); break; + case TVL_DIVIDE: LOG("/\n"); break; + case TVL_MODULO: LOG("%%\n"); break; + case TVL_ADD: LOG("+\n"); break; + case TVL_SUBTRACT: LOG("-\n"); break; + case TVL_LSHIFT: LOG("<<\n"); break; + case TVL_RSHIFT: LOG(">>\n"); break; + case TVL_LESS: LOG("<\n"); break; + case TVL_LESSOREQUAL: LOG("<=\n"); break; + case TVL_GREATER: LOG(">\n"); break; + case TVL_GREATEROREQUAL:LOG(">=\n"); break; + case TVL_EQUAL: LOG("==\n"); break; + case TVL_NOTEQUAL: LOG("!=\n"); break; + case TVL_BAND: LOG("&\n"); break; + case TVL_BXOR: LOG("^\n"); break; + case TVL_BOR: LOG("|\n"); break; + case TVL_LAND: LOG("&&\n"); break; + case TVL_LOR: LOG("||\n"); break; + case TVL_ASSIGN: LOG("=\n"); break; + case TVL_ASSIGNMULTIPLY:LOG("*=\n"); break; + case TVL_ASSIGNDIVIDE: LOG("/=\n"); break; + case TVL_ASSIGNMODULO: LOG("%%=\n"); break; + case TVL_ASSIGNADD: LOG("+=\n"); break; + case TVL_ASSIGNSUBTRACT:LOG("-=\n"); break; + case TVL_ASSIGNLSHIFT: LOG("<<=\n"); break; + case TVL_ASSIGNRSHIFT: LOG(">>=\n"); break; + case TVL_ASSIGNBAND: LOG("&=\n"); break; + case TVL_ASSIGNBXOR: LOG("^=\n"); break; + case TVL_ASSIGNBOR: LOG("|=\n"); break; + case TVL_COMMA: LOG(",\n"); break; + case TVL_MEMORYAT: LOG(token.memory_side_effects() ? "mem!\n" : "mem@\n");break; + case TVL_EXECUTEFUNC: LOG("execute\n"); break; + default: LOG("INVALID OPERATOR\n"); break; + } } + else + LOG("INVALID\n"); } - osd_printf_debug("----\n"); -#endif + LOG("----\n"); } @@ -693,7 +1170,8 @@ void parsed_expression::parse_string_into_tokens() break; // initialize the current token object - parse_token &token = m_tokenlist.append(*global_alloc(parse_token(string - stringstart))); + m_tokenlist.emplace_back(string - stringstart); + parse_token &token = m_tokenlist.back(); // switch off the first character switch (tolower(u8(string[0]))) @@ -854,12 +1332,15 @@ void parsed_expression::parse_symbol_or_number(parse_token &token, const char *& // check for memory @ and ! operators if (string[0] == '@' || string[0] == '!') { - try { + try + { bool disable_se = string[0] == '@'; parse_memory_operator(token, buffer.c_str(), disable_se); string += 1; return; - } catch(const expression_error &) { + } + catch (const expression_error &) + { // Try some other operator instead } } @@ -943,21 +1424,20 @@ void parsed_expression::parse_symbol_or_number(parse_token &token, const char *& catch (expression_error const &err) { // this is really a hack, but 0B1234 could also hex depending on default base - if (expression_error::INVALID_NUMBER == err) - return parse_number(token, buffer.c_str(), DEFAULT_BASE, expression_error::INVALID_NUMBER); + if (expression_error::INVALID_NUMBER == err && m_default_base == 16) + return parse_number(token, buffer.c_str(), m_default_base, expression_error::INVALID_NUMBER); else throw; } - // TODO: for octal address spaces, treat 0123 as octal default: ; // fall through } - // fall through + [[fallthrough]]; default: // check for a symbol match - symbol_entry *symbol = m_symtable->find_deep(buffer.c_str()); + symbol_entry *symbol = m_symtable.get().find_deep(buffer.c_str()); if (symbol != nullptr) { token.configure_symbol(*symbol); @@ -965,14 +1445,15 @@ void parsed_expression::parse_symbol_or_number(parse_token &token, const char *& // if this is a function symbol, synthesize an execute function operator if (symbol->is_function()) { - parse_token &newtoken = m_tokenlist.append(*global_alloc(parse_token(string - stringstart))); + m_tokenlist.emplace_back(string - stringstart); + parse_token &newtoken = m_tokenlist.back(); newtoken.configure_operator(TVL_EXECUTEFUNC, 0); } return; } // attempt to parse as a number in the default base - parse_number(token, buffer.c_str(), DEFAULT_BASE, expression_error::UNKNOWN_SYMBOL); + parse_number(token, buffer.c_str(), m_default_base, expression_error::UNKNOWN_SYMBOL); } } @@ -1072,7 +1553,7 @@ void parsed_expression::parse_quoted_string(parse_token &token, const char *&str string++; // make the token - token.configure_string(m_stringlist.append(*global_alloc(expression_string(buffer.c_str())))); + token.configure_string(m_stringlist.emplace(m_stringlist.end(), buffer.c_str())->c_str()); } @@ -1089,17 +1570,17 @@ void parsed_expression::parse_memory_operator(parse_token &token, const char *st const char *dot = strrchr(string, '.'); if (dot != nullptr) { - namestring = m_stringlist.append(*global_alloc(expression_string(string, dot - string))); + namestring = m_stringlist.emplace(m_stringlist.end(), string, dot)->c_str(); string = dot + 1; } - // length 3 means logical/physical, then space, then size int length = (int)strlen(string); bool physical = false; int space = 'p'; int size; if (length == 3) { + // length 3 means logical/physical, then space, then size if (string[0] != 'l' && string[0] != 'p') throw expression_error(expression_error::INVALID_MEMORY_SPACE, token.offset() + (string - startstring)); if (string[1] != 'p' && string[1] != 'd' && string[1] != 'i' && string[1] != '3') @@ -1108,21 +1589,22 @@ void parsed_expression::parse_memory_operator(parse_token &token, const char *st space = string[1]; size = string[2]; } - - // length 2 means space then size else if (length == 2) { + // length 2 means space then size space = string[0]; size = string[1]; } - - // length 1 means size else if (length == 1) + { + // length 1 means size size = string[0]; - - // anything else is invalid + } else + { + // anything else is invalid throw expression_error(expression_error::INVALID_TOKEN, token.offset()); + } // convert the space to flags expression_space memspace; @@ -1131,9 +1613,9 @@ void parsed_expression::parse_memory_operator(parse_token &token, const char *st case 'p': memspace = physical ? EXPSPACE_PROGRAM_PHYSICAL : EXPSPACE_PROGRAM_LOGICAL; break; case 'd': memspace = physical ? EXPSPACE_DATA_PHYSICAL : EXPSPACE_DATA_LOGICAL; break; case 'i': memspace = physical ? EXPSPACE_IO_PHYSICAL : EXPSPACE_IO_LOGICAL; break; - case '3': memspace = physical ? EXPSPACE_SPACE3_PHYSICAL : EXPSPACE_SPACE3_LOGICAL; break; - case 'o': memspace = EXPSPACE_OPCODE; break; - case 'r': memspace = EXPSPACE_RAMWRITE; break; + case '3': memspace = physical ? EXPSPACE_OPCODE_PHYSICAL : EXPSPACE_OPCODE_LOGICAL; break; + case 'r': memspace = EXPSPACE_PRGDIRECT; break; + case 'o': memspace = EXPSPACE_OPDIRECT; break; case 'm': memspace = EXPSPACE_REGION; break; default: throw expression_error(expression_error::INVALID_MEMORY_SPACE, token.offset() + (string - startstring)); } @@ -1150,12 +1632,9 @@ void parsed_expression::parse_memory_operator(parse_token &token, const char *st } // validate the name - if (m_symtable != nullptr) - { - expression_error::error_code err = m_symtable->memory_valid(namestring, memspace); - if (err != expression_error::NONE) - throw expression_error(err, token.offset() + (string - startstring)); - } + expression_error::error_code err = m_symtable.get().memory_valid(namestring, memspace); + if (err != expression_error::NONE) + throw expression_error(err, token.offset() + (string - startstring)); // configure the token token.configure_operator(TVL_MEMORYAT, 2).set_memory_size(memsize).set_memory_space(memspace).set_memory_source(namestring).set_memory_side_effects(disable_se); @@ -1167,9 +1646,8 @@ void parsed_expression::parse_memory_operator(parse_token &token, const char *st // ambiguities based on neighboring tokens //------------------------------------------------- -void parsed_expression::normalize_operator(parse_token *prevtoken, parse_token &thistoken) +void parsed_expression::normalize_operator(parse_token &thistoken, parse_token *prevtoken, parse_token *nexttoken, const std::list<parse_token> &stack, bool was_rparen) { - parse_token *nexttoken = thistoken.next(); switch (thistoken.optype()) { // Determine if an open paren is part of a function or not @@ -1203,15 +1681,15 @@ void parsed_expression::normalize_operator(parse_token *prevtoken, parse_token & case TVL_SUBTRACT: // Assume we're unary if we are the first token, or if the previous token is not // a symbol, a number, or a right parenthesis - if (prevtoken == nullptr || (!prevtoken->is_symbol() && !prevtoken->is_number() && !prevtoken->is_operator(TVL_RPAREN))) + if (prevtoken == nullptr || (!prevtoken->is_symbol() && !prevtoken->is_number() && !was_rparen)) thistoken.configure_operator(thistoken.is_operator(TVL_ADD) ? TVL_UPLUS : TVL_UMINUS, 2); break; // Determine if , refers to a function parameter case TVL_COMMA: - for (auto lookback = m_token_stack.rbegin(); lookback != m_token_stack.rend(); ++lookback) + for (auto lookback = stack.begin(); lookback != stack.end(); ++lookback) { - parse_token &peek = *lookback; + const parse_token &peek = *lookback; // if we hit an execute function operator, or else a left parenthesis that is // already tagged, then tag us as well @@ -1233,51 +1711,63 @@ void parsed_expression::normalize_operator(parse_token *prevtoken, parse_token & void parsed_expression::infix_to_postfix() { - simple_list<parse_token> stack; + std::list<parse_token> stack; + parse_token *prev = nullptr; + + // this flag is used to avoid looking back at a closing parenthesis that was already destroyed + bool was_rparen = false; // loop over all the original tokens - parse_token *prev = nullptr; - parse_token *next; - for (parse_token *token = m_tokenlist.detach_all(); token != nullptr; prev = token, token = next) + std::list<parse_token>::iterator next; + std::list<parse_token> origlist = std::move(m_tokenlist); + m_tokenlist.clear(); + for (std::list<parse_token>::iterator token = origlist.begin(); token != origlist.end(); token = next) { // pre-determine our next token - next = token->next(); + next = std::next(token); // if the character is an operand, append it to the result string if (token->is_number() || token->is_symbol() || token->is_string()) - m_tokenlist.append(*token); + { + m_tokenlist.splice(m_tokenlist.end(), origlist, token); + + // remember this as the previous token + prev = &*token; + was_rparen = false; + } // if this is an operator, process it else if (token->is_operator()) { // normalize the operator based on neighbors - normalize_operator(prev, *token); + normalize_operator(*token, prev, next != origlist.end() ? &*next : nullptr, stack, was_rparen); + was_rparen = false; // if the token is an opening parenthesis, push it onto the stack. if (token->is_operator(TVL_LPAREN)) - stack.prepend(*token); + stack.splice(stack.begin(), origlist, token); // if the token is a closing parenthesis, pop all operators until we // reach an opening parenthesis and append them to the result string, - // discaring the open parenthesis + // discarding the open parenthesis else if (token->is_operator(TVL_RPAREN)) { - // loop until we find our matching opener - parse_token *popped; - while ((popped = stack.detach_head()) != nullptr) - { - if (popped->is_operator(TVL_LPAREN)) - break; - m_tokenlist.append(*popped); - } + // find our matching opener + std::list<parse_token>::iterator lparen = std::find_if(stack.begin(), stack.end(), + [] (const parse_token &token) { return token.is_operator(TVL_LPAREN); } + ); // if we didn't find an open paren, it's an error - if (popped == nullptr) + if (lparen == stack.end()) throw expression_error(expression_error::UNBALANCED_PARENS, token->offset()); + // move the stacked operators to the end of the new list + m_tokenlist.splice(m_tokenlist.end(), stack, stack.begin(), lparen); + // free ourself and our matching opening parenthesis - global_free(token); - global_free(popped); + origlist.erase(token); + stack.erase(lparen); + was_rparen = true; } // if the token is an operator, pop operators until we reach an opening parenthesis, @@ -1288,8 +1778,8 @@ void parsed_expression::infix_to_postfix() int our_precedence = token->precedence(); // loop until we can't peek at the stack anymore - parse_token *peek; - while ((peek = stack.first()) != nullptr) + std::list<parse_token>::iterator peek; + for (peek = stack.begin(); peek != stack.end(); ++peek) { // break if any of the above conditions are true if (peek->is_operator(TVL_LPAREN)) @@ -1297,28 +1787,29 @@ void parsed_expression::infix_to_postfix() int stack_precedence = peek->precedence(); if (stack_precedence > our_precedence || (stack_precedence == our_precedence && peek->right_to_left())) break; - - // pop this token - m_tokenlist.append(*stack.detach_head()); } + // move the stacked operands to the end of the new list + m_tokenlist.splice(m_tokenlist.end(), stack, stack.begin(), peek); + // push the new operator - stack.prepend(*token); + stack.splice(stack.begin(), origlist, token); } + + if (!was_rparen) + prev = &*token; } } - // finish popping the stack - parse_token *popped; - while ((popped = stack.detach_head()) != nullptr) - { - // it is an error to have a left parenthesis still on the stack - if (popped->is_operator(TVL_LPAREN)) - throw expression_error(expression_error::UNBALANCED_PARENS, popped->offset()); + // it is an error to have a left parenthesis still on the stack + std::list<parse_token>::iterator lparen = std::find_if(stack.begin(), stack.end(), + [] (const parse_token &token) { return token.is_operator(TVL_LPAREN); } + ); + if (lparen != stack.end()) + throw expression_error(expression_error::UNBALANCED_PARENS, lparen->offset()); - // pop this token - m_tokenlist.append(*popped); - } + // pop all remaining tokens + m_tokenlist.splice(m_tokenlist.end(), stack, stack.begin(), stack.end()); } @@ -1664,8 +2155,7 @@ u64 parsed_expression::execute_tokens() //------------------------------------------------- parsed_expression::parse_token::parse_token(int offset) - : m_next(nullptr), - m_type(INVALID), + : m_type(INVALID), m_offset(offset), m_value(0), m_flags(0), @@ -1680,16 +2170,15 @@ parsed_expression::parse_token::parse_token(int offset) // for a SYMBOL token //------------------------------------------------- -u64 parsed_expression::parse_token::get_lval_value(symbol_table *table) +u64 parsed_expression::parse_token::get_lval_value(symbol_table &table) { // get the value of a symbol if (is_symbol()) return m_symbol->value(); // or get the value from the memory callbacks - else if (is_memory() && table != nullptr) { - return table->memory_value(m_string, memory_space(), address(), 1 << memory_size(), memory_side_effects()); - } + else if (is_memory()) + return table.memory_value(m_string, memory_space(), address(), 1 << memory_size(), memory_side_effects()); return 0; } @@ -1700,15 +2189,15 @@ u64 parsed_expression::parse_token::get_lval_value(symbol_table *table) // for a SYMBOL token //------------------------------------------------- -inline void parsed_expression::parse_token::set_lval_value(symbol_table *table, u64 value) +inline void parsed_expression::parse_token::set_lval_value(symbol_table &table, u64 value) { // set the value of a symbol if (is_symbol()) m_symbol->set_value(value); // or set the value via the memory callbacks - else if (is_memory() && table != nullptr) - table->set_memory_value(m_string, memory_space(), address(), 1 << memory_size(), value, memory_side_effects()); + else if (is_memory()) + table.set_memory_value(m_string, memory_space(), address(), 1 << memory_size(), value, memory_side_effects()); } @@ -1733,7 +2222,7 @@ void parsed_expression::execute_function(parse_token &token) // if it is a function symbol, break out of the loop if (peek.is_symbol()) { - symbol = peek.symbol(); + symbol = &peek.symbol(); if (symbol->is_function()) { m_token_stack.pop_back(); @@ -1751,8 +2240,14 @@ void parsed_expression::execute_function(parse_token &token) if (paramcount == MAX_FUNCTION_PARAMS) throw expression_error(expression_error::INVALID_PARAM_COUNT, token.offset()); - // execute the function and push the result + // validate parameters function_symbol_entry *function = downcast<function_symbol_entry *>(symbol); + if (paramcount < function->minparams()) + throw expression_error(expression_error::TOO_FEW_PARAMS, token.offset(), function->minparams()); + if (paramcount > function->maxparams()) + throw expression_error(expression_error::TOO_MANY_PARAMS, token.offset(), function->maxparams()); + + // execute the function and push the result parse_token result(token.offset()); result.configure_number(function->execute(paramcount, &funcparams[MAX_FUNCTION_PARAMS - paramcount])); push_token(result); diff --git a/src/emu/debug/express.h b/src/emu/debug/express.h index ee5a500ad0e..251d8b4365c 100644 --- a/src/emu/debug/express.h +++ b/src/emu/debug/express.h @@ -17,6 +17,8 @@ #include <deque> #include <functional> +#include <list> +#include <string_view> #include <unordered_map> @@ -32,13 +34,13 @@ enum expression_space EXPSPACE_PROGRAM_LOGICAL, EXPSPACE_DATA_LOGICAL, EXPSPACE_IO_LOGICAL, - EXPSPACE_SPACE3_LOGICAL, + EXPSPACE_OPCODE_LOGICAL, EXPSPACE_PROGRAM_PHYSICAL, EXPSPACE_DATA_PHYSICAL, EXPSPACE_IO_PHYSICAL, - EXPSPACE_SPACE3_PHYSICAL, - EXPSPACE_OPCODE, - EXPSPACE_RAMWRITE, + EXPSPACE_OPCODE_PHYSICAL, + EXPSPACE_PRGDIRECT, + EXPSPACE_OPDIRECT, EXPSPACE_REGION }; @@ -48,6 +50,8 @@ enum expression_space // TYPE DEFINITIONS //************************************************************************** +using offs_t = u32; + // ======================> expression_error // an expression_error holds an error code and a string offset @@ -70,6 +74,8 @@ public: DIVIDE_BY_ZERO, OUT_OF_MEMORY, INVALID_PARAM_COUNT, + TOO_FEW_PARAMS, + TOO_MANY_PARAMS, UNBALANCED_QUOTES, TOO_MANY_STRINGS, INVALID_MEMORY_SIZE, @@ -80,22 +86,26 @@ public: }; // construction/destruction - expression_error(error_code code, int offset = 0) - : m_code(code), - m_offset(offset) { } + constexpr expression_error(error_code code, int offset = 0, int num = 0) + : m_code(code) + , m_offset(offset) + , m_num(num) + { + } // operators - operator error_code() const { return m_code; } + constexpr operator error_code() const { return m_code; } // getters - error_code code() const { return m_code; } - int offset() const { return m_offset; } - const char *code_string() const; + constexpr error_code code() const { return m_code; } + constexpr int offset() const { return m_offset; } + std::string code_string() const; private: // internal state error_code m_code; int m_offset; + int m_num; }; @@ -104,8 +114,6 @@ private: // symbol_entry describes a symbol in a symbol table class symbol_entry { - friend class simple_list<symbol_entry>; - protected: // symbol types enum symbol_type @@ -120,7 +128,6 @@ public: virtual ~symbol_entry(); // getters - symbol_entry *next() const { return m_next; } const char *name() const { return m_name.c_str(); } const std::string &format() const { return m_format; } @@ -134,7 +141,6 @@ public: protected: // internal state - symbol_entry * m_next; // link to next entry symbol_table & m_table; // pointer back to the owning table symbol_type m_type; // type of symbol std::string m_name; // name of the symbol @@ -150,16 +156,14 @@ class symbol_table { public: // callback functions for getting/setting a symbol value - typedef std::function<u64(symbol_table &table)> getter_func; - typedef std::function<void(symbol_table &table, u64 value)> setter_func; + typedef std::function<u64()> getter_func; + typedef std::function<void(u64 value)> setter_func; // callback functions for function execution - typedef std::function<u64(symbol_table &table, int numparams, const u64 *paramlist)> execute_func; + typedef std::function<u64(int numparams, const u64 *paramlist)> execute_func; // callback functions for memory reads/writes - typedef std::function<expression_error::error_code(void *cbparam, const char *name, expression_space space)> valid_func; - typedef std::function<u64(void *cbparam, const char *name, expression_space space, u32 offset, int size, bool disable_se)> read_func; - typedef std::function<void(void *cbparam, const char *name, expression_space space, u32 offset, int size, u64 value, bool disable_se)> write_func; + typedef std::function<void()> memory_modified_func; enum read_write { @@ -168,21 +172,21 @@ public: }; // construction/destruction - symbol_table(void *globalref, symbol_table *parent = nullptr); + symbol_table(running_machine &machine, symbol_table *parent = nullptr, device_t *device = nullptr); // getters const std::unordered_map<std::string, std::unique_ptr<symbol_entry>> &entries() const { return m_symlist; } symbol_table *parent() const { return m_parent; } - void *globalref() const { return m_globalref; } + running_machine &machine() { return m_machine; } // setters - void configure_memory(void *param, valid_func valid, read_func read, write_func write); + void set_memory_modified_func(memory_modified_func modified); // symbol access - void add(const char *name, read_write rw, u64 *ptr = nullptr); - void add(const char *name, u64 constvalue); - void add(const char *name, getter_func getter, setter_func setter = nullptr, const std::string &format_string = ""); - void add(const char *name, int minparams, int maxparams, execute_func execute); + symbol_entry &add(const char *name, read_write rw, u64 *ptr = nullptr); + symbol_entry &add(const char *name, u64 constvalue); + symbol_entry &add(const char *name, getter_func getter, setter_func setter = nullptr, const std::string &format_string = ""); + symbol_entry &add(const char *name, int minparams, int maxparams, execute_func execute); symbol_entry *find(const char *name) const { if (name) { auto search = m_symlist.find(name); if (search != m_symlist.end()) return search->second.get(); else return nullptr; } else return nullptr; } symbol_entry *find_deep(const char *name); @@ -194,16 +198,24 @@ public: expression_error::error_code memory_valid(const char *name, expression_space space); u64 memory_value(const char *name, expression_space space, u32 offset, int size, bool disable_se); void set_memory_value(const char *name, expression_space space, u32 offset, int size, u64 value, bool disable_se); + u64 read_memory(address_space &space, offs_t address, int size, bool apply_translation); + void write_memory(address_space &space, offs_t address, u64 data, int size, bool apply_translation); private: + // memory helpers + u64 read_program_direct(address_space &space, int opcode, offs_t address, int size); + u64 read_memory_region(const char *rgntag, offs_t address, int size); + void write_program_direct(address_space &space, int opcode, offs_t address, int size, u64 data); + void write_memory_region(const char *rgntag, offs_t address, int size, u64 data); + expression_error expression_get_space(const char *tag, int &spacenum, device_memory_interface *&memory); + void notify_memory_modified(); + // internal state + running_machine & m_machine; // reference to the machine symbol_table * m_parent; // pointer to the parent symbol table - void * m_globalref; // global reference parameter std::unordered_map<std::string,std::unique_ptr<symbol_entry>> m_symlist; // list of symbols - void * m_memory_param; // callback parameter for memory - valid_func m_memory_valid; // validation callback - read_func m_memory_read; // read callback - write_func m_memory_write; // write callback + device_memory_interface *const m_memintf; // pointer to the local memory interface (if any) + memory_modified_func m_memory_modified; // memory modified callback }; @@ -215,30 +227,32 @@ class parsed_expression { public: // construction/destruction - parsed_expression(const parsed_expression &src) { copy(src); } - parsed_expression(symbol_table *symtable = nullptr, const char *expression = nullptr, u64 *result = nullptr); + parsed_expression(symbol_table &symtable); + parsed_expression(symbol_table &symtable, std::string_view expression, int default_base = 16); + parsed_expression(const parsed_expression &src); + parsed_expression(parsed_expression &&src) = default; // operators parsed_expression &operator=(const parsed_expression &src) { copy(src); return *this; } + parsed_expression &operator=(parsed_expression &&src) = default; // getters - bool is_empty() const { return (m_tokenlist.count() == 0); } + bool is_empty() const { return m_tokenlist.empty(); } const char *original_string() const { return m_original_string.c_str(); } - symbol_table *symbols() const { return m_symtable; } + symbol_table &symbols() const { return m_symtable.get(); } // setters - void set_symbols(symbol_table *symtable) { m_symtable = symtable; } + void set_symbols(symbol_table &symtable) { m_symtable = std::reference_wrapper<symbol_table>(symtable); } + void set_default_base(int base) { assert(base == 8 || base == 10 || base == 16); m_default_base = base; } // execution - void parse(const char *string); + void parse(std::string_view string); u64 execute() { return execute_tokens(); } private: // a single token class parse_token { - friend class simple_list<parse_token>; - // operator flags enum { @@ -274,7 +288,6 @@ private: parse_token(int offset = 0); // getters - parse_token *next() const { return m_next; } int offset() const { return m_offset; } bool is_number() const { return (m_type == NUMBER); } bool is_string() const { return (m_type == STRING); } @@ -285,8 +298,9 @@ private: bool is_lval() const { return ((m_type == SYMBOL && m_symbol->is_lval()) || m_type == MEMORY); } u64 value() const { assert(m_type == NUMBER); return m_value; } + const char *string() const { assert(m_type == STRING); return m_string; } u32 address() const { assert(m_type == MEMORY); return m_value; } - symbol_entry *symbol() const { assert(m_type == SYMBOL); return m_symbol; } + symbol_entry &symbol() const { assert(m_type == SYMBOL); return *m_symbol; } u8 optype() const { assert(m_type == OPERATOR); return (m_flags & TIN_OPTYPE_MASK) >> TIN_OPTYPE_SHIFT; } u8 precedence() const { assert(m_type == OPERATOR); return (m_flags & TIN_PRECEDENCE_MASK) >> TIN_PRECEDENCE_SHIFT; } @@ -315,12 +329,11 @@ private: parse_token &set_memory_source(const char *string) { assert(m_type == OPERATOR || m_type == MEMORY); m_string = string; return *this; } // access - u64 get_lval_value(symbol_table *symtable); - void set_lval_value(symbol_table *symtable, u64 value); + u64 get_lval_value(symbol_table &symtable); + void set_lval_value(symbol_table &symtable, u64 value); private: // internal state - parse_token * m_next; // next token in list token_type m_type; // type of token int m_offset; // offset within the string u64 m_value; // integral value @@ -329,30 +342,9 @@ private: symbol_entry * m_symbol; // symbol pointer }; - // an expression_string holds an indexed string parsed from the expression - class expression_string - { - friend class simple_list<expression_string>; - - public: - // construction/destruction - expression_string(const char *string, int length = 0) - : m_next(nullptr), - m_string(string, (length == 0) ? strlen(string) : length) { } - - // operators - operator const char *() { return m_string.c_str(); } - operator const char *() const { return m_string.c_str(); } - - private: - // internal state - expression_string * m_next; // next string in list - std::string m_string; // copy of the string - }; - // internal helpers void copy(const parsed_expression &src); - void print_tokens(FILE *out); + void print_tokens(); // parsing helpers void parse_string_into_tokens(); @@ -361,7 +353,7 @@ private: void parse_quoted_char(parse_token &token, const char *&string); void parse_quoted_string(parse_token &token, const char *&string); void parse_memory_operator(parse_token &token, const char *string, bool disable_se); - void normalize_operator(parse_token *prevtoken, parse_token &thistoken); + void normalize_operator(parse_token &thistoken, parse_token *prevtoken, parse_token *nexttoken, const std::list<parse_token> &stack, bool was_rparen); void infix_to_postfix(); // execution helpers @@ -376,10 +368,11 @@ private: static const int MAX_FUNCTION_PARAMS = 16; // internal state - symbol_table * m_symtable; // symbol table + std::reference_wrapper<symbol_table> m_symtable; // symbol table + int m_default_base; // default base std::string m_original_string; // original string (prior to parsing) - simple_list<parse_token> m_tokenlist; // token list - simple_list<expression_string> m_stringlist; // string list + std::list<parse_token> m_tokenlist; // token list + std::list<std::string> m_stringlist; // string list std::deque<parse_token> m_token_stack; // token stack (used during execution) }; diff --git a/src/emu/debug/points.cpp b/src/emu/debug/points.cpp new file mode 100644 index 00000000000..12e4b614843 --- /dev/null +++ b/src/emu/debug/points.cpp @@ -0,0 +1,525 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + points.cpp + + Debugger breakpoints, watchpoints, etc. + +***************************************************************************/ + +#include "emu.h" +#include "points.h" + +#include "debugger.h" +#include "debugcon.h" +#include "debugcpu.h" + + +//************************************************************************** +// DEBUG BREAKPOINT +//************************************************************************** + +//------------------------------------------------- +// debug_breakpoint - constructor +//------------------------------------------------- + +debug_breakpoint::debug_breakpoint( + device_debug *debugInterface, + symbol_table &symbols, + int index, + offs_t address, + const char *condition, + std::string_view action) : + m_debugInterface(debugInterface), + m_index(index), + m_enabled(true), + m_address(address), + m_condition(symbols, condition ? condition : "1"), + m_action(action) +{ +} + + +//------------------------------------------------- +// hit - detect a hit +//------------------------------------------------- + +bool debug_breakpoint::hit(offs_t pc) +{ + // don't hit if disabled + if (!m_enabled) + return false; + + // must match our address + if (m_address != pc) + return false; + + // must satisfy the condition + if (!m_condition.is_empty()) + { + try + { + return (m_condition.execute() != 0); + } + catch (expression_error const &) + { + return false; + } + } + + return true; +} + + + +//************************************************************************** +// DEBUG WATCHPOINT +//************************************************************************** + +//------------------------------------------------- +// debug_watchpoint - constructor +//------------------------------------------------- + +debug_watchpoint::debug_watchpoint( + device_debug* debugInterface, + symbol_table &symbols, + int index, + address_space &space, + read_or_write type, + offs_t address, + offs_t length, + const char *condition, + std::string_view action) : + m_debugInterface(debugInterface), + m_phr(nullptr), + m_phw(nullptr), + m_space(space), + m_index(index), + m_enabled(true), + m_type(type), + m_address(address & space.addrmask()), + m_length(length), + m_condition(symbols, condition ? condition : "1"), + m_action(action), + m_installing(false) +{ + std::fill(std::begin(m_start_address), std::end(m_start_address), 0); + std::fill(std::begin(m_end_address), std::end(m_end_address), 0); + std::fill(std::begin(m_masks), std::end(m_masks), 0); + + int ashift = m_space.addr_shift(); + endianness_t endian = m_space.endianness(); + offs_t subamask = m_space.alignment() - 1; + offs_t unit_size = ashift <= 0 ? 8 << -ashift : 8 >> ashift; + offs_t start = m_address; + offs_t end = (m_address + m_length - 1) & space.addrmask(); + if (end < start) + end = space.addrmask(); + offs_t rstart = start & ~subamask; + offs_t rend = end | subamask; + u64 smask, mmask, emask; + smask = mmask = emask = make_bitmask<u64>(m_space.data_width()); + if (start != rstart) + { + if (endian == ENDIANNESS_LITTLE) + smask &= ~make_bitmask<u64>((start - rstart) * unit_size); + else + smask &= make_bitmask<u64>((rstart + subamask + 1 - start) * unit_size); + } + if (end != rend) + { + if (endian == ENDIANNESS_LITTLE) + emask &= make_bitmask<u64>((subamask + 1 + end - rend) * unit_size); + else + emask &= ~make_bitmask<u64>((rend - end) * unit_size); + } + + if (rend == (rstart | subamask) || smask == emask) + { + m_start_address[0] = rstart; + m_end_address[0] = rend; + m_masks[0] = smask & emask; + } + else + { + int idx = 0; + if (smask != mmask) + { + m_start_address[idx] = rstart; + m_end_address[idx] = rstart | subamask; + m_masks[idx] = smask; + idx++; + rstart += subamask + 1; + } + if (mmask == emask) + { + m_start_address[idx] = rstart; + m_end_address[idx] = rend; + m_masks[idx] = emask; + } + else + { + if (rstart < rend - subamask) + { + m_start_address[idx] = rstart; + m_end_address[idx] = rend - subamask - 1; + m_masks[idx] = mmask; + idx++; + } + m_start_address[idx] = rend - subamask; + m_end_address[idx] = rend; + m_masks[idx] = emask; + } + } + + install(read_or_write::READWRITE); + m_notifier = m_space.add_change_notifier( + [this] (read_or_write mode) + { + if (m_enabled) + { + install(mode); + } + }); +} + +debug_watchpoint::~debug_watchpoint() +{ + m_notifier.reset(); + m_phr.remove(); + m_phw.remove(); +} + +void debug_watchpoint::setEnabled(bool value) +{ + if (m_enabled != value) + { + m_enabled = value; + if (m_enabled) + install(read_or_write::READWRITE); + else + { + m_installing = true; + m_phr.remove(); + m_phw.remove(); + m_installing = false; + } + } +} + +void debug_watchpoint::install(read_or_write mode) +{ + if (m_installing) + return; + m_installing = true; + if (u32(mode) & u32(read_or_write::READ)) + m_phr.remove(); + if (u32(mode) & u32(read_or_write::WRITE)) + m_phw.remove(); + std::string name = util::string_format("wp@%x", m_address); + switch (m_space.data_width()) + { + case 8: + if (u32(m_type) & u32(mode) & u32(read_or_write::READ)) + m_phr = m_space.install_read_tap( + m_start_address[0], m_end_address[0], name, + [this](offs_t offset, u8 &data, u8 mem_mask) { + triggered(read_or_write::READ, offset, data, mem_mask); + }, + &m_phr); + if (u32(m_type) & u32(mode) & u32(read_or_write::WRITE)) + m_phw = m_space.install_write_tap( + m_start_address[0], m_end_address[0], name, + [this](offs_t offset, u8 &data, u8 mem_mask) { + triggered(read_or_write::WRITE, offset, data, mem_mask); + }, + &m_phw); + break; + + case 16: + for (int i=0; i != 3; i++) + if (m_masks[i]) + { + u16 mask = m_masks[i]; + if (u32(m_type) & u32(mode) & u32(read_or_write::READ)) + m_phr = m_space.install_read_tap( + m_start_address[i], m_end_address[i], name, + [this, mask](offs_t offset, u16 &data, u16 mem_mask) { + if (mem_mask & mask) + triggered(read_or_write::READ, offset, data, mem_mask); + }, + &m_phr); + if (u32(m_type) & u32(mode) & u32(read_or_write::WRITE)) + m_phw = m_space.install_write_tap( + m_start_address[i], m_end_address[i], name, + [this, mask](offs_t offset, u16 &data, u16 mem_mask) { + if (mem_mask & mask) + triggered(read_or_write::WRITE, offset, data, mem_mask); + }, + &m_phw); + } + break; + + case 32: + for (int i=0; i != 3; i++) + if (m_masks[i]) + { + u32 mask = m_masks[i]; + if (u32(m_type) & u32(mode) & u32(read_or_write::READ)) + m_phr = m_space.install_read_tap( + m_start_address[i], m_end_address[i], name, + [this, mask](offs_t offset, u32 &data, u32 mem_mask) { + if (mem_mask & mask) + triggered(read_or_write::READ, offset, data, mem_mask); + }, + &m_phr); + if (u32(m_type) & u32(mode) & u32(read_or_write::WRITE)) + m_phw = m_space.install_write_tap( + m_start_address[i], m_end_address[i], name, + [this, mask](offs_t offset, u32 &data, u32 mem_mask) { + if (mem_mask & mask) + triggered(read_or_write::WRITE, offset, data, mem_mask); + }, + &m_phw); + } + break; + + case 64: + for (int i=0; i != 3; i++) + if (m_masks[i]) + { + u64 mask = m_masks[i]; + if (u32(m_type) & u32(mode) & u32(read_or_write::READ)) + m_phr = m_space.install_read_tap( + m_start_address[i], m_end_address[i], name, + [this, mask](offs_t offset, u64 &data, u64 mem_mask) { + if (mem_mask & mask) + triggered(read_or_write::READ, offset, data, mem_mask); + }, + &m_phr); + if (u32(m_type) & u32(mode) & u32(read_or_write::WRITE)) + m_phw = m_space.install_write_tap(m_start_address[i], m_end_address[i], name, + [this, mask](offs_t offset, u64 &data, u64 mem_mask) { + if (mem_mask & mask) + triggered(read_or_write::WRITE, offset, data, mem_mask); + }, + &m_phw); + } + break; + } + m_installing = false; +} + +void debug_watchpoint::triggered(read_or_write type, offs_t address, u64 data, u64 mem_mask) +{ + running_machine &machine = m_debugInterface->device().machine(); + debugger_manager &debug = machine.debugger(); + + // if we're within debugger code, don't trigger + if (debug.cpu().within_instruction_hook() || machine.side_effects_disabled()) + return; + + // adjust address, size & value_to_write based on mem_mask. + offs_t size = 0; + int ashift = m_space.addr_shift(); + offs_t unit_size = ashift <= 0 ? 8 << -ashift : 8 >> ashift; + u64 unit_mask = make_bitmask<u64>(unit_size); + + offs_t address_offset = 0; + + if(!mem_mask) + mem_mask = 0xff; + + while (!(mem_mask & unit_mask)) + { + address_offset++; + data >>= unit_size; + mem_mask >>= unit_size; + } + + while (mem_mask) + { + size++; + mem_mask >>= unit_size; + } + + data &= make_bitmask<u64>(size * unit_size); + + if (m_space.endianness() == ENDIANNESS_LITTLE) + address += address_offset; + else + address += m_space.alignment() - size - address_offset; + + // stash the value that will be written or has just been read + debug.cpu().set_wpinfo(address, data, size * unit_size); + + // protect against recursion + debug.cpu().set_within_instruction(true); + + // must satisfy the condition + if (!m_condition.is_empty()) + { + try + { + if (!m_condition.execute()) + { + debug.cpu().set_within_instruction(false); + return; + } + } + catch (expression_error &) + { + debug.cpu().set_within_instruction(false); + return; + } + } + + // halt in the debugger by default + bool was_stopped = debug.cpu().is_stopped(); + debug.cpu().set_execution_stopped(); + + // evaluate the action + if (!m_action.empty()) + debug.console().execute_command(m_action, false); + + // print a notification, unless the action made us go again + if (debug.cpu().is_stopped()) + { + std::string buffer; + + if (m_space.is_octal()) + buffer = string_format(type == read_or_write::READ ? + "Stopped at watchpoint %X reading %0*o from %0*o" : + "Stopped at watchpoint %X writing %0*o to %0*o", + m_index, + (size * unit_size + 2) / 3, + data, + (m_space.addr_width() + 2) / 3, + address); + else + buffer = string_format(type == read_or_write::READ ? + "Stopped at watchpoint %X reading %0*X from %0*X" : + "Stopped at watchpoint %X writing %0*X to %0*X", + m_index, + size * unit_size / 4, + data, + (m_space.addr_width() + 3) / 4, + address); + + const device_state_interface *state; + if (debug.cpu().live_cpu() == &m_debugInterface->device() && m_debugInterface->device().interface(state)) + { + debug.console().printf("%s (PC=%s)\n", buffer, state->state_string(STATE_GENPCBASE)); + m_debugInterface->compute_debug_flags(); + } + else if (!was_stopped) + { + debug.console().printf("%s\n", buffer); + debug.cpu().set_execution_running(); + debug.cpu().set_break_cpu(&m_debugInterface->device()); + } + m_debugInterface->set_triggered_watchpoint(this); + } + + debug.cpu().set_within_instruction(false); +} + +//************************************************************************** +// DEBUG REGISTERPOINT +//************************************************************************** + +//------------------------------------------------- +// debug_registerpoint - constructor +//------------------------------------------------- + +debug_registerpoint::debug_registerpoint(symbol_table &symbols, int index, const char *condition, std::string_view action) + : m_index(index), + m_enabled(true), + m_condition(symbols, (condition != nullptr) ? condition : "1"), + m_action(action) +{ +} + + +//------------------------------------------------- +// hit - detect a hit +//------------------------------------------------- + +bool debug_registerpoint::hit() +{ + // don't hit if disabled + if (!m_enabled) + return false; + + // must satisfy the condition + if (!m_condition.is_empty()) + { + try + { + return (m_condition.execute() != 0); + } + catch (expression_error &) + { + return false; + } + } + + return true; +} + + +//************************************************************************** +// DEBUG EXCEPTION POINT +//************************************************************************** + +//------------------------------------------------- +// debug_exceptionpoint - constructor +//------------------------------------------------- + +debug_exceptionpoint::debug_exceptionpoint( + device_debug *debugInterface, + symbol_table &symbols, + int index, + int type, + const char *condition, + std::string_view action) + : m_debugInterface(debugInterface), + m_index(index), + m_enabled(true), + m_type(type), + m_condition(symbols, (condition != nullptr) ? condition : "1"), + m_action(action) +{ +} + + +//------------------------------------------------- +// hit - detect a hit +//------------------------------------------------- + +bool debug_exceptionpoint::hit(int exception) +{ + // don't hit if disabled + if (!m_enabled) + return false; + + // must match our type + if (m_type != exception) + return false; + + // must satisfy the condition + if (!m_condition.is_empty()) + { + try + { + return (m_condition.execute() != 0); + } + catch (expression_error &) + { + return false; + } + } + + return true; +} diff --git a/src/emu/debug/points.h b/src/emu/debug/points.h new file mode 100644 index 00000000000..cf9e5bfc44d --- /dev/null +++ b/src/emu/debug/points.h @@ -0,0 +1,186 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + points.h + + Debugger breakpoints, watchpoints, etc. + +***************************************************************************/ + +#ifndef MAME_EMU_DEBUG_POINTS_H +#define MAME_EMU_DEBUG_POINTS_H + +#pragma once + +#include "express.h" + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + + +// ======================> debug_breakpoint + +class debug_breakpoint +{ + friend class device_debug; + +public: + // construction/destruction + debug_breakpoint( + device_debug* debugInterface, + symbol_table &symbols, + int index, + offs_t address, + const char *condition = nullptr, + std::string_view action = {}); + + // getters + const device_debug *debugInterface() const { return m_debugInterface; } + int index() const { return m_index; } + bool enabled() const { return m_enabled; } + offs_t address() const { return m_address; } + const char *condition() const { return m_condition.original_string(); } + const std::string &action() const { return m_action; } + + // setters + void setEnabled(bool value) { m_enabled = value; } // FIXME: need to update breakpoint flags but it's a private method + +private: + // internals + bool hit(offs_t pc); + const device_debug * m_debugInterface; // the interface we were created from + int m_index; // user reported index + bool m_enabled; // enabled? + offs_t m_address; // execution address + parsed_expression m_condition; // condition + std::string m_action; // action +}; + +// ======================> debug_watchpoint + +class debug_watchpoint +{ + friend class device_debug; + +public: + // construction/destruction + debug_watchpoint( + device_debug* debugInterface, + symbol_table &symbols, + int index, + address_space &space, + read_or_write type, + offs_t address, + offs_t length, + const char *condition = nullptr, + std::string_view action = {}); + ~debug_watchpoint(); + + // getters + const device_debug *debugInterface() const { return m_debugInterface; } + address_space &space() const { return m_space; } + int index() const { return m_index; } + read_or_write type() const { return m_type; } + bool enabled() const { return m_enabled; } + offs_t address() const { return m_address; } + offs_t length() const { return m_length; } + const char *condition() const { return m_condition.original_string(); } + const std::string &action() const { return m_action; } + + // setters + void setEnabled(bool value); + + // internals + bool hit(int type, offs_t address, int size); + +private: + void install(read_or_write mode); + void triggered(read_or_write type, offs_t address, u64 data, u64 mem_mask); + + device_debug * m_debugInterface; // the interface we were created from + memory_passthrough_handler m_phr; // passthrough handler reference, read access + memory_passthrough_handler m_phw; // passthrough handler reference, write access + address_space & m_space; // address space + int m_index; // user reported index + bool m_enabled; // enabled? + read_or_write m_type; // type (read/write) + offs_t m_address; // start address + offs_t m_length; // length of watch area + parsed_expression m_condition; // condition + std::string m_action; // action + util::notifier_subscription m_notifier; // address map change notifier ID + + offs_t m_start_address[3]; // the start addresses of the checks to install + offs_t m_end_address[3]; // the end addresses + u64 m_masks[3]; // the access masks + bool m_installing; // prevent recursive multiple installs +}; + +// ======================> debug_registerpoint + +class debug_registerpoint +{ + friend class device_debug; + +public: + // construction/destruction + debug_registerpoint(symbol_table &symbols, int index, const char *condition, std::string_view action = {}); + + // getters + int index() const { return m_index; } + bool enabled() const { return m_enabled; } + const char *condition() const { return m_condition.original_string(); } + const std::string &action() const { return m_action; } + +private: + // internals + bool hit(); + + int m_index; // user reported index + bool m_enabled; // enabled? + parsed_expression m_condition; // condition + std::string m_action; // action +}; + +// ======================> debug_exceptionpoint + +class debug_exceptionpoint +{ + friend class device_debug; + +public: + // construction/destruction + debug_exceptionpoint( + device_debug* debugInterface, + symbol_table &symbols, + int index, + int type, + const char *condition = nullptr, + std::string_view action = {}); + + // getters + const device_debug *debugInterface() const { return m_debugInterface; } + int index() const { return m_index; } + bool enabled() const { return m_enabled; } + int type() const { return m_type; } + const char *condition() const { return m_condition.original_string(); } + const std::string &action() const { return m_action; } + + // setters + void setEnabled(bool value) { m_enabled = value; } + +private: + // internals + bool hit(int exception); + const device_debug * m_debugInterface; // the interface we were created from + int m_index; // user reported index + bool m_enabled; // enabled? + int m_type; // exception type + parsed_expression m_condition; // condition + std::string m_action; // action +}; + +#endif // MAME_EMU_DEBUG_POINTS_H diff --git a/src/emu/debug/textbuf.cpp b/src/emu/debug/textbuf.cpp index 279ce8dead6..c70f8660185 100644 --- a/src/emu/debug/textbuf.cpp +++ b/src/emu/debug/textbuf.cpp @@ -2,15 +2,16 @@ // copyright-holders:Aaron Giles /*************************************************************************** - textbuf.c + textbuf.cpp Debugger text buffering engine. ***************************************************************************/ -#include "corealloc.h" #include "textbuf.h" +#include <new> + /*************************************************************************** @@ -27,47 +28,46 @@ struct text_buffer { - char * buffer; - s32 * lineoffs; - s32 bufsize; - s32 bufstart; - s32 bufend; - s32 linesize; - s32 linestart; - s32 lineend; - u32 linestartseq; - s32 maxwidth; -}; - - - -/*************************************************************************** - INLINE FUNCTIONS -***************************************************************************/ - -/*------------------------------------------------- - buffer_used - return the number of bytes - currently held in the buffer --------------------------------------------------*/ - -static inline s32 buffer_used(text_buffer *text) -{ - s32 used = text->bufend - text->bufstart; - if (used < 0) - used += text->bufsize; - return used; -} + text_buffer(u32 bytes, u32 lines) noexcept + : buffer(new (std::nothrow) char [bytes]) + , lineoffs(new (std::nothrow) s32 [lines]) + , bufsize(buffer ? bytes : 0) + , linesize(lineoffs ? lines : 0) + { + } + std::unique_ptr<char []> const buffer; + std::unique_ptr<s32 []> const lineoffs; + s32 const bufsize; + s32 bufstart = 0; + s32 bufend = 0; + s32 const linesize; + s32 linestart = 0; + s32 lineend = 0; + u32 linestartseq = 0; + s32 maxwidth = 0; + + /*------------------------------------------------- + buffer_used - return the number of bytes + currently held in the buffer + -------------------------------------------------*/ + + s32 buffer_used() const noexcept + { + s32 const used(bufend - bufstart); + return (used < 0) ? (used + bufsize) : used; + } -/*------------------------------------------------- - buffer_space - return the number of bytes - available in the buffer --------------------------------------------------*/ + /*------------------------------------------------- + buffer_space - return the number of bytes + available in the buffer + -------------------------------------------------*/ -static inline s32 buffer_space(text_buffer *text) -{ - return text->bufsize - buffer_used(text); -} + s32 buffer_space() const noexcept + { + return bufsize - buffer_used(); + } +}; @@ -81,36 +81,19 @@ static inline s32 buffer_space(text_buffer *text) text_buffer_alloc - allocate a new text buffer -------------------------------------------------*/ -text_buffer *text_buffer_alloc(u32 bytes, u32 lines) +text_buffer_ptr text_buffer_alloc(u32 bytes, u32 lines) { - text_buffer *text; + // allocate memory for the text buffer object + text_buffer_ptr text(new (std::nothrow) text_buffer(bytes, lines)); - /* allocate memory for the text buffer object */ - text = global_alloc_nothrow(text_buffer); if (!text) return nullptr; - /* allocate memory for the buffer itself */ - text->buffer = global_alloc_array_nothrow(char, bytes); - if (!text->buffer) - { - global_free(text); - return nullptr; - } - - /* allocate memory for the lines array */ - text->lineoffs = global_alloc_array_nothrow(s32, lines); - if (!text->lineoffs) - { - global_free_array(text->buffer); - global_free(text); + if (!text->buffer || !text->lineoffs) return nullptr; - } - /* initialize the buffer description */ - text->bufsize = bytes; - text->linesize = lines; - text_buffer_clear(text); + // initialize the buffer description + text_buffer_clear(*text); return text; } @@ -121,13 +104,9 @@ text_buffer *text_buffer_alloc(u32 bytes, u32 lines) text buffer -------------------------------------------------*/ -void text_buffer_free(text_buffer *text) +void text_buffer_deleter::operator()(text_buffer *text) const { - if (text->lineoffs) - global_free_array(text->lineoffs); - if (text->buffer) - global_free_array(text->buffer); - global_free(text); + delete text; } @@ -135,21 +114,21 @@ void text_buffer_free(text_buffer *text) text_buffer_clear - clear a text buffer -------------------------------------------------*/ -void text_buffer_clear(text_buffer *text) +void text_buffer_clear(text_buffer &text) { - /* reset all the buffer pointers and other bits */ - text->bufstart = 0; - text->bufend = 0; + // reset all the buffer pointers and other bits + text.bufstart = 0; + text.bufend = 0; - text->linestart = 0; - text->lineend = 0; - text->linestartseq = 0; + text.linestart = 0; + text.lineend = 0; + text.linestartseq = 0; - text->maxwidth = 0; + text.maxwidth = 0; - /* create the initial line */ - text->lineoffs[0] = 0; - text->buffer[text->lineoffs[0]] = 0; + // create the initial line + text.lineoffs[0] = 0; + text.buffer[text.lineoffs[0]] = 0; } @@ -160,109 +139,107 @@ void text_buffer_clear(text_buffer *text) ***************************************************************************/ -/*------------------------------------------------- - text_buffer_print - print data to the text - buffer --------------------------------------------------*/ +//------------------------------------------------- +// text_buffer_print - print data to the text +// buffer +//------------------------------------------------- -void text_buffer_print(text_buffer *text, const char *data) +void text_buffer_print(text_buffer &text, std::string_view data) { - text_buffer_print_wrap(text, data, 10000); + text_buffer_print_wrap(text, data, MAX_LINE_LENGTH); } -/*------------------------------------------------- - text_buffer_print_wrap - print data to the - text buffer with word wrapping to a given - column --------------------------------------------------*/ +//------------------------------------------------- +// text_buffer_print_wrap - print data to the +// text buffer with word wrapping to a given +// column +//------------------------------------------------- -void text_buffer_print_wrap(text_buffer *text, const char *data, int wrapcol) +void text_buffer_print_wrap(text_buffer &text, std::string_view data, int wrapcol) { - s32 stopcol = (wrapcol < MAX_LINE_LENGTH) ? wrapcol : MAX_LINE_LENGTH; - s32 needed_space; + s32 const stopcol = (wrapcol < MAX_LINE_LENGTH) ? wrapcol : MAX_LINE_LENGTH; - /* we need to ensure there is enough space for this string plus enough for the max line length */ - needed_space = s32(strlen(data)) + MAX_LINE_LENGTH; + // we need to ensure there is enough space for this string plus enough for the max line length + s32 const needed_space = s32(data.length()) + MAX_LINE_LENGTH; - /* make space in the buffer if we need to */ - while (buffer_space(text) < needed_space && text->linestart != text->lineend) + // make space in the buffer if we need to + while (text.buffer_space() < needed_space && text.linestart != text.lineend) { - text->linestartseq++; - if (++text->linestart >= text->linesize) - text->linestart = 0; - text->bufstart = text->lineoffs[text->linestart]; + text.linestartseq++; + if (++text.linestart >= text.linesize) + text.linestart = 0; + text.bufstart = text.lineoffs[text.linestart]; } - /* now add the data */ - for ( ; *data; data++) + // now add the data + for (int ch : data) { - int ch = *data; int linelen; - /* a CR resets our position */ + // a CR resets our position if (ch == '\r') - text->bufend = text->lineoffs[text->lineend]; + text.bufend = text.lineoffs[text.lineend]; - /* non-CR data is just characters */ + // non-CR data is just characters else if (ch != '\n') - text->buffer[text->bufend++] = ch; + text.buffer[text.bufend++] = ch; - /* an explicit newline or line-too-long condition inserts a newline */ - linelen = text->bufend - text->lineoffs[text->lineend]; + // an explicit newline or line-too-long condition inserts a newline */ + linelen = text.bufend - text.lineoffs[text.lineend]; if (ch == '\n' || linelen >= stopcol) { int overflow = 0; - /* if we're wrapping, back off until we hit a space */ + // if we're wrapping, back off until we hit a space if (linelen >= wrapcol) { - /* scan backwards, removing characters along the way */ + // scan backwards, removing characters along the way overflow = 1; - while (overflow < linelen && text->buffer[text->bufend - overflow] != ' ') + while (overflow < linelen && text.buffer[text.bufend - overflow] != ' ') overflow++; - /* if we found a space, take it; otherwise, reset and pretend we didn't try */ + // if we found a space, take it; otherwise, reset and pretend we didn't try if (overflow < linelen) linelen -= overflow; else overflow = 0; } - /* did we beat the max width */ - if (linelen > text->maxwidth) - text->maxwidth = linelen; + // did we beat the max width + if (linelen > text.maxwidth) + text.maxwidth = linelen; - /* append a terminator */ + // append a terminator if (overflow == 0) - text->buffer[text->bufend++] = 0; + text.buffer[text.bufend++] = 0; else - text->buffer[text->bufend - overflow] = 0; + text.buffer[text.bufend - overflow] = 0; - /* determine what the next line will be */ - if (++text->lineend >= text->linesize) - text->lineend = 0; + // determine what the next line will be + if (++text.lineend >= text.linesize) + text.lineend = 0; - /* if we're out of lines, consume the next one */ - if (text->lineend == text->linestart) + // if we're out of lines, consume the next one + if (text.lineend == text.linestart) { - text->linestartseq++; - if (++text->linestart >= text->linesize) - text->linestart = 0; - text->bufstart = text->lineoffs[text->linestart]; + text.linestartseq++; + if (++text.linestart >= text.linesize) + text.linestart = 0; + text.bufstart = text.lineoffs[text.linestart]; } - /* if we don't have enough room in the buffer for a max line, wrap to the start */ - if (text->bufend + MAX_LINE_LENGTH + 1 >= text->bufsize) - text->bufend = 0; + // if we don't have enough room in the buffer for a max line, wrap to the start + if (text.bufend + MAX_LINE_LENGTH + 1 >= text.bufsize) + text.bufend = 0; - /* create a new empty line */ - text->lineoffs[text->lineend] = text->bufend - (overflow ? (overflow - 1) : 0); + // create a new empty line + text.lineoffs[text.lineend] = text.bufend - (overflow ? (overflow - 1) : 0); } } - /* nullptr terminate what we have on this line */ - text->buffer[text->bufend] = 0; + // null terminate what we have on this line + text.buffer[text.bufend] = 0; } @@ -278,9 +255,9 @@ void text_buffer_print_wrap(text_buffer *text, const char *data, int wrapcol) width of all lines seen so far -------------------------------------------------*/ -u32 text_buffer_max_width(text_buffer *text) +u32 text_buffer_max_width(text_buffer const &text) { - return text->maxwidth; + return text.maxwidth; } @@ -289,12 +266,10 @@ u32 text_buffer_max_width(text_buffer *text) lines in the text buffer -------------------------------------------------*/ -u32 text_buffer_num_lines(text_buffer *text) +u32 text_buffer_num_lines(text_buffer const &text) { - s32 lines = text->lineend + 1 - text->linestart; - if (lines <= 0) - lines += text->linesize; - return lines; + s32 const lines = text.lineend + 1 - text.linestart; + return (lines <= 0) ? (lines + text.linesize) : lines; } @@ -303,9 +278,9 @@ u32 text_buffer_num_lines(text_buffer *text) line index into a sequence number -------------------------------------------------*/ -u32 text_buffer_line_index_to_seqnum(text_buffer *text, u32 index) +u32 text_buffer_line_index_to_seqnum(text_buffer const &text, u32 index) { - return text->linestartseq + index; + return text.linestartseq + index; } @@ -314,11 +289,68 @@ u32 text_buffer_line_index_to_seqnum(text_buffer *text, u32 index) an indexed line in the buffer -------------------------------------------------*/ -const char *text_buffer_get_seqnum_line(text_buffer *text, u32 seqnum) +const char *text_buffer_get_seqnum_line(text_buffer const &text, u32 seqnum) { - u32 numlines = text_buffer_num_lines(text); - u32 index = seqnum - text->linestartseq; + u32 const numlines = text_buffer_num_lines(text); + u32 const index = seqnum - text.linestartseq; if (index >= numlines) return nullptr; - return &text->buffer[text->lineoffs[(text->linestart + index) % text->linesize]]; + return &text.buffer[text.lineoffs[(text.linestart + index) % text.linesize]]; +} + +/*--------------------------------------------------------------------- + text_buffer_lines::text_buffer_line_iterator::operator* + Gets the line that the iterator currently points to. +-----------------------------------------------------------------------*/ + +std::string_view text_buffer_lines::text_buffer_line_iterator::operator*() const +{ + char const *const line = &m_buffer.buffer[m_buffer.lineoffs[m_lineptr]]; + + auto next_lineptr = m_lineptr + 1; + if (next_lineptr == m_buffer.linesize) + next_lineptr = 0; + + char const *const nextline = &m_buffer.buffer[m_buffer.lineoffs[next_lineptr]]; + + // -1 for the '\0' at the end of line + ptrdiff_t difference = (nextline - line) - 1; + + if (difference < 0) + difference += m_buffer.bufsize; + + return std::string_view{ line, std::string_view::size_type(difference) }; +} + +/*--------------------------------------------------------------------- + text_buffer_lines::text_buffer_line_iterator::operator++ + Moves to the next line. +-----------------------------------------------------------------------*/ + +text_buffer_lines::text_buffer_line_iterator &text_buffer_lines::text_buffer_line_iterator::operator++() +{ + if (++m_lineptr == m_buffer.linesize) + m_lineptr = 0; + + return *this; +} + +/*------------------------------------------------------ + text_buffer_lines::begin() + Returns an iterator that points to the first line. +--------------------------------------------------------*/ + +text_buffer_lines::iterator text_buffer_lines::begin() const +{ + return text_buffer_line_iterator(m_buffer, m_buffer.linestart); +} + +/*----------------------------------------------------------- + text_buffer_lines::begin() + Returns an iterator that points just past the last line. +-------------------------------------------------------------*/ + +text_buffer_lines::iterator text_buffer_lines::end() const +{ + return text_buffer_line_iterator(m_buffer, m_buffer.lineend); } diff --git a/src/emu/debug/textbuf.h b/src/emu/debug/textbuf.h index 9d42c3032ce..2ec6afca981 100644 --- a/src/emu/debug/textbuf.h +++ b/src/emu/debug/textbuf.h @@ -11,46 +11,87 @@ #ifndef MAME_EMU_DEBUG_TEXTBUF_H #define MAME_EMU_DEBUG_TEXTBUF_H +#include <memory> +#include <string_view> + #include "emucore.h" + /*************************************************************************** TYPE DEFINITIONS ***************************************************************************/ struct text_buffer; - +// helper class for iterating over the lines of a text_buffer +class text_buffer_lines +{ +private: + const text_buffer &m_buffer; + +public: + text_buffer_lines(const text_buffer& buffer) : m_buffer(buffer) { } + + class text_buffer_line_iterator + { + const text_buffer &m_buffer; + s32 m_lineptr; + public: + text_buffer_line_iterator(const text_buffer &buffer, s32 lineptr) : + m_buffer(buffer), + m_lineptr(lineptr) + { + } + + // technically this isn't a valid forward iterator, because operator * doesn't return a reference + std::string_view operator*() const; + text_buffer_line_iterator &operator++(); + + bool operator!=(const text_buffer_line_iterator& rhs) + { + return m_lineptr != rhs.m_lineptr; + } + // according to C++ spec, only != is needed; == is present for completeness. + bool operator==(const text_buffer_line_iterator& rhs) { return !operator!=(rhs); } + }; + + typedef text_buffer_line_iterator iterator; + typedef text_buffer_line_iterator const iterator_const; + + iterator begin() const; + iterator end() const; +}; /*************************************************************************** FUNCTION PROTOTYPES ***************************************************************************/ -/* allocate a new text buffer */ -text_buffer *text_buffer_alloc(u32 bytes, u32 lines); - -/* free a text buffer */ -void text_buffer_free(text_buffer *text); +// free a text buffer +struct text_buffer_deleter { void operator()(text_buffer *text) const; }; +using text_buffer_ptr = std::unique_ptr<text_buffer, text_buffer_deleter>; -/* clear a text buffer */ -void text_buffer_clear(text_buffer *text); +// allocate a new text buffer +text_buffer_ptr text_buffer_alloc(u32 bytes, u32 lines); -/* "print" data to a text buffer */ -void text_buffer_print(text_buffer *text, const char *data); +// clear a text buffer +void text_buffer_clear(text_buffer &text); -/* "print" data to a text buffer with word wrapping to a given column */ -void text_buffer_print_wrap(text_buffer *text, const char *data, int wrapcol); +// "print" data to a text buffer +void text_buffer_print(text_buffer &text, std::string_view data); -/* get the maximum width of lines seen so far */ -u32 text_buffer_max_width(text_buffer *text); +// "print" data to a text buffer with word wrapping to a given column +void text_buffer_print_wrap(text_buffer &text, std::string_view data, int wrapcol); -/* get the current number of lines in the buffer */ -u32 text_buffer_num_lines(text_buffer *text); +// get the maximum width of lines seen so far +u32 text_buffer_max_width(const text_buffer &text); -/* get an absolute sequence number for a given line */ -u32 text_buffer_line_index_to_seqnum(text_buffer *text, u32 index); +// get the current number of lines in the buffer +u32 text_buffer_num_lines(const text_buffer &text); -/* get a sequenced line from the text buffer */ -const char *text_buffer_get_seqnum_line(text_buffer *text, u32 seqnum); +// get an absolute sequence number for a given line +u32 text_buffer_line_index_to_seqnum(const text_buffer &text, u32 index); +// get a sequenced line from the text buffer +const char *text_buffer_get_seqnum_line(const text_buffer &text, u32 seqnum); -#endif /* MAME_EMU_DEBUG_TEXTBUF_H */ +#endif // MAME_EMU_DEBUG_TEXTBUF_H diff --git a/src/emu/debugger.cpp b/src/emu/debugger.cpp index b7ddea04137..6be6a75ab48 100644 --- a/src/emu/debugger.cpp +++ b/src/emu/debugger.cpp @@ -2,9 +2,10 @@ // copyright-holders:Nicola Salmoria, Aaron Giles /********************************************************************* - debugger.c + debugger.cpp Front-end debugger interfaces. + *********************************************************************/ #include "emu.h" @@ -14,7 +15,7 @@ #include "debug/debugcmd.h" #include "debug/debugcon.h" #include "debug/debugvw.h" -#include <ctype.h> +#include <cctype> /*************************************************************************** GLOBAL VARIABLES @@ -31,7 +32,7 @@ static bool g_atexit_registered = false; void debugger_manager::debug_break() { - m_cpu->get_visible_cpu()->debug()->halt_on_next_instruction("Internal breakpoint\n"); + m_console->get_visible_cpu()->debug()->halt_on_next_instruction("Internal breakpoint\n"); } diff --git a/src/emu/debugger.h b/src/emu/debugger.h index 95658eb8250..ce712dc2a9b 100644 --- a/src/emu/debugger.h +++ b/src/emu/debugger.h @@ -5,6 +5,7 @@ debugger.h General debugging interfaces + ****************************************************************************/ #ifndef MAME_EMU_DEBUGGER_H diff --git a/src/emu/devcb.cpp b/src/emu/devcb.cpp index 3fb8056c787..b6e08f13b45 100644 --- a/src/emu/devcb.cpp +++ b/src/emu/devcb.cpp @@ -18,10 +18,6 @@ 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<read8s_delegate>; template class devcb_read8::delegate_builder<read16s_delegate>; template class devcb_read8::delegate_builder<read32s_delegate>; @@ -36,10 +32,6 @@ template class devcb_read8::delegate_builder<read32smo_delegate>; template class devcb_read8::delegate_builder<read64smo_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<read8s_delegate>; template class devcb_read16::delegate_builder<read16s_delegate>; template class devcb_read16::delegate_builder<read32s_delegate>; @@ -54,10 +46,6 @@ template class devcb_read16::delegate_builder<read32smo_delegate>; template class devcb_read16::delegate_builder<read64smo_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<read8s_delegate>; template class devcb_read32::delegate_builder<read16s_delegate>; template class devcb_read32::delegate_builder<read32s_delegate>; @@ -72,10 +60,6 @@ template class devcb_read32::delegate_builder<read32smo_delegate>; template class devcb_read32::delegate_builder<read64smo_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<read8s_delegate>; template class devcb_read64::delegate_builder<read16s_delegate>; template class devcb_read64::delegate_builder<read32s_delegate>; @@ -90,10 +74,6 @@ template class devcb_read64::delegate_builder<read32smo_delegate>; template class devcb_read64::delegate_builder<read64smo_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<read8s_delegate>; template class devcb_read_line::delegate_builder<read16s_delegate>; template class devcb_read_line::delegate_builder<read32s_delegate>; @@ -108,10 +88,6 @@ template class devcb_read_line::delegate_builder<read32smo_delegate>; template class devcb_read_line::delegate_builder<read64smo_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<read8s_delegate> >; template class devcb_read8::creator_impl<devcb_read8::delegate_builder<read16s_delegate> >; template class devcb_read8::creator_impl<devcb_read8::delegate_builder<read32s_delegate> >; @@ -127,10 +103,6 @@ template class devcb_read8::creator_impl<devcb_read8::delegate_builder<read64smo 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<read8s_delegate> >; template class devcb_read16::creator_impl<devcb_read16::delegate_builder<read16s_delegate> >; template class devcb_read16::creator_impl<devcb_read16::delegate_builder<read32s_delegate> >; @@ -146,10 +118,6 @@ template class devcb_read16::creator_impl<devcb_read16::delegate_builder<read64s 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<read8s_delegate> >; template class devcb_read32::creator_impl<devcb_read32::delegate_builder<read16s_delegate> >; template class devcb_read32::creator_impl<devcb_read32::delegate_builder<read32s_delegate> >; @@ -165,10 +133,6 @@ template class devcb_read32::creator_impl<devcb_read32::delegate_builder<read64s 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<read8s_delegate> >; template class devcb_read64::creator_impl<devcb_read64::delegate_builder<read16s_delegate> >; template class devcb_read64::creator_impl<devcb_read64::delegate_builder<read32s_delegate> >; @@ -184,10 +148,6 @@ template class devcb_read64::creator_impl<devcb_read64::delegate_builder<read64s 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<read8s_delegate> >; template class devcb_read_line::creator_impl<devcb_read_line::delegate_builder<read16s_delegate> >; template class devcb_read_line::creator_impl<devcb_read_line::delegate_builder<read32s_delegate> >; @@ -209,10 +169,6 @@ 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<write8s_delegate>; template class devcb_write8::delegate_builder<write16s_delegate>; template class devcb_write8::delegate_builder<write32s_delegate>; @@ -227,10 +183,6 @@ template class devcb_write8::delegate_builder<write32smo_delegate>; template class devcb_write8::delegate_builder<write64smo_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<write8s_delegate>; template class devcb_write16::delegate_builder<write16s_delegate>; template class devcb_write16::delegate_builder<write32s_delegate>; @@ -245,10 +197,6 @@ template class devcb_write16::delegate_builder<write32smo_delegate>; template class devcb_write16::delegate_builder<write64smo_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<write8s_delegate>; template class devcb_write32::delegate_builder<write16s_delegate>; template class devcb_write32::delegate_builder<write32s_delegate>; @@ -263,10 +211,6 @@ template class devcb_write32::delegate_builder<write32smo_delegate>; template class devcb_write32::delegate_builder<write64smo_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<write8s_delegate>; template class devcb_write64::delegate_builder<write16s_delegate>; template class devcb_write64::delegate_builder<write32s_delegate>; @@ -281,10 +225,6 @@ template class devcb_write64::delegate_builder<write32smo_delegate>; template class devcb_write64::delegate_builder<write64smo_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<write8s_delegate>; template class devcb_write_line::delegate_builder<write16s_delegate>; template class devcb_write_line::delegate_builder<write32s_delegate>; @@ -299,10 +239,6 @@ template class devcb_write_line::delegate_builder<write32smo_delegate>; template class devcb_write_line::delegate_builder<write64smo_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<write8s_delegate> >; template class devcb_write8::creator_impl<devcb_write8::delegate_builder<write16s_delegate> >; template class devcb_write8::creator_impl<devcb_write8::delegate_builder<write32s_delegate> >; @@ -321,12 +257,7 @@ template class devcb_write8::creator_impl<devcb_write8::latched_inputline_builde 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<write8s_delegate> >; template class devcb_write16::creator_impl<devcb_write16::delegate_builder<write16s_delegate> >; template class devcb_write16::creator_impl<devcb_write16::delegate_builder<write32s_delegate> >; @@ -345,12 +276,7 @@ template class devcb_write16::creator_impl<devcb_write16::latched_inputline_buil 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<write8s_delegate> >; template class devcb_write32::creator_impl<devcb_write32::delegate_builder<write16s_delegate> >; template class devcb_write32::creator_impl<devcb_write32::delegate_builder<write32s_delegate> >; @@ -369,12 +295,7 @@ template class devcb_write32::creator_impl<devcb_write32::latched_inputline_buil 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<write8s_delegate> >; template class devcb_write64::creator_impl<devcb_write64::delegate_builder<write16s_delegate> >; template class devcb_write64::creator_impl<devcb_write64::delegate_builder<write32s_delegate> >; @@ -393,12 +314,7 @@ template class devcb_write64::creator_impl<devcb_write64::latched_inputline_buil 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<write8s_delegate> >; template class devcb_write_line::creator_impl<devcb_write_line::delegate_builder<write16s_delegate> >; template class devcb_write_line::creator_impl<devcb_write_line::delegate_builder<write32s_delegate> >; @@ -417,16 +333,12 @@ template class devcb_write_line::creator_impl<devcb_write_line::latched_inputlin 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) + : device_resolver_base(owner) + , m_owner(owner) { - owner.register_callback(*this); } @@ -434,31 +346,6 @@ devcb_base::~devcb_base() { } - -void devcb_base::validity_check(validity_checker &valid) const -{ - // if a default address space is configured, ensure it exists - if (m_dflt_space_finder) - { - 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()); - } - else if (m_dflt_space_finder.finder_tag() != finder_base::DUMMY_TAG) - { - osd_printf_error("Callback references non-existent memory space device tag %s\n", m_dflt_space_finder.finder_tag()); - } -} - - -void devcb_base::resolve() -{ - 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_dflt_space = &m_owner.machine().dummy_space(); -} - - devcb_read_base::~devcb_read_base() { } diff --git a/src/emu/devcb.h b/src/emu/devcb.h index 6b16c37b33a..d2ee406aa60 100644 --- a/src/emu/devcb.h +++ b/src/emu/devcb.h @@ -7,11 +7,13 @@ Device callback interface helpers. ***************************************************************************/ + #ifndef MAME_EMU_DEVCB_H #define MAME_EMU_DEVCB_H #pragma once +#include <array> #include <cassert> #include <functional> #include <memory> @@ -28,12 +30,16 @@ //************************************************************************** #if defined(__GNUC__) && !defined(__clang__) -#if __GNUC__ >= 8 +#if (__GNUC__ >= 8) #define MAME_DEVCB_GNUC_BROKEN_FRIEND 1 -#endif // __GNUC__ >= 8 -#endif // defined(__GNUC__) && !defined(__clang__) - +#endif // (__GNUC__ >= 8) && !defined(__clang__) +#endif // defined(__GNUC__) +#if defined(__clang__) +#if (__clang_major__ == 8) +#define MAME_DEVCB_GNUC_BROKEN_FRIEND 1 +#endif // (__clang_major__ == 8) +#endif // defined(__clang__) //************************************************************************** // DELEGATE TYPES @@ -42,20 +48,12 @@ typedef device_delegate<int ()> read_line_delegate; typedef device_delegate<void (int)> write_line_delegate; -namespace emu { namespace detail { - -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> -inline read_line_delegate make_delegate(T &&func, char const *name, char const *tag, rw_device_class_t<read_line_delegate, std::remove_reference_t<T> > *obj) -{ return read_line_delegate(func, name, tag, obj); } +namespace emu::detail { -template <typename T> -inline write_line_delegate make_delegate(T &&func, char const *name, char const *tag, rw_device_class_t<write_line_delegate, std::remove_reference_t<T> > *obj) -{ return write_line_delegate(func, name, tag, obj); } +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<read_line_delegate, std::remove_reference_t<T> > > > { using type = read_line_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<write_line_delegate, std::remove_reference_t<T> > > > { using type = write_line_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; -} } // namespace emu::detail +} // namespace emu::detail //************************************************************************** @@ -66,17 +64,8 @@ inline write_line_delegate make_delegate(T &&func, char const *name, char const /// /// Provides utilities for supporting multiple read/write/transform /// signatures, and the base exclusive-or/mask transform methods. -class devcb_base +class devcb_base : protected device_resolver_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; - } - - virtual void validity_check(validity_checker &valid) const; - protected: // This is in C++17 but not C++14 template <typename... T> struct void_wrapper { using type = void; }; @@ -90,82 +79,37 @@ protected: 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; }; + template <typename Input, typename Result, typename Func> using is_transform_form3 = std::is_invocable_r<Result, Func, offs_t &, Input, std::make_unsigned_t<Input> &>; + template <typename Input, typename Result, typename Func> using is_transform_form4 = std::is_invocable_r<Result, Func, offs_t &, Input>; + template <typename Input, typename Result, typename Func> using is_transform_form6 = std::is_invocable_r<Result, Func, Input>; + template <typename Input, typename Result, typename Func> using is_transform = std::bool_constant<is_transform_form3<Input, Result, Func>::value || is_transform_form4<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> struct transform_result<Input, Result, Func, std::enable_if_t<is_transform_form3<Input, Result, Func>::value> > { using type = std::invoke_result_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::invoke_result_t<Func, offs_t &, 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::invoke_result_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::rw_device_class_t<read8_delegate, std::remove_reference_t<T> > > > { using type = read8_delegate; using device_class = emu::detail::rw_device_class_t<type, std::remove_reference_t<T> >; }; - template <typename T> struct delegate_type<T, void_t<emu::detail::rw_device_class_t<read16_delegate, std::remove_reference_t<T> > > > { using type = read16_delegate; using device_class = emu::detail::rw_device_class_t<type, std::remove_reference_t<T> >; }; - template <typename T> struct delegate_type<T, void_t<emu::detail::rw_device_class_t<read32_delegate, std::remove_reference_t<T> > > > { using type = read32_delegate; using device_class = emu::detail::rw_device_class_t<type, std::remove_reference_t<T> >; }; - template <typename T> struct delegate_type<T, void_t<emu::detail::rw_device_class_t<read64_delegate, std::remove_reference_t<T> > > > { using type = read64_delegate; using device_class = emu::detail::rw_device_class_t<type, std::remove_reference_t<T> >; }; - template <typename T> struct delegate_type<T, void_t<emu::detail::rw_device_class_t<read8s_delegate, std::remove_reference_t<T> > > > { using type = read8s_delegate; using device_class = emu::detail::rw_device_class_t<type, std::remove_reference_t<T> >; }; - template <typename T> struct delegate_type<T, void_t<emu::detail::rw_device_class_t<read16s_delegate, std::remove_reference_t<T> > > > { using type = read16s_delegate; using device_class = emu::detail::rw_device_class_t<type, std::remove_reference_t<T> >; }; - template <typename T> struct delegate_type<T, void_t<emu::detail::rw_device_class_t<read32s_delegate, std::remove_reference_t<T> > > > { using type = read32s_delegate; using device_class = emu::detail::rw_device_class_t<type, std::remove_reference_t<T> >; }; - template <typename T> struct delegate_type<T, void_t<emu::detail::rw_device_class_t<read64s_delegate, std::remove_reference_t<T> > > > { using type = read64s_delegate; using device_class = emu::detail::rw_device_class_t<type, std::remove_reference_t<T> >; }; - template <typename T> struct delegate_type<T, void_t<emu::detail::rw_device_class_t<read8sm_delegate, std::remove_reference_t<T> > > > { using type = read8sm_delegate; using device_class = emu::detail::rw_device_class_t<type, std::remove_reference_t<T> >; }; - template <typename T> struct delegate_type<T, void_t<emu::detail::rw_device_class_t<read16sm_delegate, std::remove_reference_t<T> > > > { using type = read16sm_delegate; using device_class = emu::detail::rw_device_class_t<type, std::remove_reference_t<T> >; }; - template <typename T> struct delegate_type<T, void_t<emu::detail::rw_device_class_t<read32sm_delegate, std::remove_reference_t<T> > > > { using type = read32sm_delegate; using device_class = emu::detail::rw_device_class_t<type, std::remove_reference_t<T> >; }; - template <typename T> struct delegate_type<T, void_t<emu::detail::rw_device_class_t<read64sm_delegate, std::remove_reference_t<T> > > > { using type = read64sm_delegate; using device_class = emu::detail::rw_device_class_t<type, std::remove_reference_t<T> >; }; - template <typename T> struct delegate_type<T, void_t<emu::detail::rw_device_class_t<read8smo_delegate, std::remove_reference_t<T> > > > { using type = read8smo_delegate; using device_class = emu::detail::rw_device_class_t<type, std::remove_reference_t<T> >; }; - template <typename T> struct delegate_type<T, void_t<emu::detail::rw_device_class_t<read16smo_delegate, std::remove_reference_t<T> > > > { using type = read16smo_delegate; using device_class = emu::detail::rw_device_class_t<type, std::remove_reference_t<T> >; }; - template <typename T> struct delegate_type<T, void_t<emu::detail::rw_device_class_t<read32smo_delegate, std::remove_reference_t<T> > > > { using type = read32smo_delegate; using device_class = emu::detail::rw_device_class_t<type, std::remove_reference_t<T> >; }; - template <typename T> struct delegate_type<T, void_t<emu::detail::rw_device_class_t<read64smo_delegate, std::remove_reference_t<T> > > > { using type = read64smo_delegate; using device_class = emu::detail::rw_device_class_t<type, std::remove_reference_t<T> >; }; - template <typename T> struct delegate_type<T, void_t<emu::detail::rw_device_class_t<read_line_delegate, std::remove_reference_t<T> > > > { using type = read_line_delegate; using device_class = emu::detail::rw_device_class_t<type, std::remove_reference_t<T> >; }; - template <typename T> struct delegate_type<T, void_t<emu::detail::rw_device_class_t<write8_delegate, std::remove_reference_t<T> > > > { using type = write8_delegate; using device_class = emu::detail::rw_device_class_t<type, std::remove_reference_t<T> >; }; - template <typename T> struct delegate_type<T, void_t<emu::detail::rw_device_class_t<write16_delegate, std::remove_reference_t<T> > > > { using type = write16_delegate; using device_class = emu::detail::rw_device_class_t<type, std::remove_reference_t<T> >; }; - template <typename T> struct delegate_type<T, void_t<emu::detail::rw_device_class_t<write32_delegate, std::remove_reference_t<T> > > > { using type = write32_delegate; using device_class = emu::detail::rw_device_class_t<type, std::remove_reference_t<T> >; }; - template <typename T> struct delegate_type<T, void_t<emu::detail::rw_device_class_t<write64_delegate, std::remove_reference_t<T> > > > { using type = write64_delegate; using device_class = emu::detail::rw_device_class_t<type, std::remove_reference_t<T> >; }; - template <typename T> struct delegate_type<T, void_t<emu::detail::rw_device_class_t<write8s_delegate, std::remove_reference_t<T> > > > { using type = write8s_delegate; using device_class = emu::detail::rw_device_class_t<type, std::remove_reference_t<T> >; }; - template <typename T> struct delegate_type<T, void_t<emu::detail::rw_device_class_t<write16s_delegate, std::remove_reference_t<T> > > > { using type = write16s_delegate; using device_class = emu::detail::rw_device_class_t<type, std::remove_reference_t<T> >; }; - template <typename T> struct delegate_type<T, void_t<emu::detail::rw_device_class_t<write32s_delegate, std::remove_reference_t<T> > > > { using type = write32s_delegate; using device_class = emu::detail::rw_device_class_t<type, std::remove_reference_t<T> >; }; - template <typename T> struct delegate_type<T, void_t<emu::detail::rw_device_class_t<write64s_delegate, std::remove_reference_t<T> > > > { using type = write64s_delegate; using device_class = emu::detail::rw_device_class_t<type, std::remove_reference_t<T> >; }; - template <typename T> struct delegate_type<T, void_t<emu::detail::rw_device_class_t<write8sm_delegate, std::remove_reference_t<T> > > > { using type = write8sm_delegate; using device_class = emu::detail::rw_device_class_t<type, std::remove_reference_t<T> >; }; - template <typename T> struct delegate_type<T, void_t<emu::detail::rw_device_class_t<write16sm_delegate, std::remove_reference_t<T> > > > { using type = write16sm_delegate; using device_class = emu::detail::rw_device_class_t<type, std::remove_reference_t<T> >; }; - template <typename T> struct delegate_type<T, void_t<emu::detail::rw_device_class_t<write32sm_delegate, std::remove_reference_t<T> > > > { using type = write32sm_delegate; using device_class = emu::detail::rw_device_class_t<type, std::remove_reference_t<T> >; }; - template <typename T> struct delegate_type<T, void_t<emu::detail::rw_device_class_t<write64sm_delegate, std::remove_reference_t<T> > > > { using type = write64sm_delegate; using device_class = emu::detail::rw_device_class_t<type, std::remove_reference_t<T> >; }; - template <typename T> struct delegate_type<T, void_t<emu::detail::rw_device_class_t<write8smo_delegate, std::remove_reference_t<T> > > > { using type = write8smo_delegate; using device_class = emu::detail::rw_device_class_t<type, std::remove_reference_t<T> >; }; - template <typename T> struct delegate_type<T, void_t<emu::detail::rw_device_class_t<write16smo_delegate, std::remove_reference_t<T> > > > { using type = write16smo_delegate; using device_class = emu::detail::rw_device_class_t<type, std::remove_reference_t<T> >; }; - template <typename T> struct delegate_type<T, void_t<emu::detail::rw_device_class_t<write32smo_delegate, std::remove_reference_t<T> > > > { using type = write32smo_delegate; using device_class = emu::detail::rw_device_class_t<type, std::remove_reference_t<T> >; }; - template <typename T> struct delegate_type<T, void_t<emu::detail::rw_device_class_t<write64smo_delegate, std::remove_reference_t<T> > > > { using type = write64smo_delegate; using device_class = emu::detail::rw_device_class_t<type, std::remove_reference_t<T> >; }; - template <typename T> struct delegate_type<T, void_t<emu::detail::rw_device_class_t<write_line_delegate, std::remove_reference_t<T> > > > { using type = write_line_delegate; using device_class = emu::detail::rw_device_class_t<type, 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; + template <typename T> using delegate_type_t = emu::detail::rw_delegate_type_t<T>; + template <typename T> using delegate_device_class_t = emu::detail::rw_delegate_device_class_t<T>; // 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)); } + 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, 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, 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_form6<Input, Result, T>::value, mask_t<transform_result_t<Input, Result, T>, Result> > invoke_transform(T const &cb, 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); } + template <typename T, typename U> static T &cast_reference(U &obj) + { + if constexpr (std::is_convertible_v<std::add_pointer_t<U>, std::add_pointer_t<T> >) + return downcast<T &>(obj); + else + return dynamic_cast<T &>(obj); + } /// \brief Base transform helper /// @@ -184,12 +128,18 @@ protected: 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 inherited_mask() ? std::move(trans) : std::move(trans.mask(m_mask >> val)); + if (inherited_mask()) + return trans; + else + 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 inherited_mask() ? std::move(trans) : std::move(trans.mask(m_mask << val)); + if (inherited_mask()) + return trans; + else + return std::move(trans.mask(m_mask << val)); } auto bit(unsigned val) { return std::move(rshift(val).mask(T(1U))); } @@ -214,20 +164,38 @@ protected: bool m_inherited_mask = true; }; + /// \brief Callback array helper + /// + /// Simplifies construction and resolution of arrays of callbacks. + template <typename T, unsigned Count> + class array : public std::array<T, Count> + { + private: + template <unsigned... V, typename... Params> + array(device_t &owner, std::integer_sequence<unsigned, V...>, Params &&... args) + : std::array<T, Count>{{ { make_one<V>(owner), std::forward<Params>(args)... }... }} + { + } + + template <unsigned N> device_t &make_one(device_t &owner) { return owner; } + + public: + using std::array<T, Count>::array; + + template <typename... Params> + array(device_t &owner, Params &&... args) + : array(owner, std::make_integer_sequence<unsigned, Count>(), std::forward<Params>(args)...) + { + } + }; + devcb_base(device_t &owner); ~devcb_base(); device_t &owner() const { return m_owner; } - void resolve(); - - address_space &default_space() { assert(m_dflt_space); return *m_dflt_space; } - private: device_t &m_owner; - optional_device<device_memory_interface> m_dflt_space_finder; - int m_dflt_space_index; - address_space *m_dflt_space; }; @@ -238,64 +206,41 @@ class devcb_read_base : public devcb_base { protected: // 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; }; + template <typename Result, typename Func> using is_read_form1 = std::is_invocable_r<Result, Func, offs_t, Result>; + template <typename Result, typename Func> using is_read_form2 = std::is_invocable_r<Result, Func, offs_t>; + template <typename Result, typename Func> using is_read_form3 = std::is_invocable_r<Result, Func>; + template <typename Result, typename Func> using is_read = std::bool_constant<is_read_form1<Result, Func>::value || is_read_form2<Result, Func>::value || is_read_form3<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> struct read_result<Result, Func, std::enable_if_t<is_read_form1<Result, Func>::value> > { using type = std::invoke_result_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_form2<Result, Func>::value> > { using type = std::invoke_result_t<Func, 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::invoke_result_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::rw_device_class_t<read8_delegate, std::remove_reference_t<T> > > > { static constexpr bool value = true; }; - template <typename T> struct is_read_method<T, void_t<emu::detail::rw_device_class_t<read16_delegate, std::remove_reference_t<T> > > > { static constexpr bool value = true; }; - template <typename T> struct is_read_method<T, void_t<emu::detail::rw_device_class_t<read32_delegate, std::remove_reference_t<T> > > > { static constexpr bool value = true; }; - template <typename T> struct is_read_method<T, void_t<emu::detail::rw_device_class_t<read64_delegate, std::remove_reference_t<T> > > > { static constexpr bool value = true; }; - template <typename T> struct is_read_method<T, void_t<emu::detail::rw_device_class_t<read8s_delegate, std::remove_reference_t<T> > > > { static constexpr bool value = true; }; - template <typename T> struct is_read_method<T, void_t<emu::detail::rw_device_class_t<read16s_delegate, std::remove_reference_t<T> > > > { static constexpr bool value = true; }; - template <typename T> struct is_read_method<T, void_t<emu::detail::rw_device_class_t<read32s_delegate, std::remove_reference_t<T> > > > { static constexpr bool value = true; }; - template <typename T> struct is_read_method<T, void_t<emu::detail::rw_device_class_t<read64s_delegate, std::remove_reference_t<T> > > > { static constexpr bool value = true; }; - template <typename T> struct is_read_method<T, void_t<emu::detail::rw_device_class_t<read8sm_delegate, std::remove_reference_t<T> > > > { static constexpr bool value = true; }; - template <typename T> struct is_read_method<T, void_t<emu::detail::rw_device_class_t<read16sm_delegate, std::remove_reference_t<T> > > > { static constexpr bool value = true; }; - template <typename T> struct is_read_method<T, void_t<emu::detail::rw_device_class_t<read32sm_delegate, std::remove_reference_t<T> > > > { static constexpr bool value = true; }; - template <typename T> struct is_read_method<T, void_t<emu::detail::rw_device_class_t<read64sm_delegate, std::remove_reference_t<T> > > > { static constexpr bool value = true; }; - template <typename T> struct is_read_method<T, void_t<emu::detail::rw_device_class_t<read8smo_delegate, std::remove_reference_t<T> > > > { static constexpr bool value = true; }; - template <typename T> struct is_read_method<T, void_t<emu::detail::rw_device_class_t<read16smo_delegate, std::remove_reference_t<T> > > > { static constexpr bool value = true; }; - template <typename T> struct is_read_method<T, void_t<emu::detail::rw_device_class_t<read32smo_delegate, std::remove_reference_t<T> > > > { static constexpr bool value = true; }; - template <typename T> struct is_read_method<T, void_t<emu::detail::rw_device_class_t<read64smo_delegate, std::remove_reference_t<T> > > > { static constexpr bool value = true; }; - template <typename T> struct is_read_method<T, void_t<emu::detail::rw_device_class_t<read_line_delegate, std::remove_reference_t<T> > > > { static constexpr bool value = true; }; + template <typename T, typename Enable = void> struct is_read_method : public std::false_type { }; + template <typename T> struct is_read_method<T, void_t<emu::detail::rw_device_class_t<read8s_delegate, std::remove_reference_t<T> > > > : public std::true_type { }; + template <typename T> struct is_read_method<T, void_t<emu::detail::rw_device_class_t<read16s_delegate, std::remove_reference_t<T> > > > : public std::true_type { }; + template <typename T> struct is_read_method<T, void_t<emu::detail::rw_device_class_t<read32s_delegate, std::remove_reference_t<T> > > > : public std::true_type { }; + template <typename T> struct is_read_method<T, void_t<emu::detail::rw_device_class_t<read64s_delegate, std::remove_reference_t<T> > > > : public std::true_type { }; + template <typename T> struct is_read_method<T, void_t<emu::detail::rw_device_class_t<read8sm_delegate, std::remove_reference_t<T> > > > : public std::true_type { }; + template <typename T> struct is_read_method<T, void_t<emu::detail::rw_device_class_t<read16sm_delegate, std::remove_reference_t<T> > > > : public std::true_type { }; + template <typename T> struct is_read_method<T, void_t<emu::detail::rw_device_class_t<read32sm_delegate, std::remove_reference_t<T> > > > : public std::true_type { }; + template <typename T> struct is_read_method<T, void_t<emu::detail::rw_device_class_t<read64sm_delegate, std::remove_reference_t<T> > > > : public std::true_type { }; + template <typename T> struct is_read_method<T, void_t<emu::detail::rw_device_class_t<read8smo_delegate, std::remove_reference_t<T> > > > : public std::true_type { }; + template <typename T> struct is_read_method<T, void_t<emu::detail::rw_device_class_t<read16smo_delegate, std::remove_reference_t<T> > > > : public std::true_type { }; + template <typename T> struct is_read_method<T, void_t<emu::detail::rw_device_class_t<read32smo_delegate, std::remove_reference_t<T> > > > : public std::true_type { }; + template <typename T> struct is_read_method<T, void_t<emu::detail::rw_device_class_t<read64smo_delegate, std::remove_reference_t<T> > > > : public std::true_type { }; + template <typename T> struct is_read_method<T, void_t<emu::detail::rw_device_class_t<read_line_delegate, std::remove_reference_t<T> > > > : public std::true_type { }; // 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()); } + 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, 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_form2<Result, T>::value, mask_t<read_result_t<Result, T>, Result> > invoke_read(T const &cb, 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_form3<Result, T>::value, mask_t<read_result_t<Result, T>, Result> > invoke_read(T const &cb, 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<read8s_delegate, Dummy> { static constexpr u8 default_mask = ~u8(0); }; template <typename Dummy> struct delegate_traits<read16s_delegate, Dummy> { static constexpr u16 default_mask = ~u16(0); }; template <typename Dummy> struct delegate_traits<read32s_delegate, Dummy> { static constexpr u32 default_mask = ~u32(0); }; @@ -322,54 +267,34 @@ 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; }; + template <typename Input, typename Func> using is_write_form1 = std::is_invocable<Func, offs_t, Input, std::make_unsigned_t<Input> >; + template <typename Input, typename Func> using is_write_form2 = std::is_invocable<Func, offs_t, Input>; + template <typename Input, typename Func> using is_write_form3 = std::is_invocable<Func, Input>; + template <typename Input, typename Func> using is_write = std::bool_constant<is_write_form1<Input, Func>::value || is_write_form2<Input, Func>::value || is_write_form3<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::rw_device_class_t<write8_delegate, std::remove_reference_t<T> > > > { static constexpr bool value = true; }; - template <typename T> struct is_write_method<T, void_t<emu::detail::rw_device_class_t<write16_delegate, std::remove_reference_t<T> > > > { static constexpr bool value = true; }; - template <typename T> struct is_write_method<T, void_t<emu::detail::rw_device_class_t<write32_delegate, std::remove_reference_t<T> > > > { static constexpr bool value = true; }; - template <typename T> struct is_write_method<T, void_t<emu::detail::rw_device_class_t<write64_delegate, std::remove_reference_t<T> > > > { static constexpr bool value = true; }; - template <typename T> struct is_write_method<T, void_t<emu::detail::rw_device_class_t<write8s_delegate, std::remove_reference_t<T> > > > { static constexpr bool value = true; }; - template <typename T> struct is_write_method<T, void_t<emu::detail::rw_device_class_t<write16s_delegate, std::remove_reference_t<T> > > > { static constexpr bool value = true; }; - template <typename T> struct is_write_method<T, void_t<emu::detail::rw_device_class_t<write32s_delegate, std::remove_reference_t<T> > > > { static constexpr bool value = true; }; - template <typename T> struct is_write_method<T, void_t<emu::detail::rw_device_class_t<write64s_delegate, std::remove_reference_t<T> > > > { static constexpr bool value = true; }; - template <typename T> struct is_write_method<T, void_t<emu::detail::rw_device_class_t<write8sm_delegate, std::remove_reference_t<T> > > > { static constexpr bool value = true; }; - template <typename T> struct is_write_method<T, void_t<emu::detail::rw_device_class_t<write16sm_delegate, std::remove_reference_t<T> > > > { static constexpr bool value = true; }; - template <typename T> struct is_write_method<T, void_t<emu::detail::rw_device_class_t<write32sm_delegate, std::remove_reference_t<T> > > > { static constexpr bool value = true; }; - template <typename T> struct is_write_method<T, void_t<emu::detail::rw_device_class_t<write64sm_delegate, std::remove_reference_t<T> > > > { static constexpr bool value = true; }; - template <typename T> struct is_write_method<T, void_t<emu::detail::rw_device_class_t<write8smo_delegate, std::remove_reference_t<T> > > > { static constexpr bool value = true; }; - template <typename T> struct is_write_method<T, void_t<emu::detail::rw_device_class_t<write16smo_delegate, std::remove_reference_t<T> > > > { static constexpr bool value = true; }; - template <typename T> struct is_write_method<T, void_t<emu::detail::rw_device_class_t<write32smo_delegate, std::remove_reference_t<T> > > > { static constexpr bool value = true; }; - template <typename T> struct is_write_method<T, void_t<emu::detail::rw_device_class_t<write64smo_delegate, std::remove_reference_t<T> > > > { static constexpr bool value = true; }; - template <typename T> struct is_write_method<T, void_t<emu::detail::rw_device_class_t<write_line_delegate, std::remove_reference_t<T> > > > { static constexpr bool value = true; }; + template <typename T, typename Enable = void> struct is_write_method : public std::false_type { }; + template <typename T> struct is_write_method<T, void_t<emu::detail::rw_device_class_t<write8s_delegate, std::remove_reference_t<T> > > > : public std::true_type { }; + template <typename T> struct is_write_method<T, void_t<emu::detail::rw_device_class_t<write16s_delegate, std::remove_reference_t<T> > > > : public std::true_type { }; + template <typename T> struct is_write_method<T, void_t<emu::detail::rw_device_class_t<write32s_delegate, std::remove_reference_t<T> > > > : public std::true_type { }; + template <typename T> struct is_write_method<T, void_t<emu::detail::rw_device_class_t<write64s_delegate, std::remove_reference_t<T> > > > : public std::true_type { }; + template <typename T> struct is_write_method<T, void_t<emu::detail::rw_device_class_t<write8sm_delegate, std::remove_reference_t<T> > > > : public std::true_type { }; + template <typename T> struct is_write_method<T, void_t<emu::detail::rw_device_class_t<write16sm_delegate, std::remove_reference_t<T> > > > : public std::true_type { }; + template <typename T> struct is_write_method<T, void_t<emu::detail::rw_device_class_t<write32sm_delegate, std::remove_reference_t<T> > > > : public std::true_type { }; + template <typename T> struct is_write_method<T, void_t<emu::detail::rw_device_class_t<write64sm_delegate, std::remove_reference_t<T> > > > : public std::true_type { }; + template <typename T> struct is_write_method<T, void_t<emu::detail::rw_device_class_t<write8smo_delegate, std::remove_reference_t<T> > > > : public std::true_type { }; + template <typename T> struct is_write_method<T, void_t<emu::detail::rw_device_class_t<write16smo_delegate, std::remove_reference_t<T> > > > : public std::true_type { }; + template <typename T> struct is_write_method<T, void_t<emu::detail::rw_device_class_t<write32smo_delegate, std::remove_reference_t<T> > > > : public std::true_type { }; + template <typename T> struct is_write_method<T, void_t<emu::detail::rw_device_class_t<write64smo_delegate, std::remove_reference_t<T> > > > : public std::true_type { }; + template <typename T> struct is_write_method<T, void_t<emu::detail::rw_device_class_t<write_line_delegate, std::remove_reference_t<T> > > > : public std::true_type { }; // 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, offset, data); } - 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); } + template <typename Input, typename T> static std::enable_if_t<is_write_form1<Input, T>::value> invoke_write(T const &cb, 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_form2<Input, T>::value> invoke_write(T const &cb, 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_form3<Input, T>::value> invoke_write(T const &cb, 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<write8s_delegate, Dummy> { using input_t = u8; static constexpr u8 default_mask = ~u8(0); }; template <typename Dummy> struct delegate_traits<write16s_delegate, Dummy> { using input_t = u16; static constexpr u16 default_mask = ~u16(0); }; template <typename Dummy> struct delegate_traits<write32s_delegate, Dummy> { using input_t = u32; static constexpr u32 default_mask = ~u32(0); }; @@ -398,7 +323,7 @@ template <typename Result, std::make_unsigned_t<Result> DefaultMask = std::make_ class devcb_read : public devcb_read_base { private: - using func_t = std::function<Result (address_space &, offs_t, std::make_unsigned_t<Result>)>; + using func_t = std::function<Result (offs_t, std::make_unsigned_t<Result>)>; class creator { @@ -406,7 +331,7 @@ private: using ptr = std::unique_ptr<creator>; virtual ~creator() { } - virtual void validity_check(validity_checker &valid) const = 0; + virtual bool validity_check(validity_checker &valid) const = 0; virtual func_t create() = 0; std::make_unsigned_t<Result> mask() const { return m_mask; } @@ -423,12 +348,12 @@ private: public: creator_impl(T &&builder) : creator(builder.mask()), m_builder(std::move(builder)) { } - virtual void validity_check(validity_checker &valid) const override { m_builder.validity_check(valid); } + virtual bool validity_check(validity_checker &valid) const override { return m_builder.validity_check(valid); } 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); }; }); + m_builder.build([&result] (auto &&f) { result = [cb = std::move(f)] (offs_t offset, typename T::input_mask_t mem_mask) { return cb(offset, mem_mask); }; }); return result; } @@ -436,28 +361,6 @@ private: T m_builder; }; - class log_creator : public creator - { - 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 Source, typename Func> class transform_builder; // workaround for MSVC class builder_base @@ -537,7 +440,7 @@ private: 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); } + bool validity_check(validity_checker &valid) const { return m_src.validity_check(valid); } template <typename T> void build(T &&chain) @@ -558,12 +461,12 @@ private: 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) + [src = std::forward<U>(f), cb = std::move(m_cb), exor = this->exor(), mask = this->mask()] (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)); + auto const data(src(offset, source_mask)); mem_mask = source_mask & mask; - return (devcb_read::invoke_transform<input_t, Result>(cb, space, offset, data, mem_mask) ^ exor) & mask; + return (devcb_read::invoke_transform<input_t, Result>(cb, offset, data, mem_mask) ^ exor) & mask; }); } @@ -605,7 +508,7 @@ private: 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 { } + bool validity_check(validity_checker &valid) const { return true; } template <typename T> void build(T &&chain) @@ -613,8 +516,8 @@ private: 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; }); + [cb = std::move(m_cb), exor = this->exor(), mask = this->mask()] (offs_t offset, input_mask_t mem_mask) + { return (devcb_read::invoke_read<Result>(cb, offset, mem_mask & mask) ^ exor) & mask; }); } private: @@ -638,20 +541,17 @@ private: 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)) + , m_delegate(devbase, tag, std::forward<T>(func), name) { } 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) + , m_delegate(obj, std::forward<T>(func), name) { } 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(); @@ -668,10 +568,15 @@ private: 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 + bool 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()); + auto const target(m_delegate.finder_target()); + if (target.second && !target.first.subdevice(target.second)) + { + osd_printf_error("Read callback bound to non-existent object tag %s (%s)\n", target.first.subtag(target.second), m_delegate.name()); + return false; + } + return true; } template <typename T> @@ -679,10 +584,10 @@ private: { assert(this->m_consumed); this->built(); - m_delegate.bind_relative_to(m_devbase); + m_delegate.resolve(); 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; }); + [cb = std::move(m_delegate), exor = this->exor(), mask = this->mask()] (offs_t offset, input_mask_t mem_mask) + { return (devcb_read::invoke_read<Result>(cb, offset, mem_mask & mask) ^ exor) & mask; }); } private: @@ -690,7 +595,6 @@ private: delegate_builder &operator=(delegate_builder const &) = delete; delegate_builder &operator=(delegate_builder &&that) = delete; - device_t &m_devbase; Delegate m_delegate; }; @@ -728,18 +632,18 @@ private: 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 { } + bool validity_check(validity_checker &valid) const { return true; } template <typename T> void build(T &&chain) { assert(this->m_consumed); this->built(); - ioport_port *const ioport(m_devbase.ioport(m_tag.c_str())); + ioport_port *const ioport(m_devbase.ioport(m_tag)); 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()); + throw emu_fatalerror("Read callback bound to non-existent I/O port %s of device %s (%s)\n", m_tag, 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) + [&port = *ioport, exor = this->exor(), mask = this->mask()] (offs_t offset, input_mask_t mem_mask) { return (port.read() ^ exor) & mask; }); } @@ -789,6 +693,13 @@ private: return delegate_builder<delegate_type_t<T> >(m_target, m_append, m_target.owner(), devcb_read::cast_reference<delegate_device_class_t<T> >(obj), std::forward<T>(func), name); } + template <typename T> + std::enable_if_t<is_read_method<T>::value, delegate_builder<delegate_type_t<T> > > set(device_t &devbase, char const *tag, T &&func, char const *name) + { + set_used(); + return delegate_builder<delegate_type_t<T> >(m_target, m_append, devbase, tag, 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) { @@ -842,43 +753,15 @@ private: 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)...); - } + auto set_constant(Result val) { return set([val] () { return val; }); } + auto append_constant(Result val) { return append([val] () { return val; }); } - template <typename... Params> - void append_log(Params &&... args) + void remove() { - m_append = true; - set_log(std::forward<Params>(args)...); + set_used(); + m_target.m_creators.clear(); } - 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; } @@ -889,35 +772,46 @@ private: std::vector<func_t> m_functions; std::vector<typename creator::ptr> m_creators; + Result const m_default; + bool m_unset = false; + +protected: + virtual bool findit(validity_checker *valid) override; + virtual void end_configuration() override; public: - devcb_read(device_t &owner); + template <unsigned Count> + class array : public devcb_read_base::array<devcb_read<Result, DefaultMask>, Count> + { + public: + array(device_t &owner, Result dflt) + : devcb_read_base::array<devcb_read<Result, DefaultMask>, Count>::array(owner, dflt) + { + } + }; + + devcb_read(device_t &owner, Result dflt); binder bind(); void reset(); - virtual void validity_check(validity_checker &valid) const override; - - void resolve(); - void resolve_safe(Result dflt); - - 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()(); - bool isnull() const { return m_functions.empty() && m_creators.empty(); } - explicit operator bool() const { return !m_functions.empty(); } + bool isunset() const noexcept { return m_unset || (m_functions.empty() && m_creators.empty()); } }; template <typename Result, std::make_unsigned_t<Result> DefaultMask> -devcb_read<Result, DefaultMask>::devcb_read(device_t &owner) +devcb_read<Result, DefaultMask>::devcb_read(device_t &owner, Result dflt) : devcb_read_base(owner) + , m_default(dflt & DefaultMask) { } template <typename Result, std::make_unsigned_t<Result> DefaultMask> typename devcb_read<Result, DefaultMask>::binder devcb_read<Result, DefaultMask>::bind() { + assert(m_functions.empty()); return binder(*this); } @@ -929,63 +823,65 @@ void devcb_read<Result, DefaultMask>::reset() } template <typename Result, std::make_unsigned_t<Result> DefaultMask> -void devcb_read<Result, DefaultMask>::validity_check(validity_checker &valid) const +bool devcb_read<Result, DefaultMask>::findit(validity_checker *valid) { 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) + if (!valid) { - (*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) + // FIXME: report errors by returning false rather than throwing fatal errors + m_functions.reserve(m_creators.size()); + for (typename creator::ptr const &c : m_creators) + m_functions.emplace_back(c->create()); + m_creators.clear(); + if (m_functions.empty()) { - 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 + m_functions.emplace_back([dflt = m_default] (offs_t offset, std::make_unsigned_t<Result> mem_mask) { return dflt; }); + m_unset = true; } + return true; + } + else + { + bool success(true); + for (typename std::vector<typename creator::ptr>::const_iterator i = m_creators.begin(); m_creators.end() != i; ++i) + { + if (!(*i)->validity_check(*valid)) + success = false; + 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 0x%X and 0x%X overlap\n", m, n); + success = false; + } + } + } + return success; } } template <typename Result, std::make_unsigned_t<Result> DefaultMask> -void devcb_read<Result, DefaultMask>::resolve() -{ - 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) +void devcb_read<Result, DefaultMask>::end_configuration() { - 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; }); } 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) +Result devcb_read<Result, DefaultMask>::operator()(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)); + std::make_unsigned_t<Result> result((*it)(offset, mem_mask)); while (m_functions.end() != ++it) - result |= (*it)(space, offset, mem_mask); + result |= (*it)(offset, mem_mask); return result; } 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) -{ - 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); + return this->operator()(0U, DefaultMask); } @@ -998,7 +894,7 @@ template <typename Input, std::make_unsigned_t<Input> DefaultMask = std::make_un class devcb_write : public devcb_write_base { private: - using func_t = std::function<void (address_space &, offs_t, Input, std::make_unsigned_t<Input>)>; + using func_t = std::function<void (offs_t, Input, std::make_unsigned_t<Input>)>; class creator { @@ -1006,7 +902,7 @@ private: using ptr = std::unique_ptr<creator>; virtual ~creator() { } - virtual void validity_check(validity_checker &valid) const = 0; + virtual bool validity_check(validity_checker &valid) const = 0; virtual func_t create() = 0; }; @@ -1016,11 +912,11 @@ private: public: creator_impl(T &&builder) : m_builder(std::move(builder)) { } - virtual void validity_check(validity_checker &valid) const override { m_builder.validity_check(valid); } + virtual bool validity_check(validity_checker &valid) const override { return m_builder.validity_check(valid); } 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); }; + return [cb = m_builder.build()] (offs_t offset, Input data, std::make_unsigned_t<Input> mem_mask) { cb(offset, data, mem_mask); }; } private: @@ -1030,8 +926,8 @@ private: 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) { }; } + virtual bool validity_check(validity_checker &valid) const override { return true; } + virtual func_t create() override { return [] (offs_t offset, Input data, std::make_unsigned_t<Input> mem_mask) { }; } }; template <typename Source, typename Func> class transform_builder; // workaround for MSVC @@ -1121,15 +1017,15 @@ private: 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) + [cb = std::move(m_cb), exor = this->exor(), mask = this->mask()] (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)); + auto const trans(devcb_write::invoke_transform<input_t, output_t>(cb, offset, data, mem_mask)); mem_mask &= mask; return (trans ^ exor) & mask; }); } - void validity_check(validity_checker &valid) const { m_src.validity_check(valid); } + bool validity_check(validity_checker &valid) const { return m_src.validity_check(valid); } private: transform_builder(transform_builder const &) = delete; @@ -1142,11 +1038,11 @@ private: 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) + [f = std::move(chain), cb = std::move(m_cb), exor = this->exor(), mask = this->mask()] (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)); + auto const trans(devcb_write::invoke_transform<input_t, output_t>(cb, offset, data, mem_mask)); output_t out_mask(mem_mask & mask); - return f(space, offset, (trans ^ exor) & mask, out_mask); + return f(offset, (trans ^ exor) & mask, out_mask); }); } @@ -1186,7 +1082,7 @@ private: } ~first_transform_builder() { this->template register_creator<first_transform_builder>(); } - void validity_check(validity_checker &valid) const { m_sink.validity_check(valid); } + bool validity_check(validity_checker &valid) const { return 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) @@ -1202,13 +1098,13 @@ private: 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) + [sink = m_sink.build(), cb = std::move(m_cb), in_exor = m_in_exor, in_mask = m_in_mask, exor = this->exor(), mask = this->mask()] (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)); + auto const trans(devcb_write::invoke_transform<input_t, output_t>(cb, offset, data, mem_mask)); mem_mask &= mask; - sink(space, offset, (trans ^ exor) & mask, mem_mask); + sink(offset, (trans ^ exor) & mask, mem_mask); }; } @@ -1223,14 +1119,14 @@ private: 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) + [f = std::move(chain), sink = m_sink.build(), cb = std::move(m_cb), in_exor = m_in_exor, in_mask = m_in_mask, exor = this->exor(), mask = this->mask()] (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)); + auto const trans_1(devcb_write::invoke_transform<input_t, output_t>(cb, 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); + auto const trans_n(f(offset, (trans_1 ^ exor) & mask, out_mask)); + sink(offset, trans_n, out_mask); }; } @@ -1253,15 +1149,15 @@ private: 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 { } + bool validity_check(validity_checker &valid) const { return true; } 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); }; + [cb = std::move(m_cb)] (offs_t offset, input_t data, std::make_unsigned_t<input_t> mem_mask) + { devcb_write::invoke_write<Input>(cb, offset, data, mem_mask); }; } private: @@ -1303,15 +1199,15 @@ private: 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(), DefaultMask); } - void validity_check(validity_checker &valid) const { } + bool validity_check(validity_checker &valid) const { return true; } 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); }; + [cb = std::move(m_cb), exor = this->exor(), mask = this->mask()] (offs_t offset, input_t data, std::make_unsigned_t<input_t> mem_mask) + { devcb_write::invoke_write<Input>(cb, offset, (data ^ exor) & mask, mem_mask & mask); }; } }; @@ -1328,7 +1224,6 @@ private: wrapped_builder(delegate_builder &&that) : builder_base(std::move(that)) - , m_devbase(that.m_devbase) , m_delegate(std::move(that.m_delegate)) { that.consume(); @@ -1336,27 +1231,31 @@ private: } 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 + bool 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 const target(m_delegate.finder_target()); + if (target.second && !target.first.subdevice(target.second)) + { + osd_printf_error("Write callback bound to non-existent object tag %s (%s)\n", target.first.subtag(target.second), m_delegate.name()); + return false; + } + return true; } auto build() { assert(this->m_consumed); this->built(); - m_delegate.bind_relative_to(m_devbase); + m_delegate.resolve(); 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); }; + [cb = std::move(m_delegate)] (offs_t offset, input_t data, std::make_unsigned_t<input_t> mem_mask) + { devcb_write::invoke_write<Input>(cb, offset, data, mem_mask); }; } private: @@ -1364,7 +1263,6 @@ private: wrapped_builder operator=(wrapped_builder const &) = delete; wrapped_builder operator=(wrapped_builder &&that) = delete; - device_t &m_devbase; Delegate m_delegate; }; @@ -1374,7 +1272,6 @@ private: delegate_builder &operator=(delegate_builder const &) = delete; delegate_builder &operator=(delegate_builder &&that) = delete; - device_t &m_devbase; Delegate m_delegate; public: @@ -1384,20 +1281,17 @@ private: 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)) + , m_delegate(devbase, tag, std::forward<T>(func), name) { } 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) + , m_delegate(obj, std::forward<T>(func), name) { } 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(); @@ -1413,20 +1307,25 @@ private: 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(), in_mask, out_mask); } - void validity_check(validity_checker &valid) const + bool 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 const target(m_delegate.finder_target()); + if (target.second && !target.first.subdevice(target.second)) + { + osd_printf_error("Write callback bound to non-existent object tag %s (%s)\n", target.first.subtag(target.second), m_delegate.name()); + return false; + } + return true; } auto build() { assert(this->m_consumed); this->built(); - m_delegate.bind_relative_to(m_devbase); + m_delegate.resolve(); 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); }; + [cb = std::move(m_delegate), exor = this->exor(), mask = this->mask()] (offs_t offset, input_t data, std::make_unsigned_t<input_t> mem_mask) + { devcb_write::invoke_write<Input>(cb, offset, (data ^ exor) & mask, mem_mask & mask); }; } }; @@ -1461,16 +1360,23 @@ private: that.built(); } - void validity_check(validity_checker &valid) const + bool 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); + return false; + } 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()); + return false; + } } + return true; } auto build() @@ -1487,7 +1393,7 @@ private: 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 = *m_exec, linenum = m_linenum] (offs_t offset, input_t data, std::make_unsigned_t<input_t> mem_mask) { exec.set_input_line(linenum, data); }; } @@ -1552,16 +1458,23 @@ private: 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(), in_mask, 1U); } - void validity_check(validity_checker &valid) const + bool 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); + return false; + } 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()); + return false; + } } + return true; } auto build() @@ -1578,7 +1491,7 @@ private: 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 = *m_exec, linenum = m_linenum, exor = this->exor(), mask = this->mask()] (offs_t offset, input_t data, std::make_unsigned_t<input_t> mem_mask) { exec.set_input_line(linenum, (data ^ exor) & mask); }; } }; @@ -1616,16 +1529,23 @@ private: that.built(); } - void validity_check(validity_checker &valid) const + bool 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); + return false; + } 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()); + return false; + } } + return true; } auto build() @@ -1642,7 +1562,7 @@ private: 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) + [&exec = *m_exec, linenum = m_linenum, value = m_value] (offs_t offset, input_t data, std::make_unsigned_t<input_t> mem_mask) { if (data) exec.set_input_line(linenum, value); }; } @@ -1711,16 +1631,23 @@ private: 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(), DefaultMask); } - void validity_check(validity_checker &valid) const + bool 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); + return false; + } 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()); + return false; + } } + return true; } auto build() @@ -1737,7 +1664,7 @@ private: 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) + [&exec = *m_exec, linenum = m_linenum, value = m_value, exor = this->exor(), mask = this->mask()] (offs_t offset, input_t data, std::make_unsigned_t<input_t> mem_mask) { if ((data ^ exor) & mask) exec.set_input_line(linenum, value); }; } }; @@ -1769,17 +1696,17 @@ private: that.built(); } - void validity_check(validity_checker &valid) const { } + bool validity_check(validity_checker &valid) const { return true; } auto build() { assert(this->m_consumed); this->built(); - ioport_port *const ioport(m_devbase.ioport(m_tag.c_str())); + ioport_port *const ioport(m_devbase.ioport(m_tag)); 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()); + throw emu_fatalerror("Write callback bound to non-existent I/O port %s of device %s (%s)\n", m_tag, 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 = *ioport] (offs_t offset, input_t data, std::make_unsigned_t<input_t> mem_mask) { port.write(data); }; } @@ -1827,17 +1754,17 @@ private: 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(), DefaultMask); } - void validity_check(validity_checker &valid) const { } + bool validity_check(validity_checker &valid) const { return true; } auto build() { assert(this->m_consumed); this->built(); - ioport_port *const ioport(m_devbase.ioport(m_tag.c_str())); + ioport_port *const ioport(m_devbase.ioport(m_tag)); 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()); + throw emu_fatalerror("Write callback bound to non-existent I/O port %s of device %s (%s)\n", m_tag, 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 = *ioport, exor = this->exor(), mask = this->mask()] (offs_t offset, input_t data, std::make_unsigned_t<input_t> mem_mask) { port.write((data ^ exor) & mask); }; } }; @@ -1869,17 +1796,17 @@ private: that.built(); } - void validity_check(validity_checker &valid) const { } + bool validity_check(validity_checker &valid) const { return true; } auto build() { assert(this->m_consumed); this->built(); - memory_bank *const bank(m_devbase.membank(m_tag.c_str())); + memory_bank *const bank(m_devbase.membank(m_tag)); 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()); + throw emu_fatalerror("Write callback bound to non-existent memory bank %s of device %s (%s)\n", m_tag, 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 = *bank] (offs_t offset, input_t data, std::make_unsigned_t<input_t> mem_mask) { membank.set_entry(data); }; } @@ -1927,17 +1854,17 @@ private: 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(), DefaultMask); } - void validity_check(validity_checker &valid) const { } + bool validity_check(validity_checker &valid) const { return true; } auto build() { assert(this->m_consumed); this->built(); - memory_bank *const bank(m_devbase.membank(m_tag.c_str())); + memory_bank *const bank(m_devbase.membank(m_tag)); 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()); + throw emu_fatalerror("Write callback bound to non-existent memory bank %s of device %s (%s)\n", m_tag, 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 = *bank, exor = this->exor(), mask = this->mask()] (offs_t offset, input_t data, std::make_unsigned_t<input_t> mem_mask) { membank.set_entry((data ^ exor) & mask); }; } }; @@ -1969,14 +1896,14 @@ private: that.built(); } - void validity_check(validity_checker &valid) const { } + bool validity_check(validity_checker &valid) const { return true; } 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 = m_devbase.machine().output().find_or_create_item(m_tag, 0)] (offs_t offset, input_t data, std::make_unsigned_t<input_t> mem_mask) { item.set(data); }; } @@ -2024,111 +1951,18 @@ private: 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(), DefaultMask); } - void validity_check(validity_checker &valid) const { } + bool validity_check(validity_checker &valid) const { return true; } 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 = m_devbase.machine().output().find_or_create_item(m_tag, 0), exor = this->exor(), mask = this->mask()] (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; - }; - - friend class wrapped_builder; // workaround for MSVC - - 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->mask(), DefaultMask); - } - - 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: @@ -2166,6 +2000,13 @@ private: return delegate_builder<delegate_type_t<T> >(m_target, m_append, m_target.owner(), devcb_write::cast_reference<delegate_device_class_t<T> >(obj), std::forward<T>(func), name); } + template <typename T> + std::enable_if_t<is_write_method<T>::value, delegate_builder<delegate_type_t<T> > > set(device_t &devbase, char const *tag, T &&func, char const *name) + { + set_used(); + return delegate_builder<delegate_type_t<T> >(m_target, m_append, devbase, tag, 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) { @@ -2310,43 +2151,17 @@ private: return set_output(std::forward<Params>(args)...); } - template <typename... Params> - log_builder set_log(device_t &devbase, Params &&... args) + void set_nop() { 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)...); + m_target.m_creators.clear(); + m_target.m_creators.emplace_back(std::make_unique<nop_creator>()); } - void set_nop() + void remove() { set_used(); m_target.m_creators.clear(); - m_target.m_creators.emplace_back(std::make_unique<nop_creator>()); } private: @@ -2359,25 +2174,32 @@ private: std::vector<func_t> m_functions; std::vector<typename creator::ptr> m_creators; + bool m_unset = false; + +protected: + virtual bool findit(validity_checker *valid) override; + virtual void end_configuration() override; public: + template <unsigned Count> + class array : public devcb_write_base::array<devcb_write<Input, DefaultMask>, Count> + { + public: + array(device_t &owner) + : devcb_write_base::array<devcb_write<Input, DefaultMask>, Count>::array(owner) + { + } + }; + devcb_write(device_t &owner); binder bind(); void reset(); - 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(); } + bool isunset() const noexcept { return m_unset || (m_functions.empty() && m_creators.empty()); } }; template <typename Input, std::make_unsigned_t<Input> DefaultMask> @@ -2389,6 +2211,7 @@ devcb_write<Input, DefaultMask>::devcb_write(device_t &owner) template <typename Input, std::make_unsigned_t<Input> DefaultMask> typename devcb_write<Input, DefaultMask>::binder devcb_write<Input, DefaultMask>::bind() { + assert(m_functions.empty()); return binder(*this); } @@ -2400,62 +2223,56 @@ void devcb_write<Input, DefaultMask>::reset() } template <typename Input, std::make_unsigned_t<Input> DefaultMask> -void devcb_write<Input, DefaultMask>::validity_check(validity_checker &valid) const +bool devcb_write<Input, DefaultMask>::findit(validity_checker *valid) { assert(m_functions.empty()); - devcb_write_base::validity_check(valid); - for (typename creator::ptr const &c : m_creators) - c->validity_check(valid); -} - -template <typename Input, std::make_unsigned_t<Input> DefaultMask> -void devcb_write<Input, DefaultMask>::resolve() -{ - 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(); + if (!valid) + { + // FIXME: report errors by returning false rather than throwing fatal errors + m_functions.reserve(m_creators.size()); + for (typename creator::ptr const &c : m_creators) + m_functions.emplace_back(c->create()); + m_creators.clear(); + if (m_functions.empty()) + { + m_functions.emplace_back([] (offs_t offset, Input data, std::make_unsigned_t<Input> mem_mask) { }); + m_unset = true; + } + return true; + } + else + { + bool success(true); + for (typename creator::ptr const &c : m_creators) + { + if (!c->validity_check(*valid)) + success = false; + } + return success; + } } template <typename Input, std::make_unsigned_t<Input> DefaultMask> -void devcb_write<Input, DefaultMask>::resolve_safe() +void devcb_write<Input, DefaultMask>::end_configuration() { - 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) +void devcb_write<Input, DefaultMask>::operator()(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); + (*it)(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); -} - -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); + (*it)(offset, data, mem_mask); } template <typename Input, std::make_unsigned_t<Input> DefaultMask> void devcb_write<Input, DefaultMask>::operator()(Input data) { - this->operator()(this->default_space(), 0U, data, DefaultMask); + this->operator()(0U, data, DefaultMask); } - using devcb_read8 = devcb_read<u8>; using devcb_read16 = devcb_read<u16>; using devcb_read32 = devcb_read<u32>; @@ -2479,10 +2296,6 @@ 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<read8s_delegate>; extern template class devcb_read8::delegate_builder<read16s_delegate>; extern template class devcb_read8::delegate_builder<read32s_delegate>; @@ -2497,10 +2310,6 @@ extern template class devcb_read8::delegate_builder<read32smo_delegate>; extern template class devcb_read8::delegate_builder<read64smo_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<read8s_delegate>; extern template class devcb_read16::delegate_builder<read16s_delegate>; extern template class devcb_read16::delegate_builder<read32s_delegate>; @@ -2515,10 +2324,6 @@ extern template class devcb_read16::delegate_builder<read32smo_delegate>; extern template class devcb_read16::delegate_builder<read64smo_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<read8s_delegate>; extern template class devcb_read32::delegate_builder<read16s_delegate>; extern template class devcb_read32::delegate_builder<read32s_delegate>; @@ -2533,10 +2338,6 @@ extern template class devcb_read32::delegate_builder<read32smo_delegate>; extern template class devcb_read32::delegate_builder<read64smo_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<read8s_delegate>; extern template class devcb_read64::delegate_builder<read16s_delegate>; extern template class devcb_read64::delegate_builder<read32s_delegate>; @@ -2551,10 +2352,6 @@ extern template class devcb_read64::delegate_builder<read32smo_delegate>; extern template class devcb_read64::delegate_builder<read64smo_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<read8s_delegate>; extern template class devcb_read_line::delegate_builder<read16s_delegate>; extern template class devcb_read_line::delegate_builder<read32s_delegate>; @@ -2569,10 +2366,6 @@ extern template class devcb_read_line::delegate_builder<read32smo_delegate>; extern template class devcb_read_line::delegate_builder<read64smo_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<read8s_delegate> >; extern template class devcb_read8::creator_impl<devcb_read8::delegate_builder<read16s_delegate> >; extern template class devcb_read8::creator_impl<devcb_read8::delegate_builder<read32s_delegate> >; @@ -2588,10 +2381,6 @@ extern template class devcb_read8::creator_impl<devcb_read8::delegate_builder<re 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<read8s_delegate> >; extern template class devcb_read16::creator_impl<devcb_read16::delegate_builder<read16s_delegate> >; extern template class devcb_read16::creator_impl<devcb_read16::delegate_builder<read32s_delegate> >; @@ -2607,10 +2396,6 @@ extern template class devcb_read16::creator_impl<devcb_read16::delegate_builder< 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<read8s_delegate> >; extern template class devcb_read32::creator_impl<devcb_read32::delegate_builder<read16s_delegate> >; extern template class devcb_read32::creator_impl<devcb_read32::delegate_builder<read32s_delegate> >; @@ -2626,10 +2411,6 @@ extern template class devcb_read32::creator_impl<devcb_read32::delegate_builder< 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<read8s_delegate> >; extern template class devcb_read64::creator_impl<devcb_read64::delegate_builder<read16s_delegate> >; extern template class devcb_read64::creator_impl<devcb_read64::delegate_builder<read32s_delegate> >; @@ -2645,10 +2426,6 @@ extern template class devcb_read64::creator_impl<devcb_read64::delegate_builder< 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<read8s_delegate> >; extern template class devcb_read_line::creator_impl<devcb_read_line::delegate_builder<read16s_delegate> >; extern template class devcb_read_line::creator_impl<devcb_read_line::delegate_builder<read32s_delegate> >; @@ -2670,10 +2447,6 @@ 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<write8s_delegate>; extern template class devcb_write8::delegate_builder<write16s_delegate>; extern template class devcb_write8::delegate_builder<write32s_delegate>; @@ -2688,10 +2461,6 @@ extern template class devcb_write8::delegate_builder<write32smo_delegate>; extern template class devcb_write8::delegate_builder<write64smo_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<write8s_delegate>; extern template class devcb_write16::delegate_builder<write16s_delegate>; extern template class devcb_write16::delegate_builder<write32s_delegate>; @@ -2706,10 +2475,6 @@ extern template class devcb_write16::delegate_builder<write32smo_delegate>; extern template class devcb_write16::delegate_builder<write64smo_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<write8s_delegate>; extern template class devcb_write32::delegate_builder<write16s_delegate>; extern template class devcb_write32::delegate_builder<write32s_delegate>; @@ -2724,10 +2489,6 @@ extern template class devcb_write32::delegate_builder<write32smo_delegate>; extern template class devcb_write32::delegate_builder<write64smo_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<write8s_delegate>; extern template class devcb_write64::delegate_builder<write16s_delegate>; extern template class devcb_write64::delegate_builder<write32s_delegate>; @@ -2742,10 +2503,6 @@ extern template class devcb_write64::delegate_builder<write32smo_delegate>; extern template class devcb_write64::delegate_builder<write64smo_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<write8s_delegate>; extern template class devcb_write_line::delegate_builder<write16s_delegate>; extern template class devcb_write_line::delegate_builder<write32s_delegate>; @@ -2760,10 +2517,6 @@ extern template class devcb_write_line::delegate_builder<write32smo_delegate>; extern template class devcb_write_line::delegate_builder<write64smo_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<write8s_delegate> >; extern template class devcb_write8::creator_impl<devcb_write8::delegate_builder<write16s_delegate> >; extern template class devcb_write8::creator_impl<devcb_write8::delegate_builder<write32s_delegate> >; @@ -2782,12 +2535,7 @@ extern template class devcb_write8::creator_impl<devcb_write8::latched_inputline 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<write8s_delegate> >; extern template class devcb_write16::creator_impl<devcb_write16::delegate_builder<write16s_delegate> >; extern template class devcb_write16::creator_impl<devcb_write16::delegate_builder<write32s_delegate> >; @@ -2806,12 +2554,7 @@ extern template class devcb_write16::creator_impl<devcb_write16::latched_inputli 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<write8s_delegate> >; extern template class devcb_write32::creator_impl<devcb_write32::delegate_builder<write16s_delegate> >; extern template class devcb_write32::creator_impl<devcb_write32::delegate_builder<write32s_delegate> >; @@ -2830,12 +2573,7 @@ extern template class devcb_write32::creator_impl<devcb_write32::latched_inputli 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<write8s_delegate> >; extern template class devcb_write64::creator_impl<devcb_write64::delegate_builder<write16s_delegate> >; extern template class devcb_write64::creator_impl<devcb_write64::delegate_builder<write32s_delegate> >; @@ -2854,12 +2592,7 @@ extern template class devcb_write64::creator_impl<devcb_write64::latched_inputli 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<write8s_delegate> >; extern template class devcb_write_line::creator_impl<devcb_write_line::delegate_builder<write16s_delegate> >; extern template class devcb_write_line::creator_impl<devcb_write_line::delegate_builder<write32s_delegate> >; @@ -2878,6 +2611,5 @@ extern template class devcb_write_line::creator_impl<devcb_write_line::latched_i 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/devcpu.cpp b/src/emu/devcpu.cpp index e75b621c6f0..616a501f438 100644 --- a/src/emu/devcpu.cpp +++ b/src/emu/devcpu.cpp @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles /*************************************************************************** - devcpu.c + devcpu.cpp CPU device definitions. @@ -10,7 +10,7 @@ #include "emu.h" #include "emuopts.h" -#include <ctype.h> +#include <cctype> //************************************************************************** @@ -21,13 +21,15 @@ // cpu_device - constructor //------------------------------------------------- -cpu_device::cpu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock) - : device_t(mconfig, type, tag, owner, clock), - device_execute_interface(mconfig, *this), - device_memory_interface(mconfig, *this), - device_state_interface(mconfig, *this), - device_disasm_interface(mconfig, *this), - m_force_no_drc(false) +cpu_device::cpu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock) : + device_t(mconfig, type, tag, owner, clock), + device_execute_interface(mconfig, *this), + device_memory_interface(mconfig, *this), + device_state_interface(mconfig, *this), + device_disasm_interface(mconfig, *this), + m_force_no_drc(false), + m_access_to_be_redone(false), + m_access_before_delay_tag(nullptr) { } @@ -49,3 +51,60 @@ bool cpu_device::allow_drc() const { return mconfig().options().drc() && !m_force_no_drc; } + + + +bool cpu_device::cpu_is_interruptible() const +{ + return false; +} + +bool cpu_device::access_before_time(u64 access_time, u64 current_time) noexcept +{ + s32 delta = access_time - current_time; + if(*m_icountptr <= delta) { + defer_access(); + return true; + } + + *m_icountptr -= delta; + + return false; +} + +bool cpu_device::access_before_delay(u32 cycles, const void *tag) noexcept +{ + if(tag == m_access_before_delay_tag) { + m_access_before_delay_tag = nullptr; + return false; + } + + *m_icountptr -= cycles; + + if(*m_icountptr <= 0) { + m_access_before_delay_tag = tag; + m_access_to_be_redone = true; + return true; + } + + m_access_before_delay_tag = nullptr; + return false; +} + +void cpu_device::access_after_delay(u32 cycles) noexcept +{ + *m_icountptr -= cycles; +} + +void cpu_device::defer_access() noexcept +{ + if(*m_icountptr > 0) + *m_icountptr = 0; + m_access_to_be_redone = true; +} + +void cpu_device::retry_access() noexcept +{ + abort_timeslice(); + m_access_to_be_redone = true; +} diff --git a/src/emu/devcpu.h b/src/emu/devcpu.h index 4cce31b5e38..e4a37575858 100644 --- a/src/emu/devcpu.h +++ b/src/emu/devcpu.h @@ -7,6 +7,7 @@ CPU device definitions. ***************************************************************************/ + #ifndef MAME_EMU_DEVCPU_H #define MAME_EMU_DEVCPU_H @@ -15,6 +16,8 @@ #include "didisasm.h" #include "diexec.h" +#include <utility> + //************************************************************************** // TYPE DEFINITIONS @@ -35,13 +38,32 @@ public: void set_force_no_drc(bool value) { m_force_no_drc = value; } bool allow_drc() const; + virtual bool cpu_is_interruptible() const; + + // To be used only for interruptible cpus + bool access_to_be_redone() noexcept { return std::exchange(m_access_to_be_redone, false); } + bool access_to_be_redone_noclear() noexcept { return m_access_to_be_redone; } + + // Returns true if the access must be aborted + bool access_before_time(u64 access_time, u64 current_time) noexcept; + bool access_before_delay(u32 cycles, const void *tag) noexcept; + + // The access has already happened, nothing to abort + void access_after_delay(u32 cycles) noexcept; + + void defer_access() noexcept; + void retry_access() noexcept; + protected: // construction/destruction cpu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock); private: // configured state - bool m_force_no_drc; // whether or not to force DRC off + bool m_force_no_drc; // whether or not to force DRC off + + bool m_access_to_be_redone; // whether an access needs to be redone + const void *m_access_before_delay_tag; // if the tag matches on access_before_delay, consider the delay to have already happened }; #endif // MAME_EMU_DEVCPU_H diff --git a/src/emu/devdelegate.cpp b/src/emu/devdelegate.cpp index 380f01180e2..7f50f3112e1 100644 --- a/src/emu/devdelegate.cpp +++ b/src/emu/devdelegate.cpp @@ -1,37 +1,34 @@ // license:BSD-3-Clause -// copyright-holders:Aaron Giles +// copyright-holders:Aaron Giles, Vas Crabb /*************************************************************************** - devdelegate.c + devdelegate.cpp Delegates that are late-bound to MAME devices. ***************************************************************************/ #include "emu.h" +#include "devdelegate.h" -//------------------------------------------------- -// bound_object - use the device name to locate -// a device relative to the given search root; -// fatal error if not found -//------------------------------------------------- +namespace emu::detail { -delegate_late_bind &device_delegate_helper::bound_object(device_t &search_root) const +delegate_late_bind &device_delegate_helper::bound_object() const { - device_t *device = search_root.subdevice(m_device_name); - if (device == nullptr) - throw emu_fatalerror("Unable to locate device '%s' relative to '%s'\n", m_device_name, search_root.tag()); + if (!m_tag) + return m_base.get(); + device_t *const device(m_base.get().subdevice(m_tag)); + if (!device) + throw emu_fatalerror("Unable to locate device '%s' relative to '%s'\n", m_tag, m_base.get().tag()); return *device; } -//------------------------------------------------- -// safe_tag - return a tag string or (unknown) if -// the object is not valid -//------------------------------------------------- - -const char *device_delegate_helper::safe_tag(device_t *object) +void device_delegate_helper::set_tag(char const *tag) { - return (object != nullptr) ? object->tag() : "(unknown)"; + m_base = m_base.get().mconfig().current_device(); + m_tag = tag; } + +} // namespace emu::detail diff --git a/src/emu/devdelegate.h b/src/emu/devdelegate.h index 8bdef60f880..3a63c874d92 100644 --- a/src/emu/devdelegate.h +++ b/src/emu/devdelegate.h @@ -1,5 +1,5 @@ // license:BSD-3-Clause -// copyright-holders:Aaron Giles +// copyright-holders:Aaron Giles, Vas Crabb /*************************************************************************** devdelegate.h @@ -15,98 +15,417 @@ #include "delegate.h" +#include <array> +#include <functional> +#include <type_traits> +#include <utility> + + +// older versions of libc++ are missing deduction guides that the things using this constructor require +#if defined(_LIBCPP_VERSION) && (_LIBCPP_VERSION < 10000) +namespace std { inline namespace __1 { +template<class R, class... ArgTypes > function( R(*)(ArgTypes...) ) -> function<R(ArgTypes...)>; +} } +#endif + + +namespace emu { //************************************************************************** -// TYPE DEFINITIONS +// FORWARD DECLARATIONS //************************************************************************** -// ======================> device_delegate_helper - -// device_delegate_helper does non-template work -class device_delegate_helper -{ -protected: - // constructor - device_delegate_helper(const char *devname) : m_device_name(devname) { } +template <typename Signature, unsigned Count> class device_delegate_array; - // internal helpers - delegate_late_bind &bound_object(device_t &search_root) const; - static const char *safe_tag(device_t *object); - // internal state - const char *m_device_name; -}; +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** -// ======================> named_delegate template <typename Signature> class named_delegate : public delegate<Signature> { private: using basetype = delegate<Signature>; - const char * m_name; // name string + char const *m_name = nullptr; // name string protected: template <class FunctionClass> using member_func_type = typename basetype::template member_func_type<FunctionClass>; template <class FunctionClass> using const_member_func_type = typename basetype::template const_member_func_type<FunctionClass>; template <class FunctionClass> using static_ref_func_type = typename basetype::template static_ref_func_type<FunctionClass>; + template <typename T> using suitable_functoid = typename basetype::template suitable_functoid<T>; + public: // create a standard set of constructors - named_delegate() : basetype(), m_name(nullptr) { } - explicit named_delegate(const basetype &src) : basetype(src), m_name(src.m_name) { } - named_delegate(const basetype &src, delegate_late_bind &object) : basetype(src, object), m_name(src.m_name) { } - template <class FunctionClass> named_delegate(member_func_type<FunctionClass> funcptr, const char *name, FunctionClass *object) : basetype(funcptr, object), m_name(name) { } - template <class FunctionClass> named_delegate(const_member_func_type<FunctionClass> funcptr, const char *name, FunctionClass *object) : basetype(funcptr, object), m_name(name) { } - explicit named_delegate(std::function<Signature> funcptr, const char *name) : basetype(funcptr), m_name(name) { } - template <class FunctionClass> named_delegate(static_ref_func_type<FunctionClass> funcptr, const char *name, FunctionClass *object) : basetype(funcptr, object), m_name(name) { } - named_delegate &operator=(const basetype &src) { basetype::operator=(src); m_name = src.m_name; return *this; } - - const char *name() const { return m_name; } + named_delegate() = default; + named_delegate(named_delegate const &) = default; + named_delegate(named_delegate const &src, delegate_late_bind &object) : basetype(src, object), m_name(src.m_name) { } + template <class FunctionClass> named_delegate(member_func_type<FunctionClass> funcptr, char const *name, FunctionClass *object) : basetype(funcptr, object), m_name(name) { } + template <class FunctionClass> named_delegate(const_member_func_type<FunctionClass> funcptr, char const *name, FunctionClass *object) : basetype(funcptr, object), m_name(name) { } + template <class FunctionClass> named_delegate(static_ref_func_type<FunctionClass> funcptr, char const *name, FunctionClass *object) : basetype(funcptr, object), m_name(name) { } + template <typename T> named_delegate(T &&funcptr, std::enable_if_t<suitable_functoid<T>::value, char const *> name) : basetype(std::forward<T>(funcptr)), m_name(name) { } + + // allow assignment + named_delegate &operator=(named_delegate const &) = default; + named_delegate &operator=(named_delegate &&) = default; + named_delegate &operator=(std::nullptr_t) noexcept { reset(); return *this; } + + // getters + char const *name() const noexcept { return m_name; } + + // unsetter + void reset() noexcept { basetype::reset(); m_name = nullptr; } }; -// ======================> device_delegate +template <class FunctionClass, typename ReturnType, typename... Params> +named_delegate(ReturnType (FunctionClass::*)(Params...), char const *, FunctionClass *) -> named_delegate<ReturnType (Params...)>; -// device_delegate is a delegate that wraps with a device tag and can be easily -// late bound without replicating logic everywhere -template <typename Signature> -class device_delegate : public named_delegate<Signature>, public device_delegate_helper +template <class FunctionClass, typename ReturnType, typename... Params> +named_delegate(ReturnType (FunctionClass::*)(Params...) const, char const *, FunctionClass *) -> named_delegate<ReturnType (Params...)>; + +template <class FunctionClass, typename ReturnType, typename... Params> +named_delegate(ReturnType (*)(FunctionClass &, Params...), char const *, FunctionClass *) -> named_delegate<ReturnType (Params...)>; + + +namespace detail { + +// device_delegate_helper does non-template work + +class device_delegate_helper { - using thistype = device_delegate<Signature>; - using basetype = named_delegate<Signature>; - template <class FunctionClass> using member_func_type = typename basetype::template member_func_type<FunctionClass>; - template <class FunctionClass> using const_member_func_type = typename basetype::template const_member_func_type<FunctionClass>; - template <class FunctionClass> using static_ref_func_type = typename basetype::template static_ref_func_type<FunctionClass>; +public: + // accessors + char const *finder_tag() const { return m_tag; } + std::pair<device_t &, char const *> finder_target() const { return std::make_pair(m_base, m_tag); } + +protected: + // construct/assign + device_delegate_helper(device_t &owner, char const *tag = nullptr) : m_base(owner), m_tag(tag) { } + template <class DeviceClass, bool Required> device_delegate_helper(device_finder<DeviceClass, Required> const &finder); + device_delegate_helper(device_delegate_helper const &) = default; + device_delegate_helper &operator=(device_delegate_helper const &) = default; + + // internal helpers + delegate_late_bind &bound_object() const; + void set_tag(device_t &object) { m_base = object; m_tag = nullptr; } + void set_tag(device_t &base, char const *tag) { m_base = base; m_tag = tag; } + void set_tag(char const *tag); + template <class DeviceClass, bool Required> void set_tag(device_finder<DeviceClass, Required> const &finder); + +private: + std::reference_wrapper<device_t> m_base; + char const *m_tag; +}; + + +// device_delegate_base doesn't need to be specialised on return type + +template <typename Signature> class device_delegate_base; + +template <typename ReturnType, typename... Params> +class device_delegate_base<ReturnType (Params...)> : protected named_delegate<ReturnType (Params...)>, public device_delegate_helper +{ +private: + using basetype = named_delegate<ReturnType (Params...)>; + + template <typename T> + using suitable_functoid = typename basetype::template suitable_functoid<T>; + + template <class T, class U> + using is_related_device_implementation = std::bool_constant<std::is_base_of_v<T, U> && std::is_base_of_v<device_t, U> >; + template <class T, class U> + using is_related_device_interface = std::bool_constant<std::is_base_of_v<T, U> && std::is_base_of_v<device_interface, U> && !std::is_base_of_v<device_t, U> >; + template <class T, class U> + using is_related_device = std::bool_constant<is_related_device_implementation<T, U>::value || is_related_device_interface<T, U>::value>; + + template <class T> static std::enable_if_t<is_related_device_implementation<T, T>::value, device_t &> get_device(T &object) { return object; } + template <class T> static std::enable_if_t<is_related_device_interface<T, T>::value, device_t &> get_device(T &object) { return object.device(); } + +public: + // construct/assign + explicit device_delegate_base(device_t &owner) : basetype(), device_delegate_helper(owner) { } + device_delegate_base(device_delegate_base const &) = default; + device_delegate_base &operator=(device_delegate_base const &) = default; + + // construct with prototype and target + device_delegate_base(basetype const &proto, device_t &object) : basetype(proto, object), device_delegate_helper(object) { } + device_delegate_base(device_delegate_base const &proto, device_t &object) : basetype(proto, object), device_delegate_helper(object) { } + + // construct with base and tag + template <class D> + device_delegate_base(device_t &base, char const *tag, ReturnType (D::*funcptr)(Params...), char const *name) + : basetype(funcptr, name, static_cast<D *>(nullptr)) + , device_delegate_helper(base, tag) + { } + template <class D> + device_delegate_base(device_t &base, char const *tag, ReturnType (D::*funcptr)(Params...) const, char const *name) + : basetype(funcptr, name, static_cast<D *>(nullptr)) + , device_delegate_helper(base, tag) + { } + template <class D> + device_delegate_base(device_t &base, char const *tag, ReturnType (*funcptr)(D &, Params...), char const *name) + : basetype(funcptr, name, static_cast<D *>(nullptr)) + , device_delegate_helper(base, tag) + { } + + // construct with device finder + template <class D, bool R, class E> + device_delegate_base(device_finder<D, R> const &finder, ReturnType (E::*funcptr)(Params...), char const *name) + : basetype(funcptr, name, static_cast<E *>(nullptr)) + , device_delegate_helper(finder) + { } + template <class D, bool R, class E> + device_delegate_base(device_finder<D, R> const &finder, ReturnType (E::*funcptr)(Params...) const, char const *name) + : basetype(funcptr, name, static_cast<E *>(nullptr)) + , device_delegate_helper(finder) + { } + template <class D, bool R, class E> + device_delegate_base(device_finder<D, R> const &finder, ReturnType (*funcptr)(E &, Params...), char const *name) + : basetype(funcptr, name, static_cast<E *>(nullptr)) + , device_delegate_helper(finder) + { } + + // construct with target object + template <class T, class D> + device_delegate_base(T &object, ReturnType (D::*funcptr)(Params...), std::enable_if_t<is_related_device<D, T>::value, char const *> name) + : basetype(funcptr, name, static_cast<D *>(&object)) + , device_delegate_helper(get_device(object)) + { } + template <class T, class D> + device_delegate_base(T &object, ReturnType (D::*funcptr)(Params...) const, std::enable_if_t<is_related_device<D, T>::value, char const *> name) + : basetype(funcptr, name, static_cast<D *>(&object)) + , device_delegate_helper(get_device(object)) + { } + template <class T, class D> + device_delegate_base(T &object, ReturnType (*funcptr)(D &, Params...), std::enable_if_t<is_related_device<D, T>::value, char const *> name) + : basetype(funcptr, name, static_cast<D *>(&object)) + , device_delegate_helper(get_device(object)) + { } + + // construct with callable object + template <typename T> + device_delegate_base(device_t &owner, T &&funcptr, std::enable_if_t<suitable_functoid<T>::value, char const *> name) + : basetype(std::forward<T>(funcptr), name) + , device_delegate_helper(owner) + { } + // setters that implicitly bind to the current device + template <class D> void set(ReturnType (D::*funcptr)(Params...), char const *name) + { basetype::operator=(basetype(funcptr, name, static_cast<D *>(nullptr))); set_tag(nullptr); } + template <class D> void set(ReturnType (D::*funcptr)(Params...) const, char const *name) + { basetype::operator=(basetype(funcptr, name, static_cast<D *>(nullptr))); set_tag(nullptr); } + template <class D> void set(ReturnType (*funcptr)(D &, Params...), char const *name) + { basetype::operator=(basetype(funcptr, name, static_cast<D *>(nullptr))); set_tag(nullptr); } + + // setters that take a tag-like object specifying the target + template <typename T, class D> void set(T &&tag, ReturnType (D::*funcptr)(Params...), char const *name) + { basetype::operator=(basetype(funcptr, name, static_cast<D *>(nullptr))); set_tag(std::forward<T>(tag)); } + template <typename T, class D> void set(T &&tag, ReturnType (D::*funcptr)(Params...) const, char const *name) + { basetype::operator=(basetype(funcptr, name, static_cast<D *>(nullptr))); set_tag(std::forward<T>(tag)); } + template <typename T, class D> void set(T &&tag, ReturnType (*funcptr)(D &, Params...), char const *name) + { basetype::operator=(basetype(funcptr, name, static_cast<D *>(nullptr))); set_tag(std::forward<T>(tag)); } + + // setters that take a target object + template <class T, class D> std::enable_if_t<std::is_base_of<D, T>::value> set(T &object, ReturnType (D::*funcptr)(Params...), char const *name) + { basetype::operator=(basetype(funcptr, name, static_cast<D *>(&object))); set_tag(finder_target().first); } + template <class T, class D> std::enable_if_t<std::is_base_of<D, T>::value> set(T &object, ReturnType (D::*funcptr)(Params...) const, char const *name) + { basetype::operator=(basetype(funcptr, name, static_cast<D *>(&object))); set_tag(finder_target().first); } + template <class T, class D> std::enable_if_t<std::is_base_of<D, T>::value> set(T &object, ReturnType (*funcptr)(D &, Params...), char const *name) + { basetype::operator=(basetype(funcptr, name, static_cast<D *>(&object))); set_tag(finder_target().first); } + + // setter that takes a functoid + template <typename T> std::enable_if_t<suitable_functoid<T>::value> set(T &&funcptr, char const *name) + { basetype::operator=(basetype(std::forward<T>(funcptr), name)); } + + // unsetter + void set(std::nullptr_t) + { reset(); } + void reset() + { basetype::reset(); set_tag(finder_target().first); } + + // accessors + using basetype::operator(); + using basetype::isnull; + using basetype::has_object; + using basetype::name; + + // perform the binding + void resolve() + { + if (!this->isnull() && !this->has_object()) + this->late_bind(bound_object()); + } +}; + +} // namespace detail + + +// device_delegate is a delegate that can be late bound to a device specification + +template <typename Signature> class device_delegate; + +template <typename ReturnType, typename... Params> +class device_delegate<ReturnType (Params...)> : protected detail::device_delegate_base<ReturnType (Params...)> +{ public: - // provide the same constructors as the base class - device_delegate() : basetype(), device_delegate_helper(nullptr) { } - device_delegate(const basetype &src) : basetype(src), device_delegate_helper(src.m_device_name) { } - device_delegate(const basetype &src, delegate_late_bind &object) : basetype(src, object), device_delegate_helper(src.m_device_name) { } - template <class FunctionClass> device_delegate(member_func_type<FunctionClass> funcptr, const char *name, FunctionClass *object) : basetype(funcptr, name, object), device_delegate_helper(safe_tag(dynamic_cast<device_t *>(object))) { } - template <class FunctionClass> device_delegate(const_member_func_type<FunctionClass> funcptr, const char *name, FunctionClass *object) : basetype(funcptr, name, object), device_delegate_helper(safe_tag(dynamic_cast<device_t *>(object))) { } - template <class FunctionClass> device_delegate(static_ref_func_type<FunctionClass> funcptr, const char *name, FunctionClass *object) : basetype(funcptr, name, object), device_delegate_helper(safe_tag(dynamic_cast<device_t *>(object))) { } - device_delegate(std::function<Signature> funcptr, const char *name) : basetype(funcptr, name), device_delegate_helper(nullptr) { } - device_delegate &operator=(const thistype &src) { basetype::operator=(src); m_device_name = src.m_device_name; return *this; } - - // provide additional constructors that take a device name string - template <class FunctionClass> device_delegate(member_func_type<FunctionClass> funcptr, const char *name, const char *devname) : basetype(funcptr, name, static_cast<FunctionClass *>(nullptr)), device_delegate_helper(devname) { } - template <class FunctionClass> device_delegate(member_func_type<FunctionClass> funcptr, const char *name, const char *devname, FunctionClass *) : basetype(funcptr, name, static_cast<FunctionClass *>(nullptr)), device_delegate_helper(devname) { } - template <class FunctionClass> device_delegate(const_member_func_type<FunctionClass> funcptr, const char *name, const char *devname) : basetype(funcptr, name, static_cast<FunctionClass *>(nullptr)), device_delegate_helper(devname) { } - template <class FunctionClass> device_delegate(const_member_func_type<FunctionClass> funcptr, const char *name, const char *devname, FunctionClass *) : basetype(funcptr, name, static_cast<FunctionClass *>(nullptr)), device_delegate_helper(devname) { } - template <class FunctionClass> device_delegate(static_ref_func_type<FunctionClass> funcptr, const char *name, const char *devname, FunctionClass *) : basetype(funcptr, name, static_cast<FunctionClass *>(nullptr)), device_delegate_helper(devname) { } - device_delegate(static_ref_func_type<device_t> funcptr, const char *name) : basetype(funcptr, name, static_cast<device_t *>(nullptr)), device_delegate_helper(nullptr) { } - - // and constructors that provide a search root - device_delegate(const thistype &src, device_t &search_root) : basetype(src), device_delegate_helper(src.m_device_name) { bind_relative_to(search_root); } + template <unsigned Count> using array = device_delegate_array<ReturnType (Params...), Count>; + + template <typename T> + using supports_callback = std::bool_constant<std::is_constructible_v<device_delegate, device_t &, char const *, T, char const *> >; + + // constructors + using detail::device_delegate_base<ReturnType (Params...)>::device_delegate_base; + device_delegate(device_delegate const &) = default; + device_delegate(device_delegate const &proto, device_t &object) : detail::device_delegate_base<ReturnType (Params...)>(proto, object) { } + + // assignment + device_delegate &operator=(device_delegate const &) = default; + device_delegate &operator=(std::nullptr_t) { this->reset(); } + + // setters/unsetters + void set(device_delegate const &src) { operator=(src); } + using detail::device_delegate_base<ReturnType (Params...)>::set; + using detail::device_delegate_base<ReturnType (Params...)>::reset; + + // accessors + using detail::device_delegate_base<ReturnType (Params...)>::operator(); + using detail::device_delegate_base<ReturnType (Params...)>::isnull; + using detail::device_delegate_base<ReturnType (Params...)>::has_object; + using detail::device_delegate_base<ReturnType (Params...)>::name; + using detail::device_delegate_base<ReturnType (Params...)>::finder_tag; + using detail::device_delegate_base<ReturnType (Params...)>::finder_target; + + // perform the binding + using detail::device_delegate_base<ReturnType (Params...)>::resolve; + + template <typename T> + void resolve_safe(T &&dflt) + { + if (this->isnull()) + this->set([result = std::decay_t<T>(std::forward<T>(dflt))] (Params...) { return ReturnType(result); }, "default"); + else if (!this->has_object()) + this->late_bind(this->bound_object()); + } +}; + +template <typename... Params> +class device_delegate<void (Params...)> : protected detail::device_delegate_base<void (Params...)> +{ +public: + template <unsigned Count> using array = device_delegate_array<void (Params...), Count>; + + template <typename T> + using supports_callback = std::bool_constant<std::is_constructible_v<device_delegate, device_t &, char const *, T, char const *> >; + + // constructors + using detail::device_delegate_base<void (Params...)>::device_delegate_base; + device_delegate(device_delegate const &) = default; + device_delegate(device_delegate const &proto, device_t &object) : detail::device_delegate_base<void (Params...)>(proto, object) { } + + // assignment + device_delegate &operator=(device_delegate const &) = default; + device_delegate &operator=(std::nullptr_t) { this->reset(); } + + // setters/unsetters + void set(device_delegate const &src) { operator=(src); } + using detail::device_delegate_base<void (Params...)>::set; + using detail::device_delegate_base<void (Params...)>::reset; + + // accessors + using detail::device_delegate_base<void (Params...)>::operator(); + using detail::device_delegate_base<void (Params...)>::isnull; + using detail::device_delegate_base<void (Params...)>::has_object; + using detail::device_delegate_base<void (Params...)>::name; + using detail::device_delegate_base<void (Params...)>::finder_tag; + using detail::device_delegate_base<void (Params...)>::finder_target; // perform the binding - void bind_relative_to(device_t &search_root) { if (!basetype::isnull()) basetype::late_bind(bound_object(search_root)); } + using detail::device_delegate_base<void (Params...)>::resolve; - // getter (for validation purposes) - const char *device_name() const { return m_device_name; } + void resolve_safe() + { + if (this->isnull()) + this->set([] (Params...) { }, "default"); + else if (!this->has_object()) + this->late_bind(this->bound_object()); + } }; +// simplifies constructing an array of delegates with a single owner + +template <typename Signature, unsigned Count> +class device_delegate_array : public std::array<device_delegate<Signature>, Count> +{ +private: + template <unsigned... V> + device_delegate_array(device_t &owner, std::integer_sequence<unsigned, V...> const &) + : std::array<device_delegate<Signature>, Count>{{ make_one<V>(owner)... }} + { + } + + template <unsigned N> + device_delegate<Signature> make_one(device_t &owner) + { + return device_delegate<Signature>(owner); + } + +public: + using std::array<device_delegate<Signature>, Count>::array; + + device_delegate_array(device_t &owner) + : device_delegate_array(owner, std::make_integer_sequence<unsigned, Count>()) + { + } + + void resolve_all() + { + for (device_delegate<Signature> &elem : *this) + elem.resolve(); + } + + template <typename... T> + void resolve_all_safe(T &&... args) + { + for (device_delegate<Signature> &elem : *this) + elem.resolve_safe(args...); + } +}; + +template <class D, typename ReturnType, typename... Params> +device_delegate(device_t &, char const *, ReturnType (D::*)(Params...), char const *) -> device_delegate<ReturnType (Params...)>; + +template <class D, typename ReturnType, typename... Params> +device_delegate(device_t &, char const *, ReturnType (D::*)(Params...) const, char const *) -> device_delegate<ReturnType (Params...)>; + +template <class D, typename ReturnType, typename... Params> +device_delegate(device_t &, char const *, ReturnType (*)(D &, Params...), char const *) -> device_delegate<ReturnType (Params...)>; + +template <class D, bool R, class E, typename ReturnType, typename... Params> +device_delegate(device_finder<D, R> const &, ReturnType (E::*)(Params...), char const *) -> device_delegate<ReturnType (Params...)>; + +template <class D, bool R, class E, typename ReturnType, typename... Params> +device_delegate(device_finder<D, R> const &, ReturnType (E::*)(Params...) const, char const *) -> device_delegate<ReturnType (Params...)>; + +template <class D, bool R, class E, typename ReturnType, typename... Params> +device_delegate(device_finder<D, R> const &, ReturnType (*)(E &, Params...), char const *) -> device_delegate<ReturnType (Params...)>; + +template <class T, class D, typename ReturnType, typename... Params> +device_delegate(T &, ReturnType (D::*)(Params...), char const *) -> device_delegate<ReturnType (Params...)>; + +template <class T, class D, typename ReturnType, typename... Params> +device_delegate(T &, ReturnType (D::*)(Params...) const, char const *) -> device_delegate<ReturnType (Params...)>; + +template <class T, class D, typename ReturnType, typename... Params> +device_delegate(T &, ReturnType (*)(D &, Params...), char const *) -> device_delegate<ReturnType (Params...)>; + +} // namespace emu + + +using emu::named_delegate; +using emu::device_delegate; + #endif // MAME_EMU_DEVDELEGATE_H diff --git a/src/emu/devfind.cpp b/src/emu/devfind.cpp index 4128123e0f2..6170b2fca8c 100644 --- a/src/emu/devfind.cpp +++ b/src/emu/devfind.cpp @@ -2,94 +2,39 @@ // copyright-holders:Aaron Giles /*************************************************************************** - devfind.c + devfind.cpp Device finding template helpers. ***************************************************************************/ #include "emu.h" + #include "romload.h" +#include "validity.h" //************************************************************************** -// EXPLICIT TEMPLATE INSTANTIATIONS +// BASE FINDER CLASS //************************************************************************** -template class object_finder_base<memory_region, false>; -template class object_finder_base<memory_region, true>; -template class object_finder_base<memory_bank, false>; -template class object_finder_base<memory_bank, true>; -template class object_finder_base<ioport_port, false>; -template class object_finder_base<ioport_port, true>; -template class object_finder_base<address_space, false>; -template class object_finder_base<address_space, true>; - -template class object_finder_base<u8, false>; -template class object_finder_base<u8, true>; -template class object_finder_base<u16, false>; -template class object_finder_base<u16, true>; -template class object_finder_base<u32, false>; -template class object_finder_base<u32, true>; -template class object_finder_base<u64, false>; -template class object_finder_base<u64, true>; - -template class object_finder_base<s8, false>; -template class object_finder_base<s8, true>; -template class object_finder_base<s16, false>; -template class object_finder_base<s16, true>; -template class object_finder_base<s32, false>; -template class object_finder_base<s32, true>; -template class object_finder_base<s64, false>; -template class object_finder_base<s64, true>; - -template class memory_region_finder<false>; -template class memory_region_finder<true>; - -template class memory_bank_finder<false>; -template class memory_bank_finder<true>; - -template class ioport_finder<false>; -template class ioport_finder<true>; - -template class address_space_finder<false>; -template class address_space_finder<true>; +//------------------------------------------------- +// device_resolver_base - constructor +//------------------------------------------------- -template class region_ptr_finder<u8, false>; -template class region_ptr_finder<u8, true>; -template class region_ptr_finder<u16, false>; -template class region_ptr_finder<u16, true>; -template class region_ptr_finder<u32, false>; -template class region_ptr_finder<u32, true>; -template class region_ptr_finder<u64, false>; -template class region_ptr_finder<u64, true>; +device_resolver_base::device_resolver_base(device_t &base) + : m_next(base.register_auto_finder(*this)) +{ +} -template class region_ptr_finder<s8, false>; -template class region_ptr_finder<s8, true>; -template class region_ptr_finder<s16, false>; -template class region_ptr_finder<s16, true>; -template class region_ptr_finder<s32, false>; -template class region_ptr_finder<s32, true>; -template class region_ptr_finder<s64, false>; -template class region_ptr_finder<s64, true>; -template class shared_ptr_finder<u8, false>; -template class shared_ptr_finder<u8, true>; -template class shared_ptr_finder<u16, false>; -template class shared_ptr_finder<u16, true>; -template class shared_ptr_finder<u32, false>; -template class shared_ptr_finder<u32, true>; -template class shared_ptr_finder<u64, false>; -template class shared_ptr_finder<u64, true>; +//------------------------------------------------- +// ~device_resolver_base - destructor +//------------------------------------------------- -template class shared_ptr_finder<s8, false>; -template class shared_ptr_finder<s8, true>; -template class shared_ptr_finder<s16, false>; -template class shared_ptr_finder<s16, true>; -template class shared_ptr_finder<s32, false>; -template class shared_ptr_finder<s32, true>; -template class shared_ptr_finder<s64, false>; -template class shared_ptr_finder<s64, true>; +device_resolver_base::~device_resolver_base() +{ +} @@ -105,7 +50,7 @@ constexpr char finder_base::DUMMY_TAG[]; //------------------------------------------------- finder_base::finder_base(device_t &base, char const *tag) - : m_next(base.register_auto_finder(*this)) + : device_resolver_base(base) , m_base(base) , m_tag(tag) , m_resolved(false) @@ -114,21 +59,13 @@ finder_base::finder_base(device_t &base, char const *tag) //------------------------------------------------- -// ~finder_base - destructor -//------------------------------------------------- - -finder_base::~finder_base() -{ -} - - -//------------------------------------------------- // set_tag - set tag //------------------------------------------------- void finder_base::set_tag(char const *tag) { assert(!m_resolved); + assert(tag); m_base = m_base.get().mconfig().current_device(); m_tag = tag; } @@ -152,23 +89,13 @@ void *finder_base::find_memregion(u8 width, size_t &length, bool required) const if (region->bytewidth() != width) { if (required) - osd_printf_warning("Region '%s' found but is width %d, not %d as requested\n", m_tag, region->bitwidth(), width*8); - length = 0; - return nullptr; - } - - // check the length and warn if other than specified - size_t const length_found = region->bytes() / width; - if (length != 0 && length != length_found) - { - if (required) - osd_printf_warning("Region '%s' found but has %d bytes, not %ld as requested\n", m_tag, region->bytes(), long(length*width)); + osd_printf_warning("Region '%s' found but is width %d, not %d as requested\n", m_tag, region->bitwidth(), width * 8); length = 0; return nullptr; } // return results - length = length_found; + length = region->bytes() / width; return region->base(); } @@ -177,14 +104,14 @@ void *finder_base::find_memregion(u8 width, size_t &length, bool required) const // validate_memregion - find memory region //------------------------------------------------- -bool finder_base::validate_memregion(size_t bytes, bool required) const +bool finder_base::validate_memregion(bool required) const { // make sure we can resolve the full path to the region size_t bytes_found = 0; std::string const region_fulltag(m_base.get().subtag(m_tag)); // look for the region - for (device_t const &dev : device_iterator(m_base.get().mconfig().root_device())) + for (device_t const &dev : device_enumerator(m_base.get().mconfig().root_device())) { for (romload::region const ®ion : romload::entries(dev.rom_region()).get_regions()) { @@ -198,13 +125,6 @@ bool finder_base::validate_memregion(size_t bytes, bool required) const break; } - // check the length and warn if other than specified - if ((bytes_found != 0) && (bytes != 0) && (bytes != bytes_found)) - { - osd_printf_warning("Region '%s' found but has %ld bytes, not %ld as requested\n", m_tag, long(bytes_found), long(bytes)); - bytes_found = 0; - } - return report_missing(bytes_found != 0, "memory region", required); } @@ -223,8 +143,7 @@ void *finder_base::find_memshare(u8 width, size_t &bytes, bool required) const // check the width and warn if not correct if (width != 0 && share->bitwidth() != width) { - if (required) - osd_printf_warning("Shared ptr '%s' found but is width %d, not %d as requested\n", m_tag, share->bitwidth(), width); + osd_printf_warning("Shared ptr '%s' found but is width %d, not %d as requested\n", m_tag, share->bitwidth(), width); return nullptr; } @@ -264,8 +183,7 @@ address_space *finder_base::find_addrspace(int spacenum, u8 width, bool required address_space &space(memory->space(spacenum)); if (width != 0 && width != space.data_width()) { - if (required) - osd_printf_warning("Device '%s' found but address space #%d has the wrong data width (expected %d, found %d)\n", m_tag, spacenum, width, space.data_width()); + osd_printf_warning("Device '%s' found but address space #%d has the wrong data width (expected %d, found %d)\n", m_tag, spacenum, width, space.data_width()); return nullptr; } @@ -282,7 +200,7 @@ bool finder_base::validate_addrspace(int spacenum, u8 width, bool required) cons { // look up the device and return false if not found device_t *const device(m_base.get().subdevice(m_tag)); - if (device == nullptr) + if (!device) return report_missing(false, "address space", required); // check for memory interface and a configuration for the designated space @@ -329,22 +247,277 @@ bool finder_base::report_missing(bool found, const char *objname, bool required) // otherwise, report std::string const region_fulltag(m_base.get().subtag(m_tag)); if (required) - osd_printf_error("Required %s '%s' not found\n", objname, region_fulltag.c_str()); + osd_printf_error("Required %s '%s' not found\n", objname, region_fulltag); else if (DUMMY_TAG != m_tag) - osd_printf_verbose("Optional %s '%s' not found\n", objname, region_fulltag.c_str()); + osd_printf_verbose("Optional %s '%s' not found\n", objname, region_fulltag); return !required; } } -void finder_base::printf_warning(const char *format, ...) + +//************************************************************************** +// MEMORY REGION FINDER +//************************************************************************** + +template <bool Required> +memory_region_finder<Required>::memory_region_finder(device_t &base, char const *tag) + : object_finder_base<memory_region, Required>(base, tag) +{ +} + + +template <bool Required> +bool memory_region_finder<Required>::findit(validity_checker *valid) +{ + if (valid) + return this->validate_memregion(Required); + + assert(!this->m_resolved); + this->m_resolved = true; + this->m_target = this->m_base.get().memregion(this->m_tag); + return this->report_missing("memory region"); +} + + + +//************************************************************************** +// MEMORY BANK FINDER +//************************************************************************** + +template <bool Required> +memory_bank_finder<Required>::memory_bank_finder(device_t &base, char const *tag) + : object_finder_base<memory_bank, Required>(base, tag) +{ +} + + +template <bool Required> +bool memory_bank_finder<Required>::findit(validity_checker *valid) { - va_list argptr; - char buffer[1024]; - - // do the output - va_start(argptr, format); - vsnprintf(buffer, 1024, format, argptr); - osd_printf_warning("%s", buffer); - va_end(argptr); + if (valid) + return true; + + assert(!this->m_resolved); + this->m_resolved = true; + this->m_target = this->m_base.get().membank(this->m_tag); + return this->report_missing("memory bank"); } + + + +//************************************************************************** +// I/O PORT FINDER +//************************************************************************** + +template <bool Required> +ioport_finder<Required>::ioport_finder(device_t &base, char const *tag) + : object_finder_base<ioport_port, Required>(base, tag) +{ +} + + +template <bool Required> +bool ioport_finder<Required>::findit(validity_checker *valid) +{ + if (valid) + return finder_base::report_missing(!valid->ioport_missing(this->m_base.get().subtag(this->m_tag).c_str()), "I/O port", Required); + + assert(!this->m_resolved); + this->m_resolved = true; + this->m_target = this->m_base.get().ioport(this->m_tag); + return this->report_missing("I/O port"); +} + + + +//************************************************************************** +// ADDRESS SPACE FINDER +//************************************************************************** + +template <bool Required> +address_space_finder<Required>::address_space_finder(device_t &base, char const *tag, int spacenum, u8 width) + : object_finder_base<address_space, Required>(base, tag) + , m_spacenum(spacenum) + , m_data_width(width) +{ +} + + +template <bool Required> +bool address_space_finder<Required>::findit(validity_checker *valid) +{ + if (valid) + return this->validate_addrspace(this->m_spacenum, this->m_data_width, Required); + + assert(!this->m_resolved); + this->m_resolved = true; + this->m_target = this->find_addrspace(this->m_spacenum, this->m_data_width, Required); + return this->report_missing("address space"); +} + + + +//************************************************************************** +// MEMORY BANK CREATOR +//************************************************************************** + +bool memory_bank_creator::findit(validity_checker *valid) +{ + if (valid) + return true; + + device_t &dev = m_base.get(); + memory_manager &manager = dev.machine().memory(); + std::string const tag = dev.subtag(m_tag); + memory_bank *const bank = manager.bank_find(tag); + m_target = bank ? bank : manager.bank_alloc(dev, tag); + return true; +} + + +void memory_bank_creator::end_configuration() +{ + m_target = nullptr; +} + + + +//************************************************************************** +// MEMORY SHARE CREATOR +//************************************************************************** + +template <typename PointerType> +memory_share_creator<PointerType>::memory_share_creator(device_t &base, char const *tag, size_t bytes, endianness_t endianness) + : finder_base(base, tag) + , m_width(sizeof(PointerType) * 8) + , m_bytes(bytes) + , m_endianness(endianness) +{ +} + + +template <typename PointerType> +bool memory_share_creator<PointerType>::findit(validity_checker *valid) +{ + if (valid) + return true; + + device_t &dev = m_base.get(); + memory_manager &manager = dev.machine().memory(); + std::string const tag = dev.subtag(m_tag); + memory_share *const share = manager.share_find(tag); + if (share) + { + std::string const result = share->compare(m_width, m_bytes, m_endianness); + if (!result.empty()) + { + osd_printf_error("%s\n", result); + return false; + } + m_target = share; + } + else + { + m_target = manager.share_alloc(dev, tag, m_width, m_bytes, m_endianness); + } + return true; +} + + +template <typename PointerType> +void memory_share_creator<PointerType>::end_configuration() +{ + m_target = nullptr; +} + + + +//************************************************************************** +// EXPLICIT TEMPLATE INSTANTIATIONS +//************************************************************************** + +template class object_finder_base<memory_region, false>; +template class object_finder_base<memory_region, true>; +template class object_finder_base<memory_bank, false>; +template class object_finder_base<memory_bank, true>; +template class object_finder_base<ioport_port, false>; +template class object_finder_base<ioport_port, true>; +template class object_finder_base<address_space, false>; +template class object_finder_base<address_space, true>; + +template class object_finder_base<u8, false>; +template class object_finder_base<u8, true>; +template class object_finder_base<u16, false>; +template class object_finder_base<u16, true>; +template class object_finder_base<u32, false>; +template class object_finder_base<u32, true>; +template class object_finder_base<u64, false>; +template class object_finder_base<u64, true>; + +template class object_finder_base<s8, false>; +template class object_finder_base<s8, true>; +template class object_finder_base<s16, false>; +template class object_finder_base<s16, true>; +template class object_finder_base<s32, false>; +template class object_finder_base<s32, true>; +template class object_finder_base<s64, false>; +template class object_finder_base<s64, true>; + +template class memory_region_finder<false>; +template class memory_region_finder<true>; + +template class memory_bank_finder<false>; +template class memory_bank_finder<true>; + +template class ioport_finder<false>; +template class ioport_finder<true>; + +template class address_space_finder<false>; +template class address_space_finder<true>; + +template class region_ptr_finder<u8, false>; +template class region_ptr_finder<u8, true>; +template class region_ptr_finder<u16, false>; +template class region_ptr_finder<u16, true>; +template class region_ptr_finder<u32, false>; +template class region_ptr_finder<u32, true>; +template class region_ptr_finder<u64, false>; +template class region_ptr_finder<u64, true>; + +template class region_ptr_finder<s8, false>; +template class region_ptr_finder<s8, true>; +template class region_ptr_finder<s16, false>; +template class region_ptr_finder<s16, true>; +template class region_ptr_finder<s32, false>; +template class region_ptr_finder<s32, true>; +template class region_ptr_finder<s64, false>; +template class region_ptr_finder<s64, true>; + +template class shared_ptr_finder<u8, false>; +template class shared_ptr_finder<u8, true>; +template class shared_ptr_finder<u16, false>; +template class shared_ptr_finder<u16, true>; +template class shared_ptr_finder<u32, false>; +template class shared_ptr_finder<u32, true>; +template class shared_ptr_finder<u64, false>; +template class shared_ptr_finder<u64, true>; + +template class shared_ptr_finder<s8, false>; +template class shared_ptr_finder<s8, true>; +template class shared_ptr_finder<s16, false>; +template class shared_ptr_finder<s16, true>; +template class shared_ptr_finder<s32, false>; +template class shared_ptr_finder<s32, true>; +template class shared_ptr_finder<s64, false>; +template class shared_ptr_finder<s64, true>; + +template class memory_share_creator<u8>; +template class memory_share_creator<u16>; +template class memory_share_creator<u32>; +template class memory_share_creator<u64>; + +template class memory_share_creator<s8>; +template class memory_share_creator<s16>; +template class memory_share_creator<s32>; +template class memory_share_creator<s64>; diff --git a/src/emu/devfind.h b/src/emu/devfind.h index 2428e1a7325..81ebb9f9812 100644 --- a/src/emu/devfind.h +++ b/src/emu/devfind.h @@ -1,13 +1,9 @@ // license:BSD-3-Clause // copyright-holders:Aaron Giles, Vas Crabb -/** - * \file devfind.h - * Object auto-discovery helpers - * \defgroup devfind - * \{ - * Object auto-discovery helpers - */ - +/// \file +/// \brief Object auto-discovery helpers +/// \defgroup devfind Object auto-discovery helpers +/// \{ #ifndef __EMU_H__ #error Dont include this file directly; include emu.h instead. #endif @@ -19,11 +15,13 @@ #include <functional> #include <iterator> +#include <memory> #include <stdexcept> #include <string> #include <type_traits> #include <utility> + //************************************************************************** // TYPE DEFINITIONS //************************************************************************** @@ -100,8 +98,8 @@ public: /// int argument. /// \param [in] start Number to add to element index when /// calculating values for string format argument. - /// \arg [in] Optional additional constructor argument(s) passed to - /// all elements. + /// \param [in] arg Optional additional constructor argument(s) passed + /// to all elements. /// \sa util::string_format template <typename F, typename... Param> object_array_finder(device_t &base, F const &fmt, unsigned start, Param const &... arg) @@ -117,8 +115,8 @@ public: /// \param [in] tags Tags to search for, e.g. { "player", "dips" }. /// The tags are not copied, it is the caller's responsibility to /// ensure the pointers remain valid until resolution time. - /// \arg [in] Optional additional constructor argument(s) passed to - /// all elements. + /// \param [in] arg Optional additional constructor argument(s) passed + /// to all elements. template <typename... Param> object_array_finder(device_t &base, std::array<char const *, Count> const &tags, Param const &... arg) : object_array_finder(base, tags, std::make_integer_sequence<unsigned, Count>(), arg...) @@ -147,7 +145,7 @@ public: /// \brief Get constant iterator beyond last element /// - /// Returns aconstant iterator one past the last element in the + /// Returns a constant iterator one past the last element in the /// array. /// \return Constant iterator one past last element. const_iterator cend() const { return m_array + Count; } @@ -167,7 +165,7 @@ public: /// \brief Does array have no elements /// - /// Returns whether the arary has no elements (compile-time + /// Returns whether the array has no elements (compile-time /// constant). /// \return True if the array has no elements, false otherwise. constexpr bool empty() const { return !Count; } @@ -200,7 +198,7 @@ public: /// \return Reference to element at specified index. T &operator[](unsigned index) { assert(index < Count); return m_array[index]; } - /// \brief Checked element accesor + /// \brief Checked element accessor /// /// Returns a reference to the element at the supplied index if less /// than the size of the array, or throws std::out_of_range @@ -218,33 +216,34 @@ public: /// Abstract non-template base class for object auto-discovery helpers. /// Provides the interface that the device_t uses to manage discovery at /// resolution time. -class finder_base +class device_resolver_base { public: /// \brief Destructor /// /// Destruction via base class pointer and dynamic type behaviour /// are allowed. - virtual ~finder_base(); + virtual ~device_resolver_base(); /// \brief Get next registered object discovery helper /// /// Implementation of basic single-linked list behaviour. /// \return Pointer to the next registered object discovery helper, /// or nullptr if this is the last. - finder_base *next() const { return m_next; } + device_resolver_base *next() const noexcept { return m_next; } /// \brief Attempt discovery /// /// Concrete derived classes must implement this member function. - /// Should return false if the the object is required but not found, - /// or true otherwise (the report_missing member function can assist + /// Should return false if the object is required but not found, or + /// true otherwise (the report_missing member function can assist /// in implementing this behaviour). - /// \param [in] isvalidation Pass true if this is a dry run (i.e. no - /// intention to actually start the device), or false otherwise. + /// \param [in] valid Pass a pointer to the validity checker if this + /// is a dry run (i.e. no intention to actually start the device), + /// or nullptr otherwise. /// \return False if the object is required but not found, or true /// otherwise. - virtual bool findit(bool isvalidation = false) = 0; + virtual bool findit(validity_checker *valid) = 0; /// \brief Clear temporary binding from configuration /// @@ -255,6 +254,32 @@ public: /// subsequently removes or replaces devices. virtual void end_configuration() = 0; +protected: + /// \brief Designated constructor + /// + /// Construct base object discovery helper and register with device + /// to be invoked at resolution time. + /// \param [in] base Device to register with for resolution. + device_resolver_base(device_t &base); + + + /// \brief Pointer to next registered discovery helper + /// + /// This is a polymorphic class, so it can't be held in a standard + /// list container that requires elements of the same type. Hence + /// it implements basic intrusive single-linked list behaviour. + device_resolver_base *const m_next; +}; + + +/// \brief Base class for object discovery helpers +/// +/// Abstract non-template base class for object auto-discovery helpers. +/// Provides functionality for specifying a target as a base device and +/// relative tag. Also supplies helpers for looking up resources. +class finder_base : protected device_resolver_base +{ +public: /// \brief Get search tag /// /// Returns the search tag. @@ -281,6 +306,7 @@ public: void set_tag(device_t &base, char const *tag) { assert(!m_resolved); + assert(tag); m_base = base; m_tag = tag; } @@ -309,14 +335,16 @@ public: } /// \brief Dummy tag always treated as not found - constexpr static char DUMMY_TAG[17] = "finder_dummy_tag"; + static constexpr char DUMMY_TAG[17] = "finder_dummy_tag"; protected: /// \brief Designated constructor /// /// Construct base object discovery helper and register with device /// to be invoked at resolution time. - /// \param [in] base Base device to search from. + /// \param [in] base Device to register with for resolution. Also + /// sets initial base device to search from. The tag must be + /// specified relative to this device. /// \param [in] tag Object tag to search for. This is not copied, /// it is the caller's responsibility to ensure this pointer /// remains valid until resolution time. @@ -351,13 +379,11 @@ protected: /// report_missing to print an error message if the region is /// not found. Returns true if the region is required but no /// matching region is found, or false otherwise. - /// \param [in] bytes Desired region length in bytes, or 0U to match - /// any length. /// \param [in] required True if the region is required, or false if /// it is optional. /// \return True if the region is optional, or if the region is /// required and a matching region is found, or false otherwise. - bool validate_memregion(size_t bytes, bool required) const; + bool validate_memregion(bool required) const; /// \brief Find a memory share /// @@ -368,9 +394,9 @@ protected: /// with the requested tag is found, but it doesn't match the /// desired width. /// \param [in] width Desired memory share width in bits. - /// \param [out] bytes Set to memoyr share length in bytes if a + /// \param [out] bytes Set to memory share length in bytes if a /// matching memory share is found, otherwise left unchanged. - /// \param [in] required. Whether warning message should be printed + /// \param [in] required Whether warning message should be printed /// if a memory share with matching tag of incorrect width is /// found. /// \return Pointer to base of memory share if a matching memory @@ -387,7 +413,7 @@ protected: /// or a space with the designated number. /// \param [in] spacenum Address space number. /// \param [in] width Specific data width, or 0. - /// \param [in] required. Whether warning message should be printed + /// \param [in] required Whether warning message should be printed /// if a device with no memory interface or space of that number /// is found. /// \return Pointer to address space if a matching address space @@ -400,7 +426,7 @@ protected: /// found, or false otherwise. /// \param [in] spacenum Address space number. /// \param [in] width Specific data width, or 0. - /// \param [in] required. Whether warning message should be printed + /// \param [in] required Whether warning message should be printed /// if a device with no memory interface or space of that number /// is found. /// \return True if the space is optional, or if the space is @@ -421,21 +447,6 @@ protected: /// \return True if found or not required, false otherwise. bool report_missing(bool found, char const *objname, bool required) const; - /// \brief Print a message at warning level - /// - /// Prints a message if logging is enabled at warning level or more - /// detailed. Uses printf semantics of the C runtime library. - /// \param [in] format Format string as used by printf function in - /// runtime library - void printf_warning(char const *format, ...) ATTR_PRINTF(2,3); - - - /// \brief Pointer to next registered discovery helper - /// - /// This is a polymorphic class, so it can't be held in a standardlist - /// container that requires elements of the same type. Hence it - /// implements basic single-linked list behaviour. - finder_base *const m_next; /// \brief Base device to search from std::reference_wrapper<device_t> m_base; @@ -457,7 +468,7 @@ protected: /// Assumes that non-null pointer is found, and null pointer is not /// found. template <class ObjectClass, bool Required> -class object_finder_base : public finder_base +class object_finder_common_base : public finder_base { public: /// \brief Clear temporary binding from configuration @@ -469,16 +480,10 @@ public: virtual void end_configuration() override { assert(!m_resolved); m_target = nullptr; } /// \brief Get pointer to target object + /// /// \return Pointer to target object if found, or nullptr otherwise. ObjectClass *target() const { return m_target; } - /// \brief Return whether target has been found - /// - /// Works on the assumption that the target object pointer will be - /// non-null if the target has been found, and null otherwise. - /// \return True if object has been found, or false otherwise. - bool found() const { return m_target != nullptr; } - /// \brief Cast-to-pointer operator /// /// Allows implicit casting to a pointer to the target object. @@ -492,7 +497,7 @@ public: /// Allows pointer-member-style access to members of the target /// object. Asserts that the target object has been found. /// \return Pointer to target object if found, or nullptr otherwise. - virtual ObjectClass *operator->() const { assert(m_target); return m_target; } + ObjectClass *operator->() const { assert(m_target); return m_target; } protected: /// \brief Designated constructor @@ -504,7 +509,7 @@ protected: /// \param [in] tag Object tag to search for. This is not copied, /// it is the caller's responsibility to ensure this pointer /// remains valid until resolution time. - object_finder_base(device_t &base, const char *tag) : finder_base(base, tag) { } + object_finder_common_base(device_t &base, const char *tag) : finder_base(base, tag) { } /// \brief Log if object was not found /// @@ -514,7 +519,7 @@ protected: /// implementation for more detail. /// \param [in] objname Display name for target object type. /// \return True if found or not required, false otherwise. - bool report_missing(char const *objname) const { return finder_base::report_missing(found(), objname, Required); } + bool report_missing(char const *objname) const { return finder_base::report_missing(m_target != nullptr, objname, Required); } /// \brief Pointer to target object /// @@ -525,6 +530,48 @@ protected: }; +/// \brief Base class for object discovery helpers +/// +/// Allows partial specialisations to add members based on object class +/// and/or whether the object is required. Template arguments are the +/// type of object to discover, and whether failure to find the object +/// is considered an error. +template <class ObjectClass, bool Required> +class object_finder_base : public object_finder_common_base<ObjectClass, Required> +{ + using object_finder_common_base<ObjectClass, Required>::object_finder_common_base; +}; + + +/// \brief Base class for optional object discovery helpers +/// +/// Adds helpers for to test whether the object was found, and by +/// extension whether it's safe to dereference. Template argument is +/// the type of object to discover. +template <class ObjectClass> +class object_finder_base<ObjectClass, false> : public object_finder_common_base<ObjectClass, false> +{ +public: + /// \brief Return whether target has been found + /// + /// Works on the assumption that the target object pointer will + /// be non-null if the target has been found, and null + /// otherwise. + /// \return True if object has been found, or false otherwise. + bool found() const { return this->m_target != nullptr; } + + /// \brief Cast-to-Boolean operator + /// + /// Allows truth tests to test whether it's safe to dereference + /// the object, similar to pointers and various C++ standard + /// library objects. + /// \return True if safe to dereference, or false otherwise. + explicit operator bool() const { return this->m_target != nullptr; } + + using object_finder_common_base<ObjectClass, false>::object_finder_common_base; +}; + + /// \brief Device finder template /// /// Template arguments are the device class to find, and whether the @@ -575,6 +622,12 @@ public: return device; } + /// \brief Look up target during configuration + /// Look up the current target device during configuration, before + /// resolution. + /// \return Pointer to target device if found, or nullptr otherwise. + DeviceClass *lookup() const { return this->m_base.get().template subdevice<DeviceClass>(this->m_tag); } + private: /// \brief Check that device implementation has expected tag /// \param [in] device Reference to device. @@ -602,13 +655,14 @@ private: /// with the requested tag is found but the type is incorrect, a /// warning message will be printed. This method is called by the /// base device at resolution time. - /// \param [in] isvalidation True if this is a dry run (not - /// intending to run the machine, just checking for errors). + /// \param [in] valid Pass a pointer to the validity checker if this + /// is a dry run (i.e. no intention to actually start the device), + /// or nullptr otherwise. /// \return True if the device is optional or if a matching device /// is found, false otherwise. - virtual bool findit(bool isvalidation) override + virtual bool findit(validity_checker *valid) override { - if (!isvalidation) + if (!valid) { assert(!this->m_resolved); this->m_resolved = true; @@ -617,7 +671,7 @@ private: device_t *const device = this->m_base.get().subdevice(this->m_tag); this->m_target = dynamic_cast<DeviceClass *>(device); if (device && !this->m_target) - this->printf_warning("Device '%s' found but is of incorrect type (actual type is %s)\n", this->m_tag, device->name()); + osd_printf_warning("Device '%s' found but is of incorrect type (actual type is %s)\n", this->m_tag, device->name()); return this->report_missing("device"); } @@ -625,7 +679,7 @@ private: /// \brief Optional device finder /// -/// Finds device with maching type and tag. If a device with matching +/// Finds device with matching type and tag. If a device with matching /// tag is found but the type does not match, a message is printed at /// warning level. No error is generated if a matching device is not /// found (the target object pointer will be null). If you have a @@ -636,7 +690,7 @@ template <class DeviceClass> using optional_device = device_finder<DeviceClass, /// \brief Required device finder /// -/// Finds device with maching type and tag. If a device with matching +/// Finds device with matching type and tag. If a device with matching /// tag is found but the type does not match, a message is printed at /// warning level. A validation error is generated if a matching device /// is not found. If you have a number of similar required devices, @@ -665,7 +719,7 @@ public: /// \param [in] tag Memory region tag to search for. This is not /// copied, it is the caller's responsibility to ensure this /// pointer remains valid until resolution time. - memory_region_finder(device_t &base, char const *tag) : object_finder_base<memory_region, Required>(base, tag) { } + memory_region_finder(device_t &base, char const *tag); private: /// \brief Find memory region @@ -673,25 +727,17 @@ private: /// Find memory region with requested tag. For a dry run, the /// target object pointer will not be set. This method is called by /// the base device at resolution time. - /// \param [in] isvalidation True if this is a dry run (not - /// intending to run the machine, just checking for errors). + /// \param [in] valid Pass a pointer to the validity checker if this + /// is a dry run (i.e. no intention to actually start the device), + /// or nullptr otherwise. /// \return True if the memory region is optional or if a matching /// memory region is found, false otherwise. - virtual bool findit(bool isvalidation) override - { - if (isvalidation) - return this->validate_memregion(0, Required); - - assert(!this->m_resolved); - this->m_resolved = true; - this->m_target = this->m_base.get().memregion(this->m_tag); - return this->report_missing("memory region"); - } + virtual bool findit(validity_checker *valid) override; }; /// \brief Optional memory region finder /// -/// Finds memory region with maching tag. No error is generated if a +/// Finds memory region with matching tag. No error is generated if a /// matching memory region is not found (the target object pointer will /// be null). If you have a number of similar optional memory regions, /// consider using optional_memory_region_array. @@ -701,7 +747,7 @@ using optional_memory_region = memory_region_finder<false>; /// \brief Required memory region finder /// -/// Finds memory region with maching tag. A validation error is +/// Finds memory region with matching tag. A validation error is /// generated if a matching memory region is not found. If you have a /// number of similar required memory regions, consider using /// required_memory_region_array. @@ -730,32 +776,24 @@ public: /// \param [in] tag Memory bank tag to search for. This is not /// copied, it is the caller's responsibility to ensure this /// pointer remains valid until resolution time. - memory_bank_finder(device_t &base, char const *tag) : object_finder_base<memory_bank, Required>(base, tag) { } + memory_bank_finder(device_t &base, char const *tag); /// \brief Find memory bank /// /// Find memory bank with requested tag. Just returns true for a /// dry run. This method is called by the base device at resolution /// time. - /// \param [in] isvalidation True if this is a dry run (not - /// intending to run the machine, just checking for errors). + /// \param [in] valid Pass a pointer to the validity checker if this + /// is a dry run (i.e. no intention to actually start the device), + /// or nullptr otherwise. /// \return True if the memory bank is optional, a matching memory /// bank is found or this is a dry run, false otherwise. - virtual bool findit(bool isvalidation) override - { - if (isvalidation) - return true; - - assert(!this->m_resolved); - this->m_resolved = true; - this->m_target = this->m_base.get().membank(this->m_tag); - return this->report_missing("memory bank"); - } + virtual bool findit(validity_checker *valid) override; }; /// \brief Optional memory bank finder /// -/// Finds memory bank with maching tag. No error is generated if a +/// Finds memory bank with matching tag. No error is generated if a /// matching memory bank is not found (the target object pointer will /// be null). If you have a number of similar optional memory banks, /// consider using optional_memory_bank_array. @@ -765,7 +803,7 @@ using optional_memory_bank = memory_bank_finder<false>; /// \brief Required memory bank finder /// -/// Finds memory bank with maching tag. A validation error is +/// Finds memory bank with matching tag. A validation error is /// generated if a matching memory bank is not found. If you have a /// number of similar required memory banks, consider using /// required_memory_bank_array. @@ -778,6 +816,90 @@ template <unsigned Count> using optional_memory_bank_array = memory_bank_array_f template <unsigned Count> using required_memory_bank_array = memory_bank_array_finder<Count, true>; +/// \brief Memory bank creator +/// +/// Creates a memory bank or finds an existing one instantiated via an +/// address map. +class memory_bank_creator : public finder_base +{ +public: + /// \brief Memory bank creator constructor + /// \param [in] base Base device to search from. + /// \param [in] tag Memory bank tag to search for or create. This + /// is not copied, it is the caller's responsibility to ensure + /// this pointer remains valid until resolution time. + memory_bank_creator(device_t &base, char const *tag) : finder_base(base, tag) { } + + /// \brief Get pointer to memory bank object + /// \return Pointer to found or created bank object. + memory_bank *target() const { return m_target; } + + /// \brief Cast-to-pointer operator + /// + /// Allows implicit casting to a pointer to the target + /// memory bank object. + /// \return Pointer to found or created memory bank object. + operator memory_bank *() const { return m_target; } + + /// \brief Pointer member access operator + /// + /// Allows pointer-member-style access to members of the target + /// memory bank object. Asserts that the target has been found. + /// \return Pointer to found or created bank object. + memory_bank *operator->() const { assert(m_target); return m_target; } + +protected: + virtual bool findit(validity_checker *valid) override; + virtual void end_configuration() override; + + /// \brief Pointer to target object + /// + /// Pointer to target memory bank object, or nullptr if creation has + /// not been attempted yet. + memory_bank *m_target = nullptr; +}; + +template <unsigned Count> using memory_bank_array_creator = object_array_finder<memory_bank_creator, Count>; + + +/// \brief Base class for optional I/O port finders +/// +/// Adds helpers for to test whether the I/O port was found, and to read +/// the port value or return a default if the I/O port was not found. +template <> +class object_finder_base<ioport_port, false> : public object_finder_common_base<ioport_port, false> +{ +public: + /// \brief Read I/O port if found or return default value + /// + /// If the I/O port was found, this reads a value from the I/O port + /// and returns it. If the I/O port was not found, the default + /// value (supplied as a parameter) is returned. + /// \param [in] defval Value to return if I/O port was not found. + /// \return Value read from I/O port if found, or supplied default + /// value otherwise. + ioport_value read_safe(ioport_value defval) { return this->m_target ? this->m_target->read() : defval; } + + /// \brief Return whether target has been found + /// + /// Works on the assumption that the target object pointer will + /// be non-null if the target has been found, and null + /// otherwise. + /// \return True if object has been found, or false otherwise. + bool found() const { return this->m_target != nullptr; } + + /// \brief Cast-to-Boolean operator + /// + /// Allows truth tests to test whether it's safe to dereference + /// the object, similar to pointers and various C++ standard + /// library objects. + /// \return True if safe to dereference, or false otherwise. + explicit operator bool() const { return this->m_target != nullptr; } + + using object_finder_common_base<ioport_port, false>::object_finder_common_base; +}; + + /// \brief I/O port finder template /// /// Template argument is whether the I/O port is required. It is a @@ -794,17 +916,7 @@ public: /// \param [in] tag I/O port tag to search for. This is not copied, /// it is the caller's responsibility to ensure this pointer /// remains valid until resolution time. - ioport_finder(device_t &base, char const *tag) : object_finder_base<ioport_port, Required>(base, tag) { } - - /// \brief Read I/O port if found or return default value - /// - /// If the I/O port was found, this reads a value from the I/O port - /// and returns it. If the I/O port was not found, the default - /// value (supplied as a parameter) is returned. - /// \param [in] defval Value to return if I/O port was not found. - /// \return Value read from I/O port if found, or supplied default - /// value otherwise. - ioport_value read_safe(ioport_value defval) { return this->m_target ? this->m_target->read() : defval; } + ioport_finder(device_t &base, char const *tag); private: /// \brief Find I/O port @@ -812,25 +924,17 @@ private: /// Find I/O port with requested tag. Just returns true for a dry /// run. This method is called by the base device at resolution /// time. - /// \param [in] isvalidation True if this is a dry run (not - /// intending to run the machine, just checking for errors). + /// \param [in] valid Pass a pointer to the validity checker if this + /// is a dry run (i.e. no intention to actually start the device), + /// or nullptr otherwise. /// \return True if the I/O port is optional, a matching I/O port is /// is found or this is a dry run, false otherwise. - virtual bool findit(bool isvalidation) override - { - if (isvalidation) - return true; - - assert(!this->m_resolved); - this->m_resolved = true; - this->m_target = this->m_base.get().ioport(this->m_tag); - return this->report_missing("I/O port"); - } + virtual bool findit(validity_checker *valid) override; }; /// \brief Optional I/O port finder /// -/// Finds I/O port with maching tag. No error is generated if a +/// Finds I/O port with matching tag. No error is generated if a /// matching I/O port is not found (the target object pointer will be /// null). If you have a number of similar optional I/O ports, consider /// using optional_ioport_array. @@ -839,9 +943,9 @@ using optional_ioport = ioport_finder<false>; /// \brief Required I/O port finder /// -/// Finds I/O port with maching tag. A validation error is generated if -/// a matching I/O port is not found. If you have a number of similar -/// required I/O ports, consider using required_ioport_array. +/// Finds I/O port with matching tag. A validation error is generated +/// if a matching I/O port is not found. If you have a number of +/// similar required I/O ports, consider using required_ioport_array. /// \sa optional_ioport required_ioport_array ioport_finder using required_ioport = ioport_finder<true>; @@ -853,9 +957,9 @@ template <unsigned Count> using required_ioport_array = ioport_array_finder<Coun /// \brief Address space finder template /// /// Template argument is whether the address space is required. It is a -/// validation error if a required address space is not found. This class is -/// generally not used directly, instead the optional_address_space and -/// required_address_space helpers are used. +/// validation error if a required address space is not found. This +/// class is generally not used directly, instead the +/// optional_address_space and required_address_space helpers are used. /// \sa optional_address_space required_address_space template <bool Required> class address_space_finder : public object_finder_base<address_space, Required> @@ -863,12 +967,13 @@ class address_space_finder : public object_finder_base<address_space, Required> public: /// \brief Address space finder constructor /// \param [in] base Base device to search from. - /// \param [in] tag Address space tag to search for. This is not copied, - /// it is the caller's responsibility to ensure this pointer - /// remains valid until resolution time. + /// \param [in] tag Address space device tag to search for. + /// This is not copied, it is the caller's responsibility to + /// ensure this pointer remains valid until resolution time. /// \param [in] spacenum Address space number. - /// \param [in] width Specific data width (optional). - address_space_finder(device_t &base, char const *tag, int spacenum, u8 width = 0) : object_finder_base<address_space, Required>(base, tag), m_spacenum(spacenum), m_data_width(width) { } + /// \param [in] width Required data width in bits, or zero if + /// any data width is acceptable. + address_space_finder(device_t &base, char const *tag, int spacenum, u8 width = 0); /// \brief Set search tag and space number /// @@ -885,9 +990,9 @@ public: /// \brief Set search tag and space number /// - /// Allows search tag to be changed after construction. Note that - /// this must be done before resolution time to take effect. Also - /// note that the tag is not copied. + /// Allows search tag and address space number to be changed after + /// construction. Note that this must be done before resolution + /// time to take effect. Also note that the tag is not copied. /// \param [in] tag Updated search tag relative to the current /// device being configured. This is not copied, it is the /// caller's responsibility to ensure this pointer remains valid @@ -897,19 +1002,30 @@ public: /// \brief Set search tag and space number /// - /// Allows search tag to be changed after construction. Note that - /// this must be done before resolution time to take effect. + /// Allows search tag and address space number to be changed after + /// construction. Note that this must be done before resolution + /// time to take effect. Also note that the tag is not copied. /// \param [in] finder Object finder to take the search base and tag /// from. /// \param [in] spacenum Address space number. void set_tag(finder_base const &finder, int spacenum) { finder_base::set_tag(finder); this->m_spacenum = spacenum; } + /// \brief Set search tag and space number + /// + /// Allows search tag and address space number to be changed after + /// construction. Note that this must be done before resolution + /// time to take effect. Also note that the tag is not copied. + /// \param [in] finder Address space finder to take the search base, + /// tag and address space number from. + template <bool R> void set_tag(address_space_finder<R> const &finder) { set_tag(finder, finder.spacenum()); } + /// \brief Set data width of space /// - /// Allows data width to be specified after construction. Note that - /// this must be done before resolution time to take effect. - /// \param [in] width Data width in bits (0 = don't care). - void set_data_width(u8 width) { this->m_data_width = width; } + /// Allows data width to be specified after construction. Note + /// that this must be done before resolution time to take effect. + /// \param [in] width Required data width in bits, or zero if any + /// data width is acceptable. + void set_data_width(u8 width) { assert(!this->m_resolved); this->m_data_width = width; } /// \brief Get space number /// @@ -923,20 +1039,12 @@ private: /// Find address space with requested tag. For a dry run, the /// target object pointer will not be set. This method is called by /// the base device at resolution time. - /// \param [in] isvalidation True if this is a dry run (not - /// intending to run the machine, just checking for errors). - /// \return True if the address space is optional, a matching address space is - /// is found or this is a dry run, false otherwise. - virtual bool findit(bool isvalidation) override - { - if (isvalidation) - return this->validate_addrspace(this->m_spacenum, this->m_data_width, Required); - - assert(!this->m_resolved); - this->m_resolved = true; - this->m_target = this->find_addrspace(this->m_spacenum, this->m_data_width, Required); - return this->report_missing("address space"); - } + /// \param [in] valid Pass a pointer to the validity checker if this + /// is a dry run (i.e. no intention to actually start the device), + /// or nullptr otherwise. + /// \return True if the address space is optional, a matching + /// address space is found or this is a dry run, false otherwise. + virtual bool findit(validity_checker *valid) override; int m_spacenum; u8 m_data_width; @@ -944,16 +1052,16 @@ private: /// \brief Optional address space finder /// -/// Finds address space with maching tag and number. No error is generated if a -/// matching address space is not found (the target object pointer will be -/// null). +/// Finds address space with matching tag and number. No error is +/// generated if a matching address space is not found (the target +/// object pointer will be null). /// \sa required_address_space address_space_finder using optional_address_space = address_space_finder<false>; /// \brief Required address space finder /// -/// Finds address space with maching tag and number. A validation error is generated if -/// a matching address space is not found. +/// Finds address space with matching tag and number. A validation +/// error is generated if a matching address space is not found. /// \sa optional_address_space address_space_finder using required_address_space = address_space_finder<true>; @@ -977,11 +1085,8 @@ public: /// \param [in] tag Memory region tag to search for. This is not /// copied, it is the caller's responsibility to ensure this /// pointer remains valid until resolution time. - /// \param [in] length Desired memory region length in units of the - /// size of the element type, or zero to match any region length. - region_ptr_finder(device_t &base, char const *tag, size_t length = 0) + region_ptr_finder(device_t &base, char const *tag) : object_finder_base<PointerType, Required>(base, tag) - , m_desired_length(length) , m_length(0) { } @@ -989,29 +1094,23 @@ public: /// \brief Array access operator /// /// Returns a non-const reference to the element of the memory - /// region at the supplied zero-based index. - /// Behaviour is undefined for negative element indices. + /// region at the supplied zero-based index. Behaviour is undefined + /// for negative element indices. /// \param [in] index Non-negative element index. /// \return Non-const reference to element at requested index. PointerType &operator[](int index) const { assert(index < m_length); return this->m_target[index]; } /// \brief Get length in units of elements + /// /// \return Length in units of elements or zero if no matching /// memory region has been found. - u32 length() const { return m_length; } + size_t length() const { return m_length; } /// \brief Get length in units of bytes + /// /// \return Length in units of bytes or zero if no matching memory /// region has been found. - u32 bytes() const { return m_length * sizeof(PointerType); } - - /// \brief Get index mask - /// - /// Returns the length in units of elements minus one, which can be - /// used as a mask for index values if the length is a power of two. - /// Result is undefined if no matching memory region has been found. - /// \return Length in units of elements minus one. - u32 mask() const { return m_length - 1; } + size_t bytes() const { return m_length * sizeof(PointerType); } private: /// \brief Find memory region base pointer @@ -1022,28 +1121,23 @@ private: /// checked - the width is not checked and the target pointer is not /// set. This method is called by the base device at resolution /// time. - /// \param [in] isvalidation True if this is a dry run (not - /// intending to run the machine, just checking for errors). + /// \param [in] valid Pass a pointer to the validity checker if this + /// is a dry run (i.e. no intention to actually start the device), + /// or nullptr otherwise. /// \return True if the memory region is optional or a matching /// memory region is found, or false otherwise. - virtual bool findit(bool isvalidation) override + virtual bool findit(validity_checker *valid) override { - if (isvalidation) - return this->validate_memregion(sizeof(PointerType) * m_desired_length, Required); + if (valid) + return this->validate_memregion(Required); assert(!this->m_resolved); this->m_resolved = true; - m_length = m_desired_length; this->m_target = reinterpret_cast<PointerType *>(this->find_memregion(sizeof(PointerType), m_length, Required)); return this->report_missing("memory region"); } - /// \brief Desired region length - /// - /// Desired region length in units of elements. - size_t const m_desired_length; - - /// \brief Matched region length + /// \brief Region length /// /// Actual length of the region that was found in units of /// elements, or zero if no matching region has been found. @@ -1052,7 +1146,7 @@ private: /// \brief Optional memory region base pointer finder /// -/// Finds base pointer of memory region with maching tag, width and +/// Finds base pointer of memory region with matching tag, width and /// length. No error is generated if a matching memory region is not /// found (the target pointer will be null). If you have a number of /// similar optional memory regions, consider using @@ -1062,7 +1156,7 @@ template <typename PointerType> using optional_region_ptr = region_ptr_finder<Po /// \brief Required memory region base pointer finder /// -/// Finds base pointer of memory region with maching tag, width and +/// Finds base pointer of memory region with matching tag, width and /// length. A validation error is generated if a matching memory region /// is not found. If you have a number of similar required memory /// regions, consider using required_region_ptr_array. @@ -1074,68 +1168,256 @@ template <typename PointerType, unsigned Count> using optional_region_ptr_array template <typename PointerType, unsigned Count> using required_region_ptr_array = region_ptr_array_finder<PointerType, Count, true>; -// ======================> shared_ptr_finder - -// shared pointer finder template +/// \brief Memory share base pointer finder +/// +/// Template arguments are the element type of the memory share and +/// whether the memory share is required. It is a validation error if a +/// required memory share is not found. This class is generally not +/// used directly, instead the optional_shared_ptr and +/// required_shared_ptr helpers are used. +/// \sa optional_shared_ptr required_shared_ptr template <typename PointerType, bool Required> class shared_ptr_finder : public object_finder_base<PointerType, Required> { public: - // construction/destruction - shared_ptr_finder(device_t &base, char const *tag, u8 width = sizeof(PointerType) * 8) + /// \brief Memory share finder constructor + /// + /// Desired width is implied by sizeof(PointerType). + /// \param [in] base Base device to search from. + /// \param [in] tag Memory share tag to search for. This is not + /// copied, it is the caller's responsibility to ensure this + /// pointer remains valid until resolution time. + shared_ptr_finder(device_t &base, char const *tag) : object_finder_base<PointerType, Required>(base, tag) - , m_width(width) , m_bytes(0) - , m_allocated(0) { } - // operators to make use transparent + /// \brief Array access operator + /// + /// Returns a non-const reference to the element of the memory + /// share at the supplied zero-based index. Behaviour is undefined + /// for negative element indices. + /// \param [in] index Non-negative element index. + /// \return Non-const reference to element at requested index. PointerType &operator[](int index) const { return this->m_target[index]; } - // getter for explicit fetching - u32 bytes() const { return m_bytes; } - u32 mask() const { return m_bytes - 1; } // FIXME: wrong when sizeof(PointerType) != 1 + /// \brief Get length in units of elements + /// + /// \return Length in units of elements or zero if no matching + /// memory region has been found. + size_t length() const { return m_bytes / sizeof(PointerType); } - // setter for setting the object - void set_target(PointerType *target, size_t bytes) { this->m_target = target; m_bytes = bytes; } + /// \brief Get length in bytes + /// + /// \return Length in bytes or zero if no matching memory share has + /// been found. + size_t bytes() const { return m_bytes; } - // dynamic allocation of a shared pointer - void allocate(u32 entries) - { - assert(m_allocated.empty()); - m_allocated.resize(entries); - this->m_target = &m_allocated[0]; - m_bytes = entries * sizeof(PointerType); - this->m_base.get().save_item(m_allocated, this->m_tag); - } + /// \brief Get iterator to first element + /// + /// Returns an iterator to the first element of the memory share. + /// \return Iterator to first element. + PointerType *begin() const { return this->m_target; } + + /// \brief Get iterator beyond last element + /// + /// Returns an iterator one past the last element of the memory + /// share. + /// \return Iterator one past last element. + PointerType *end() const { return this->m_target + length(); } + + /// \brief Get constant iterator to first element + /// + /// Returns a constant iterator to the first element of the memory + /// share. + /// \return Constant iterator to first element. + PointerType const *cbegin() const { return this->m_target; } + + /// \brief Get constant iterator beyond last element + /// + /// Returns a constant iterator one past the last element of the + /// memory share. + /// \return Constant iterator one past last element. + PointerType const *cend() const { return this->m_target + length(); } private: - // finder - virtual bool findit(bool isvalidation) override + /// \brief Find memory share base pointer + /// + /// Find base pointer of memory share with with requested tag. + /// Width of memory share is checked against sizeof(PointerType). + /// This method is called by the base device at resolution time. + /// \param [in] valid Pass a pointer to the validity checker if this + /// is a dry run (i.e. no intention to actually start the device), + /// or nullptr otherwise. + /// \return True if the memory share is optional or a matching + /// memory share is found, or false otherwise. + virtual bool findit(validity_checker *valid) override { - if (isvalidation) + if (valid) return true; assert(!this->m_resolved); this->m_resolved = true; - this->m_target = reinterpret_cast<PointerType *>(this->find_memshare(m_width, m_bytes, Required)); + this->m_target = reinterpret_cast<PointerType *>(this->find_memshare(sizeof(PointerType)*8, m_bytes, Required)); return this->report_missing("shared pointer"); } - // internal state - u8 const m_width; + /// \brief Memory share length + /// + /// Actual length of the memory share that was found in bytes, or + /// zero if no matching region has been found. size_t m_bytes; - std::vector<PointerType> m_allocated; }; +/// \brief Optional memory share base pointer finder +/// +/// Finds base pointer of memory share with matching tag and width. No +/// error is generated if a matching memory share is not found (the +/// target pointer will be null). If you have a number of similar +/// optional memory regions, consider using optional_shared_ptr_array. +/// \sa required_shared_ptr optional_shared_ptr_array shared_ptr_finder template <typename PointerType> using optional_shared_ptr = shared_ptr_finder<PointerType, false>; + +/// \brief Required memory share base pointer finder +/// +/// Finds base pointer of memory share with matching tag and width. A +/// validation error is generated if a matching memory share is not +/// found. If you have a number of similar required memory shares, +/// consider using required_shared_ptr_array. +/// \sa optional_shared_ptr required_shared_ptr_array shared_ptr_finder template <typename PointerType> using required_shared_ptr = shared_ptr_finder<PointerType, true>; + template <typename PointerType, unsigned Count, bool Required> using shared_ptr_array_finder = object_array_finder<shared_ptr_finder<PointerType, Required>, Count>; template <typename PointerType, unsigned Count> using optional_shared_ptr_array = shared_ptr_array_finder<PointerType, Count, false>; template <typename PointerType, unsigned Count> using required_shared_ptr_array = shared_ptr_array_finder<PointerType, Count, true>; +/// \brief Memory share creator template +/// +/// Creates a memory share or finds an existing one instantiated via an +/// address map. Template argument is the element type of the memory +/// share. If an existing memory share is found, it is an error if it +/// doesn't match the requested width, length and endianness. +template <typename PointerType> +class memory_share_creator : public finder_base +{ +public: + /// \brief Memory share creator constructor + /// + /// Desired width is implied by sizeof(PointerType). + /// \param [in] base Base device to search from. + /// \param [in] tag Memory share tag to search for or create. This + /// is not copied, it is the caller's responsibility to ensure + /// this pointer remains valid until resolution time. + /// \param [in] bytes Desired memory share length in bytes. + /// \param [in] endianness Desired endianness of the memory share. + memory_share_creator(device_t &base, char const *tag, size_t bytes, endianness_t endianness); + + /// \brief Get target memory share base pointer + /// + /// Must not be called before creation is attempted. + /// \return Base pointer of found or created memory share. + PointerType *target() const { return reinterpret_cast<PointerType *>(m_target->ptr()); } + + /// \brief Cast-to-pointer operator + /// + /// Allows implicit casting to the base pointer of the target memory + /// share. Must not be called before creation is attempted. + /// \return Base pointer of found or created memory share. + operator PointerType *() const { return target(); } + + /// \brief Array access operator + /// + /// Returns a non-const reference to the element of the memory + /// share at the supplied zero-based index. Behaviour is undefined + /// for negative element indices. Must not be called before + /// creation is attempted. + /// \param [in] index Non-negative element index. + /// \return Non-const reference to element at requested index. + PointerType &operator[](int index) const { return target()[index]; } + + /// \brief Get length in units of elements + /// + /// Must not be called before creation is attempted. + /// \return Length of memory share in units of elements. + size_t length() const { return m_target->bytes() / sizeof(PointerType); } + + /// \brief Get length in bytes + /// + /// Must not be called before creation is attempted. + /// \return Length of memory share in bytes. + size_t bytes() const { return m_target->bytes(); } + + /// \brief Get endianness + /// + /// Must not be called before creation is attempted. + /// \return Endianness of memory share. + endianness_t endianness() const { return m_target->endianness(); } + + /// \brief Get width in bits + /// + /// Must not be called before creation is attempted. + /// \return Memory share width in bits. + u8 bitwidth() const { return m_target->bitwidth(); } + + /// \brief Get width in bytes + /// + /// Must not be called before creation is attempted. + /// \return Memory share width in bytes. + u8 bytewidth() const { return m_target->bytewidth(); } + + /// \brief Get iterator to first element + /// + /// Returns an iterator to the first element of the memory share. + /// Must not be called before creation is attempted. + /// \return Iterator to first element. + PointerType *begin() const { return target(); } + + /// \brief Get iterator beyond last element + /// + /// Returns an iterator one past the last element of the memory + /// share. Must not be called before creation is attempted. + /// \return Iterator one past last element. + PointerType *end() const { return target() + length(); } + + /// \brief Get constant iterator to first element + /// + /// Returns a constant iterator to the first element of the memory + /// share. Must not be called before creation is attempted. + /// \return Constant iterator to first element. + PointerType const *cbegin() const { return target(); } + + /// \brief Get constant iterator beyond last element + /// + /// Returns a constant iterator one past the last element of the + /// memory share. Must not be called before creation is attempted. + /// \return Constant iterator one past last element. + PointerType const *cend() const { return target() + length(); } + +protected: + virtual bool findit(validity_checker *valid) override; + virtual void end_configuration() override; + + /// \brief Pointer to target object + /// + /// Pointer to target memory share object, or nullptr if creation + /// has not been attempted yet. + memory_share *m_target = nullptr; + + /// \brief Requested memory share width in bits + u8 const m_width; + + /// \brief Requested memory share length in bytes + size_t const m_bytes; + + /// \brief Requested memory share endianness + endianness_t const m_endianness; +}; + +template <typename PointerType, unsigned Count> using memory_share_array_creator = object_array_finder<memory_share_creator<PointerType>, Count>; + + //************************************************************************** // EXTERNAL TEMPLATE INSTANTIATIONS @@ -1175,6 +1457,9 @@ extern template class memory_bank_finder<true>; extern template class ioport_finder<false>; extern template class ioport_finder<true>; +extern template class address_space_finder<false>; +extern template class address_space_finder<true>; + extern template class region_ptr_finder<u8, false>; extern template class region_ptr_finder<u8, true>; extern template class region_ptr_finder<u16, false>; @@ -1211,5 +1496,15 @@ extern template class shared_ptr_finder<s32, true>; extern template class shared_ptr_finder<s64, false>; extern template class shared_ptr_finder<s64, true>; +extern template class memory_share_creator<u8>; +extern template class memory_share_creator<u16>; +extern template class memory_share_creator<u32>; +extern template class memory_share_creator<u64>; + +extern template class memory_share_creator<s8>; +extern template class memory_share_creator<s16>; +extern template class memory_share_creator<s32>; +extern template class memory_share_creator<s64>; + #endif // MAME_EMU_DEVFIND_H -/** \} */ +/// \} diff --git a/src/emu/device.cpp b/src/emu/device.cpp index 42edf893168..272df837cd4 100644 --- a/src/emu/device.cpp +++ b/src/emu/device.cpp @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles /*************************************************************************** - device.c + device.cpp Device interface functions. @@ -13,14 +13,14 @@ #include "speaker.h" #include "debug/debugcpu.h" -#include <string.h> +#include <cstring> //************************************************************************** // DEVICE TYPE REGISTRATION //************************************************************************** -namespace emu { namespace detail { +namespace emu::detail { namespace { @@ -66,7 +66,7 @@ device_type_impl_base *device_registrar::register_device(device_type_impl_base & return nullptr; } -} } // namespace emu::detail +} // namespace emu::detail emu::detail::device_registrar const registered_device_types; @@ -84,7 +84,6 @@ emu::detail::device_registrar const registered_device_types; device_t::device_t(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock) : m_type(type) - , m_searchpath(type.shortname()) , m_owner(owner) , m_next(nullptr) @@ -96,6 +95,8 @@ device_t::device_t(const machine_config &mconfig, device_type type, const char * , m_machine_config(mconfig) , m_input_defaults(nullptr) + , m_system_bios(0) + , m_default_bios(0) , m_default_bios_tag("") , m_machine(nullptr) @@ -123,14 +124,34 @@ device_t::~device_t() //------------------------------------------------- +// searchpath - get the media search path for a +// device +//------------------------------------------------- + +std::vector<std::string> device_t::searchpath() const +{ + std::vector<std::string> result; + device_t const *system(owner()); + while (system && !dynamic_cast<driver_device const *>(system)) + system = system->owner(); + if (system) + result = system->searchpath(); + if (type().parent_rom_device_type()) + result.emplace(result.begin(), type().parent_rom_device_type()->shortname()); + result.emplace(result.begin(), shortname()); + return result; +} + + +//------------------------------------------------- // memregion - return a pointer to the region // info for a given region //------------------------------------------------- -memory_region *device_t::memregion(std::string _tag) const +memory_region *device_t::memregion(std::string_view tag) const { // build a fully-qualified name and look it up - auto search = machine().memory().regions().find(subtag(_tag).c_str()); + auto search = machine().memory().regions().find(subtag(tag)); if (search != machine().memory().regions().end()) return search->second.get(); else @@ -143,10 +164,10 @@ memory_region *device_t::memregion(std::string _tag) const // info for a given share //------------------------------------------------- -memory_share *device_t::memshare(std::string _tag) const +memory_share *device_t::memshare(std::string_view tag) const { // build a fully-qualified name and look it up - auto search = machine().memory().shares().find(subtag(_tag).c_str()); + auto search = machine().memory().shares().find(subtag(tag)); if (search != machine().memory().shares().end()) return search->second.get(); else @@ -159,9 +180,9 @@ memory_share *device_t::memshare(std::string _tag) const // bank info for a given bank //------------------------------------------------- -memory_bank *device_t::membank(std::string _tag) const +memory_bank *device_t::membank(std::string_view tag) const { - auto search = machine().memory().banks().find(subtag(_tag).c_str()); + auto search = machine().memory().banks().find(subtag(tag)); if (search != machine().memory().banks().end()) return search->second.get(); else @@ -174,19 +195,19 @@ memory_bank *device_t::membank(std::string _tag) const // object for a given port name //------------------------------------------------- -ioport_port *device_t::ioport(std::string tag) const +ioport_port *device_t::ioport(std::string_view tag) const { // build a fully-qualified name and look it up - return machine().ioport().port(subtag(tag).c_str()); + return machine().ioport().port(subtag(tag)); } //------------------------------------------------- -// ioport - return a pointer to the I/O port -// object for a given port name +// parameter - return a pointer to a given +// parameter //------------------------------------------------- -std::string device_t::parameter(const char *tag) const +std::string device_t::parameter(std::string_view tag) const { // build a fully-qualified name and look it up return machine().parameters().lookup(subtag(tag)); @@ -203,7 +224,7 @@ void device_t::add_machine_configuration(machine_config &config) assert(&config == &m_machine_config); machine_config::token const tok(config.begin_configuration(*this)); device_add_mconfig(config); - for (finder_base *autodev = m_auto_finder_list; autodev != nullptr; autodev = autodev->next()) + for (auto *autodev = m_auto_finder_list; autodev; autodev = autodev->next()) autodev->end_configuration(); } @@ -300,11 +321,7 @@ 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 + // validate mixins for (device_interface &intf : interfaces()) intf.interface_validity_check(valid); @@ -344,14 +361,14 @@ void device_t::reset() // unscaled clock //------------------------------------------------- -void device_t::set_unscaled_clock(u32 clock) +void device_t::set_unscaled_clock(u32 clock, bool sync_on_new_clock_domain) { // do nothing if no actual change if (clock == m_unscaled_clock) return; m_unscaled_clock = clock; - m_clock = m_unscaled_clock * m_clock_scale; + m_clock = m_unscaled_clock * m_clock_scale + 0.5; m_attoseconds_per_clock = (m_clock == 0) ? 0 : HZ_TO_ATTOSECONDS(m_clock); // recalculate all derived clocks @@ -360,7 +377,7 @@ void device_t::set_unscaled_clock(u32 clock) // if the device has already started, make sure it knows about the new clock if (m_started) - notify_clock_changed(); + notify_clock_changed(sync_on_new_clock_domain); } @@ -376,7 +393,7 @@ void device_t::set_clock_scale(double clockscale) return; m_clock_scale = clockscale; - m_clock = m_unscaled_clock * m_clock_scale; + m_clock = m_unscaled_clock * m_clock_scale + 0.5; m_attoseconds_per_clock = (m_clock == 0) ? 0 : HZ_TO_ATTOSECONDS(m_clock); // recalculate all derived clocks @@ -409,7 +426,7 @@ void device_t::calculate_derived_clock() // clock ticks to an attotime //------------------------------------------------- -attotime device_t::clocks_to_attotime(u64 numclocks) const +attotime device_t::clocks_to_attotime(u64 numclocks) const noexcept { if (m_clock == 0) return attotime::never; @@ -418,7 +435,7 @@ attotime device_t::clocks_to_attotime(u64 numclocks) const else { u32 remainder; - u32 quotient = divu_64x32_rem(numclocks, m_clock, &remainder); + u32 quotient = divu_64x32_rem(numclocks, m_clock, remainder); return attotime(quotient, u64(remainder) * u64(m_attoseconds_per_clock)); } } @@ -429,7 +446,7 @@ attotime device_t::clocks_to_attotime(u64 numclocks) const // attotime to CPU clock ticks //------------------------------------------------- -u64 device_t::attotime_to_clocks(const attotime &duration) const +u64 device_t::attotime_to_clocks(const attotime &duration) const noexcept { if (m_clock == 0) return 0; @@ -439,28 +456,6 @@ u64 device_t::attotime_to_clocks(const attotime &duration) const //------------------------------------------------- -// timer_alloc - allocate a timer for our device -// callback -//------------------------------------------------- - -emu_timer *device_t::timer_alloc(device_timer_id id, void *ptr) -{ - return machine().scheduler().timer_alloc(*this, id, ptr); -} - - -//------------------------------------------------- -// timer_set - set a temporary timer that will -// call our device callback -//------------------------------------------------- - -void device_t::timer_set(const attotime &duration, device_timer_id id, int param, void *ptr) -{ - machine().scheduler().timer_set(duration, *this, id, param, ptr); -} - - -//------------------------------------------------- // set_machine - notify that the machine now // exists //------------------------------------------------- @@ -476,33 +471,18 @@ void device_t::set_machine(running_machine &machine) // list and return status //------------------------------------------------- -bool device_t::findit(bool isvalidation) const +bool device_t::findit(validity_checker *valid) const { bool allfound = true; - for (finder_base *autodev = m_auto_finder_list; autodev != nullptr; autodev = autodev->next()) + for (auto *autodev = m_auto_finder_list; autodev; autodev = autodev->next()) { - if (isvalidation) - { - // sanity checking - char const *const tag = autodev->finder_tag(); - if (!tag) - { - osd_printf_error("Finder tag is null!\n"); - allfound = false; - continue; - } - if (tag[0] == '^' && tag[1] == ':') - { - osd_printf_error("Malformed finder tag: %s\n", tag); - allfound = false; - continue; - } - } - allfound &= autodev->findit(isvalidation); + if (!autodev->findit(valid)) + allfound = false; } return allfound; } + //------------------------------------------------- // resolve_pre_map - find objects that may be used // in memory maps @@ -515,6 +495,7 @@ void device_t::resolve_pre_map() m_string_buffer.reserve(1024); } + //------------------------------------------------- // resolve - find objects //------------------------------------------------- @@ -522,7 +503,7 @@ void device_t::resolve_pre_map() void device_t::resolve_post_map() { // find all the registered post-map objects - if (!findit(false)) + if (!findit(nullptr)) throw emu_fatalerror("Missing some required objects, unable to proceed"); // allow implementation to do additional setup @@ -531,6 +512,16 @@ void device_t::resolve_post_map() //------------------------------------------------- +// view_register - register a view for future state saving +//------------------------------------------------- + +void device_t::view_register(memory_view *view) +{ + m_viewlist.push_back(view); +} + + +//------------------------------------------------- // start - start a device //------------------------------------------------- @@ -553,11 +544,10 @@ void device_t::start() // complain if nothing was registered by the device state_registrations = machine().save().registration_count() - state_registrations; device_execute_interface *exec; - device_sound_interface *sound; - if (state_registrations == 0 && (interface(exec) || interface(sound)) && type() != SPEAKER) + if ((state_registrations == 0) && interface(exec)) { logerror("Device did not register any state to save!\n"); - if ((machine().system().flags & MACHINE_SUPPORTS_SAVE) != 0) + if (!(type().emulation_flags() & flags::SAVE_UNSUPPORTED)) fatalerror("Device '%s' did not register any state to save!\n", tag()); } @@ -576,10 +566,15 @@ void device_t::start() } // register our save states - save_item(NAME(m_clock)); save_item(NAME(m_unscaled_clock)); save_item(NAME(m_clock_scale)); + // have the views register their state + if (!m_viewlist.empty()) + osd_printf_verbose("%s: Registering %d views\n", m_tag, int(m_viewlist.size())); + for (memory_view *view : m_viewlist) + view->register_state(); + // we're now officially started m_started = true; } @@ -649,6 +644,21 @@ void device_t::pre_save() void device_t::post_load() { + // recompute clock-related parameters if something changed + u32 const scaled_clock = m_unscaled_clock * m_clock_scale + 0.5; + if (m_clock != scaled_clock) + { + m_clock = scaled_clock; + m_attoseconds_per_clock = (scaled_clock == 0) ? 0 : HZ_TO_ATTOSECONDS(scaled_clock); + + // recalculate all derived clocks + for (device_t &child : subdevices()) + child.calculate_derived_clock(); + + // make sure the device knows about the new clock + notify_clock_changed(); + } + // notify the interface for (device_interface &intf : interfaces()) intf.interface_post_load(); @@ -663,11 +673,11 @@ void device_t::post_load() // that the clock has changed //------------------------------------------------- -void device_t::notify_clock_changed() +void device_t::notify_clock_changed(bool sync_on_new_clock_domain) { // first notify interfaces for (device_interface &intf : interfaces()) - intf.interface_clock_changed(); + intf.interface_clock_changed(sync_on_new_clock_domain); // then notify the device device_clock_changed(); @@ -793,7 +803,7 @@ void device_t::device_pre_save() //------------------------------------------------- -// device_post_load - called after the loading a +// device_post_load - called after loading a // saved state, so that registered variables can // be expanded as necessary //------------------------------------------------- @@ -829,22 +839,11 @@ void device_t::device_debug_setup() //------------------------------------------------- -// device_timer - called whenever a device timer -// fires -//------------------------------------------------- - -void device_t::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) -{ - // do nothing by default -} - - -//------------------------------------------------- // subdevice_slow - perform a slow name lookup, // caching the results //------------------------------------------------- -device_t *device_t::subdevice_slow(const char *tag) const +device_t *device_t::subdevice_slow(std::string_view tag) const { // resolve the full path std::string fulltag = subtag(tag); @@ -852,20 +851,26 @@ device_t *device_t::subdevice_slow(const char *tag) const // we presume the result is a rooted path; also doubled colons mess up our // tree walk, so catch them early assert(fulltag[0] == ':'); - assert(fulltag.find("::") == -1); + assert(fulltag.find("::") == std::string::npos); // walk the device list to the final path device_t *curdevice = &mconfig().root_device(); - if (fulltag.length() > 1) - for (int start = 1, end = fulltag.find_first_of(':', start); start != 0 && curdevice != nullptr; start = end + 1, end = fulltag.find_first_of(':', start)) + std::string_view part(std::string_view(fulltag).substr(1)); + while (!part.empty() && curdevice != nullptr) + { + std::string_view::size_type end = part.find_first_of(':'); + if (end == std::string::npos) { - std::string part(fulltag, start, (end == -1) ? -1 : end - start); curdevice = curdevice->subdevices().find(part); + part = std::string_view(); } + else + { + curdevice = curdevice->subdevices().find(part.substr(0, end)); + part.remove_prefix(end + 1); + } + } - // if we got a match, add to the fast map - if (curdevice != nullptr) - m_subdevices.m_tagmap.insert(std::make_pair(tag, curdevice)); return curdevice; } @@ -875,44 +880,55 @@ device_t *device_t::subdevice_slow(const char *tag) const // to our device based on the provided tag //------------------------------------------------- -std::string device_t::subtag(std::string _tag) const +std::string device_t::subtag(std::string_view tag) const { - const char *tag = _tag.c_str(); std::string result; - // if the tag begins with a colon, ignore our path and start from the root - if (*tag == ':') + if (!tag.empty() && (tag[0] == ':')) { - tag++; + // if the tag begins with a colon, ignore our path and start from the root + tag.remove_prefix(1); result.assign(":"); } - - // otherwise, start with our path else { + // otherwise, start with our path result.assign(m_tag); if (result != ":") - result.append(":"); + result.append(1, ':'); } // iterate over the tag, look for special path characters to resolve - const char *caret; - while ((caret = strchr(tag, '^')) != nullptr) + std::string_view::size_type delimiter; + while ((delimiter = tag.find_first_of("^:")) != std::string_view::npos) { // copy everything up to there - result.append(tag, caret - tag); - tag = caret + 1; + bool const parent = tag[delimiter] == '^'; + result.append(tag, 0, delimiter); + tag.remove_prefix(delimiter + 1); - // strip trailing colons - int len = result.length(); - while (result[--len] == ':') - result = result.substr(0, len); + if (parent) + { + // strip trailing colons + std::string::size_type len = result.length(); + while ((len > 1) && (result[--len] == ':')) + result.resize(len); - // remove the last path part, leaving the last colon - if (result != ":") + // remove the last path part, leaving the last colon + if (result != ":") + { + std::string::size_type lastcolon = result.find_last_of(':'); + if (lastcolon != std::string::npos) + result.resize(lastcolon + 1); + } + } + else { - int lastcolon = result.find_last_of(':'); - if (lastcolon != -1) - result = result.substr(0, lastcolon + 1); + // collapse successive colons + if (result.back() != ':') + result.append(1, ':'); + delimiter = tag.find_first_not_of(':'); + if (delimiter != std::string_view::npos) + tag.remove_prefix(delimiter); } } @@ -920,30 +936,58 @@ std::string device_t::subtag(std::string _tag) const result.append(tag); // strip trailing colons up to the root - int len = result.length(); - while (len > 1 && result[--len] == ':') - result = result.substr(0, len); + std::string::size_type len = result.length(); + while ((len > 1) && (result[--len] == ':')) + result.resize(len); return result; } //------------------------------------------------- -// register_auto_finder - add a new item to the -// list of stuff to find after we go live +// append - add a new subdevice to the list //------------------------------------------------- -finder_base *device_t::register_auto_finder(finder_base &autodev) +device_t &device_t::subdevice_list::append(std::unique_ptr<device_t> &&device) { - // add to this list - finder_base *old = m_auto_finder_list; - m_auto_finder_list = &autodev; - return old; + device_t &result(m_list.append(*device.release())); + m_tagmap.emplace(result.m_basetag, std::ref(result)); + return result; +} + + +//------------------------------------------------- +// replace_and_remove - add a new device to +// replace an existing subdevice +//------------------------------------------------- + +device_t &device_t::subdevice_list::replace_and_remove(std::unique_ptr<device_t> &&device, device_t &existing) +{ + m_tagmap.erase(existing.m_basetag); + device_t &result(m_list.replace_and_remove(*device.release(), existing)); + m_tagmap.emplace(result.m_basetag, std::ref(result)); + return result; +} + + +//------------------------------------------------- +// remove - remove a subdevice from the list +//------------------------------------------------- + +void device_t::subdevice_list::remove(device_t &device) +{ + m_tagmap.erase(device.m_basetag); + m_list.remove(device); } -void device_t::register_callback(devcb_base &callback) +//------------------------------------------------- +// register_auto_finder - add a new item to the +// list of stuff to find after we go live +//------------------------------------------------- + +device_resolver_base *device_t::register_auto_finder(device_resolver_base &autodev) { - m_callbacks.emplace_back(&callback); + return std::exchange(m_auto_finder_list, &autodev); } @@ -1077,9 +1121,9 @@ void device_interface::interface_pre_save() //------------------------------------------------- -// interface_post_load - called after the loading a +// interface_post_load - called after loading a // saved state, so that registered variables can -// be expaneded as necessary +// be expanded as necessary //------------------------------------------------- void device_interface::interface_post_load() @@ -1095,7 +1139,7 @@ void device_interface::interface_post_load() // implementation //------------------------------------------------- -void device_interface::interface_clock_changed() +void device_interface::interface_clock_changed(bool sync_on_new_clock_domain) { // do nothing by default } diff --git a/src/emu/device.h b/src/emu/device.h index fc8da68f65c..4c2e1d8a180 100644 --- a/src/emu/device.h +++ b/src/emu/device.h @@ -37,19 +37,6 @@ //************************************************************************** -// DEVICE CONFIGURATION MACROS -//************************************************************************** - -// configure devices - -#define DECLARE_READ_LINE_MEMBER(name) int name() -#define READ_LINE_MEMBER(name) int name() -#define DECLARE_WRITE_LINE_MEMBER(name) void name(ATTR_UNUSED int state) -#define WRITE_LINE_MEMBER(name) void name(ATTR_UNUSED int state) - - - -//************************************************************************** // GLOBAL VARIABLES //************************************************************************** @@ -64,21 +51,29 @@ static const char DEVICE_SELF_OWNER[] = "^"; // TYPE DEFINITIONS //************************************************************************** -namespace emu { namespace detail { +namespace emu::detail { class device_type_impl_base; -template <typename T> struct is_device_implementation -{ - static constexpr bool value = std::is_base_of<device_t, T>::value; -}; +template <typename T> +using is_device_implementation = std::bool_constant<std::is_base_of_v<device_t, T> >; + +template <typename T> +using is_device_interface = std::bool_constant<std::is_base_of_v<device_interface, T> && !is_device_implementation<T>::value>; -template <typename T> struct is_device_interface + +struct device_flags { - static constexpr bool value = std::is_base_of<device_interface, T>::value && !is_device_implementation<T>::value; -}; + enum type : u16 + { + NOT_WORKING = u16(1) << 0, + SAVE_UNSUPPORTED = u16(1) << 1, + NONE = u16(0), + ALL = (u16(1) << 2) - 1U + }; +}; struct device_feature { @@ -124,6 +119,7 @@ struct device_feature }; }; +DECLARE_ENUM_BITWISE_OPERATORS(device_flags::type); DECLARE_ENUM_BITWISE_OPERATORS(device_feature::type); @@ -182,14 +178,9 @@ private: template <class DeviceClass, char const *ShortName, char const *FullName, char const *Source> struct device_tag_struct { typedef DeviceClass type; }; -template <class DriverClass, char const *ShortName, char const *FullName, char const *Source, device_feature::type Unemulated, device_feature::type Imperfect> +template <class DriverClass, char const *ShortName, char const *FullName, char const *Source, device_flags::type Flags, device_feature::type Unemulated, device_feature::type Imperfect> struct driver_tag_struct { typedef DriverClass type; }; -template <class DeviceClass, char const *ShortName, char const *FullName, char const *Source> -auto device_tag_func() { return device_tag_struct<DeviceClass, ShortName, FullName, Source>{ }; }; -template <class DriverClass, char const *ShortName, char const *FullName, char const *Source, device_feature::type Unemulated, device_feature::type Imperfect> -auto driver_tag_func() { return driver_tag_struct<DriverClass, ShortName, FullName, Source, Unemulated, Imperfect>{ }; }; - class device_type_impl_base { private: @@ -205,7 +196,7 @@ private: template <typename DeviceClass> static std::unique_ptr<device_t> create_device(device_type_impl_base const &type, machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) { - return make_unique_clear<DeviceClass>(mconfig, tag, owner, clock); + return std::make_unique<DeviceClass>(mconfig, tag, owner, clock); } template <typename DriverClass> @@ -214,7 +205,7 @@ private: assert(!owner); assert(!clock); - return make_unique_clear<DriverClass>(mconfig, type, tag); + return std::make_unique<DriverClass>(mconfig, type, tag); } create_func const m_creator; @@ -222,8 +213,10 @@ private: char const *const m_shortname; char const *const m_fullname; char const *const m_source; + device_flags::type const m_emulation_flags; device_feature::type const m_unemulated_features; device_feature::type const m_imperfect_features; + device_type_impl_base const *const m_parent_rom; device_type_impl_base *m_next; @@ -236,34 +229,40 @@ public: , m_shortname(nullptr) , m_fullname(nullptr) , m_source(nullptr) + , m_emulation_flags(device_flags::NONE) , m_unemulated_features(device_feature::NONE) , m_imperfect_features(device_feature::NONE) + , m_parent_rom(nullptr) , m_next(nullptr) { } template <class DeviceClass, char const *ShortName, char const *FullName, char const *Source> - device_type_impl_base(device_tag_struct<DeviceClass, ShortName, FullName, Source> (*)()) + device_type_impl_base(device_tag_struct<DeviceClass, ShortName, FullName, Source>) : m_creator(&create_device<DeviceClass>) , m_type(typeid(DeviceClass)) , m_shortname(ShortName) , m_fullname(FullName) , m_source(Source) + , m_emulation_flags(DeviceClass::emulation_flags()) , m_unemulated_features(DeviceClass::unemulated_features()) , m_imperfect_features(DeviceClass::imperfect_features()) + , m_parent_rom(DeviceClass::parent_rom_device_type()) , m_next(device_registrar::register_device(*this)) { } - template <class DriverClass, char const *ShortName, char const *FullName, char const *Source, device_feature::type Unemulated, device_feature::type Imperfect> - device_type_impl_base(driver_tag_struct<DriverClass, ShortName, FullName, Source, Unemulated, Imperfect> (*)()) + template <class DriverClass, char const *ShortName, char const *FullName, char const *Source, device_flags::type Flags, device_feature::type Unemulated, device_feature::type Imperfect> + device_type_impl_base(driver_tag_struct<DriverClass, ShortName, FullName, Source, Flags, Unemulated, Imperfect>) : m_creator(&create_driver<DriverClass>) , m_type(typeid(DriverClass)) , m_shortname(ShortName) , m_fullname(FullName) , m_source(Source) + , m_emulation_flags(DriverClass::emulation_flags() | Flags) , m_unemulated_features(DriverClass::unemulated_features() | Unemulated) , m_imperfect_features((DriverClass::imperfect_features() & ~Unemulated) | Imperfect) + , m_parent_rom(DriverClass::parent_rom_device_type()) , m_next(nullptr) { } @@ -272,8 +271,10 @@ public: char const *shortname() const { return m_shortname; } char const *fullname() const { return m_fullname; } char const *source() const { return m_source; } + device_flags::type emulation_flags() const { return m_emulation_flags; } device_feature::type unemulated_features() const { return m_unemulated_features; } device_feature::type imperfect_features() const { return m_imperfect_features; } + device_type_impl_base const *parent_rom_device_type() const { return m_parent_rom; } std::unique_ptr<device_t> create(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) const { @@ -298,7 +299,7 @@ public: template <typename... Params> std::unique_ptr<DeviceClass> create(machine_config &mconfig, char const *tag, device_t *owner, Params &&... args) const { - return make_unique_clear<DeviceClass>(mconfig, tag, owner, std::forward<Params>(args)...); + return std::make_unique<DeviceClass>(mconfig, tag, owner, std::forward<Params>(args)...); } template <typename... Params> DeviceClass &operator()(machine_config &mconfig, char const *tag, Params &&... args) const; @@ -310,7 +311,7 @@ public: inline device_registrar::const_iterator &device_registrar::const_iterator::operator++() { m_type = m_type->m_next; return *this; } -} } // namespace emu::detail +} // namespace emu::detail // device types @@ -318,77 +319,150 @@ typedef emu::detail::device_type_impl_base const &device_type; typedef std::add_pointer_t<device_type> device_type_ptr; extern emu::detail::device_registrar const registered_device_types; -template < - typename DeviceClass, - char const *ShortName, - char const *FullName, - char const *Source> -constexpr auto device_creator = &emu::detail::device_tag_func<DeviceClass, ShortName, FullName, Source>; - -template < - typename DriverClass, - char const *ShortName, - char const *FullName, - char const *Source, - emu::detail::device_feature::type Unemulated, - emu::detail::device_feature::type Imperfect> -constexpr auto driver_device_creator = &emu::detail::driver_tag_func<DriverClass, ShortName, FullName, Source, Unemulated, Imperfect>; +/// \addtogroup machinedef +/// \{ + +/// \brief Declare a device type +/// +/// Declares a device type where the exposed device class is in the +/// global namespace. Must be used in the global namespace. +/// +/// In addition to declaring the device type itself, a forward +/// declaration for the exposed device class is generated, and automatic +/// instantiation of device finder templates for the exposed device +/// class is suppressed. +/// \param Type The device type name (an identifier). By convention, +/// these start with an uppercase letter and consist only of uppercase +/// letters and underscores. +/// \param Class The exposed device class name. Must be the device +/// implementation class, or a public base of it, and must be derived +/// from #device_t or #device_interface. +/// \sa DECLARE_DEVICE_TYPE_NS DEFINE_DEVICE_TYPE +/// DEFINE_DEVICE_TYPE_PRIVATE #define DECLARE_DEVICE_TYPE(Type, Class) \ class Class; \ - extern emu::detail::device_type_impl<Class> const &Type; \ + extern emu::detail::device_type_impl<Class> const Type; \ extern template class device_finder<Class, false>; \ extern template class device_finder<Class, true>; + +/// \brief Declare a device type for a class in a namespace +/// +/// Declares a device type where the exposed device class is not in the +/// global namespace. Must be used in the global namespace. +/// +/// In addition to declaring the device type itself, a forward +/// declaration for the exposed device class is generated, and automatic +/// instantiation of device finder templates for the exposed device +/// class is suppressed. +/// \param Type The device type name (an identifier). By convention, +/// these start with an uppercase letter and consist only of uppercase +/// letters and underscores. +/// \param Namespace The fully qualified name of the namespace +/// containing the exposed device class. +/// \param Class The exposed device class name, without namespace +/// qualifiers. Must be the device implementation class, or a public +/// base of it, and must be derived from #device_t or +/// #device_interface. +/// \sa DECLARE_DEVICE_TYPE DEFINE_DEVICE_TYPE +/// DEFINE_DEVICE_TYPE_PRIVATE #define DECLARE_DEVICE_TYPE_NS(Type, Namespace, Class) \ - extern emu::detail::device_type_impl<Namespace::Class> const &Type; \ + namespace Namespace { class Class; } \ + extern emu::detail::device_type_impl<Namespace::Class> const Type; \ extern template class device_finder<Namespace::Class, false>; \ extern template class device_finder<Namespace::Class, true>; + +/// \brief Define a device type +/// +/// Defines a device type where the exposed device class is the same as +/// the device implementation class. Must be used in the global +/// namespace. +/// +/// As well as defining the device type, device finder templates are +/// instantiated for the device class. +/// \param Type The device type name (an identifier). By convention, +/// these start with an uppercase letter and consist only of uppercase +/// letters and underscores. +/// \param Class The device implementation class name. Must be the same +/// as the exposed device class, and must be derived from #device_t. +/// \param ShortName The short name of the device, used for +/// identification, and in filesystem paths for assets and data. Must +/// be a string no longer than thirty-two characters, containing only +/// ASCII lowercase letters, digits and underscores. Must be globally +/// unique across systems and devices. +/// \param FullName Display name for the device. Must be a string, and +/// must be globally unique across systems and devices. +/// \sa DECLARE_DEVICE_TYPE DECLARE_DEVICE_TYPE_NS +/// DEFINE_DEVICE_TYPE_PRIVATE #define DEFINE_DEVICE_TYPE(Type, Class, ShortName, FullName) \ namespace { \ - struct Class##_device_traits { static constexpr char const shortname[] = ShortName, fullname[] = FullName, source[] = __FILE__; }; \ - constexpr char const Class##_device_traits::shortname[], Class##_device_traits::fullname[], Class##_device_traits::source[]; \ + struct Type##_device_traits { static constexpr char const shortname[] = ShortName, fullname[] = FullName, source[] = __FILE__; }; \ + constexpr char const Type##_device_traits::shortname[], Type##_device_traits::fullname[], Type##_device_traits::source[]; \ } \ - emu::detail::device_type_impl<Class> const &Type = device_creator<Class, (Class##_device_traits::shortname), (Class##_device_traits::fullname), (Class##_device_traits::source)>; \ + emu::detail::device_type_impl<Class> const Type = emu::detail::device_tag_struct<Class, (Type##_device_traits::shortname), (Type##_device_traits::fullname), (Type##_device_traits::source)>{ }; \ template class device_finder<Class, false>; \ template class device_finder<Class, true>; + +/// \brief Define a device type with a private implementation class +/// +/// Defines a device type where the exposed device class is a base of +/// the device implementation class. Must be used in the global +/// namespace. +/// +/// Device finder templates are not instantiated for the exposed device +/// class. This must be done explicitly in a single location for the +/// project. +/// \param Type The device type name (an identifier). By convention, +/// these start with an uppercase letter and consist only of uppercase +/// letters and underscores. +/// \param Base The fully-qualified exposed device class name. Must be +/// a public base of the device implementation class, and must be +/// derived from #device_t or #device_interface. +/// \param Class The fully-qualified device implementation class name. +/// Must be derived from the exposed device class, and indirectly from +/// #device_t. +/// \param ShortName The short name of the device, used for +/// identification, and in filesystem paths for assets and data. Must +/// be a string no longer than thirty-two characters, containing only +/// ASCII lowercase letters, digits and underscores. Must be globally +/// unique across systems and devices. +/// \param FullName Display name for the device. Must be a string, and +/// must be globally unique across systems and devices. +/// \sa DECLARE_DEVICE_TYPE DECLARE_DEVICE_TYPE_NS DEFINE_DEVICE_TYPE #define DEFINE_DEVICE_TYPE_PRIVATE(Type, Base, Class, ShortName, FullName) \ namespace { \ - struct Class##_device_traits { static constexpr char const shortname[] = ShortName, fullname[] = FullName, source[] = __FILE__; }; \ - constexpr char const Class##_device_traits::shortname[], Class##_device_traits::fullname[], Class##_device_traits::source[]; \ + struct Type##_device_traits { static constexpr char const shortname[] = ShortName, fullname[] = FullName, source[] = __FILE__; }; \ + constexpr char const Type##_device_traits::shortname[], Type##_device_traits::fullname[], Type##_device_traits::source[]; \ } \ - emu::detail::device_type_impl<Base> const &Type = device_creator<Class, (Class##_device_traits::shortname), (Class##_device_traits::fullname), (Class##_device_traits::source)>; + emu::detail::device_type_impl<Base> const Type = emu::detail::device_tag_struct<Class, (Type##_device_traits::shortname), (Type##_device_traits::fullname), (Type##_device_traits::source)>{ }; -#define DEFINE_DEVICE_TYPE_NS(Type, Namespace, Class, ShortName, FullName) \ - namespace { \ - struct Class##_device_traits { static constexpr char const shortname[] = ShortName, fullname[] = FullName, source[] = __FILE__; }; \ - constexpr char const Class##_device_traits::shortname[], Class##_device_traits::fullname[], Class##_device_traits::source[]; \ - } \ - emu::detail::device_type_impl<Namespace::Class> const &Type = device_creator<Namespace::Class, (Class##_device_traits::shortname), (Class##_device_traits::fullname), (Class##_device_traits::source)>; \ - template class device_finder<Namespace::Class, false>; \ - template class device_finder<Namespace::Class, true>; +/// \} -// exception classes +/// \brief Start order dependencies not satisfied exception +/// +/// May be thrown from the start member functions of #device_t and +/// #device_interface implementations if start order dependencies have +/// not been satisfied. MAME will start additional devices before +/// reattempting to start the device that threw the exception. +/// \sa device_t::device_start device_interface::interface_pre_start class device_missing_dependencies : public emu_exception { }; -// timer IDs for devices -typedef u32 device_timer_id; - -// ======================> device_t - -// device_t represents a device +/// \brief Base class for devices +/// +/// The base class for all device implementations in MAME's modular +/// architecture. class device_t : public delegate_late_bind { DISABLE_COPYING(device_t); friend class simple_list<device_t>; friend class running_machine; - friend class finder_base; - friend class devcb_base; + friend class device_resolver_base; class subdevice_list { @@ -411,18 +485,21 @@ class device_t : public delegate_late_bind private: // private helpers - device_t *find(const std::string &name) const + device_t &append(std::unique_ptr<device_t> &&device); + device_t &replace_and_remove(std::unique_ptr<device_t> &&device, device_t &existing); + void remove(device_t &device); + device_t *find(std::string_view name) const { - device_t *curdevice; - for (curdevice = m_list.first(); curdevice != nullptr; curdevice = curdevice->next()) - if (name.compare(curdevice->m_basetag) == 0) - return curdevice; - return nullptr; + auto result = m_tagmap.find(name); + if (result != m_tagmap.end()) + return &result->second.get(); + else + return nullptr; } // private state simple_list<device_t> m_list; // list of sub-devices we own - mutable std::unordered_map<std::string,device_t *> m_tagmap; // map of devices looked up and found by subtag + std::unordered_map<std::string_view, std::reference_wrapper<device_t>> m_tagmap; // map of devices looked up and found by subtag }; class interface_list @@ -430,7 +507,6 @@ class device_t : public delegate_late_bind friend class device_t; friend class device_interface; friend class device_memory_interface; - friend class device_state_interface; friend class device_execute_interface; public: @@ -460,7 +536,7 @@ class device_t : public delegate_late_bind }; // construction/destruction - interface_list() : m_head(nullptr), m_execute(nullptr), m_memory(nullptr), m_state(nullptr) { } + interface_list() : m_head(nullptr), m_execute(nullptr), m_memory(nullptr) { } // getters device_interface *first() const { return m_head; } @@ -473,7 +549,6 @@ class device_t : public delegate_late_bind device_interface *m_head; // head of interface list device_execute_interface *m_execute; // pre-cached pointer to execute interface device_memory_interface *m_memory; // pre-cached pointer to memory interface - device_state_interface *m_state; // pre-cached pointer to state interface }; protected: @@ -487,11 +562,60 @@ protected: public: // device flags + using flags = emu::detail::device_flags; + using flags_type = emu::detail::device_flags::type; using feature = emu::detail::device_feature; using feature_type = emu::detail::device_feature::type; + + /// \brief Report emulation status flags + /// + /// Implement this member in a derived class to declare flags + /// pertaining to the overall emulation status of the device. Some + /// flags propagate to all other devices and systems that use the + /// device. + /// \return Bitwise or of the flag constants pertaining to the + /// device emulation. + /// \sa unemulated_features imperfect_features + static constexpr flags_type emulation_flags() { return flags::NONE; } + + /// \brief Report unemulated features + /// + /// Implement this member in a derived class to declare features + /// that are not emulated. This will propagate to all other devices + /// and systems that use the device. Unemulated features are shown + /// in the system selection UI, and cause a red warning to be + /// displayed on starting a system. + /// \return Bitwise or of the feature constants for unemulated + /// features of the device. + /// \sa emulation_flags imperfect_features static constexpr feature_type unemulated_features() { return feature::NONE; } + + /// \brief Report imperfectly emulated features + /// + /// Implement this member in a derived class to declare features + /// that are imperfectly emulated. This will propagate to all other + /// devices and systems that use the device. Imperfectly emulated + /// features are shown in the system selection UI, and cause a + /// yellow warning to be displayed on starting a system (provided + /// there are no unemulated features, which take precedence and + /// cause the warning to be red). + /// + /// An exception is imperfectly emulated protection, which results + /// in a red warning being displayed when starting a system. + /// \return Bitwise or of the feature constants for imperfectly + /// emulated features of the device. + /// \sa emulation_flags unemulated_features static constexpr feature_type imperfect_features() { return feature::NONE; } + /// \brief Get parent device type for ROM search + /// + /// Implement this member in a derived class to declare the parent + /// device type for the purpose of searching for ROMs. Only one + /// level is allowed. It is an error if the parent device type + /// itself declares a parent device type. + /// \return Pointer to parent device type, or nullptr. + static auto parent_rom_device_type() { return nullptr; } + virtual ~device_t(); // getters @@ -502,7 +626,7 @@ public: device_type type() const { return m_type; } const char *name() const { return m_type.fullname(); } const char *shortname() const { return m_type.shortname(); } - const char *searchpath() const { return m_searchpath.c_str(); } + virtual std::vector<std::string> searchpath() const; const char *source() const { return m_type.source(); } device_t *owner() const { return m_owner; } device_t *next() const { return m_next; } @@ -519,36 +643,67 @@ public: // interface helpers interface_list &interfaces() { return m_interfaces; } const interface_list &interfaces() const { return m_interfaces; } - template<class DeviceClass> bool interface(DeviceClass *&intf) { intf = dynamic_cast<DeviceClass *>(this); return (intf != nullptr); } - template<class DeviceClass> bool interface(DeviceClass *&intf) const { intf = dynamic_cast<const DeviceClass *>(this); return (intf != nullptr); } + template <class DeviceClass> bool interface(DeviceClass *&intf) { intf = dynamic_cast<DeviceClass *>(this); return (intf != nullptr); } + template <class DeviceClass> bool interface(DeviceClass *&intf) const { intf = dynamic_cast<const DeviceClass *>(this); return (intf != nullptr); } // specialized helpers for common core interfaces bool interface(device_execute_interface *&intf) { intf = m_interfaces.m_execute; return (intf != nullptr); } bool interface(device_execute_interface *&intf) const { intf = m_interfaces.m_execute; return (intf != nullptr); } bool interface(device_memory_interface *&intf) { intf = m_interfaces.m_memory; return (intf != nullptr); } bool interface(device_memory_interface *&intf) const { intf = m_interfaces.m_memory; return (intf != nullptr); } - bool interface(device_state_interface *&intf) { intf = m_interfaces.m_state; return (intf != nullptr); } - bool interface(device_state_interface *&intf) const { intf = m_interfaces.m_state; return (intf != nullptr); } device_execute_interface &execute() const { assert(m_interfaces.m_execute != nullptr); return *m_interfaces.m_execute; } device_memory_interface &memory() const { assert(m_interfaces.m_memory != nullptr); return *m_interfaces.m_memory; } - device_state_interface &state() const { assert(m_interfaces.m_state != nullptr); return *m_interfaces.m_state; } // owned object helpers subdevice_list &subdevices() { return m_subdevices; } const subdevice_list &subdevices() const { return m_subdevices; } // device-relative tag lookups - std::string subtag(std::string tag) const; - std::string siblingtag(std::string tag) const { return (m_owner != nullptr) ? m_owner->subtag(tag) : tag; } - memory_region *memregion(std::string tag) const; - memory_share *memshare(std::string tag) const; - memory_bank *membank(std::string tag) const; - ioport_port *ioport(std::string tag) const; - device_t *subdevice(const char *tag) const; - device_t *siblingdevice(const char *tag) const; - template<class DeviceClass> DeviceClass *subdevice(const char *tag) const { return downcast<DeviceClass *>(subdevice(tag)); } - template<class DeviceClass> DeviceClass *siblingdevice(const char *tag) const { return downcast<DeviceClass *>(siblingdevice(tag)); } - std::string parameter(const char *tag) const; + std::string subtag(std::string_view tag) const; + std::string siblingtag(std::string_view tag) const { return (m_owner != nullptr) ? m_owner->subtag(tag) : std::string(tag); } + memory_region *memregion(std::string_view tag) const; + memory_share *memshare(std::string_view tag) const; + memory_bank *membank(std::string_view tag) const; + ioport_port *ioport(std::string_view tag) const; + device_t *subdevice(std::string_view tag) const; + device_t *siblingdevice(std::string_view tag) const; + template <class DeviceClass> + DeviceClass *subdevice(std::string_view tag) const + { + device_t *const found = subdevice(tag); + if constexpr (std::is_base_of_v<device_t, DeviceClass>) + { + return downcast<DeviceClass *>(found); + } + else + { + auto const result = dynamic_cast<DeviceClass *>(found); +#if defined(MAME_DEBUG) + if (found && !result) + report_bad_device_cast(found, typeid(device_t), typeid(DeviceClass)); +#endif + return result; + } + } + template <class DeviceClass> + DeviceClass *siblingdevice(std::string_view tag) const + { + device_t *const found = siblingdevice(tag); + if constexpr (std::is_base_of_v<device_t, DeviceClass>) + { + return downcast<DeviceClass *>(found); + } + else + { + auto const result = dynamic_cast<DeviceClass *>(found); +#if defined(MAME_DEBUG) + if (found && !result) + report_bad_device_cast(found, typeid(device_t), typeid(DeviceClass)); +#endif + return result; + } + } + std::string parameter(std::string_view tag) const; // configuration helpers void add_machine_configuration(machine_config &config); @@ -567,34 +722,131 @@ public: // clock/timing accessors u32 clock() const { return m_clock; } u32 unscaled_clock() const { return m_unscaled_clock; } - void set_unscaled_clock(u32 clock); - void set_unscaled_clock(const XTAL &xtal) { set_unscaled_clock(xtal.value()); } + void set_unscaled_clock(u32 clock, bool sync_on_new_clock_domain = false); + void set_unscaled_clock(const XTAL &xtal, bool sync_on_new_clock_domain = false) { set_unscaled_clock(xtal.value(), sync_on_new_clock_domain); } + void set_unscaled_clock_int(u32 clock) { set_unscaled_clock(clock, false); } // non-overloaded name because binding to overloads is ugly + void set_unscaled_clock_int_sync(u32 clock) { set_unscaled_clock(clock, true); } // non-overloaded name because binding to overloads is ugly double clock_scale() const { return m_clock_scale; } void set_clock_scale(double clockscale); - attotime clocks_to_attotime(u64 clocks) const; - u64 attotime_to_clocks(const attotime &duration) const; + attotime clocks_to_attotime(u64 clocks) const noexcept; + u64 attotime_to_clocks(const attotime &duration) const noexcept; // timer interfaces - emu_timer *timer_alloc(device_timer_id id = 0, void *ptr = nullptr); - void timer_set(const attotime &duration, device_timer_id id = 0, int param = 0, void *ptr = nullptr); - void synchronize(device_timer_id id = 0, int param = 0, void *ptr = nullptr) { timer_set(attotime::zero, id, param, ptr); } - void timer_expired(emu_timer &timer, device_timer_id id, int param, void *ptr) { device_timer(timer, id, param, ptr); } + template <typename... T> emu_timer *timer_alloc(T &&... args); + + /// \brief Register data for save states + /// + /// Registers data to be automatically saved/restored. Can be used + /// with fixed-sized integer types, enumerated types marked safe for + /// saving (see #ALLOW_SAVE_TYPE and #ALLOW_SAVE_TYPE_AND_VECTOR). + /// Supports C arrays, \c std::array and \c std::vector. Note that + /// \c std::vector instances must not be resized after being + /// registered to be saved/restored. + /// \param value Reference to the data to be saved/restored. The + /// \c NAME macro can be used to simplify specifying the + /// \p valname argument at the same time. + /// \param [in] valname The name of the saved item. The combination + /// of the \p valname and \p index arguments must be unique across + /// saved items for a device. + /// \param [in] index A numeric value to distinguish between saved + /// items with the same name. + template <typename ItemType> + void ATTR_COLD save_item(ItemType &value, const char *valname, int index = 0) + { + assert(m_save); + m_save->save_item(this, name(), tag(), index, value, valname); + } - // state saving interfaces - template<typename ItemType> - void ATTR_COLD save_item(ItemType &&value, const char *valname, int index = 0) { assert(m_save != nullptr); m_save->save_item(this, name(), tag(), index, std::forward<ItemType>(value), valname); } - template<typename ItemType> - void ATTR_COLD save_pointer(ItemType &&value, const char *valname, u32 count, int index = 0) { assert(m_save != nullptr); m_save->save_pointer(this, name(), tag(), index, std::forward<ItemType>(value), valname, count); } + /// \brief Register a member of a structure in an array for save + /// states + /// + /// Registers data to be automatically saved/restored. Can be used + /// with fixed-sized integer types, enumerated types marked safe for + /// saving (see #ALLOW_SAVE_TYPE and #ALLOW_SAVE_TYPE_AND_VECTOR). + /// Used to allow saving/restoring members of structures in C arrays + /// or \c std::array instances. + /// \param value Reference to the array of structures containing the + /// member to be saved/restored. The #STRUCT_MEMBER macro can be + /// used to simplify specifying the \p element and \p valname + /// arguments at the same time. + /// \param [in] element Pointer to the member of the structure to + /// save/restore. + /// \param [in] valname The name of the saved item. The combination + /// of the \p valname and \p index arguments must be unique across + /// saved items for a device. + /// \param [in] index A numeric value to distinguish between saved + /// items with the same name. + template <typename ItemType, typename StructType, typename ElementType> + void ATTR_COLD save_item(ItemType &value, ElementType StructType::*element, const char *valname, int index = 0) + { + assert(m_save); + m_save->save_item(this, name(), tag(), index, value, element, valname); + } + + /// \brief Register an array of indeterminate for save states + /// + /// Registers data to be automatically saved/restored when the + /// length of the outermost array cannot be automatically determined + /// at compile time. Can be used with C arrays of indeterminate + /// length, pointers, and \c std::unique_ptr instances pointing to + /// arrays. Use #save_item if length of the array can be determined + /// at compile time. + /// \param value Pointer to the array containing the data to be + /// saved/restored. The \c NAME macro can be used to simplify + /// specifying the \p valname argument at the same time. + /// \param [in] valname The name of the saved item. The combination + /// of the \p valname and \p index arguments must be unique across + /// saved items for a device. + /// \param [in] count The number of elements in the outermost array. + /// \param [in] index A numeric value to distinguish between saved + /// items with the same name. + /// \sa save_item + template <typename ItemType> + void ATTR_COLD save_pointer(ItemType &&value, const char *valname, u32 count, int index = 0) + { + assert(m_save); + m_save->save_pointer(this, name(), tag(), index, std::forward<ItemType>(value), valname, count); + } + + /// \brief Register a member of a structure in an array of + /// indeterminate for save states + /// + /// Registers data to be automatically saved/restored when the + /// length of the outermost array cannot be automatically determined + /// at compile time. Can be used with C arrays of indeterminate + /// length, pointers, and \c std::unique_ptr instances pointing to + /// arrays. Use #save_item if length of the array can be determined + /// at compile time. + /// \param value Pointer to the array of structures containing the + /// member to be saved/restored. The #STRUCT_MEMBER macro can be + /// used to simplify specifying the \p element and \p valname + /// arguments at the same time. + /// \param [in] element Pointer to the member of the structure to + /// save/restore. + /// \param [in] valname The name of the saved item. The combination + /// of the \p valname and \p index arguments must be unique across + /// saved items for a device. + /// \param [in] count The number of elements in the outermost array. + /// \param [in] index A numeric value to distinguish between saved + /// items with the same name. + /// \sa save_item + template <typename ItemType, typename StructType, typename ElementType> + void ATTR_COLD save_pointer(ItemType &&value, ElementType StructType::*element, const char *valname, u32 count, int index = 0) + { + assert(m_save); + m_save->save_pointer(this, name(), tag(), index, std::forward<ItemType>(value), element, valname, count); + } // debugging device_debug *debug() const { return m_debug.get(); } void set_system_bios(u8 bios) { m_system_bios = bios; } - bool findit(bool isvalidation) const; + bool findit(validity_checker *valid) const; // misc template <typename Format, typename... Params> void popmessage(Format &&fmt, Params &&... args) const; template <typename Format, typename... Params> void logerror(Format &&fmt, Params &&... args) const; + void view_register(memory_view *view); protected: // miscellaneous helpers @@ -606,9 +858,8 @@ protected: void debug_setup(); void pre_save(); void post_load(); - void notify_clock_changed(); - finder_base *register_auto_finder(finder_base &autodev); - void register_callback(devcb_base &callback); + void notify_clock_changed(bool sync_on_new_clock_domain = false); + device_resolver_base *register_auto_finder(device_resolver_base &autodev); //------------------- begin derived class overrides @@ -616,24 +867,143 @@ protected: virtual const tiny_rom_entry *device_rom_region() const; virtual void device_add_mconfig(machine_config &config); virtual ioport_constructor device_input_ports() const; + + /// \brief Finalise device configuration + /// + /// Perform any final configuration tasks after all devices in the + /// system have added machine configuration. This is called after + /// any #device_interface mix-in \c interface_config_complete + /// members have completed. + /// + /// Note that automatic object finders will not have been resolved + /// at the time this member is called. virtual void device_config_complete(); + + /// \brief Additional device validity checks + /// + /// Implement this member to provide additional validity checks. + /// Report errors using #osd_printf_error and report warnings using + /// #osd_printf_warning. The system being validated, device type + /// and device tag are collected automatically. Do not throw + /// exceptions to report errors. + /// + /// This provides an opportunity to check that the device has been + /// configured correctly. Systems are validated on start, and also + /// when the user manually runs a validity check. Validity checks + /// are only run for devices configured in runnable systems, not + /// when checking that a device can be instantiated in isolation. + /// \param [in] valid Reference to the validity checker object + /// performing validation (provides some helper member functions). + /// \sa device_interface::interface_validity_check virtual void device_validity_check(validity_checker &valid) const ATTR_COLD; + + /// \brief Resolve objects that may be needed while starting + /// + /// Implement this member to complete object resolution before any + /// devices are started. For example it may be necessary to resolve + /// callbacks before any devices start so initial input conditions + /// can be set. This is called after all registered automatic + /// object finders are resolved. virtual void device_resolve_objects() ATTR_COLD; + + /// \brief Device start handler + /// + /// Implement this member to set up the initial state of the device + /// on start. This will be called after all #device_interface + /// mix-in \c interface_pre_start members have completed + /// successfully. If the device can't start until another device + /// has completed starting, throw a #device_missing_dependencies + /// exception. Starting will be postponed until additional devices + /// have started. + /// + /// If a device's direct base class is not #device_t, it's good + /// practice to check start order dependencies (and throw + /// #device_missing_dependencies if necessary) before calling the + /// base implementation. This will ensure that the base + /// implementation won't be called twice if starting needs to be + /// postponed. + /// + /// This is the correct place to register for save states. + /// \sa device_reset device_stop save_item save_pointer + /// device_interface::interface_pre_start + /// device_interface::interface_post_start virtual void device_start() ATTR_COLD = 0; + + /// \brief Device stop handler + /// + /// Implement this member to perform additional tasks on ending an + /// emulation session. You may deallocate memory here. This is + /// called after \c interface_pre_stop is called for all + /// #device_interface mix-ins, and before \c interface_post_stop is + /// called for any #device_interface mix-ins. + /// \sa device_interface::interface_pre_stop + /// device_interface::interface_post_stop virtual void device_stop() ATTR_COLD; + + /// \brief Device reset handler + /// + /// Implement this member to provide reset behaviour. This is + /// called after all #device_interface mix-in \c interface_pre_reset + /// members have completed, and before any child devices are reset. + /// All devices are reset at the beginning of an emulation session + /// (after all devices have been started), and also when the user + /// requests a soft reset (by pressing F3 by default, and also + /// available from the debugger). + /// + /// Note that child devices are reset automatically when a device is + /// reset. You should not reset child devices manually from this + /// member. If you need to provide additional behaviour after child + /// devices are reset, implement #device_reset_after_children. + /// + /// Only implement warm reset behaviour in this member. Initial + /// cold reset conditions should be set up in #device_start. + /// \sa device_reset_after_children device_start + /// device_interface::interface_pre_reset + /// device_interface::interface_post_reset virtual void device_reset() ATTR_COLD; + + /// \brief Additional reset behaviour after child device reset + /// + /// Implement this member to provide additional reset behaviour + /// after child devices are reset. This is called when resetting a + /// device after #device_reset has been called and all child devices + /// have been reset, and before any #device_interface mix-in + /// \c interface_post_reset members are called. + /// \sa device_reset device_interface::interface_pre_reset + /// device_interface::interface_post_reset virtual void device_reset_after_children() ATTR_COLD; + + /// \brief Prepare for a save state to be written + /// + /// Implement this member to perform any tasks necessary before any + /// registered save state items are recorded. For example it may be + /// necessary to flush caches, serialise self-referencing members or + /// pointers into data structures. This is called after all + /// #device_interface mix-in \c interface_pre_save members are + /// called. + /// \sa device_post_load save_item save_pointer + /// device_interface::interface_pre_save virtual void device_pre_save() ATTR_COLD; + + /// \brief Complete save state loading + /// + /// Implement this member to perform any tasks necessary after + /// registered save state items are loaded. For example it may be + /// necessary to update or invalidate caches, or de-serialise + /// pointers into data structures. This is called after all + /// #device_interface mix-in \c interface_post_load members are + /// called. + /// \sa device_pre_save save_item save_pointer + /// device_interface::interface_post_load virtual void device_post_load() ATTR_COLD; + virtual void device_clock_changed(); virtual void device_debug_setup(); - virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); //------------------- end derived class overrides // core device properties device_type m_type; // device type - std::string m_searchpath; // search path, used for media loading // device relationships & interfaces device_t * m_owner; // device that owns us @@ -658,7 +1028,7 @@ protected: private: // internal helpers - device_t *subdevice_slow(const char *tag) const; + device_t *subdevice_slow(std::string_view tag) const; void calculate_derived_clock(); // private state; accessor use required @@ -668,18 +1038,21 @@ private: std::string m_basetag; // base part of the tag bool m_config_complete; // have we completed our configuration? bool m_started; // true if the start function has succeeded - finder_base * m_auto_finder_list; // list of objects to auto-find + device_resolver_base * m_auto_finder_list; // list of objects to auto-find mutable std::vector<rom_entry> m_rom_entries; - std::list<devcb_base *> m_callbacks; + std::vector<memory_view *> m_viewlist; // list of views // string formatting buffer for logerror mutable util::ovectorstream m_string_buffer; }; -// ======================> device_interface - -// device_interface represents runtime information for a particular device interface +/// \brief Device mix-in base +/// +/// Provides a base for #device_t mix-ins that integrate with the device +/// lifecycle. Derived classes are used to implement a number of +/// standard concepts and interfaces, and integrate with the scheduler, +/// debugger and user interface. class device_interface { DISABLE_COPYING(device_interface); @@ -701,24 +1074,139 @@ public: device_interface *interface_next() const { return m_interface_next; } // optional operation overrides - // - // WARNING: interface_pre_start must be callable multiple times in - // case another interface throws a missing dependency. In - // particular, state saving registrations should be done in post. + + /// \brief Finalise mix-in configuration + /// + /// Perform any final configuration tasks after all devices in the + /// system have added machine configuration. This is called before + /// \c device_config_complete is called for the device. + /// + /// Note that automatic object finders will not have been resolved + /// at this time. + /// \sa device_t::device_config_complete virtual void interface_config_complete(); - virtual void interface_validity_check(validity_checker &valid) const; - virtual void interface_pre_start(); - virtual void interface_post_start(); - virtual void interface_pre_reset(); - virtual void interface_post_reset(); - virtual void interface_pre_stop(); - virtual void interface_post_stop(); - virtual void interface_pre_save(); - virtual void interface_post_load(); - virtual void interface_clock_changed(); + + /// \brief Additional mix-in validity checks + /// + /// Implement this member to provide additional validity checks. + /// Report errors using #osd_printf_error and report warnings using + /// #osd_printf_warning. The system being validated, device type + /// and device tag are collected automatically. Do not throw + /// exceptions to report errors. + /// + /// This provides an opportunity to check that the mix-in has been + /// configured correctly. Systems are validated on start, and also + /// when the user manually runs a validity check. Validity checks + /// are only run for devices configured in runnable systems, not + /// when checking that a device can be instantiated in isolation. + /// \param [in] valid Reference to the validity checker object + /// performing validation (provides some helper member functions). + /// \sa device_t::device_validity_check + virtual void interface_validity_check(validity_checker &valid) const ATTR_COLD; + + /// \brief Mix-in start handler + /// + /// Implement this member to set up the initial state of the mix-in + /// on start. This is called before the device_start member is + /// called for the device. If the mix-in can't be started until + /// another device has started, throw a #device_missing_dependencies + /// exception. Starting will be postponed until additional devices + /// have been started. + /// + /// Note that this member may be called multiple times if another + /// \c device_interface mix-in throws a #device_missing_dependencies + /// exception from its \c interface_pre_start member, or if the + /// device throws a #device_missing_dependencies exception from its + /// \c device_start member. You must check to ensure that + /// operations like resource allocation are not performed multiple + /// times, or postpone them until #interface_post_start is called. + /// + /// It's simpler to register for save states when + /// #interface_post_start is called. + /// \sa interface_post_start device_t::device_start + virtual void interface_pre_start() ATTR_COLD; + + /// \brief Mix-in start completion handler + /// + /// Implement this member to complete mix-in start-up. This is + /// called after #interface_pre_start is called for all + /// device_interface mix-ins, and after \c device_start is called + /// for the device. This member will only be called once, it will + /// not be called multiple times if device starting is postponed. + /// + /// This member must not throw #device_missing_dependencies (start + /// order dependencies should be checked in #interface_pre_start). + /// This is the appropriate place to allocate resources like + /// timers and register for save states. + /// \sa interface_pre_start device_t::device_start + /// device_t::save_item device_t::save_pointer + virtual void interface_post_start() ATTR_COLD; + + /// \brief Mix-in reset handler + /// + /// Implement this member to provide reset behaviour. This is + /// called before \c device_reset is called for the device, and + /// before any child devices are reset. Only implement warm reset + /// behaviour in this member. Initial cold reset conditions should + /// be set up in #interface_pre_start and/or #interface_post_start. + /// If you need to provide additional behaviour after child devices + /// are reset, implement #interface_post_reset. + /// \sa interface_post_reset device_t::device_reset + virtual void interface_pre_reset() ATTR_COLD; + + /// \brief Mix-in reset completion handler + /// + /// Implement this member to provide additional reset behaviour + /// after child devices are reset. This is called after + /// \c device_reset_after_children has been called for the device. + /// \sa interface_pre_reset device_t::device_reset + /// device_t::device_reset_after_children + virtual void interface_post_reset() ATTR_COLD; + + /// \brief Mix-in stop handler + /// + /// Implement this member to perform additional tasks on ending an + /// emulation session. Do not deallocate anything that may need to + /// be referenced from another device_interface mix-in's + /// \c interface_pre_stop member or from the device's \c device_stop + /// member. This is called before device_stop is called for the + /// device. + /// \sa interface_post_stop device_t::device_stop + virtual void interface_pre_stop() ATTR_COLD; + + /// \brief Mix-in stop completion handler + /// + /// Implement this member to perform additional tasks on ending an + /// emulation session after the device is stopped. You can + /// deallocate memory here. This is called after \c device_stop is + /// called for the device. + /// \sa interface_pre_stop device_t::device_stop + virtual void interface_post_stop() ATTR_COLD; + + /// \brief Prepare for a save state to be written + /// + /// Implement this member to perform any tasks necessary before any + /// registered save state items are recorded. For example it may be + /// necessary to flush caches, serialise self-referencing members or + /// pointers into data structures. This is called before + /// \c device_pre_save is called for the device. + /// \sa interface_post_load device_t::device_pre_save + virtual void interface_pre_save() ATTR_COLD; + + /// \brief Complete save state loading + /// + /// Implement this member to perform any tasks necessary after + /// registered save state items are loaded. For example it may be + /// necessary to update or invalidate caches, or de-serialise + /// pointers into data structures. This is called before + /// \c device_post_load is called for the device. + /// \sa interface_pre_save device_t::device_post_load + virtual void interface_post_load() ATTR_COLD; + + virtual void interface_clock_changed(bool sync_on_new_clock_domain); virtual void interface_debug_setup(); -protected: +private: // internal state device_interface * m_interface_next; device_t & m_device; @@ -726,13 +1214,13 @@ protected: }; -// ======================> device_iterator +// ======================> device_enumerator // helper class to iterate over the hierarchy of devices depth-first -class device_iterator +class device_enumerator { public: - class auto_iterator + class iterator { public: typedef std::ptrdiff_t difference_type; @@ -742,7 +1230,7 @@ public: typedef std::forward_iterator_tag iterator_category; // construction - auto_iterator(device_t *devptr, int curdepth, int maxdepth) + iterator(device_t *devptr, int curdepth, int maxdepth) : m_curdevice(devptr) , m_curdepth(curdepth) , m_maxdepth(maxdepth) @@ -754,12 +1242,12 @@ public: int depth() const { return m_curdepth; } // required operator overrides - bool operator==(auto_iterator const &iter) const { return m_curdevice == iter.m_curdevice; } - bool operator!=(auto_iterator const &iter) const { return m_curdevice != iter.m_curdevice; } + bool operator==(iterator const &iter) const { return m_curdevice == iter.m_curdevice; } + bool operator!=(iterator const &iter) const { return m_curdevice != iter.m_curdevice; } device_t &operator*() const { assert(m_curdevice); return *m_curdevice; } device_t *operator->() const { return m_curdevice; } - auto_iterator &operator++() { advance(); return *this; } - auto_iterator operator++(int) { auto_iterator const result(*this); ++*this; return result; } + iterator &operator++() { advance(); return *this; } + iterator operator++(int) { iterator const result(*this); ++*this; return result; } protected: // search depth-first for the next device @@ -800,18 +1288,17 @@ public: } // protected state - device_t * m_curdevice; - int m_curdepth; - const int m_maxdepth; + device_t * m_curdevice; + int m_curdepth; + int m_maxdepth; }; // construction - device_iterator(device_t &root, int maxdepth = 255) - : m_root(root), m_maxdepth(maxdepth) { } + device_enumerator(device_t &root, int maxdepth = 255) : m_root(root), m_maxdepth(maxdepth) { } // standard iterators - auto_iterator begin() const { return auto_iterator(&m_root, 0, m_maxdepth); } - auto_iterator end() const { return auto_iterator(nullptr, 0, m_maxdepth); } + iterator begin() const { return iterator(&m_root, 0, m_maxdepth); } + iterator end() const { return iterator(nullptr, 0, m_maxdepth); } // return first item device_t *first() const { return begin().current(); } @@ -853,32 +1340,31 @@ public: private: // internal state - device_t & m_root; - int m_maxdepth; + device_t & m_root; + int m_maxdepth; }; -// ======================> device_type_iterator +// ======================> device_type_enumerator // helper class to find devices of a given type in the device hierarchy template <class DeviceType, class DeviceClass = DeviceType> -class device_type_iterator +class device_type_enumerator { public: - class auto_iterator : protected device_iterator::auto_iterator + class iterator : protected device_enumerator::iterator { public: - using device_iterator::auto_iterator::difference_type; - using device_iterator::auto_iterator::iterator_category; - using device_iterator::auto_iterator::depth; + using device_enumerator::iterator::difference_type; + using device_enumerator::iterator::iterator_category; + using device_enumerator::iterator::depth; typedef DeviceClass value_type; typedef DeviceClass *pointer; typedef DeviceClass &reference; // construction - auto_iterator(device_t *devptr, int curdepth, int maxdepth) - : device_iterator::auto_iterator(devptr, curdepth, maxdepth) + iterator(device_t *devptr, int curdepth, int maxdepth) : device_enumerator::iterator(devptr, curdepth, maxdepth) { // make sure the first device is of the specified type while (m_curdevice && (m_curdevice->type().type() != typeid(DeviceType))) @@ -886,8 +1372,8 @@ public: } // required operator overrides - bool operator==(auto_iterator const &iter) const { return m_curdevice == iter.m_curdevice; } - bool operator!=(auto_iterator const &iter) const { return m_curdevice != iter.m_curdevice; } + bool operator==(iterator const &iter) const { return m_curdevice == iter.m_curdevice; } + bool operator!=(iterator const &iter) const { return m_curdevice != iter.m_curdevice; } // getters returning specified device type DeviceClass *current() const { return downcast<DeviceClass *>(m_curdevice); } @@ -895,25 +1381,23 @@ public: DeviceClass *operator->() const { return downcast<DeviceClass *>(m_curdevice); } // search for devices of the specified type - auto_iterator &operator++() + iterator &operator++() { - advance(); - while (m_curdevice && (m_curdevice->type().type() != typeid(DeviceType))) - advance(); + do { advance(); } while (m_curdevice && (m_curdevice->type().type() != typeid(DeviceType))); return *this; } - auto_iterator operator++(int) { auto_iterator const result(*this); ++*this; return result; } + iterator operator++(int) { iterator const result(*this); ++*this; return result; } }; // construction - device_type_iterator(device_t &root, int maxdepth = 255) : m_root(root), m_maxdepth(maxdepth) { } + device_type_enumerator(device_t &root, int maxdepth = 255) : m_root(root), m_maxdepth(maxdepth) { } // standard iterators - auto_iterator begin() const { return auto_iterator(&m_root, 0, m_maxdepth); } - auto_iterator end() const { return auto_iterator(nullptr, 0, m_maxdepth); } - auto_iterator cbegin() const { return auto_iterator(&m_root, 0, m_maxdepth); } - auto_iterator cend() const { return auto_iterator(nullptr, 0, m_maxdepth); } + iterator begin() const { return iterator(&m_root, 0, m_maxdepth); } + iterator end() const { return iterator(nullptr, 0, m_maxdepth); } + iterator cbegin() const { return iterator(&m_root, 0, m_maxdepth); } + iterator cend() const { return iterator(nullptr, 0, m_maxdepth); } // return first item DeviceClass *first() const { return begin().current(); } @@ -951,20 +1435,19 @@ private: }; -// ======================> device_interface_iterator +// ======================> device_interface_enumerator // helper class to find devices with a given interface in the device hierarchy // also works for finding devices derived from a given subclass -template<class InterfaceClass> -class device_interface_iterator +template <class InterfaceClass> +class device_interface_enumerator { public: - class auto_iterator : public device_iterator::auto_iterator + class iterator : public device_enumerator::iterator { -public: + public: // construction - auto_iterator(device_t *devptr, int curdepth, int maxdepth) - : device_iterator::auto_iterator(devptr, curdepth, maxdepth) + iterator(device_t *devptr, int curdepth, int maxdepth) : device_enumerator::iterator(devptr, curdepth, maxdepth) { // set the iterator for the first device with the interface find_interface(); @@ -975,14 +1458,15 @@ public: InterfaceClass &operator*() const { assert(m_interface != nullptr); return *m_interface; } // search for devices with the specified interface - const auto_iterator &operator++() { advance(); find_interface(); return *this; } + const iterator &operator++() { advance(); find_interface(); return *this; } + iterator operator++(int) { iterator const result(*this); ++*this; return result; } -private: + private: // private helper void find_interface() { // advance until finding a device with the interface - for ( ; m_curdevice != nullptr; advance()) + for ( ; m_curdevice; advance()) if (m_curdevice->interface(m_interface)) return; @@ -994,14 +1478,12 @@ private: InterfaceClass *m_interface; }; -public: // construction - device_interface_iterator(device_t &root, int maxdepth = 255) - : m_root(root), m_maxdepth(maxdepth) { } + device_interface_enumerator(device_t &root, int maxdepth = 255) : m_root(root), m_maxdepth(maxdepth) { } // standard iterators - auto_iterator begin() const { return auto_iterator(&m_root, 0, m_maxdepth); } - auto_iterator end() const { return auto_iterator(nullptr, 0, m_maxdepth); } + iterator begin() const { return iterator(&m_root, 0, m_maxdepth); } + iterator end() const { return iterator(nullptr, 0, m_maxdepth); } // return first item InterfaceClass *first() const { return begin().current(); } @@ -1058,15 +1540,15 @@ private: // name relative to this device //------------------------------------------------- -inline device_t *device_t::subdevice(const char *tag) const +inline device_t *device_t::subdevice(std::string_view tag) const { - // empty string or nullptr means this device - if (tag == nullptr || *tag == 0) + // empty string means this device (DEVICE_SELF) + if (tag.empty()) return const_cast<device_t *>(this); // do a quick lookup and return that if possible auto quick = m_subdevices.m_tagmap.find(tag); - return (quick != m_subdevices.m_tagmap.end()) ? quick->second : subdevice_slow(tag); + return (quick != m_subdevices.m_tagmap.end()) ? &quick->second.get() : subdevice_slow(tag); } @@ -1075,21 +1557,21 @@ inline device_t *device_t::subdevice(const char *tag) const // by name relative to this device's parent //------------------------------------------------- -inline device_t *device_t::siblingdevice(const char *tag) const +inline device_t *device_t::siblingdevice(std::string_view tag) const { - // empty string or nullptr means this device - if (tag == nullptr || *tag == 0) + // empty string means this device (DEVICE_SELF) + if (tag.empty()) return const_cast<device_t *>(this); // leading caret implies the owner, just skip it - if (tag[0] == '^') tag++; + if (tag[0] == '^') tag.remove_prefix(1); // query relative to the parent, if we have one if (m_owner != nullptr) return m_owner->subdevice(tag); // otherwise, it's nullptr unless the tag is absolute - return (tag[0] == ':') ? subdevice(tag) : nullptr; + return (!tag.empty() && tag[0] == ':') ? subdevice(tag) : nullptr; } @@ -1108,4 +1590,4 @@ inline device_t::interface_list::auto_iterator device_t::interface_list::auto_it } -#endif /* MAME_EMU_DEVICE_H */ +#endif // MAME_EMU_DEVICE_H diff --git a/src/emu/device.ipp b/src/emu/device.ipp index 9cc7a791278..e633ead4bda 100644 --- a/src/emu/device.ipp +++ b/src/emu/device.ipp @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles /*************************************************************************** - device.h + device.ipp Device interface functions. @@ -28,7 +28,7 @@ typedef device_delegate<void (u32)> clock_update_delegate; // MEMBER TEMPLATES //************************************************************************** -namespace emu { namespace detail { +namespace emu::detail { template <class DeviceClass> template <typename... Params> inline DeviceClass &device_type_impl<DeviceClass>::operator()(machine_config &mconfig, char const *tag, Params &&... args) const @@ -60,8 +60,26 @@ inline DeviceClass &device_type_impl<DeviceClass>::operator()(machine_config_rep return finder = result; } -} } // namespace emu::detail +template <class DeviceClass, bool Required> +inline device_delegate_helper::device_delegate_helper(device_finder<DeviceClass, Required> const &finder) + : device_delegate_helper(finder.finder_target().first, finder.finder_tag()) +{ +} + +template <class DeviceClass, bool Required> +inline void device_delegate_helper::set_tag(device_finder<DeviceClass, Required> const &finder) +{ + std::tie(m_base, m_tag) = finder.finder_target(); +} + +} // namespace emu::detail + +template <typename... T> +inline emu_timer *device_t::timer_alloc(T &&... args) +{ + return machine().scheduler().timer_alloc(timer_expired_delegate(std::forward<T>(args)...)); +} template <typename Format, typename... Params> inline void device_t::popmessage(Format &&fmt, Params &&... args) const @@ -75,7 +93,7 @@ inline void device_t::logerror(Format &&fmt, Params &&... args) const { if (m_machine != nullptr && m_machine->allow_logging()) { - g_profiler.start(PROFILER_LOGERROR); + auto profile = g_profiler.start(PROFILER_LOGERROR); // dump to the buffer m_string_buffer.clear(); @@ -85,23 +103,17 @@ inline void device_t::logerror(Format &&fmt, Params &&... args) const m_string_buffer.put('\0'); m_machine->strlog(&m_string_buffer.vec()[0]); - - g_profiler.stop(); } } template <typename T, typename Ret, typename... Params> -inline std::enable_if_t<device_memory_interface::is_related_class<device_t, T>::value> device_memory_interface::set_addrmap(int spacenum, Ret (T::*func)(Params... args)) -{ - device_t &dev(device().mconfig().current_device()); - set_addrmap(spacenum, address_map_constructor(func, dev.tag(), &downcast<T &>(dev))); -} - -template <typename T, typename Ret, typename... Params> -inline std::enable_if_t<!device_memory_interface::is_related_class<device_t, T>::value> device_memory_interface::set_addrmap(int spacenum, Ret (T::*func)(Params... args)) +inline void device_memory_interface::set_addrmap(int spacenum, Ret (T::*func)(Params... args)) { device_t &dev(device().mconfig().current_device()); - set_addrmap(spacenum, address_map_constructor(func, dev.tag(), &dynamic_cast<T &>(dev))); + if constexpr (is_related_class<device_t, T>::value) + set_addrmap(spacenum, address_map_constructor(func, dev.tag(), &downcast<T &>(dev))); + else + set_addrmap(spacenum, address_map_constructor(func, dev.tag(), &dynamic_cast<T &>(dev))); } #endif // MAME_EMU_DEVICE_IPP diff --git a/src/emu/didisasm.cpp b/src/emu/didisasm.cpp index 7454e54ec3c..1acad7fb050 100644 --- a/src/emu/didisasm.cpp +++ b/src/emu/didisasm.cpp @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles /*************************************************************************** - didisasm.c + didisasm.cpp Device disasm interfaces. @@ -49,7 +49,7 @@ private: device_disasm_interface::device_disasm_interface(const machine_config &mconfig, device_t &device) - : device_interface(device, "disasm"), m_disasm(), m_dasm_override(), m_started(false) + : device_interface(device, "disasm"), m_disasm(), m_dasm_override(device), m_started(false) { } @@ -69,6 +69,6 @@ void device_disasm_interface::interface_pre_start() if (!m_started) { m_started = true; - m_dasm_override.bind_relative_to(*device().owner()); + m_dasm_override.resolve(); } } diff --git a/src/emu/didisasm.h b/src/emu/didisasm.h index c63fe0cc421..fef59c3372e 100644 --- a/src/emu/didisasm.h +++ b/src/emu/didisasm.h @@ -7,6 +7,7 @@ Device disassembly interfaces. ***************************************************************************/ + #ifndef MAME_EMU_DIDISASM_H #define MAME_EMU_DIDISASM_H @@ -35,13 +36,7 @@ public: virtual ~device_disasm_interface() = default; // Override - template <typename Obj> void set_dasm_override(Obj &&cb) { m_dasm_override = std::forward<Obj>(cb); } - void set_dasm_override(dasm_override_delegate callback) { m_dasm_override = callback; } - template <class FunctionClass> void set_dasm_override(offs_t (FunctionClass::*callback)(std::ostream &, offs_t, - const util::disasm_interface::data_buffer &, const util::disasm_interface::data_buffer &), const char *name) - { - set_dasm_override(dasm_override_delegate(callback, name, nullptr, static_cast<FunctionClass *>(nullptr))); - } + template <typename... T> void set_dasm_override(T &&... args) { m_dasm_override.set(std::forward<T>(args)...); } // disassembler request util::disasm_interface &get_disassembler(); @@ -60,6 +55,6 @@ private: }; // iterator -typedef device_interface_iterator<device_disasm_interface> disasm_interface_iterator; +typedef device_interface_enumerator<device_disasm_interface> disasm_interface_enumerator; #endif // MAME_EMU_DIDISASM_H diff --git a/src/emu/diexec.cpp b/src/emu/diexec.cpp index fba63aa1e7d..1cd5266c518 100644 --- a/src/emu/diexec.cpp +++ b/src/emu/diexec.cpp @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles /*************************************************************************** - diexec.c + diexec.cpp Device execution interfaces. @@ -46,9 +46,12 @@ device_execute_interface::device_execute_interface(const machine_config &mconfig : device_interface(device, "execute") , m_scheduler(nullptr) , m_disabled(false) + , m_vblank_interrupt(device) , m_vblank_interrupt_screen(nullptr) + , m_timed_interrupt(device) , m_timed_interrupt_period(attotime::zero) , m_nextexec(nullptr) + , m_driver_irq(device) , m_timedint_timer(nullptr) , m_profiler(PROFILER_IDLE) , m_icountptr(nullptr) @@ -65,9 +68,8 @@ device_execute_interface::device_execute_interface(const machine_config &mconfig , m_divshift(0) , m_cycles_per_second(0) , m_attoseconds_per_cycle(0) + , m_spin_end_timer(nullptr) { - memset(&m_localtime, 0, sizeof(m_localtime)); - // configure the fast accessor assert(!device.interfaces().m_execute); device.interfaces().m_execute = this; @@ -89,19 +91,18 @@ device_execute_interface::~device_execute_interface() // run before we run again //------------------------------------------------- -void device_execute_interface::abort_timeslice() +void device_execute_interface::abort_timeslice() noexcept { // ignore if not the executing device if (!executing()) return; // swallow the remaining cycles - if (m_icountptr != nullptr) + if (m_icountptr != nullptr && *m_icountptr > 0) { - int delta = *m_icountptr; - m_cycles_stolen += delta; - m_cycles_running -= delta; - *m_icountptr -= delta; + m_cycles_stolen += *m_icountptr; + m_cycles_running -= *m_icountptr; + *m_icountptr = 0; } } @@ -126,7 +127,8 @@ void device_execute_interface::suspend_resume_changed() void device_execute_interface::suspend(u32 reason, bool eatcycles) { -if (TEMPLOG) printf("suspend %s (%X)\n", device().tag(), reason); + if (TEMPLOG) printf("suspend %s (%X)\n", device().tag(), reason); + // set the suspend reason and eat cycles flag m_nextsuspend |= reason; m_nexteatcycles = eatcycles; @@ -141,7 +143,8 @@ if (TEMPLOG) printf("suspend %s (%X)\n", device().tag(), reason); void device_execute_interface::resume(u32 reason) { -if (TEMPLOG) printf("resume %s (%X)\n", device().tag(), reason); + if (TEMPLOG) printf("resume %s (%X)\n", device().tag(), reason); + // clear the suspend reason and eat cycles flag m_nextsuspend &= ~reason; suspend_resume_changed(); @@ -161,7 +164,7 @@ void device_execute_interface::spin_until_time(const attotime &duration) suspend_until_trigger(TRIGGER_SUSPENDTIME + timetrig, true); // then set a timer for it - m_scheduler->timer_set(duration, timer_expired_delegate(FUNC(device_execute_interface::timed_trigger_callback),this), TRIGGER_SUSPENDTIME + timetrig, this); + m_spin_end_timer->adjust(duration, TRIGGER_SUSPENDTIME + timetrig); timetrig = (timetrig + 1) % 256; } @@ -204,7 +207,7 @@ void device_execute_interface::trigger(int trigid) // for a device //------------------------------------------------- -attotime device_execute_interface::local_time() const +attotime device_execute_interface::local_time() const noexcept { // if we're active, add in the time from the current slice if (executing()) @@ -222,7 +225,7 @@ attotime device_execute_interface::local_time() const // cycles executed on this device //------------------------------------------------- -u64 device_execute_interface::total_cycles() const +u64 device_execute_interface::total_cycles() const noexcept { if (executing()) { @@ -239,7 +242,7 @@ u64 device_execute_interface::total_cycles() const // of clocks to cycles, rounding down if necessary //------------------------------------------------- -u64 device_execute_interface::execute_clocks_to_cycles(u64 clocks) const +u64 device_execute_interface::execute_clocks_to_cycles(u64 clocks) const noexcept { return clocks; } @@ -250,7 +253,7 @@ u64 device_execute_interface::execute_clocks_to_cycles(u64 clocks) const // of cycles to clocks, rounding down if necessary //------------------------------------------------- -u64 device_execute_interface::execute_cycles_to_clocks(u64 cycles) const +u64 device_execute_interface::execute_cycles_to_clocks(u64 cycles) const noexcept { return cycles; } @@ -262,7 +265,7 @@ u64 device_execute_interface::execute_cycles_to_clocks(u64 cycles) const // operation can take //------------------------------------------------- -u32 device_execute_interface::execute_min_cycles() const +u32 device_execute_interface::execute_min_cycles() const noexcept { return 1; } @@ -274,29 +277,18 @@ u32 device_execute_interface::execute_min_cycles() const // operation can take //------------------------------------------------- -u32 device_execute_interface::execute_max_cycles() const +u32 device_execute_interface::execute_max_cycles() const noexcept { return 1; } //------------------------------------------------- -// execute_input_lines - return the total number -// of input lines for the device -//------------------------------------------------- - -u32 device_execute_interface::execute_input_lines() const -{ - return 0; -} - - -//------------------------------------------------- // execute_default_irq_vector - return the default // IRQ vector when an acknowledge is processed //------------------------------------------------- -u32 device_execute_interface::execute_default_irq_vector(int linenum) const +u32 device_execute_interface::execute_default_irq_vector(int linenum) const noexcept { return 0; } @@ -307,25 +299,13 @@ u32 device_execute_interface::execute_default_irq_vector(int linenum) const // the input line has an asynchronous edge trigger //------------------------------------------------- -bool device_execute_interface::execute_input_edge_triggered(int linenum) const +bool device_execute_interface::execute_input_edge_triggered(int linenum) const noexcept { return false; } //------------------------------------------------- -// execute_burn - called after we consume a bunch -// of cycles for artifical reasons (such as -// spinning devices for performance optimization) -//------------------------------------------------- - -void device_execute_interface::execute_burn(s32 cycles) -{ - // by default, do nothing -} - - -//------------------------------------------------- // execute_set_input - called when a synchronized // input is changed //------------------------------------------------- @@ -347,7 +327,7 @@ void device_execute_interface::interface_validity_check(validity_checker &valid) // validate the interrupts if (!m_vblank_interrupt.isnull()) { - screen_device_iterator iter(device().mconfig().root_device()); + screen_device_enumerator iter(device().mconfig().root_device()); if (iter.first() == nullptr) osd_printf_error("VBLANK interrupt specified, but the driver is screenless\n"); else if (m_vblank_interrupt_screen != nullptr && device().siblingdevice(m_vblank_interrupt_screen) == nullptr) @@ -371,19 +351,26 @@ void device_execute_interface::interface_pre_start() m_scheduler = &device().machine().scheduler(); // bind delegates - m_vblank_interrupt.bind_relative_to(*device().owner()); - m_timed_interrupt.bind_relative_to(*device().owner()); - m_driver_irq.bind_relative_to(*device().owner()); + m_vblank_interrupt.resolve(); + m_timed_interrupt.resolve(); + m_driver_irq.resolve(); // fill in the initial states - int const index = device_iterator(device().machine().root_device()).indexof(*this); + int const index = device_enumerator(device().machine().root_device()).indexof(*this); m_suspend = SUSPEND_REASON_RESET; m_profiler = profile_type(index + PROFILER_DEVICE_FIRST); m_inttrigger = index + TRIGGER_INT; - // allocate timers if we need them + // allocate a timed-interrupt timer if we need it if (m_timed_interrupt_period != attotime::zero) m_timedint_timer = m_scheduler->timer_alloc(timer_expired_delegate(FUNC(device_execute_interface::trigger_periodic_interrupt), this)); + + // allocate a timer for triggering the end of spin-until conditions + m_spin_end_timer = m_scheduler->timer_alloc(timer_expired_delegate(FUNC(device_execute_interface::timed_trigger_callback), this)); + + // allocate input-pulse timers if we have input lines + for (u32 i = 0; i < MAX_INPUT_LINES; i++) + m_pulse_end_timers[i] = m_scheduler->timer_alloc(timer_expired_delegate(FUNC(device_execute_interface::irq_pulse_clear), this)); } @@ -407,9 +394,14 @@ void device_execute_interface::interface_post_start() device().save_item(NAME(m_totalcycles)); device().save_item(NAME(m_localtime)); + // it's more efficient and causes less clutter to save these this way + device().save_item(STRUCT_MEMBER(m_input, m_stored_vector)); + device().save_item(STRUCT_MEMBER(m_input, m_curvector)); + device().save_item(STRUCT_MEMBER(m_input, m_curstate)); + // fill in the input states and IRQ callback information - for (int line = 0; line < ARRAY_LENGTH(m_input); line++) - m_input[line].start(this, line); + for (int line = 0; line < std::size(m_input); line++) + m_input[line].start(*this, line); } @@ -446,7 +438,7 @@ void device_execute_interface::interface_post_reset() if (m_vblank_interrupt_screen != nullptr) { // get the screen that will trigger the VBLANK - screen_device * screen = device().siblingdevice<screen_device>(m_vblank_interrupt_screen); + screen_device *const screen = device().siblingdevice<screen_device>(m_vblank_interrupt_screen); assert(screen != nullptr); screen->register_vblank_callback(vblank_state_delegate(&device_execute_interface::on_vblank, this)); @@ -467,7 +459,7 @@ void device_execute_interface::interface_post_reset() // information for this device //------------------------------------------------- -void device_execute_interface::interface_clock_changed() +void device_execute_interface::interface_clock_changed(bool sync_on_new_clock_domain) { // a clock of zero disables the device if (device().clock() == 0) @@ -484,6 +476,10 @@ void device_execute_interface::interface_clock_changed() m_cycles_per_second = clocks_to_cycles(device().clock()); m_attoseconds_per_cycle = HZ_TO_ATTOSECONDS(m_cycles_per_second); + // resynchronize the localtime to the clock domain when asked to + if (sync_on_new_clock_domain) + m_localtime = attotime::from_ticks(m_localtime.as_ticks(device().clock())+1, device().clock()); + // update the device's divisor s64 attos = m_attoseconds_per_cycle; m_divshift = 0; @@ -500,17 +496,12 @@ void device_execute_interface::interface_clock_changed() //------------------------------------------------- -// standard_irq_callback_member - IRQ acknowledge +// standard_irq_callback - IRQ acknowledge // callback; handles HOLD_LINE case and signals // to the debugger //------------------------------------------------- -IRQ_CALLBACK_MEMBER( device_execute_interface::standard_irq_callback_member ) -{ - return device.execute().standard_irq_callback(irqline); -} - -int device_execute_interface::standard_irq_callback(int irqline) +int device_execute_interface::standard_irq_callback(int irqline, offs_t pc) { // get the default vector and acknowledge the interrupt if needed int vector = m_input[irqline].default_irq_callback(); @@ -522,8 +513,8 @@ int device_execute_interface::standard_irq_callback(int irqline) vector = m_driver_irq(device(), irqline); // notify the debugger - if (device().machine().debug_flags & DEBUG_FLAG_ENABLED) - device().debug()->interrupt_hook(irqline); + if (debugger_enabled()) + device().debug()->interrupt_hook(irqline, pc); return vector; } @@ -593,48 +584,28 @@ TIMER_CALLBACK_MEMBER(device_execute_interface::trigger_periodic_interrupt) void device_execute_interface::pulse_input_line(int irqline, const attotime &duration) { - // treat instantaneous pulses as ASSERT+CLEAR - if (duration == attotime::zero) - { - if (irqline != INPUT_LINE_RESET && !input_edge_triggered(irqline)) - throw emu_fatalerror("device '%s': zero-width pulse is not allowed for input line %d\n", device().tag(), irqline); - - set_input_line(irqline, ASSERT_LINE); - set_input_line(irqline, CLEAR_LINE); - } - else - { - set_input_line(irqline, ASSERT_LINE); - - attotime target_time = local_time() + duration; - m_scheduler->timer_set(target_time - m_scheduler->time(), timer_expired_delegate(FUNC(device_execute_interface::irq_pulse_clear), this), irqline); - } -} - - -//------------------------------------------------- -// pulse_input_line_and_vector - "pulse" an -// input line by asserting it and then clearing it -// later, specifying a vector -//------------------------------------------------- - -void device_execute_interface::pulse_input_line_and_vector(int irqline, int vector, const attotime &duration) -{ - // treat instantaneous pulses as ASSERT+CLEAR + const attotime expiry = m_pulse_end_timers[irqline]->expire(); if (duration == attotime::zero) { - if (irqline != INPUT_LINE_RESET && !input_edge_triggered(irqline)) + // treat instantaneous pulses as ASSERT+CLEAR + if ((irqline != INPUT_LINE_RESET) && !input_edge_triggered(irqline)) throw emu_fatalerror("device '%s': zero-width pulse is not allowed for input line %d\n", device().tag(), irqline); - set_input_line_and_vector(irqline, ASSERT_LINE, vector); - set_input_line_and_vector(irqline, CLEAR_LINE, vector); + if (expiry.is_never() || (expiry <= m_scheduler->time())) + { + set_input_line(irqline, ASSERT_LINE); + set_input_line(irqline, CLEAR_LINE); + } } else { - set_input_line_and_vector(irqline, ASSERT_LINE, vector); + const attotime target_time = local_time() + duration; + if (expiry.is_never() || (target_time > expiry)) + { + set_input_line(irqline, ASSERT_LINE); - attotime target_time = local_time() + duration; - m_scheduler->timer_set(target_time - m_scheduler->time(), timer_expired_delegate(FUNC(device_execute_interface::irq_pulse_clear), this), irqline); + m_pulse_end_timers[irqline]->adjust(target_time - m_scheduler->time(), irqline); + } } } @@ -665,17 +636,12 @@ device_execute_interface::device_input::device_input() // can set ourselves up //------------------------------------------------- -void device_execute_interface::device_input::start(device_execute_interface *execute, int linenum) +void device_execute_interface::device_input::start(device_execute_interface &execute, int linenum) { - m_execute = execute; + m_execute = &execute; m_linenum = linenum; reset(); - - device_t &device = m_execute->device(); - device.save_item(NAME(m_stored_vector), m_linenum); - device.save_item(NAME(m_curvector), m_linenum); - device.save_item(NAME(m_curstate), m_linenum); } @@ -686,7 +652,6 @@ void device_execute_interface::device_input::start(device_execute_interface *exe void device_execute_interface::device_input::reset() { m_curvector = m_stored_vector = m_execute->default_irq_vector(m_linenum); - m_qindex = 0; } @@ -699,21 +664,21 @@ void device_execute_interface::device_input::set_state_synced(int state, int vec { LOG(("set_state_synced('%s',%d,%d,%02x)\n", m_execute->device().tag(), m_linenum, state, vector)); -if (TEMPLOG) printf("setline(%s,%d,%d,%d)\n", m_execute->device().tag(), m_linenum, state, (vector == USE_STORED_VECTOR) ? 0 : vector); + if (TEMPLOG) printf("setline(%s,%d,%d,%d)\n", m_execute->device().tag(), m_linenum, state, (vector == USE_STORED_VECTOR) ? 0 : vector); assert(state == ASSERT_LINE || state == HOLD_LINE || state == CLEAR_LINE); // if we're full of events, flush the queue and log a message int event_index = m_qindex++; - if (event_index >= ARRAY_LENGTH(m_queue)) + if (event_index >= std::size(m_queue)) { m_qindex--; - empty_event_queue(nullptr,0); + empty_event_queue(0); event_index = m_qindex++; m_execute->device().logerror("Exceeded pending input line event queue on device '%s'!\n", m_execute->device().tag()); } // enqueue the event - if (event_index < ARRAY_LENGTH(m_queue)) + if (event_index < std::size(m_queue)) { if (vector == USE_STORED_VECTOR) vector = m_stored_vector; @@ -721,7 +686,7 @@ if (TEMPLOG) printf("setline(%s,%d,%d,%d)\n", m_execute->device().tag(), m_linen // if this is the first one, set the timer if (event_index == 0) - m_execute->scheduler().synchronize(timer_expired_delegate(FUNC(device_execute_interface::device_input::empty_event_queue),this), 0, this); + m_execute->scheduler().synchronize(timer_expired_delegate(FUNC(device_execute_interface::device_input::empty_event_queue),this)); } } @@ -732,7 +697,8 @@ if (TEMPLOG) printf("setline(%s,%d,%d,%d)\n", m_execute->device().tag(), m_linen TIMER_CALLBACK_MEMBER(device_execute_interface::device_input::empty_event_queue) { -if (TEMPLOG) printf("empty_queue(%s,%d,%d)\n", m_execute->device().tag(), m_linenum, m_qindex); + if (TEMPLOG) printf("empty_queue(%s,%d,%d)\n", m_execute->device().tag(), m_linenum, m_qindex); + // loop over all events for (int curevent = 0; curevent < m_qindex; curevent++) { @@ -741,7 +707,7 @@ if (TEMPLOG) printf("empty_queue(%s,%d,%d)\n", m_execute->device().tag(), m_line // set the input line state and vector m_curstate = input_event & 0xff; m_curvector = input_event >> 8; -if (TEMPLOG) printf(" (%d,%d)\n", m_curstate, m_curvector); + if (TEMPLOG) printf(" (%d,%d)\n", m_curstate, m_curvector); assert(m_curstate == ASSERT_LINE || m_curstate == HOLD_LINE || m_curstate == CLEAR_LINE); diff --git a/src/emu/diexec.h b/src/emu/diexec.h index 663cc6ddb1c..1e8303287e8 100644 --- a/src/emu/diexec.h +++ b/src/emu/diexec.h @@ -7,6 +7,7 @@ Device execution interfaces. ***************************************************************************/ + #ifndef MAME_EMU_DIEXEC_H #define MAME_EMU_DIEXEC_H @@ -108,81 +109,67 @@ public: u32 max_cycles() const { return execute_max_cycles(); } attotime cycles_to_attotime(u64 cycles) const { return device().clocks_to_attotime(cycles_to_clocks(cycles)); } u64 attotime_to_cycles(const attotime &duration) const { return clocks_to_cycles(device().attotime_to_clocks(duration)); } - u32 input_lines() const { return execute_input_lines(); } u32 default_irq_vector(int linenum) const { return execute_default_irq_vector(linenum); } bool input_edge_triggered(int linenum) const { return execute_input_edge_triggered(linenum); } // inline configuration helpers void set_disable() { m_disabled = true; } - template <typename Object> void set_vblank_int(Object &&cb, const char *tag) - { - m_vblank_interrupt = std::forward<Object>(cb); - m_vblank_interrupt_screen = tag; - } - void set_vblank_int(device_interrupt_delegate callback, const char *tag) + + template <typename... T> void set_vblank_int(const char *tag, T &&... args) { - m_vblank_interrupt = callback; + m_vblank_interrupt.set(std::forward<T>(args)...); m_vblank_interrupt_screen = tag; } - template <class FunctionClass> void set_vblank_int(const char *tag, const char *devname, void (FunctionClass::*callback)(device_t &), const char *name) - { - set_vblank_int(device_interrupt_delegate(callback, name, devname, static_cast<FunctionClass *>(nullptr)), tag); - } - template <class FunctionClass> void set_vblank_int(const char *tag, void (FunctionClass::*callback)(device_t &), const char *name) + void remove_vblank_int() { - set_vblank_int(device_interrupt_delegate(callback, name, nullptr, static_cast<FunctionClass *>(nullptr)), tag); + m_vblank_interrupt = device_interrupt_delegate(*this); + m_vblank_interrupt_screen = nullptr; } - template <typename Object> void set_periodic_int(Object &&cb, const attotime &rate) + template <typename F> void set_periodic_int(F &&cb, const char *name, const attotime &rate) { - m_timed_interrupt = std::forward<Object>(cb); + m_timed_interrupt.set(std::forward<F>(cb), name); m_timed_interrupt_period = rate; } - void set_periodic_int(device_interrupt_delegate callback, const attotime &rate) + template <typename T, typename F> void set_periodic_int(T &&target, F &&cb, const char *name, const attotime &rate) { - m_timed_interrupt = callback; + m_timed_interrupt.set(std::forward<T>(target), std::forward<F>(cb), name); m_timed_interrupt_period = rate; } - template <class FunctionClass> void set_periodic_int(const char *devname, void (FunctionClass::*callback)(device_t &), const char *name, const attotime &rate) - { - set_periodic_int(device_interrupt_delegate(callback, name, devname, static_cast<FunctionClass *>(nullptr)), rate); - } - template <class FunctionClass> void set_periodic_int(void (FunctionClass::*callback)(device_t &), const char *name, const attotime &rate) + void remove_periodic_int() { - set_periodic_int(device_interrupt_delegate(callback, name, nullptr, static_cast<FunctionClass *>(nullptr)), rate); + m_timed_interrupt = device_interrupt_delegate(*this); + m_timed_interrupt_period = attotime(); } - template <typename Object> void set_irq_acknowledge_callback(Object &&cb) { m_driver_irq = std::forward<Object>(cb); } - void set_irq_acknowledge_callback(device_irq_acknowledge_delegate callback) { m_driver_irq = callback; } - template <class FunctionClass> void set_irq_acknowledge_callback(const char *devname, int (FunctionClass::*callback)(device_t &, int), const char *name) + template <typename... T> void set_irq_acknowledge_callback(T &&... args) { - set_irq_acknowledge_callback(device_irq_acknowledge_delegate(callback, name, devname, static_cast<FunctionClass *>(nullptr))); + m_driver_irq.set(std::forward<T>(args)...); } - template <class FunctionClass> void set_irq_acknowledge_callback(int (FunctionClass::*callback)(device_t &, int), const char *name) + void remove_irq_acknowledge_callback() { - set_irq_acknowledge_callback(device_irq_acknowledge_delegate(callback, name, nullptr, static_cast<FunctionClass *>(nullptr))); + m_driver_irq = device_irq_acknowledge_delegate(*this); } // execution management - device_scheduler &scheduler() const { assert(m_scheduler != nullptr); return *m_scheduler; } - bool executing() const { return scheduler().currently_executing() == this; } - s32 cycles_remaining() const { return executing() ? *m_icountptr : 0; } // cycles remaining in this timeslice - void eat_cycles(int cycles) { if (executing()) *m_icountptr = (cycles > *m_icountptr) ? 0 : (*m_icountptr - cycles); } - void adjust_icount(int delta) { if (executing()) *m_icountptr += delta; } - void abort_timeslice(); + device_scheduler &scheduler() const noexcept { assert(m_scheduler != nullptr); return *m_scheduler; } + bool executing() const noexcept { return scheduler().currently_executing() == this; } + s32 cycles_remaining() const noexcept { return executing() ? *m_icountptr : 0; } // cycles remaining in this timeslice + void eat_cycles(int cycles) noexcept { if (executing()) *m_icountptr = (cycles > *m_icountptr) ? 0 : (*m_icountptr - cycles); } + void adjust_icount(int delta) noexcept { if (executing()) *m_icountptr += delta; } + void abort_timeslice() noexcept; // input and interrupt management - void set_input_line(int linenum, int state) { m_input[linenum].set_state_synced(state); } - void set_input_line_vector(int linenum, int vector) { m_input[linenum].set_vector(vector); } - void set_input_line_and_vector(int linenum, int state, int vector) { m_input[linenum].set_state_synced(state, vector); } - int input_state(int linenum) const { return m_input[linenum].m_curstate; } + void set_input_line(int linenum, int state) { assert(device().started()); m_input[linenum].set_state_synced(state); } + void set_input_line_vector(int linenum, int vector) { assert(device().started()); m_input[linenum].set_vector(vector); } + void set_input_line_and_vector(int linenum, int state, int vector) { assert(device().started()); m_input[linenum].set_state_synced(state, vector); } + int input_line_state(int linenum) const { assert(device().started()); return m_input[linenum].m_curstate; } void pulse_input_line(int irqline, const attotime &duration); - void pulse_input_line_and_vector(int irqline, int vector, const attotime &duration); // suspend/resume void suspend(u32 reason, bool eatcycles); void resume(u32 reason); - bool suspended(u32 reason = SUSPEND_ANY_REASON) const { return (m_nextsuspend & reason) != 0; } + bool suspended(u32 reason = SUSPEND_ANY_REASON) const noexcept { return (m_nextsuspend & reason) != 0; } void yield() { suspend(SUSPEND_REASON_TIMESLICE, false); } void spin() { suspend(SUSPEND_REASON_TIMESLICE, true); } void spin_until_trigger(int trigid) { suspend_until_trigger(trigid, true); } @@ -195,31 +182,28 @@ public: void signal_interrupt_trigger() { trigger(m_inttrigger); } // time and cycle accounting - attotime local_time() const; - u64 total_cycles() const; + attotime local_time() const noexcept; + u64 total_cycles() const noexcept; // required operation overrides void run() { execute_run(); } - // deliberately ambiguous functions; if you have the execute interface - // just use it + // deliberately ambiguous functions; if you have the execute interface just use it device_execute_interface &execute() { return *this; } protected: // clock and cycle information getters - virtual u64 execute_clocks_to_cycles(u64 clocks) const; - virtual u64 execute_cycles_to_clocks(u64 cycles) const; - virtual u32 execute_min_cycles() const; - virtual u32 execute_max_cycles() const; + virtual u64 execute_clocks_to_cycles(u64 clocks) const noexcept; + virtual u64 execute_cycles_to_clocks(u64 cycles) const noexcept; + virtual u32 execute_min_cycles() const noexcept; + virtual u32 execute_max_cycles() const noexcept; // input line information getters - virtual u32 execute_input_lines() const; - virtual u32 execute_default_irq_vector(int linenum) const; - virtual bool execute_input_edge_triggered(int linenum) const; + virtual u32 execute_default_irq_vector(int linenum) const noexcept; + virtual bool execute_input_edge_triggered(int linenum) const noexcept; // optional operation overrides virtual void execute_run() = 0; - virtual void execute_burn(s32 cycles); virtual void execute_set_input(int linenum, int state); // interface-level overrides @@ -228,13 +212,11 @@ protected: virtual void interface_post_start() override; virtual void interface_pre_reset() override; virtual void interface_post_reset() override; - virtual void interface_clock_changed() override; + virtual void interface_clock_changed(bool sync_on_new_clock_domain) override; // for use by devcpu for now... - int current_input_state(unsigned i) const { return m_input[i].m_curstate; } void set_icountptr(int &icount) { assert(!m_icountptr); m_icountptr = &icount; } - IRQ_CALLBACK_MEMBER(standard_irq_callback_member); - int standard_irq_callback(int irqline); + int standard_irq_callback(int irqline, offs_t pc); // debugger hooks bool debugger_enabled() const { return bool(device().machine().debug_flags & DEBUG_FLAG_ENABLED); } @@ -255,16 +237,22 @@ protected: device().debug()->privilege_hook(); } + void debugger_wait_hook() + { + if (device().machine().debug_flags & DEBUG_FLAG_CALL_HOOK) + device().debug()->wait_hook(); + } + private: // internal information about the state of inputs class device_input { - static const int USE_STORED_VECTOR = 0xff000000; + static constexpr int USE_STORED_VECTOR = 0xff000000; public: device_input(); - void start(device_execute_interface *execute, int linenum); + void start(device_execute_interface &execute, int linenum); void reset(); void set_state_synced(int state, int vector = USE_STORED_VECTOR); @@ -316,7 +304,9 @@ private: // cycle counting and executing profile_type m_profiler; // profiler tag +protected: // TODO: decide whether to bring up the wait-state methods int * m_icountptr; // pointer to the icount +private: int m_cycles_running; // number of cycles we are executing int m_cycles_stolen; // number of cycles we artificially stole @@ -329,15 +319,16 @@ private: s32 m_inttrigger; // interrupt trigger index // clock and timing information -protected: // FIXME: MIPS3 accesses m_totalcycles directly from execute_burn - devise a better solution u64 m_totalcycles; // total device cycles executed -private: attotime m_localtime; // local time, relative to the timer system's global time s32 m_divisor; // 32-bit attoseconds_per_cycle divisor u8 m_divshift; // right shift amount to fit the divisor into 32 bits u32 m_cycles_per_second; // cycles per second, adjusted for multipliers attoseconds_t m_attoseconds_per_cycle; // attoseconds per adjusted clock cycle + emu_timer * m_spin_end_timer; // timer for triggering the end of spin_until_time + emu_timer * m_pulse_end_timers[MAX_INPUT_LINES]; // timer for ending input-line pulses + // callbacks TIMER_CALLBACK_MEMBER(timed_trigger_callback) { trigger(param); } @@ -354,6 +345,6 @@ public: }; // iterator -typedef device_interface_iterator<device_execute_interface> execute_interface_iterator; +typedef device_interface_enumerator<device_execute_interface> execute_interface_enumerator; #endif // MAME_EMU_DIEXEC_H diff --git a/src/emu/digfx.cpp b/src/emu/digfx.cpp index 0055421b80c..31f87216775 100644 --- a/src/emu/digfx.cpp +++ b/src/emu/digfx.cpp @@ -2,7 +2,7 @@ // copyright-holders:Nicola Salmoria, Aaron Giles, Alex W. Jackson /*************************************************************************** - digfx.c + digfx.cpp Device graphics interfaces. @@ -99,6 +99,24 @@ void device_gfx_interface::interface_post_start() //------------------------------------------------- +// interface_post_load - mark RAM-based entries +// dirty after loading save state +//------------------------------------------------- + +void device_gfx_interface::interface_post_load() +{ + if (!m_gfxdecodeinfo) + return; + + for (int curgfx = 0; curgfx < MAX_GFX_ELEMENTS && m_gfxdecodeinfo[curgfx].gfxlayout != nullptr; curgfx++) + { + if (GFXENTRY_ISRAM(m_gfxdecodeinfo[curgfx].flags)) + m_gfx[curgfx]->mark_all_dirty(); + } +} + + +//------------------------------------------------- // decode_gfx - parse gfx decode info and // create gfx elements //------------------------------------------------- @@ -106,7 +124,7 @@ void device_gfx_interface::interface_post_start() void device_gfx_interface::decode_gfx(const gfx_decode_entry *gfxdecodeinfo) { // skip if nothing to do - if (gfxdecodeinfo == nullptr) + if (!gfxdecodeinfo) return; // local variables to hold mutable copies of gfx layout data @@ -298,9 +316,9 @@ void device_gfx_interface::interface_validity_check(validity_checker &valid) con return; // validate graphics decoding entries - for (int gfxnum = 0; gfxnum < MAX_GFX_ELEMENTS && m_gfxdecodeinfo[gfxnum].gfxlayout != nullptr; gfxnum++) + for (int curgfx = 0; curgfx < MAX_GFX_ELEMENTS && m_gfxdecodeinfo[curgfx].gfxlayout != nullptr; curgfx++) { - const gfx_decode_entry &gfx = m_gfxdecodeinfo[gfxnum]; + const gfx_decode_entry &gfx = m_gfxdecodeinfo[curgfx]; const gfx_layout &layout = *gfx.gfxlayout; // currently we are unable to validate RAM-based entries @@ -316,7 +334,7 @@ void device_gfx_interface::interface_validity_check(validity_checker &valid) con u32 region_length = valid.region_length(gfxregion.c_str()); if (region_length == 0) - osd_printf_error("gfx[%d] references nonexistent region '%s'\n", gfxnum, gfxregion.c_str()); + osd_printf_error("gfx[%d] references nonexistent region '%s'\n", curgfx, gfxregion); // if we have a valid region, and we're not using auto-sizing, check the decode against the region length else if (!IS_FRAC(layout.total)) @@ -336,7 +354,7 @@ void device_gfx_interface::interface_validity_check(validity_checker &valid) con // if not, this is an error if ((start + len) / 8 > avail) - osd_printf_error("gfx[%d] extends past allocated memory of region '%s'\n", gfxnum, region); + osd_printf_error("gfx[%d] extends past allocated memory of region '%s'\n", curgfx, region); } } @@ -347,9 +365,9 @@ void device_gfx_interface::interface_validity_check(validity_checker &valid) con if (layout.planeoffset[0] == GFX_RAW) { if (layout.total != RGN_FRAC(1,1)) - osd_printf_error("gfx[%d] RAW layouts can only be RGN_FRAC(1,1)\n", gfxnum); + osd_printf_error("gfx[%d] RAW layouts can only be RGN_FRAC(1,1)\n", curgfx); if (xscale != 1 || yscale != 1) - osd_printf_error("gfx[%d] RAW layouts do not support xscale/yscale\n", gfxnum); + osd_printf_error("gfx[%d] RAW layouts do not support xscale/yscale\n", curgfx); } // verify traditional decode doesn't have too many planes, @@ -357,11 +375,11 @@ void device_gfx_interface::interface_validity_check(validity_checker &valid) con else { if (layout.planes > MAX_GFX_PLANES) - osd_printf_error("gfx[%d] planes > %d\n", gfxnum, MAX_GFX_PLANES); + osd_printf_error("gfx[%d] planes > %d\n", curgfx, MAX_GFX_PLANES); if (layout.width > MAX_GFX_SIZE && layout.extxoffs == nullptr) - osd_printf_error("gfx[%d] width > %d but missing extended xoffset info\n", gfxnum, MAX_GFX_SIZE); + osd_printf_error("gfx[%d] width > %d but missing extended xoffset info\n", curgfx, MAX_GFX_SIZE); if (layout.height > MAX_GFX_SIZE && layout.extyoffs == nullptr) - osd_printf_error("gfx[%d] height > %d but missing extended yoffset info\n", gfxnum, MAX_GFX_SIZE); + osd_printf_error("gfx[%d] height > %d but missing extended yoffset info\n", curgfx, MAX_GFX_SIZE); } } } diff --git a/src/emu/digfx.h b/src/emu/digfx.h index 3829cd2882e..8c18c1f37da 100644 --- a/src/emu/digfx.h +++ b/src/emu/digfx.h @@ -72,7 +72,7 @@ const gfx_layout name = { width, height, RGN_FRAC(1,1), 8, { GFX_RAW }, { 0 }, { #define STEP2048(START,STEP) STEP1024(START,STEP),STEP1024((START)+1024*(STEP),STEP) #define STEP2_INV(START,STEP) (START)+(STEP),(START) -#define STEP4_INV(START,STEP) STEP2_INV(START+2*STEP,STEP),STEP2_INV(START,STEP) +#define STEP4_INV(START,STEP) STEP2_INV(START+2*STEP,STEP),STEP2_INV(START,STEP) //************************************************************************** // GRAPHICS INFO MACROS @@ -118,6 +118,7 @@ const gfx_layout name = { width, height, RGN_FRAC(1,1), 8, { GFX_RAW }, { 0 }, { #define GFXDECODE_DEVICE_RAM(region,offset,layout,start,colors) { region, offset, &layout, start, colors, GFXENTRY_DEVICE | GFXENTRY_RAM }, #define GFXDECODE_SCALE(region,offset,layout,start,colors,x,y) { region, offset, &layout, start, colors, GFXENTRY_XSCALE(x) | GFXENTRY_YSCALE(y) }, #define GFXDECODE_REVERSEBITS(region,offset,layout,start,colors) { region, offset, &layout, start, colors, GFXENTRY_REVERSE }, +#define GFXDECODE_DEVICE_REVERSEBITS(region,offset,layout,start,colors) { region, offset, &layout, start, colors, GFXENTRY_DEVICE | GFXENTRY_REVERSE }, @@ -185,21 +186,22 @@ protected: virtual void interface_validity_check(validity_checker &valid) const override; virtual void interface_pre_start() override; virtual void interface_post_start() override; + virtual void interface_post_load() override; private: - optional_device<device_palette_interface> m_palette; // configured tag for palette device - std::unique_ptr<gfx_element> m_gfx[MAX_GFX_ELEMENTS]; // array of pointers to graphic sets + optional_device<device_palette_interface> m_palette; // configured tag for palette device + std::unique_ptr<gfx_element> m_gfx[MAX_GFX_ELEMENTS]; // array of pointers to graphic sets // configuration - const gfx_decode_entry * m_gfxdecodeinfo; // pointer to array of gfx decode information - bool m_palette_is_disabled; // no palette associated with this gfx decode + const gfx_decode_entry * m_gfxdecodeinfo; // pointer to array of gfx decode information + bool m_palette_is_disabled; // no palette associated with this gfx decode // internal state - bool m_decoded; // have we processed our decode info yet? + bool m_decoded; // have we processed our decode info yet? }; // iterator -typedef device_interface_iterator<device_gfx_interface> gfx_interface_iterator; +typedef device_interface_enumerator<device_gfx_interface> gfx_interface_enumerator; #endif /* MAME_EMU_DIGFX_H */ diff --git a/src/emu/diimage.cpp b/src/emu/diimage.cpp index 953e89dc42c..59d1523a6b1 100644 --- a/src/emu/diimage.cpp +++ b/src/emu/diimage.cpp @@ -2,7 +2,7 @@ // copyright-holders:Miodrag Milanovic /*************************************************************************** - diimage.c + diimage.cpp Device image interfaces. @@ -11,44 +11,20 @@ #include "emu.h" #include "emuopts.h" -#include "drivenum.h" +#include "fileio.h" #include "romload.h" -#include "ui/uimain.h" -#include "zippath.h" #include "softlist.h" #include "softlist_dev.h" -#include "formats/ioprocs.h" - -#include <regex> +#include "corestr.h" +#include "opresolv.h" +#include "path.h" +#include "zippath.h" -//************************************************************************** -// DEVICE CONFIG IMAGE INTERFACE -//************************************************************************** -const image_device_type_info device_image_interface::m_device_info_array[] = - { - { IO_UNKNOWN, "unknown", "unkn" }, /* 0 */ - { IO_CARTSLOT, "cartridge", "cart" }, /* 1 */ - { IO_FLOPPY, "floppydisk", "flop" }, /* 2 */ - { IO_HARDDISK, "harddisk", "hard" }, /* 3 */ - { IO_CYLINDER, "cylinder", "cyln" }, /* 4 */ - { IO_CASSETTE, "cassette", "cass" }, /* 5 */ - { IO_PUNCHCARD, "punchcard", "pcrd" }, /* 6 */ - { IO_PUNCHTAPE, "punchtape", "ptap" }, /* 7 */ - { IO_PRINTER, "printout", "prin" }, /* 8 */ - { IO_SERIAL, "serial", "serl" }, /* 9 */ - { IO_PARALLEL, "parallel", "parl" }, /* 10 */ - { IO_SNAPSHOT, "snapshot", "dump" }, /* 11 */ - { IO_QUICKLOAD, "quickload", "quik" }, /* 12 */ - { IO_MEMCARD, "memcard", "memc" }, /* 13 */ - { IO_CDROM, "cdrom", "cdrm" }, /* 14 */ - { IO_MAGTAPE, "magtape", "magt" }, /* 15 */ - { IO_ROM, "romimage", "rom" }, /* 16 */ - { IO_MIDIIN, "midiin", "min" }, /* 17 */ - { IO_MIDIOUT, "midiout", "mout" }, /* 18 */ - { IO_PICTURE, "picture", "pic" }, /* 19 */ - { IO_VIDEO, "vidfile", "vid" } /* 20 */ - }; +#include <algorithm> +#include <cstring> +#include <regex> +#include <sstream> //************************************************************************** @@ -89,16 +65,18 @@ image_device_format::~image_device_format() device_image_interface::device_image_interface(const machine_config &mconfig, device_t &device) : device_interface(device, "image") - , m_err() , m_file() , m_mame_file() + , m_default_region(-1) + , m_current_region(-1) , m_software_part_ptr(nullptr) - , m_supported(0) + , m_sequence_counter(0) , m_readonly(false) , m_created(false) , m_create_format(0) , m_create_args(nullptr) , m_user_loadable(true) + , m_must_be_loaded(false) , m_is_loading(false) , m_is_reset_and_loading(false) { @@ -113,6 +91,17 @@ device_image_interface::~device_image_interface() { } +//------------------------------------------------- +// add_region - register a region that may +// have a chd image +//------------------------------------------------- +void device_image_interface::add_region(std::string name, bool is_default) +{ + if (is_default) + m_default_region = m_possible_preset_regions.size(); + m_possible_preset_regions.push_back(name); +} + //------------------------------------------------- // interface_config_complete - perform any @@ -126,75 +115,150 @@ void device_image_interface::interface_config_complete() update_names(); } - //------------------------------------------------- -// find_device_type - search trough list of -// device types to extract data +// check_preset_images - lookup the CHDs from the +// region(s), if any //------------------------------------------------- -const image_device_type_info *device_image_interface::find_device_type(iodevice_t type) +void device_image_interface::check_preset_images() { - int i; - for (i = 0; i < ARRAY_LENGTH(device_image_interface::m_device_info_array); i++) + if (!m_possible_preset_regions.empty()) + { + for(const auto &r : m_possible_preset_regions) + m_preset_images.push_back(device().machine().rom_load().get_disk_handle(":" + r)); + if (m_default_region != -1 && m_preset_images[m_default_region]) + m_current_region = m_default_region; + else + { + for (m_current_region = 0; m_current_region != int(m_preset_images.size()) && !m_preset_images[m_current_region]; m_current_region++); + if (m_current_region == int(m_preset_images.size())) + fatalerror("%s: No configured region has an image\n", device().tag()); + } + set_image_tag(); + set_user_loadable(false); + } + else { - if (m_device_info_array[i].m_type == type) - return &m_device_info_array[i]; + std::string tag = device().owner()->tag(); + auto *const chd = device().machine().rom_load().get_disk_handle(tag); + if (chd) + { + m_possible_preset_regions.emplace_back(std::move(tag)); + m_preset_images.push_back(chd); + m_current_region = 0; + set_image_tag(); + set_user_loadable(false); + } } - return nullptr; } //------------------------------------------------- -// device_typename - retrieves device type name +// has_preset_images - does the device have an +// image to retrieve through current_image_* //------------------------------------------------- -const char *device_image_interface::device_typename(iodevice_t type) +bool device_image_interface::has_preset_images() const { - const image_device_type_info *info = find_device_type(type); - return (info != nullptr) ? info->m_name : "unknown"; + return !m_possible_preset_regions.empty(); } //------------------------------------------------- -// device_brieftypename - retrieves device -// brief type name +// has_preset_images - does the device have +// multiple preset images with user selection //------------------------------------------------- -const char *device_image_interface::device_brieftypename(iodevice_t type) +bool device_image_interface::has_preset_images_selection() const { - const image_device_type_info *info = find_device_type(type); - return (info != nullptr) ? info->m_shortname : "unk"; + int icount = 0; + for (const auto *f : m_preset_images) + if (f) + icount ++; + return icount > 1; } + //------------------------------------------------- -// device_typeid - retrieves device type id +// preset_images_list -- generate the list of +// available image names //------------------------------------------------- -iodevice_t device_image_interface::device_typeid(const char *name) +std::vector<std::string> device_image_interface::preset_images_list() const { - int i; - for (i = 0; i < ARRAY_LENGTH(device_image_interface::m_device_info_array); i++) - { - if (!core_stricmp(name, m_device_info_array[i].m_name) || !core_stricmp(name, m_device_info_array[i].m_shortname)) - return m_device_info_array[i].m_type; - } - return (iodevice_t)-1; + std::vector<std::string> result; + for (unsigned int i = 0; i != m_preset_images.size(); i++) + if (m_preset_images[i]) + result.push_back(m_possible_preset_regions[i]); + return result; +} + +//------------------------------------------------- +// current_preset_image_id -- current image id, +// recomputed to ignore non-present images. +// returns -1 if not in preset mode +//------------------------------------------------- + +int device_image_interface::current_preset_image_id() const +{ + if (m_current_region == -1) + return -1; + int id = 0; + for (int i = 0; i != m_current_region; i++) + if (m_preset_images[i]) + id++; + return id; +} + +//------------------------------------------------- +// current_preset_image_chd -- return the chd of +// the current active image, nullptr if non +//------------------------------------------------- + +chd_file *device_image_interface::current_preset_image_chd() const +{ + if (m_current_region == -1) + return nullptr; + return m_preset_images[m_current_region]; +} + +//------------------------------------------------- +// switch_preset_image -- change of preset image +//------------------------------------------------- + +void device_image_interface::switch_preset_image(int id) +{ + for (unsigned int i = 0; i != m_preset_images.size(); i++) + if (m_preset_images[i]) + { + if(!id) + { + call_unload(); + m_current_region = i; + call_load(); + break; + } + id--; + } + + return; } + //------------------------------------------------- // set_image_filename - specifies the filename of // an image //------------------------------------------------- -void device_image_interface::set_image_filename(const std::string &filename) +void device_image_interface::set_image_filename(std::string_view filename) { m_image_name = filename; - util::zippath_parent(m_working_directory, filename); + m_working_directory = util::zippath_parent(m_image_name); m_basename.assign(m_image_name); // find the last "path separator" auto iter = std::find_if( - m_image_name.rbegin(), - m_image_name.rend(), - [](char c) { return (c == '\\') || (c == '/') || (c == ':'); }); + m_image_name.rbegin(), + m_image_name.rend(), + [] (char c) { return (c == '\\') || (c == '/') || (c == ':'); }); if (iter != m_image_name.rend()) m_basename.assign(iter.base(), m_image_name.end()); @@ -207,222 +271,119 @@ void device_image_interface::set_image_filename(const std::string &filename) m_filetype = core_filename_extract_extension(m_basename, true); } - -/**************************************************************************** - CREATION FORMATS -****************************************************************************/ - //------------------------------------------------- -// device_get_named_creatable_format - -// accesses a specific image format available for -// image creation by name -//------------------------------------------------- - -const image_device_format *device_image_interface::device_get_named_creatable_format(const std::string &format_name) -{ - for (auto &format : m_formatlist) - if (format->name() == format_name) - return format.get(); - return nullptr; -} - - -//------------------------------------------------- -// add_format +// set_image_tag - specifies the filename of +// an image as the device tag //------------------------------------------------- -void device_image_interface::add_format(std::unique_ptr<image_device_format> &&format) +void device_image_interface::set_image_tag() { - m_formatlist.push_back(std::move(format)); + m_image_name = device().owner()->tag(); + m_working_directory = ""; + m_basename = ""; + m_basename_noext = ""; + m_filetype = ""; } //------------------------------------------------- -// add_format +// is_filetype - check if the filetype matches //------------------------------------------------- -void device_image_interface::add_format(std::string &&name, std::string &&description, std::string &&extensions, std::string &&optspec) +bool device_image_interface::is_filetype(std::string_view candidate_filetype) const { - auto format = std::make_unique<image_device_format>(std::move(name), std::move(description), std::move(extensions), std::move(optspec)); - add_format(std::move(format)); + return util::streqlower(m_filetype, candidate_filetype); } -/**************************************************************************** - ERROR HANDLING -****************************************************************************/ - //------------------------------------------------- -// image_clear_error - clear out any specified -// error +// add_media_change_notifier - subscribe for +// media change notifications //------------------------------------------------- -void device_image_interface::clear_error() +util::notifier_subscription device_image_interface::add_media_change_notifier(delegate<void (media_change_event)> &&n) { - m_err = IMAGE_ERROR_SUCCESS; - if (!m_err_message.empty()) - { - m_err_message.clear(); - } + return m_media_change_notifier.subscribe(std::move(n)); } +//*************************************************************************** +// CREATION FORMATS +//*************************************************************************** //------------------------------------------------- -// error - returns the error text for an image -// error +// device_get_named_creatable_format - +// accesses a specific image format available for +// image creation by name //------------------------------------------------- -static const char *const messages[] = +const image_device_format *device_image_interface::device_get_named_creatable_format(std::string_view format_name) const noexcept { - "", - "Internal error", - "Unsupported operation", - "Out of memory", - "File not found", - "Invalid image", - "File already open", - "Unspecified error" -}; - -const char *device_image_interface::error() -{ - return (!m_err_message.empty()) ? m_err_message.c_str() : messages[m_err]; + for (const auto &format : m_formatlist) + if (std::string_view(format->name()) == format_name) + return format.get(); + return nullptr; } - //------------------------------------------------- -// seterror - specifies an error on an image +// add_format //------------------------------------------------- -void device_image_interface::seterror(image_error_t err, const char *message) +void device_image_interface::add_format(std::unique_ptr<image_device_format> &&format) { - clear_error(); - m_err = err; - if (message != nullptr) - { - m_err_message = message; - } + m_formatlist.push_back(std::move(format)); } - //------------------------------------------------- -// message - used to display a message while -// loading +// add_format //------------------------------------------------- -void device_image_interface::message(const char *format, ...) +void device_image_interface::add_format(std::string &&name, std::string &&description, std::string &&extensions, std::string &&optspec) { - va_list args; - char buffer[256]; - - /* format the message */ - va_start(args, format); - vsnprintf(buffer, ARRAY_LENGTH(buffer), format, args); - va_end(args); - - /* display the popup for a standard amount of time */ - device().machine().ui().popup_time(5, "%s: %s", - basename(), - buffer); + auto format = std::make_unique<image_device_format>(std::move(name), std::move(description), std::move(extensions), std::move(optspec)); + add_format(std::move(format)); } -/*************************************************************************** - WORKING DIRECTORIES -***************************************************************************/ - -//------------------------------------------------- -// try_change_working_directory - tries to change -// the working directory, but only if the directory -// actually exists -//------------------------------------------------- - -bool device_image_interface::try_change_working_directory(const std::string &subdir) -{ - const osd::directory::entry *entry; - bool success = false; - bool done = false; - - auto directory = osd::directory::open(m_working_directory); - if (directory) - { - while (!done && (entry = directory->read()) != nullptr) - { - if (!core_stricmp(subdir.c_str(), entry->name)) - { - done = true; - success = entry->type == osd::directory::entry::entry_type::DIR; - } - } - - directory.reset(); - } - - // did we successfully identify the directory? - if (success) - m_working_directory = util::zippath_combine(m_working_directory, subdir); - - return success; -} - +//*************************************************************************** +// ERROR HANDLING +//*************************************************************************** //------------------------------------------------- -// setup_working_directory - sets up the working -// directory according to a few defaults +// error - returns the error text for an image +// error //------------------------------------------------- -void device_image_interface::setup_working_directory() +std::error_category const &image_category() noexcept { - bool success = false; - // get user-specified directory and make sure it exists - m_working_directory = device().mconfig().options().sw_path(); - // if multipath, get first - size_t i = m_working_directory.find_first_of(";"); - if (i != std::string::npos) - m_working_directory.resize(i); - // validate directory - if (!m_working_directory.empty()) - if (osd::directory::open(m_working_directory)) - success = true; - - // if not exist, use previous method - if (!success) + class image_category_impl : public std::error_category { - // first set up the working directory to be the starting directory - osd_get_full_path(m_working_directory, "."); - // now try browsing down to "software" - if (try_change_working_directory("software")) - success = true; - } + public: + virtual char const *name() const noexcept override { return "image"; } - if (success) - { - // now down to a directory for this computer - int gamedrv = driver_list::find(device().machine().system()); - while(gamedrv != -1 && !try_change_working_directory(driver_list::driver(gamedrv).name)) + virtual std::string message(int condition) const override { - gamedrv = driver_list::compatible_with(gamedrv); + using namespace std::literals; + static std::string_view const s_messages[] = { + "No error"sv, + "Internal error"sv, + "Unsupported operation"sv, + "Invalid image"sv, + "Invalid image length"sv, + "File already open"sv, + "Unrecognized software item"sv, + "Invalid software item"sv, + "Unspecified error"sv }; + if ((0 <= condition) && (std::size(s_messages) > condition)) + return std::string(s_messages[condition]); + else + return "Unknown error"s; } - } -} - - -//------------------------------------------------- -// working_directory - returns the working -// directory to use for this image; this is -// valid even if not mounted -//------------------------------------------------- - -const std::string &device_image_interface::working_directory() -{ - // check to see if we've never initialized the working directory - if (m_working_directory.empty()) - setup_working_directory(); - - return m_working_directory; + }; + static image_category_impl const s_image_category_instance; + return s_image_category_instance; } @@ -431,7 +392,7 @@ const std::string &device_image_interface::working_directory() // software_info structure from the softlist //------------------------------------------------- -const software_info *device_image_interface::software_entry() const +const software_info *device_image_interface::software_entry() const noexcept { return !m_software_part_ptr ? nullptr : &m_software_part_ptr->info(); } @@ -441,14 +402,13 @@ const software_info *device_image_interface::software_entry() const // get_software_region //------------------------------------------------- -u8 *device_image_interface::get_software_region(const char *tag) +u8 *device_image_interface::get_software_region(std::string_view tag) { if (!loaded_through_softlist()) return nullptr; - std::string full_tag = util::string_format("%s:%s", device().tag(), tag); - memory_region *region = device().machine().root_device().memregion(full_tag.c_str()); - return region != nullptr ? region->base() : nullptr; + memory_region *const region = device().memregion(tag); + return region ? region->base() : nullptr; } @@ -456,11 +416,13 @@ u8 *device_image_interface::get_software_region(const char *tag) // image_get_software_region_length //------------------------------------------------- -u32 device_image_interface::get_software_region_length(const char *tag) +u32 device_image_interface::get_software_region_length(std::string_view tag) { - std::string full_tag = util::string_format("%s:%s", device().tag(), tag); - memory_region *region = device().machine().root_device().memregion(full_tag.c_str()); - return region != nullptr ? region->bytes() : 0; + if (!loaded_through_softlist()) + return 0; + + memory_region *const region = device().memregion(tag); + return region ? region->bytes() : 0; } @@ -468,7 +430,7 @@ u32 device_image_interface::get_software_region_length(const char *tag) // image_get_feature //------------------------------------------------- -const char *device_image_interface::get_feature(const char *feature_name) const +const char *device_image_interface::get_feature(std::string_view feature_name) const { return !m_software_part_ptr ? nullptr : m_software_part_ptr->feature(feature_name); } @@ -478,18 +440,18 @@ const char *device_image_interface::get_feature(const char *feature_name) const // load_software_region - //------------------------------------------------- -bool device_image_interface::load_software_region(const char *tag, optional_shared_ptr<u8> &ptr) +std::error_condition device_image_interface::load_software_region(std::string_view tag, std::unique_ptr<u8[]> &ptr) { size_t size = get_software_region_length(tag); if (size) { - ptr.allocate(size); - - memcpy(ptr, get_software_region(tag), size); + ptr = std::make_unique<u8[]>(size); + memcpy(ptr.get(), get_software_region(tag), size); + return std::error_condition(); } - - return size > 0; + else + return image_error::UNSUPPORTED; } @@ -500,45 +462,30 @@ bool device_image_interface::load_software_region(const char *tag, optional_shar // to be loaded // **************************************************************************** -bool device_image_interface::run_hash(util::core_file &file, u32 skip_bytes, util::hash_collection &hashes, const char *types) +std::error_condition device_image_interface::run_hash(util::random_read &file, u32 skip_bytes, util::hash_collection &hashes, const char *types) { // reset the hash; we want to override existing data hashes.reset(); // figure out the size, and "cap" the skip bytes - u64 size = file.size(); - skip_bytes = (u32) std::min((u64) skip_bytes, size); - - // seek to the beginning - file.seek(skip_bytes, SEEK_SET); - u64 position = skip_bytes; - - // keep on reading hashes - hashes.begin(types); - while (position < size) - { - uint8_t buffer[8192]; - - // read bytes - const u32 count = (u32) std::min(size - position, (u64) sizeof(buffer)); - const u32 actual_count = file.read(buffer, count); - if (actual_count == 0) - return false; - position += actual_count; - - // and compute the hashes - hashes.buffer(buffer, actual_count); - } - hashes.end(); - - // cleanup - file.seek(0, SEEK_SET); - return true; + u64 size; + std::error_condition filerr = file.length(size); + if (filerr) + return filerr; + skip_bytes = u32(std::min<u64>(skip_bytes, size)); + + // and compute the hashes + size_t actual_count; + filerr = hashes.compute(file, skip_bytes, size - skip_bytes, actual_count, types); + if (filerr) + return filerr; + + return std::error_condition(); } -bool device_image_interface::image_checkhash() +std::error_condition device_image_interface::image_checkhash() { // only calculate CRC if it hasn't been calculated, and the open_mode is read only u32 crcval; @@ -546,26 +493,25 @@ bool device_image_interface::image_checkhash() { // do not cause a linear read of 600 megs please // TODO: use SHA1 in the CHD header as the hash - if (image_type() == IO_CDROM) - return true; + if (image_is_chd_type()) + return std::error_condition(); // Skip calculating the hash when we have an image mounted through a software list if (loaded_through_softlist()) - return true; + return std::error_condition(); // run the hash - if (!run_hash(*m_file, unhashed_header_length(), m_hash, util::hash_collection::HASH_TYPES_ALL)) - return false; + return run_hash(*m_file, unhashed_header_length(), m_hash, util::hash_collection::HASH_TYPES_ALL); } - return true; + return std::error_condition(); } -util::hash_collection device_image_interface::calculate_hash_on_file(util::core_file &file) const +util::hash_collection device_image_interface::calculate_hash_on_file(util::random_read &file) const { // calculate the hash util::hash_collection hash; - if (!run_hash(file, unhashed_header_length(), hash, util::hash_collection::HASH_TYPES_ALL)) + if (run_hash(file, unhashed_header_length(), hash, util::hash_collection::HASH_TYPES_ALL)) hash.reset(); return hash; } @@ -576,39 +522,13 @@ u32 device_image_interface::crc() u32 crc = 0; image_checkhash(); - m_hash.crc(crc); + if (!m_hash.crc(crc)) + crc = 0; return crc; } -//------------------------------------------------- -// support_command_line_image_creation - do we -// want to support image creation from the front -// end command line? -//------------------------------------------------- - -bool device_image_interface::support_command_line_image_creation() const -{ - bool result; - switch (image_type()) - { - case IO_PRINTER: - case IO_SERIAL: - case IO_PARALLEL: - // going by the assumption that these device image types should support this - // behavior; ideally we'd get rid of IO_* and just push this to the specific - // devices - result = true; - break; - default: - result = false; - break; - } - return result; -} - - // **************************************************************************** // Battery functions // @@ -629,38 +549,36 @@ void device_image_interface::battery_load(void *buffer, int length, int fill) if (!buffer || (length <= 0)) throw emu_fatalerror("device_image_interface::battery_load: Must specify sensical buffer/length"); - osd_file::error filerr; - int bytes_read = 0; - std::string fname = std::string(device().machine().system().name).append(PATH_SEPARATOR).append(m_basename_noext.c_str()).append(".nv"); + std::string const fname = std::string(device().machine().system().name).append(PATH_SEPARATOR).append(m_basename_noext).append(".nv"); /* try to open the battery file and read it in, if possible */ emu_file file(device().machine().options().nvram_directory(), OPEN_FLAG_READ); - filerr = file.open(fname); - if (filerr == osd_file::error::NONE) + std::error_condition const filerr = file.open(fname); + int bytes_read = 0; + if (!filerr) bytes_read = file.read(buffer, length); // fill remaining bytes (if necessary) memset(((char *)buffer) + bytes_read, fill, length - bytes_read); } -void device_image_interface::battery_load(void *buffer, int length, void *def_buffer) +void device_image_interface::battery_load(void *buffer, int length, const void *def_buffer) { if (!buffer || (length <= 0)) throw emu_fatalerror("device_image_interface::battery_load: Must specify sensical buffer/length"); - osd_file::error filerr; - int bytes_read = 0; - std::string fname = std::string(device().machine().system().name).append(PATH_SEPARATOR).append(m_basename_noext.c_str()).append(".nv"); + std::string const fname = std::string(device().machine().system().name).append(PATH_SEPARATOR).append(m_basename_noext).append(".nv"); // try to open the battery file and read it in, if possible emu_file file(device().machine().options().nvram_directory(), OPEN_FLAG_READ); - filerr = file.open(fname); - if (filerr == osd_file::error::NONE) + std::error_condition const filerr = file.open(fname); + int bytes_read = 0; + if (!filerr) bytes_read = file.read(buffer, length); - // if no file was present, copy the default battery - if (bytes_read == 0 && def_buffer) - memcpy((char *)buffer, (char *)def_buffer, length); + // if no file was present, copy the default contents + if (!bytes_read && def_buffer) + std::memcpy(buffer, def_buffer, length); } @@ -679,111 +597,45 @@ void device_image_interface::battery_save(const void *buffer, int length) if (!device().machine().options().nvram_save()) return; - std::string fname = std::string(device().machine().system().name).append(PATH_SEPARATOR).append(m_basename_noext.c_str()).append(".nv"); + std::string fname = std::string(device().machine().system().name).append(PATH_SEPARATOR).append(m_basename_noext).append(".nv"); // try to open the battery file and write it out, if possible emu_file file(device().machine().options().nvram_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); - osd_file::error filerr = file.open(fname); - if (filerr == osd_file::error::NONE) + std::error_condition const filerr = file.open(fname); + if (!filerr) file.write(buffer, length); } -//------------------------------------------------- -// uses_file_extension - update configuration -// based on completed device setup -//------------------------------------------------- - -bool device_image_interface::uses_file_extension(const char *file_extension) const -{ - bool result = false; - - if (file_extension[0] == '.') - file_extension++; - - /* find the extensions */ - std::string extensions(file_extensions()); - char *ext = strtok((char*)extensions.c_str(),","); - while (ext != nullptr) - { - if (!core_stricmp(ext, file_extension)) - { - result = true; - break; - } - ext = strtok (nullptr, ","); - } - return result; -} - - // *************************************************************************** // IMAGE LOADING // *************************************************************************** //------------------------------------------------- -// is_loaded - quick check to determine whether an -// image is loaded -//------------------------------------------------- - -bool device_image_interface::is_loaded() -{ - return (m_file != nullptr); -} - -//------------------------------------------------- -// image_error_from_file_error - converts an image -// error to a file error -//------------------------------------------------- - -image_error_t device_image_interface::image_error_from_file_error(osd_file::error filerr) -{ - switch (filerr) - { - case osd_file::error::NONE: - return IMAGE_ERROR_SUCCESS; - - case osd_file::error::NOT_FOUND: - case osd_file::error::ACCESS_DENIED: - // file not found (or otherwise cannot open) - return IMAGE_ERROR_FILENOTFOUND; - - case osd_file::error::OUT_OF_MEMORY: - // out of memory - return IMAGE_ERROR_OUTOFMEMORY; - - case osd_file::error::ALREADY_OPEN: - // this shouldn't happen - return IMAGE_ERROR_ALREADYOPEN; - - case osd_file::error::FAILURE: - case osd_file::error::TOO_MANY_FILES: - case osd_file::error::INVALID_DATA: - default: - // other errors - return IMAGE_ERROR_INTERNAL; - } -} - - -//------------------------------------------------- // load_image_by_path - loads an image with a // specific path //------------------------------------------------- -image_error_t device_image_interface::load_image_by_path(u32 open_flags, const std::string &path) +std::error_condition device_image_interface::load_image_by_path(u32 open_flags, std::string_view path) { std::string revised_path; // attempt to read the file - auto const filerr = util::zippath_fopen(path, open_flags, m_file, revised_path); - if (filerr != osd_file::error::NONE) - return image_error_from_file_error(filerr); + auto filerr = util::zippath_fopen(path, open_flags, m_file, revised_path); + if (filerr) + { + osd_printf_verbose("%s: error opening image file %s with flags=%08X (%s:%d %s)\n", device().tag(), path, open_flags, filerr.category().name(), filerr.value(), filerr.message()); + return filerr; + } + else + { + osd_printf_verbose("%s: opened image file %s with flags=%08X\n", device().tag(), path, open_flags); + } m_readonly = (open_flags & OPEN_FLAG_WRITE) ? 0 : 1; m_created = (open_flags & OPEN_FLAG_CREATE) ? 1 : 0; set_image_filename(revised_path); - return IMAGE_ERROR_SUCCESS; + return std::error_condition(); } @@ -791,7 +643,7 @@ image_error_t device_image_interface::load_image_by_path(u32 open_flags, const s // reopen_for_write //------------------------------------------------- -int device_image_interface::reopen_for_write(const std::string &path) +std::error_condition device_image_interface::reopen_for_write(std::string_view path) { m_file.reset(); @@ -799,15 +651,15 @@ int device_image_interface::reopen_for_write(const std::string &path) // attempt to open the file for writing auto const filerr = util::zippath_fopen(path, OPEN_FLAG_READ|OPEN_FLAG_WRITE|OPEN_FLAG_CREATE, m_file, revised_path); - if (filerr != osd_file::error::NONE) - return image_error_from_file_error(filerr); + if (filerr) + return filerr; // success! m_readonly = 0; m_created = 1; set_image_filename(revised_path); - return IMAGE_ERROR_SUCCESS; + return std::error_condition(); } @@ -820,71 +672,64 @@ std::vector<u32> device_image_interface::determine_open_plan(bool is_create) { std::vector<u32> open_plan; - // emit flags into a vector - if (!is_create && is_readable() && is_writeable()) - open_plan.push_back(OPEN_FLAG_READ | OPEN_FLAG_WRITE); - if (!is_create && !is_readable() && is_writeable()) - open_plan.push_back(OPEN_FLAG_WRITE); - if (!is_create && is_readable()) - open_plan.push_back(OPEN_FLAG_READ); - if (is_create && is_writeable() && is_creatable()) - open_plan.push_back(OPEN_FLAG_READ | OPEN_FLAG_WRITE | OPEN_FLAG_CREATE); + if (!is_create) + { + if (is_writeable()) + open_plan.push_back(is_readable() ? (OPEN_FLAG_READ | OPEN_FLAG_WRITE) : OPEN_FLAG_WRITE); + if (is_readable()) + open_plan.push_back(OPEN_FLAG_READ); + } + else if (is_writeable() && is_creatable()) + { + if (is_readable()) + open_plan.push_back(OPEN_FLAG_READ | OPEN_FLAG_WRITE | OPEN_FLAG_CREATE); + else + open_plan.push_back(OPEN_FLAG_WRITE | OPEN_FLAG_CREATE); + } return open_plan; } //------------------------------------------------- -// dump_wrong_and_correct_checksums - dump an -// error message containing the wrong and the -// correct checksums for a given software item -//------------------------------------------------- - -static void dump_wrong_and_correct_checksums(const util::hash_collection &hashes, const util::hash_collection &acthashes) -{ - osd_printf_error(" EXPECTED: %s\n", hashes.macro_string().c_str()); - osd_printf_error(" FOUND: %s\n", acthashes.macro_string().c_str()); -} - - -//------------------------------------------------- // verify_length_and_hash - verify the length // and hash signatures of a file //------------------------------------------------- -static int verify_length_and_hash(emu_file *file, const char *name, u32 explength, const util::hash_collection &hashes) +static int verify_length_and_hash(emu_file *file, std::string_view name, u32 explength, const util::hash_collection &hashes) { - int retVal = 0; - if (file==nullptr) return 0; + int retval = 0; + if (!file) + return 0; // verify length u32 actlength = file->size(); if (explength != actlength) { osd_printf_error("%s WRONG LENGTH (expected: %d found: %d)\n", name, explength, actlength); - retVal++; + retval++; } - // If there is no good dump known, write it - util::hash_collection &acthashes = file->hashes(hashes.hash_types().c_str()); + util::hash_collection &acthashes = file->hashes(hashes.hash_types()); if (hashes.flag(util::hash_collection::FLAG_NO_DUMP)) { + // If there is no good dump known, write it osd_printf_error("%s NO GOOD DUMP KNOWN\n", name); } - // verify checksums else if (hashes != acthashes) { // otherwise, it's just bad osd_printf_error("%s WRONG CHECKSUMS:\n", name); - dump_wrong_and_correct_checksums(hashes, acthashes); - retVal++; + osd_printf_error(" EXPECTED: %s\n", hashes.macro_string()); + osd_printf_error(" FOUND: %s\n", acthashes.macro_string()); + retval++; } - // If it matches, but it is actually a bad dump, write it else if (hashes.flag(util::hash_collection::FLAG_BAD_DUMP)) { + // If it matches, but it is actually a bad dump, write it osd_printf_error("%s NEEDS REDUMP\n",name); } - return retVal; + return retval; } @@ -892,104 +737,85 @@ static int verify_length_and_hash(emu_file *file, const char *name, u32 explengt // load_software - software image loading //------------------------------------------------- -bool device_image_interface::load_software(software_list_device &swlist, const char *swname, const rom_entry *start) +std::error_condition device_image_interface::load_software(software_list_device &swlist, std::string_view swname, const rom_entry *start) { - std::string locationtag, breakstr("%"); - const rom_entry *region; - bool retVal = false; + std::error_condition retval; int warningcount = 0; - for (region = start; region != nullptr; region = rom_next_region(region)) + for (const rom_entry *region = start; region; region = rom_next_region(region)) { // loop until we hit the end of this region - const rom_entry *romp = region + 1; - while (!ROMENTRY_ISREGIONEND(romp)) + for (const rom_entry *romp = region + 1; !ROMENTRY_ISREGIONEND(romp); romp++) { // handle files if (ROMENTRY_ISFILE(romp)) { - osd_file::error filerr = osd_file::error::NOT_FOUND; + const software_info *const swinfo = swlist.find(std::string(swname)); + if (!swinfo) + return image_error::NOSOFTWARE; - u32 crc = 0; - bool has_crc = util::hash_collection(ROM_GETHASHDATA(romp)).crc(crc); + if (swinfo->supported() == software_support::PARTIALLY_SUPPORTED) + osd_printf_error("WARNING: support for software %s (in list %s) is only partial\n", swname, swlist.list_name()); + else if (swinfo->supported() == software_support::UNSUPPORTED) + osd_printf_error("WARNING: support for software %s (in list %s) is only preliminary\n", swname, swlist.list_name()); - const software_info *swinfo = swlist.find(swname); - if (swinfo == nullptr) - return false; - - u32 supported = swinfo->supported(); - if (supported == SOFTWARE_SUPPORTED_PARTIAL) - osd_printf_error("WARNING: support for software %s (in list %s) is only partial\n", swname, swlist.list_name().c_str()); - if (supported == SOFTWARE_SUPPORTED_NO) - osd_printf_error("WARNING: support for software %s (in list %s) is only preliminary\n", swname, swlist.list_name().c_str()); - - // attempt reading up the chain through the parents and create a locationtag std::string in the format - // " swlist % clonename % parentname " - // below, we have the code to split the elements and to create paths to load from + u32 crc = 0; + const bool has_crc = util::hash_collection(romp->hashdata()).crc(crc); + std::vector<const software_info *> parents; + std::vector<std::string> searchpath = rom_load_manager::get_software_searchpath(swlist, *swinfo); - while (swinfo != nullptr) + // for historical reasons, add the search path for the software list device's owner + const device_t *const listowner = swlist.owner(); + if (listowner) { - locationtag.append(swinfo->shortname()).append(breakstr); - swinfo = !swinfo->parentname().empty() ? swlist.find(swinfo->parentname().c_str()) : nullptr; + std::vector<std::string> devsearch = listowner->searchpath(); + for (std::string &path : devsearch) + searchpath.emplace_back(std::move(path)); } - // strip the final '%' - locationtag.erase(locationtag.length() - 1, 1); - - // check if locationtag actually contains two locations separated by '%' - // (i.e. check if we are dealing with a clone in softwarelist) - std::string tag2, tag3, tag4(locationtag), tag5; - int separator = tag4.find_first_of('%'); - if (separator != -1) + // try to load the file + m_mame_file.reset(new emu_file(device().machine().options().media_path(), searchpath, OPEN_FLAG_READ)); + m_mame_file->set_restrict_to_mediapath(1); + std::error_condition filerr; + if (has_crc) + filerr = m_mame_file->open(romp->name(), crc); + else + filerr = m_mame_file->open(romp->name()); + if (filerr) { - // we are loading a clone through softlists, split the setname from the parentname - tag5.assign(tag4.substr(separator + 1, tag4.length() - separator + 1)); - tag4.erase(separator, tag4.length() - separator); + m_mame_file.reset(); + std::ostringstream msg; + util::stream_format(msg, + "%s: error opening image file %s: %s (%s:%d)", + device().tag(), romp->name(), + filerr.message(), + filerr.category().name(), + filerr.value()); + if (!searchpath.empty()) + { + msg << " (tried in"; + for (auto const &path : searchpath) + msg << ' ' << path; + msg << ')'; + } + osd_printf_error("%s\n", std::move(msg).str()); } - // prepare locations where we have to load from: list/parentname & list/clonename - std::string tag1(swlist.list_name()); - tag1.append(PATH_SEPARATOR); - tag2.assign(tag1.append(tag4)); - tag1.assign(swlist.list_name()); - tag1.append(PATH_SEPARATOR); - tag3.assign(tag1.append(tag5)); - - if (tag5.find_first_of('%') != -1) - fatalerror("We do not support clones of clones!\n"); - - // try to load from the available location(s): - // - if we are not using lists, we have regiontag only; - // - if we are using lists, we have: list/clonename, list/parentname, clonename, parentname - // try to load from list/setname - if ((m_mame_file == nullptr) && (tag2.c_str() != nullptr)) - m_mame_file = common_process_file(device().machine().options(), tag2.c_str(), has_crc, crc, romp, filerr); - // try to load from list/parentname - if ((m_mame_file == nullptr) && (tag3.c_str() != nullptr)) - m_mame_file = common_process_file(device().machine().options(), tag3.c_str(), has_crc, crc, romp, filerr); - // try to load from setname - if ((m_mame_file == nullptr) && (tag4.c_str() != nullptr)) - m_mame_file = common_process_file(device().machine().options(), tag4.c_str(), has_crc, crc, romp, filerr); - // try to load from parentname - if ((m_mame_file == nullptr) && (tag5.c_str() != nullptr)) - m_mame_file = common_process_file(device().machine().options(), tag5.c_str(), has_crc, crc, romp, filerr); - - warningcount += verify_length_and_hash(m_mame_file.get(),ROM_GETNAME(romp),ROM_GETLENGTH(romp), util::hash_collection(ROM_GETHASHDATA(romp))); - - if (filerr == osd_file::error::NONE) + warningcount += verify_length_and_hash(m_mame_file.get(), romp->name(), romp->get_length(), util::hash_collection(romp->hashdata())); + + if (!filerr) filerr = util::core_file::open_proxy(*m_mame_file, m_file); - if (filerr == osd_file::error::NONE) - retVal = true; + if (filerr) + retval = filerr; break; // load first item for start } - romp++; /* something else; skip */ } } + if (warningcount > 0) - { osd_printf_error("WARNING: the software item might not run correctly.\n"); - } - return retVal; + + return retval; } @@ -997,16 +823,16 @@ bool device_image_interface::load_software(software_list_device &swlist, const c // load_internal - core image loading //------------------------------------------------- -image_init_result device_image_interface::load_internal(const std::string &path, bool is_create, int create_format, util::option_resolution *create_args) +std::pair<std::error_condition, std::string> device_image_interface::load_internal(std::string_view path, bool is_create, int create_format, util::option_resolution *create_args) { + std::pair<std::error_condition, std::string> err; + // first unload the image unload(); - // clear any possible error messages - clear_error(); - // we are now loading m_is_loading = true; + m_sequence_counter++; // record the filename set_image_filename(path); @@ -1014,48 +840,47 @@ image_init_result device_image_interface::load_internal(const std::string &path, if (core_opens_image_file()) { // determine open plan - std::vector<u32> open_plan = determine_open_plan(is_create); + std::vector<u32> const open_plan = determine_open_plan(is_create); // attempt to open the file in various ways for (auto iter = open_plan.cbegin(); !m_file && iter != open_plan.cend(); iter++) { // open the file - m_err = load_image_by_path(*iter, path); - if (m_err && (m_err != IMAGE_ERROR_FILENOTFOUND)) - goto done; + err.first = load_image_by_path(*iter, path); + if (err.first && (err.first != std::errc::no_such_file_or_directory) && (err.first != std::errc::permission_denied)) + break; } // did we fail to find the file? - if (!is_loaded()) - { - m_err = IMAGE_ERROR_FILENOTFOUND; - goto done; - } + if (!err.first && !m_file) + err.first = std::errc::no_such_file_or_directory; } - // call device load or create - m_create_format = create_format; - m_create_args = create_args; + if (!err.first) + { + // call device load or create + m_create_format = create_format; + m_create_args = create_args; - if (init_phase()==false) { - m_err = (finish_load() == image_init_result::PASS) ? IMAGE_ERROR_SUCCESS : IMAGE_ERROR_INTERNAL; - if (m_err) - goto done; + if (!init_phase()) + err = finish_load(); } - // success! -done: - if (m_err!=0) { - if (!init_phase()) - { - if (device().machine().phase() == machine_phase::RUNNING) - device().popmessage("Error: Unable to %s image '%s': %s", is_create ? "create" : "load", path, error()); - else - osd_printf_error("Error: Unable to %s image '%s': %s\n", is_create ? "create" : "load", path.c_str(), error()); - } + if (err.first) + { + osd_printf_error( + !err.second.empty() + ? (is_create ? "Unable to create image '%1$s': %2$s (%3$s:%4$d %5$s)\n" : "Unable to load image '%1$s': %2$s (%3$s:%4$d %5$s)\n") + : (is_create ? "Unable to create image '%1$s': %5$s (%3$s:%4$d)\n" : "Unable to load image '%1$s': %5$s (%3$s:%4$d)\n"), + path, + err.second, + err.first.category().name(), + err.first.value(), + err.first.message()); clear(); } - return m_err ? image_init_result::FAIL : image_init_result::PASS; + + return err; } @@ -1063,13 +888,13 @@ done: // load - load an image into MAME //------------------------------------------------- -image_init_result device_image_interface::load(const std::string &path) +std::pair<std::error_condition, std::string> device_image_interface::load(std::string_view path) { // is this a reset on load item? if (is_reset_on_load() && !init_phase()) { reset_and_load(path); - return image_init_result::PASS; + return std::make_pair(std::error_condition(), std::string()); } return load_internal(path, false, 0, nullptr); @@ -1080,26 +905,26 @@ image_init_result device_image_interface::load(const std::string &path) // load_software - loads a softlist item by name //------------------------------------------------- -image_init_result device_image_interface::load_software(const std::string &software_identifier) +std::pair<std::error_condition, std::string> device_image_interface::load_software(std::string_view software_identifier) { // Is this a software part that forces a reset and we're at runtime? If so, get this loaded through reset_and_load if (is_reset_on_load() && !init_phase()) { reset_and_load(software_identifier); - return image_init_result::PASS; + return std::make_pair(std::error_condition(), std::string()); } // Prepare to load unload(); - clear_error(); m_is_loading = true; // Check if there's a software list defined for this device and use that if we're not creating an image - bool softload = load_software_part(software_identifier); - if (!softload) + std::pair<std::error_condition, std::string> err; + err.first = load_software_part(software_identifier); + if (err.first) { m_is_loading = false; - return image_init_result::FAIL; + return err; } // set up softlist stuff @@ -1109,9 +934,10 @@ image_init_result device_image_interface::load_software(const std::string &softw m_image_name = m_full_software_name; m_basename = m_full_software_name; m_basename_noext = m_full_software_name; - m_filetype = use_software_list_file_extension_for_filetype() && m_mame_file != nullptr - ? core_filename_extract_extension(m_mame_file->filename(), true) - : ""; + if (use_software_list_file_extension_for_filetype() && m_mame_file) + m_filetype = core_filename_extract_extension(m_mame_file->filename(), true); + else + m_filetype.clear(); // Copy some image information when we have been loaded through a software list software_info &swinfo = m_software_part_ptr->info(); @@ -1120,22 +946,32 @@ image_init_result device_image_interface::load_software(const std::string &softw if (swinfo.longname().empty() || swinfo.publisher().empty() || swinfo.year().empty()) fatalerror("Each entry in an XML list must have all of the following fields: description, publisher, year!\n"); - // store - m_longname = swinfo.longname(); - m_manufacturer = swinfo.publisher(); - m_year = swinfo.year(); - // set file type - std::string filename = (m_mame_file != nullptr) && (m_mame_file->filename() != nullptr) + std::string filename = (m_mame_file && m_mame_file->filename()) ? m_mame_file->filename() : ""; m_filetype = core_filename_extract_extension(filename, true); // call finish_load if necessary - if (init_phase() == false && (finish_load() != image_init_result::PASS)) - return image_init_result::FAIL; + if (!init_phase()) + { + err = finish_load(); + if (err.first) + { + osd_printf_error( + !err.second.empty() + ? "Unable to load software item '%1$s': %2$s (%3$s:%4$d %5$s)\n" + : "Unable to load software item '%1$s': %5$s (%3$s:%4$d)\n", + software_identifier, + err.second, + err.first.category().name(), + err.first.value(), + err.first.message()); + clear(); + } + } - return image_init_result::PASS; + return err; } @@ -1144,53 +980,38 @@ image_init_result device_image_interface::load_software(const std::string &softw // from core //------------------------------------------------- -image_init_result device_image_interface::finish_load() +std::pair<std::error_condition, std::string> device_image_interface::finish_load() { - image_init_result err = image_init_result::PASS; + std::pair<std::error_condition, std::string> err; if (m_is_loading) { - if (!image_checkhash()) - { - m_err = IMAGE_ERROR_INVALIDIMAGE; - err = image_init_result::FAIL; - } + err.first = image_checkhash(); - if (err == image_init_result::PASS) + if (!err.first) { if (m_created) - { err = call_create(m_create_format, m_create_args); - if (err != image_init_result::PASS) - { - if (!m_err) - m_err = IMAGE_ERROR_UNSPECIFIED; - } - } else - { - // using device load - err = call_load(); - if (err != image_init_result::PASS) - { - if (!m_err) - m_err = IMAGE_ERROR_UNSPECIFIED; - } - } + err = call_load(); // using device load } } + m_is_loading = false; m_create_format = 0; m_create_args = nullptr; + if (!err.first) + m_media_change_notifier(media_change_event::LOADED); + return err; } //------------------------------------------------- -// create - create a image +// create - create an image //------------------------------------------------- -image_init_result device_image_interface::create(const std::string &path) +std::pair<std::error_condition, std::string> device_image_interface::create(std::string_view path) { return create(path, nullptr, nullptr); } @@ -1200,13 +1021,14 @@ image_init_result device_image_interface::create(const std::string &path) // create - create a image //------------------------------------------------- -image_init_result device_image_interface::create(const std::string &path, const image_device_format *create_format, util::option_resolution *create_args) +std::pair<std::error_condition, std::string> device_image_interface::create(std::string_view path, const image_device_format *create_format, util::option_resolution *create_args) { int format_index = 0; int cnt = 0; for (auto &format : m_formatlist) { - if (create_format == format.get()) { + if (create_format == format.get()) + { format_index = cnt; break; } @@ -1222,7 +1044,7 @@ image_init_result device_image_interface::create(const std::string &path, const // the emulation and record this image to be loaded //------------------------------------------------- -void device_image_interface::reset_and_load(const std::string &path) +void device_image_interface::reset_and_load(std::string_view path) { // first make sure the reset is scheduled device().machine().schedule_hard_reset(); @@ -1240,7 +1062,7 @@ void device_image_interface::reset_and_load(const std::string &path) // to an image //------------------------------------------------- -void device_image_interface::clear() +void device_image_interface::clear() noexcept { m_mame_file.reset(); m_file.reset(); @@ -1251,9 +1073,6 @@ void device_image_interface::clear() m_create_format = 0; m_create_args = nullptr; - m_longname.clear(); - m_manufacturer.clear(); - m_year.clear(); m_basename.clear(); m_basename_noext.clear(); m_filetype.clear(); @@ -1261,6 +1080,10 @@ void device_image_interface::clear() m_full_software_name.clear(); m_software_part_ptr = nullptr; m_software_list_name.clear(); + + m_hash.reset(); + + m_sequence_counter++; } @@ -1272,10 +1095,15 @@ void device_image_interface::unload() { if (is_loaded() || loaded_through_softlist()) { + m_sequence_counter++; call_unload(); + clear(); + m_media_change_notifier(media_change_event::UNLOADED); + } + else + { + clear(); // just to make sure everything is reset } - clear(); - clear_error(); } @@ -1297,33 +1125,33 @@ const util::option_guide &device_image_interface::create_option_guide() const void device_image_interface::update_names() { - const char *inst_name = custom_instance_name(); - const char *brief_name = custom_brief_instance_name(); - if (inst_name == nullptr) - inst_name = device_typename(image_type()); - if (brief_name == nullptr) - brief_name = device_brieftypename(image_type()); + const char *inst_name = image_type_name(); + const char *brief_name = image_brief_type_name(); + assert(inst_name != nullptr); + assert(brief_name != nullptr); // count instances of the general image type, or device type if custom int count = 0; int index = -1; - for (const device_image_interface &image : image_interface_iterator(device().mconfig().root_device())) + for (const device_image_interface &image : image_interface_enumerator(device().mconfig().root_device())) { if (this == &image) index = count; - const char *other_name = image.custom_instance_name(); - if (!other_name) - other_name = device_typename(image.image_type()); + const char *other_name = image.image_type_name(); + const char *other_brief_name = image.image_brief_type_name(); + assert(other_name != nullptr); + assert(other_brief_name != nullptr); - if (other_name == inst_name || !strcmp(other_name, inst_name)) + if (other_name == inst_name || !strcmp(other_name, inst_name) || + other_brief_name == brief_name || !strcmp(other_brief_name, brief_name)) count++; } - m_canonical_instance_name = string_format("%s%d", inst_name, index + 1); + m_canonical_instance_name = util::string_format("%s%d", inst_name, index + 1); if (count > 1) { m_instance_name = m_canonical_instance_name; - m_brief_instance_name = string_format("%s%d", brief_name, index + 1); + m_brief_instance_name = util::string_format("%s%d", brief_name, index + 1); } else { @@ -1336,7 +1164,7 @@ void device_image_interface::update_names() // find_software_item //------------------------------------------------- -const software_part *device_image_interface::find_software_item(const std::string &identifier, bool restrict_to_interface, software_list_device **dev) const +const software_part *device_image_interface::find_software_item(std::string_view identifier, bool restrict_to_interface, software_list_device **dev) const { // split full software name into software list name and short software name std::string list_name, software_name, part_name; @@ -1349,7 +1177,7 @@ const software_part *device_image_interface::find_software_item(const std::strin : nullptr; // find the software list if explicitly specified - for (software_list_device &swlistdev : software_list_device_iterator(device().mconfig().root_device())) + for (software_list_device &swlistdev : software_list_device_enumerator(device().mconfig().root_device())) { if (list_name.empty() || (list_name == swlistdev.list_name())) { @@ -1416,7 +1244,7 @@ const software_list_loader &device_image_interface::get_software_list_loader() c // sw_info and sw_part are also set. //------------------------------------------------- -bool device_image_interface::load_software_part(const std::string &identifier) +std::error_condition device_image_interface::load_software_part(std::string_view identifier) { // if no match has been found, we suggest similar shortnames software_list_device *swlist; @@ -1424,14 +1252,14 @@ bool device_image_interface::load_software_part(const std::string &identifier) if (m_software_part_ptr == nullptr) { software_list_device::display_matches(device().machine().config(), image_interface(), identifier); - return false; + return image_error::NOSOFTWARE; } // Load the software part - const char *swname = m_software_part_ptr->info().shortname().c_str(); + const std::string &swname = m_software_part_ptr->info().shortname(); const rom_entry *start_entry = m_software_part_ptr->romdata().data(); const software_list_loader &loader = get_software_list_loader(); - bool result = loader.load_software(*this, *swlist, swname, start_entry); + std::error_condition result = loader.load_software(*this, *swlist, swname, start_entry); // check compatibility switch (swlist->is_compatible(*m_software_part_ptr)) @@ -1441,10 +1269,12 @@ bool device_image_interface::load_software_part(const std::string &identifier) case SOFTWARE_IS_INCOMPATIBLE: swlist->popmessage("WARNING! the set %s might not work on this system due to incompatible filter(s) '%s'\n", m_software_part_ptr->info().shortname(), swlist->filter()); + osd_printf_warning("WARNING: the set %s might not work on this system due to incompatible filter(s) '%s'\n", m_software_part_ptr->info().shortname(), swlist->filter()); break; case SOFTWARE_NOT_COMPATIBLE: swlist->popmessage("WARNING! the set %s might not work on this system due to missing filter(s) '%s'\n", m_software_part_ptr->info().shortname(), swlist->filter()); + osd_printf_warning("WARNING! the set %s might not work on this system due to missing filter(s) '%s'\n", m_software_part_ptr->info().shortname(), swlist->filter()); break; } @@ -1469,7 +1299,7 @@ bool device_image_interface::load_software_part(const std::string &identifier) // software_get_default_slot //------------------------------------------------- -std::string device_image_interface::software_get_default_slot(const char *default_card_slot) const +std::string device_image_interface::software_get_default_slot(std::string_view default_card_slot) const { std::string result; @@ -1501,41 +1331,3 @@ bool device_image_interface::init_phase() const return !device().has_running_machine() || device().machine().phase() == machine_phase::INIT; } - - -//---------------------------------------------------------------------------- - -static int image_fseek_thunk(void *file, s64 offset, int whence) -{ - device_image_interface *image = (device_image_interface *) file; - return image->fseek(offset, whence); -} - -static size_t image_fread_thunk(void *file, void *buffer, size_t length) -{ - device_image_interface *image = (device_image_interface *) file; - return image->fread(buffer, length); -} - -static size_t image_fwrite_thunk(void *file, const void *buffer, size_t length) -{ - device_image_interface *image = (device_image_interface *) file; - return image->fwrite(buffer, length); -} - -static u64 image_fsize_thunk(void *file) -{ - device_image_interface *image = (device_image_interface *) file; - return image->length(); -} - -//---------------------------------------------------------------------------- - -struct io_procs image_ioprocs = -{ - nullptr, - image_fseek_thunk, - image_fread_thunk, - image_fwrite_thunk, - image_fsize_thunk -}; diff --git a/src/emu/diimage.h b/src/emu/diimage.h index 437546f27c6..aa9c991c955 100644 --- a/src/emu/diimage.h +++ b/src/emu/diimage.h @@ -17,8 +17,13 @@ #ifndef MAME_EMU_DIIMAGE_H #define MAME_EMU_DIIMAGE_H +#include "notifier.h" +#include "utilfwd.h" + #include <memory> #include <string> +#include <system_error> +#include <utility> #include <vector> @@ -26,53 +31,21 @@ // TYPE DEFINITIONS //************************************************************************** -extern struct io_procs image_ioprocs; - -enum iodevice_t +enum class image_error : int { - /* List of all supported devices. Refer to the device by these names only */ - IO_UNKNOWN, - IO_CARTSLOT, /* 1 - Cartridge Port, as found on most console and on some computers */ - IO_FLOPPY, /* 2 - Floppy Disk unit */ - IO_HARDDISK, /* 3 - Hard Disk unit */ - IO_CYLINDER, /* 4 - Magnetically-Coated Cylinder */ - IO_CASSETTE, /* 5 - Cassette Recorder (common on early home computers) */ - IO_PUNCHCARD, /* 6 - Card Puncher/Reader */ - IO_PUNCHTAPE, /* 7 - Tape Puncher/Reader (reels instead of punchcards) */ - IO_PRINTER, /* 8 - Printer device */ - IO_SERIAL, /* 9 - Generic Serial Port */ - IO_PARALLEL, /* 10 - Generic Parallel Port */ - IO_SNAPSHOT, /* 11 - Complete 'snapshot' of the state of the computer */ - IO_QUICKLOAD, /* 12 - Allow to load program/data into memory, without matching any actual device */ - IO_MEMCARD, /* 13 - Memory card */ - IO_CDROM, /* 14 - optical CD-ROM disc */ - IO_MAGTAPE, /* 15 - Magnetic tape */ - IO_ROM, /* 16 - Individual ROM image - the Amstrad CPC has a few applications that were sold on 16kB ROMs */ - IO_MIDIIN, /* 17 - MIDI In port */ - IO_MIDIOUT, /* 18 - MIDI Out port */ - IO_PICTURE, /* 19 - A single-frame image */ - IO_VIDEO, /* 20 - A video file */ - IO_COUNT /* 21 - Total Number of IO_devices for searching */ + INTERNAL = 1, + UNSUPPORTED, + INVALIDIMAGE, + INVALIDLENGTH, + ALREADYOPEN, + NOSOFTWARE, + BADSOFTWARE, + UNSPECIFIED }; -enum image_error_t -{ - IMAGE_ERROR_SUCCESS, - IMAGE_ERROR_INTERNAL, - IMAGE_ERROR_UNSUPPORTED, - IMAGE_ERROR_OUTOFMEMORY, - IMAGE_ERROR_FILENOTFOUND, - IMAGE_ERROR_INVALIDIMAGE, - IMAGE_ERROR_ALREADYOPEN, - IMAGE_ERROR_UNSPECIFIED -}; - -struct image_device_type_info -{ - iodevice_t m_type; - const char *m_name; - const char *m_shortname; -}; +const std::error_category &image_category() noexcept; +inline std::error_condition make_error_condition(image_error e) noexcept { return std::error_condition(int(e), image_category()); } +namespace std { template <> struct is_error_condition_enum<image_error> : public std::true_type { }; } class image_device_format { @@ -80,10 +53,10 @@ public: image_device_format(const std::string &name, const std::string &description, const std::string &extensions, const std::string &optspec); ~image_device_format(); - const std::string &name() const { return m_name; } - const std::string &description() const { return m_description; } - const std::vector<std::string> &extensions() const { return m_extensions; } - const std::string &optspec() const { return m_optspec; } + const std::string &name() const noexcept { return m_name; } + const std::string &description() const noexcept { return m_description; } + const std::vector<std::string> &extensions() const noexcept { return m_extensions; } + const std::string &optspec() const noexcept { return m_optspec; } private: std::string m_name; @@ -93,183 +66,200 @@ private: }; -enum class image_init_result { PASS, FAIL }; -enum class image_verify_result { PASS, FAIL }; - -//************************************************************************** -// MACROS -//************************************************************************** - -#define DEVICE_IMAGE_LOAD_MEMBER(_name) image_init_result _name(device_image_interface &image) -#define DECLARE_DEVICE_IMAGE_LOAD_MEMBER(_name) DEVICE_IMAGE_LOAD_MEMBER(_name) - -#define DEVICE_IMAGE_UNLOAD_MEMBER(_name) void _name(device_image_interface &image) -#define DECLARE_DEVICE_IMAGE_UNLOAD_MEMBER(_name) DEVICE_IMAGE_UNLOAD_MEMBER(_name) - - // ======================> device_image_interface // class representing interface-specific live image class device_image_interface : public device_interface { public: - typedef device_delegate<image_init_result (device_image_interface &)> load_delegate; - typedef device_delegate<void (device_image_interface &)> unload_delegate; + enum class media_change_event + { + LOADED, + UNLOADED + }; - typedef std::vector<std::unique_ptr<image_device_format>> formatlist_type; + using formatlist_type = std::vector<std::unique_ptr<image_device_format> >; // construction/destruction device_image_interface(const machine_config &mconfig, device_t &device); virtual ~device_image_interface(); - static const char *device_typename(iodevice_t type); - static const char *device_brieftypename(iodevice_t type); - static iodevice_t device_typeid(const char *name); - - virtual image_init_result call_load() { return image_init_result::PASS; } - virtual image_init_result call_create(int format_type, util::option_resolution *format_options) { return image_init_result::PASS; } + virtual std::pair<std::error_condition, std::string> call_load() { return std::make_pair(std::error_condition(), std::string()); } + virtual std::pair<std::error_condition, std::string> call_create(int format_type, util::option_resolution *format_options) { return std::make_pair(std::error_condition(), std::string()); } virtual void call_unload() { } virtual std::string call_display() { return std::string(); } - virtual u32 unhashed_header_length() const { return 0; } - virtual bool core_opens_image_file() const { return true; } - virtual iodevice_t image_type() const = 0; - virtual bool is_readable() const = 0; - virtual bool is_writeable() const = 0; - virtual bool is_creatable() const = 0; - virtual bool must_be_loaded() const = 0; - virtual bool is_reset_on_load() const = 0; - virtual bool support_command_line_image_creation() const; - virtual const char *image_interface() const { return nullptr; } - virtual const char *file_extensions() const = 0; + virtual u32 unhashed_header_length() const noexcept { return 0; } + virtual bool core_opens_image_file() const noexcept { return true; } + virtual bool image_is_chd_type() const noexcept { return false; } + virtual bool is_readable() const noexcept = 0; + virtual bool is_writeable() const noexcept = 0; + virtual bool is_creatable() const noexcept = 0; + virtual bool is_reset_on_load() const noexcept = 0; + virtual bool support_command_line_image_creation() const noexcept { return false; } + virtual const char *image_interface() const noexcept { return nullptr; } + virtual const char *file_extensions() const noexcept = 0; virtual const util::option_guide &create_option_guide() const; - virtual const char *custom_instance_name() const { return nullptr; } - virtual const char *custom_brief_instance_name() const { return nullptr; } - - const image_device_format *device_get_indexed_creatable_format(int index) const { if (index < m_formatlist.size()) return m_formatlist.at(index).get(); else return nullptr; } - const image_device_format *device_get_named_creatable_format(const std::string &format_name); + virtual const char *image_type_name() const noexcept = 0; + virtual const char *image_brief_type_name() const noexcept = 0; + + // Set block device image regions for arcade systems + void add_region(std::string name, bool is_default = false); + bool has_preset_images() const; + bool has_preset_images_selection() const; + std::vector<std::string> preset_images_list() const; + int current_preset_image_id() const; + void switch_preset_image(int id); + chd_file *current_preset_image_chd() const; + void check_preset_images(); + + const image_device_format *device_get_indexed_creatable_format(int index) const noexcept { return (index < m_formatlist.size()) ? m_formatlist.at(index).get() : nullptr; } + const image_device_format *device_get_named_creatable_format(std::string_view format_name) const noexcept; const util::option_guide &device_get_creation_option_guide() const { return create_option_guide(); } - const char *error(); - void seterror(image_error_t err, const char *message); - void message(const char *format, ...) ATTR_PRINTF(2,3); - - bool exists() const { return !m_image_name.empty(); } - const char *filename() const { if (m_image_name.empty()) return nullptr; else return m_image_name.c_str(); } - const char *basename() const { if (m_basename.empty()) return nullptr; else return m_basename.c_str(); } - const char *basename_noext() const { if (m_basename_noext.empty()) return nullptr; else return m_basename_noext.c_str(); } - const std::string &filetype() const { return m_filetype; } - bool is_filetype(const std::string &candidate_filetype) { return !core_stricmp(filetype().c_str(), candidate_filetype.c_str()); } - bool is_open() const { return bool(m_file); } - util::core_file &image_core_file() const { return *m_file; } - u64 length() { check_for_file(); return m_file->size(); } - bool is_readonly() const { return m_readonly; } - u32 fread(void *buffer, u32 length) { check_for_file(); return m_file->read(buffer, length); } - u32 fread(optional_shared_ptr<u8> &ptr, u32 length) { ptr.allocate(length); return fread(ptr.target(), length); } - u32 fread(optional_shared_ptr<u8> &ptr, u32 length, offs_t offset) { ptr.allocate(length); return fread(ptr + offset, length - offset); } - u32 fwrite(const void *buffer, u32 length) { check_for_file(); return m_file->write(buffer, length); } - int fseek(s64 offset, int whence) { check_for_file(); return m_file->seek(offset, whence); } - u64 ftell() { check_for_file(); return m_file->tell(); } - int fgetc() { char ch; if (fread(&ch, 1) != 1) ch = '\0'; return ch; } - char *fgets(char *buffer, u32 length) { check_for_file(); return m_file->gets(buffer, length); } - int image_feof() { check_for_file(); return m_file->eof(); } - void *ptr() {check_for_file(); return const_cast<void *>(m_file->buffer()); } - // configuration access - - const std::string &longname() const { return m_longname; } - const std::string &manufacturer() const { return m_manufacturer; } - const std::string &year() const { return m_year; } - u32 supported() const { return m_supported; } - - const software_info *software_entry() const; - const software_part *part_entry() const { return m_software_part_ptr; } - const char *software_list_name() const { return m_software_list_name.c_str(); } - bool loaded_through_softlist() const { return m_software_part_ptr != nullptr; } - - void set_working_directory(const char *working_directory) { m_working_directory = working_directory; } - const std::string &working_directory(); - - u8 *get_software_region(const char *tag); - u32 get_software_region_length(const char *tag); - const char *get_feature(const char *feature_name) const; - bool load_software_region(const char *tag, optional_shared_ptr<u8> &ptr); + bool exists() const noexcept { return !m_image_name.empty(); } + + // get image file path/name + const char *filename() const noexcept { return m_image_name.empty() ? nullptr : m_image_name.c_str(); } + const char *basename() const noexcept { return m_basename.empty() ? nullptr : m_basename.c_str(); } + const char *basename_noext() const noexcept { return m_basename_noext.empty() ? nullptr : m_basename_noext.c_str(); } + const std::string &filetype() const noexcept { return m_filetype; } + bool is_filetype(std::string_view candidate_filetype) const; + + bool is_open() const noexcept { return bool(m_file); } + util::core_file &image_core_file() const noexcept { assert(is_open()); return *m_file; } + bool is_readonly() const noexcept { return m_readonly; } + + u32 sequence_counter() const { return m_sequence_counter; } // Increments on media load/unload/etc + util::notifier_subscription add_media_change_notifier(delegate<void (media_change_event)> &&n); + template <typename T> + util::notifier_subscription add_media_change_notifier(T &&n) + { return add_media_change_notifier(delegate<void (media_change_event)>(std::forward<T>(n))); } + + // image file I/O wrappers + // TODO: move away from using these and let implementations use the I/O interface directly + // FIXME: don't swallow errors + u64 length() + { + check_for_file(); + u64 result = 0; + m_file->length(result); + return result; + } + u32 fread(void *buffer, u32 length) + { + check_for_file(); + auto const [err, actual] = read(*m_file, buffer, length); + return actual; + } + u32 fwrite(const void *buffer, u32 length) + { + check_for_file(); + auto const [err, actual] = write(*m_file, buffer, length); + return actual; + } + std::error_condition fseek(s64 offset, int whence) + { + check_for_file(); + return m_file->seek(offset, whence); + } + u64 ftell() + { + check_for_file(); + u64 result = 0; + m_file->tell(result); + return result; + } + + // access to software list item information + const software_info *software_entry() const noexcept; + const software_part *part_entry() const noexcept { return m_software_part_ptr; } + const char *software_list_name() const noexcept { return m_software_list_name.c_str(); } + bool loaded_through_softlist() const noexcept { return m_software_part_ptr != nullptr; } + + // working directory + void set_working_directory(std::string_view working_directory) { m_working_directory = working_directory; } + void set_working_directory(std::string &&working_directory) { m_working_directory = std::move(working_directory); } + const std::string &working_directory() const { return m_working_directory; } + + // access to software list properties and ROM data areas + u8 *get_software_region(std::string_view tag); + u32 get_software_region_length(std::string_view tag); + const char *get_feature(std::string_view feature_name) const; + std::error_condition load_software_region(std::string_view tag, std::unique_ptr<u8[]> &ptr); u32 crc(); - util::hash_collection& hash() { return m_hash; } - util::hash_collection calculate_hash_on_file(util::core_file &file) const; + util::hash_collection &hash() { return m_hash; } + util::hash_collection calculate_hash_on_file(util::random_read &file) const; void battery_load(void *buffer, int length, int fill); - void battery_load(void *buffer, int length, void *def_buffer); + void battery_load(void *buffer, int length, const void *def_buffer); void battery_save(const void *buffer, int length); - const char *image_type_name() const { return device_typename(image_type()); } - const std::string &instance_name() const { return m_instance_name; } const std::string &brief_instance_name() const { return m_brief_instance_name; } const std::string &canonical_instance_name() const { return m_canonical_instance_name; } - bool uses_file_extension(const char *file_extension) const; const formatlist_type &formatlist() const { return m_formatlist; } // loads an image file - image_init_result load(const std::string &path); + std::pair<std::error_condition, std::string> load(std::string_view path); // loads a softlist item by name - image_init_result load_software(const std::string &software_identifier); + std::pair<std::error_condition, std::string> load_software(std::string_view software_identifier); - image_init_result finish_load(); + std::pair<std::error_condition, std::string> finish_load(); void unload(); - image_init_result create(const std::string &path, const image_device_format *create_format, util::option_resolution *create_args); - image_init_result create(const std::string &path); - bool load_software(software_list_device &swlist, const char *swname, const rom_entry *entry); - int reopen_for_write(const std::string &path); + std::pair<std::error_condition, std::string> create(std::string_view path, const image_device_format *create_format, util::option_resolution *create_args); + std::pair<std::error_condition, std::string> create(std::string_view path); + std::error_condition load_software(software_list_device &swlist, std::string_view swname, const rom_entry *entry); + std::error_condition reopen_for_write(std::string_view path); - void set_user_loadable(bool user_loadable) { m_user_loadable = user_loadable; } + void set_user_loadable(bool user_loadable) noexcept { m_user_loadable = user_loadable; } + void set_must_be_loaded(bool must_be_loaded) noexcept { m_must_be_loaded = must_be_loaded; } - bool user_loadable() const { return m_user_loadable; } - bool is_reset_and_loading() const { return m_is_reset_and_loading; } - const std::string &full_software_name() const { return m_full_software_name; } + bool user_loadable() const noexcept { return m_user_loadable; } + bool must_be_loaded() const noexcept { return m_must_be_loaded; } + bool is_reset_and_loading() const noexcept { return m_is_reset_and_loading; } + const std::string &full_software_name() const noexcept { return m_full_software_name; } protected: - // interface-level overrides + // device_interface implementation virtual void interface_config_complete() override; virtual const software_list_loader &get_software_list_loader() const; - virtual const bool use_software_list_file_extension_for_filetype() const { return false; } - - image_init_result load_internal(const std::string &path, bool is_create, int create_format, util::option_resolution *create_args); - image_error_t load_image_by_path(u32 open_flags, const std::string &path); - void clear(); - bool is_loaded(); + virtual bool use_software_list_file_extension_for_filetype() const noexcept { return false; } - void set_image_filename(const std::string &filename); + std::error_condition load_image_by_path(u32 open_flags, std::string_view path); + bool is_loaded() const noexcept { return m_file != nullptr; } - void clear_error(); + void set_image_tag(); void check_for_file() const { if (!m_file) throw emu_fatalerror("%s(%s): Illegal operation on unmounted image", device().shortname(), device().tag()); } - void setup_working_directory(); - bool try_change_working_directory(const std::string &subdir); + void make_readonly() noexcept { m_readonly = true; } - void make_readonly() { m_readonly = true; } + std::error_condition image_checkhash(); - bool image_checkhash(); - - const software_part *find_software_item(const std::string &identifier, bool restrict_to_interface, software_list_device **device = nullptr) const; - std::string software_get_default_slot(const char *default_card_slot) const; + const software_part *find_software_item(std::string_view identifier, bool restrict_to_interface, software_list_device **device = nullptr) const; + std::string software_get_default_slot(std::string_view default_card_slot) const; void add_format(std::unique_ptr<image_device_format> &&format); void add_format(std::string &&name, std::string &&description, std::string &&extensions, std::string &&optspec); - // derived class overrides +private: + std::vector<u32> determine_open_plan(bool is_create); + void update_names(); + void set_image_filename(std::string_view filename); + void clear() noexcept; + std::pair<std::error_condition, std::string> load_internal(std::string_view path, bool is_create, int create_format, util::option_resolution *create_args); + std::error_condition load_software_part(std::string_view identifier); - // configuration - static const image_device_type_info *find_device_type(iodevice_t type); - static const image_device_type_info m_device_info_array[]; + bool init_phase() const; + static std::error_condition run_hash(util::random_read &file, u32 skip_bytes, util::hash_collection &hashes, const char *types); - // error related info - image_error_t m_err; - std::string m_err_message; + // loads an image or software items and resets - called internally when we + // load an is_reset_on_load() item + void reset_and_load(std::string_view path); -private: // variables that are only non-zero when an image is mounted util::core_file::ptr m_file; std::unique_ptr<emu_file> m_mame_file; @@ -278,36 +268,27 @@ private: std::string m_basename_noext; std::string m_filetype; + // preset images regions + std::vector<std::string> m_possible_preset_regions; + std::vector<chd_file *> m_preset_images; + int m_default_region, m_current_region; + // Software information std::string m_full_software_name; const software_part *m_software_part_ptr; std::string m_software_list_name; - static image_error_t image_error_from_file_error(osd_file::error filerr); - std::vector<u32> determine_open_plan(bool is_create); - void update_names(); - bool load_software_part(const std::string &identifier); - - bool init_phase() const; - static bool run_hash(util::core_file &file, u32 skip_bytes, util::hash_collection &hashes, const char *types); - - // loads an image or software items and resets - called internally when we - // load an is_reset_on_load() item - void reset_and_load(const std::string &path); - // creation info formatlist_type m_formatlist; // working directory; persists across mounts std::string m_working_directory; - // info read from the hash file/software list - std::string m_longname; - std::string m_manufacturer; - std::string m_year; - u32 m_supported; + // to notify interested parties when media changes + util::notifier<media_change_event> m_media_change_notifier; // flags + u32 m_sequence_counter; bool m_readonly; bool m_created; @@ -325,12 +306,14 @@ private: // we want to disable command line cart loading... bool m_user_loadable; + bool m_must_be_loaded; + bool m_is_loading; bool m_is_reset_and_loading; }; // iterator -typedef device_interface_iterator<device_image_interface> image_interface_iterator; +typedef device_interface_enumerator<device_image_interface> image_interface_enumerator; -#endif /* MAME_EMU_DIIMAGE_H */ +#endif // MAME_EMU_DIIMAGE_H diff --git a/src/emu/dimemory.cpp b/src/emu/dimemory.cpp index fd8d4311a3a..81958b4f571 100644 --- a/src/emu/dimemory.cpp +++ b/src/emu/dimemory.cpp @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles /*************************************************************************** - dimemory.c + dimemory.cpp Device memory interfaces. @@ -10,6 +10,8 @@ #include "emu.h" +#include <map> + //************************************************************************** // CONSTANTS @@ -50,22 +52,24 @@ device_memory_interface::~device_memory_interface() void device_memory_interface::set_addrmap(int spacenum, address_map_constructor map) { + assert(0 <= spacenum); if (spacenum >= int(m_address_map.size())) - m_address_map.resize(spacenum+1); - m_address_map[spacenum] = map; + m_address_map.resize(spacenum + 1); + m_address_map[spacenum] = std::move(map); } //------------------------------------------------- // memory_translate - translate from logical to -// phyiscal addresses; designed to be overridden +// physical addresses; designed to be overridden // by the actual device implementation if address // translation is supported //------------------------------------------------- -bool device_memory_interface::memory_translate(int spacenum, int intention, offs_t &address) +bool device_memory_interface::memory_translate(int spacenum, int intention, offs_t &address, address_space *&target_space) { // by default it maps directly + target_space = &space(spacenum); return true; } @@ -94,11 +98,49 @@ void device_memory_interface::interface_config_complete() void device_memory_interface::interface_validity_check(validity_checker &valid) const { // loop over all address spaces + std::map<std::string, int> space_name_map; const int max_spaces = std::max(m_address_map.size(), m_address_config.size()); for (int spacenum = 0; spacenum < max_spaces; ++spacenum) { - if (space_config(spacenum)) + const address_space_config *config = space_config(spacenum); + if (config) { + // validate name + if (!config->name() || !*config->name()) + { + osd_printf_error("Name is empty for address space %d\n", config->name(), spacenum); + } + else + { + static char const *const validchars = "abcdefghijklmnopqrstuvwxyz0123456789_"; + for (char const *p = config->name(); *p; ++p) + { + if (*p == ' ') + { + osd_printf_error("Name for address space %s (%d) contains spaces\n", config->name(), spacenum); + break; + } + if (!strchr(validchars, *p)) + { + osd_printf_error("Name for address space %s (%d) contains invalid character '%c'\n", config->name(), spacenum, *p); + break; + } + } + auto const ins = space_name_map.emplace(config->name(), spacenum); + if (!ins.second) + osd_printf_error("Name for address space %s (%d) already used for space %d\n", config->name(), spacenum, ins.first->second); + } + + // validate data width + int width = config->data_width(); + if (width != 8 && width != 16 && width != 32 && width != 64) + osd_printf_error("Invalid data width %d specified for address space %s (%d)\n", width, config->name(), spacenum); + + // validate address shift + int shift = config->addr_shift(); + if (shift < 0 && (width >> -shift) < 8) + osd_printf_error("Invalid shift %d specified for address space %s (%d)\n", shift, config->name(), spacenum); + // construct the map ::address_map addrmap(const_cast<device_t &>(device()), spacenum); diff --git a/src/emu/dimemory.h b/src/emu/dimemory.h index 3d43c6a6a4d..14cd4d48ae2 100644 --- a/src/emu/dimemory.h +++ b/src/emu/dimemory.h @@ -19,44 +19,23 @@ #include <type_traits> - -//************************************************************************** -// CONSTANTS -//************************************************************************** - -// Translation intentions -constexpr int TRANSLATE_TYPE_MASK = 0x03; // read write or fetch -constexpr int TRANSLATE_USER_MASK = 0x04; // user mode or fully privileged -constexpr int TRANSLATE_DEBUG_MASK = 0x08; // debug mode (no side effects) - -constexpr int TRANSLATE_READ = 0; // translate for read -constexpr int TRANSLATE_WRITE = 1; // translate for write -constexpr int TRANSLATE_FETCH = 2; // translate for instruction fetch -constexpr int TRANSLATE_READ_USER = (TRANSLATE_READ | TRANSLATE_USER_MASK); -constexpr int TRANSLATE_WRITE_USER = (TRANSLATE_WRITE | TRANSLATE_USER_MASK); -constexpr int TRANSLATE_FETCH_USER = (TRANSLATE_FETCH | TRANSLATE_USER_MASK); -constexpr int TRANSLATE_READ_DEBUG = (TRANSLATE_READ | TRANSLATE_DEBUG_MASK); -constexpr int TRANSLATE_WRITE_DEBUG = (TRANSLATE_WRITE | TRANSLATE_DEBUG_MASK); -constexpr int TRANSLATE_FETCH_DEBUG = (TRANSLATE_FETCH | TRANSLATE_DEBUG_MASK); - - - -//************************************************************************** -// TYPE DEFINITIONS -//************************************************************************** - -// ======================> device_memory_interface - class device_memory_interface : public device_interface { friend class device_scheduler; - template <typename T, typename U> struct is_related_class { static constexpr bool value = std::is_convertible<std::add_pointer_t<T>, std::add_pointer_t<U> >::value; }; - template <typename T, typename U> struct is_related_device { static constexpr bool value = emu::detail::is_device_implementation<T>::value && is_related_class<T, U>::value; }; - template <typename T, typename U> struct is_related_interface { static constexpr bool value = emu::detail::is_device_interface<T>::value && is_related_class<T, U>::value; }; - template <typename T, typename U> struct is_unrelated_device { static constexpr bool value = emu::detail::is_device_implementation<T>::value && !is_related_class<T, U>::value; }; - template <typename T, typename U> struct is_unrelated_interface { static constexpr bool value = emu::detail::is_device_interface<T>::value && !is_related_class<T, U>::value; }; + template <typename T, typename U> using is_related_class = std::bool_constant<std::is_convertible_v<std::add_pointer_t<T>, std::add_pointer_t<U> > >; + template <typename T, typename U> using is_related_device = std::bool_constant<emu::detail::is_device_implementation<T>::value && is_related_class<T, U>::value >; + template <typename T, typename U> using is_related_interface = std::bool_constant<emu::detail::is_device_interface<T>::value && is_related_class<T, U>::value >; + template <typename T, typename U> using is_unrelated_device = std::bool_constant<emu::detail::is_device_implementation<T>::value && !is_related_class<T, U>::value >; + template <typename T, typename U> using is_unrelated_interface = std::bool_constant<emu::detail::is_device_interface<T>::value && !is_related_class<T, U>::value >; public: + // Translation intentions for the translate() call + enum { + TR_READ, + TR_WRITE, + TR_FETCH + }; + // construction/destruction device_memory_interface(const machine_config &mconfig, device_t &device); virtual ~device_memory_interface(); @@ -76,9 +55,7 @@ public: template <typename T, typename U, typename Ret, typename... Params> std::enable_if_t<is_unrelated_interface<T, U>::value> set_addrmap(int spacenum, T &obj, Ret (U::*func)(Params...)) { set_addrmap(spacenum, address_map_constructor(func, obj.device().tag(), &dynamic_cast<U &>(obj))); } template <typename T, typename Ret, typename... Params> - std::enable_if_t<is_related_class<device_t, T>::value> set_addrmap(int spacenum, Ret (T::*func)(Params...)); - template <typename T, typename Ret, typename... Params> - std::enable_if_t<!is_related_class<device_t, T>::value> set_addrmap(int spacenum, Ret (T::*func)(Params...)); + void set_addrmap(int spacenum, Ret (T::*func)(Params...)); void set_addrmap(int spacenum, address_map_constructor map); // basic information getters @@ -87,7 +64,7 @@ public: address_space &space(int index = 0) const { assert(index >= 0 && index < int(m_addrspace.size()) && m_addrspace[index]); return *m_addrspace[index]; } // address translation - bool translate(int spacenum, int intention, offs_t &address) { return memory_translate(spacenum, intention, address); } + bool translate(int spacenum, int intention, offs_t &address, address_space *&target_space) { return memory_translate(spacenum, intention, address, target_space); } // deliberately ambiguous functions; if you have the memory interface // just use it @@ -103,8 +80,6 @@ public: } void prepare_maps() { for (auto const &space : m_addrspace) { if (space) { space->prepare_map(); } } } void populate_from_maps() { for (auto const &space : m_addrspace) { if (space) { space->populate_from_map(); } } } - void allocate_memory() { for (auto const &space : m_addrspace) { if (space) { space->allocate_memory(); } } } - void locate_memory() { for (auto const &space : m_addrspace) { if (space) { space->locate_memory(); } } } void set_log_unmap(bool log) { for (auto const &space : m_addrspace) { if (space) { space->set_log_unmap(log); } } } protected: @@ -114,7 +89,7 @@ protected: virtual space_config_vector memory_space_config() const = 0; // optional operation overrides - virtual bool memory_translate(int spacenum, int intention, offs_t &address); + virtual bool memory_translate(int spacenum, int intention, offs_t &address, address_space *&target_space); // interface-level overrides virtual void interface_config_complete() override; @@ -130,8 +105,7 @@ private: }; // iterator -typedef device_interface_iterator<device_memory_interface> memory_interface_iterator; - +typedef device_interface_enumerator<device_memory_interface> memory_interface_enumerator; -#endif /* MAME_EMU_DIMEMORY_H */ +#endif // MAME_EMU_DIMEMORY_H diff --git a/src/emu/dinetwork.cpp b/src/emu/dinetwork.cpp index 0e9c0610566..30e10361c97 100644 --- a/src/emu/dinetwork.cpp +++ b/src/emu/dinetwork.cpp @@ -1,15 +1,24 @@ // license:BSD-3-Clause // copyright-holders:Carl, Miodrag Milanovic + #include "emu.h" -#include "osdnet.h" +#include "dinetwork.h" + +#include "osdepend.h" + +#include <algorithm> + -device_network_interface::device_network_interface(const machine_config &mconfig, device_t &device, float bandwidth) +device_network_interface::device_network_interface(const machine_config &mconfig, device_t &device, u32 bandwidth, u32 mtu) : device_interface(device, "network") + , m_poll_timer(nullptr) + , m_send_timer(nullptr) + , m_recv_timer(nullptr) { - m_promisc = false; - m_bandwidth = bandwidth; - set_mac("\0\0\0\0\0\0"); - m_intf = 0; + // Convert to Mibps to Bps + m_bandwidth = bandwidth << (20 - 3); + m_mtu = mtu; + m_intf = -1; m_loopback_control = false; } @@ -17,18 +26,26 @@ device_network_interface::~device_network_interface() { } -void device_network_interface::interface_pre_start() +void device_network_interface::interface_post_start() { + m_poll_timer = device().machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(device_network_interface::poll_device), this)); m_send_timer = device().machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(device_network_interface::send_complete), this)); m_recv_timer = device().machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(device_network_interface::recv_complete), this)); + + device().save_item(NAME(m_loopback_control)); } -void device_network_interface::interface_post_start() +void device_network_interface::interface_post_load() { - device().save_item(NAME(m_loopback_control)); + if (!m_dev) + m_poll_timer->reset(); + else if (!m_loopback_control && !m_recv_timer->enabled()) + start_net_device(); + else + stop_net_device(); } -int device_network_interface::send(u8 *buf, int len) +int device_network_interface::send(u8 *buf, int len, int fcs) { // TODO: enable this check when other devices implement delayed transmit //if (m_send_timer->enabled()) @@ -44,21 +61,42 @@ int device_network_interface::send(u8 *buf, int len) if (result) { // schedule receive complete callback - m_recv_timer->adjust(attotime::from_ticks(len, m_bandwidth * 1'000'000 / 8), result); + m_recv_timer->adjust(attotime::from_ticks(len, m_bandwidth), result); } } else if (m_dev) { - // send the data - result = m_dev->send(buf, len); + // send the data (excluding fcs) + result = m_dev->send(buf, len - fcs); + if (result) + result += fcs; } // schedule transmit complete callback - m_send_timer->adjust(attotime::from_ticks(len, m_bandwidth * 1'000'000 / 8), result); + m_send_timer->adjust(attotime::from_ticks(len, m_bandwidth), result); return result; } +TIMER_CALLBACK_MEMBER(device_network_interface::poll_device) +{ + m_dev->poll(); +} + +void device_network_interface::start_net_device() +{ + // Set device polling time to transfer time for one MTU + m_dev->start(); + const attotime interval = attotime::from_hz(m_bandwidth / m_mtu); + m_poll_timer->adjust(attotime::zero, 0, interval); +} + +void device_network_interface::stop_net_device() +{ + m_poll_timer->reset(); + m_dev->stop(); +} + TIMER_CALLBACK_MEMBER(device_network_interface::send_complete) { send_complete_cb(param); @@ -79,10 +117,10 @@ void device_network_interface::recv_cb(u8 *buf, int len) { // stop receiving more data from the network if (m_dev) - m_dev->stop(); + stop_net_device(); // schedule receive complete callback - m_recv_timer->adjust(attotime::from_ticks(len, m_bandwidth * 1'000'000 / 8), result); + m_recv_timer->adjust(attotime::from_ticks(len, m_bandwidth), result); } } @@ -92,27 +130,27 @@ TIMER_CALLBACK_MEMBER(device_network_interface::recv_complete) // start receiving data from the network again if (m_dev && !m_loopback_control) - m_dev->start(); -} - -void device_network_interface::set_promisc(bool promisc) -{ - m_promisc = promisc; - if(m_dev) m_dev->set_promisc(promisc); + start_net_device(); } -void device_network_interface::set_mac(const char *mac) +void device_network_interface::set_mac(const u8 *mac) { - memcpy(m_mac, mac, 6); - if(m_dev) m_dev->set_mac(m_mac); + std::copy_n(mac, std::size(m_mac), std::begin(m_mac)); } void device_network_interface::set_interface(int id) { - if(m_dev) - m_dev->stop(); - m_dev.reset(open_netdev(id, this, (int)(m_bandwidth*1000000/8.0f/1500))); - if(!m_dev) { + if (m_dev) + stop_net_device(); + + m_dev = device().machine().osd().open_network_device(id, *this); + if (m_dev) + { + if (!m_loopback_control) + start_net_device(); + } + else + { device().logerror("Network interface %d not found\n", id); id = -1; } @@ -129,8 +167,8 @@ void device_network_interface::set_loopback(bool loopback) if (m_dev) { if (loopback) - m_dev->stop(); + stop_net_device(); else if (!m_recv_timer->enabled()) - m_dev->start(); + start_net_device(); } } diff --git a/src/emu/dinetwork.h b/src/emu/dinetwork.h index d844be951ab..99fc1cb3a88 100644 --- a/src/emu/dinetwork.h +++ b/src/emu/dinetwork.h @@ -3,30 +3,28 @@ #ifndef MAME_EMU_DINETWORK_H #define MAME_EMU_DINETWORK_H -class osd_netdev; +#include "interface/nethandler.h" -class device_network_interface : public device_interface + +class device_network_interface : public device_interface, public osd::network_handler { public: - device_network_interface(const machine_config &mconfig, device_t &device, float bandwidth); + device_network_interface(const machine_config &mconfig, device_t &device, u32 bandwidth, u32 mtu = 1500); virtual ~device_network_interface(); - void interface_pre_start() override; - void interface_post_start() override; + void interface_post_start() override ATTR_COLD; + void interface_post_load() override ATTR_COLD; - void set_interface(int id); - void set_promisc(bool promisc); - void set_mac(const char *mac); + void set_interface(int id) ATTR_COLD; + void set_mac(const u8 *mac); void set_loopback(bool loopback); - const char *get_mac() const { return m_mac; } - bool get_promisc() const { return m_promisc; } int get_interface() const { return m_intf; } - int send(u8 *buf, int len); + int send(u8 *buf, int len, int fcs = 0); // TODO: de-virtualise this when existing devices implement delayed receive - virtual void recv_cb(u8 *buf, int len); + virtual void recv_cb(u8 *buf, int len) override; // delayed transmit/receive handlers virtual void send_complete_cb(int result) {} @@ -34,22 +32,31 @@ public: virtual void recv_complete_cb(int result) {} protected: - TIMER_CALLBACK_MEMBER(send_complete); - TIMER_CALLBACK_MEMBER(recv_complete); + bool has_net_device() const noexcept { return bool(m_dev); } - bool m_promisc; - char m_mac[6]; - float m_bandwidth; - std::unique_ptr<osd_netdev> m_dev; + // bandwidth in bytes per second + u32 m_bandwidth; + // maximum transmission unit, used for device polling time + u32 m_mtu; int m_intf; bool m_loopback_control; +private: + TIMER_CALLBACK_MEMBER(poll_device); + TIMER_CALLBACK_MEMBER(send_complete); + TIMER_CALLBACK_MEMBER(recv_complete); + + void start_net_device(); + void stop_net_device(); + + std::unique_ptr<osd::network_device> m_dev; + emu_timer *m_poll_timer; emu_timer *m_send_timer; emu_timer *m_recv_timer; }; // iterator -typedef device_interface_iterator<device_network_interface> network_interface_iterator; +typedef device_interface_enumerator<device_network_interface> network_interface_enumerator; #endif // MAME_EMU_DINETWORK_H diff --git a/src/emu/dinvram.cpp b/src/emu/dinvram.cpp index 4767d7d633c..7a3ec7f658f 100644 --- a/src/emu/dinvram.cpp +++ b/src/emu/dinvram.cpp @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles /*************************************************************************** - dinvram.c + dinvram.cpp Device NVRAM interfaces. @@ -20,8 +20,9 @@ // device_nvram_interface - constructor //------------------------------------------------- -device_nvram_interface::device_nvram_interface(const machine_config &mconfig, device_t &device) +device_nvram_interface::device_nvram_interface(const machine_config &mconfig, device_t &device, bool backup_enabled) : device_interface(device, "nvram") + , m_backup_enabled(backup_enabled) { } diff --git a/src/emu/dinvram.h b/src/emu/dinvram.h index d48889d29dd..0efcc9d1054 100644 --- a/src/emu/dinvram.h +++ b/src/emu/dinvram.h @@ -30,23 +30,32 @@ class device_nvram_interface : public device_interface { public: // construction/destruction - device_nvram_interface(const machine_config &mconfig, device_t &device); + device_nvram_interface(const machine_config &mconfig, device_t &device, bool backup_enabled = true); virtual ~device_nvram_interface(); + // configuration + void nvram_enable_backup(bool enabled) { assert(!device().started()); m_backup_enabled = enabled; } + // public accessors... for now void nvram_reset() { nvram_default(); } - void nvram_load(emu_file &file) { nvram_read(file); } - void nvram_save(emu_file &file) { nvram_write(file); } + bool nvram_load(util::read_stream &file) { return nvram_read(file); } + bool nvram_save(util::write_stream &file) { return nvram_write(file); } + bool nvram_backup_enabled() const noexcept { return m_backup_enabled; } + bool nvram_can_save() const { return m_backup_enabled && nvram_can_write(); } protected: // derived class overrides virtual void nvram_default() = 0; - virtual void nvram_read(emu_file &file) = 0; - virtual void nvram_write(emu_file &file) = 0; + virtual bool nvram_read(util::read_stream &file) = 0; + virtual bool nvram_write(util::write_stream &file) = 0; + virtual bool nvram_can_write() const { return true; } + +private: + bool m_backup_enabled; }; // iterator -typedef device_interface_iterator<device_nvram_interface> nvram_interface_iterator; +typedef device_interface_enumerator<device_nvram_interface> nvram_interface_enumerator; #endif /* MAME_EMU_DINVRAM */ diff --git a/src/emu/dipalette.cpp b/src/emu/dipalette.cpp index b5ceeb05d4c..7cd58665a4d 100644 --- a/src/emu/dipalette.cpp +++ b/src/emu/dipalette.cpp @@ -491,7 +491,7 @@ void device_palette_interface::configure_rgb_shadows(int mode, float factor) assert(m_format != BITMAP_FORMAT_IND16); // verify the shadow table - assert(mode >= 0 && mode < ARRAY_LENGTH(m_shadow_tables)); + assert(mode >= 0 && mode < std::size(m_shadow_tables)); shadow_table_data &stable = m_shadow_tables[mode]; assert(stable.base != nullptr); diff --git a/src/emu/dipalette.h b/src/emu/dipalette.h index 7e41f968cea..5d7f6b30506 100644 --- a/src/emu/dipalette.h +++ b/src/emu/dipalette.h @@ -43,8 +43,8 @@ class device_palette_interface : public device_interface public: // getters - u32 entries() const { return palette_entries(); } - u32 indirect_entries() const { return palette_indirect_entries(); } + u32 entries() const noexcept { return palette_entries(); } + u32 indirect_entries() const noexcept { return palette_indirect_entries(); } palette_t *palette() const { return m_palette; } const pen_t &pen(int index) const { return m_pens[index]; } const pen_t *pens() const { return m_pens; } @@ -53,8 +53,8 @@ public: double pen_contrast(pen_t pen) const { return m_palette->entry_contrast(pen); } pen_t black_pen() const { return m_black_pen; } pen_t white_pen() const { return m_white_pen; } - bool shadows_enabled() const { return palette_shadows_enabled(); } - bool hilights_enabled() const { return palette_hilights_enabled(); } + bool shadows_enabled() const noexcept { return palette_shadows_enabled(); } + bool hilights_enabled() const noexcept { return palette_hilights_enabled(); } // setters void set_pen_color(pen_t pen, rgb_t rgb) { m_palette->entry_set_color(pen, rgb); } @@ -92,10 +92,10 @@ protected: virtual void interface_post_stop() override; // configuration-related overrides - virtual u32 palette_entries() const = 0; - virtual u32 palette_indirect_entries() const { return 0; } - virtual bool palette_shadows_enabled() const { return false; } - virtual bool palette_hilights_enabled() const { return false; } + virtual u32 palette_entries() const noexcept = 0; + virtual u32 palette_indirect_entries() const noexcept { return 0; } + virtual bool palette_shadows_enabled() const noexcept { return false; } + virtual bool palette_hilights_enabled() const noexcept { return false; } private: // internal helpers @@ -140,7 +140,7 @@ private: }; // interface type iterator -typedef device_interface_iterator<device_palette_interface> palette_interface_iterator; +typedef device_interface_enumerator<device_palette_interface> palette_interface_enumerator; #endif // MAME_EMU_DIPALETTE_H diff --git a/src/emu/dipty.cpp b/src/emu/dipty.cpp index 0a894cfbfec..6a323d84e7f 100644 --- a/src/emu/dipty.cpp +++ b/src/emu/dipty.cpp @@ -2,7 +2,7 @@ // copyright-holders:F.Ulivi /*************************************************************************** - dipty.h + dipty.cpp Device PTY interface @@ -27,7 +27,7 @@ bool device_pty_interface::open() { if (!m_opened) { - if (osd_file::openpty(m_pty_master, m_slave_name) == osd_file::error::NONE) + if (!osd_file::openpty(m_pty_master, m_slave_name)) { m_opened = true; } @@ -54,7 +54,7 @@ bool device_pty_interface::is_open() const ssize_t device_pty_interface::read(u8 *rx_chars , size_t count) const { u32 actual_bytes; - if (m_opened && m_pty_master->read(rx_chars, 0, count, actual_bytes) == osd_file::error::NONE) + if (m_opened && !m_pty_master->read(rx_chars, 0, count, actual_bytes)) return actual_bytes; else return -1; @@ -72,8 +72,3 @@ bool device_pty_interface::is_slave_connected() const // TODO: really check for slave status return m_opened; } - -const char *device_pty_interface::slave_name() const -{ - return m_slave_name.c_str(); -} diff --git a/src/emu/dipty.h b/src/emu/dipty.h index e3297da9967..d2e92f14969 100644 --- a/src/emu/dipty.h +++ b/src/emu/dipty.h @@ -7,10 +7,14 @@ Device PTY interface ***************************************************************************/ - #ifndef MAME_EMU_DIPTY_H #define MAME_EMU_DIPTY_H +#pragma once + +#include <string> + + class device_pty_interface : public device_interface { public: @@ -23,12 +27,12 @@ public: bool is_open() const; - ssize_t read(u8 *rx_chars , size_t count) const; + ssize_t read(u8 *rx_chars, size_t count) const; void write(u8 tx_char) const; bool is_slave_connected() const; - const char *slave_name() const; + const std::string &slave_name() const { return m_slave_name; } protected: osd_file::ptr m_pty_master; @@ -37,6 +41,6 @@ protected: }; // iterator -typedef device_interface_iterator<device_pty_interface> pty_interface_iterator; +typedef device_interface_enumerator<device_pty_interface> pty_interface_enumerator; #endif // MAME_EMU_DIPTY_H diff --git a/src/emu/dirom.cpp b/src/emu/dirom.cpp deleted file mode 100644 index 6440fa2cfc6..00000000000 --- a/src/emu/dirom.cpp +++ /dev/null @@ -1,285 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Olivier Galibert - -#include "emu.h" - - -device_rom_interface::device_rom_interface(const machine_config &mconfig, device_t &device, u8 addrwidth, endianness_t endian, u8 datawidth) : - device_memory_interface(mconfig, device), - m_rom_tag(device.basetag()), - m_rom_config("rom", endian, datawidth, addrwidth), - m_bank(nullptr), - m_cur_bank(-1) -{ -} - -device_rom_interface::~device_rom_interface() -{ -} - -device_memory_interface::space_config_vector device_rom_interface::memory_space_config() const -{ - return space_config_vector { - std::make_pair(0, &m_rom_config) - }; -} - -void device_rom_interface::rom_bank_updated() -{ -} - -void device_rom_interface::set_rom_bank(int bank) -{ - if(!m_bank) - emu_fatalerror("%s: device_rom_interface::set_rom_bank called without banking setup", device().tag()); - - if(bank >= m_bank_count) { - device().logerror("Warning: requested bank %x higher than actual bank count %x\n", bank, m_bank_count); - bank = bank % m_bank_count; - } - - if (m_cur_bank != bank) { - m_cur_bank = bank; - m_bank->set_entry(bank); - rom_bank_updated(); - } -} - -void device_rom_interface::interface_post_load() -{ - if(m_bank) - m_bank->set_entry(m_cur_bank); -} - -void device_rom_interface::set_rom(const void *base, u32 size) -{ - u32 mend = m_rom_config.addr_width() == 32 ? 0xffffffff : (1 << m_rom_config.addr_width()) - 1; - u32 rend = size-1; - m_bank_count = mend == 0xffffffff ? 1 : (rend+1) / (mend+1); - if(m_bank_count < 1) - m_bank_count = 1; - - if(rend >= mend) { - space().install_read_bank(0, mend, device().tag()); - m_bank = device().machine().memory().banks().find(device().tag())->second.get(); - m_bank->configure_entries(0, m_bank_count, const_cast<void *>(base), mend+1); - m_cur_bank = 0; - - } else { - // Round up to the nearest power-of-two-minus-one - u32 rmask = rend; - rmask |= rmask >> 1; - rmask |= rmask >> 2; - rmask |= rmask >> 4; - rmask |= rmask >> 8; - rmask |= rmask >> 16; - if(rmask != rend) - space().unmap_read(0, mend); - // Mirror over the high bits. mend and rmask are both - // powers-of-two-minus-one, so the xor works - space().install_rom(0, rend, mend ^ rmask, const_cast<void *>(base)); - } -} - -void device_rom_interface::interface_pre_start() -{ - if(!has_space(0)) - return; - - switch(space().data_width()) { - case 8: - if(space().endianness() == ENDIANNESS_LITTLE) { - auto cache = space().cache<0, 0, ENDIANNESS_LITTLE>(); - m_r8 = [cache] (offs_t byteaddress) -> u8 { return cache->read_byte(byteaddress); }; - m_r16 = [cache] (offs_t byteaddress) -> u16 { return cache->read_word(byteaddress); }; - m_r32 = [cache] (offs_t byteaddress) -> u32 { return cache->read_dword(byteaddress); }; - m_r64 = [cache] (offs_t byteaddress) -> u64 { return cache->read_qword(byteaddress); }; - } else { - auto cache = space().cache<0, 0, ENDIANNESS_BIG>(); - m_r8 = [cache] (offs_t byteaddress) -> u8 { return cache->read_byte(byteaddress); }; - m_r16 = [cache] (offs_t byteaddress) -> u16 { return cache->read_word(byteaddress); }; - m_r32 = [cache] (offs_t byteaddress) -> u32 { return cache->read_dword(byteaddress); }; - m_r64 = [cache] (offs_t byteaddress) -> u64 { return cache->read_qword(byteaddress); }; - } - break; - - case 16: - switch(space().addr_shift()) { - case 3: - if(space().endianness() == ENDIANNESS_LITTLE) { - auto cache = space().cache<1, 3, ENDIANNESS_LITTLE>(); - m_r8 = [cache] (offs_t byteaddress) -> u8 { return cache->read_byte(byteaddress); }; - m_r16 = [cache] (offs_t byteaddress) -> u16 { return cache->read_word(byteaddress); }; - m_r32 = [cache] (offs_t byteaddress) -> u32 { return cache->read_dword(byteaddress); }; - m_r64 = [cache] (offs_t byteaddress) -> u64 { return cache->read_qword(byteaddress); }; - } else { - auto cache = space().cache<1, 3, ENDIANNESS_BIG>(); - m_r8 = [cache] (offs_t byteaddress) -> u8 { return cache->read_byte(byteaddress); }; - m_r16 = [cache] (offs_t byteaddress) -> u16 { return cache->read_word(byteaddress); }; - m_r32 = [cache] (offs_t byteaddress) -> u32 { return cache->read_dword(byteaddress); }; - m_r64 = [cache] (offs_t byteaddress) -> u64 { return cache->read_qword(byteaddress); }; - } - break; - case 0: - if(space().endianness() == ENDIANNESS_LITTLE) { - auto cache = space().cache<1, 0, ENDIANNESS_LITTLE>(); - m_r8 = [cache] (offs_t byteaddress) -> u8 { return cache->read_byte(byteaddress); }; - m_r16 = [cache] (offs_t byteaddress) -> u16 { return cache->read_word(byteaddress); }; - m_r32 = [cache] (offs_t byteaddress) -> u32 { return cache->read_dword(byteaddress); }; - m_r64 = [cache] (offs_t byteaddress) -> u64 { return cache->read_qword(byteaddress); }; - } else { - auto cache = space().cache<1, 0, ENDIANNESS_BIG>(); - m_r8 = [cache] (offs_t byteaddress) -> u8 { return cache->read_byte(byteaddress); }; - m_r16 = [cache] (offs_t byteaddress) -> u16 { return cache->read_word(byteaddress); }; - m_r32 = [cache] (offs_t byteaddress) -> u32 { return cache->read_dword(byteaddress); }; - m_r64 = [cache] (offs_t byteaddress) -> u64 { return cache->read_qword(byteaddress); }; - } - break; - case -1: - if(space().endianness() == ENDIANNESS_LITTLE) { - auto cache = space().cache<1, -1, ENDIANNESS_LITTLE>(); - m_r8 = nullptr; - m_r16 = nullptr; - m_r32 = [cache] (offs_t byteaddress) -> u32 { return cache->read_dword(byteaddress); }; - m_r64 = [cache] (offs_t byteaddress) -> u64 { return cache->read_qword(byteaddress); }; - } else { - auto cache = space().cache<1, -1, ENDIANNESS_BIG>(); - m_r8 = nullptr; - m_r16 = nullptr; - m_r32 = [cache] (offs_t byteaddress) -> u32 { return cache->read_dword(byteaddress); }; - m_r64 = [cache] (offs_t byteaddress) -> u64 { return cache->read_qword(byteaddress); }; - } - break; - } - break; - - case 32: - switch(space().addr_shift()) { - case 0: - if(space().endianness() == ENDIANNESS_LITTLE) { - auto cache = space().cache<2, 0, ENDIANNESS_LITTLE>(); - m_r8 = [cache] (offs_t byteaddress) -> u8 { return cache->read_byte(byteaddress); }; - m_r16 = [cache] (offs_t byteaddress) -> u16 { return cache->read_word(byteaddress); }; - m_r32 = [cache] (offs_t byteaddress) -> u32 { return cache->read_dword(byteaddress); }; - m_r64 = [cache] (offs_t byteaddress) -> u64 { return cache->read_qword(byteaddress); }; - } else { - auto cache = space().cache<2, 0, ENDIANNESS_BIG>(); - m_r8 = [cache] (offs_t byteaddress) -> u8 { return cache->read_byte(byteaddress); }; - m_r16 = [cache] (offs_t byteaddress) -> u16 { return cache->read_word(byteaddress); }; - m_r32 = [cache] (offs_t byteaddress) -> u32 { return cache->read_dword(byteaddress); }; - m_r64 = [cache] (offs_t byteaddress) -> u64 { return cache->read_qword(byteaddress); }; - } - break; - case -1: - if(space().endianness() == ENDIANNESS_LITTLE) { - auto cache = space().cache<2, -1, ENDIANNESS_LITTLE>(); - m_r8 = nullptr; - m_r16 = [cache] (offs_t byteaddress) -> u16 { return cache->read_word(byteaddress); }; - m_r32 = [cache] (offs_t byteaddress) -> u32 { return cache->read_dword(byteaddress); }; - m_r64 = [cache] (offs_t byteaddress) -> u64 { return cache->read_qword(byteaddress); }; - } else { - auto cache = space().cache<2, -1, ENDIANNESS_BIG>(); - m_r8 = nullptr; - m_r16 = [cache] (offs_t byteaddress) -> u16 { return cache->read_word(byteaddress); }; - m_r32 = [cache] (offs_t byteaddress) -> u32 { return cache->read_dword(byteaddress); }; - m_r64 = [cache] (offs_t byteaddress) -> u64 { return cache->read_qword(byteaddress); }; - } - break; - case -2: - if(space().endianness() == ENDIANNESS_LITTLE) { - auto cache = space().cache<2, -2, ENDIANNESS_LITTLE>(); - m_r8 = nullptr; - m_r16 = nullptr; - m_r32 = [cache] (offs_t byteaddress) -> u32 { return cache->read_dword(byteaddress); }; - m_r64 = [cache] (offs_t byteaddress) -> u64 { return cache->read_qword(byteaddress); }; - } else { - auto cache = space().cache<2, -2, ENDIANNESS_BIG>(); - m_r8 = nullptr; - m_r16 = nullptr; - m_r32 = [cache] (offs_t byteaddress) -> u32 { return cache->read_dword(byteaddress); }; - m_r64 = [cache] (offs_t byteaddress) -> u64 { return cache->read_qword(byteaddress); }; - } - break; - } - break; - - case 64: - switch(space().addr_shift()) { - case 0: - if(space().endianness() == ENDIANNESS_LITTLE) { - auto cache = space().cache<3, 0, ENDIANNESS_LITTLE>(); - m_r8 = [cache] (offs_t byteaddress) -> u8 { return cache->read_byte(byteaddress); }; - m_r16 = [cache] (offs_t byteaddress) -> u16 { return cache->read_word(byteaddress); }; - m_r32 = [cache] (offs_t byteaddress) -> u32 { return cache->read_dword(byteaddress); }; - m_r64 = [cache] (offs_t byteaddress) -> u64 { return cache->read_qword(byteaddress); }; - } else { - auto cache = space().cache<3, 0, ENDIANNESS_BIG>(); - m_r8 = [cache] (offs_t byteaddress) -> u8 { return cache->read_byte(byteaddress); }; - m_r16 = [cache] (offs_t byteaddress) -> u16 { return cache->read_word(byteaddress); }; - m_r32 = [cache] (offs_t byteaddress) -> u32 { return cache->read_dword(byteaddress); }; - m_r64 = [cache] (offs_t byteaddress) -> u64 { return cache->read_qword(byteaddress); }; - } - break; - case -1: - if(space().endianness() == ENDIANNESS_LITTLE) { - auto cache = space().cache<3, -1, ENDIANNESS_LITTLE>(); - m_r8 = nullptr; - m_r16 = [cache] (offs_t byteaddress) -> u16 { return cache->read_word(byteaddress); }; - m_r32 = [cache] (offs_t byteaddress) -> u32 { return cache->read_dword(byteaddress); }; - m_r64 = [cache] (offs_t byteaddress) -> u64 { return cache->read_qword(byteaddress); }; - } else { - auto cache = space().cache<3, -1, ENDIANNESS_BIG>(); - m_r8 = nullptr; - m_r16 = [cache] (offs_t byteaddress) -> u16 { return cache->read_word(byteaddress); }; - m_r32 = [cache] (offs_t byteaddress) -> u32 { return cache->read_dword(byteaddress); }; - m_r64 = [cache] (offs_t byteaddress) -> u64 { return cache->read_qword(byteaddress); }; - } - break; - case -2: - if(space().endianness() == ENDIANNESS_LITTLE) { - auto cache = space().cache<3, -2, ENDIANNESS_LITTLE>(); - m_r8 = nullptr; - m_r16 = nullptr; - m_r32 = [cache] (offs_t byteaddress) -> u32 { return cache->read_dword(byteaddress); }; - m_r64 = [cache] (offs_t byteaddress) -> u64 { return cache->read_qword(byteaddress); }; - } else { - auto cache = space().cache<3, -2, ENDIANNESS_BIG>(); - m_r8 = nullptr; - m_r16 = nullptr; - m_r32 = [cache] (offs_t byteaddress) -> u32 { return cache->read_dword(byteaddress); }; - m_r64 = [cache] (offs_t byteaddress) -> u64 { return cache->read_qword(byteaddress); }; - } - break; - case -3: - if(space().endianness() == ENDIANNESS_LITTLE) { - auto cache = space().cache<3, -3, ENDIANNESS_LITTLE>(); - m_r8 = nullptr; - m_r16 = nullptr; - m_r32 = nullptr; - m_r64 = [cache] (offs_t byteaddress) -> u64 { return cache->read_qword(byteaddress); }; - } else { - auto cache = space().cache<3, -3, ENDIANNESS_BIG>(); - m_r8 = nullptr; - m_r16 = nullptr; - m_r32 = nullptr; - m_r64 = [cache] (offs_t byteaddress) -> u64 { return cache->read_qword(byteaddress); }; - } - break; - } - break; - } - - device().save_item(NAME(m_cur_bank)); - device().save_item(NAME(m_bank_count)); - - if(!has_configured_map(0)) { - memory_region *reg = device().owner()->memregion(m_rom_tag); - if(reg) - set_rom(reg->base(), reg->bytes()); - else { - device().logerror("ROM region '%s' not found\n", m_rom_tag); - u32 end = m_rom_config.addr_width() == 32 ? 0xffffffff : (1 << m_rom_config.addr_width()) - 1; - space().unmap_read(0, end); - } - } -} diff --git a/src/emu/dirom.h b/src/emu/dirom.h index 761e1d357ec..72d844f8b0e 100644 --- a/src/emu/dirom.h +++ b/src/emu/dirom.h @@ -7,53 +7,53 @@ Interface to a rom, either through a memory map or a region ***************************************************************************/ +#ifndef MAME_EMU_DIROM_H +#define MAME_EMU_DIROM_H #pragma once -#ifndef __EMU_H__ -#error Dont include this file directly; include emu.h instead. -#endif - -#ifndef MAME_EMU_DIROM_H -#define MAME_EMU_DIROM_H -class device_rom_interface : public device_memory_interface +// Beware, DataWidth is 0-3 +template<int AddrWidth, int DataWidth = 0, int AddrShift = 0, endianness_t Endian = ENDIANNESS_LITTLE> class device_rom_interface : public device_memory_interface { public: - device_rom_interface(const machine_config &mconfig, device_t &device, u8 addrwidth, endianness_t endian = ENDIANNESS_LITTLE, u8 datawidth = 8); - virtual ~device_rom_interface(); + device_rom_interface(const machine_config &mconfig, device_t &device); + virtual ~device_rom_interface() = default; - void set_device_rom_tag(const char *tag) { m_rom_tag = tag; } + template <typename... T> void set_map(T &&... args) { set_addrmap(0, std::forward<T>(args)...); } + template <typename T> void set_device_rom_tag(T &&tag) { m_rom_region.set_tag(std::forward<T>(tag)); } + template <typename T> void set_space(T &&tag, int spacenum) { m_rom_space.set_tag(tag, spacenum); } - inline u8 read_byte(offs_t byteaddress) { return m_r8(byteaddress); } - inline u16 read_word(offs_t byteaddress) { return m_r16(byteaddress); } - inline u32 read_dword(offs_t byteaddress) { return m_r32(byteaddress); } - inline u64 read_qword(offs_t byteaddress) { return m_r64(byteaddress); } + u8 read_byte(offs_t addr) { return m_rom_cache.read_byte(addr); } + u16 read_word(offs_t addr) { return m_rom_cache.read_word(addr); } + u32 read_dword(offs_t addr) { return m_rom_cache.read_dword(addr); } + u64 read_qword(offs_t addr) { return m_rom_cache.read_qword(addr); } void set_rom(const void *base, u32 size); void set_rom_bank(int bank); protected: - virtual void rom_bank_updated() = 0; + virtual void rom_bank_pre_change() { } + virtual void rom_bank_post_change() { } virtual space_config_vector memory_space_config() const override; - void set_rom_endianness(endianness_t endian) { assert(!device().configured()); m_rom_config.m_endianness = endian; } - void set_rom_data_width(u8 width) { assert(!device().configured()); m_rom_config.m_data_width = width; } - void set_rom_addr_width(u8 width) { assert(!device().configured()); m_rom_config.m_addr_width = m_rom_config.m_logaddr_width = width; } + void override_address_width(u8 width); private: - const char *m_rom_tag; + optional_memory_region m_rom_region; + optional_address_space m_rom_space; address_space_config m_rom_config; - std::function<u8 (offs_t)> m_r8; - std::function<u16 (offs_t)> m_r16; - std::function<u32 (offs_t)> m_r32; - std::function<u64 (offs_t)> m_r64; + typename memory_access<AddrWidth, DataWidth, AddrShift, Endian>::cache m_rom_cache; - memory_bank *m_bank; - int m_cur_bank, m_bank_count; + memory_bank_creator m_bank; + u32 m_cur_bank, m_bank_count; + virtual void interface_validity_check(validity_checker &valid) const override; virtual void interface_pre_start() override; + virtual void interface_post_start() override; virtual void interface_post_load() override; }; +#include "dirom.ipp" + #endif // MAME_EMU_DIROM_H diff --git a/src/emu/dirom.ipp b/src/emu/dirom.ipp new file mode 100644 index 00000000000..d41da70a618 --- /dev/null +++ b/src/emu/dirom.ipp @@ -0,0 +1,150 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert +#ifndef MAME_EMU_DIROM_IPP +#define MAME_EMU_DIROM_IPP + +#pragma once + +template<int AddrWidth, int DataWidth, int AddrShift, endianness_t Endian> +device_rom_interface<AddrWidth, DataWidth, AddrShift, Endian>::device_rom_interface(const machine_config &mconfig, device_t &device) : + device_memory_interface(mconfig, device), + m_rom_region(device, DEVICE_SELF), + m_rom_space(device, finder_base::DUMMY_TAG, -1), + m_rom_config("rom", Endian, 8 << DataWidth, AddrWidth, AddrShift), + m_bank(device, "bank"), + m_cur_bank(-1) +{ +} + +template<int AddrWidth, int DataWidth, int AddrShift, endianness_t Endian> +void device_rom_interface<AddrWidth, DataWidth, AddrShift, Endian>::override_address_width(u8 width) +{ + // cach does not need level match, only specific does at this point + // if(emu::detail::handler_entry_dispatch_level(AddrWidth) != emu::detail::handler_entry_dispatch_level(width)) + // emu_fatalerror("%s: Widths %d and %d are incompatible", device().tag(), width, AddrWidth); + + m_rom_config.m_addr_width = width; +} + +template<int AddrWidth, int DataWidth, int AddrShift, endianness_t Endian> +device_memory_interface::space_config_vector device_rom_interface<AddrWidth, DataWidth, AddrShift, Endian>::memory_space_config() const +{ + return space_config_vector { + std::make_pair(0, &m_rom_config) + }; +} + +template<int AddrWidth, int DataWidth, int AddrShift, endianness_t Endian> +void device_rom_interface<AddrWidth, DataWidth, AddrShift, Endian>::set_rom_bank(int bank) +{ + if(!m_bank) + throw emu_fatalerror("%s: device_rom_interface::set_rom_bank called without banking setup", device().tag()); + + if(bank >= m_bank_count) { + device().logerror("Warning: requested bank %x higher than actual bank count %x\n", bank, m_bank_count); + bank = bank % m_bank_count; + } + + if (m_cur_bank != bank) { + rom_bank_pre_change(); + m_cur_bank = bank; + m_bank->set_entry(bank); + rom_bank_post_change(); + } +} + +template<int AddrWidth, int DataWidth, int AddrShift, endianness_t Endian> +void device_rom_interface<AddrWidth, DataWidth, AddrShift, Endian>::interface_post_load() +{ + device_memory_interface::interface_post_load(); + + if(m_bank) + m_bank->set_entry(m_cur_bank); +} + +template<int AddrWidth, int DataWidth, int AddrShift, endianness_t Endian> +void device_rom_interface<AddrWidth, DataWidth, AddrShift, Endian>::set_rom(const void *base, u32 size) +{ + const u32 mend = make_bitmask<u32>(m_rom_config.addr_width()); + const u32 rend = size-1; + m_bank_count = (mend == 0xffffffff) ? 1 : (rend+1) / (mend+1); + if(m_bank_count < 1) + m_bank_count = 1; + + if(rend >= mend) { + space().install_read_bank(0, mend, m_bank); + m_bank->configure_entries(0, m_bank_count, const_cast<void *>(base), mend+1); + m_bank->set_entry(0); + m_cur_bank = 0; + + } else { + // Round up to the nearest power-of-two-minus-one + u32 rmask = rend; + rmask |= rmask >> 1; + rmask |= rmask >> 2; + rmask |= rmask >> 4; + rmask |= rmask >> 8; + rmask |= rmask >> 16; + if(rmask != rend) + space().unmap_read(0, mend); + // Mirror over the high bits. mend and rmask are both + // powers-of-two-minus-one, so the xor works + space().install_rom(0, rend, mend ^ rmask, const_cast<void *>(base)); + } +} + +template<int AddrWidth, int DataWidth, int AddrShift, endianness_t Endian> +void device_rom_interface<AddrWidth, DataWidth, AddrShift, Endian>::interface_validity_check(validity_checker &valid) const +{ + device_memory_interface::interface_validity_check(valid); + + if(has_configured_map(0)) { + const auto rom_target = m_rom_region.finder_target(); + if((&rom_target.first != &device()) || strcmp(rom_target.second, DEVICE_SELF)) + osd_printf_error("Address map and ROM region both specified\n"); + } +} + +template<int AddrWidth, int DataWidth, int AddrShift, endianness_t Endian> +void device_rom_interface<AddrWidth, DataWidth, AddrShift, Endian>::interface_pre_start() +{ + device_memory_interface::interface_pre_start(); + + if(m_rom_space.spacenum() != -1) { + m_rom_space->cache(m_rom_cache); + return; + } + + if(!has_space(0)) + return; + + space().cache(m_rom_cache); + + if(!has_configured_map(0)) { + if(m_rom_region.found()) + set_rom(m_rom_region->base(), m_rom_region->bytes()); + else { + const auto rom_target = m_rom_region.finder_target(); + if((&rom_target.first != &device()) || strcmp(rom_target.second, DEVICE_SELF)) + throw emu_fatalerror("%s: device_rom_interface ROM region '%s' not found", device().tag(), rom_target.first.subtag(rom_target.second)); + + device().logerror("ROM region '%s' not found\n", rom_target.first.subtag(rom_target.second)); + space().unmap_read(0, make_bitmask<offs_t>(m_rom_config.addr_width())); + } + } else { + const auto rom_target = m_rom_region.finder_target(); + if((&rom_target.first != &device()) || strcmp(rom_target.second, DEVICE_SELF)) + throw emu_fatalerror("%s: device_rom_interface has configured address map and ROM region", device().tag()); + } +} + +template<int AddrWidth, int DataWidth, int AddrShift, endianness_t Endian> +void device_rom_interface<AddrWidth, DataWidth, AddrShift, Endian>::interface_post_start() +{ + device_memory_interface::interface_post_start(); + + device().save_item(NAME(m_cur_bank)); + device().save_item(NAME(m_bank_count)); +} + +#endif // MAME_EMU_DIROM_IPP diff --git a/src/emu/dirtc.cpp b/src/emu/dirtc.cpp index 20ebbbacef8..bf1a9366578 100644 --- a/src/emu/dirtc.cpp +++ b/src/emu/dirtc.cpp @@ -2,7 +2,7 @@ // copyright-holders:Curt Coder /*************************************************************************** - dirtc.c + dirtc.cpp Device Real Time Clock interfaces. @@ -30,6 +30,7 @@ static const int DAYS_PER_MONTH[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, device_rtc_interface::device_rtc_interface(const machine_config &mconfig, device_t &device) : device_interface(device, "rtc") + , m_use_utc(false) { } @@ -78,8 +79,9 @@ void device_rtc_interface::set_time(bool update, int year, int month, int day, i void device_rtc_interface::set_current_time(const system_time &systime) { - set_time(true, systime.local_time.year, systime.local_time.month + 1, systime.local_time.mday, systime.local_time.weekday + 1, - systime.local_time.hour, systime.local_time.minute, systime.local_time.second); + const system_time::full_time &time = m_use_utc ? systime.utc_time : systime.local_time; + set_time(true, time.year, time.month + 1, time.mday, time.weekday + 1, + time.hour, time.minute, time.second); } diff --git a/src/emu/dirtc.h b/src/emu/dirtc.h index 76658b12a9f..e76a8d3dba3 100644 --- a/src/emu/dirtc.h +++ b/src/emu/dirtc.h @@ -47,6 +47,9 @@ public: device_rtc_interface(const machine_config &mconfig, device_t &device); virtual ~device_rtc_interface(); + // use UTC instead of local time when syncing at startup + void set_use_utc(bool use_utc) { m_use_utc = use_utc; } + void set_time(bool update, int year, int month, int day, int day_of_week, int hour, int minute, int second); void set_current_time(const system_time &systime); @@ -72,10 +75,12 @@ protected: virtual void rtc_clock_updated(int year, int month, int day, int day_of_week, int hour, int minute, int second) = 0; int m_register[7]; + + bool m_use_utc; }; // iterator -typedef device_interface_iterator<device_rtc_interface> rtc_interface_iterator; +typedef device_interface_enumerator<device_rtc_interface> rtc_interface_enumerator; #endif // MAME_EMU_DIRTC_H diff --git a/src/emu/diserial.cpp b/src/emu/diserial.cpp index 0852a1bc666..2d900f27459 100644 --- a/src/emu/diserial.cpp +++ b/src/emu/diserial.cpp @@ -10,6 +10,14 @@ #include "emu.h" #include "diserial.h" +#define LOG_SETUP (1U << 1) +#define LOG_TX (1U << 2) +#define LOG_RX (1U << 3) +#define VERBOSE (0) + +#define LOG_OUTPUT_FUNC device().logerror +#include "logmacro.h" + device_serial_interface::device_serial_interface(const machine_config &mconfig, device_t &device) : device_interface(device, "serial"), m_start_bit_hack_for_external_clocks(false), @@ -17,6 +25,7 @@ device_serial_interface::device_serial_interface(const machine_config &mconfig, m_df_word_length(0), m_df_parity(PARITY_NONE), m_df_stop_bit_count(STOP_BITS_0), + m_df_min_rx_stop_bit_count(0), m_rcv_register_data(0x8000), m_rcv_flags(0), m_rcv_bit_count_received(0), @@ -36,21 +45,6 @@ device_serial_interface::device_serial_interface(const machine_config &mconfig, m_tra_clock_state(false), m_rcv_clock_state(false) { - /* if sum of all bits in the byte is even, then the data - has even parity, otherwise it has odd parity */ - for (int i=0; i<256; i++) - { - int sum = 0; - int data = i; - - for (int b=0; b<8; b++) - { - sum+=data & 0x01; - data = data>>1; - } - - m_serial_parity_table[i] = sum & 0x01; - } } device_serial_interface::~device_serial_interface() @@ -73,6 +67,7 @@ void device_serial_interface::interface_post_start() device().save_item(NAME(m_df_word_length)); device().save_item(NAME(m_df_parity)); device().save_item(NAME(m_df_stop_bit_count)); + device().save_item(NAME(m_df_min_rx_stop_bit_count)); device().save_item(NAME(m_rcv_register_data)); device().save_item(NAME(m_rcv_flags)); device().save_item(NAME(m_rcv_bit_count_received)); @@ -130,7 +125,7 @@ void device_serial_interface::rcv_edge() } } -WRITE_LINE_MEMBER(device_serial_interface::tx_clock_w) +void device_serial_interface::tx_clock_w(int state) { if(state != m_tra_clock_state) { m_tra_clock_state = state; @@ -139,7 +134,7 @@ WRITE_LINE_MEMBER(device_serial_interface::tx_clock_w) } } -WRITE_LINE_MEMBER(device_serial_interface::rx_clock_w) +void device_serial_interface::rx_clock_w(int state) { if(state != m_rcv_clock_state) { m_rcv_clock_state = state; @@ -148,7 +143,7 @@ WRITE_LINE_MEMBER(device_serial_interface::rx_clock_w) } } -WRITE_LINE_MEMBER(device_serial_interface::clock_w) +void device_serial_interface::clock_w(int state) { tx_clock_w(state); rx_clock_w(state); @@ -157,7 +152,7 @@ WRITE_LINE_MEMBER(device_serial_interface::clock_w) void device_serial_interface::set_data_frame(int start_bit_count, int data_bit_count, parity_t parity, stop_bits_t stop_bits) { - //device().logerror("Start bits: %d; Data bits: %d; Parity: %s; Stop bits: %s\n", start_bit_count, data_bit_count, parity_tostring(parity), stop_bits_tostring(stop_bits)); + LOGMASKED(LOG_SETUP, "Start bits: %d; Data bits: %d; Parity: %s; Stop bits: %s\n", start_bit_count, data_bit_count, parity_tostring(parity), stop_bits_tostring(stop_bits)); m_df_word_length = data_bit_count; @@ -166,25 +161,30 @@ void device_serial_interface::set_data_frame(int start_bit_count, int data_bit_c case STOP_BITS_0: default: m_df_stop_bit_count = 0; + m_df_min_rx_stop_bit_count = 0; break; case STOP_BITS_1: m_df_stop_bit_count = 1; + m_df_min_rx_stop_bit_count = 1; break; case STOP_BITS_1_5: m_df_stop_bit_count = 2; // TODO: support 1.5 stop bits + m_df_min_rx_stop_bit_count = 1; break; case STOP_BITS_2: m_df_stop_bit_count = 2; + m_df_min_rx_stop_bit_count = 1; break; } m_df_parity = parity; m_df_start_bit_count = start_bit_count; - m_rcv_bit_count = m_df_word_length + m_df_stop_bit_count; + /* Require at least one stop bit in async RX mode, none in sync RX mode. */ + m_rcv_bit_count = m_df_word_length + m_df_min_rx_stop_bit_count; if (m_df_parity != PARITY_NONE) { @@ -208,19 +208,21 @@ void device_serial_interface::receive_register_reset() } } -WRITE_LINE_MEMBER(device_serial_interface::rx_w) +void device_serial_interface::rx_w(int state) { m_rcv_line = state; - if(m_rcv_flags & RECEIVE_REGISTER_SYNCHRONISED) + if (m_rcv_flags & RECEIVE_REGISTER_SYNCHRONISED) return; receive_register_update_bit(state); - if(m_rcv_flags & RECEIVE_REGISTER_SYNCHRONISED) + if (m_rcv_flags & RECEIVE_REGISTER_SYNCHRONISED) { - //device().logerror("Receiver is synchronized\n"); - if(m_rcv_clock && !(m_rcv_rate.is_never())) - // make start delay just a bit longer to make sure we are called after the sender - m_rcv_clock->adjust(((m_rcv_rate*3)/2), 0, m_rcv_rate); - else if(m_start_bit_hack_for_external_clocks) + LOGMASKED(LOG_RX, "Receiver is synchronized\n"); + if (m_rcv_clock && !(m_rcv_rate.is_never())) + { + // make start delay half a cycle longer to make sure we are called after the sender + m_rcv_clock->adjust(m_rcv_rate*2, 0, m_rcv_rate); + } + else if (m_start_bit_hack_for_external_clocks) m_rcv_bit_count_received--; } return; @@ -235,7 +237,6 @@ void device_serial_interface::receive_register_update_bit(int bit) { int previous_bit; - //LOG(("receive register receive bit: %1x\n",bit)); previous_bit = (m_rcv_register_data & 0x8000) ? 1 : 0; /* shift previous bit 7 out */ @@ -254,7 +255,7 @@ void device_serial_interface::receive_register_update_bit(int bit) /* yes */ if (bit==0) { - //logerror("receive register saw start bit\n"); + LOGMASKED(LOG_RX, "Receiver saw start bit (%s)\n", device().machine().time().to_string()); /* seen start bit! */ /* not waiting for start bit now! */ @@ -265,16 +266,20 @@ void device_serial_interface::receive_register_update_bit(int bit) m_rcv_framing_error = false; m_rcv_parity_error = false; } + else + { + LOGMASKED(LOG_RX, "Receiver saw stop bit (%s)\n", device().machine().time().to_string()); + } } } - else - if (m_rcv_flags & RECEIVE_REGISTER_SYNCHRONISED) + else if (m_rcv_flags & RECEIVE_REGISTER_SYNCHRONISED) { - //device().logerror("Received bit %d\n", m_rcv_bit_count_received); + LOGMASKED(LOG_RX, "Received bit %d as %d (%s)\n", m_rcv_bit_count_received, bit, device().machine().time().to_string()); m_rcv_bit_count_received++; - if (!bit && (m_rcv_bit_count_received > (m_rcv_bit_count - m_df_stop_bit_count))) + if (!bit && (m_rcv_bit_count_received > (m_rcv_bit_count - m_df_min_rx_stop_bit_count))) { + LOGMASKED(LOG_RX, "Framing error\n"); m_rcv_framing_error = true; } @@ -284,7 +289,7 @@ void device_serial_interface::receive_register_update_bit(int bit) m_rcv_bit_count_received = 0; m_rcv_flags &=~RECEIVE_REGISTER_SYNCHRONISED; m_rcv_flags |= RECEIVE_REGISTER_WAITING_FOR_START_BIT; - //device().logerror("Receive register full\n"); + LOGMASKED(LOG_RX, "Receive register full\n"); m_rcv_flags |= RECEIVE_REGISTER_FULL; } } @@ -297,13 +302,14 @@ void device_serial_interface::receive_register_extract() receive_register_reset(); /* strip off stop bits and parity */ - assert(m_rcv_bit_count >0 && m_rcv_bit_count <= 16); - data = m_rcv_register_data>>(16-m_rcv_bit_count); + assert(m_rcv_bit_count > 0 && m_rcv_bit_count <= 16); + data = m_rcv_register_data >> (16 - m_rcv_bit_count); /* mask off other bits so data byte has 0's in unused bits */ - data &= ~(0xff<<m_df_word_length); + data &= ~(0xff << m_df_word_length); - m_rcv_byte_received = data; + m_rcv_byte_received = data; + LOGMASKED(LOG_RX, "Receive data 0x%02x\n", m_rcv_byte_received); if(m_df_parity == PARITY_NONE) return; @@ -315,12 +321,12 @@ void device_serial_interface::receive_register_extract() switch (m_df_parity) { case PARITY_ODD: - if (parity_received == serial_helper_get_parity(data)) + if (parity_received == BIT(population_count_32(data), 0)) m_rcv_parity_error = true; break; case PARITY_EVEN: - if (parity_received != serial_helper_get_parity(data)) + if (parity_received != BIT(population_count_32(data), 0)) m_rcv_parity_error = true; break; @@ -348,9 +354,8 @@ void device_serial_interface::transmit_register_reset() void device_serial_interface::transmit_register_add_bit(int bit) { /* combine bit */ - m_tra_register_data = m_tra_register_data<<1; - m_tra_register_data &=~1; - m_tra_register_data|=(bit & 0x01); + m_tra_register_data = m_tra_register_data << 1; + m_tra_register_data |= (bit & 0x01); m_tra_bit_count++; } @@ -358,10 +363,9 @@ void device_serial_interface::transmit_register_add_bit(int bit) /* generate data in stream format ready for transfer */ void device_serial_interface::transmit_register_setup(u8 data_byte) { - int i; u8 transmit_data; - if(m_tra_clock && !m_tra_rate.is_never()) + if (m_tra_clock && !m_tra_rate.is_never()) m_tra_clock->adjust(m_tra_rate, 0, m_tra_rate); m_tra_bit_count_transmitted = 0; @@ -369,40 +373,35 @@ void device_serial_interface::transmit_register_setup(u8 data_byte) m_tra_flags &=~TRANSMIT_REGISTER_EMPTY; /* start bit */ - for (i=0; i<m_df_start_bit_count; i++) + for (int i = 0; i < m_df_start_bit_count; i++) { transmit_register_add_bit(0); } /* data bits */ transmit_data = data_byte; - for (i=0; i<m_df_word_length; i++) + for (int i = 0; i < m_df_word_length; i++) { - int databit; - - /* get bit from data */ - databit = transmit_data & 0x01; /* add bit to formatted byte */ - transmit_register_add_bit(databit); - transmit_data = transmit_data>>1; + transmit_register_add_bit(BIT(transmit_data, 0)); + transmit_data >>= 1; } /* parity */ - if (m_df_parity!=PARITY_NONE) + if (m_df_parity != PARITY_NONE) { /* odd or even parity */ u8 parity = 0; switch (m_df_parity) { case PARITY_ODD: - - /* get parity */ - /* if parity = 0, data has even parity - i.e. there is an even number of one bits in the data */ - /* if parity = 1, data has odd parity - i.e. there is an odd number of one bits in the data */ - parity = serial_helper_get_parity(data_byte) ^ 1; + // get parity + // if parity[0] = 0, data has even parity - i.e. there is an even number of one bits in the data + // if parity[0] = 1, data has odd parity - i.e. there is an odd number of one bits in the data + parity = BIT(population_count_32(data_byte), 0) ^ 1; break; case PARITY_EVEN: - parity = serial_helper_get_parity(data_byte); + parity = BIT(population_count_32(data_byte), 0); break; case PARITY_MARK: parity = 1; @@ -414,10 +413,9 @@ void device_serial_interface::transmit_register_setup(u8 data_byte) transmit_register_add_bit(parity); } - /* stop bit(s) + 1 extra bit as delay between bytes, needed to get 1 stop bit to work. */ - if (m_df_stop_bit_count) // no stop bits for synchronous - for (i=0; i<=m_df_stop_bit_count; i++) // ToDo - see if the hack on this line is still needed (was added 2016-04-10) - transmit_register_add_bit(1); + /* TX stop bit(s) */ + for (int i = 0; i < m_df_stop_bit_count; i++) + transmit_register_add_bit(1); } @@ -428,13 +426,17 @@ u8 device_serial_interface::transmit_register_get_data_bit() bit = (m_tra_register_data>>(m_tra_bit_count-1-m_tra_bit_count_transmitted))&1; + if (m_tra_bit_count_transmitted < m_df_start_bit_count) + LOGMASKED(LOG_TX, "Transmitting start bit %d as %d (%s)\n", m_tra_bit_count_transmitted, bit, device().machine().time().to_string()); + else + LOGMASKED(LOG_TX, "Transmitting bit %d as %d (%s)\n", m_tra_bit_count_transmitted - m_df_start_bit_count, bit, device().machine().time().to_string()); m_tra_bit_count_transmitted++; - //device().logerror("%d bits transmitted\n", m_tra_bit_count_transmitted); /* have all bits of this stream formatted byte been sent? */ if (m_tra_bit_count_transmitted==m_tra_bit_count) { /* yes - generate a new byte to send */ + LOGMASKED(LOG_TX, "Transmit register empty\n"); m_tra_flags |= TRANSMIT_REGISTER_EMPTY; } diff --git a/src/emu/diserial.h b/src/emu/diserial.h index 7d7717c5158..eac3599e68a 100644 --- a/src/emu/diserial.h +++ b/src/emu/diserial.h @@ -76,10 +76,10 @@ public: device_serial_interface(const machine_config &mconfig, device_t &device); virtual ~device_serial_interface(); - DECLARE_WRITE_LINE_MEMBER(rx_w); - DECLARE_WRITE_LINE_MEMBER(tx_clock_w); - DECLARE_WRITE_LINE_MEMBER(rx_clock_w); - DECLARE_WRITE_LINE_MEMBER(clock_w); + void rx_w(int state); + void tx_clock_w(int state); + void rx_clock_w(int state); + void clock_w(int state); protected: void set_data_frame(int start_bit_count, int data_bit_count, parity_t parity, stop_bits_t stop_bits); @@ -103,8 +103,6 @@ protected: void transmit_register_setup(u8 data_byte); u8 transmit_register_get_data_bit(); - u8 serial_helper_get_parity(u8 data) { return m_serial_parity_table[data]; } - bool is_receive_register_full() const { return m_rcv_flags & RECEIVE_REGISTER_FULL; } bool is_transmit_register_empty() const { return m_tra_flags & TRANSMIT_REGISTER_EMPTY; } bool is_receive_register_synchronized() const { return m_rcv_flags & RECEIVE_REGISTER_SYNCHRONISED; } @@ -132,8 +130,6 @@ private: TIMER_CALLBACK_MEMBER(rcv_clock) { rx_clock_w(!m_rcv_clock_state); } TIMER_CALLBACK_MEMBER(tra_clock) { tx_clock_w(!m_tra_clock_state); } - u8 m_serial_parity_table[256]; - // Data frame // number of start bits int m_df_start_bit_count; @@ -141,8 +137,10 @@ private: u8 m_df_word_length; // parity state u8 m_df_parity; - // number of stop bits + // number of TX stop bits u8 m_df_stop_bit_count; + // min. number of RX stop bits + u8 m_df_min_rx_stop_bit_count; // Receive register /* data */ @@ -201,8 +199,8 @@ protected: virtual void tra_complete() override { assert(!m_empty || (m_head == m_tail)); - assert(m_head < ARRAY_LENGTH(m_fifo)); - assert(m_tail < ARRAY_LENGTH(m_fifo)); + assert(m_head < std::size(m_fifo)); + assert(m_tail < std::size(m_fifo)); if (!m_empty) { @@ -227,8 +225,8 @@ protected: void transmit_byte(u8 byte) { assert(!m_empty || (m_head == m_tail)); - assert(m_head < ARRAY_LENGTH(m_fifo)); - assert(m_tail < ARRAY_LENGTH(m_fifo)); + assert(m_head < std::size(m_fifo)); + assert(m_tail < std::size(m_fifo)); if (m_empty && is_transmit_register_empty()) { diff --git a/src/emu/dislot.cpp b/src/emu/dislot.cpp index e5bb4cff0d3..43d2ed96cdd 100644 --- a/src/emu/dislot.cpp +++ b/src/emu/dislot.cpp @@ -8,12 +8,11 @@ #include "emu.h" #include "emuopts.h" -#include "zippath.h" +#include "corestr.h" +#include "path.h" +#include "zippath.h" -// ------------------------------------------------- -// ctor -// ------------------------------------------------- device_slot_interface::device_slot_interface(const machine_config &mconfig, device_t &device) : device_interface(device, "slot"), @@ -24,21 +23,12 @@ device_slot_interface::device_slot_interface(const machine_config &mconfig, devi { } - -// ------------------------------------------------- -// dtor -// ------------------------------------------------- - device_slot_interface::~device_slot_interface() { } -// ------------------------------------------------- -// device_slot_option ctor -// ------------------------------------------------- - -device_slot_interface::slot_option::slot_option(const char *name, const device_type &devtype, bool selectable) : +device_slot_interface::slot_option::slot_option(const char *name, device_type devtype, bool selectable) : m_name(name), m_devtype(devtype), m_selectable(selectable), @@ -50,43 +40,74 @@ device_slot_interface::slot_option::slot_option(const char *name, const device_t } -// ------------------------------------------------- -// option_add -// ------------------------------------------------- +void device_slot_interface::interface_validity_check(validity_checker &valid) const +{ + if (m_default_option && (m_options.find(m_default_option) == m_options.end())) + osd_printf_error("Default option '%s' does not correspond to any configured option\n", m_default_option); +} + -device_slot_interface::slot_option &device_slot_interface::option_add(const char *name, const device_type &devtype) +device_slot_interface::slot_option &device_slot_interface::option_add(const char *name, device_type devtype) { + if (!name || !*name) + throw emu_fatalerror("slot '%s' attempt to add option without name\n", device().tag()); + const slot_option *const existing = option(name); if (existing) throw emu_fatalerror("slot '%s' duplicate option '%s'\n", device().tag(), name); - if (m_options.count(name)) - throw tag_add_exception(name); - return m_options.emplace(name, std::make_unique<slot_option>(name, devtype, true)).first->second->clock(m_default_clock); } -// ------------------------------------------------- -// option_add_internal -// ------------------------------------------------- - -device_slot_interface::slot_option &device_slot_interface::option_add_internal(const char *name, const device_type &devtype) +device_slot_interface::slot_option &device_slot_interface::option_add_internal(const char *name, device_type devtype) { + if (!name || !*name) + throw emu_fatalerror("slot '%s' attempt to add option without name\n", device().tag()); + const slot_option *const existing = option(name); if (existing) throw emu_fatalerror("slot '%s' duplicate option '%s'\n", device().tag(), name); - if (m_options.count(name)) - throw tag_add_exception(name); - return m_options.emplace(name, std::make_unique<slot_option>(name, devtype, false)).first->second->clock(m_default_clock); } -// ------------------------------------------------- -// option -// ------------------------------------------------- +device_slot_interface::slot_option &device_slot_interface::option_replace(const char *name, device_type devtype) +{ + if (!name || !*name) + throw emu_fatalerror("slot '%s' attempt to replace option without name\n", device().tag()); + + auto search = m_options.find(name); + if (search == m_options.end()) + throw emu_fatalerror("slot '%s' attempt to replace nonexistent option '%s'\n", device().tag(), name); + + return (search->second = std::make_unique<slot_option>(name, devtype, true))->clock(m_default_clock); +} + + +device_slot_interface::slot_option &device_slot_interface::option_replace_internal(const char *name, device_type devtype) +{ + if (!name || !*name) + throw emu_fatalerror("slot '%s' attempt to replace option without name\n", device().tag()); + + auto search = m_options.find(name); + if (search == m_options.end()) + throw emu_fatalerror("slot '%s' attempt to replace nonexistent option '%s'\n", device().tag(), name); + + return (search->second = std::make_unique<slot_option>(name, devtype, false))->clock(m_default_clock); +} + + +void device_slot_interface::option_remove(const char *name) +{ + if (!name || !*name) + throw emu_fatalerror("slot '%s' attempt to remove option without name\n", device().tag()); + + if (m_options.erase(name) == 0) + throw emu_fatalerror("slot '%s' attempt to remove nonexistent option '%s'\n", device().tag(), name); +} + device_slot_interface::slot_option *device_slot_interface::config_option(const char *name) { @@ -98,10 +119,6 @@ device_slot_interface::slot_option *device_slot_interface::config_option(const c } -// ------------------------------------------------- -// has_selectable_options -// ------------------------------------------------- - bool device_slot_interface::has_selectable_options() const { if (!fixed()) @@ -114,10 +131,6 @@ bool device_slot_interface::has_selectable_options() const } -// ------------------------------------------------- -// option -// ------------------------------------------------- - const device_slot_interface::slot_option *device_slot_interface::option(const char *name) const { if (name) @@ -130,15 +143,6 @@ const device_slot_interface::slot_option *device_slot_interface::option(const ch } -device_slot_card_interface::device_slot_card_interface(const machine_config &mconfig, device_t &device) - : device_interface(device, "slot") -{ -} - -device_slot_card_interface::~device_slot_card_interface() -{ -} - get_default_card_software_hook::get_default_card_software_hook(const std::string &path, std::function<bool(util::core_file &, std::string&)> &&get_hashfile_extrainfo) : m_get_hashfile_extrainfo(std::move(get_hashfile_extrainfo)) , m_called_get_hashfile_extrainfo(false) @@ -164,3 +168,8 @@ bool get_default_card_software_hook::hashfile_extrainfo(std::string &extrainfo) extrainfo = m_hash_extrainfo; return m_has_hash_extrainfo; } + +bool get_default_card_software_hook::is_filetype(std::string_view candidate_filetype) const +{ + return util::streqlower(m_file_type, candidate_filetype); +} diff --git a/src/emu/dislot.h b/src/emu/dislot.h index da6a1b0aa3d..f6e1f86fcb3 100644 --- a/src/emu/dislot.h +++ b/src/emu/dislot.h @@ -9,13 +9,10 @@ #ifndef MAME_EMU_DISLOT_H #define MAME_EMU_DISLOT_H - //************************************************************************** // TYPE DEFINITIONS //************************************************************************** -// ======================> get_default_card_software_hook - class get_default_card_software_hook { // goofy "hook" to pass to device_slot_interface::get_default_card_software @@ -27,7 +24,7 @@ public: util::core_file::ptr &image_file() { return m_image_file; } // checks to see if image is of the specified "file type" (in practice, file extension) - bool is_filetype(const char *candidate_filetype) const { return !core_stricmp(m_file_type.c_str(), candidate_filetype); } + bool is_filetype(std::string_view candidate_filetype) const; // extra info from hashfile bool hashfile_extrainfo(std::string &extrainfo); @@ -42,18 +39,17 @@ private: }; -// ======================> device_slot_interface - +/// \brief Base class for configurable slot devices class device_slot_interface : public device_interface { public: class slot_option { public: - slot_option(char const *name, device_type const &devtype, bool selectable); + slot_option(char const *name, device_type devtype, bool selectable); char const *name() const { return m_name; } - device_type const &devtype() const { return m_devtype; } + device_type devtype() const { return m_devtype; } bool selectable() const { return m_selectable; } char const *default_bios() const { return m_default_bios; } std::function<void (device_t *)> const &machine_config() const { return m_machine_config; } @@ -77,20 +73,92 @@ public: u32 m_clock; }; - // construction/destruction - device_slot_interface(const machine_config &mconfig, device_t &device); virtual ~device_slot_interface(); - void set_fixed(bool fixed) { m_fixed = fixed; } - void set_default_option(const char *option) { m_default_option = option; } - void option_reset() { m_options.clear(); } - slot_option &option_add(const char *option, const device_type &devtype); - slot_option &option_add_internal(const char *option, const device_type &devtype); + /// \brief Set whether slot is fixed + /// + /// Allows a slot to be configured as fixed. Fixed slots can only + /// be configured programmatically. Options for fixed slots are not + /// user-selectable. By default, slots are not fixed. + /// \param [in] fixed True to mark the slot as fixed, or false to + /// mark it user-configurable. + /// \sa fixed + device_slot_interface &set_fixed(bool fixed) { m_fixed = fixed; return *this; } + + /// \brief Set the default option + /// + /// Set the default option the slot. The corresponding card device + /// will be loaded if no other option is selected by the user or + /// programmatically. + /// \param [in] option The name of the default option. This must be + /// correspond to an option added using #option_add or + /// #option_add_internal, or be nullptr to not load any card + /// device by default. The string is not copied and must remain + /// valid for the lifetime of the device (or until another default + /// option is configured). + /// \sa default_option + device_slot_interface &set_default_option(const char *option) { m_default_option = option; return *this; } + + /// \brief Clear options + /// + /// This removes all previously added options. + device_slot_interface &option_reset() { m_options.clear(); return *this; } + + /// \brief Add a user-selectable option + /// + /// Adds an option that may be selected by the user via the command + /// line or other configureation methods. + /// \param [in] option The name used to select the option. This + /// will also be used as the tag when instantiating the card. It + /// must be a valid device tag, and should be terse but + /// descriptive. The string is copied when the option is added. + /// \param [in] devtype Device type of the option. The device type + /// description is used in the user interface. + /// \return A reference to the added option for additional + /// configuration. + slot_option &option_add(char const *option, device_type devtype); + + /// \brief Add an internal option + /// + /// Adds an option that may only be selected programmatically. This + /// is most often used for options used for loading software. + /// \param [in] option The name used to select the option. This + /// will also be used as the tag when instantiating the card. It + /// must be a valid device tag. The string is copied when the + /// option is added. + /// \param [in] devtype Device type of the option. The device type + /// description is used in the user interface. + /// \return A reference to the added option for additional + /// configuration. + slot_option &option_add_internal(const char *option, device_type devtype); + + slot_option &option_replace(const char *option, device_type devtype); + slot_option &option_replace_internal(const char *option, device_type devtype); + void option_remove(const char *option); + void set_option_default_bios(const char *option, const char *default_bios) { config_option(option)->default_bios(default_bios); } template <typename T> void set_option_machine_config(const char *option, T &&machine_config) { config_option(option)->machine_config(std::forward<T>(machine_config)); } void set_option_device_input_defaults(const char *option, const input_device_default *default_input) { config_option(option)->input_device_defaults(default_input); } + + /// \brief Returns whether the slot is fixed + /// + /// Fixed slots can only be configured programmatically. Slots are + /// not fixed by default. + /// \return True if the slot is fixed, or false if it is + /// user-configurable. + /// \sa set_fixed bool fixed() const { return m_fixed; } + + /// \brief Returns true if the slot has user-selectable options + /// + /// Returns true if the slot is not marked as fixed and has at least + /// one user-selectable option (added using #option_add rather than + /// #option_add_internal). Returns false if the slot is marked as + /// fixed, all options are internal, or there are no options. + /// \return True if the slot has user-selectable options, or false + /// otherwise. bool has_selectable_options() const; + const char *default_option() const { return m_default_option; } const std::unordered_map<std::string, std::unique_ptr<slot_option>> &option_list() const { return m_options; } const slot_option *option(const char *name) const; @@ -98,33 +166,71 @@ public: device_t *get_card_device() const { return m_card_device; } void set_card_device(device_t *dev) { m_card_device = dev; } const char *slot_name() const { return device().tag() + 1; } - slot_option &option_set(const char *tag, const device_type &devtype) { m_default_option = tag; m_fixed = true; return option_add_internal(tag, devtype); } + slot_option &option_set(const char *tag, device_type devtype) { m_default_option = tag; m_fixed = true; return option_add_internal(tag, devtype); } protected: + device_slot_interface(machine_config const &mconfig, device_t &device); + virtual void interface_validity_check(validity_checker &valid) const override ATTR_COLD; void set_default_clock(u32 clock) { m_default_clock = clock; } private: // internal state - std::unordered_map<std::string,std::unique_ptr<slot_option>> m_options; + std::unordered_map<std::string, std::unique_ptr<slot_option>> m_options; u32 m_default_clock; - const char *m_default_option; + char const *m_default_option; bool m_fixed; device_t *m_card_device; - slot_option *config_option(const char *option); + slot_option *config_option(char const *option); }; -// iterator -typedef device_interface_iterator<device_slot_interface> slot_interface_iterator; - -// ======================> device_slot_card_interface -class device_slot_card_interface : public device_interface +/// \brief Base class for slots that accept a single card interface +/// +/// Performs basic validity checks to ensure the configured card (if +/// any) implements the required interface. +template <typename Card> +class device_single_card_slot_interface : public device_slot_interface { public: - // construction/destruction - device_slot_card_interface(const machine_config &mconfig, device_t &device); - virtual ~device_slot_card_interface(); + Card *get_card_device() const { return dynamic_cast<Card *>(device_slot_interface::get_card_device()); } + +protected: + device_single_card_slot_interface(machine_config const &mconfig, device_t &device) : + device_slot_interface(mconfig, device) + { + } + + virtual void interface_validity_check(validity_checker &valid) const override ATTR_COLD + { + device_slot_interface::interface_validity_check(valid); + device_t *const card(device_slot_interface::get_card_device()); + if (card && !dynamic_cast<Card *>(card)) + { + osd_printf_error( + "Card device %s (%s) does not implement %s\n", + card->tag(), + card->name(), + typeid(Card).name()); + } + } + + virtual void interface_pre_start() override ATTR_COLD + { + device_slot_interface::interface_pre_start(); + device_t *const card(device_slot_interface::get_card_device()); + if (card && !dynamic_cast<Card *>(card)) + { + throw emu_fatalerror( + "slot '%s' card device %s (%s) does not implement %s\n", + device().tag(), + card->tag(), + card->name(), + typeid(Card).name()); + } + } }; -#endif /* MAME_EMU_DISLOT_H */ +typedef device_interface_enumerator<device_slot_interface> slot_interface_enumerator; + +#endif // MAME_EMU_DISLOT_H diff --git a/src/emu/disound.cpp b/src/emu/disound.cpp index f4d9b194f8f..57fd6a9c030 100644 --- a/src/emu/disound.cpp +++ b/src/emu/disound.cpp @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles /*************************************************************************** - disound.c + disound.cpp Device sound interfaces. @@ -21,10 +21,13 @@ // device_sound_interface - constructor //------------------------------------------------- -device_sound_interface::device_sound_interface(const machine_config &mconfig, device_t &device) - : device_interface(device, "sound") - , m_outputs(0) - , m_auto_allocated_inputs(0) +device_sound_interface::device_sound_interface(const machine_config &mconfig, device_t &device) : + device_interface(device, "sound"), + m_sound_requested_inputs_mask(0), + m_sound_requested_outputs_mask(0), + m_sound_requested_inputs(0), + m_sound_requested_outputs(0), + m_sound_hook(false) { } @@ -42,25 +45,20 @@ device_sound_interface::~device_sound_interface() // add_route - send sound output to a consumer //------------------------------------------------- -device_sound_interface &device_sound_interface::add_route(u32 output, const char *target, double gain, u32 input, u32 mixoutput) +device_sound_interface &device_sound_interface::add_route(u32 output, const char *target, double gain, u32 channel) { - return add_route(output, device().mconfig().current_device(), target, gain, input, mixoutput); + return add_route(output, device().mconfig().current_device(), target, gain, channel); } -device_sound_interface &device_sound_interface::add_route(u32 output, device_sound_interface &target, double gain, u32 input, u32 mixoutput) +device_sound_interface &device_sound_interface::add_route(u32 output, device_sound_interface &target, double gain, u32 channel) { - return add_route(output, target.device(), DEVICE_SELF, gain, input, mixoutput); + return add_route(output, target.device(), DEVICE_SELF, gain, channel); } -device_sound_interface &device_sound_interface::add_route(u32 output, speaker_device &target, double gain, u32 input, u32 mixoutput) -{ - return add_route(output, target, DEVICE_SELF, gain, input, mixoutput); -} - -device_sound_interface &device_sound_interface::add_route(u32 output, device_t &base, const char *target, double gain, u32 input, u32 mixoutput) +device_sound_interface &device_sound_interface::add_route(u32 output, device_t &base, const char *target, double gain, u32 channel) { assert(!device().started()); - m_route_list.emplace_back(sound_route{ output, input, mixoutput, float(gain), base, target }); + m_route_list.emplace_back(sound_route{ output, channel, float(gain), base, target, nullptr }); return *this; } @@ -70,40 +68,41 @@ device_sound_interface &device_sound_interface::add_route(u32 output, device_t & // associated with this device //------------------------------------------------- -sound_stream *device_sound_interface::stream_alloc(int inputs, int outputs, int sample_rate) +sound_stream *device_sound_interface::stream_alloc(int inputs, int outputs, int sample_rate, sound_stream_flags flags) { - return device().machine().sound().stream_alloc(*this, inputs, outputs, sample_rate); + sound_stream *stream = device().machine().sound().stream_alloc(*this, inputs, outputs, sample_rate, stream_update_delegate(&device_sound_interface::sound_stream_update, this), flags); + m_sound_streams.push_back(stream); + return stream; } + //------------------------------------------------- // inputs - return the total number of inputs -// for the given device +// forthe given device //------------------------------------------------- int device_sound_interface::inputs() const { // scan the list counting streams we own and summing their inputs int inputs = 0; - for (auto &stream : m_device.machine().sound().streams()) - if (&stream->device() == &m_device) - inputs += stream->input_count(); + for(sound_stream *stream : m_sound_streams) + inputs += stream->input_count(); return inputs; } //------------------------------------------------- // outputs - return the total number of outputs -// for the given device +// forthe given device //------------------------------------------------- int device_sound_interface::outputs() const { // scan the list counting streams we own and summing their outputs int outputs = 0; - for (auto &stream : m_device.machine().sound().streams()) - if (&stream->device() == &m_device) - outputs += stream->output_count(); + for(auto *stream : m_sound_streams) + outputs += stream->output_count(); return outputs; } @@ -114,24 +113,19 @@ int device_sound_interface::outputs() const // on that stream //------------------------------------------------- -sound_stream *device_sound_interface::input_to_stream_input(int inputnum, int &stream_inputnum) const +std::pair<sound_stream *, int> device_sound_interface::input_to_stream_input(int inputnum) const { assert(inputnum >= 0); + int orig_inputnum = inputnum; - // scan the list looking for streams owned by this device - for (auto &stream : m_device.machine().sound().streams()) - if (&stream->device() == &m_device) - { - if (inputnum < stream->input_count()) - { - stream_inputnum = inputnum; - return stream.get(); - } - inputnum -= stream->input_count(); - } + // scan the list looking forstreams owned by this device + for(auto *stream : m_sound_streams) { + if(inputnum < stream->input_count()) + return std::make_pair(stream, inputnum); + inputnum -= stream->input_count(); + } - // not found - return nullptr; + fatalerror("Requested input %d on sound device %s which only has %d.", orig_inputnum, device().tag(), inputs()); } @@ -141,24 +135,19 @@ sound_stream *device_sound_interface::input_to_stream_input(int inputnum, int &s // on that stream //------------------------------------------------- -sound_stream *device_sound_interface::output_to_stream_output(int outputnum, int &stream_outputnum) const +std::pair<sound_stream *, int> device_sound_interface::output_to_stream_output(int outputnum) const { assert(outputnum >= 0); + int orig_outputnum = outputnum; - // scan the list looking for streams owned by this device - for (auto &stream : m_device.machine().sound().streams()) - if (&stream->device() == &device()) - { - if (outputnum < stream->output_count()) - { - stream_outputnum = outputnum; - return stream.get(); - } - outputnum -= stream->output_count(); - } + // scan the list looking forstreams owned by this device + for(auto *stream : m_sound_streams) { + if(outputnum < stream->output_count()) + return std::make_pair(stream, outputnum); + outputnum -= stream->output_count(); + } - // not found - return nullptr; + fatalerror("Requested output %d on sound device %s which only has %d.", orig_outputnum, device().tag(), outputs()); } @@ -169,9 +158,8 @@ sound_stream *device_sound_interface::output_to_stream_output(int outputnum, int float device_sound_interface::input_gain(int inputnum) const { - int stream_inputnum; - sound_stream *stream = input_to_stream_input(inputnum, stream_inputnum); - return (stream != nullptr) ? stream->input_gain(stream_inputnum) : 0.0f; + auto [stream, input] = input_to_stream_input(inputnum); + return stream->input_gain(input); } @@ -182,9 +170,32 @@ float device_sound_interface::input_gain(int inputnum) const float device_sound_interface::output_gain(int outputnum) const { - int stream_outputnum; - sound_stream *stream = output_to_stream_output(outputnum, stream_outputnum); - return (stream != nullptr) ? stream->output_gain(stream_outputnum) : 0.0f; + auto [stream, output] = output_to_stream_output(outputnum); + return stream->output_gain(output); +} + + +//------------------------------------------------- +// user_output_gain - return the user gain for the device +//------------------------------------------------- + +float device_sound_interface::user_output_gain() const +{ + if(!outputs()) + fatalerror("Requested user output gain on sound device %s which has no outputs.", device().tag()); + return m_sound_streams.front()->user_output_gain(); +} + + +//------------------------------------------------- +// user_output_gain - return the user gain on the given +// output index of the device +//------------------------------------------------- + +float device_sound_interface::user_output_gain(int outputnum) const +{ + auto [stream, output] = output_to_stream_output(outputnum); + return stream->user_output_gain(output); } @@ -195,10 +206,8 @@ float device_sound_interface::output_gain(int outputnum) const void device_sound_interface::set_input_gain(int inputnum, float gain) { - int stream_inputnum; - sound_stream *stream = input_to_stream_input(inputnum, stream_inputnum); - if (stream != nullptr) - stream->set_input_gain(stream_inputnum, gain); + auto [stream, input] = input_to_stream_input(inputnum); + stream->set_input_gain(input, gain); } @@ -210,42 +219,66 @@ void device_sound_interface::set_input_gain(int inputnum, float gain) void device_sound_interface::set_output_gain(int outputnum, float gain) { // handle ALL_OUTPUTS as a special case - if (outputnum == ALL_OUTPUTS) + if(outputnum == ALL_OUTPUTS) { - for (auto &stream : m_device.machine().sound().streams()) - if (&stream->device() == &device()) - for (int num = 0; num < stream->output_count(); num++) - stream->set_output_gain(num, gain); + if(!outputs()) + fatalerror("Requested setting output gain on sound device %s which has no outputs.", device().tag()); + for(auto *stream : m_sound_streams) + for(int num = 0; num < stream->output_count(); num++) + stream->set_output_gain(num, gain); } // look up the stream and stream output index else { - int stream_outputnum; - sound_stream *stream = output_to_stream_output(outputnum, stream_outputnum); - if (stream != nullptr) - stream->set_output_gain(stream_outputnum, gain); + auto [stream, output] = output_to_stream_output(outputnum); + stream->set_output_gain(output, gain); } } +//------------------------------------------------- +// user_set_output_gain - set the user gain on the device +//------------------------------------------------- + +void device_sound_interface::set_user_output_gain(float gain) +{ + if(!outputs()) + fatalerror("Requested setting user output gain on sound device %s which has no outputs.", device().tag()); + for(auto *stream : m_sound_streams) + stream->set_user_output_gain(gain); +} + + + +//------------------------------------------------- +// set_user_output_gain - set the user gain on the given +// output index of the device +//------------------------------------------------- + +void device_sound_interface::set_user_output_gain(int outputnum, float gain) +{ + auto [stream, output] = output_to_stream_output(outputnum); + stream->set_user_output_gain(output, gain); +} + //------------------------------------------------- -// inputnum_from_device - return the input number -// that is connected to the given device's output +// set_route_gain - set the gain on a route //------------------------------------------------- -int device_sound_interface::inputnum_from_device(device_t &source_device, int outputnum) const +void device_sound_interface::set_route_gain(int source_channel, device_sound_interface *target, int target_channel, float gain) { - int overall = 0; - for (auto &stream : m_device.machine().sound().streams()) - if (&stream->device() == &device()) - for (int inputnum = 0; inputnum < stream->input_count(); inputnum++, overall++) - if (stream->input_source_device(inputnum) == &source_device && stream->input_source_outputnum(inputnum) == outputnum) - return overall; - return -1; + auto [sstream, schan] = output_to_stream_output(source_channel); + auto [tstream, tchan] = target->input_to_stream_input(target_channel); + tstream->update(); + if(tstream->set_route_gain(sstream, schan, tchan, gain)) + return; + + fatalerror("Trying to change the gain on a non-existant route between %s channel %d and %s channel %d\n", device().tag(), source_channel, target->device().tag(), target_channel); } + //------------------------------------------------- // interface_validity_check - validation for a // device after the configuration has been @@ -255,16 +288,16 @@ int device_sound_interface::inputnum_from_device(device_t &source_device, int ou void device_sound_interface::interface_validity_check(validity_checker &valid) const { // loop over all the routes - for (sound_route const &route : routes()) + for(sound_route const &route : routes()) { // find a device with the requested tag - device_t const *const target = route.m_base.get().subdevice(route.m_target.c_str()); - if (!target) - osd_printf_error("Attempting to route sound to non-existent device '%s'\n", route.m_base.get().subtag(route.m_target.c_str()).c_str()); + device_t const *const target = route.m_base.get().subdevice(route.m_target); + if(!target) + osd_printf_error("Attempting to route sound to non-existent device '%s'\n", route.m_base.get().subtag(route.m_target)); - // if it's not a speaker or a sound device, error + // ifit's not a speaker or a sound device, error device_sound_interface const *sound; - if (target && (target->type() != SPEAKER) && !target->interface(sound)) + if(target && !target->interface(sound)) osd_printf_error("Attempting to route sound to a non-sound device '%s' (%s)\n", target->tag(), target->name()); } } @@ -275,200 +308,68 @@ void device_sound_interface::interface_validity_check(validity_checker &valid) c // devices are started //------------------------------------------------- -void device_sound_interface::interface_pre_start() +void device_sound_interface::sound_before_devices_init() { - // scan all the sound devices - sound_interface_iterator iter(m_device.machine().root_device()); - for (device_sound_interface const &sound : iter) - { - // scan each route on the device - for (sound_route const &route : sound.routes()) - { - // see if we are the target of this route; if we are, make sure the source device is started - device_t *const target_device = route.m_base.get().subdevice(route.m_target.c_str()); - if ((target_device == &m_device) && !sound.device().started()) - throw device_missing_dependencies(); - } - } - - // now iterate through devices again and assign any auto-allocated inputs - m_auto_allocated_inputs = 0; - for (device_sound_interface &sound : iter) - { - // scan each route on the device - for (sound_route &route : sound.routes()) - { - // see if we are the target of this route - device_t *const target_device = route.m_base.get().subdevice(route.m_target.c_str()); - if ((target_device == &m_device) && (route.m_input == AUTO_ALLOC_INPUT)) - { - route.m_input = m_auto_allocated_inputs; - m_auto_allocated_inputs += (route.m_output == ALL_OUTPUTS) ? sound.outputs() : 1; - } + for(sound_route &route : routes()) { + device_t *dev = route.m_base.get().subdevice(route.m_target); + dev->interface(route.m_interface); + if(route.m_output != ALL_OUTPUTS && m_sound_requested_outputs <= route.m_output) { + m_sound_requested_outputs_mask |= u64(1) << route.m_output; + m_sound_requested_outputs = route.m_output + 1; } + route.m_interface->sound_request_input(route.m_input); } } - -//------------------------------------------------- -// interface_post_start - verify that state was -// properly set up -//------------------------------------------------- - -void device_sound_interface::interface_post_start() +void device_sound_interface::sound_after_devices_init() { - // iterate over all the sound devices - for (device_sound_interface &sound : sound_interface_iterator(m_device.machine().root_device())) - { - // scan each route on the device - for (sound_route const &route : sound.routes()) - { - // if we are the target of this route, hook it up - device_t *const target_device = route.m_base.get().subdevice(route.m_target.c_str()); - if (target_device == &m_device) - { - // iterate over all outputs, matching any that apply - int inputnum = route.m_input; - int const numoutputs = sound.outputs(); - for (int outputnum = 0; outputnum < numoutputs; outputnum++) - if ((route.m_output == outputnum) || (route.m_output == ALL_OUTPUTS)) - { - // find the output stream to connect from - int streamoutputnum; - sound_stream *const outputstream = sound.output_to_stream_output(outputnum, streamoutputnum); - if (!outputstream) - fatalerror("Sound device '%s' specifies route for nonexistent output #%d\n", sound.device().tag(), outputnum); - - // find the input stream to connect to - int streaminputnum; - sound_stream *const inputstream = input_to_stream_input(inputnum++, streaminputnum); - if (!inputstream) - fatalerror("Sound device '%s' targeted output #%d to nonexistent device '%s' input %d\n", sound.device().tag(), outputnum, m_device.tag(), inputnum - 1); - - // set the input - inputstream->set_input(streaminputnum, outputstream, streamoutputnum, route.m_gain); - } - } + for(sound_route &route : routes()) { + auto [si, ii] = route.m_interface->input_to_stream_input(route.m_input); + if(!si) + fatalerror("Requesting sound route to device %s input %d which doesn't exist\n", route.m_interface->device().tag(), route.m_input); + if(route.m_output != ALL_OUTPUTS) { + auto [so, io] = output_to_stream_output(route.m_output); + if(!so) + fatalerror("Requesting sound route from device %s output %d which doesn't exist\n", device().tag(), route.m_output); + si->add_bw_route(so, io, ii, route.m_gain); + so->add_fw_route(si, ii, io); + + } else { + for(sound_stream *so : m_sound_streams) + for(int io = 0; io != so->output_count(); io ++) { + si->add_bw_route(so, io, ii, route.m_gain); + so->add_fw_route(si, ii, io); + } } } } - -//------------------------------------------------- -// interface_pre_reset - called prior to -// resetting the device -//------------------------------------------------- - -void device_sound_interface::interface_pre_reset() +void device_sound_interface::sound_request_input(u32 input) { - // update all streams on this device prior to reset - for (auto &stream : m_device.machine().sound().streams()) - if (&stream->device() == &device()) - stream->update(); + m_sound_requested_inputs_mask |= u64(1) << input; + if(m_sound_requested_inputs <= input) + m_sound_requested_inputs = input + 1; } - - -//************************************************************************** -// SIMPLE DERIVED MIXER INTERFACE -//************************************************************************** - -//------------------------------------------------- -// device_mixer_interface - constructor -//------------------------------------------------- - -device_mixer_interface::device_mixer_interface(const machine_config &mconfig, device_t &device, int outputs) - : device_sound_interface(mconfig, device), - m_outputs(outputs), - m_mixer_stream(nullptr) +device_mixer_interface::device_mixer_interface(const machine_config &mconfig, device_t &device) : + device_sound_interface(mconfig, device) { } - -//------------------------------------------------- -// ~device_mixer_interface - destructor -//------------------------------------------------- - device_mixer_interface::~device_mixer_interface() { } - -//------------------------------------------------- -// interface_pre_start - perform startup prior -// to the device startup -//------------------------------------------------- - void device_mixer_interface::interface_pre_start() { - // call our parent - device_sound_interface::interface_pre_start(); - - // no inputs? that's weird - if (m_auto_allocated_inputs == 0) - { - device().logerror("Warning: mixer \"%s\" has no inputs\n", device().tag()); - return; - } - - // generate the output map - m_outputmap.resize(m_auto_allocated_inputs); - - // iterate through all routes that point to us and note their mixer output - for (device_sound_interface const &sound : sound_interface_iterator(m_device.machine().root_device())) - { - for (sound_route const &route : sound.routes()) - { - // see if we are the target of this route - device_t *const target_device = route.m_base.get().subdevice(route.m_target.c_str()); - if ((target_device == &device()) && (route.m_input < m_auto_allocated_inputs)) - { - int const count = (route.m_output == ALL_OUTPUTS) ? sound.outputs() : 1; - for (int output = 0; output < count; output++) - m_outputmap[route.m_input + output] = route.m_mixoutput; - } - } - } - - // allocate the mixer stream - m_mixer_stream = stream_alloc(m_auto_allocated_inputs, m_outputs, device().machine().sample_rate()); -} - - -//------------------------------------------------- -// interface_post_load - after we load a save -// state be sure to update the mixer stream's -// output sample rate -//------------------------------------------------- - -void device_mixer_interface::interface_post_load() -{ - // Beware that there's not going to be a mixer stream if there was - // no inputs - if (m_mixer_stream) - m_mixer_stream->set_sample_rate(device().machine().sample_rate()); - - // call our parent - device_sound_interface::interface_post_load(); + u32 ni = get_sound_requested_inputs(); + u32 no = get_sound_requested_outputs(); + u32 nc = ni > no ? ni : no; + for(u32 i = 0; i != nc; i++) + stream_alloc(1, 1, SAMPLE_RATE_ADAPTIVE); } - -//------------------------------------------------- -// mixer_update - mix all inputs to one output -//------------------------------------------------- - -void device_mixer_interface::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) +void device_mixer_interface::sound_stream_update(sound_stream &stream) { - // clear output buffers - for (int output = 0; output < m_outputs; output++) - memset(outputs[output], 0, samples * sizeof(outputs[0][0])); - - // loop over samples - const u8 *outmap = &m_outputmap[0]; - for (int pos = 0; pos < samples; pos++) - { - // for each input, add it to the appropriate output - for (int inp = 0; inp < m_auto_allocated_inputs; inp++) - outputs[outmap[inp]][pos] += inputs[inp][pos]; - } + stream.copy(0, 0); } diff --git a/src/emu/disound.h b/src/emu/disound.h index 37a7df462be..eefe1197784 100644 --- a/src/emu/disound.h +++ b/src/emu/disound.h @@ -26,7 +26,6 @@ //************************************************************************** constexpr int ALL_OUTPUTS = 65535; // special value indicating all outputs for the current chip -constexpr int AUTO_ALLOC_INPUT = 65535; @@ -34,80 +33,100 @@ constexpr int AUTO_ALLOC_INPUT = 65535; // TYPE DEFINITIONS //************************************************************************** +enum sound_stream_flags : u32; + // ======================> device_sound_interface class device_sound_interface : public device_interface { + friend class sound_manager; + public: class sound_route { public: u32 m_output; // output index, or ALL_OUTPUTS u32 m_input; // target input index - u32 m_mixoutput; // target mixer output float m_gain; // gain std::reference_wrapper<device_t> m_base; // target search base std::string m_target; // target tag + device_sound_interface *m_interface; // target device interface }; // construction/destruction device_sound_interface(const machine_config &mconfig, device_t &device); virtual ~device_sound_interface(); - virtual bool issound() { return true; } /// HACK: allow devices to hide from the ui - // configuration access std::vector<sound_route> const &routes() const { return m_route_list; } // configuration helpers template <typename T, bool R> - device_sound_interface &add_route(u32 output, const device_finder<T, R> &target, double gain, u32 input = AUTO_ALLOC_INPUT, u32 mixoutput = 0) + device_sound_interface &add_route(u32 output, const device_finder<T, R> &target, double gain, u32 channel = 0) { const std::pair<device_t &, const char *> ft(target.finder_target()); - return add_route(output, ft.first, ft.second, gain, input, mixoutput); + return add_route(output, ft.first, ft.second, gain, channel); } - device_sound_interface &add_route(u32 output, const char *target, double gain, u32 input = AUTO_ALLOC_INPUT, u32 mixoutput = 0); - device_sound_interface &add_route(u32 output, device_sound_interface &target, double gain, u32 input = AUTO_ALLOC_INPUT, u32 mixoutput = 0); - device_sound_interface &add_route(u32 output, speaker_device &target, double gain, u32 input = AUTO_ALLOC_INPUT, u32 mixoutput = 0); + device_sound_interface &add_route(u32 output, const char *target, double gain, u32 channel = 0); + device_sound_interface &add_route(u32 output, device_sound_interface &target, double gain, u32 channel = 0); device_sound_interface &reset_routes() { m_route_list.clear(); return *this; } // sound stream update overrides - virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) = 0; + virtual void sound_stream_update(sound_stream &stream) = 0; // stream creation - sound_stream *stream_alloc(int inputs, int outputs, int sample_rate); + sound_stream *stream_alloc(int inputs, int outputs, int sample_rate, sound_stream_flags flags = sound_stream_flags(0)); // helpers int inputs() const; int outputs() const; - sound_stream *input_to_stream_input(int inputnum, int &stream_inputnum) const; - sound_stream *output_to_stream_output(int outputnum, int &stream_outputnum) const; + std::pair<sound_stream *, int> input_to_stream_input(int inputnum) const; + std::pair<sound_stream *, int> output_to_stream_output(int outputnum) const; float input_gain(int inputnum) const; float output_gain(int outputnum) const; + float user_output_gain() const; + float user_output_gain(int outputnum) const; void set_input_gain(int inputnum, float gain); void set_output_gain(int outputnum, float gain); - int inputnum_from_device(device_t &device, int outputnum = 0) const; + void set_user_output_gain(float gain); + void set_user_output_gain(int outputnum, float gain); + void set_route_gain(int source_channel, device_sound_interface *target, int target_channel, float gain); + void set_sound_hook(bool enable) { m_sound_hook = enable; } + bool get_sound_hook() const { return m_sound_hook; } + protected: // configuration access std::vector<sound_route> &routes() { return m_route_list; } - device_sound_interface &add_route(u32 output, device_t &base, const char *tag, double gain, u32 input, u32 mixoutput); + device_sound_interface &add_route(u32 output, device_t &base, const char *tag, double gain, u32 channel); // optional operation overrides virtual void interface_validity_check(validity_checker &valid) const override; - virtual void interface_pre_start() override; - virtual void interface_post_start() override; - virtual void interface_pre_reset() override; + + u32 get_sound_requested_inputs() const { return m_sound_requested_inputs; } + u32 get_sound_requested_outputs() const { return m_sound_requested_outputs; } + u64 get_sound_requested_inputs_mask() const { return m_sound_requested_inputs_mask; } + u64 get_sound_requested_outputs_mask() const { return m_sound_requested_outputs_mask; } + +private: + void sound_request_input(u32 input); // internal state std::vector<sound_route> m_route_list; // list of sound routes - int m_outputs; // number of outputs from this instance - int m_auto_allocated_inputs; // number of auto-allocated inputs targeting us + std::vector<sound_stream *> m_sound_streams; + u64 m_sound_requested_inputs_mask; + u64 m_sound_requested_outputs_mask; + u32 m_sound_requested_inputs; + u32 m_sound_requested_outputs; + bool m_sound_hook; + + void sound_before_devices_init(); + void sound_after_devices_init(); }; // iterator -typedef device_interface_iterator<device_sound_interface> sound_interface_iterator; +typedef device_interface_enumerator<device_sound_interface> sound_interface_enumerator; @@ -117,25 +136,19 @@ class device_mixer_interface : public device_sound_interface { public: // construction/destruction - device_mixer_interface(const machine_config &mconfig, device_t &device, int outputs = 1); + device_mixer_interface(const machine_config &mconfig, device_t &device); virtual ~device_mixer_interface(); protected: // optional operation overrides virtual void interface_pre_start() override; - virtual void interface_post_load() override; // sound interface overrides - virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) override; - - // internal state - u8 m_outputs; // number of outputs - std::vector<u8> m_outputmap; // map of inputs to outputs - sound_stream * m_mixer_stream; // mixing stream + virtual void sound_stream_update(sound_stream &stream) override; }; // iterator -typedef device_interface_iterator<device_mixer_interface> mixer_interface_iterator; +typedef device_interface_enumerator<device_mixer_interface> mixer_interface_enumerator; #endif // MAME_EMU_DISOUND_H diff --git a/src/emu/distate.cpp b/src/emu/distate.cpp index 2dcf4301ac4..76ede23dfe2 100644 --- a/src/emu/distate.cpp +++ b/src/emu/distate.cpp @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles /*************************************************************************** - distate.c + distate.cpp Device state interfaces. @@ -56,8 +56,7 @@ device_state_entry::device_state_entry(int index, const char *symbol, u8 size, u m_datasize(size), m_flags(flags), m_symbol(symbol), - m_default_format(true), - m_sizemask(sizemask) + m_default_format(true) { assert(size == 1 || size == 2 || size == 4 || size == 8 || (flags & DSF_FLOATING_POINT) != 0); @@ -66,8 +65,6 @@ device_state_entry::device_state_entry(int index, const char *symbol, u8 size, u // override well-known symbols if (index == STATE_GENPCBASE) m_symbol.assign("CURPC"); - else if (index == STATE_GENSP) - m_symbol.assign("CURSP"); else if (index == STATE_GENFLAGS) m_symbol.assign("CURFLAGS"); } @@ -79,8 +76,7 @@ device_state_entry::device_state_entry(int index, device_state_interface *dev) m_datasize(0), m_flags(DSF_DIVIDER | DSF_READONLY), m_symbol(), - m_default_format(true), - m_sizemask(0) + m_default_format(true) { } @@ -105,7 +101,7 @@ device_state_entry &device_state_entry::formatstr(const char *_format) // set the DSF_CUSTOM_STRING flag by formatting with a nullptr string m_flags &= ~DSF_CUSTOM_STRING; - format(nullptr); + format(nullptr, 0); return *this; } @@ -130,15 +126,43 @@ void device_state_entry::format_from_mask() // make up a format based on the mask if (m_datamask == 0) - throw emu_fatalerror("%s state entry requires a nonzero mask\n", m_symbol.c_str()); - int width = 0; - for (u64 tempmask = m_datamask; tempmask != 0; tempmask >>= 4) - width++; + throw emu_fatalerror("%s state entry requires a nonzero mask\n", m_symbol); + int width = (63 - count_leading_zeros_64(m_datamask)) / 4 + 1; m_format = string_format("%%0%dX", width); } //------------------------------------------------- +// value - return the current value as a u64 +//------------------------------------------------- + +u64 device_state_entry::value() const +{ + // call the exporter before we do anything + if ((m_flags & DSF_EXPORT) != 0) + m_device_state->state_export(*this); + + // pick up the value + return entry_value() & m_datamask; +} + + +//------------------------------------------------- +// dvalue - return the current value as a double +//------------------------------------------------- + +double device_state_entry::dvalue() const +{ + // call the exporter before we do anything + if ((m_flags & DSF_EXPORT) != 0) + m_device_state->state_export(*this); + + // pick up the value + return entry_dvalue(); +} + + +//------------------------------------------------- // entry_baseptr - return a pointer to where the // data lives (if applicable) //------------------------------------------------- @@ -175,12 +199,10 @@ double device_state_entry::entry_dvalue() const // pieces of indexed state as a string //------------------------------------------------- -std::string device_state_entry::format(const char *string, bool maxout) const +std::string device_state_entry::format(const char *string, u64 result, bool maxout) const { - std::string dest; - u64 result = value(); - // parse the format + std::string dest; bool leadzero = false; bool percent = false; bool explicitsign = false; @@ -303,18 +325,18 @@ std::string device_state_entry::format(const char *string, bool maxout) const width--; } // fall through to unsigned case - + [[fallthrough]]; // u outputs as unsigned decimal case 'u': if (width == 0) throw emu_fatalerror("Width required for %%u formats\n"); hitnonzero = false; - while (leadzero && width > ARRAY_LENGTH(k_decimal_divisor)) + while (leadzero && width > std::size(k_decimal_divisor)) { dest.append(" "); width--; } - for (int digitnum = ARRAY_LENGTH(k_decimal_divisor) - 1; digitnum >= 0; digitnum--) + for (int digitnum = std::size(k_decimal_divisor) - 1; digitnum >= 0; digitnum--) { int digit = (result >= k_decimal_divisor[digitnum]) ? (result / k_decimal_divisor[digitnum]) % 10 : 0; if (digit != 0) @@ -360,6 +382,37 @@ std::string device_state_entry::format(const char *string, bool maxout) const //------------------------------------------------- +// to_string - return the value of the given +// piece of indexed state as a string +//------------------------------------------------- + +std::string device_state_entry::to_string() const +{ + // get the custom string if needed + std::string custom; + if ((m_flags & DSF_CUSTOM_STRING) != 0) + m_device_state->state_string_export(*this, custom); + else if ((m_flags & DSF_FLOATING_POINT) != 0) + custom = string_format("%-12G", entry_dvalue()); + + // ask the entry to format itself + return format(custom.c_str(), value()); +} + + +//------------------------------------------------- +// max_length - return the maximum length of the +// given state as a string +//------------------------------------------------- + +int device_state_entry::max_length() const +{ + // ask the entry to format itself maximally + return format("", 0, true).length(); +} + + +//------------------------------------------------- // set_value - set the value from a u64 //------------------------------------------------- @@ -376,6 +429,10 @@ void device_state_entry::set_value(u64 value) const // store the value entry_set_value(value); + + // call the importer to finish up + if ((m_flags & DSF_IMPORT) != 0) + m_device_state->state_import(*this); } @@ -389,6 +446,10 @@ void device_state_entry::set_dvalue(double value) const // store the value entry_set_dvalue(value); + + // call the importer to finish up + if ((m_flags & DSF_IMPORT) != 0) + m_device_state->state_import(*this); } @@ -411,16 +472,6 @@ void device_state_entry::entry_set_dvalue(double value) const } -//------------------------------------------------- -// set_value - set the value from a string -//------------------------------------------------- - -void device_state_entry::set_value(const char *string) const -{ - // not implemented -} - - //************************************************************************** // DEVICE STATE INTERFACE @@ -434,9 +485,6 @@ device_state_interface::device_state_interface(const machine_config &mconfig, de : device_interface(device, "state") { memset(m_fast_state, 0, sizeof(m_fast_state)); - - // configure the fast accessor - device.interfaces().m_state = this; } @@ -450,110 +498,6 @@ device_state_interface::~device_state_interface() //------------------------------------------------- -// state_int - return the value of the given piece -// of indexed state as a u64 -//------------------------------------------------- - -u64 device_state_interface::state_int(int index) -{ - // nullptr or out-of-range entry returns 0 - const device_state_entry *entry = state_find_entry(index); - if (entry == nullptr) - return 0; - - // call the exporter before we do anything - if (entry->needs_export()) - state_export(*entry); - - // pick up the value - return entry->value(); -} - - -//------------------------------------------------- -// state_string - return the value of the given -// pieces of indexed state as a string -//------------------------------------------------- - -std::string device_state_interface::state_string(int index) const -{ - // nullptr or out-of-range entry returns bogus string - const device_state_entry *entry = state_find_entry(index); - if (entry == nullptr) - return std::string("???"); - - // get the custom string if needed - std::string custom; - if (entry->needs_custom_string()) - state_string_export(*entry, custom); - else if (entry->is_float()) - custom = string_format("%-12G", entry->dvalue()); - - // ask the entry to format itself - return entry->format(custom.c_str()); -} - - -//------------------------------------------------- -// state_string_max_length - return the maximum -// length of the given state string -//------------------------------------------------- - -int device_state_interface::state_string_max_length(int index) -{ - // nullptr or out-of-range entry returns bogus string - const device_state_entry *entry = state_find_entry(index); - if (entry == nullptr) - return 3; - - // ask the entry to format itself maximally - return entry->format("", true).length(); -} - - -//------------------------------------------------- -// set_state_int - set the value of the given -// piece of indexed state from a u64 -//------------------------------------------------- - -void device_state_interface::set_state_int(int index, u64 value) -{ - // nullptr or out-of-range entry is a no-op - const device_state_entry *entry = state_find_entry(index); - if (entry == nullptr) - return; - - // set the value - entry->set_value(value); - - // call the importer to finish up - if (entry->needs_import()) - state_import(*entry); -} - - -//------------------------------------------------- -// set_state - set the value of the given piece -// of indexed state from a string -//------------------------------------------------- - -void device_state_interface::set_state_string(int index, const char *string) -{ - // nullptr or out-of-range entry is a no-op - const device_state_entry *entry = state_find_entry(index); - if (entry == nullptr) - return; - - // set the value - entry->set_value(string); - - // call the importer to finish up - if (entry->needs_import()) - state_import(*entry); -} - - -//------------------------------------------------- // state_add - add a new piece of indexed state //------------------------------------------------- @@ -624,26 +568,5 @@ void device_state_interface::interface_post_start() { // make sure we got something during startup if (m_state_list.size() == 0) - throw emu_fatalerror("No state registered for device '%s' that supports it!", m_device.tag()); -} - - -//------------------------------------------------- -// state_find_entry - return a pointer to the -// state entry for the given index -//------------------------------------------------- - -const device_state_entry *device_state_interface::state_find_entry(int index) const -{ - // use fast lookup if possible - if (index >= FAST_STATE_MIN && index <= FAST_STATE_MAX) - return m_fast_state[index - FAST_STATE_MIN]; - - // otherwise, scan the first - for (auto &entry : m_state_list) - if (entry->m_index == index) - return entry.get(); - - // handle failure by returning nullptr - return nullptr; + throw emu_fatalerror("No state registered for device '%s' that supports it!", device().tag()); } diff --git a/src/emu/distate.h b/src/emu/distate.h index 5951a0143d4..b4727c259f4 100644 --- a/src/emu/distate.h +++ b/src/emu/distate.h @@ -27,8 +27,7 @@ enum { STATE_GENPC = -1, // generic program counter (live) STATE_GENPCBASE = -2, // generic program counter (base of current instruction) - STATE_GENSP = -3, // generic stack pointer - STATE_GENFLAGS = -4 // generic flags + STATE_GENFLAGS = -3 // generic flags }; @@ -43,7 +42,6 @@ enum // class describing a single item of exposed device state class device_state_entry { - friend class device_state_interface; public: // construction/destruction device_state_entry(int index, const char *symbol, u8 size, u64 sizemask, u8 flags, device_state_interface *dev); @@ -73,6 +71,18 @@ public: device_state_interface *parent_state() const {return m_device_state;} const std::string &format_string() const { return m_format; } + // return the current value + u64 value() const; + double dvalue() const; + + // return the current value as a string + std::string to_string() const; + int max_length() const; + + // set the current value + void set_value(u64 value) const; + void set_dvalue(double value) const; + protected: // device state flags static constexpr u8 DSF_NOSHOW = 0x01; // don't display this entry in the registers view @@ -84,22 +94,6 @@ protected: static constexpr u8 DSF_READONLY = 0x40; // set if this entry does not permit writes static constexpr u8 DSF_FLOATING_POINT = 0x80; // set if this entry represents a floating-point value - // helpers - bool needs_custom_string() const { return ((m_flags & DSF_CUSTOM_STRING) != 0); } - void format_from_mask(); - - // return the current value -- only for our friends who handle export - bool needs_export() const { return ((m_flags & DSF_EXPORT) != 0); } - u64 value() const { return entry_value() & m_datamask; } - double dvalue() const { return entry_dvalue(); } - std::string format(const char *string, bool maxout = false) const; - - // set the current value -- only for our friends who handle import - bool needs_import() const { return ((m_flags & DSF_IMPORT) != 0); } - void set_value(u64 value) const; - void set_dvalue(double value) const; - void set_value(const char *string) const; - // overrides virtual void *entry_baseptr() const; virtual u64 entry_value() const; @@ -107,8 +101,13 @@ protected: virtual double entry_dvalue() const; virtual void entry_set_dvalue(double value) const; +private: + // helpers + void format_from_mask(); + std::string format(const char *string, u64 result, bool maxout = false) const; + // statics - static const u64 k_decimal_divisor[20]; // divisors for outputting decimal values + static const u64 k_decimal_divisor[20]; // divisors for outputting decimal values // public state description device_state_interface *m_device_state; // link to parent device state @@ -119,7 +118,6 @@ protected: std::string m_symbol; // symbol for display; all lower-case version for expressions std::string m_format; // supported formats bool m_default_format; // true if we are still using default format - u64 m_sizemask; // mask derived from the data size }; @@ -195,18 +193,47 @@ private: }; -// ======================> device_state_register +// ======================> device_latched_functional_state_register // class template representing a state register of a specific width template<class ItemType> -class device_pseudo_state_register : public device_state_entry +class device_latched_functional_state_register : public device_state_entry +{ +public: + typedef typename std::function<void (ItemType)> setter_func; + + // construction/destruction + device_latched_functional_state_register(int index, const char *symbol, ItemType &data, setter_func &&setter, device_state_interface *dev) + : device_state_entry(index, symbol, sizeof(ItemType), std::numeric_limits<ItemType>::max(), 0, dev), + m_data(data), + m_setter(std::move(setter)) + { + } + +protected: + // device_state_entry overrides + virtual void *entry_baseptr() const override { return &m_data; } + virtual u64 entry_value() const override { return m_data; } + virtual void entry_set_value(u64 value) const override { m_setter(value); } + +private: + ItemType & m_data; // reference to where the data lives + setter_func m_setter; // function to store the data +}; + + +// ======================> device_functional_state_register + +// class template representing a state register of a specific width +template<class ItemType> +class device_functional_state_register : public device_state_entry { public: typedef typename std::function<ItemType ()> getter_func; typedef typename std::function<void (ItemType)> setter_func; // construction/destruction - device_pseudo_state_register(int index, const char *symbol, getter_func &&getter, setter_func &&setter, device_state_interface *dev) + device_functional_state_register(int index, const char *symbol, getter_func &&getter, setter_func &&setter, device_state_interface *dev) : device_state_entry(index, symbol, sizeof(ItemType), std::numeric_limits<ItemType>::max(), 0, dev), m_getter(std::move(getter)), m_setter(std::move(setter)) @@ -223,41 +250,65 @@ private: setter_func m_setter; // function to store the data }; +template<> +class device_functional_state_register<double> : public device_state_entry +{ +public: + typedef typename std::function<double ()> getter_func; + typedef typename std::function<void (double)> setter_func; + + // construction/destruction + device_functional_state_register(int index, const char *symbol, getter_func &&getter, setter_func &&setter, device_state_interface *dev) + : device_state_entry(index, symbol, sizeof(double), ~u64(0), DSF_FLOATING_POINT, dev), + m_getter(std::move(getter)), + m_setter(std::move(setter)) + { + } + +protected: + // device_state_entry overrides + virtual u64 entry_value() const override { return u64(m_getter()); } + virtual void entry_set_value(u64 value) const override { m_setter(double(value)); } + virtual double entry_dvalue() const override { return m_getter(); } + virtual void entry_set_dvalue(double value) const override { m_setter(value); } + +private: + getter_func m_getter; // function to retrieve the data + setter_func m_setter; // function to store the data +}; + // ======================> device_state_interface // class representing interface-specific live state class device_state_interface : public device_interface { + friend class device_state_entry; + public: + using entrylist_type = std::vector<std::unique_ptr<device_state_entry> >; + // construction/destruction device_state_interface(const machine_config &mconfig, device_t &device); virtual ~device_state_interface(); // configuration access - const std::vector<std::unique_ptr<device_state_entry>> &state_entries() const { return m_state_list; } + const entrylist_type &state_entries() const { return m_state_list; } // state getters - u64 state_int(int index); - std::string state_string(int index) const; - int state_string_max_length(int index); - offs_t pc() { return state_int(STATE_GENPC); } - offs_t pcbase() { return state_int(STATE_GENPCBASE); } - offs_t sp() { return state_int(STATE_GENSP); } - u64 flags() { return state_int(STATE_GENFLAGS); } + u64 state_int(int index) const { const device_state_entry *entry = state_find_entry(index); return (entry == nullptr) ? 0 : entry->value(); } + std::string state_string(int index) const { const device_state_entry *entry = state_find_entry(index); return (entry == nullptr) ? std::string("???") : entry->to_string(); } + offs_t pc() const { return state_int(STATE_GENPC); } + offs_t pcbase() const { return state_int(STATE_GENPCBASE); } + u64 flags() const { return state_int(STATE_GENFLAGS); } // state setters - void set_state_int(int index, u64 value); - void set_state_string(int index, const char *string); + void set_state_int(int index, u64 value) { const device_state_entry *entry = state_find_entry(index); if (entry != nullptr) entry->set_value(value); } void set_pc(offs_t pc) { set_state_int(STATE_GENPC, pc); } // find the entry for a given index const device_state_entry *state_find_entry(int index) const; - // deliberately ambiguous functions; if you have the state interface - // just use it directly - device_state_interface &state() { return *this; } - public: // protected eventually // add a new state register item @@ -267,19 +318,29 @@ public: // protected eventually return state_add(std::make_unique<device_state_register<ItemType>>(index, symbol, data, this)); } - // add a new state pseudo-register item (template argument must be explicit) + // add a new state register item using functional setter + template<class ItemType> device_state_entry &state_add(int index, const char *symbol, ItemType &data, + typename device_latched_functional_state_register<ItemType>::setter_func &&setter) + { + assert(symbol != nullptr); + return state_add(std::make_unique<device_latched_functional_state_register<ItemType>>(index, symbol, data, std::move(setter), this)); + } + + // add a new state register item using functional getter and setter (template argument must be explicit) template<class ItemType> device_state_entry &state_add(int index, const char *symbol, - typename device_pseudo_state_register<ItemType>::getter_func &&getter, - typename device_pseudo_state_register<ItemType>::setter_func &&setter) + typename device_functional_state_register<ItemType>::getter_func &&getter, + typename device_functional_state_register<ItemType>::setter_func &&setter) { assert(symbol != nullptr); - return state_add(std::make_unique<device_pseudo_state_register<ItemType>>(index, symbol, std::move(getter), std::move(setter), this)); + return state_add(std::make_unique<device_functional_state_register<ItemType>>(index, symbol, std::move(getter), std::move(setter), this)); } + + // add a new read-only state register item using functional getter (template argument must be explicit) template<class ItemType> device_state_entry &state_add(int index, const char *symbol, - typename device_pseudo_state_register<ItemType>::getter_func &&getter) + typename device_functional_state_register<ItemType>::getter_func &&getter) { assert(symbol != nullptr); - return state_add(std::make_unique<device_pseudo_state_register<ItemType>>(index, symbol, std::move(getter), [](ItemType){}, this)).readonly(); + return state_add(std::make_unique<device_functional_state_register<ItemType>>(index, symbol, std::move(getter), [](ItemType){}, this)).readonly(); } device_state_entry &state_add(std::unique_ptr<device_state_entry> &&entry); @@ -298,16 +359,41 @@ protected: virtual void interface_post_start() override; // constants - static constexpr int FAST_STATE_MIN = -4; // range for fast state - static constexpr int FAST_STATE_MAX = 256; // lookups + static constexpr int FAST_STATE_MIN = -4; // range for fast state + static constexpr int FAST_STATE_MAX = 256; // lookups // state - std::vector<std::unique_ptr<device_state_entry>> m_state_list; // head of state list - device_state_entry * m_fast_state[FAST_STATE_MAX + 1 - FAST_STATE_MIN]; - // fast access to common entries + entrylist_type m_state_list; // head of state list + device_state_entry *m_fast_state[FAST_STATE_MAX + 1 - FAST_STATE_MIN]; // fast access to common entries }; // iterator -typedef device_interface_iterator<device_state_interface> state_interface_iterator; +typedef device_interface_enumerator<device_state_interface> state_interface_enumerator; + + + +//************************************************************************** +// INLINE FUNCTIONS +//************************************************************************** + +//------------------------------------------------- +// state_find_entry - return a pointer to the +// state entry for the given index +//------------------------------------------------- + +inline const device_state_entry *device_state_interface::state_find_entry(int index) const +{ + // use fast lookup if possible + if (index >= FAST_STATE_MIN && index <= FAST_STATE_MAX) + return m_fast_state[index - FAST_STATE_MIN]; + + // otherwise, scan the first + for (auto &entry : m_state_list) + if (entry->index() == index) + return entry.get(); + + // handle failure by returning nullptr + return nullptr; +} #endif /* MAME_EMU_DISTATE_H */ diff --git a/src/emu/divideo.cpp b/src/emu/divideo.cpp index 61287e0e35d..9ce8d53ac10 100644 --- a/src/emu/divideo.cpp +++ b/src/emu/divideo.cpp @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles /*************************************************************************** - divideo.c + divideo.cpp Device video interfaces. @@ -88,7 +88,7 @@ void device_video_interface::interface_validity_check(validity_checker &valid) c else { // otherwise, look for a single match - screen_device_iterator iter(device().mconfig().root_device()); + screen_device_enumerator iter(device().mconfig().root_device()); screen = iter.first(); if (iter.count() > 1) osd_printf_error("No screen specified for device '%s', but multiple screens found\n", device().tag()); @@ -121,7 +121,7 @@ void device_video_interface::interface_pre_start() else { // otherwise, look for a single match - screen_device_iterator iter(device().machine().root_device()); + screen_device_enumerator iter(device().machine().root_device()); m_screen = iter.first(); if (iter.count() > 1) throw emu_fatalerror("No screen specified for device '%s', but multiple screens found", device().tag()); diff --git a/src/emu/divideo.h b/src/emu/divideo.h index 626d5bc66b4..096f4fd9958 100644 --- a/src/emu/divideo.h +++ b/src/emu/divideo.h @@ -68,7 +68,7 @@ private: }; // iterator -typedef device_interface_iterator<device_video_interface> video_interface_iterator; +typedef device_interface_enumerator<device_video_interface> video_interface_enumerator; #endif /* MAME_EMU_DIVIDEO_H */ diff --git a/src/emu/divtlb.cpp b/src/emu/divtlb.cpp index fea9d22c107..ec0469cac02 100644 --- a/src/emu/divtlb.cpp +++ b/src/emu/divtlb.cpp @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles /*************************************************************************** - divtlb.c + divtlb.cpp Device generic virtual TLB interface. @@ -68,7 +68,7 @@ void device_vtlb_interface::interface_validity_check(validity_checker &valid) co const address_space_config *spaceconfig = intf->space_config(m_space); if (spaceconfig == nullptr) osd_printf_error("No memory address space configuration found for space %d\n", m_space); - else if ((1 << spaceconfig->page_shift()) <= VTLB_FLAGS_MASK || spaceconfig->logaddr_width() <= spaceconfig->page_shift()) + else if ((1 << spaceconfig->page_shift()) <= FLAGS_MASK || spaceconfig->logaddr_width() <= spaceconfig->page_shift()) osd_printf_error("Invalid page shift %d for VTLB\n", spaceconfig->page_shift()); } } @@ -142,11 +142,10 @@ void device_vtlb_interface::interface_pre_reset() // response to an unmapped access //------------------------------------------------- -bool device_vtlb_interface::vtlb_fill(offs_t address, int intention) +bool device_vtlb_interface::vtlb_fill(offs_t address, offs_t taddress, int intention) { offs_t tableindex = address >> m_pageshift; vtlb_entry entry = m_table[tableindex]; - offs_t taddress; #if PRINTF_TLB osd_printf_debug("vtlb_fill: %08X(%X) ... ", address, intention); @@ -164,21 +163,12 @@ bool device_vtlb_interface::vtlb_fill(offs_t address, int intention) return false; } - // ask the CPU core to translate for us - taddress = address; - if (!device().memory().translate(m_space, intention, taddress)) - { -#if PRINTF_TLB - osd_printf_debug("failed: no translation\n"); -#endif - return false; - } - // if this is the first successful translation for this address, allocate a new entry - if ((entry & VTLB_FLAGS_MASK) == 0) + if ((entry & FLAGS_MASK) == 0) { - int liveindex = m_dynindex++ % m_dynamic; + int liveindex = m_dynindex; + m_dynindex = (m_dynindex + 1) % m_dynamic; // if an entry already exists at this index, free it if (m_live[liveindex] != 0) @@ -195,7 +185,7 @@ bool device_vtlb_interface::vtlb_fill(offs_t address, int intention) // form a new blank entry entry = (taddress >> m_pageshift) << m_pageshift; - entry |= VTLB_FLAG_VALID; + entry |= FLAG_VALID; #if PRINTF_TLB osd_printf_debug("success (%08X), new entry\n", taddress); @@ -206,7 +196,7 @@ bool device_vtlb_interface::vtlb_fill(offs_t address, int intention) else { assert((entry >> m_pageshift) == (taddress >> m_pageshift)); - assert(entry & VTLB_FLAG_VALID); + assert(entry & FLAG_VALID); #if PRINTF_TLB osd_printf_debug("success (%08X), existing entry\n", taddress); @@ -214,7 +204,7 @@ bool device_vtlb_interface::vtlb_fill(offs_t address, int intention) } // add the intention to the list of valid intentions and store - entry |= 1 << (intention & (TRANSLATE_TYPE_MASK | TRANSLATE_USER_MASK)); + entry |= 1 << intention; m_table[tableindex] = entry; return true; } @@ -255,7 +245,7 @@ void device_vtlb_interface::vtlb_load(int entrynum, int numpages, offs_t address m_refcnt[tableindex]++; // store the raw value, making sure the "fixed" flag is set - value |= VTLB_FLAG_FIXED; + value |= FLAG_FIXED; m_fixedpages[entrynum] = numpages; for (pagenum = 0; pagenum < numpages; pagenum++) m_table[tableindex + pagenum] = value + (pagenum << m_pageshift); @@ -277,9 +267,12 @@ void device_vtlb_interface::vtlb_dynload(u32 index, offs_t address, vtlb_entry v return; } - int liveindex = m_dynindex++ % m_dynamic; + int liveindex = m_dynindex; + + m_dynindex = (m_dynindex + 1) % m_dynamic; + // is entry already live? - if (!(entry & VTLB_FLAG_VALID)) + if (!(entry & FLAG_VALID)) { // if an entry already exists at this index, free it if (m_live[liveindex] != 0) @@ -290,7 +283,7 @@ void device_vtlb_interface::vtlb_dynload(u32 index, offs_t address, vtlb_entry v } // form a new blank entry entry = (address >> m_pageshift) << m_pageshift; - entry |= VTLB_FLAG_VALID | value; + entry |= FLAG_VALID | value; #if PRINTF_TLB osd_printf_debug("success (%08X), new entry\n", address); @@ -352,7 +345,7 @@ void device_vtlb_interface::vtlb_flush_address(offs_t address) // the linear VTLB lookup table //------------------------------------------------- -const vtlb_entry *device_vtlb_interface::vtlb_table() const +const device_vtlb_interface::vtlb_entry *device_vtlb_interface::vtlb_table() const { return m_table_base; } diff --git a/src/emu/divtlb.h b/src/emu/divtlb.h index c64bca0a21c..13adbd4a4ce 100644 --- a/src/emu/divtlb.h +++ b/src/emu/divtlb.h @@ -13,38 +13,35 @@ #pragma once - - -/*************************************************************************** - CONSTANTS -***************************************************************************/ - -constexpr u32 VTLB_FLAGS_MASK = 0xff; - -constexpr u32 VTLB_READ_ALLOWED = 0x01; /* (1 << TRANSLATE_READ) */ -constexpr u32 VTLB_WRITE_ALLOWED = 0x02; /* (1 << TRANSLATE_WRITE) */ -constexpr u32 VTLB_FETCH_ALLOWED = 0x04; /* (1 << TRANSLATE_FETCH) */ -constexpr u32 VTLB_FLAG_VALID = 0x08; -constexpr u32 VTLB_USER_READ_ALLOWED = 0x10; /* (1 << TRANSLATE_READ_USER) */ -constexpr u32 VTLB_USER_WRITE_ALLOWED = 0x20; /* (1 << TRANSLATE_WRITE_USER) */ -constexpr u32 VTLB_USER_FETCH_ALLOWED = 0x40; /* (1 << TRANSLATE_FETCH_USER) */ -constexpr u32 VTLB_FLAG_FIXED = 0x80; - - - -/*************************************************************************** - TYPE DEFINITIONS -***************************************************************************/ - -/* represents an entry in the VTLB */ -typedef u32 vtlb_entry; - - -// ======================> device_vtlb_interface - class device_vtlb_interface : public device_interface { public: + using vtlb_entry = u32; + + // Translation type flags + // TR_READ/WRITE/FETCH come from device_memory_interface + enum { + TR_UREAD = 4, + TR_UWRITE = 5, + TR_UFETCH = 6, + + TR_TYPE = 3, + TR_USER = 4 + }; + + enum { + FLAGS_MASK = 0xff, + + READ_ALLOWED = 0x01, /* (1 << TR_READ) */ + WRITE_ALLOWED = 0x02, /* (1 << TR_WRITE) */ + FETCH_ALLOWED = 0x04, /* (1 << TR_FETCH) */ + FLAG_VALID = 0x08, + USER_READ_ALLOWED = 0x10, /* (1 << TR_UREAD) */ + USER_WRITE_ALLOWED = 0x20, /* (1 << TR_UWRITE) */ + USER_FETCH_ALLOWED = 0x40, /* (1 << TR_UFETCH) */ + FLAG_FIXED = 0x80 + }; + // construction/destruction device_vtlb_interface(const machine_config &mconfig, device_t &device, int space); virtual ~device_vtlb_interface(); @@ -54,7 +51,7 @@ public: void set_vtlb_fixed_entries(int entries) { m_fixed = entries; } // filling - bool vtlb_fill(offs_t address, int intention); + bool vtlb_fill(offs_t address, offs_t taddress, int intention); void vtlb_load(int entrynum, int numpages, offs_t address, vtlb_entry value); void vtlb_dynload(u32 index, offs_t address, vtlb_entry value); diff --git a/src/emu/drawgfx.cpp b/src/emu/drawgfx.cpp index fe909496342..d7648483f21 100644 --- a/src/emu/drawgfx.cpp +++ b/src/emu/drawgfx.cpp @@ -2,9 +2,10 @@ // copyright-holders:Nicola Salmoria, Aaron Giles /********************************************************************* - drawgfx.c + drawgfx.cpp Generic graphic functions. + *********************************************************************/ #include "emu.h" @@ -20,7 +21,7 @@ offset -------------------------------------------------*/ -static inline int readbit(const u8 *src, unsigned int bitnum) +constexpr int readbit(const u8 *src, unsigned int bitnum) { return src[bitnum / 8] & (0x80 >> (bitnum % 8)); } @@ -65,63 +66,61 @@ gfxdecode_device::gfxdecode_device(const machine_config &mconfig, const char *ta - /*************************************************************************** GRAPHICS ELEMENTS ***************************************************************************/ - //------------------------------------------------- // gfx_element - constructor //------------------------------------------------- -gfx_element::gfx_element(device_palette_interface *palette, u8 *base, u16 width, u16 height, u32 rowbytes, u32 total_colors, u32 color_base, u32 color_granularity) - : m_palette(palette), - m_width(width), - m_height(height), - m_startx(0), - m_starty(0), - m_origwidth(width), - m_origheight(height), - m_total_elements(1), - m_color_base(color_base), - m_color_depth(color_granularity), - m_color_granularity(color_granularity), - m_total_colors((total_colors - color_base) / color_granularity), - m_line_modulo(rowbytes), - m_char_modulo(0), - m_srcdata(base), - m_dirtyseq(1), - m_gfxdata(base), - m_layout_is_raw(true), - m_layout_planes(0), - m_layout_xormask(0), - m_layout_charincrement(0) -{ -} - -gfx_element::gfx_element(device_palette_interface *palette, const gfx_layout &gl, const u8 *srcdata, u32 xormask, u32 total_colors, u32 color_base) - : m_palette(palette), - m_width(0), - m_height(0), - m_startx(0), - m_starty(0), - m_origwidth(0), - m_origheight(0), - m_total_elements(0), - m_color_base(color_base), - m_color_depth(0), - m_color_granularity(0), - m_total_colors(total_colors), - m_line_modulo(0), - m_char_modulo(0), - m_srcdata(nullptr), - m_dirtyseq(1), - m_gfxdata(nullptr), - m_layout_is_raw(false), - m_layout_planes(0), - m_layout_xormask(xormask), - m_layout_charincrement(0) +gfx_element::gfx_element(device_palette_interface *palette, u8 *base, u16 width, u16 height, u32 rowbytes, u32 total_colors, u32 color_base, u32 color_granularity) : + m_palette(palette), + m_width(width), + m_height(height), + m_startx(0), + m_starty(0), + m_origwidth(width), + m_origheight(height), + m_total_elements(1), + m_color_base(color_base), + m_color_depth(color_granularity), + m_color_granularity(color_granularity), + m_total_colors((total_colors - color_base) / color_granularity), + m_line_modulo(rowbytes), + m_char_modulo(0), + m_srcdata(base), + m_dirtyseq(1), + m_gfxdata(base), + m_layout_is_raw(true), + m_layout_planes(0), + m_layout_xormask(0), + m_layout_charincrement(0) +{ +} + +gfx_element::gfx_element(device_palette_interface *palette, const gfx_layout &gl, const u8 *srcdata, u32 xormask, u32 total_colors, u32 color_base) : + m_palette(palette), + m_width(0), + m_height(0), + m_startx(0), + m_starty(0), + m_origwidth(0), + m_origheight(0), + m_total_elements(0), + m_color_base(color_base), + m_color_depth(0), + m_color_granularity(0), + m_total_colors(total_colors), + m_line_modulo(0), + m_char_modulo(0), + m_srcdata(nullptr), + m_dirtyseq(1), + m_gfxdata(nullptr), + m_layout_is_raw(false), + m_layout_planes(0), + m_layout_xormask(xormask), + m_layout_charincrement(0) { // set the layout set_layout(gl, srcdata); @@ -2132,8 +2131,8 @@ void primask_copyscrollbitmap(bitmap_rgb32 &dest, const bitmap_rgb32 &src, u32 n scrolling to one or more rows/columns -------------------------------------------------*/ -template<class _BitmapClass> -static inline void copyscrollbitmap_trans_common(_BitmapClass &dest, const _BitmapClass &src, u32 numrows, const s32 *rowscroll, u32 numcols, const s32 *colscroll, const rectangle &cliprect, u32 trans_pen) +template<class BitmapClass> +static inline void copyscrollbitmap_trans_common(BitmapClass &dest, const BitmapClass &src, u32 numrows, const s32 *rowscroll, u32 numcols, const s32 *colscroll, const rectangle &cliprect, u32 trans_pen) { // no rowscroll and no colscroll means no scroll if (numrows == 0 && numcols == 0) @@ -2235,8 +2234,8 @@ void copyscrollbitmap_trans(bitmap_ind16 &dest, const bitmap_ind16 &src, u32 num void copyscrollbitmap_trans(bitmap_rgb32 &dest, const bitmap_rgb32 &src, u32 numrows, const s32 *rowscroll, u32 numcols, const s32 *colscroll, const rectangle &cliprect, u32 trans_pen) { copyscrollbitmap_trans_common(dest, src, numrows, rowscroll, numcols, colscroll, cliprect, trans_pen); } -template<class _BitmapClass> -static inline void prio_copyscrollbitmap_trans_common(_BitmapClass &dest, const _BitmapClass &src, u32 numrows, const s32 *rowscroll, u32 numcols, const s32 *colscroll, const rectangle &cliprect, bitmap_ind8 &priority, u32 pmask, u32 trans_pen) +template<class BitmapClass> +static inline void prio_copyscrollbitmap_trans_common(BitmapClass &dest, const BitmapClass &src, u32 numrows, const s32 *rowscroll, u32 numcols, const s32 *colscroll, const rectangle &cliprect, bitmap_ind8 &priority, u32 pmask, u32 trans_pen) { // no rowscroll and no colscroll means no scroll if (numrows == 0 && numcols == 0) @@ -2338,8 +2337,8 @@ void prio_copyscrollbitmap_trans(bitmap_ind16 &dest, const bitmap_ind16 &src, u3 void prio_copyscrollbitmap_trans(bitmap_rgb32 &dest, const bitmap_rgb32 &src, u32 numrows, const s32 *rowscroll, u32 numcols, const s32 *colscroll, const rectangle &cliprect, bitmap_ind8 &priority, u32 pmask, u32 trans_pen) { prio_copyscrollbitmap_trans_common(dest, src, numrows, rowscroll, numcols, colscroll, cliprect, priority, pmask, trans_pen); } -template<class _BitmapClass> -static inline void primask_copyscrollbitmap_trans_common(_BitmapClass &dest, const _BitmapClass &src, u32 numrows, const s32 *rowscroll, u32 numcols, const s32 *colscroll, const rectangle &cliprect, u32 trans_pen, bitmap_ind8 &priority, u8 pcode = 0, u8 pmask = 0xff) +template<class BitmapClass> +static inline void primask_copyscrollbitmap_trans_common(BitmapClass &dest, const BitmapClass &src, u32 numrows, const s32 *rowscroll, u32 numcols, const s32 *colscroll, const rectangle &cliprect, u32 trans_pen, bitmap_ind8 &priority, u8 pcode = 0, u8 pmask = 0xff) { // no rowscroll and no colscroll means no scroll if (pcode == 0 && pmask == 0xff) diff --git a/src/emu/drawgfx.h b/src/emu/drawgfx.h index 164e452b193..d28024bb442 100644 --- a/src/emu/drawgfx.h +++ b/src/emu/drawgfx.h @@ -5,6 +5,7 @@ drawgfx.h Generic graphic functions. + ********************************************************************** How to use priority-masked drawing (formerly pdrawgfx): @@ -537,8 +538,8 @@ class gfxdecode_device : public device_t, public device_gfx_interface public: // construction/destruction template <typename T> - gfxdecode_device(const machine_config &mconfig, const char *tag, device_t *owner, T &&palette_tag, const gfx_decode_entry *gfxinfo) - : gfxdecode_device(mconfig, tag, owner, 0) + gfxdecode_device(const machine_config &mconfig, const char *tag, device_t *owner, T &&palette_tag, const gfx_decode_entry *gfxinfo) : + gfxdecode_device(mconfig, tag, owner, 0) { set_palette(std::forward<T>(palette_tag)); set_info(gfxinfo); @@ -546,7 +547,7 @@ public: gfxdecode_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock = 0); protected: - virtual void device_start() override {} + virtual void device_start() override { } }; #endif // MAME_EMU_DRAWGFX_H diff --git a/src/emu/drawgfxt.ipp b/src/emu/drawgfxt.ipp index 97117ba6cd0..4277bf21634 100644 --- a/src/emu/drawgfxt.ipp +++ b/src/emu/drawgfxt.ipp @@ -421,7 +421,7 @@ while (0) template <typename BitmapType, typename FunctionClass> inline void gfx_element::drawgfx_core(BitmapType &dest, const rectangle &cliprect, u32 code, int flipx, int flipy, s32 destx, s32 desty, FunctionClass pixel_op) { - g_profiler.start(PROFILER_DRAWGFX); + auto profile = g_profiler.start(PROFILER_DRAWGFX); do { assert(dest.valid()); assert(dest.cliprect().contains(cliprect)); @@ -551,14 +551,13 @@ inline void gfx_element::drawgfx_core(BitmapType &dest, const rectangle &cliprec } } } while (0); - g_profiler.stop(); } template <typename BitmapType, typename PriorityType, typename FunctionClass> inline void gfx_element::drawgfx_core(BitmapType &dest, const rectangle &cliprect, u32 code, int flipx, int flipy, s32 destx, s32 desty, PriorityType &priority, FunctionClass pixel_op) { - g_profiler.start(PROFILER_DRAWGFX); + auto profile = g_profiler.start(PROFILER_DRAWGFX); do { assert(dest.valid()); assert(priority.valid()); @@ -695,7 +694,6 @@ inline void gfx_element::drawgfx_core(BitmapType &dest, const rectangle &cliprec } } } while (0); - g_profiler.stop(); } @@ -724,7 +722,7 @@ inline void gfx_element::drawgfx_core(BitmapType &dest, const rectangle &cliprec template <typename BitmapType, typename FunctionClass> inline void gfx_element::drawgfxzoom_core(BitmapType &dest, const rectangle &cliprect, u32 code, int flipx, int flipy, s32 destx, s32 desty, u32 scalex, u32 scaley, FunctionClass pixel_op) { - g_profiler.start(PROFILER_DRAWGFX); + auto profile = g_profiler.start(PROFILER_DRAWGFX); do { assert(dest.valid()); assert(dest.cliprect().contains(cliprect)); @@ -830,14 +828,13 @@ inline void gfx_element::drawgfxzoom_core(BitmapType &dest, const rectangle &cli } } } while (0); - g_profiler.stop(); } template <typename BitmapType, typename PriorityType, typename FunctionClass> inline void gfx_element::drawgfxzoom_core(BitmapType &dest, const rectangle &cliprect, u32 code, int flipx, int flipy, s32 destx, s32 desty, u32 scalex, u32 scaley, PriorityType &priority, FunctionClass pixel_op) { - g_profiler.start(PROFILER_DRAWGFX); + auto profile = g_profiler.start(PROFILER_DRAWGFX); do { assert(dest.valid()); assert(priority.valid()); @@ -877,10 +874,7 @@ inline void gfx_element::drawgfxzoom_core(BitmapType &dest, const rectangle &cli // compute final pixel in Y and exit if we are entirely clipped s32 destendy = desty + dstheight - 1; if (desty > cliprect.bottom() || destendy < cliprect.top()) - { - g_profiler.stop(); return; - } // apply top clip s32 srcy = 0; @@ -950,7 +944,6 @@ inline void gfx_element::drawgfxzoom_core(BitmapType &dest, const rectangle &cli } } } while (0); - g_profiler.stop(); } @@ -975,7 +968,7 @@ inline void gfx_element::drawgfxzoom_core(BitmapType &dest, const rectangle &cli template <typename BitmapType, typename FunctionClass> inline void copybitmap_core(BitmapType &dest, const BitmapType &src, int flipx, int flipy, s32 destx, s32 desty, const rectangle &cliprect, FunctionClass pixel_op) { - g_profiler.start(PROFILER_COPYBITMAP); + auto profile = g_profiler.start(PROFILER_COPYBITMAP); do { assert(dest.valid()); assert(src.valid()); @@ -1108,14 +1101,13 @@ inline void copybitmap_core(BitmapType &dest, const BitmapType &src, int flipx, } } } while (0); - g_profiler.stop(); } template <typename BitmapType, typename PriorityType, typename FunctionClass> inline void copybitmap_core(BitmapType &dest, const BitmapType &src, int flipx, int flipy, s32 destx, s32 desty, const rectangle &cliprect, PriorityType &priority, FunctionClass pixel_op) { - g_profiler.start(PROFILER_COPYBITMAP); + auto profile = g_profiler.start(PROFILER_COPYBITMAP); do { assert(dest.valid()); assert(src.valid()); @@ -1255,7 +1247,6 @@ inline void copybitmap_core(BitmapType &dest, const BitmapType &src, int flipx, } } } while (0); - g_profiler.stop(); } @@ -1283,7 +1274,7 @@ inline void copybitmap_core(BitmapType &dest, const BitmapType &src, int flipx, template <typename BitmapType, typename FunctionClass> inline void copyrozbitmap_core(BitmapType &dest, const rectangle &cliprect, const BitmapType &src, s32 startx, s32 starty, s32 incxx, s32 incxy, s32 incyx, s32 incyy, bool wraparound, FunctionClass pixel_op) { - g_profiler.start(PROFILER_COPYBITMAP); + auto profile = g_profiler.start(PROFILER_COPYBITMAP); assert(dest.valid()); assert(dest.valid()); @@ -1293,10 +1284,7 @@ inline void copyrozbitmap_core(BitmapType &dest, const rectangle &cliprect, cons // ignore empty/invalid cliprects if (cliprect.empty()) - { - g_profiler.stop(); return; - } // compute fixed-point 16.16 size of the source bitmap u32 srcfixwidth = src.width() << 16; @@ -1537,14 +1525,13 @@ inline void copyrozbitmap_core(BitmapType &dest, const rectangle &cliprect, cons } } } - g_profiler.stop(); } template <typename BitmapType, typename PriorityType, typename FunctionClass> inline void copyrozbitmap_core(BitmapType &dest, const rectangle &cliprect, const BitmapType &src, s32 startx, s32 starty, s32 incxx, s32 incxy, s32 incyx, s32 incyy, bool wraparound, PriorityType &priority, FunctionClass pixel_op) { - g_profiler.start(PROFILER_COPYBITMAP); + auto profile = g_profiler.start(PROFILER_COPYBITMAP); assert(dest.valid()); assert(dest.valid()); @@ -1555,10 +1542,7 @@ inline void copyrozbitmap_core(BitmapType &dest, const rectangle &cliprect, cons // ignore empty/invalid cliprects if (cliprect.empty()) - { - g_profiler.stop(); return; - } // compute fixed-point 16.16 size of the source bitmap u32 srcfixwidth = src.width() << 16; @@ -1811,7 +1795,6 @@ inline void copyrozbitmap_core(BitmapType &dest, const rectangle &cliprect, cons } } } - g_profiler.stop(); } @@ -1892,15 +1875,15 @@ inline void drawscanline_core(BitmapType &bitmap, s32 destx, s32 desty, s32 leng srcptr += 4; destptr += 4; priptr += 4; + } - // iterate over leftover pixels - while (length-- > 0) - { - pixel_op(destptr[0], priptr[0], srcptr[0]); - srcptr++; - destptr++; - priptr++; - } + // iterate over leftover pixels + while (length-- > 0) + { + pixel_op(destptr[0], priptr[0], srcptr[0]); + srcptr++; + destptr++; + priptr++; } } diff --git a/src/emu/drivenum.cpp b/src/emu/drivenum.cpp index 989a9cc8f46..84158749189 100644 --- a/src/emu/drivenum.cpp +++ b/src/emu/drivenum.cpp @@ -10,11 +10,14 @@ #include "emu.h" #include "drivenum.h" + +#include "corestr.h" #include "softlist_dev.h" +#include "unicode.h" #include <algorithm> -#include <ctype.h> +#include <cctype> @@ -257,7 +260,7 @@ void driver_enumerator::find_approximate_matches(std::string const &string, std: { // allocate memory to track the penalty value std::vector<std::pair<double, int> > penalty; - penalty.reserve(count); + penalty.reserve(count + 1); std::u32string const search(ustr_from_utf8(normalize_unicode(string, unicode_normalization_form::D, true))); std::string composed; std::u32string candidate; @@ -300,9 +303,9 @@ void driver_enumerator::find_approximate_matches(std::string const &string, std: auto const it(std::upper_bound(penalty.begin(), penalty.end(), std::make_pair(curpenalty, index))); if (penalty.end() != it) { - if (penalty.size() >= count) - penalty.resize(count - 1); penalty.emplace(it, curpenalty, index); + if (penalty.size() > count) + penalty.pop_back(); } else if (penalty.size() < count) { @@ -340,7 +343,7 @@ void driver_enumerator::release_current() const if (cached != m_config.end()) { // iterate over software lists in this entry and reset - for (software_list_device &swlistdev : software_list_device_iterator(cached->second->root_device())) + for (software_list_device &swlistdev : software_list_device_enumerator(cached->second->root_device())) swlistdev.release(); } } diff --git a/src/emu/drivenum.h b/src/emu/drivenum.h index 8c0f12b832f..fe9bbcc7193 100644 --- a/src/emu/drivenum.h +++ b/src/emu/drivenum.h @@ -13,6 +13,8 @@ #pragma once +#include "lrucache.h" + #include <algorithm> #include <cassert> #include <memory> diff --git a/src/emu/driver.cpp b/src/emu/driver.cpp index 8dde7622da6..9b0dd9c5e59 100644 --- a/src/emu/driver.cpp +++ b/src/emu/driver.cpp @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles /*************************************************************************** - driver.c + driver.cpp Core driver device base class. @@ -24,10 +24,19 @@ driver_device::driver_device(const machine_config &mconfig, device_type type, const char *tag) : device_t(mconfig, type, tag, nullptr, 0) - , m_system(nullptr) + , m_system(mconfig.gamedrv()) , m_flip_screen_x(0) , m_flip_screen_y(0) { + // set the search path to include all parents and cache it because devices search system paths + m_searchpath.emplace_back(m_system.name); + std::set<game_driver const *> seen; + for (int ancestor = driver_list::clone(m_system); 0 <= ancestor; ancestor = driver_list::clone(ancestor)) + { + if (!seen.insert(&driver_list::driver(ancestor)).second) + throw emu_fatalerror("driver_device(%s): parent/clone relationships form a loop", m_system.name); + m_searchpath.emplace_back(driver_list::driver(ancestor).name); + } } @@ -41,37 +50,13 @@ driver_device::~driver_device() //------------------------------------------------- -// set_game_driver - set the game in the device -// configuration -//------------------------------------------------- - -void driver_device::set_game_driver(const game_driver &game) -{ - assert(!m_system); - - // set the system - m_system = &game; - - // and set the search path to include all parents - m_searchpath = game.name; - std::set<game_driver const *> seen; - for (int parent = driver_list::clone(game); parent != -1; parent = driver_list::clone(parent)) - { - if (!seen.insert(&driver_list::driver(parent)).second) - throw emu_fatalerror("driver_device::set_game_driver(%s): parent/clone relationships form a loop", game.name); - m_searchpath.append(";").append(driver_list::driver(parent).name); - } -} - - -//------------------------------------------------- // static_set_callback - set the a callback in // the device configuration //------------------------------------------------- void driver_device::static_set_callback(device_t &device, callback_type type, driver_callback_delegate callback) { - downcast<driver_device &>(device).m_callbacks[type] = callback; + downcast<driver_device &>(device).m_callbacks[type] = std::move(callback); } @@ -82,17 +67,16 @@ void driver_device::static_set_callback(device_t &device, callback_type type, dr void driver_device::empty_init() { - driver_init(); } //------------------------------------------------- -// driver_init - default implementation which -// does nothing +// searchpath - return cached search path //------------------------------------------------- -void driver_device::driver_init() +std::vector<std::string> driver_device::searchpath() const { + return m_searchpath; } @@ -183,8 +167,7 @@ void driver_device::video_reset() const tiny_rom_entry *driver_device::device_rom_region() const { - assert(m_system); - return m_system->rom; + return m_system.rom; } @@ -194,8 +177,7 @@ const tiny_rom_entry *driver_device::device_rom_region() const void driver_device::device_add_mconfig(machine_config &config) { - assert(m_system); - m_system->machine_creator(config, *this); + m_system.machine_creator(config, *this); } @@ -206,7 +188,7 @@ void driver_device::device_add_mconfig(machine_config &config) ioport_constructor driver_device::device_input_ports() const { - return m_system->ipt; + return m_system.ipt; } @@ -218,12 +200,12 @@ ioport_constructor driver_device::device_input_ports() const void driver_device::device_start() { // reschedule ourselves to be last - for (device_t &test : device_iterator(*this)) + for (device_t &test : device_enumerator(*this)) if (&test != this && !test.started()) throw device_missing_dependencies(); // call the game-specific init - m_system->driver_init(*this); + m_system.driver_init(*this); // finish image devices init process machine().image().postdevice_init(); @@ -236,10 +218,7 @@ void driver_device::device_start() else machine_start(); - if (!m_callbacks[CB_SOUND_START].isnull()) - m_callbacks[CB_SOUND_START](); - else - sound_start(); + sound_start(); if (!m_callbacks[CB_VIDEO_START].isnull()) m_callbacks[CB_VIDEO_START](); @@ -268,15 +247,9 @@ void driver_device::device_reset_after_children() else machine_reset(); - if (!m_callbacks[CB_SOUND_RESET].isnull()) - m_callbacks[CB_SOUND_RESET](); - else - sound_reset(); + sound_reset(); - if (!m_callbacks[CB_VIDEO_RESET].isnull()) - m_callbacks[CB_VIDEO_RESET](); - else - video_reset(); + video_reset(); } @@ -341,16 +314,16 @@ void driver_device::updateflip() // flip_screen_set - set global flip //------------------------------------------------- -void driver_device::flip_screen_set(u32 on) +void driver_device::flip_screen_set(int state) { // normalize to all 1 - if (on) - on = ~0; + if (state) + state = 0xff; // if something's changed, handle it - if (m_flip_screen_x != on || m_flip_screen_y != on) + if (m_flip_screen_x != state || m_flip_screen_y != state) { - m_flip_screen_x = m_flip_screen_y = on; + m_flip_screen_x = m_flip_screen_y = state; updateflip(); } } @@ -360,16 +333,16 @@ void driver_device::flip_screen_set(u32 on) // flip_screen_x_set - set global horizontal flip //------------------------------------------------- -void driver_device::flip_screen_x_set(u32 on) +void driver_device::flip_screen_x_set(int state) { // normalize to all 1 - if (on) - on = ~0; + if (state) + state = 0xff; // if something's changed, handle it - if (m_flip_screen_x != on) + if (m_flip_screen_x != state) { - m_flip_screen_x = on; + m_flip_screen_x = state; updateflip(); } } @@ -379,16 +352,16 @@ void driver_device::flip_screen_x_set(u32 on) // flip_screen_y_set - set global vertical flip //------------------------------------------------- -void driver_device::flip_screen_y_set(u32 on) +void driver_device::flip_screen_y_set(int state) { // normalize to all 1 - if (on) - on = ~0; + if (state) + state = 0xff; // if something's changed, handle it - if (m_flip_screen_y != on) + if (m_flip_screen_y != state) { - m_flip_screen_y = on; + m_flip_screen_y = state; updateflip(); } } diff --git a/src/emu/driver.h b/src/emu/driver.h index 4f6576aa737..ed7172d25ef 100644 --- a/src/emu/driver.h +++ b/src/emu/driver.h @@ -26,26 +26,22 @@ #define MCFG_MACHINE_START_OVERRIDE(_class, _func) \ driver_device::static_set_callback(config.root_device(), driver_device::CB_MACHINE_START, driver_callback_delegate(&_class::MACHINE_START_NAME(_func), this)); +#define MCFG_MACHINE_START_REMOVE() \ + driver_device::static_set_callback(config.root_device(), driver_device::CB_MACHINE_START, driver_callback_delegate()); + #define MCFG_MACHINE_RESET_OVERRIDE(_class, _func) \ driver_device::static_set_callback(config.root_device(), driver_device::CB_MACHINE_RESET, driver_callback_delegate(&_class::MACHINE_RESET_NAME(_func), this)); #define MCFG_MACHINE_RESET_REMOVE() \ driver_device::static_set_callback(config.root_device(), driver_device::CB_MACHINE_RESET, driver_callback_delegate()); -// core sound callbacks -#define MCFG_SOUND_START_OVERRIDE(_class, _func) \ - driver_device::static_set_callback(config.root_device(), driver_device::CB_SOUND_START, driver_callback_delegate(&_class::SOUND_START_NAME(_func), this)); - -#define MCFG_SOUND_RESET_OVERRIDE(_class, _func) \ - driver_device::static_set_callback(config.root_device(), driver_device::CB_SOUND_RESET, driver_callback_delegate(&_class::SOUND_RESET_NAME(_func), this)); - // core video callbacks #define MCFG_VIDEO_START_OVERRIDE(_class, _func) \ driver_device::static_set_callback(config.root_device(), driver_device::CB_VIDEO_START, driver_callback_delegate(&_class::VIDEO_START_NAME(_func), this)); -#define MCFG_VIDEO_RESET_OVERRIDE(_class, _func) \ - driver_device::static_set_callback(config.root_device(), driver_device::CB_VIDEO_RESET, driver_callback_delegate(&_class::VIDEO_RESET_NAME(_func), this)); +#define MCFG_VIDEO_START_REMOVE() \ + driver_device::static_set_callback(config.root_device(), driver_device::CB_VIDEO_START, driver_callback_delegate()); @@ -63,25 +59,11 @@ #define DECLARE_MACHINE_RESET(name) void MACHINE_RESET_NAME(name)() #define MACHINE_RESET_MEMBER(cls,name) void cls::MACHINE_RESET_NAME(name)() -#define SOUND_START_NAME(name) sound_start_##name -#define DECLARE_SOUND_START(name) void SOUND_START_NAME(name)() ATTR_COLD -#define SOUND_START_MEMBER(cls,name) void cls::SOUND_START_NAME(name)() - -#define SOUND_RESET_NAME(name) sound_reset_##name -#define SOUND_RESET_CALL_MEMBER(name) SOUND_RESET_NAME(name)() -#define DECLARE_SOUND_RESET(name) void SOUND_RESET_NAME(name)() -#define SOUND_RESET_MEMBER(cls,name) void cls::SOUND_RESET_NAME(name)() - #define VIDEO_START_NAME(name) video_start_##name #define VIDEO_START_CALL_MEMBER(name) VIDEO_START_NAME(name)() #define DECLARE_VIDEO_START(name) void VIDEO_START_NAME(name)() ATTR_COLD #define VIDEO_START_MEMBER(cls,name) void cls::VIDEO_START_NAME(name)() -#define VIDEO_RESET_NAME(name) video_reset_##name -#define VIDEO_RESET_CALL_MEMBER(name) VIDEO_RESET_NAME(name)() -#define DECLARE_VIDEO_RESET(name) void VIDEO_RESET_NAME(name)() -#define VIDEO_RESET_MEMBER(cls,name) void cls::VIDEO_RESET_NAME(name)() - //************************************************************************** @@ -92,9 +74,14 @@ typedef delegate<void ()> driver_callback_delegate; -// ======================> driver_device - -// base class for machine driver-specific devices +/// \brief Base class for system device classes +/// +/// System devices can be used as the root device of a system. +/// Indirection for metadata, input port definitons, initialisation +/// functions, ROM definitions, internal artwork and emulation status +/// flags is provided via the #game_driver structure. This allows +/// multiple systems to be be implemented using a single +/// system device class. class driver_device : public device_t { public: @@ -103,30 +90,26 @@ public: virtual ~driver_device(); // getters - const game_driver &system() const { assert(m_system != nullptr); return *m_system; } + const game_driver &system() const { return m_system; } // indexes into our generic callbacks enum callback_type { CB_MACHINE_START, CB_MACHINE_RESET, - CB_SOUND_START, - CB_SOUND_RESET, CB_VIDEO_START, - CB_VIDEO_RESET, CB_COUNT }; // inline configuration helpers - void set_game_driver(const game_driver &game); static void static_set_callback(device_t &device, callback_type type, driver_callback_delegate callback); - // dummy driver_init callback + /// \brief Empty system initialisation function + /// + /// Provided as a convenience for systems that have no additional + /// initialisation tasks. void empty_init(); - // memory helpers - address_space &generic_space() const { return machine().dummy_space(); } - // output heler output_manager &output() const { return machine().output(); } @@ -157,7 +140,7 @@ public: void irq7_line_hold(device_t &device); void irq7_line_assert(device_t &device); - virtual void driver_init(); + virtual std::vector<std::string> searchpath() const override; protected: // helpers called at startup @@ -173,31 +156,32 @@ protected: virtual void video_reset(); // device-level overrides - virtual const tiny_rom_entry *device_rom_region() const override; - virtual void device_add_mconfig(machine_config &config) override; - virtual ioport_constructor device_input_ports() const override; - virtual void device_start() override; + virtual const tiny_rom_entry *device_rom_region() const override ATTR_COLD; + virtual void device_add_mconfig(machine_config &config) override ATTR_COLD; + virtual ioport_constructor device_input_ports() const override ATTR_COLD; + virtual void device_start() override ATTR_COLD; virtual void device_reset_after_children() override; // generic video - void flip_screen_set(u32 on); - void flip_screen_x_set(u32 on); - void flip_screen_y_set(u32 on); - u32 flip_screen() const { return m_flip_screen_x; } - u32 flip_screen_x() const { return m_flip_screen_x; } - u32 flip_screen_y() const { return m_flip_screen_y; } + void flip_screen_set(int state); + void flip_screen_x_set(int state); + void flip_screen_y_set(int state); + u8 flip_screen() const { return m_flip_screen_x; } // & m_flip_screen_y? + u8 flip_screen_x() const { return m_flip_screen_x; } + u8 flip_screen_y() const { return m_flip_screen_y; } private: // helpers void updateflip(); // internal state - const game_driver *m_system; // pointer to the game driver + const game_driver &m_system; // reference to the system description + std::vector<std::string> m_searchpath; // media search path following parent/clone links driver_callback_delegate m_callbacks[CB_COUNT]; // start/reset callbacks // generic video - u8 m_flip_screen_x; - u8 m_flip_screen_y; + u8 m_flip_screen_x; + u8 m_flip_screen_y; }; diff --git a/src/emu/drivers/empty.cpp b/src/emu/drivers/empty.cpp index 4dea1cceb9d..9b907214487 100644 --- a/src/emu/drivers/empty.cpp +++ b/src/emu/drivers/empty.cpp @@ -2,15 +2,18 @@ // copyright-holders:Aaron Giles /************************************************************************* - empty.c + empty.cpp Empty driver. **************************************************************************/ #include "emu.h" + #include "emuopts.h" -#include "render.h" +#include "main.h" +#include "screen.h" + //************************************************************************** // DRIVER STATE @@ -24,6 +27,8 @@ public: void ___empty(machine_config &config); + virtual std::vector<std::string> searchpath() const override { return std::vector<std::string>(); } + protected: virtual void machine_start() override { @@ -60,7 +65,6 @@ void empty_state::___empty(machine_config &config) //************************************************************************** ROM_START( ___empty ) - ROM_REGION( 0x10, "user1", ROMREGION_ERASEFF ) ROM_END diff --git a/src/emu/drivers/testcpu.cpp b/src/emu/drivers/testcpu.cpp index cee5fbbe070..c6df4ff74af 100644 --- a/src/emu/drivers/testcpu.cpp +++ b/src/emu/drivers/testcpu.cpp @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles /************************************************************************* - testcpu.c + testcpu.cpp Example driver for performing CPU stress tests. @@ -12,6 +12,10 @@ #include "emu.h" #include "cpu/powerpc/ppc.h" +#include <sstream> + + +namespace { //************************************************************************** // CONSTANTS @@ -29,18 +33,37 @@ class testcpu_state : public driver_device { public: // constructor - testcpu_state(const machine_config &mconfig, device_type type, const char *tag) - : driver_device(mconfig, type, tag), - m_cpu(*this, "maincpu"), - m_ram(*this, "ram"), - m_space(nullptr) + testcpu_state(const machine_config &mconfig, device_type type, const char *tag) : + driver_device(mconfig, type, tag), + m_cpu(*this, "maincpu"), + m_ram(*this, "ram"), + m_space(nullptr) { } + void testcpu(machine_config &config); + +private: + class disasm_data_buffer : public util::disasm_interface::data_buffer + { + public: + disasm_data_buffer(address_space &space) : m_space(space) + { + } + + virtual u8 r8(offs_t pc) const override { return m_space.read_byte(pc); } + virtual u16 r16(offs_t pc) const override { return m_space.read_word(pc); } + virtual u32 r32(offs_t pc) const override { return m_space.read_dword(pc); } + virtual u64 r64(offs_t pc) const override { return m_space.read_qword(pc); } + + private: + address_space &m_space; + }; + // timer callback; used to wrest control of the system - virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override + TIMER_CALLBACK_MEMBER(timer_tick) { - static const u32 sample_instructions[] = + static constexpr u32 sample_instructions[] = { 0x3d40f900, // li r10,0xf9000000 0x394af000, // addi r10,r10,-0x1000 @@ -52,7 +75,7 @@ public: }; // iterate over instructions - for (auto & sample_instruction : sample_instructions) + for (auto &sample_instruction : sample_instructions) { // write the instruction to execute, followed by a BLR which will terminate the // basic block in the DRC @@ -74,8 +97,8 @@ public: } // output initial state - printf("==================================================\n"); - printf("Initial state:\n"); + osd_printf_info("==================================================\n"); + osd_printf_info("Initial state:\n"); dump_state(true); // execute one instruction @@ -83,7 +106,7 @@ public: m_cpu->run(); // dump the final register state - printf("Final state:\n"); + osd_printf_info("Final state:\n"); dump_state(false); } @@ -101,71 +124,63 @@ public: m_cpu->ppcdrc_set_options(PPCDRC_COMPATIBLE_OPTIONS); // set a timer to go off right away - timer_set(attotime::zero); + timer_alloc(FUNC(testcpu_state::timer_tick), this)->adjust(attotime::zero); } // dump the current CPU state void dump_state(bool disassemble) { - char buffer[256]; - u8 instruction[32]; - buffer[0] = 0; + std::ostringstream buffer; int bytes = 0; if (disassemble) { - // fill in an array of bytes in the CPU's natural order - int maxbytes = m_cpu->max_opcode_bytes(); - for (int bytenum = 0; bytenum < maxbytes; bytenum++) - instruction[bytenum] = m_space->read_byte(RAM_BASE + bytenum); - // disassemble the current instruction - bytes = m_cpu->disassemble(buffer, RAM_BASE, instruction, instruction) & DASMFLAG_LENGTHMASK; + disasm_data_buffer databuf(*m_space); + bytes = m_cpu->get_disassembler().disassemble(buffer, RAM_BASE, databuf, databuf) & util::disasm_interface::LENGTHMASK; } // output the registers - printf("PC : %08X", u32(m_cpu->state_int(PPC_PC))); - if (disassemble && bytes > 0) + osd_printf_info("PC : %08X", u32(m_cpu->state_int(PPC_PC))); + if (bytes > 0) { - printf(" => "); + osd_printf_info(" => "); for (int bytenum = 0; bytenum < bytes; bytenum++) - printf("%02X", instruction[bytenum]); - printf(" %s", buffer); + osd_printf_info("%02X", m_space->read_byte(RAM_BASE + bytenum)); + osd_printf_info(" %s", buffer.str()); } - printf("\n"); + osd_printf_info("\n"); for (int regnum = 0; regnum < 32; regnum++) { - printf("R%-2d: %08X ", regnum, u32(m_cpu->state_int(PPC_R0 + regnum))); - if (regnum % 4 == 3) printf("\n"); + osd_printf_info("R%-2d: %08X ", regnum, u32(m_cpu->state_int(PPC_R0 + regnum))); + if (regnum % 4 == 3) osd_printf_info("\n"); } - printf("CR : %08X LR : %08X CTR: %08X XER: %08X\n", + osd_printf_info("CR : %08X LR : %08X CTR: %08X XER: %08X\n", u32(m_cpu->state_int(PPC_CR)), u32(m_cpu->state_int(PPC_LR)), u32(m_cpu->state_int(PPC_CTR)), u32(m_cpu->state_int(PPC_XER))); for (int regnum = 0; regnum < 32; regnum++) { - printf("F%-2d: %10g ", regnum, u2d(m_cpu->state_int(PPC_F0 + regnum))); - if (regnum % 4 == 3) printf("\n"); + osd_printf_info("F%-2d: %10g ", regnum, u2d(m_cpu->state_int(PPC_F0 + regnum))); + if (regnum % 4 == 3) osd_printf_info("\n"); } } // report reads from anywhere - READ64_MEMBER( general_r ) + u64 general_r(offs_t offset, u64 mem_mask = ~0) { u64 fulloffs = offset; u64 result = fulloffs + (fulloffs << 8) + (fulloffs << 16) + (fulloffs << 24) + (fulloffs << 32); - printf("Read from %08X & %08X%08X = %08X%08X\n", offset * 8, (int)((mem_mask&0xffffffff00000000LL) >> 32) , (int)(mem_mask&0xffffffff), (int)((result&0xffffffff00000000LL) >> 32), (int)(result&0xffffffff)); + osd_printf_info("Read from %08X & %016X = %016X\n", offset * 8, mem_mask, result); return result; } // report writes to anywhere - WRITE64_MEMBER( general_w ) + void general_w(offs_t offset, u64 data, u64 mem_mask = ~0) { - printf("Write to %08X & %08X%08X = %08X%08X\n", offset * 8, (int)((mem_mask&0xffffffff00000000LL) >> 32) , (int)(mem_mask&0xffffffff), (int)((data&0xffffffff00000000LL) >> 32), (int)(data&0xffffffff)); + osd_printf_info("Write to %08X & %016X = %016X\n", offset * 8, mem_mask, data); } - void testcpu(machine_config &config); + void ppc_mem(address_map &map) ATTR_COLD; - void ppc_mem(address_map &map); -private: // internal state required_device<ppc603e_device> m_cpu; required_shared_ptr<u64> m_ram; @@ -180,8 +195,8 @@ private: void testcpu_state::ppc_mem(address_map &map) { + map(0x00000000, 0xffffffff).rw(FUNC(testcpu_state::general_r), FUNC(testcpu_state::general_w)); map(RAM_BASE, RAM_BASE+7).ram().share("ram"); - map(0x00000000, 0xffffffff).rw(this, FUNC(testcpu_state::general_r), FUNC(testcpu_state::general_w)); } @@ -193,8 +208,8 @@ void testcpu_state::ppc_mem(address_map &map) void testcpu_state::testcpu(machine_config &config) { // CPUs - PPC603E(config, m_cpu, 66000000); - m_cpu->set_bus_frequency(66000000); // Multiplier 1, Bus = 66MHz, Core = 66MHz + PPC603E(config, m_cpu, 66'000'000); + m_cpu->set_bus_frequency(66'000'000); // Multiplier 1, Bus = 66MHz, Core = 66MHz m_cpu->set_addrmap(AS_PROGRAM, &testcpu_state::ppc_mem); } @@ -208,10 +223,11 @@ ROM_START( testcpu ) ROM_REGION( 0x10, "user1", ROMREGION_ERASEFF ) ROM_END +} // anonymous namespace //************************************************************************** // GAME DRIVERS //************************************************************************** -GAME( 2012, testcpu, 0, testcpu, 0, driver_device, 0, ROT0, "MAME", "CPU Tester", MACHINE_NO_SOUND ) +GAME( 2012, testcpu, 0, testcpu, 0, testcpu_state, empty_init, ROT0, "MAME", "CPU Tester", MACHINE_NO_SOUND_HW ) diff --git a/src/emu/emu.h b/src/emu/emu.h index 35a5d6a3f60..679687f772d 100644 --- a/src/emu/emu.h +++ b/src/emu/emu.h @@ -18,6 +18,9 @@ #ifndef __EMU_H__ #define __EMU_H__ +// get forward declarations before anything else +#include "emufwd.h" + #include <list> #include <forward_list> #include <vector> @@ -28,29 +31,23 @@ // core emulator headers -- must be first (profiler needs attotime, attotime needs xtal) #include "emucore.h" +#include "osdcore.h" #include "eminline.h" #include "xtal.h" #include "attotime.h" #include "profiler.h" -// http interface helpers -#include "http.h" - // commonly-referenced utilities imported from lib/util +#include "corefile.h" +#include "delegate.h" +#include "hash.h" #include "palette.h" -#include "unicode.h" -#include "strformat.h" -#include "vecstream.h" // emulator-specific utilities -#include "hash.h" -#include "fileio.h" -#include "delegate.h" #include "devdelegate.h" // memory and address spaces #include "emumem.h" -#include "memarray.h" // machine-wide utilities #include "romentry.h" @@ -67,8 +64,6 @@ #include "addrmap.h" // Needs optional_device<> and required_device<> #include "distate.h" #include "dimemory.h" -#include "dirom.h" -#include "opresolv.h" #include "dipalette.h" #include "digfx.h" #include "diimage.h" @@ -77,7 +72,6 @@ #include "divideo.h" #include "dinvram.h" #include "schedule.h" -#include "dinetwork.h" // machine and driver configuration #include "mconfig.h" @@ -85,7 +79,6 @@ #include "parameters.h" // the running machine -#include "main.h" #include "machine.h" #include "driver.h" diff --git a/src/emu/emualloc.cpp b/src/emu/emualloc.cpp deleted file mode 100644 index 60bbc11f01f..00000000000 --- a/src/emu/emualloc.cpp +++ /dev/null @@ -1,207 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Aaron Giles -/*************************************************************************** - - emualloc.c - - Memory allocation helpers for the core emulator. - -***************************************************************************/ - -#include "emucore.h" - -//************************************************************************** -// DEBUGGING -//************************************************************************** - -#define LOG_ALLOCS (0) - - - -//************************************************************************** -// GLOBALS -//************************************************************************** - -osd::u64 resource_pool::s_id = 0; - - - -//************************************************************************** -// RESOURCE POOL -//************************************************************************** - -//------------------------------------------------- -// resource_pool - constructor for a new resource -// pool -//------------------------------------------------- - -resource_pool::resource_pool(int hash_size) - : m_hash_size(hash_size), - m_hash(hash_size), - m_ordered_head(nullptr), - m_ordered_tail(nullptr) -{ - memset(&m_hash[0], 0, hash_size*sizeof(m_hash[0])); -} - - -//------------------------------------------------- -// ~resource_pool - destructor for a resource -// pool; make sure all tracked objects are -// deleted -//------------------------------------------------- - -resource_pool::~resource_pool() -{ - clear(); -} - - -//------------------------------------------------- -// add - add a new item to the resource pool -//------------------------------------------------- - -void resource_pool::add(resource_pool_item &item, size_t size, const char *type) -{ - std::lock_guard<std::mutex> lock(m_listlock); - - // insert into hash table - int hashval = reinterpret_cast<uintptr_t>(item.m_ptr) % m_hash_size; - item.m_next = m_hash[hashval]; - m_hash[hashval] = &item; - - // fetch the ID of this item's pointer; some implementations put hidden data - // before, so if we don't find it, check 4 bytes ahead - item.m_id = ++s_id; - if (LOG_ALLOCS) - fprintf(stderr, "#%06d, add %s, %d bytes\n", u32(item.m_id), type, u32(size)); - - // find the entry to insert after - resource_pool_item *insert_after; - for (insert_after = m_ordered_tail; insert_after != nullptr; insert_after = insert_after->m_ordered_prev) - if (insert_after->m_id < item.m_id) - break; - - // insert into the appropriate spot - if (insert_after != nullptr) - { - item.m_ordered_next = insert_after->m_ordered_next; - if (item.m_ordered_next != nullptr) - item.m_ordered_next->m_ordered_prev = &item; - else - m_ordered_tail = &item; - item.m_ordered_prev = insert_after; - insert_after->m_ordered_next = &item; - } - else - { - item.m_ordered_next = m_ordered_head; - if (item.m_ordered_next != nullptr) - item.m_ordered_next->m_ordered_prev = &item; - else - m_ordered_tail = &item; - item.m_ordered_prev = nullptr; - m_ordered_head = &item; - } -} - - -//------------------------------------------------- -// remove - remove a specific item from the -// resource pool -//------------------------------------------------- - -void resource_pool::remove(void *ptr) -{ - // ignore NULLs - if (ptr == nullptr) - return; - - // search for the item - std::lock_guard<std::mutex> lock(m_listlock); - - int hashval = reinterpret_cast<uintptr_t>(ptr) % m_hash_size; - for (resource_pool_item **scanptr = &m_hash[hashval]; *scanptr != nullptr; scanptr = &(*scanptr)->m_next) - - // must match the pointer - if ((*scanptr)->m_ptr == ptr) - { - // remove from hash table - resource_pool_item *deleteme = *scanptr; - *scanptr = deleteme->m_next; - - // remove from ordered list - if (deleteme->m_ordered_prev != nullptr) - deleteme->m_ordered_prev->m_ordered_next = deleteme->m_ordered_next; - else - m_ordered_head = deleteme->m_ordered_next; - if (deleteme->m_ordered_next != nullptr) - deleteme->m_ordered_next->m_ordered_prev = deleteme->m_ordered_prev; - else - m_ordered_tail = deleteme->m_ordered_prev; - - // delete the object and break - if (LOG_ALLOCS) - fprintf(stderr, "#%06d, delete %d bytes\n", u32(deleteme->m_id), u32(deleteme->m_size)); - global_free(deleteme); - break; - } -} - - -//------------------------------------------------- -// find - find a specific item in the resource -// pool -//------------------------------------------------- - -resource_pool_item *resource_pool::find(void *ptr) -{ - // search for the item - std::lock_guard<std::mutex> lock(m_listlock); - - int hashval = reinterpret_cast<uintptr_t>(ptr) % m_hash_size; - resource_pool_item *item; - for (item = m_hash[hashval]; item != nullptr; item = item->m_next) - if (item->m_ptr == ptr) - break; - - return item; -} - - -//------------------------------------------------- -// contains - return true if given ptr is -// contained by one of the objects in the pool -//------------------------------------------------- - -bool resource_pool::contains(void *_ptrstart, void *_ptrend) -{ - u8 *ptrstart = reinterpret_cast<u8 *>(_ptrstart); - u8 *ptrend = reinterpret_cast<u8 *>(_ptrend); - - // search for the item - std::lock_guard<std::mutex> lock(m_listlock); - - resource_pool_item *item = nullptr; - for (item = m_ordered_head; item != nullptr; item = item->m_ordered_next) - { - u8 *objstart = reinterpret_cast<u8 *>(item->m_ptr); - u8 *objend = objstart + item->m_size; - if (ptrstart >= objstart && ptrend <= objend) - return true; - } - return false; -} - - -//------------------------------------------------- -// clear - remove all items from a resource pool -//------------------------------------------------- - -void resource_pool::clear() -{ - // important: delete from earliest to latest; this allows objects to clean up after - // themselves if they wish - while (m_ordered_head != nullptr) - remove(m_ordered_head->m_ptr); -} diff --git a/src/emu/emualloc.h b/src/emu/emualloc.h deleted file mode 100644 index 0ed7cc7ea75..00000000000 --- a/src/emu/emualloc.h +++ /dev/null @@ -1,146 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Aaron Giles -/*************************************************************************** - - emualloc.h - - Memory allocation helpers for the core emulator. - -***************************************************************************/ - -#ifndef MAME_EMU_EMUALLOC_H -#define MAME_EMU_EMUALLOC_H - -#pragma once - -#include "osdcore.h" -#include "coretmpl.h" - -#include <memory> -#include <mutex> -#include <new> - - -//************************************************************************** -// MACROS -//************************************************************************** - -// pool allocation helpers -#define pool_alloc(_pool, _type) (_pool).add_object(global_alloc(_type)) -#define pool_alloc_clear(_pool, _type) (_pool).add_object(global_alloc_clear _type) -#define pool_alloc_array(_pool, _type, _num) (_pool).add_array(global_alloc_array(_type,_num), (_num)) -#define pool_alloc_array_clear(_pool, _type, _num) (_pool).add_array(global_alloc_array_clear<_type>(_num), (_num)) -#define pool_free(_pool, v) (_pool).remove(v) - - - -//************************************************************************** -// TYPE DEFINITIONS -//************************************************************************** - -// resource_pool_item is a base class for items that are tracked by a resource pool -class resource_pool_item -{ -private: - resource_pool_item(const resource_pool_item &) = delete; - resource_pool_item &operator=(const resource_pool_item &) = delete; - -public: - resource_pool_item(void *ptr, size_t size) - : m_next(nullptr) - , m_ordered_next(nullptr) - , m_ordered_prev(nullptr) - , m_ptr(ptr) - , m_size(size) - , m_id(~osd::u64(0)) - { } - virtual ~resource_pool_item() { } - - resource_pool_item * m_next; - resource_pool_item * m_ordered_next; - resource_pool_item * m_ordered_prev; - void * m_ptr; - size_t m_size; - osd::u64 m_id; -}; - - -// a resource_pool_object is a simple object wrapper for the templatized type -template <class ObjectClass> -class resource_pool_object : public resource_pool_item -{ -private: - resource_pool_object(const resource_pool_object<ObjectClass> &) = delete; - resource_pool_object &operator=(const resource_pool_object<ObjectClass> &) = delete; - -public: - resource_pool_object(ObjectClass *object) - : resource_pool_item(reinterpret_cast<void *>(object), sizeof(ObjectClass)) - , m_object(object) - { } - resource_pool_object(std::unique_ptr<ObjectClass> &&object) - : resource_pool_object(object.release()) - { } - virtual ~resource_pool_object() { delete m_object; } - -private: - ObjectClass *const m_object; -}; - - -// a resource_pool_array is a simple object wrapper for an allocated array of -// the templatized type -template <class ObjectClass> class resource_pool_array : public resource_pool_item -{ -private: - resource_pool_array(const resource_pool_array<ObjectClass> &) = delete; - resource_pool_array &operator=(const resource_pool_array<ObjectClass> &) = delete; - -public: - resource_pool_array(ObjectClass *array, int count) - : resource_pool_item(reinterpret_cast<void *>(array), sizeof(ObjectClass) * count) - , m_array(array) - , m_count(count) - { } - virtual ~resource_pool_array() { delete[] m_array; } - -private: - ObjectClass *const m_array; - int const m_count; -}; - - -// a resource pool tracks items and frees them upon reset or destruction -class resource_pool -{ -private: - resource_pool(const resource_pool &) = delete; - resource_pool &operator=(const resource_pool &) = delete; - -public: - resource_pool(int hash_size = 193); - virtual ~resource_pool(); - - void add(resource_pool_item &item, size_t size, const char *type); - void remove(resource_pool_item &item) { remove(item.m_ptr); } - void remove(void *ptr); - void remove(const void *ptr) { remove(const_cast<void *>(ptr)); } - resource_pool_item *find(void *ptr); - bool contains(void *ptrstart, void *ptrend); - void clear(); - - template <class ObjectClass> ObjectClass *add_object(ObjectClass *object) { add(*new resource_pool_object<ObjectClass>(object), sizeof(ObjectClass), typeid(ObjectClass).name()); return object; } - template <class ObjectClass> ObjectClass *add_object(std::unique_ptr<ObjectClass> &&object) { return add_object(object.release()); } - template <class ObjectClass> ObjectClass *add_array(ObjectClass *array, int count) { add(*new resource_pool_array<ObjectClass>(array, count), sizeof(ObjectClass), typeid(ObjectClass).name()); return array; } - -private: - int m_hash_size; - std::mutex m_listlock; - std::vector<resource_pool_item *> m_hash; - resource_pool_item * m_ordered_head; - resource_pool_item * m_ordered_tail; - static osd::u64 s_id; -}; - - -#endif // MAME_EMU_EMUALLOC_H diff --git a/src/emu/emucore.cpp b/src/emu/emucore.cpp index 35256b4f364..d9a314cfaa2 100644 --- a/src/emu/emucore.cpp +++ b/src/emu/emucore.cpp @@ -2,72 +2,27 @@ // copyright-holders:Nicola Salmoria, Aaron Giles /*************************************************************************** - emucore.c + emucore.cpp Simple core functions that are defined in emucore.h and which may need to be accessed by other MAME-related tools. + ****************************************************************************/ #include "emu.h" #include "emucore.h" #include "osdcore.h" -const char *const endianness_names[2] = { "little", "big" }; - -emu_fatalerror::emu_fatalerror(const char *format, ...) : code(0) -{ - if (format == nullptr) - { - text[0] = '\0'; - } - else - { - va_list ap; - va_start(ap, format); - vsnprintf(text, sizeof(text), format, ap); - va_end(ap); - } - osd_break_into_debugger(text); -} - -emu_fatalerror::emu_fatalerror(const char *format, va_list ap) : code(0) -{ - if (format == nullptr) - { - text[0] = '\0'; - } - else - { - vsnprintf(text, sizeof(text), format, ap); - } - osd_break_into_debugger(text); -} - -emu_fatalerror::emu_fatalerror(int _exitcode, const char *format, ...) : code(_exitcode) +emu_fatalerror::emu_fatalerror(util::format_argument_pack<char> const &args) + : emu_fatalerror(0, args) { - if (format == nullptr) - { - text[0] = '\0'; - } - else - { - va_list ap; - va_start(ap, format); - vsnprintf(text, sizeof(text), format, ap); - va_end(ap); - } + osd_break_into_debugger(m_text.c_str()); } -emu_fatalerror::emu_fatalerror(int _exitcode, const char *format, va_list ap) : code(_exitcode) +emu_fatalerror::emu_fatalerror(int _exitcode, util::format_argument_pack<char> const &args) + : m_text(util::string_format(args)) + , m_code(_exitcode) { - if (format == nullptr) - { - text[0] = '\0'; - } - else - { - vsnprintf(text, sizeof(text), format, ap); - } } @@ -82,21 +37,3 @@ void report_bad_device_cast(const device_t *dev, const std::type_info &src_type, throw emu_fatalerror("Error: bad downcast<> or device<>. Tried to convert the device %s (%s) of type %s to a %s, which are incompatible.\n", dev->tag(), dev->name(), src_type.name(), dst_type.name()); } - -void fatalerror(const char *format, ...) -{ - va_list ap; - va_start(ap, format); - emu_fatalerror error(format, ap); - va_end(ap); - throw error; -} - -void fatalerror_exitcode(running_machine &machine, int exitcode, const char *format, ...) -{ - va_list ap; - va_start(ap, format); - emu_fatalerror error(exitcode, format, ap); - va_end(ap); - throw error; -} diff --git a/src/emu/emucore.h b/src/emu/emucore.h index 4c8b9c96056..f6552461131 100644 --- a/src/emu/emucore.h +++ b/src/emu/emucore.h @@ -5,6 +5,7 @@ emucore.h General core utilities and macros used throughout the emulator. + ***************************************************************************/ #ifndef MAME_EMU_EMUCORE_H @@ -12,33 +13,38 @@ #pragma once -// standard C includes -#include <assert.h> -#include <math.h> -#include <stdio.h> -#include <string.h> -#include <stdlib.h> -#include <stdarg.h> - // some cleanups for Solaris for things defined in stdlib.h #if defined(__sun__) && defined(__svr4__) #undef si_status #undef WWORD #endif +// centralised forward declarations +#include "emufwd.h" + +// common stuff from lib/util +#include "corealloc.h" +#include "coretmpl.h" +#include "bitmap.h" +#include "endianness.h" +#include "strformat.h" +#include "vecstream.h" + +// common stuff from osd +#include "osdcomm.h" + // standard C++ includes -#include <cassert> #include <exception> +#include <string> #include <type_traits> #include <typeinfo> -// core system includes -#include "osdcomm.h" -#include "emualloc.h" -#include "corestr.h" -#include "bitmap.h" - -#include "emufwd.h" +// standard C includes +#include <cassert> +#include <cmath> +#include <cstdio> +#include <cstdlib> +#include <cstring> //************************************************************************** @@ -70,20 +76,34 @@ using osd::s64; // useful utility functions using util::underlying_value; using util::enum_value; +using util::make_bitmask; using util::BIT; using util::bitswap; using util::iabs; +using util::string_format; + +using endianness_t = util::endianness; + +using util::BYTE_XOR_BE; +using util::BYTE_XOR_LE; +using util::BYTE4_XOR_BE; +using util::BYTE4_XOR_LE; +using util::WORD_XOR_BE; +using util::WORD_XOR_LE; +using util::BYTE8_XOR_BE; +using util::BYTE8_XOR_LE; +using util::WORD2_XOR_BE; +using util::WORD2_XOR_LE; +using util::DWORD_XOR_BE; +using util::DWORD_XOR_LE; -// genf is a generic function pointer; cast function pointers to this instead of void * -typedef void genf(void); +// input ports support up to 32 bits each +typedef u32 ioport_value; // pen_t is used to represent pixel values in bitmaps typedef u32 pen_t; -// stream_sample_t is used to represent a single sample in a sound stream -typedef s32 stream_sample_t; - //************************************************************************** @@ -152,21 +172,9 @@ union PAIR64 // COMMON CONSTANTS //************************************************************************** -// constants for expression endianness -enum endianness_t -{ - ENDIANNESS_LITTLE, - ENDIANNESS_BIG -}; - -extern const char *const endianness_names[2]; - -// declare native endianness to be one or the other -#ifdef LSB_FIRST -const endianness_t ENDIANNESS_NATIVE = ENDIANNESS_LITTLE; -#else -const endianness_t ENDIANNESS_NATIVE = ENDIANNESS_BIG; -#endif +constexpr endianness_t ENDIANNESS_LITTLE = util::endianness::little; +constexpr endianness_t ENDIANNESS_BIG = util::endianness::big; +constexpr endianness_t ENDIANNESS_NATIVE = util::endianness::native; // M_PI is not part of the C/C++ standards and is not present on @@ -176,15 +184,19 @@ const endianness_t ENDIANNESS_NATIVE = ENDIANNESS_BIG; #endif -// orientation of bitmaps -constexpr int ORIENTATION_FLIP_X = 0x0001; // mirror everything in the X direction -constexpr int ORIENTATION_FLIP_Y = 0x0002; // mirror everything in the Y direction -constexpr int ORIENTATION_SWAP_XY = 0x0004; // mirror along the top-left/bottom-right diagonal +/// \name Image orientation flags +/// \{ + +constexpr int ORIENTATION_FLIP_X = 0x0001; ///< Mirror horizontally (in the X direction) +constexpr int ORIENTATION_FLIP_Y = 0x0002; ///< Mirror vertically (in the Y direction) +constexpr int ORIENTATION_SWAP_XY = 0x0004; ///< Mirror along the top-left/bottom-right diagonal constexpr int ROT0 = 0; -constexpr int ROT90 = ORIENTATION_SWAP_XY | ORIENTATION_FLIP_X; // rotate clockwise 90 degrees -constexpr int ROT180 = ORIENTATION_FLIP_X | ORIENTATION_FLIP_Y; // rotate 180 degrees -constexpr int ROT270 = ORIENTATION_SWAP_XY | ORIENTATION_FLIP_Y; // rotate counter-clockwise 90 degrees +constexpr int ROT90 = ORIENTATION_SWAP_XY | ORIENTATION_FLIP_X; ///< Rotate 90 degrees clockwise +constexpr int ROT180 = ORIENTATION_FLIP_X | ORIENTATION_FLIP_Y; ///< Rotate 180 degrees +constexpr int ROT270 = ORIENTATION_SWAP_XY | ORIENTATION_FLIP_Y; ///< Rotate 90 degrees anti-clockwise (270 degrees clockwise) + +/// \} @@ -192,27 +204,6 @@ constexpr int ROT270 = ORIENTATION_SWAP_XY | ORIENTATION_FLIP_Y; // COMMON MACROS //************************************************************************** -// macro for defining a copy constructor and assignment operator to prevent copying -#define DISABLE_COPYING(TYPE) \ - TYPE(const TYPE &) = delete; \ - TYPE &operator=(const TYPE &) = delete - -// macro for declaring enumeration operators that increment/decrement like plain old C -#define DECLARE_ENUM_INCDEC_OPERATORS(TYPE) \ -inline TYPE &operator++(TYPE &value) { return value = TYPE(std::underlying_type_t<TYPE>(value) + 1); } \ -inline TYPE &operator--(TYPE &value) { return value = TYPE(std::underlying_type_t<TYPE>(value) - 1); } \ -inline TYPE operator++(TYPE &value, int) { TYPE const old(value); ++value; return old; } \ -inline TYPE operator--(TYPE &value, int) { TYPE const old(value); --value; return old; } - -// macro for declaring bitwise operators for an enumerated type -#define DECLARE_ENUM_BITWISE_OPERATORS(TYPE) \ -constexpr TYPE operator~(TYPE value) { return TYPE(~std::underlying_type_t<TYPE>(value)); } \ -constexpr TYPE operator&(TYPE a, TYPE b) { return TYPE(std::underlying_type_t<TYPE>(a) & std::underlying_type_t<TYPE>(b)); } \ -constexpr TYPE operator|(TYPE a, TYPE b) { return TYPE(std::underlying_type_t<TYPE>(a) | std::underlying_type_t<TYPE>(b)); } \ -inline TYPE &operator&=(TYPE &a, TYPE b) { return a = a & b; } \ -inline TYPE &operator|=(TYPE &a, TYPE b) { return a = a | b; } - - // this macro passes an item followed by a string version of itself as two consecutive parameters #define NAME(x) x, #x @@ -225,16 +216,6 @@ template <typename T> constexpr auto RADIAN_TO_DEGREE(T const &x) { return (180. template <typename T> constexpr auto DEGREE_TO_RADIAN(T const &x) { return (M_PI / 180.0) * x; } -// endian-based value: first value is if 'endian' is little-endian, second is if 'endian' is big-endian -#define ENDIAN_VALUE_LE_BE(endian,leval,beval) (((endian) == ENDIANNESS_LITTLE) ? (leval) : (beval)) - -// endian-based value: first value is if native endianness is little-endian, second is if native is big-endian -#define NATIVE_ENDIAN_VALUE_LE_BE(leval,beval) ENDIAN_VALUE_LE_BE(ENDIANNESS_NATIVE, leval, beval) - -// endian-based value: first value is if 'endian' matches native, second is if 'endian' doesn't match native -#define ENDIAN_VALUE_NE_NNE(endian,neval,nneval) (((endian) == ENDIANNESS_NATIVE) ? (neval) : (nneval)) - - //************************************************************************** // EXCEPTION CLASSES //************************************************************************** @@ -247,17 +228,28 @@ class emu_exception : public std::exception { }; class emu_fatalerror : public emu_exception { public: - emu_fatalerror(const char *format, ...) ATTR_PRINTF(2,3); - emu_fatalerror(const char *format, va_list ap); - emu_fatalerror(int _exitcode, const char *format, ...) ATTR_PRINTF(3,4); - emu_fatalerror(int _exitcode, const char *format, va_list ap); - - const char *string() const { return text; } - int exitcode() const { return code; } + emu_fatalerror(emu_fatalerror const &) = default; + emu_fatalerror(emu_fatalerror &&) = default; + emu_fatalerror(util::format_argument_pack<char> const &args); + emu_fatalerror(int _exitcode, util::format_argument_pack<char> const &args); + + template <typename Format, typename... Params, typename = std::enable_if_t<!std::is_base_of_v<emu_fatalerror, std::remove_reference_t<Format> > > > + emu_fatalerror(Format &&fmt, Params &&... args) + : emu_fatalerror(static_cast<util::format_argument_pack<char> const &>(util::make_format_argument_pack(std::forward<Format>(fmt), std::forward<Params>(args)...))) + { + } + template <typename Format, typename... Params> + emu_fatalerror(int _exitcode, Format &&fmt, Params &&... args) + : emu_fatalerror(_exitcode, static_cast<util::format_argument_pack<char> const &>(util::make_format_argument_pack(std::forward<Format>(fmt), std::forward<Params>(args)...))) + { + } + + virtual char const *what() const noexcept override { return m_text.c_str(); } + int exitcode() const noexcept { return m_code; } private: - char text[1024]; - int code; + std::string m_text; + int m_code; }; class tag_add_exception @@ -269,26 +261,28 @@ private: std::string m_tag; }; + //************************************************************************** // CASTING TEMPLATES //************************************************************************** -void report_bad_cast(const std::type_info &src_type, const std::type_info &dst_type); -void report_bad_device_cast(const device_t *dev, const std::type_info &src_type, const std::type_info &dst_type); - -template <typename Dest, typename Source> -inline std::enable_if_t<std::is_base_of<device_t, Source>::value> report_bad_cast(Source *const src) -{ - if (src) report_bad_device_cast(src, typeid(Source), typeid(Dest)); - else report_bad_cast(typeid(Source), typeid(Dest)); -} +[[noreturn]] void report_bad_cast(const std::type_info &src_type, const std::type_info &dst_type); +[[noreturn]] void report_bad_device_cast(const device_t *dev, const std::type_info &src_type, const std::type_info &dst_type); template <typename Dest, typename Source> -inline std::enable_if_t<!std::is_base_of<device_t, Source>::value> report_bad_cast(Source *const src) +inline void report_bad_cast(Source *src) { - device_t const *dev(dynamic_cast<device_t const *>(src)); - if (dev) report_bad_device_cast(dev, typeid(Source), typeid(Dest)); - else report_bad_cast(typeid(Source), typeid(Dest)); + if constexpr (std::is_base_of_v<device_t, Source>) + { + if (src) report_bad_device_cast(src, typeid(Source), typeid(Dest)); + else report_bad_cast(typeid(Source), typeid(Dest)); + } + else + { + device_t const *dev(dynamic_cast<device_t const *>(src)); + if (dev) report_bad_device_cast(dev, typeid(Source), typeid(Dest)); + else report_bad_cast(typeid(Source), typeid(Dest)); + } } // template function for casting from a base class to a derived class that is checked @@ -296,7 +290,7 @@ inline std::enable_if_t<!std::is_base_of<device_t, Source>::value> report_bad_ca template <typename Dest, typename Source> inline Dest downcast(Source *src) { -#if defined(MAME_DEBUG) && !defined(MAME_DEBUG_FAST) +#if defined(MAME_DEBUG) Dest const chk(dynamic_cast<Dest>(src)); if (chk != src) report_bad_cast<std::remove_pointer_t<Dest>, Source>(src); #endif @@ -306,7 +300,7 @@ inline Dest downcast(Source *src) template<class Dest, class Source> inline Dest downcast(Source &src) { -#if defined(MAME_DEBUG) && !defined(MAME_DEBUG_FAST) +#if defined(MAME_DEBUG) std::remove_reference_t<Dest> *const chk(dynamic_cast<std::remove_reference_t<Dest> *>(&src)); if (chk != &src) report_bad_cast<std::remove_reference_t<Dest>, Source>(&src); #endif @@ -316,15 +310,15 @@ inline Dest downcast(Source &src) //************************************************************************** -// FUNCTION PROTOTYPES +// INLINE FUNCTIONS //************************************************************************** -[[noreturn]] void fatalerror(const char *format, ...) ATTR_PRINTF(1,2); -[[noreturn]] void fatalerror_exitcode(running_machine &machine, int exitcode, const char *format, ...) ATTR_PRINTF(3,4); +template <typename... T> +[[noreturn]] inline void fatalerror(T &&... args) +{ + throw emu_fatalerror(std::forward<T>(args)...); +} -//************************************************************************** -// INLINE FUNCTIONS -//************************************************************************** // convert a series of 32 bits into a float inline float u2f(u32 v) @@ -373,4 +367,11 @@ inline u64 d2u(double d) return u.vv; } -#endif /* MAME_EMU_EMUCORE_H */ + +//************************************************************************** +// USEFUL UTILITIES +//************************************************************************** + +using util::make_unique_clear; + +#endif // MAME_EMU_EMUCORE_H diff --git a/src/emu/emufwd.h b/src/emu/emufwd.h index cb0af700c64..cf14dcb8163 100644 --- a/src/emu/emufwd.h +++ b/src/emu/emufwd.h @@ -14,6 +14,8 @@ #pragma once +#include "utilfwd.h" + #include <type_traits> @@ -40,27 +42,6 @@ class osd_interface; //---------------------------------- -// lib/util -//---------------------------------- - -// declared in aviio.h -class avi_file; - -// declared in chd.h -class chd_file; - -// declared in unzip.h -namespace util { class archive_file; } - -// declared in wavwrite.h -struct wav_file; - -// declared in xmlfile.h -namespace util { namespace xml { class data_node; } } - - - -//---------------------------------- // emu //---------------------------------- @@ -72,7 +53,8 @@ class address_map_entry; class bookkeeping_manager; // declared in config.h -enum class config_type; +enum class config_type : int; +enum class config_level : int; class configuration_manager; // declared in crsshair.h @@ -81,7 +63,7 @@ class crosshair_manager; // declared in debug/debugcmd.h class debugger_commands; -// declared in debug/debugcmd.h +// declared in debug/debugcon.h class debugger_console; // declared in debug/debugcpu.h @@ -96,6 +78,12 @@ class debug_view_manager; class parsed_expression; class symbol_table; +// declared in debug/points.h +class debug_breakpoint; +class debug_watchpoint; +class debug_registerpoint; +class debug_exceptionpoint; + // declared in debugger.h class debugger_manager; @@ -104,6 +92,7 @@ class devcb_base; template <typename Input, std::make_unsigned_t<Input> DefaultMask> class devcb_write; // declared in devfind.h +class device_resolver_base; class finder_base; template <class DeviceClass, bool Required> class device_finder; @@ -126,6 +115,9 @@ class device_image_interface; // declared in dimemory.h class device_memory_interface; +// declared in dinetwork.h +class device_network_interface; + // declared in dipalette.h class device_palette_interface; @@ -140,16 +132,21 @@ class driver_device; // declared in emumem.h class address_space; -template<int Width, int AddrShift, int Endian> class memory_access_cache; class memory_bank; -class memory_block; class memory_manager; class memory_region; class memory_share; +class memory_view; // declared in emuopts.h class emu_options; +// declared in fileio.h +class emu_file; + +// declared in http.h +class http_manager; + // declared in gamedrv.h class game_driver; @@ -178,9 +175,13 @@ struct ioport_port_live; class running_machine; // declared in mconfig.h -namespace emu { namespace detail { struct machine_config_replace; } } +namespace emu::detail { class machine_config_replace; } +struct internal_layout; class machine_config; +// declared in main.h +class machine_manager; + // declared in natkeyboard.h class natural_keyboard; @@ -191,16 +192,23 @@ class network_manager; class output_manager; // declared in render.h -class layout_element; -class layout_view; class render_container; class render_manager; class render_target; class render_texture; +// declared in rendertypes.h +struct render_bounds; + // declared in rendfont.h class render_font; +// declared in rendlay.h +class layout_element; +class layout_view_item; +class layout_view; +class layout_file; + // declared in romentry.h class rom_entry; @@ -227,7 +235,9 @@ class sound_manager; class sound_stream; // declared in speaker.h +class sound_io_device; class speaker_device; +class microphone_device; // declared in tilemap.h class tilemap_device; diff --git a/src/emu/emumem.cpp b/src/emu/emumem.cpp index fc38a61a276..679c2374e78 100644 --- a/src/emu/emumem.cpp +++ b/src/emu/emumem.cpp @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles,Olivier Galibert /*************************************************************************** - emumem.c + emumem.cpp Functions which handle device memory access. @@ -30,17 +30,39 @@ // DEBUGGING //************************************************************************** -#define VERBOSE (0) +#define VERBOSE 0 -#define VPRINTF(x) do { if (VERBOSE) printf x; } while (0) +#if VERBOSE +template <typename Format, typename... Params> static void VPRINTF(Format &&fmt, Params &&...args) +{ + util::stream_format(std::cerr, std::forward<Format>(fmt), std::forward<Params>(args)...); +} +#else +template <typename Format, typename... Params> static void VPRINTF(Format &&, Params &&...) {} +#endif -#define VALIDATE_REFCOUNTS 1 +#define VALIDATE_REFCOUNTS 0 + +offs_t handler_entry::dispatch_entry(offs_t address) const +{ + fatalerror("dispatch_entry called on non-dispatching class\n"); +} void handler_entry::dump_map(std::vector<memory_entry> &map) const { fatalerror("dump_map called on non-dispatching class\n"); } +void handler_entry::select_a(int slot) +{ + fatalerror("select_a called on non-view\n"); +} + +void handler_entry::select_u(int slot) +{ + fatalerror("select_u called on non-view\n"); +} + void handler_entry::reflist::add(const handler_entry *entry) { refcounts[entry]++; @@ -80,638 +102,156 @@ void handler_entry::enumerate_references(handler_entry::reflist &refs) const { } -template<int Width, int AddrShift, int Endian> void handler_entry_read<Width, AddrShift, Endian>::populate_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_read<Width, AddrShift, Endian> *handler) +template<int Width, int AddrShift> const handler_entry_read<Width, AddrShift> *const *handler_entry_read<Width, AddrShift>::get_dispatch() const +{ + fatalerror("get_dispatch called on non-dispatching class\n"); +} + +template<int Width, int AddrShift> void handler_entry_read<Width, AddrShift>::populate_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_read<Width, AddrShift> *handler) { fatalerror("populate called on non-dispatching class\n"); } -template<int Width, int AddrShift, int Endian> void handler_entry_read<Width, AddrShift, Endian>::populate_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_read<Width, AddrShift, Endian> *handler) +template<int Width, int AddrShift> void handler_entry_read<Width, AddrShift>::populate_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_read<Width, AddrShift> *handler) { fatalerror("populate called on non-dispatching class\n"); } -template<int Width, int AddrShift, int Endian> void handler_entry_read<Width, AddrShift, Endian>::populate_mismatched_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 rkey, std::vector<mapping> &mappings) +template<int Width, int AddrShift> void handler_entry_read<Width, AddrShift>::populate_mismatched_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, const memory_units_descriptor<Width, AddrShift> &descriptor, u8 rkey, std::vector<mapping> &mappings) { fatalerror("populate_mismatched called on non-dispatching class\n"); } -template<int Width, int AddrShift, int Endian> void handler_entry_read<Width, AddrShift, Endian>::populate_mismatched_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, std::vector<mapping> &mappings) +template<int Width, int AddrShift> void handler_entry_read<Width, AddrShift>::populate_mismatched_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, const memory_units_descriptor<Width, AddrShift> &descriptor, std::vector<mapping> &mappings) { fatalerror("populate_mismatched called on non-dispatching class\n"); } -template<int Width, int AddrShift, int Endian> void handler_entry_read<Width, AddrShift, Endian>::populate_passthrough_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_read_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings) +template<int Width, int AddrShift> void handler_entry_read<Width, AddrShift>::populate_passthrough_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_read_passthrough<Width, AddrShift> *handler, std::vector<mapping> &mappings) { fatalerror("populate_passthrough called on non-dispatching class\n"); } -template<int Width, int AddrShift, int Endian> void handler_entry_read<Width, AddrShift, Endian>::populate_passthrough_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_read_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings) +template<int Width, int AddrShift> void handler_entry_read<Width, AddrShift>::populate_passthrough_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_read_passthrough<Width, AddrShift> *handler, std::vector<mapping> &mappings) { fatalerror("populate_passthrough called on non-dispatching class\n"); } -template<int Width, int AddrShift, int Endian> void handler_entry_read<Width, AddrShift, Endian>::lookup(offs_t address, offs_t &start, offs_t &end, handler_entry_read<Width, AddrShift, Endian> *&handler) const +template<int Width, int AddrShift> void handler_entry_read<Width, AddrShift>::lookup(offs_t address, offs_t &start, offs_t &end, handler_entry_read<Width, AddrShift> *&handler) const { fatalerror("lookup called on non-dispatching class\n"); } -template<int Width, int AddrShift, int Endian> void *handler_entry_read<Width, AddrShift, Endian>::get_ptr(offs_t offset) const +template<int Width, int AddrShift> void *handler_entry_read<Width, AddrShift>::get_ptr(offs_t offset) const { return nullptr; } -template<int Width, int AddrShift, int Endian> void handler_entry_read<Width, AddrShift, Endian>::detach(const std::unordered_set<handler_entry *> &handlers) +template<int Width, int AddrShift> handler_entry_read<Width, AddrShift> *handler_entry_read<Width, AddrShift>::dup() +{ + ref(); + return this; +} + +template<int Width, int AddrShift> void handler_entry_read<Width, AddrShift>::detach(const std::unordered_set<handler_entry *> &handlers) { fatalerror("detach called on non-dispatching class\n"); } +template<int Width, int AddrShift> void handler_entry_read<Width, AddrShift>::init_handlers(offs_t start_entry, offs_t end_entry, u32 lowbits, offs_t ostart, offs_t oend, handler_entry_read<Width, AddrShift> **dispatch, handler_entry::range *ranges) +{ + fatalerror("init_handlers called on non-view class\n"); +} + -template<int Width, int AddrShift, int Endian> void handler_entry_write<Width, AddrShift, Endian>::populate_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_write<Width, AddrShift, Endian> *handler) +template<int Width, int AddrShift> const handler_entry_write<Width, AddrShift> *const *handler_entry_write<Width, AddrShift>::get_dispatch() const +{ + fatalerror("get_dispatch called on non-dispatching class\n"); +} + +template<int Width, int AddrShift> void handler_entry_write<Width, AddrShift>::populate_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_write<Width, AddrShift> *handler) { fatalerror("populate called on non-dispatching class\n"); } -template<int Width, int AddrShift, int Endian> void handler_entry_write<Width, AddrShift, Endian>::populate_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_write<Width, AddrShift, Endian> *handler) +template<int Width, int AddrShift> void handler_entry_write<Width, AddrShift>::populate_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_write<Width, AddrShift> *handler) { fatalerror("populate called on non-dispatching class\n"); } -template<int Width, int AddrShift, int Endian> void handler_entry_write<Width, AddrShift, Endian>::populate_mismatched_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 rkey, std::vector<mapping> &mappings) +template<int Width, int AddrShift> void handler_entry_write<Width, AddrShift>::populate_mismatched_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, const memory_units_descriptor<Width, AddrShift> &descriptor, u8 rkey, std::vector<mapping> &mappings) { fatalerror("populate_mismatched called on non-dispatching class\n"); } -template<int Width, int AddrShift, int Endian> void handler_entry_write<Width, AddrShift, Endian>::populate_mismatched_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, std::vector<mapping> &mappings) +template<int Width, int AddrShift> void handler_entry_write<Width, AddrShift>::populate_mismatched_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, const memory_units_descriptor<Width, AddrShift> &descriptor, std::vector<mapping> &mappings) { fatalerror("populate_mismatched called on non-dispatching class\n"); } -template<int Width, int AddrShift, int Endian> void handler_entry_write<Width, AddrShift, Endian>::populate_passthrough_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_write_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings) +template<int Width, int AddrShift> void handler_entry_write<Width, AddrShift>::populate_passthrough_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_write_passthrough<Width, AddrShift> *handler, std::vector<mapping> &mappings) { fatalerror("populate_passthrough called on non-dispatching class\n"); } -template<int Width, int AddrShift, int Endian> void handler_entry_write<Width, AddrShift, Endian>::populate_passthrough_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_write_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings) +template<int Width, int AddrShift> void handler_entry_write<Width, AddrShift>::populate_passthrough_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_write_passthrough<Width, AddrShift> *handler, std::vector<mapping> &mappings) { fatalerror("populate_passthrough called on non-dispatching class\n"); } -template<int Width, int AddrShift, int Endian> void handler_entry_write<Width, AddrShift, Endian>::lookup(offs_t address, offs_t &start, offs_t &end, handler_entry_write<Width, AddrShift, Endian> *&handler) const +template<int Width, int AddrShift> void handler_entry_write<Width, AddrShift>::lookup(offs_t address, offs_t &start, offs_t &end, handler_entry_write<Width, AddrShift> *&handler) const { fatalerror("lookup called on non-dispatching class\n"); } -template<int Width, int AddrShift, int Endian> void *handler_entry_write<Width, AddrShift, Endian>::get_ptr(offs_t offset) const +template<int Width, int AddrShift> void *handler_entry_write<Width, AddrShift>::get_ptr(offs_t offset) const { return nullptr; } -template<int Width, int AddrShift, int Endian> void handler_entry_write<Width, AddrShift, Endian>::detach(const std::unordered_set<handler_entry *> &handlers) +template<int Width, int AddrShift> handler_entry_write<Width, AddrShift> *handler_entry_write<Width, AddrShift>::dup() { - fatalerror("detach called on non-dispatching class\n"); + ref(); + return this; } - - -/*------------------------------------------------- - core_i64_hex_format - i64 format printf helper --------------------------------------------------*/ - -static char *core_i64_hex_format(u64 value, u8 mindigits) +template<int Width, int AddrShift> void handler_entry_write<Width, AddrShift>::detach(const std::unordered_set<handler_entry *> &handlers) { - static char buffer[16][64]; - // TODO: this can overflow - e.g. when a lot of unmapped writes are logged - static int index; - char *bufbase = &buffer[index++ % 16][0]; - char *bufptr = bufbase; - s8 curdigit; - - for (curdigit = 15; curdigit >= 0; curdigit--) - { - int nibble = (value >> (curdigit * 4)) & 0xf; - if (nibble != 0 || curdigit < mindigits) - { - mindigits = curdigit; - *bufptr++ = "0123456789ABCDEF"[nibble]; - } - } - if (bufptr == bufbase) - *bufptr++ = '0'; - *bufptr = 0; - - return bufbase; + fatalerror("detach called on non-dispatching class\n"); } - - -//************************************************************************** -// CONSTANTS -//************************************************************************** - -// other address map constants -const int MEMORY_BLOCK_CHUNK = 65536; // minimum chunk size of allocated memory blocks - - -//************************************************************************** -// TYPE DEFINITIONS -//************************************************************************** - -// ======================> address_space_specific - -// this is a derived class of address_space with specific width, endianness, and table size -template<int Width, int AddrShift, endianness_t Endian> -class address_space_specific : public address_space -{ - using uX = typename emu::detail::handler_entry_size<Width>::uX; - using NativeType = uX; - using this_type = address_space_specific<Width, AddrShift, Endian>; - - // constants describing the native size - static constexpr u32 NATIVE_BYTES = 1 << Width; - static constexpr u32 NATIVE_STEP = AddrShift >= 0 ? NATIVE_BYTES << iabs(AddrShift) : NATIVE_BYTES >> iabs(AddrShift); - static constexpr u32 NATIVE_MASK = NATIVE_STEP - 1; - static constexpr u32 NATIVE_BITS = 8 * NATIVE_BYTES; - - static constexpr offs_t offset_to_byte(offs_t offset) { return AddrShift < 0 ? offset << iabs(AddrShift) : offset >> iabs(AddrShift); } - -public: - std::string get_handler_string(read_or_write readorwrite, offs_t byteaddress) const override; - void dump_maps(std::vector<memory_entry> &read_map, std::vector<memory_entry> &write_map) const override; - - void unmap_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, read_or_write readorwrite, bool quiet) override; - void install_ram_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, read_or_write readorwrite, void *baseptr) override; - void install_bank_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string rtag, std::string wtag) override; - void install_bank_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, memory_bank *rbank, memory_bank *wbank) override; - void install_readwrite_port(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string rtag, std::string wtag) override; - void install_device_delegate(offs_t addrstart, offs_t addrend, device_t &device, address_map_constructor &map, u64 unitmask = 0, int cswidth = 0) override; - - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override; - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8_delegate rhandler, write8_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override; - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16_delegate rhandler, write16_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override; - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32_delegate rhandler, write32_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override; - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64_delegate rhandler, write64_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8m_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override; - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8m_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8m_delegate rhandler, write8m_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16m_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override; - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16m_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16m_delegate rhandler, write16m_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32m_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override; - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32m_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32m_delegate rhandler, write32m_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64m_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override; - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64m_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64m_delegate rhandler, write64m_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8s_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override; - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8s_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8s_delegate rhandler, write8s_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16s_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override; - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16s_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16s_delegate rhandler, write16s_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32s_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override; - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32s_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32s_delegate rhandler, write32s_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64s_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override; - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64s_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64s_delegate rhandler, write64s_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override; - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8sm_delegate rhandler, write8sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override; - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16sm_delegate rhandler, write16sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override; - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32sm_delegate rhandler, write32sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override; - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64sm_delegate rhandler, write64sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override; - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8mo_delegate rhandler, write8mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override; - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16mo_delegate rhandler, write16mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override; - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32mo_delegate rhandler, write32mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override; - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64mo_delegate rhandler, write64mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override; - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8smo_delegate rhandler, write8smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override; - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16smo_delegate rhandler, write16smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override; - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32smo_delegate rhandler, write32smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override; - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64smo_delegate rhandler, write64smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override; - - using address_space::install_read_tap; - using address_space::install_write_tap; - using address_space::install_readwrite_tap; - - virtual memory_passthrough_handler *install_read_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, uX &data, uX mem_mask)> tap, memory_passthrough_handler *mph) override; - virtual memory_passthrough_handler *install_write_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, uX &data, uX mem_mask)> tap, memory_passthrough_handler *mph) override; - virtual memory_passthrough_handler *install_readwrite_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, uX &data, uX mem_mask)> tapr, std::function<void (offs_t offset, uX &data, uX mem_mask)> tapw, memory_passthrough_handler *mph) override; - - // construction/destruction - address_space_specific(memory_manager &manager, device_memory_interface &memory, int spacenum, int address_width) - : address_space(manager, memory, spacenum) - { - m_unmap_r = new handler_entry_read_unmapped <Width, AddrShift, Endian>(this); - m_unmap_w = new handler_entry_write_unmapped<Width, AddrShift, Endian>(this); - m_nop_r = new handler_entry_read_nop <Width, AddrShift, Endian>(this); - m_nop_w = new handler_entry_write_nop<Width, AddrShift, Endian>(this); - - handler_entry::range r{ 0, 0xffffffff >> (32 - address_width) }; - - switch (address_width) { - case 1: m_root_read = new handler_entry_read_dispatch< std::max(1, Width), Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch< std::max(1, Width), Width, AddrShift, Endian>(this, r, nullptr); break; - case 2: m_root_read = new handler_entry_read_dispatch< std::max(2, Width), Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch< std::max(2, Width), Width, AddrShift, Endian>(this, r, nullptr); break; - case 3: m_root_read = new handler_entry_read_dispatch< std::max(3, Width), Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch< std::max(3, Width), Width, AddrShift, Endian>(this, r, nullptr); break; - case 4: m_root_read = new handler_entry_read_dispatch< 4, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch< 4, Width, AddrShift, Endian>(this, r, nullptr); break; - case 5: m_root_read = new handler_entry_read_dispatch< 5, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch< 5, Width, AddrShift, Endian>(this, r, nullptr); break; - case 6: m_root_read = new handler_entry_read_dispatch< 6, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch< 6, Width, AddrShift, Endian>(this, r, nullptr); break; - case 7: m_root_read = new handler_entry_read_dispatch< 7, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch< 7, Width, AddrShift, Endian>(this, r, nullptr); break; - case 8: m_root_read = new handler_entry_read_dispatch< 8, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch< 8, Width, AddrShift, Endian>(this, r, nullptr); break; - case 9: m_root_read = new handler_entry_read_dispatch< 9, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch< 9, Width, AddrShift, Endian>(this, r, nullptr); break; - case 10: m_root_read = new handler_entry_read_dispatch<10, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<10, Width, AddrShift, Endian>(this, r, nullptr); break; - case 11: m_root_read = new handler_entry_read_dispatch<11, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<11, Width, AddrShift, Endian>(this, r, nullptr); break; - case 12: m_root_read = new handler_entry_read_dispatch<12, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<12, Width, AddrShift, Endian>(this, r, nullptr); break; - case 13: m_root_read = new handler_entry_read_dispatch<13, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<13, Width, AddrShift, Endian>(this, r, nullptr); break; - case 14: m_root_read = new handler_entry_read_dispatch<14, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<14, Width, AddrShift, Endian>(this, r, nullptr); break; - case 15: m_root_read = new handler_entry_read_dispatch<15, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<15, Width, AddrShift, Endian>(this, r, nullptr); break; - case 16: m_root_read = new handler_entry_read_dispatch<16, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<16, Width, AddrShift, Endian>(this, r, nullptr); break; - case 17: m_root_read = new handler_entry_read_dispatch<17, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<17, Width, AddrShift, Endian>(this, r, nullptr); break; - case 18: m_root_read = new handler_entry_read_dispatch<18, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<18, Width, AddrShift, Endian>(this, r, nullptr); break; - case 19: m_root_read = new handler_entry_read_dispatch<19, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<19, Width, AddrShift, Endian>(this, r, nullptr); break; - case 20: m_root_read = new handler_entry_read_dispatch<20, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<20, Width, AddrShift, Endian>(this, r, nullptr); break; - case 21: m_root_read = new handler_entry_read_dispatch<21, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<21, Width, AddrShift, Endian>(this, r, nullptr); break; - case 22: m_root_read = new handler_entry_read_dispatch<22, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<22, Width, AddrShift, Endian>(this, r, nullptr); break; - case 23: m_root_read = new handler_entry_read_dispatch<23, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<23, Width, AddrShift, Endian>(this, r, nullptr); break; - case 24: m_root_read = new handler_entry_read_dispatch<24, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<24, Width, AddrShift, Endian>(this, r, nullptr); break; - case 25: m_root_read = new handler_entry_read_dispatch<25, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<25, Width, AddrShift, Endian>(this, r, nullptr); break; - case 26: m_root_read = new handler_entry_read_dispatch<26, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<26, Width, AddrShift, Endian>(this, r, nullptr); break; - case 27: m_root_read = new handler_entry_read_dispatch<27, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<27, Width, AddrShift, Endian>(this, r, nullptr); break; - case 28: m_root_read = new handler_entry_read_dispatch<28, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<28, Width, AddrShift, Endian>(this, r, nullptr); break; - case 29: m_root_read = new handler_entry_read_dispatch<29, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<29, Width, AddrShift, Endian>(this, r, nullptr); break; - case 30: m_root_read = new handler_entry_read_dispatch<30, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<30, Width, AddrShift, Endian>(this, r, nullptr); break; - case 31: m_root_read = new handler_entry_read_dispatch<31, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<31, Width, AddrShift, Endian>(this, r, nullptr); break; - case 32: m_root_read = new handler_entry_read_dispatch<32, Width, AddrShift, Endian>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<32, Width, AddrShift, Endian>(this, r, nullptr); break; - default: fatalerror("Unhandled address bus width %d\n", address_width); - } - } - - void *create_cache() override { - return new memory_access_cache<Width, AddrShift, Endian>(*this, m_root_read, m_root_write); - } - - void delayed_ref(handler_entry *e) { - e->ref(); - m_delayed_unrefs.insert(e); - } - - void delayed_unref(handler_entry *e) { - m_delayed_unrefs.erase(m_delayed_unrefs.find(e)); - e->unref(); - } - - void validate_reference_counts() const override { - handler_entry::reflist refs; - refs.add(m_root_read); - refs.add(m_root_write); - refs.add(m_unmap_r); - refs.add(m_unmap_w); - refs.add(m_nop_r); - refs.add(m_nop_w); - for(handler_entry *e : m_delayed_unrefs) - refs.add(e); - refs.propagate(); - refs.check(); - } - - virtual void remove_passthrough(std::unordered_set<handler_entry *> &handlers) override { - invalidate_caches(read_or_write::READWRITE); - m_root_read->detach(handlers); - m_root_write->detach(handlers); - } - - // generate accessor table - virtual void accessors(data_accessors &accessors) const override - { - accessors.read_byte = reinterpret_cast<u8 (*)(address_space &, offs_t)>(&read_byte_static); - accessors.read_word = reinterpret_cast<u16 (*)(address_space &, offs_t)>(&read_word_static); - accessors.read_word_masked = reinterpret_cast<u16 (*)(address_space &, offs_t, u16)>(&read_word_masked_static); - accessors.read_dword = reinterpret_cast<u32 (*)(address_space &, offs_t)>(&read_dword_static); - accessors.read_dword_masked = reinterpret_cast<u32 (*)(address_space &, offs_t, u32)>(&read_dword_masked_static); - accessors.read_qword = reinterpret_cast<u64 (*)(address_space &, offs_t)>(&read_qword_static); - accessors.read_qword_masked = reinterpret_cast<u64 (*)(address_space &, offs_t, u64)>(&read_qword_masked_static); - accessors.write_byte = reinterpret_cast<void (*)(address_space &, offs_t, u8)>(&write_byte_static); - accessors.write_word = reinterpret_cast<void (*)(address_space &, offs_t, u16)>(&write_word_static); - accessors.write_word_masked = reinterpret_cast<void (*)(address_space &, offs_t, u16, u16)>(&write_word_masked_static); - accessors.write_dword = reinterpret_cast<void (*)(address_space &, offs_t, u32)>(&write_dword_static); - accessors.write_dword_masked = reinterpret_cast<void (*)(address_space &, offs_t, u32, u32)>(&write_dword_masked_static); - accessors.write_qword = reinterpret_cast<void (*)(address_space &, offs_t, u64)>(&write_qword_static); - accessors.write_qword_masked = reinterpret_cast<void (*)(address_space &, offs_t, u64, u64)>(&write_qword_masked_static); - } - - // return a pointer to the read bank, or nullptr if none - virtual void *get_read_ptr(offs_t address) const override - { - return m_root_read->get_ptr(address); - } - - // return a pointer to the write bank, or nullptr if none - virtual void *get_write_ptr(offs_t address) const override - { - return m_root_write->get_ptr(address); - } - - // native read - NativeType read_native(offs_t offset, NativeType mask) - { - g_profiler.start(PROFILER_MEMREAD); - - uX result = m_root_read->read(offset, mask); - - g_profiler.stop(); - return result; - } - - // mask-less native read - NativeType read_native(offs_t offset) - { - g_profiler.start(PROFILER_MEMREAD); - - uX result = m_root_read->read(offset, uX(0xffffffffffffffffU)); - - g_profiler.stop(); - return result; - } - - // native write - void write_native(offs_t offset, NativeType data, NativeType mask) - { - g_profiler.start(PROFILER_MEMWRITE); - - m_root_write->write(offset, data, mask); - - g_profiler.stop(); - } - - // mask-less native write - void write_native(offs_t offset, NativeType data) - { - g_profiler.start(PROFILER_MEMWRITE); - - m_root_write->write(offset, data, uX(0xffffffffffffffffU)); - - g_profiler.stop(); - } - - // virtual access to these functions - u8 read_byte(offs_t address) override { address &= m_addrmask; return Width == 0 ? read_native(address & ~NATIVE_MASK) : memory_read_generic<Width, AddrShift, Endian, 0, true>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xff); } - u16 read_word(offs_t address) override { address &= m_addrmask; return Width == 1 ? read_native(address & ~NATIVE_MASK) : memory_read_generic<Width, AddrShift, Endian, 1, true>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffff); } - u16 read_word(offs_t address, u16 mask) override { address &= m_addrmask; return memory_read_generic<Width, AddrShift, Endian, 1, true>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); } - u16 read_word_unaligned(offs_t address) override { address &= m_addrmask; return memory_read_generic<Width, AddrShift, Endian, 1, false>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffff); } - u16 read_word_unaligned(offs_t address, u16 mask) override { address &= m_addrmask; return memory_read_generic<Width, AddrShift, Endian, 1, false>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); } - u32 read_dword(offs_t address) override { address &= m_addrmask; return Width == 2 ? read_native(address & ~NATIVE_MASK) : memory_read_generic<Width, AddrShift, Endian, 2, true>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffffffff); } - u32 read_dword(offs_t address, u32 mask) override { address &= m_addrmask; return memory_read_generic<Width, AddrShift, Endian, 2, true>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); } - u32 read_dword_unaligned(offs_t address) override { address &= m_addrmask; return memory_read_generic<Width, AddrShift, Endian, 2, false>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffffffff); } - u32 read_dword_unaligned(offs_t address, u32 mask) override { address &= m_addrmask; return memory_read_generic<Width, AddrShift, Endian, 2, false>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); } - u64 read_qword(offs_t address) override { address &= m_addrmask; return Width == 3 ? read_native(address & ~NATIVE_MASK) : memory_read_generic<Width, AddrShift, Endian, 3, true>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffffffffffffffffU); } - u64 read_qword(offs_t address, u64 mask) override { address &= m_addrmask; return memory_read_generic<Width, AddrShift, Endian, 3, true>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); } - u64 read_qword_unaligned(offs_t address) override { address &= m_addrmask; return memory_read_generic<Width, AddrShift, Endian, 3, false>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffffffffffffffffU); } - u64 read_qword_unaligned(offs_t address, u64 mask) override { address &= m_addrmask; return memory_read_generic<Width, AddrShift, Endian, 3, false>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); } - - void write_byte(offs_t address, u8 data) override { address &= m_addrmask; if (Width == 0) write_native(address & ~NATIVE_MASK, data); else memory_write_generic<Width, AddrShift, Endian, 0, true>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xff); } - void write_word(offs_t address, u16 data) override { address &= m_addrmask; if (Width == 1) write_native(address & ~NATIVE_MASK, data); else memory_write_generic<Width, AddrShift, Endian, 1, true>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffff); } - void write_word(offs_t address, u16 data, u16 mask) override { address &= m_addrmask; memory_write_generic<Width, AddrShift, Endian, 1, true>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); } - void write_word_unaligned(offs_t address, u16 data) override { address &= m_addrmask; memory_write_generic<Width, AddrShift, Endian, 1, false>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffff); } - void write_word_unaligned(offs_t address, u16 data, u16 mask) override { address &= m_addrmask; memory_write_generic<Width, AddrShift, Endian, 1, false>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); } - void write_dword(offs_t address, u32 data) override { address &= m_addrmask; if (Width == 2) write_native(address & ~NATIVE_MASK, data); else memory_write_generic<Width, AddrShift, Endian, 2, true>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffffffff); } - void write_dword(offs_t address, u32 data, u32 mask) override { address &= m_addrmask; memory_write_generic<Width, AddrShift, Endian, 2, true>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); } - void write_dword_unaligned(offs_t address, u32 data) override { address &= m_addrmask; memory_write_generic<Width, AddrShift, Endian, 2, false>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffffffff); } - void write_dword_unaligned(offs_t address, u32 data, u32 mask) override { address &= m_addrmask; memory_write_generic<Width, AddrShift, Endian, 2, false>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); } - void write_qword(offs_t address, u64 data) override { address &= m_addrmask; if (Width == 3) write_native(address & ~NATIVE_MASK, data); else memory_write_generic<Width, AddrShift, Endian, 3, true>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffffffffffffffffU); } - void write_qword(offs_t address, u64 data, u64 mask) override { address &= m_addrmask; memory_write_generic<Width, AddrShift, Endian, 3, true>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); } - void write_qword_unaligned(offs_t address, u64 data) override { address &= m_addrmask; memory_write_generic<Width, AddrShift, Endian, 3, false>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffffffffffffffffU); } - void write_qword_unaligned(offs_t address, u64 data, u64 mask) override {address &= m_addrmask; memory_write_generic<Width, AddrShift, Endian, 3, false>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); } - - // static access to these functions - static u8 read_byte_static(this_type &space, offs_t address) { address &= space.m_addrmask; return Width == 0 ? space.read_native(address & ~NATIVE_MASK) : memory_read_generic<Width, AddrShift, Endian, 0, true>([&space](offs_t offset, NativeType mask) -> NativeType { return space.read_native(offset, mask); }, address, 0xff); } - static u16 read_word_static(this_type &space, offs_t address) { address &= space.m_addrmask; return Width == 1 ? space.read_native(address & ~NATIVE_MASK) : memory_read_generic<Width, AddrShift, Endian, 1, true>([&space](offs_t offset, NativeType mask) -> NativeType { return space.read_native(offset, mask); }, address, 0xffff); } - static u16 read_word_masked_static(this_type &space, offs_t address, u16 mask) { address &= space.m_addrmask; return memory_read_generic<Width, AddrShift, Endian, 1, true>([&space](offs_t offset, NativeType mask) -> NativeType { return space.read_native(offset, mask); }, address, mask); } - static u32 read_dword_static(this_type &space, offs_t address) { address &= space.m_addrmask; return Width == 2 ? space.read_native(address & ~NATIVE_MASK) : memory_read_generic<Width, AddrShift, Endian, 2, true>([&space](offs_t offset, NativeType mask) -> NativeType { return space.read_native(offset, mask); }, address, 0xffffffff); } - static u32 read_dword_masked_static(this_type &space, offs_t address, u32 mask) { address &= space.m_addrmask; return memory_read_generic<Width, AddrShift, Endian, 2, true>([&space](offs_t offset, NativeType mask) -> NativeType { return space.read_native(offset, mask); }, address, mask); } - static u64 read_qword_static(this_type &space, offs_t address) { address &= space.m_addrmask; return Width == 3 ? space.read_native(address & ~NATIVE_MASK) : memory_read_generic<Width, AddrShift, Endian, 3, true>([&space](offs_t offset, NativeType mask) -> NativeType { return space.read_native(offset, mask); }, address, 0xffffffffffffffffU); } - static u64 read_qword_masked_static(this_type &space, offs_t address, u64 mask) { address &= space.m_addrmask; return memory_read_generic<Width, AddrShift, Endian, 3, true>([&space](offs_t offset, NativeType mask) -> NativeType { return space.read_native(offset, mask); }, address, mask); } - static void write_byte_static(this_type &space, offs_t address, u8 data) { address &= space.m_addrmask; if (Width == 0) space.write_native(address & ~NATIVE_MASK, data); else memory_write_generic<Width, AddrShift, Endian, 0, true>([&space](offs_t offset, NativeType data, NativeType mask) { space.write_native(offset, data, mask); }, address, data, 0xff); } - static void write_word_static(this_type &space, offs_t address, u16 data) { address &= space.m_addrmask; if (Width == 1) space.write_native(address & ~NATIVE_MASK, data); else memory_write_generic<Width, AddrShift, Endian, 1, true>([&space](offs_t offset, NativeType data, NativeType mask) { space.write_native(offset, data, mask); }, address, data, 0xffff); } - static void write_word_masked_static(this_type &space, offs_t address, u16 data, u16 mask) { address &= space.m_addrmask; memory_write_generic<Width, AddrShift, Endian, 1, true>([&space](offs_t offset, NativeType data, NativeType mask) { space.write_native(offset, data, mask); }, address, data, mask); } - static void write_dword_static(this_type &space, offs_t address, u32 data) { address &= space.m_addrmask; if (Width == 2) space.write_native(address & ~NATIVE_MASK, data); else memory_write_generic<Width, AddrShift, Endian, 2, true>([&space](offs_t offset, NativeType data, NativeType mask) { space.write_native(offset, data, mask); }, address, data, 0xffffffff); } - static void write_dword_masked_static(this_type &space, offs_t address, u32 data, u32 mask) { address &= space.m_addrmask; memory_write_generic<Width, AddrShift, Endian, 2, true>([&space](offs_t offset, NativeType data, NativeType mask) { space.write_native(offset, data, mask); }, address, data, mask); } - static void write_qword_static(this_type &space, offs_t address, u64 data) { address &= space.m_addrmask; if (Width == 3) space.write_native(address & ~NATIVE_MASK, data); else memory_write_generic<Width, AddrShift, Endian, 3, false>([&space](offs_t offset, NativeType data, NativeType mask) { space.write_native(offset, data, mask); }, address, data, 0xffffffffffffffffU); } - static void write_qword_masked_static(this_type &space, offs_t address, u64 data, u64 mask) { address &= space.m_addrmask; memory_write_generic<Width, AddrShift, Endian, 3, false>([&space](offs_t offset, NativeType data, NativeType mask) { space.write_native(offset, data, mask); }, address, data, mask); } - - handler_entry_read <Width, AddrShift, Endian> *m_root_read; - handler_entry_write<Width, AddrShift, Endian> *m_root_write; - - std::unordered_set<handler_entry *> m_delayed_unrefs; - -private: - template<int AccessWidth, typename READ> std::enable_if_t<(Width == AccessWidth)> - install_read_handler_helper(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, - READ handler_r) - { - VPRINTF(("address_space::install_read_handler(%s-%s mask=%s mirror=%s, space width=%d, handler width=%d, %s, %s)\n", - core_i64_hex_format(addrstart, m_addrchars), core_i64_hex_format(addrend, m_addrchars), - core_i64_hex_format(addrmask, m_addrchars), core_i64_hex_format(addrmirror, m_addrchars), - 8 << Width, 8 << AccessWidth, - handler_r.name(), core_i64_hex_format(unitmask, data_width() / 4))); - - offs_t nstart, nend, nmask, nmirror; - u64 nunitmask; - int ncswidth; - check_optimize_all("install_read_handler", 8 << AccessWidth, addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, nstart, nend, nmask, nmirror, nunitmask, ncswidth); - - auto hand_r = new handler_entry_read_delegate<Width, AddrShift, Endian, READ>(this, handler_r); - hand_r->set_address_info(nstart, nmask); - m_root_read->populate(nstart, nend, nmirror, hand_r); - invalidate_caches(read_or_write::READ); - } - - template<int AccessWidth, typename READ> std::enable_if_t<(Width > AccessWidth)> - install_read_handler_helper(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, - READ handler_r) - { - VPRINTF(("address_space::install_read_handler(%s-%s mask=%s mirror=%s, space width=%d, handler width=%d, %s, %s)\n", - core_i64_hex_format(addrstart, m_addrchars), core_i64_hex_format(addrend, m_addrchars), - core_i64_hex_format(addrmask, m_addrchars), core_i64_hex_format(addrmirror, m_addrchars), - 8 << Width, 8 << AccessWidth, - handler_r.name(), core_i64_hex_format(unitmask, data_width() / 4))); - - offs_t nstart, nend, nmask, nmirror; - u64 nunitmask; - int ncswidth; - check_optimize_all("install_read_handler", 8 << AccessWidth, addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, nstart, nend, nmask, nmirror, nunitmask, ncswidth); - - auto hand_r = new handler_entry_read_delegate<AccessWidth, -AccessWidth, Endian, READ>(this, handler_r); - memory_units_descriptor<Width, AddrShift, Endian> descriptor(AccessWidth, Endian, hand_r, nstart, nend, nmask, nunitmask, ncswidth); - hand_r->set_address_info(descriptor.get_handler_start(), descriptor.get_handler_mask()); - m_root_read->populate_mismatched(nstart, nend, nmirror, descriptor); - hand_r->unref(); - invalidate_caches(read_or_write::READ); - } - - - template<int AccessWidth, typename READ> std::enable_if_t<(Width < AccessWidth)> - install_read_handler_helper(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, - READ handler_r) - { - fatalerror("install_read_handler: cannot install a %d-wide handler in a %d-wide bus", 8 << AccessWidth, 8 << Width); - } - - template<int AccessWidth, typename WRITE> std::enable_if_t<(Width == AccessWidth)> - install_write_handler_helper(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, - WRITE handler_w) - { - VPRINTF(("address_space::install_write_handler(%s-%s mask=%s mirror=%s, space width=%d, handler width=%d, %s, %s)\n", - core_i64_hex_format(addrstart, m_addrchars), core_i64_hex_format(addrend, m_addrchars), - core_i64_hex_format(addrmask, m_addrchars), core_i64_hex_format(addrmirror, m_addrchars), - 8 << Width, 8 << AccessWidth, - handler_w.name(), core_i64_hex_format(unitmask, data_width() / 4))); - - offs_t nstart, nend, nmask, nmirror; - u64 nunitmask; - int ncswidth; - check_optimize_all("install_write_handler", 8 << AccessWidth, addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, nstart, nend, nmask, nmirror, nunitmask, ncswidth); - - auto hand_w = new handler_entry_write_delegate<Width, AddrShift, Endian, WRITE>(this, handler_w); - hand_w->set_address_info(nstart, nmask); - m_root_write->populate(nstart, nend, nmirror, hand_w); - invalidate_caches(read_or_write::WRITE); - } - - template<int AccessWidth, typename WRITE> std::enable_if_t<(Width > AccessWidth)> - install_write_handler_helper(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, - WRITE handler_w) - { - VPRINTF(("address_space::install_write_handler(%s-%s mask=%s mirror=%s, space width=%d, handler width=%d, %s, %s)\n", - core_i64_hex_format(addrstart, m_addrchars), core_i64_hex_format(addrend, m_addrchars), - core_i64_hex_format(addrmask, m_addrchars), core_i64_hex_format(addrmirror, m_addrchars), - 8 << Width, 8 << AccessWidth, - handler_w.name(), core_i64_hex_format(unitmask, data_width() / 4))); - - offs_t nstart, nend, nmask, nmirror; - u64 nunitmask; - int ncswidth; - check_optimize_all("install_write_handler", 8 << AccessWidth, addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, nstart, nend, nmask, nmirror, nunitmask, ncswidth); - - auto hand_w = new handler_entry_write_delegate<AccessWidth, -AccessWidth, Endian, WRITE>(this, handler_w); - memory_units_descriptor<Width, AddrShift, Endian> descriptor(AccessWidth, Endian, hand_w, nstart, nend, nmask, nunitmask, ncswidth); - hand_w->set_address_info(descriptor.get_handler_start(), descriptor.get_handler_mask()); - m_root_write->populate_mismatched(nstart, nend, nmirror, descriptor); - hand_w->unref(); - invalidate_caches(read_or_write::WRITE); - } - - - template<int AccessWidth, typename WRITE> std::enable_if_t<(Width < AccessWidth)> - install_write_handler_helper(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, - WRITE handler_w) - { - fatalerror("install_write_handler: cannot install a %d-wide handler in a %d-wide bus", 8 << AccessWidth, 8 << Width); - } - - - - template<int AccessWidth, typename READ, typename WRITE> std::enable_if_t<(Width == AccessWidth)> - install_readwrite_handler_helper(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, - READ handler_r, - WRITE handler_w) - { - VPRINTF(("address_space::install_readwrite_handler(%s-%s mask=%s mirror=%s, space width=%d, handler width=%d, %s, %s, %s)\n", - core_i64_hex_format(addrstart, m_addrchars), core_i64_hex_format(addrend, m_addrchars), - core_i64_hex_format(addrmask, m_addrchars), core_i64_hex_format(addrmirror, m_addrchars), - 8 << Width, 8 << AccessWidth, - handler_r.name(), handler_w.name(), core_i64_hex_format(unitmask, data_width() / 4))); - - offs_t nstart, nend, nmask, nmirror; - u64 nunitmask; - int ncswidth; - check_optimize_all("install_readwrite_handler", 8 << AccessWidth, addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, nstart, nend, nmask, nmirror, nunitmask, ncswidth); - - auto hand_r = new handler_entry_read_delegate <Width, AddrShift, Endian, READ>(this, handler_r); - hand_r->set_address_info(nstart, nmask); - m_root_read ->populate(nstart, nend, nmirror, hand_r); - - auto hand_w = new handler_entry_write_delegate<Width, AddrShift, Endian, WRITE>(this, handler_w); - hand_w->set_address_info(nstart, nmask); - m_root_write->populate(nstart, nend, nmirror, hand_w); - - invalidate_caches(read_or_write::READWRITE); - } - - template<int AccessWidth, typename READ, typename WRITE> std::enable_if_t<(Width > AccessWidth)> - install_readwrite_handler_helper(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, - READ handler_r, - WRITE handler_w) - { - VPRINTF(("address_space::install_readwrite_handler(%s-%s mask=%s mirror=%s, space width=%d, handler width=%d, %s, %s, %s)\n", - core_i64_hex_format(addrstart, m_addrchars), core_i64_hex_format(addrend, m_addrchars), - core_i64_hex_format(addrmask, m_addrchars), core_i64_hex_format(addrmirror, m_addrchars), - 8 << Width, 8 << AccessWidth, - handler_r.name(), handler_w.name(), core_i64_hex_format(unitmask, data_width() / 4))); - - offs_t nstart, nend, nmask, nmirror; - u64 nunitmask; - int ncswidth; - check_optimize_all("install_readwrite_handler", 8 << AccessWidth, addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, nstart, nend, nmask, nmirror, nunitmask, ncswidth); - - auto hand_r = new handler_entry_read_delegate <AccessWidth, -AccessWidth, Endian, READ>(this, handler_r); - memory_units_descriptor<Width, AddrShift, Endian> descriptor(AccessWidth, Endian, hand_r, nstart, nend, nmask, nunitmask, ncswidth); - hand_r->set_address_info(descriptor.get_handler_start(), descriptor.get_handler_mask()); - m_root_read ->populate_mismatched(nstart, nend, nmirror, descriptor); - hand_r->unref(); - - auto hand_w = new handler_entry_write_delegate<AccessWidth, -AccessWidth, Endian, WRITE>(this, handler_w); - descriptor.set_subunit_handler(hand_w); - hand_w->set_address_info(descriptor.get_handler_start(), descriptor.get_handler_mask()); - m_root_write->populate_mismatched(nstart, nend, nmirror, descriptor); - hand_w->unref(); - - invalidate_caches(read_or_write::READWRITE); - } - - - template<int AccessWidth, typename READ, typename WRITE> std::enable_if_t<(Width < AccessWidth)> - install_readwrite_handler_helper(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, - READ handler_r, - WRITE handler_w) - { - fatalerror("install_readwrite_handler: cannot install a %d-wide handler in a %d-wide bus", 8 << AccessWidth, 8 << Width); - } -}; - - +template<int Width, int AddrShift> void handler_entry_write<Width, AddrShift>::init_handlers(offs_t start_entry, offs_t end_entry, u32 lowbits, offs_t ostart, offs_t oend, handler_entry_write<Width, AddrShift> **dispatch, handler_entry::range *ranges) +{ + fatalerror("init_handlers called on non-view class\n"); +} + +template class handler_entry_read<0, 1>; +template class handler_entry_read<0, 0>; +template class handler_entry_read<1, 3>; +template class handler_entry_read<1, 0>; +template class handler_entry_read<1, -1>; +template class handler_entry_read<2, 3>; +template class handler_entry_read<2, 0>; +template class handler_entry_read<2, -1>; +template class handler_entry_read<2, -2>; +template class handler_entry_read<3, 0>; +template class handler_entry_read<3, -1>; +template class handler_entry_read<3, -2>; +template class handler_entry_read<3, -3>; + +template class handler_entry_write<0, 1>; +template class handler_entry_write<0, 0>; +template class handler_entry_write<1, 3>; +template class handler_entry_write<1, 0>; +template class handler_entry_write<1, -1>; +template class handler_entry_write<2, 3>; +template class handler_entry_write<2, 0>; +template class handler_entry_write<2, -1>; +template class handler_entry_write<2, -2>; +template class handler_entry_write<3, 0>; +template class handler_entry_write<3, -1>; +template class handler_entry_write<3, -2>; +template class handler_entry_write<3, -3>; //************************************************************************** // MEMORY MANAGER @@ -722,115 +262,16 @@ private: //------------------------------------------------- memory_manager::memory_manager(running_machine &machine) - : m_machine(machine), - m_initialized(false) + : m_machine(machine) { } //------------------------------------------------- -// allocate - allocate memory spaces +// ~memory_manager - free the allocated memory banks //------------------------------------------------- -void memory_manager::allocate(device_memory_interface &memory) +memory_manager::~memory_manager() { - for (int spacenum = 0; spacenum < memory.max_space_count(); ++spacenum) - { - // if there is a configuration for this space, we need an address space - address_space_config const *const spaceconfig = memory.space_config(spacenum); - if (spaceconfig) - { - // allocate one of the appropriate type - switch (spaceconfig->data_width() | (spaceconfig->addr_shift() + 4)) - { - case 8|(4+1): - if (spaceconfig->endianness() == ENDIANNESS_LITTLE) - memory.allocate<address_space_specific<0, 1, ENDIANNESS_LITTLE>>(*this, spacenum); - else - memory.allocate<address_space_specific<0, 1, ENDIANNESS_BIG >>(*this, spacenum); - break; - - case 8|(4-0): - if (spaceconfig->endianness() == ENDIANNESS_LITTLE) - memory.allocate<address_space_specific<0, 0, ENDIANNESS_LITTLE>>(*this, spacenum); - else - memory.allocate<address_space_specific<0, 0, ENDIANNESS_BIG >>(*this, spacenum); - break; - - case 16|(4+3): - if (spaceconfig->endianness() == ENDIANNESS_LITTLE) - memory.allocate<address_space_specific<1, 3, ENDIANNESS_LITTLE>>(*this, spacenum); - else - memory.allocate<address_space_specific<1, 3, ENDIANNESS_BIG >>(*this, spacenum); - break; - - case 16|(4-0): - if (spaceconfig->endianness() == ENDIANNESS_LITTLE) - memory.allocate<address_space_specific<1, 0, ENDIANNESS_LITTLE>>(*this, spacenum); - else - memory.allocate<address_space_specific<1, 0, ENDIANNESS_BIG >>(*this, spacenum); - break; - - case 16|(4-1): - if (spaceconfig->endianness() == ENDIANNESS_LITTLE) - memory.allocate<address_space_specific<1, -1, ENDIANNESS_LITTLE>>(*this, spacenum); - else - memory.allocate<address_space_specific<1, -1, ENDIANNESS_BIG >>(*this, spacenum); - break; - - case 32|(4-0): - if (spaceconfig->endianness() == ENDIANNESS_LITTLE) - memory.allocate<address_space_specific<2, 0, ENDIANNESS_LITTLE>>(*this, spacenum); - else - memory.allocate<address_space_specific<2, 0, ENDIANNESS_BIG >>(*this, spacenum); - break; - - case 32|(4-1): - if (spaceconfig->endianness() == ENDIANNESS_LITTLE) - memory.allocate<address_space_specific<2, -1, ENDIANNESS_LITTLE>>(*this, spacenum); - else - memory.allocate<address_space_specific<2, -1, ENDIANNESS_BIG >>(*this, spacenum); - break; - - case 32|(4-2): - if (spaceconfig->endianness() == ENDIANNESS_LITTLE) - memory.allocate<address_space_specific<2, -2, ENDIANNESS_LITTLE>>(*this, spacenum); - else - memory.allocate<address_space_specific<2, -2, ENDIANNESS_BIG >>(*this, spacenum); - break; - - case 64|(4-0): - if (spaceconfig->endianness() == ENDIANNESS_LITTLE) - memory.allocate<address_space_specific<3, 0, ENDIANNESS_LITTLE>>(*this, spacenum); - else - memory.allocate<address_space_specific<3, 0, ENDIANNESS_BIG >>(*this, spacenum); - break; - - case 64|(4-1): - if (spaceconfig->endianness() == ENDIANNESS_LITTLE) - memory.allocate<address_space_specific<3, -1, ENDIANNESS_LITTLE>>(*this, spacenum); - else - memory.allocate<address_space_specific<3, -1, ENDIANNESS_BIG >>(*this, spacenum); - break; - - case 64|(4-2): - if (spaceconfig->endianness() == ENDIANNESS_LITTLE) - memory.allocate<address_space_specific<3, -2, ENDIANNESS_LITTLE>>(*this, spacenum); - else - memory.allocate<address_space_specific<3, -2, ENDIANNESS_BIG >>(*this, spacenum); - break; - - case 64|(4-3): - if (spaceconfig->endianness() == ENDIANNESS_LITTLE) - memory.allocate<address_space_specific<3, -3, ENDIANNESS_LITTLE>>(*this, spacenum); - else - memory.allocate<address_space_specific<3, -3, ENDIANNESS_BIG >>(*this, spacenum); - break; - - default: - throw emu_fatalerror("Invalid width %d/shift %d specified for address_space::allocate", spaceconfig->data_width(), spaceconfig->addr_shift()); - } - } - } } //------------------------------------------------- @@ -840,16 +281,13 @@ void memory_manager::allocate(device_memory_interface &memory) void memory_manager::initialize() { // loop over devices and spaces within each device - memory_interface_iterator iter(machine().root_device()); std::vector<device_memory_interface *> memories; - for (device_memory_interface &memory : iter) + for (device_memory_interface &memory : memory_interface_enumerator(machine().root_device())) { memories.push_back(&memory); allocate(memory); } - allocate(m_machine.m_dummy_space); - // construct and preprocess the address_map for each space for (auto const memory : memories) memory->prepare_maps(); @@ -858,38 +296,50 @@ void memory_manager::initialize() for (auto const memory : memories) memory->populate_from_maps(); - // allocate memory needed to back each address space - for (auto const memory : memories) - memory->allocate_memory(); - - // find all the allocated pointers - for (auto const memory : memories) - memory->locate_memory(); - // disable logging of unmapped access when no one receives it if (!machine().options().log() && !machine().options().oslog() && !(machine().debug_flags & DEBUG_FLAG_ENABLED)) for (auto const memory : memories) memory->set_log_unmap(false); +} + - // we are now initialized - m_initialized = true; +//------------------------------------------------- +// allocate_memory - allocate some ram and register it for saving +//------------------------------------------------- + +void *memory_manager::allocate_memory(device_t &dev, int spacenum, std::string name, u8 width, size_t bytes) +{ + void *const ptr = m_datablocks.emplace_back(malloc(bytes)).get(); + memset(ptr, 0, bytes); + machine().save().save_memory(&dev, "memory", dev.tag(), spacenum, name.c_str(), ptr, width/8, u32(bytes) / (width/8)); + return ptr; } + //------------------------------------------------- // region_alloc - allocates memory for a region //------------------------------------------------- -memory_region *memory_manager::region_alloc(const char *name, u32 length, u8 width, endianness_t endian) +memory_region *memory_manager::region_alloc(std::string name, u32 length, u8 width, endianness_t endian) { - osd_printf_verbose("Region '%s' created\n", name); // make sure we don't have a region of the same name; also find the end of the list if (m_regionlist.find(name) != m_regionlist.end()) fatalerror("region_alloc called with duplicate region name \"%s\"\n", name); // allocate the region - m_regionlist.emplace(name, std::make_unique<memory_region>(machine(), name, length, width, endian)); - return m_regionlist.find(name)->second.get(); + return m_regionlist.emplace(name, std::make_unique<memory_region>(machine(), name, length, width, endian)).first->second.get(); +} + + +//------------------------------------------------- +// region_find - find a region by name +//------------------------------------------------- + +memory_region *memory_manager::region_find(std::string name) +{ + auto i = m_regionlist.find(name); + return i != m_regionlist.end() ? i->second.get() : nullptr; } @@ -897,61 +347,81 @@ memory_region *memory_manager::region_alloc(const char *name, u32 length, u8 wid // region_free - releases memory for a region //------------------------------------------------- -void memory_manager::region_free(const char *name) +void memory_manager::region_free(std::string name) { m_regionlist.erase(name); } //------------------------------------------------- -// region_containing - helper to determine if -// a block of memory is part of a region +// anonymous_alloc - allocates a anonymous memory zone //------------------------------------------------- -memory_region *memory_manager::region_containing(const void *memory, offs_t bytes) const +void *memory_manager::anonymous_alloc(address_space &space, size_t bytes, u8 width, offs_t start, offs_t end, const std::string &key) { - const u8 *data = reinterpret_cast<const u8 *>(memory); + std::string name = util::string_format("%s%x-%x", key, start, end); + return allocate_memory(space.device(), space.spacenum(), name, width, bytes); +} - // look through the region list and return the first match - for (auto ®ion : m_regionlist) - if (data >= region.second->base() && (data + bytes) <= region.second->end()) - return region.second.get(); - // didn't find one - return nullptr; +//------------------------------------------------- +// share_alloc - allocates a shared memory zone +//------------------------------------------------- + +memory_share *memory_manager::share_alloc(device_t &dev, std::string name, u8 width, size_t bytes, endianness_t endianness) +{ + // make sure we don't have a share of the same name; also find the end of the list + if (m_sharelist.find(name) != m_sharelist.end()) + fatalerror("share_alloc called with duplicate share name \"%s\"\n", name); + + // allocate and register the memory + void *ptr = allocate_memory(dev, 0, name, width, bytes); + + // allocate the region + return m_sharelist.emplace(name, std::make_unique<memory_share>(name, width, bytes, endianness, ptr)).first->second.get(); } -memory_bank *memory_manager::find(const char *tag) const + +//------------------------------------------------- +// share_find - find a share by name +//------------------------------------------------- + +memory_share *memory_manager::share_find(std::string name) { - auto bank = m_banklist.find(tag); - if (bank != m_banklist.end()) - return bank->second.get(); - return nullptr; + auto i = m_sharelist.find(name); + return i != m_sharelist.end() ? i->second.get() : nullptr; } -memory_bank *memory_manager::find(address_space &space, offs_t addrstart, offs_t addrend) const + + +//------------------------------------------------- +// share_alloc - allocates a banking zone +//------------------------------------------------- + +memory_bank *memory_manager::bank_alloc(device_t &device, std::string name) { - // try to find an exact match - for (auto &bank : m_banklist) - if (bank.second->anonymous() && bank.second->references_space(space, read_or_write::READWRITE) && bank.second->matches_exactly(addrstart, addrend)) - return bank.second.get(); + // allocate the bank + auto const ins = m_banklist.emplace(name, std::make_unique<memory_bank>(device, name)); - // not found - return nullptr; + // make sure we don't have a bank of the same name + if (!ins.second) + fatalerror("bank_alloc called with duplicate bank name \"%s\"\n", name); + + return ins.first->second.get(); } -memory_bank *memory_manager::allocate(address_space &space, offs_t addrstart, offs_t addrend, const char *tag) + +//------------------------------------------------- +// bank_find - find a bank by name +//------------------------------------------------- + +memory_bank *memory_manager::bank_find(std::string name) { - auto bank = std::make_unique<memory_bank>(space, m_banklist.size(), addrstart, addrend, tag); - std::string temptag; - if (tag == nullptr) { - temptag = string_format("anon_%p", bank.get()); - tag = temptag.c_str(); - } - m_banklist.emplace(tag, std::move(bank)); - return m_banklist.find(tag)->second.get(); + auto i = m_banklist.find(name); + return i != m_banklist.end() ? i->second.get() : nullptr; } + //************************************************************************** // ADDRESS SPACE CONFIG //************************************************************************** @@ -1008,58 +478,7 @@ address_space_config::address_space_config(const char *name, endianness_t endian } -//************************************************************************** -// ADDRESS SPACE -//************************************************************************** - -//------------------------------------------------- -// address_space - constructor -//------------------------------------------------- - -address_space::address_space(memory_manager &manager, device_memory_interface &memory, int spacenum) - : m_config(*memory.space_config(spacenum)), - m_device(memory.device()), - m_addrmask(make_bitmask<offs_t>(m_config.addr_width())), - m_logaddrmask(make_bitmask<offs_t>(m_config.logaddr_width())), - m_unmap(0), - m_spacenum(spacenum), - m_log_unmap(true), - m_name(memory.space_config(spacenum)->name()), - m_addrchars((m_config.addr_width() + 3) / 4), - m_logaddrchars((m_config.logaddr_width() + 3) / 4), - m_notifier_id(0), - m_in_notification(0), - m_manager(manager) -{ -} - - -//------------------------------------------------- -// ~address_space - destructor -//------------------------------------------------- - -address_space::~address_space() -{ - m_unmap_r->unref(); - m_unmap_w->unref(); - m_nop_r->unref(); - m_nop_w->unref(); -} - -//------------------------------------------------- -// adjust_addresses - adjust addresses for a -// given address space in a standard fashion -//------------------------------------------------- - -inline void address_space::adjust_addresses(offs_t &start, offs_t &end, offs_t &mask, offs_t &mirror) -{ - // adjust start/end/mask values - mask &= m_addrmask; - start &= ~mirror & m_addrmask; - end &= ~mirror & m_addrmask; -} - -void address_space::check_optimize_all(const char *function, int width, offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, offs_t &nstart, offs_t &nend, offs_t &nmask, offs_t &nmirror, u64 &nunitmask, int &ncswidth) +void address_space_installer::check_optimize_all(const char *function, int width, offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, offs_t &nstart, offs_t &nend, offs_t &nmask, offs_t &nmirror, u64 &nunitmask, int &ncswidth) { if (addrstart > addrend) fatalerror("%s: In range %x-%x mask %x mirror %x select %x, start address is after the end address.\n", function, addrstart, addrend, addrmask, addrmirror, addrselect); @@ -1082,7 +501,6 @@ void address_space::check_optimize_all(const char *function, int width, offs_t a if ((~addrend) & lowbits_mask) fatalerror("%s: In range %x-%x mask %x mirror %x select %x, end address has low bits unset, did you mean %x ?\n", function, addrstart, addrend, addrmask, addrmirror, addrselect, addrend | lowbits_mask); - offs_t set_bits = addrstart | addrend; offs_t changing_bits = addrstart ^ addrend; // Round up to the nearest power-of-two-minus-one @@ -1127,7 +545,7 @@ void address_space::check_optimize_all(const char *function, int width, offs_t a while (cmask != 0 && (cmask & block_mask) == 0) cmask >>= width; if (cmask != 0 && cmask != block_mask) - fatalerror("%s: In range %x-%x mask %x mirror %x select %x, the unitmask of %s has incorrect granularity for %d-bit chip selection.\n", function, addrstart, addrend, addrmask, addrmirror, addrselect, core_i64_hex_format(unitmask, 16), cswidth); + fatalerror("%s: In range %x-%x mask %x mirror %x select %x, the unitmask of %016x has incorrect granularity for %d-bit chip selection.\n", function, addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth); } } @@ -1139,6 +557,56 @@ void address_space::check_optimize_all(const char *function, int width, offs_t a nend = addrend; nmask = (addrmask ? addrmask : changing_bits) | addrselect; nmirror = (addrmirror & m_addrmask) | addrselect; + + if(nmirror & default_lowbits_mask) { + // If the mirroring/select "reaches" within the bus + // granularity we have to adapt it and the unitmask. + + // We're sure start/end are on the same data-width-sized + // entry, because otherwise the previous tests wouldn't have + // passed. So we need to clear the part of the unitmask that + // not in the range, then replicate it following the mirror. + // The start/end also need to be adjusted to the bus + // granularity. + + // 1. Adjusting + nstart &= ~default_lowbits_mask; + nend |= default_lowbits_mask; + + // 2. Clearing + u64 smask, emask; + if(m_config.endianness() == ENDIANNESS_BIG) { + smask = make_bitmask<u64>(m_config.data_width() - ((addrstart - nstart) << (3 - m_config.addr_shift()))); + emask = ~make_bitmask<u64>(m_config.data_width() - ((addrend - nstart + 1) << (3 - m_config.addr_shift()))); + } else { + smask = ~make_bitmask<u64>((addrstart - nstart) << (3 - m_config.addr_shift())); + emask = make_bitmask<u64>((addrend - nstart + 1) << (3 - m_config.addr_shift())); + } + nunitmask &= smask & emask; + + // 3. Mirroring + offs_t to_mirror = nmirror & default_lowbits_mask; + if(m_config.endianness() == ENDIANNESS_BIG) { + for(int i=0; to_mirror; i++) + if((to_mirror >> i) & 1) { + to_mirror &= ~(1 << i); + nunitmask |= nunitmask >> (1 << (3 + i - m_config.addr_shift())); + } + } else { + for(int i=0; to_mirror; i++) + if((to_mirror >> i) & 1) { + to_mirror &= ~(1 << i); + nunitmask |= nunitmask << (1 << (3 + i - m_config.addr_shift())); + } + } + + // 4. Ajusting the mirror + nmirror &= ~default_lowbits_mask; + + // 5. Recompute changing_bits, it matters for the next optimization. No need to round up through + changing_bits = nstart ^ nend; + } + if(nmirror && !(nstart & changing_bits) && !((~nend) & changing_bits)) { // If the range covers the a complete power-of-two zone, it is // possible to remove 1 bits from the mirror, pushing the end @@ -1153,7 +621,7 @@ void address_space::check_optimize_all(const char *function, int width, offs_t a } } -void address_space::check_optimize_mirror(const char *function, offs_t addrstart, offs_t addrend, offs_t addrmirror, offs_t &nstart, offs_t &nend, offs_t &nmask, offs_t &nmirror) +void address_space_installer::check_optimize_mirror(const char *function, offs_t addrstart, offs_t addrend, offs_t addrmirror, offs_t &nstart, offs_t &nend, offs_t &nmask, offs_t &nmirror) { if (addrstart > addrend) fatalerror("%s: In range %x-%x mirror %x, start address is after the end address.\n", function, addrstart, addrend, addrmirror); @@ -1203,7 +671,7 @@ void address_space::check_optimize_mirror(const char *function, offs_t addrstart } } -void address_space::check_address(const char *function, offs_t addrstart, offs_t addrend) +void address_space_installer::check_address(const char *function, offs_t addrstart, offs_t addrend) { if (addrstart > addrend) fatalerror("%s: In range %x-%x, start address is after the end address.\n", function, addrstart, addrend); @@ -1221,125 +689,12 @@ void address_space::check_address(const char *function, offs_t addrstart, offs_t //------------------------------------------------- -// prepare_map - allocate the address map and -// walk through it to find implicit memory regions -// and identify shared regions -//------------------------------------------------- - -void address_space::prepare_map() -{ - memory_region *devregion = (m_spacenum == 0) ? m_device.memregion(DEVICE_SELF) : nullptr; - u32 devregionsize = (devregion != nullptr) ? devregion->bytes() : 0; - - // allocate the address map - m_map = std::make_unique<address_map>(m_device, m_spacenum); - - // merge in the submaps - m_map->import_submaps(m_manager.machine(), m_device.owner() ? *m_device.owner() : m_device, data_width(), endianness(), addr_shift()); - - // extract global parameters specified by the map - m_unmap = (m_map->m_unmapval == 0) ? 0 : ~0; - if (m_map->m_globalmask != 0) - { - if (m_map->m_globalmask & ~m_addrmask) - fatalerror("Can't set a global address mask of %08x on a %d-bits address width bus.\n", m_map->m_globalmask, addr_width()); - - m_addrmask = m_map->m_globalmask; - } - - // make a pass over the address map, adjusting for the device and getting memory pointers - for (address_map_entry &entry : m_map->m_entrylist) - { - // computed adjusted addresses first - adjust_addresses(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror); - - // if we have a share entry, add it to our map - if (entry.m_share != nullptr) - { - // if we can't find it, add it to our map - std::string fulltag = entry.m_devbase.subtag(entry.m_share); - if (m_manager.m_sharelist.find(fulltag.c_str()) == m_manager.m_sharelist.end()) - { - VPRINTF(("Creating share '%s' of length 0x%X\n", fulltag.c_str(), entry.m_addrend + 1 - entry.m_addrstart)); - m_manager.m_sharelist.emplace(fulltag.c_str(), std::make_unique<memory_share>(m_config.data_width(), address_to_byte(entry.m_addrend + 1 - entry.m_addrstart), endianness())); - } - } - - // if this is a ROM handler without a specified region, attach it to the implicit region - if (m_spacenum == 0 && entry.m_read.m_type == AMH_ROM && entry.m_region == nullptr) - { - // make sure it fits within the memory region before doing so, however - if (entry.m_addrend < devregionsize) - { - entry.m_region = m_device.tag(); - entry.m_rgnoffs = address_to_byte(entry.m_addrstart); - } - } - - // validate adjusted addresses against implicit regions - if (entry.m_region != nullptr && entry.m_share == nullptr) - { - // determine full tag - std::string fulltag = entry.m_devbase.subtag(entry.m_region); - - // find the region - memory_region *region = m_manager.machine().root_device().memregion(fulltag.c_str()); - if (region == nullptr) - fatalerror("device '%s' %s space memory map entry %X-%X references nonexistent region \"%s\"\n", m_device.tag(), m_name, entry.m_addrstart, entry.m_addrend, entry.m_region); - - // validate the region - if (entry.m_rgnoffs + m_config.addr2byte(entry.m_addrend - entry.m_addrstart + 1) > region->bytes()) - fatalerror("device '%s' %s space memory map entry %X-%X extends beyond region \"%s\" size (%X)\n", m_device.tag(), m_name, entry.m_addrstart, entry.m_addrend, entry.m_region, region->bytes()); - } - - // convert any region-relative entries to their memory pointers - if (entry.m_region != nullptr) - { - // determine full tag - std::string fulltag = entry.m_devbase.subtag(entry.m_region); - - // set the memory address - entry.m_memory = m_manager.machine().root_device().memregion(fulltag.c_str())->base() + entry.m_rgnoffs; - } - } -} - - -//------------------------------------------------- -// populate_from_map - walk the map in reverse -// order and install the appropriate handler for -// each case -//------------------------------------------------- - -void address_space::populate_from_map(address_map *map) -{ - // no map specified, use the space-specific one - if (map == nullptr) - map = m_map.get(); - - // no map, nothing to do - if (map == nullptr) - return; - - // install the handlers, using the original, unadjusted memory map - for (const address_map_entry &entry : map->m_entrylist) { - // map both read and write halves - populate_map_entry(entry, read_or_write::READ); - populate_map_entry(entry, read_or_write::WRITE); - } - - if(VALIDATE_REFCOUNTS) - validate_reference_counts(); -} - - -//------------------------------------------------- // populate_map_entry - map a single read or // write entry based on information from an // address map entry //------------------------------------------------- -void address_space::populate_map_entry(const address_map_entry &entry, read_or_write readorwrite) +void address_space_installer::populate_map_entry(const address_map_entry &entry, read_or_write readorwrite) { const map_handler_data &data = (readorwrite == read_or_write::READ) ? entry.m_read : entry.m_write; // based on the handler type, alter the bits, name, funcptr, and object @@ -1353,35 +708,35 @@ void address_space::populate_map_entry(const address_map_entry &entry, read_or_w if (readorwrite == read_or_write::WRITE) return; // fall through to the RAM case otherwise - + [[fallthrough]]; case AMH_RAM: - install_ram_generic(entry.m_addrstart, entry.m_addrend, entry.m_addrmirror, readorwrite, nullptr); + install_ram_generic(entry.m_addrstart, entry.m_addrend, entry.m_addrmirror, entry.m_flags, readorwrite, entry.m_memory); break; case AMH_NOP: - unmap_generic(entry.m_addrstart, entry.m_addrend, entry.m_addrmirror, readorwrite, true); + unmap_generic(entry.m_addrstart, entry.m_addrend, entry.m_addrmirror, entry.m_flags, readorwrite, true); break; case AMH_UNMAP: - unmap_generic(entry.m_addrstart, entry.m_addrend, entry.m_addrmirror, readorwrite, false); + unmap_generic(entry.m_addrstart, entry.m_addrend, entry.m_addrmirror, entry.m_flags, readorwrite, false); break; case AMH_DEVICE_DELEGATE: if (readorwrite == read_or_write::READ) switch (data.m_bits) { - case 8: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, read8_delegate(entry.m_rproto8, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; - case 16: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, read16_delegate(entry.m_rproto16, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; - case 32: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, read32_delegate(entry.m_rproto32, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; - case 64: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, read64_delegate(entry.m_rproto64, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; + case 8: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto8, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 16: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto16, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 32: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto32, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 64: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto64, entry.m_mask, entry.m_cswidth, entry.m_flags); break; } else switch (data.m_bits) { - case 8: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, write8_delegate(entry.m_wproto8, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; - case 16: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, write16_delegate(entry.m_wproto16, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; - case 32: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, write32_delegate(entry.m_wproto32, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; - case 64: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, write64_delegate(entry.m_wproto64, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; + case 8: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto8, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 16: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto16, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 32: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto32, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 64: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto64, entry.m_mask, entry.m_cswidth, entry.m_flags); break; } break; @@ -1389,18 +744,18 @@ void address_space::populate_map_entry(const address_map_entry &entry, read_or_w if (readorwrite == read_or_write::READ) switch (data.m_bits) { - case 8: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, read8m_delegate(entry.m_rproto8m, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; - case 16: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, read16m_delegate(entry.m_rproto16m, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; - case 32: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, read32m_delegate(entry.m_rproto32m, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; - case 64: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, read64m_delegate(entry.m_rproto64m, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; + case 8: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto8m, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 16: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto16m, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 32: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto32m, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 64: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto64m, entry.m_mask, entry.m_cswidth, entry.m_flags); break; } else switch (data.m_bits) { - case 8: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, write8m_delegate(entry.m_wproto8m, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; - case 16: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, write16m_delegate(entry.m_wproto16m, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; - case 32: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, write32m_delegate(entry.m_wproto32m, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; - case 64: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, write64m_delegate(entry.m_wproto64m, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; + case 8: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto8m, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 16: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto16m, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 32: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto32m, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 64: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto64m, entry.m_mask, entry.m_cswidth, entry.m_flags); break; } break; @@ -1408,18 +763,18 @@ void address_space::populate_map_entry(const address_map_entry &entry, read_or_w if (readorwrite == read_or_write::READ) switch (data.m_bits) { - case 8: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, read8s_delegate(entry.m_rproto8s, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; - case 16: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, read16s_delegate(entry.m_rproto16s, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; - case 32: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, read32s_delegate(entry.m_rproto32s, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; - case 64: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, read64s_delegate(entry.m_rproto64s, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; + case 8: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto8s, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 16: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto16s, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 32: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto32s, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 64: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto64s, entry.m_mask, entry.m_cswidth, entry.m_flags); break; } else switch (data.m_bits) { - case 8: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, write8s_delegate(entry.m_wproto8s, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; - case 16: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, write16s_delegate(entry.m_wproto16s, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; - case 32: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, write32s_delegate(entry.m_wproto32s, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; - case 64: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, write64s_delegate(entry.m_wproto64s, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; + case 8: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto8s, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 16: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto16s, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 32: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto32s, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 64: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto64s, entry.m_mask, entry.m_cswidth, entry.m_flags); break; } break; @@ -1427,18 +782,18 @@ void address_space::populate_map_entry(const address_map_entry &entry, read_or_w if (readorwrite == read_or_write::READ) switch (data.m_bits) { - case 8: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, read8sm_delegate(entry.m_rproto8sm, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; - case 16: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, read16sm_delegate(entry.m_rproto16sm, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; - case 32: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, read32sm_delegate(entry.m_rproto32sm, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; - case 64: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, read64sm_delegate(entry.m_rproto64sm, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; + case 8: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto8sm, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 16: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto16sm, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 32: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto32sm, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 64: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto64sm, entry.m_mask, entry.m_cswidth, entry.m_flags); break; } else switch (data.m_bits) { - case 8: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, write8sm_delegate(entry.m_wproto8sm, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; - case 16: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, write16sm_delegate(entry.m_wproto16sm, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; - case 32: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, write32sm_delegate(entry.m_wproto32sm, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; - case 64: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, write64sm_delegate(entry.m_wproto64sm, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; + case 8: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto8sm, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 16: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto16sm, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 32: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto32sm, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 64: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto64sm, entry.m_mask, entry.m_cswidth, entry.m_flags); break; } break; @@ -1446,18 +801,18 @@ void address_space::populate_map_entry(const address_map_entry &entry, read_or_w if (readorwrite == read_or_write::READ) switch (data.m_bits) { - case 8: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, read8mo_delegate(entry.m_rproto8mo, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; - case 16: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, read16mo_delegate(entry.m_rproto16mo, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; - case 32: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, read32mo_delegate(entry.m_rproto32mo, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; - case 64: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, read64mo_delegate(entry.m_rproto64mo, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; + case 8: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto8mo, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 16: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto16mo, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 32: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto32mo, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 64: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto64mo, entry.m_mask, entry.m_cswidth, entry.m_flags); break; } else switch (data.m_bits) { - case 8: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, write8mo_delegate(entry.m_wproto8mo, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; - case 16: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, write16mo_delegate(entry.m_wproto16mo, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; - case 32: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, write32mo_delegate(entry.m_wproto32mo, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; - case 64: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, write64mo_delegate(entry.m_wproto64mo, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; + case 8: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto8mo, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 16: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto16mo, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 32: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto32mo, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 64: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto64mo, entry.m_mask, entry.m_cswidth, entry.m_flags); break; } break; @@ -1465,1256 +820,144 @@ void address_space::populate_map_entry(const address_map_entry &entry, read_or_w if (readorwrite == read_or_write::READ) switch (data.m_bits) { - case 8: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, read8smo_delegate(entry.m_rproto8smo, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; - case 16: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, read16smo_delegate(entry.m_rproto16smo, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; - case 32: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, read32smo_delegate(entry.m_rproto32smo, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; - case 64: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, read64smo_delegate(entry.m_rproto64smo, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; + case 8: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto8smo, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 16: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto16smo, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 32: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto32smo, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 64: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto64smo, entry.m_mask, entry.m_cswidth, entry.m_flags); break; } else switch (data.m_bits) { - case 8: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, write8smo_delegate(entry.m_wproto8smo, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; - case 16: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, write16smo_delegate(entry.m_wproto16smo, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; - case 32: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, write32smo_delegate(entry.m_wproto32smo, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; - case 64: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, write64smo_delegate(entry.m_wproto64smo, entry.m_devbase), entry.m_mask, entry.m_cswidth); break; + case 8: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto8smo, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 16: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto16smo, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 32: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto32smo, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 64: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto64smo, entry.m_mask, entry.m_cswidth, entry.m_flags); break; } break; case AMH_PORT: - install_readwrite_port(entry.m_addrstart, entry.m_addrend, entry.m_addrmirror, + install_readwrite_port(entry.m_addrstart, entry.m_addrend, entry.m_addrmirror, entry.m_flags, (readorwrite == read_or_write::READ) ? entry.m_devbase.subtag(data.m_tag) : "", (readorwrite == read_or_write::WRITE) ? entry.m_devbase.subtag(data.m_tag) : ""); break; case AMH_BANK: - install_bank_generic(entry.m_addrstart, entry.m_addrend, entry.m_addrmirror, - (readorwrite == read_or_write::READ) ? entry.m_devbase.subtag(data.m_tag) : "", - (readorwrite == read_or_write::WRITE) ? entry.m_devbase.subtag(data.m_tag) : ""); + { + std::string tag = entry.m_devbase.subtag(data.m_tag); + memory_bank *bank = m_manager.bank_find(tag); + if (!bank) + bank = m_manager.bank_alloc(entry.m_devbase, tag); + install_bank_generic(entry.m_addrstart, entry.m_addrend, entry.m_addrmirror, entry.m_flags, + (readorwrite == read_or_write::READ) ? bank : nullptr, + (readorwrite == read_or_write::WRITE) ? bank : nullptr); + } break; case AMH_DEVICE_SUBMAP: throw emu_fatalerror("Internal mapping error: leftover mapping of '%s'.\n", data.m_tag); - } -} - -//------------------------------------------------- -// allocate_memory - determine all neighboring -// address ranges and allocate memory to back -// them -//------------------------------------------------- - -void address_space::allocate_memory() -{ - auto &blocklist = m_manager.m_blocklist; - - // make a first pass over the memory map and track blocks with hardcoded pointers - // we do this to make sure they are found by space_find_backing_memory first - // do it back-to-front so that overrides work correctly - int tail = blocklist.size(); - for (address_map_entry &entry : m_map->m_entrylist) - if (entry.m_memory != nullptr) - blocklist.insert(blocklist.begin() + tail, std::make_unique<memory_block>(*this, entry.m_addrstart, entry.m_addrend, entry.m_memory)); - - // loop over all blocks just allocated and assign pointers from them - address_map_entry *unassigned = nullptr; - - for (auto memblock = blocklist.begin() + tail; memblock != blocklist.end(); ++memblock) - unassigned = block_assign_intersecting(memblock->get()->addrstart(), memblock->get()->addrend(), memblock->get()->data()); - - // if we don't have an unassigned pointer yet, try to find one - if (unassigned == nullptr) - unassigned = block_assign_intersecting(~0, 0, nullptr); - - // loop until we've assigned all memory in this space - while (unassigned != nullptr) - { - // work in MEMORY_BLOCK_CHUNK-sized chunks - offs_t curblockstart = unassigned->m_addrstart / MEMORY_BLOCK_CHUNK; - offs_t curblockend = unassigned->m_addrend / MEMORY_BLOCK_CHUNK; - - // loop while we keep finding unassigned blocks in neighboring MEMORY_BLOCK_CHUNK chunks - bool changed; - do - { - changed = false; - - // scan for unmapped blocks in the adjusted map - for (address_map_entry &entry : m_map->m_entrylist) - if (entry.m_memory == nullptr && &entry != unassigned && needs_backing_store(entry)) - { - // get block start/end blocks for this block - offs_t blockstart = entry.m_addrstart / MEMORY_BLOCK_CHUNK; - offs_t blockend = entry.m_addrend / MEMORY_BLOCK_CHUNK; - - // if we intersect or are adjacent, adjust the start/end - if (blockstart <= curblockend + 1 && blockend >= curblockstart - 1) - { - if (blockstart < curblockstart) - curblockstart = blockstart, changed = true; - if (blockend > curblockend) - curblockend = blockend, changed = true; - } - } - } while (changed); - - // we now have a block to allocate; do it - offs_t curaddrstart = curblockstart * MEMORY_BLOCK_CHUNK; - offs_t curaddrend = curblockend * MEMORY_BLOCK_CHUNK + (MEMORY_BLOCK_CHUNK - 1); - auto block = std::make_unique<memory_block>(*this, curaddrstart, curaddrend); - // assign memory that intersected the new block - unassigned = block_assign_intersecting(curaddrstart, curaddrend, block.get()->data()); - blocklist.push_back(std::move(block)); + case AMH_VIEW: + if (readorwrite == read_or_write::READ) + install_view(entry.m_addrstart, entry.m_addrend, entry.m_addrmirror, *entry.m_view); + break; } -} - - -//------------------------------------------------- -// locate_memory - find all the requested -// pointers into the final allocated memory -//------------------------------------------------- - -void address_space::locate_memory() -{ - // once this is done, find the starting bases for the banks - for (auto &bank : m_manager.banks()) - if (bank.second->base() == nullptr && bank.second->references_space(*this, read_or_write::READWRITE)) - { - // set the initial bank pointer - for (address_map_entry &entry : m_map->m_entrylist) - if (entry.m_addrstart == bank.second->addrstart() && entry.m_memory != nullptr) - { - bank.second->set_base(entry.m_memory); - VPRINTF(("assigned bank '%s' pointer to memory from range %08X-%08X [%p]\n", bank.second->tag(), entry.m_addrstart, entry.m_addrend, entry.m_memory)); - break; - } - } -} - - - - -//------------------------------------------------- -// block_assign_intersecting - find all -// intersecting blocks and assign their pointers -//------------------------------------------------- -address_map_entry *address_space::block_assign_intersecting(offs_t addrstart, offs_t addrend, u8 *base) -{ - address_map_entry *unassigned = nullptr; - - // loop over the adjusted map and assign memory to any blocks we can - for (address_map_entry &entry : m_map->m_entrylist) - { - // if we haven't assigned this block yet, see if we have a mapped shared pointer for it - if (entry.m_memory == nullptr && entry.m_share != nullptr) - { - std::string fulltag = entry.m_devbase.subtag(entry.m_share); - auto share = m_manager.shares().find(fulltag.c_str()); - if (share != m_manager.shares().end() && share->second->ptr() != nullptr) - { - entry.m_memory = share->second->ptr(); - VPRINTF(("memory range %08X-%08X -> shared_ptr '%s' [%p]\n", entry.m_addrstart, entry.m_addrend, entry.m_share, entry.m_memory)); - } - else - { - VPRINTF(("memory range %08X-%08X -> shared_ptr '%s' but not found\n", entry.m_addrstart, entry.m_addrend, entry.m_share)); - } - } + if (data.m_type == AMH_VIEW) + return; - // otherwise, look for a match in this block - if (entry.m_memory == nullptr && entry.m_addrstart >= addrstart && entry.m_addrend <= addrend) - { - entry.m_memory = base + m_config.addr2byte(entry.m_addrstart - addrstart); - VPRINTF(("memory range %08X-%08X -> found in block from %08X-%08X [%p]\n", entry.m_addrstart, entry.m_addrend, addrstart, addrend, entry.m_memory)); - } + offs_t lowbits_mask = (m_config.data_width() >> (3 - m_config.addr_shift())) - 1; - // if we're the first match on a shared pointer, assign it now - if (entry.m_memory != nullptr && entry.m_share != nullptr) - { - std::string fulltag = entry.m_devbase.subtag(entry.m_share); - auto share = m_manager.shares().find(fulltag.c_str()); - if (share != m_manager.shares().end() && share->second->ptr() == nullptr) - { - share->second->set_ptr(entry.m_memory); - VPRINTF(("setting shared_ptr '%s' = %p\n", entry.m_share, entry.m_memory)); - } - } + if (!entry.m_before_time.isnull()) { + auto d = entry.m_before_time; + d.resolve(); + if (readorwrite == read_or_write::READ) + install_read_before_time(entry.m_addrstart & ~lowbits_mask, entry.m_addrend | lowbits_mask, entry.m_addrmirror, d); + else + install_write_before_time(entry.m_addrstart & ~lowbits_mask, entry.m_addrend | lowbits_mask, entry.m_addrmirror, d); + } - // keep track of the first unassigned entry - if (entry.m_memory == nullptr && unassigned == nullptr && needs_backing_store(entry)) - unassigned = &entry; + if (!entry.m_before_delay.isnull()) { + auto d = entry.m_before_delay; + d.resolve(); + if (readorwrite == read_or_write::READ) + install_read_before_delay(entry.m_addrstart & ~lowbits_mask, entry.m_addrend | lowbits_mask, entry.m_addrmirror, d); + else + install_write_before_delay(entry.m_addrstart & ~lowbits_mask, entry.m_addrend | lowbits_mask, entry.m_addrmirror, d); } - return unassigned; + if (!entry.m_after_delay.isnull()) { + auto d = entry.m_after_delay; + d.resolve(); + if (readorwrite == read_or_write::READ) + install_read_after_delay(entry.m_addrstart & ~lowbits_mask, entry.m_addrend | lowbits_mask, entry.m_addrmirror, d); + else + install_write_after_delay(entry.m_addrstart & ~lowbits_mask, entry.m_addrend | lowbits_mask, entry.m_addrmirror, d); + } } -memory_passthrough_handler *address_space::install_read_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u8 &data, u8 mem_mask)> tap, memory_passthrough_handler *mph) + +memory_passthrough_handler address_space_installer::install_read_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u8 &data, u8 mem_mask)> tap, memory_passthrough_handler *mph) { fatalerror("Trying to install a 8-bits wide bus read tap in a %d-bits wide bus\n", data_width()); } -memory_passthrough_handler *address_space::install_read_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u16 &data, u16 mem_mask)> tap, memory_passthrough_handler *mph) +memory_passthrough_handler address_space_installer::install_read_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u16 &data, u16 mem_mask)> tap, memory_passthrough_handler *mph) { fatalerror("Trying to install a 16-bits wide bus read tap in a %d-bits wide bus\n", data_width()); } -memory_passthrough_handler *address_space::install_read_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u32 &data, u32 mem_mask)> tap, memory_passthrough_handler *mph) +memory_passthrough_handler address_space_installer::install_read_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u32 &data, u32 mem_mask)> tap, memory_passthrough_handler *mph) { fatalerror("Trying to install a 32-bits wide bus read tap in a %d-bits wide bus\n", data_width()); } -memory_passthrough_handler *address_space::install_read_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u64 &data, u64 mem_mask)> tap, memory_passthrough_handler *mph) +memory_passthrough_handler address_space_installer::install_read_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u64 &data, u64 mem_mask)> tap, memory_passthrough_handler *mph) { fatalerror("Trying to install a 64-bits wide bus read tap in a %d-bits wide bus\n", data_width()); } -memory_passthrough_handler *address_space::install_write_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u8 &data, u8 mem_mask)> tap, memory_passthrough_handler *mph) +memory_passthrough_handler address_space_installer::install_write_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u8 &data, u8 mem_mask)> tap, memory_passthrough_handler *mph) { fatalerror("Trying to install a 8-bits wide bus write tap in a %d-bits wide bus\n", data_width()); } -memory_passthrough_handler *address_space::install_write_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u16 &data, u16 mem_mask)> tap, memory_passthrough_handler *mph) +memory_passthrough_handler address_space_installer::install_write_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u16 &data, u16 mem_mask)> tap, memory_passthrough_handler *mph) { fatalerror("Trying to install a 16-bits wide bus write tap in a %d-bits wide bus\n", data_width()); } -memory_passthrough_handler *address_space::install_write_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u32 &data, u32 mem_mask)> tap, memory_passthrough_handler *mph) +memory_passthrough_handler address_space_installer::install_write_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u32 &data, u32 mem_mask)> tap, memory_passthrough_handler *mph) { fatalerror("Trying to install a 32-bits wide bus write tap in a %d-bits wide bus\n", data_width()); } -memory_passthrough_handler *address_space::install_write_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u64 &data, u64 mem_mask)> tap, memory_passthrough_handler *mph) +memory_passthrough_handler address_space_installer::install_write_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u64 &data, u64 mem_mask)> tap, memory_passthrough_handler *mph) { fatalerror("Trying to install a 64-bits wide bus write tap in a %d-bits wide bus\n", data_width()); } -memory_passthrough_handler *address_space::install_readwrite_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u8 &data, u8 mem_mask)> tapr, std::function<void (offs_t offset, u8 &data, u8 mem_mask)> tapw, memory_passthrough_handler *mph) +memory_passthrough_handler address_space_installer::install_readwrite_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u8 &data, u8 mem_mask)> tapr, std::function<void (offs_t offset, u8 &data, u8 mem_mask)> tapw, memory_passthrough_handler *mph) { fatalerror("Trying to install a 8-bits wide bus read/write tap in a %d-bits wide bus\n", data_width()); } -memory_passthrough_handler *address_space::install_readwrite_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u16 &data, u16 mem_mask)> tapr, std::function<void (offs_t offset, u16 &data, u16 mem_mask)> tapw, memory_passthrough_handler *mph) +memory_passthrough_handler address_space_installer::install_readwrite_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u16 &data, u16 mem_mask)> tapr, std::function<void (offs_t offset, u16 &data, u16 mem_mask)> tapw, memory_passthrough_handler *mph) { fatalerror("Trying to install a 16-bits wide bus read/write tap in a %d-bits wide bus\n", data_width()); } -memory_passthrough_handler *address_space::install_readwrite_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u32 &data, u32 mem_mask)> tapr, std::function<void (offs_t offset, u32 &data, u32 mem_mask)> tap, memory_passthrough_handler *mph) +memory_passthrough_handler address_space_installer::install_readwrite_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u32 &data, u32 mem_mask)> tapr, std::function<void (offs_t offset, u32 &data, u32 mem_mask)> tap, memory_passthrough_handler *mph) { fatalerror("Tryingw to install a 32-bits wide bus read/write tap in a %d-bits wide bus\n", data_width()); } -memory_passthrough_handler *address_space::install_readwrite_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u64 &data, u64 mem_mask)> tapr, std::function<void (offs_t offset, u64 &data, u64 mem_mask)> tapw, memory_passthrough_handler *mph) +memory_passthrough_handler address_space_installer::install_readwrite_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u64 &data, u64 mem_mask)> tapr, std::function<void (offs_t offset, u64 &data, u64 mem_mask)> tapw, memory_passthrough_handler *mph) { fatalerror("Trying to install a 64-bits wide bus read/write tap in a %d-bits wide bus\n", data_width()); } - -//------------------------------------------------- -// get_handler_string - return a string -// describing the handler at a particular offset -//------------------------------------------------- - -template<int Width, int AddrShift, endianness_t Endian> std::string address_space_specific<Width, AddrShift, Endian>::get_handler_string(read_or_write readorwrite, offs_t address) const -{ - if (readorwrite == read_or_write::READ) { - offs_t start, end; - handler_entry_read<Width, AddrShift, Endian> *handler; - m_root_read->lookup(address, start, end, handler); - return handler->name(); - } else { - offs_t start, end; - handler_entry_write<Width, AddrShift, Endian> *handler; - m_root_write->lookup(address, start, end, handler); - return handler->name(); - } -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::dump_maps(std::vector<memory_entry> &read_map, std::vector<memory_entry> &write_map) const -{ - read_map.clear(); - write_map.clear(); - m_root_read->dump_map(read_map); - m_root_write->dump_map(write_map); -} - - -//************************************************************************** -// DYNAMIC ADDRESS SPACE MAPPING -//************************************************************************** - -//------------------------------------------------- -// unmap - unmap a section of address space -//------------------------------------------------- - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::unmap_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, read_or_write readorwrite, bool quiet) -{ - VPRINTF(("address_space::unmap(%s-%s mirror=%s, %s, %s)\n", - core_i64_hex_format(addrstart, m_addrchars), core_i64_hex_format(addrend, m_addrchars), - core_i64_hex_format(addrmirror, m_addrchars), - (readorwrite == read_or_write::READ) ? "read" : (readorwrite == read_or_write::WRITE) ? "write" : (readorwrite == read_or_write::READWRITE) ? "read/write" : "??", - quiet ? "quiet" : "normal")); - - offs_t nstart, nend, nmask, nmirror; - check_optimize_mirror("unmap_generic", addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror); - - // read space - if (readorwrite == read_or_write::READ || readorwrite == read_or_write::READWRITE) { - auto handler = static_cast<handler_entry_read<Width, AddrShift, Endian> *>(quiet ? m_nop_r : m_unmap_r); - handler->ref(); - m_root_read->populate(nstart, nend, nmirror, handler); - } - - // write space - if (readorwrite == read_or_write::WRITE || readorwrite == read_or_write::READWRITE) { - auto handler = static_cast<handler_entry_write<Width, AddrShift, Endian> *>(quiet ? m_nop_w : m_unmap_w); - handler->ref(); - m_root_write->populate(nstart, nend, nmirror, handler); - } - - invalidate_caches(readorwrite); -} - -//------------------------------------------------- -// install_read_tap - install a read tap on the bus -//------------------------------------------------- - -template<int Width, int AddrShift, endianness_t Endian> memory_passthrough_handler *address_space_specific<Width, AddrShift, Endian>::install_read_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, uX &data, uX mem_mask)> tap, memory_passthrough_handler *mph) -{ - offs_t nstart, nend, nmask, nmirror; - check_optimize_mirror("install_read_tap", addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror); - if(!mph) { - m_mphs.emplace_back(std::make_unique<memory_passthrough_handler>(*this)); - mph = m_mphs.back().get(); - } - - auto handler = new handler_entry_read_tap<Width, AddrShift, Endian>(this, *mph, name, tap); - m_root_read->populate_passthrough(nstart, nend, nmirror, handler); - handler->unref(); - - invalidate_caches(read_or_write::READ); - - return mph; -} - -//------------------------------------------------- -// install_write_tap - install a write tap on the bus -//------------------------------------------------- - -template<int Width, int AddrShift, endianness_t Endian> memory_passthrough_handler *address_space_specific<Width, AddrShift, Endian>::install_write_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, uX &data, uX mem_mask)> tap, memory_passthrough_handler *mph) -{ - offs_t nstart, nend, nmask, nmirror; - check_optimize_mirror("install_write_tap", addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror); - if(!mph) { - m_mphs.emplace_back(std::make_unique<memory_passthrough_handler>(*this)); - mph = m_mphs.back().get(); - } - - auto handler = new handler_entry_write_tap<Width, AddrShift, Endian>(this, *mph, name, tap); - m_root_write->populate_passthrough(nstart, nend, nmirror, handler); - handler->unref(); - - invalidate_caches(read_or_write::WRITE); - - return mph; -} -//------------------------------------------------- -// install_write_tap - install a read and a write tap on the bus -//------------------------------------------------- - -template<int Width, int AddrShift, endianness_t Endian> memory_passthrough_handler *address_space_specific<Width, AddrShift, Endian>::install_readwrite_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, uX &data, uX mem_mask)> tapr, std::function<void (offs_t offset, uX &data, uX mem_mask)> tapw, memory_passthrough_handler *mph) -{ - offs_t nstart, nend, nmask, nmirror; - check_optimize_mirror("install_readwrite_tap", addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror); - if(!mph) { - m_mphs.emplace_back(std::make_unique<memory_passthrough_handler>(*this)); - mph = m_mphs.back().get(); - } - - auto rhandler = new handler_entry_read_tap <Width, AddrShift, Endian>(this, *mph, name, tapr); - m_root_read ->populate_passthrough(nstart, nend, nmirror, rhandler); - rhandler->unref(); - - auto whandler = new handler_entry_write_tap<Width, AddrShift, Endian>(this, *mph, name, tapw); - m_root_write->populate_passthrough(nstart, nend, nmirror, whandler); - whandler->unref(); - - invalidate_caches(read_or_write::READWRITE); - - return mph; -} - - - - - -//------------------------------------------------- -// install_device_delegate - install the memory map -// of a live device into this address space -//------------------------------------------------- - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_device_delegate(offs_t addrstart, offs_t addrend, device_t &device, address_map_constructor &delegate, u64 unitmask, int cswidth) -{ - check_address("install_device_delegate", addrstart, addrend); - address_map map(*this, addrstart, addrend, unitmask, cswidth, m_device, delegate); - map.import_submaps(m_manager.machine(), device, data_width(), endianness(), addr_shift()); - populate_from_map(&map); -} - - - -//------------------------------------------------- -// install_readwrite_port - install a new I/O port -// handler into this address space -//------------------------------------------------- - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_readwrite_port(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string rtag, std::string wtag) -{ - VPRINTF(("address_space::install_readwrite_port(%s-%s mirror=%s, read=\"%s\" / write=\"%s\")\n", - core_i64_hex_format(addrstart, m_addrchars), core_i64_hex_format(addrend, m_addrchars), - core_i64_hex_format(addrmirror, m_addrchars), - rtag.empty() ? "(none)" : rtag.c_str(), wtag.empty() ? "(none)" : wtag.c_str())); - - offs_t nstart, nend, nmask, nmirror; - check_optimize_mirror("install_readwrite_port", addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror); - - // read handler - if (rtag != "") - { - // find the port - ioport_port *port = device().owner()->ioport(rtag); - if (port == nullptr) - throw emu_fatalerror("Attempted to map non-existent port '%s' for read in space %s of device '%s'\n", rtag.c_str(), m_name, m_device.tag()); - - // map the range and set the ioport - auto hand_r = new handler_entry_read_ioport<Width, AddrShift, Endian>(this, port); - m_root_read->populate(nstart, nend, nmirror, hand_r); - } - - if (wtag != "") - { - // find the port - ioport_port *port = device().owner()->ioport(wtag); - if (port == nullptr) - fatalerror("Attempted to map non-existent port '%s' for write in space %s of device '%s'\n", wtag.c_str(), m_name, m_device.tag()); - - // map the range and set the ioport - auto hand_w = new handler_entry_write_ioport<Width, AddrShift, Endian>(this, port); - m_root_write->populate(nstart, nend, nmirror, hand_w); - } - - invalidate_caches(rtag != "" ? wtag != "" ? read_or_write::READWRITE : read_or_write::READ : read_or_write::WRITE); -} - - -//------------------------------------------------- -// install_bank_generic - install a range as -// mapping to a particular bank -//------------------------------------------------- - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_bank_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string rtag, std::string wtag) -{ - VPRINTF(("address_space::install_readwrite_bank(%s-%s mirror=%s, read=\"%s\" / write=\"%s\")\n", - core_i64_hex_format(addrstart, m_addrchars), core_i64_hex_format(addrend, m_addrchars), - core_i64_hex_format(addrmirror, m_addrchars), - rtag.empty() ? "(none)" : rtag.c_str(), wtag.empty() ? "(none)" : wtag.c_str())); - - offs_t nstart, nend, nmask, nmirror; - check_optimize_mirror("install_bank_generic", addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror); - - // map the read bank - if (rtag != "") - { - std::string fulltag = device().siblingtag(rtag); - memory_bank &bank = bank_find_or_allocate(fulltag.c_str(), addrstart, addrend, addrmirror, read_or_write::READ); - - auto hand_r = new handler_entry_read_memory_bank<Width, AddrShift, Endian>(this, bank); - hand_r->set_address_info(nstart, nmask); - m_root_read->populate(nstart, nend, nmirror, hand_r); - } - - // map the write bank - if (wtag != "") - { - std::string fulltag = device().siblingtag(wtag); - memory_bank &bank = bank_find_or_allocate(fulltag.c_str(), addrstart, addrend, addrmirror, read_or_write::WRITE); - - auto hand_w = new handler_entry_write_memory_bank<Width, AddrShift, Endian>(this, bank); - hand_w->set_address_info(nstart, nmask); - m_root_write->populate(nstart, nend, nmirror, hand_w); - } - - invalidate_caches(rtag != "" ? wtag != "" ? read_or_write::READWRITE : read_or_write::READ : read_or_write::WRITE); -} - - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_bank_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, memory_bank *rbank, memory_bank *wbank) -{ - VPRINTF(("address_space::install_readwrite_bank(%s-%s mirror=%s, read=\"%s\" / write=\"%s\")\n", - core_i64_hex_format(addrstart, m_addrchars), core_i64_hex_format(addrend, m_addrchars), - core_i64_hex_format(addrmirror, m_addrchars), - (rbank != nullptr) ? rbank->tag() : "(none)", (wbank != nullptr) ? wbank->tag() : "(none)")); - - offs_t nstart, nend, nmask, nmirror; - check_optimize_mirror("install_bank_generic", addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror); - - // map the read bank - if (rbank != nullptr) - { - auto hand_r = new handler_entry_read_memory_bank<Width, AddrShift, Endian>(this, *rbank); - hand_r->set_address_info(nstart, nmask); - m_root_read->populate(nstart, nend, nmirror, hand_r); - } - - // map the write bank - if (wbank != nullptr) - { - auto hand_w = new handler_entry_write_memory_bank<Width, AddrShift, Endian>(this, *wbank); - hand_w->set_address_info(nstart, nmask); - m_root_write->populate(nstart, nend, nmirror, hand_w); - } - - invalidate_caches(rbank ? wbank ? read_or_write::READWRITE : read_or_write::READ : read_or_write::WRITE); -} - - -//------------------------------------------------- -// install_ram_generic - install a simple fixed -// RAM region into the given address space -//------------------------------------------------- - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_ram_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, read_or_write readorwrite, void *baseptr) -{ - VPRINTF(("address_space::install_ram_generic(%s-%s mirror=%s, %s, %p)\n", - core_i64_hex_format(addrstart, m_addrchars), core_i64_hex_format(addrend, m_addrchars), - core_i64_hex_format(addrmirror, m_addrchars), - (readorwrite == read_or_write::READ) ? "read" : (readorwrite == read_or_write::WRITE) ? "write" : (readorwrite == read_or_write::READWRITE) ? "read/write" : "??", - baseptr)); - - offs_t nstart, nend, nmask, nmirror; - check_optimize_mirror("install_ram_generic", addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror); - - // map for read - if (readorwrite == read_or_write::READ || readorwrite == read_or_write::READWRITE) - { - // find a bank and map it - memory_bank &bank = bank_find_or_allocate(nullptr, addrstart, addrend, addrmirror, read_or_write::READ); - - // if we are provided a pointer, set it - if (baseptr != nullptr) - bank.set_base(baseptr); - - // if we don't have a bank pointer yet, try to find one - if (bank.base() == nullptr) - { - void *backing = find_backing_memory(addrstart, addrend); - if (backing != nullptr) - bank.set_base(backing); - } - - // if we still don't have a pointer, and we're past the initialization phase, allocate a new block - if (bank.base() == nullptr && m_manager.m_initialized) - { - if (m_manager.machine().phase() >= machine_phase::RESET) - fatalerror("Attempted to call install_ram_generic() after initialization time without a baseptr!\n"); - auto block = std::make_unique<memory_block>(*this, addrstart, addrend); - bank.set_base(block.get()->data()); - m_manager.m_blocklist.push_back(std::move(block)); - } - - auto hand_r = new handler_entry_read_memory<Width, AddrShift, Endian>(this); - if (bank.base()) - hand_r->set_base(static_cast<uX *>(bank.base())); - else { - delayed_ref(hand_r); - bank.add_notifier([this, hand_r](void *base) { hand_r->set_base(static_cast<uX *>(base)); delayed_unref(hand_r); }); - } - - hand_r->set_address_info(nstart, nmask); - m_root_read->populate(nstart, nend, nmirror, hand_r); - } - - // map for write - if (readorwrite == read_or_write::WRITE || readorwrite == read_or_write::READWRITE) - { - // find a bank and map it - memory_bank &bank = bank_find_or_allocate(nullptr, addrstart, addrend, addrmirror, read_or_write::WRITE); - - // if we are provided a pointer, set it - if (baseptr != nullptr) - bank.set_base(baseptr); - - // if we don't have a bank pointer yet, try to find one - if (bank.base() == nullptr) - { - void *backing = find_backing_memory(addrstart, addrend); - if (backing != nullptr) - bank.set_base(backing); - } - - // if we still don't have a pointer, and we're past the initialization phase, allocate a new block - if (bank.base() == nullptr && m_manager.m_initialized) - { - if (m_manager.machine().phase() >= machine_phase::RESET) - fatalerror("Attempted to call install_ram_generic() after initialization time without a baseptr!\n"); - auto block = std::make_unique<memory_block>(*this, address_to_byte(addrstart), address_to_byte_end(addrend)); - bank.set_base(block.get()->data()); - m_manager.m_blocklist.push_back(std::move(block)); - } - - auto hand_w = new handler_entry_write_memory<Width, AddrShift, Endian>(this); - if (bank.base()) - hand_w->set_base(static_cast<uX *>(bank.base())); - else { - delayed_ref(hand_w); - bank.add_notifier([this, hand_w](void *base) { hand_w->set_base(static_cast<uX *>(base)); delayed_unref(hand_w); }); - } - - hand_w->set_address_info(nstart, nmask); - m_root_write->populate(nstart, nend, nmirror, hand_w); - } - - invalidate_caches(readorwrite); -} - - -//------------------------------------------------- -// install_handler - install 8-bit read/write -// delegate handlers for the space -//------------------------------------------------- - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8_delegate handler, u64 unitmask, int cswidth) -{ - install_read_handler_helper<0>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8_delegate handler, u64 unitmask, int cswidth) -{ - install_write_handler_helper<0>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8_delegate rhandler, write8_delegate whandler, u64 unitmask, int cswidth) -{ - install_readwrite_handler_helper<0>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8m_delegate handler, u64 unitmask, int cswidth) -{ - install_read_handler_helper<0>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8m_delegate handler, u64 unitmask, int cswidth) -{ - install_write_handler_helper<0>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8m_delegate rhandler, write8m_delegate whandler, u64 unitmask, int cswidth) -{ - install_readwrite_handler_helper<0>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8s_delegate handler, u64 unitmask, int cswidth) -{ - install_read_handler_helper<0>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8s_delegate handler, u64 unitmask, int cswidth) -{ - install_write_handler_helper<0>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8s_delegate rhandler, write8s_delegate whandler, u64 unitmask, int cswidth) -{ - install_readwrite_handler_helper<0>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8sm_delegate handler, u64 unitmask, int cswidth) -{ - install_read_handler_helper<0>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8sm_delegate handler, u64 unitmask, int cswidth) -{ - install_write_handler_helper<0>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8sm_delegate rhandler, write8sm_delegate whandler, u64 unitmask, int cswidth) -{ - install_readwrite_handler_helper<0>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8mo_delegate handler, u64 unitmask, int cswidth) -{ - install_read_handler_helper<0>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8mo_delegate handler, u64 unitmask, int cswidth) -{ - install_write_handler_helper<0>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8mo_delegate rhandler, write8mo_delegate whandler, u64 unitmask, int cswidth) -{ - install_readwrite_handler_helper<0>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8smo_delegate handler, u64 unitmask, int cswidth) -{ - install_read_handler_helper<0>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8smo_delegate handler, u64 unitmask, int cswidth) -{ - install_write_handler_helper<0>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8smo_delegate rhandler, write8smo_delegate whandler, u64 unitmask, int cswidth) -{ - install_readwrite_handler_helper<0>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); -} - - -//------------------------------------------------- -// install_handler - install 16-bit read/write -// delegate handlers for the space -//------------------------------------------------- - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16_delegate handler, u64 unitmask, int cswidth) -{ - install_read_handler_helper<1>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16_delegate handler, u64 unitmask, int cswidth) -{ - install_write_handler_helper<1>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16_delegate rhandler, write16_delegate whandler, u64 unitmask, int cswidth) -{ - install_readwrite_handler_helper<1>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16m_delegate handler, u64 unitmask, int cswidth) -{ - install_read_handler_helper<1>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16m_delegate handler, u64 unitmask, int cswidth) -{ - install_write_handler_helper<1>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16m_delegate rhandler, write16m_delegate whandler, u64 unitmask, int cswidth) -{ - install_readwrite_handler_helper<1>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16s_delegate handler, u64 unitmask, int cswidth) -{ - install_read_handler_helper<1>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16s_delegate handler, u64 unitmask, int cswidth) -{ - install_write_handler_helper<1>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16s_delegate rhandler, write16s_delegate whandler, u64 unitmask, int cswidth) -{ - install_readwrite_handler_helper<1>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16sm_delegate handler, u64 unitmask, int cswidth) -{ - install_read_handler_helper<1>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16sm_delegate handler, u64 unitmask, int cswidth) -{ - install_write_handler_helper<1>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16sm_delegate rhandler, write16sm_delegate whandler, u64 unitmask, int cswidth) -{ - install_readwrite_handler_helper<1>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16smo_delegate handler, u64 unitmask, int cswidth) -{ - install_read_handler_helper<1>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16smo_delegate handler, u64 unitmask, int cswidth) -{ - install_write_handler_helper<1>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16smo_delegate rhandler, write16smo_delegate whandler, u64 unitmask, int cswidth) -{ - install_readwrite_handler_helper<1>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16mo_delegate handler, u64 unitmask, int cswidth) -{ - install_read_handler_helper<1>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16mo_delegate handler, u64 unitmask, int cswidth) -{ - install_write_handler_helper<1>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16mo_delegate rhandler, write16mo_delegate whandler, u64 unitmask, int cswidth) -{ - install_readwrite_handler_helper<1>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); -} - - -//------------------------------------------------- -// install_handler - install 32-bit read/write -// delegate handlers for the space -//------------------------------------------------- - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32_delegate handler, u64 unitmask, int cswidth) -{ - install_read_handler_helper<2>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32_delegate handler, u64 unitmask, int cswidth) -{ - install_write_handler_helper<2>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32_delegate rhandler, write32_delegate whandler, u64 unitmask, int cswidth) -{ - install_readwrite_handler_helper<2>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32m_delegate handler, u64 unitmask, int cswidth) -{ - install_read_handler_helper<2>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32m_delegate handler, u64 unitmask, int cswidth) -{ - install_write_handler_helper<2>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32m_delegate rhandler, write32m_delegate whandler, u64 unitmask, int cswidth) -{ - install_readwrite_handler_helper<2>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32s_delegate handler, u64 unitmask, int cswidth) -{ - install_read_handler_helper<2>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32s_delegate handler, u64 unitmask, int cswidth) -{ - install_write_handler_helper<2>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32s_delegate rhandler, write32s_delegate whandler, u64 unitmask, int cswidth) -{ - install_readwrite_handler_helper<2>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32sm_delegate handler, u64 unitmask, int cswidth) -{ - install_read_handler_helper<2>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32sm_delegate handler, u64 unitmask, int cswidth) -{ - install_write_handler_helper<2>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32sm_delegate rhandler, write32sm_delegate whandler, u64 unitmask, int cswidth) -{ - install_readwrite_handler_helper<2>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32mo_delegate handler, u64 unitmask, int cswidth) -{ - install_read_handler_helper<2>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32mo_delegate handler, u64 unitmask, int cswidth) -{ - install_write_handler_helper<2>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32mo_delegate rhandler, write32mo_delegate whandler, u64 unitmask, int cswidth) -{ - install_readwrite_handler_helper<2>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32smo_delegate handler, u64 unitmask, int cswidth) -{ - install_read_handler_helper<2>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32smo_delegate handler, u64 unitmask, int cswidth) -{ - install_write_handler_helper<2>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32smo_delegate rhandler, write32smo_delegate whandler, u64 unitmask, int cswidth) -{ - install_readwrite_handler_helper<2>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); -} - - -//------------------------------------------------- -// install_handler64 - install 64-bit read/write -// delegate handlers for the space -//------------------------------------------------- - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64_delegate handler, u64 unitmask, int cswidth) -{ - install_read_handler_helper<3>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64_delegate handler, u64 unitmask, int cswidth) -{ - install_write_handler_helper<3>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64_delegate rhandler, write64_delegate whandler, u64 unitmask, int cswidth) -{ - install_readwrite_handler_helper<3>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64m_delegate handler, u64 unitmask, int cswidth) -{ - install_read_handler_helper<3>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64m_delegate handler, u64 unitmask, int cswidth) -{ - install_write_handler_helper<3>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64m_delegate rhandler, write64m_delegate whandler, u64 unitmask, int cswidth) -{ - install_readwrite_handler_helper<3>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64s_delegate handler, u64 unitmask, int cswidth) -{ - install_read_handler_helper<3>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64s_delegate handler, u64 unitmask, int cswidth) -{ - install_write_handler_helper<3>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64s_delegate rhandler, write64s_delegate whandler, u64 unitmask, int cswidth) -{ - install_readwrite_handler_helper<3>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64sm_delegate handler, u64 unitmask, int cswidth) -{ - install_read_handler_helper<3>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64sm_delegate handler, u64 unitmask, int cswidth) -{ - install_write_handler_helper<3>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64sm_delegate rhandler, write64sm_delegate whandler, u64 unitmask, int cswidth) -{ - install_readwrite_handler_helper<3>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64mo_delegate handler, u64 unitmask, int cswidth) -{ - install_read_handler_helper<3>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64mo_delegate handler, u64 unitmask, int cswidth) -{ - install_write_handler_helper<3>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64mo_delegate rhandler, write64mo_delegate whandler, u64 unitmask, int cswidth) -{ - install_readwrite_handler_helper<3>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64smo_delegate handler, u64 unitmask, int cswidth) -{ - install_read_handler_helper<3>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64smo_delegate handler, u64 unitmask, int cswidth) -{ - install_write_handler_helper<3>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler); -} - -template<int Width, int AddrShift, endianness_t Endian> void address_space_specific<Width, AddrShift, Endian>::install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64smo_delegate rhandler, write64smo_delegate whandler, u64 unitmask, int cswidth) -{ - install_readwrite_handler_helper<3>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); -} - -//************************************************************************** -// MEMORY MAPPING HELPERS -//************************************************************************** - -//------------------------------------------------- -// find_backing_memory - return a pointer to -// the base of RAM associated with the given -// device and offset -//------------------------------------------------- - -void *address_space::find_backing_memory(offs_t addrstart, offs_t addrend) -{ - VPRINTF(("address_space::find_backing_memory('%s',%s,%08X-%08X) -> ", m_device.tag(), m_name, addrstart, addrend)); - - if (m_map == nullptr) - return nullptr; - - // look in the address map first, last winning for overrides - void *result = nullptr; - for (address_map_entry &entry : m_map->m_entrylist) - { - if (entry.m_memory != nullptr && addrstart >= entry.m_addrstart && addrend <= entry.m_addrend) - { - VPRINTF(("found in entry %08X-%08X [%p]\n", entry.m_addrstart, entry.m_addrend, (u8 *)entry.m_memory + address_to_byte(addrstart - entry.m_addrstart))); - result = (u8 *)entry.m_memory + address_to_byte(addrstart - entry.m_addrstart); - } - } - if (result) - return result; - - // if not found there, look in the allocated blocks - for (auto &block : m_manager.m_blocklist) - if (block->contains(*this, addrstart, addrend)) - { - VPRINTF(("found in allocated memory block %08X-%08X [%p]\n", block->addrstart(), block->addrend(), block->data() + address_to_byte(addrstart - block->addrstart()))); - return block->data() + address_to_byte(addrstart - block->addrstart()); - } - - VPRINTF(("did not find\n")); - return nullptr; -} - - -//------------------------------------------------- -// space_needs_backing_store - return whether a -// given memory map entry implies the need of -// allocating and registering memory -//------------------------------------------------- - -bool address_space::needs_backing_store(const address_map_entry &entry) -{ - // if we are sharing, and we don't have a pointer yet, create one - if (entry.m_share != nullptr) - { - std::string fulltag = entry.m_devbase.subtag(entry.m_share); - auto share = m_manager.shares().find(fulltag.c_str()); - if (share != m_manager.shares().end() && share->second->ptr() == nullptr) - return true; - } - - // if we're writing to any sort of bank or RAM, then yes, we do need backing - if (entry.m_write.m_type == AMH_BANK || entry.m_write.m_type == AMH_RAM) - return true; - - // if we're reading from RAM or from ROM outside of address space 0 or its region, then yes, we do need backing - memory_region *region = m_manager.machine().root_device().memregion(m_device.tag()); - if (entry.m_read.m_type == AMH_RAM || - (entry.m_read.m_type == AMH_ROM && (m_spacenum != 0 || region == nullptr || entry.m_addrstart >= region->bytes()))) - return true; - - // all other cases don't need backing - return false; -} - -int address_space::add_change_notifier(std::function<void (read_or_write)> n) -{ - int id = m_notifier_id++; - m_notifiers.emplace_back(notifier_t{ n, id }); - return id; -} - -void address_space::remove_change_notifier(int id) -{ - for(auto i = m_notifiers.begin(); i != m_notifiers.end(); i++) - if(i->m_id == id) { - m_notifiers.erase(i); - return; - } - fatalerror("Unknown notifier id %d, double remove?\n", id); -} - - -//************************************************************************** -// BANKING HELPERS -//************************************************************************** - -//------------------------------------------------- -// bank_find_or_allocate - allocate a new -// bank, or find an existing one, and return the -// read/write handler -//------------------------------------------------- - -memory_bank &address_space::bank_find_or_allocate(const char *tag, offs_t addrstart, offs_t addrend, offs_t addrmirror, read_or_write readorwrite) -{ - // adjust the addresses, handling mirrors and such - offs_t addrmask = ~addrmirror; - adjust_addresses(addrstart, addrend, addrmask, addrmirror); - - // look up the bank by name, or else by byte range - memory_bank *membank = tag ? m_manager.find(tag) : m_manager.find(*this, addrstart, addrend); - - // if we don't have a bank yet, create a new one - if (!membank) - membank = m_manager.allocate(*this, addrstart, addrend, tag); - - // add a reference for this space - membank->add_reference(*this, readorwrite); - return *membank; -} - - -//************************************************************************** -// CACHE MEMORY RANGES -//************************************************************************** - -//------------------------------------------------- -// memory_access_cache - constructor -//------------------------------------------------- - -template<int Width, int AddrShift, int Endian> memory_access_cache<Width, AddrShift, Endian>::memory_access_cache(address_space &space, - handler_entry_read <Width, AddrShift, Endian> *root_read, - handler_entry_write<Width, AddrShift, Endian> *root_write) - : m_space(space), - m_addrmask(space.addrmask()), - m_addrstart_r(1), - m_addrend_r(0), - m_addrstart_w(1), - m_addrend_w(0), - m_cache_r(nullptr), - m_cache_w(nullptr), - m_root_read(root_read), - m_root_write(root_write) -{ - m_notifier_id = space.add_change_notifier([this](read_or_write mode) { - if(u32(mode) & u32(read_or_write::READ)) { - m_addrend_r = 0; - m_addrstart_r = 1; - m_cache_r = nullptr; - } - if(u32(mode) & u32(read_or_write::WRITE)) { - m_addrend_w = 0; - m_addrstart_w = 1; - m_cache_w = nullptr; - } - }); -} - - -//------------------------------------------------- -// ~memory_access_cache - destructor -//------------------------------------------------- - -template<int Width, int AddrShift, int Endian> memory_access_cache<Width, AddrShift, Endian>::~memory_access_cache() -{ - m_space.remove_change_notifier(m_notifier_id); -} - - -template class memory_access_cache<0, 1, ENDIANNESS_LITTLE>; -template class memory_access_cache<0, 1, ENDIANNESS_BIG>; -template class memory_access_cache<0, 0, ENDIANNESS_LITTLE>; -template class memory_access_cache<0, 0, ENDIANNESS_BIG>; -template class memory_access_cache<1, 3, ENDIANNESS_LITTLE>; -template class memory_access_cache<1, 3, ENDIANNESS_BIG>; -template class memory_access_cache<1, 0, ENDIANNESS_LITTLE>; -template class memory_access_cache<1, 0, ENDIANNESS_BIG>; -template class memory_access_cache<1, -1, ENDIANNESS_LITTLE>; -template class memory_access_cache<1, -1, ENDIANNESS_BIG>; -template class memory_access_cache<2, 0, ENDIANNESS_LITTLE>; -template class memory_access_cache<2, 0, ENDIANNESS_BIG>; -template class memory_access_cache<2, -1, ENDIANNESS_LITTLE>; -template class memory_access_cache<2, -1, ENDIANNESS_BIG>; -template class memory_access_cache<2, -2, ENDIANNESS_LITTLE>; -template class memory_access_cache<2, -2, ENDIANNESS_BIG>; -template class memory_access_cache<3, 0, ENDIANNESS_LITTLE>; -template class memory_access_cache<3, 0, ENDIANNESS_BIG>; -template class memory_access_cache<3, -1, ENDIANNESS_LITTLE>; -template class memory_access_cache<3, -1, ENDIANNESS_BIG>; -template class memory_access_cache<3, -2, ENDIANNESS_LITTLE>; -template class memory_access_cache<3, -2, ENDIANNESS_BIG>; -template class memory_access_cache<3, -3, ENDIANNESS_LITTLE>; -template class memory_access_cache<3, -3, ENDIANNESS_BIG>; - - - -//************************************************************************** -// MEMORY BLOCK -//************************************************************************** - -//------------------------------------------------- -// memory_block - constructor -//------------------------------------------------- - -memory_block::memory_block(address_space &space, offs_t addrstart, offs_t addrend, void *memory) - : m_machine(space.m_manager.machine()), - m_space(space), - m_addrstart(addrstart), - m_addrend(addrend), - m_data(reinterpret_cast<u8 *>(memory)) -{ - offs_t const length = space.address_to_byte(addrend + 1 - addrstart); - VPRINTF(("block_allocate('%s',%s,%08X,%08X,%p)\n", space.device().tag(), space.name(), addrstart, addrend, memory)); - - // allocate a block if needed - if (m_data == nullptr) - { - if (length < 4096) - { - m_allocated.resize(length); - memset(&m_allocated[0], 0, length); - m_data = &m_allocated[0]; - } - else - { - m_allocated.resize(length + 0xfff); - memset(&m_allocated[0], 0, length + 0xfff); - m_data = reinterpret_cast<u8 *>((reinterpret_cast<uintptr_t>(&m_allocated[0]) + 0xfff) & ~0xfff); - } - } - - // register for saving, but only if we're not part of a memory region - if (space.m_manager.region_containing(m_data, length) != nullptr) - VPRINTF(("skipping save of this memory block as it is covered by a memory region\n")); - else - { - int bytes_per_element = space.data_width() / 8; - std::string name = string_format("%08x-%08x", addrstart, addrend); - machine().save().save_memory(&space.device(), "memory", space.device().tag(), space.spacenum(), name.c_str(), m_data, bytes_per_element, (u32)length / bytes_per_element); - } -} - - -//------------------------------------------------- -// memory_block - destructor -//------------------------------------------------- - -memory_block::~memory_block() -{ -} - - - //************************************************************************** // MEMORY BANK //************************************************************************** @@ -2723,27 +966,13 @@ memory_block::~memory_block() // memory_bank - constructor //------------------------------------------------- -memory_bank::memory_bank(address_space &space, int index, offs_t addrstart, offs_t addrend, const char *tag) - : m_machine(space.m_manager.machine()), - m_anonymous(tag == nullptr), - m_addrstart(addrstart), - m_addrend(addrend), +memory_bank::memory_bank(device_t &device, std::string tag) + : m_machine(device.machine()), m_curentry(0) { - // generate an internal tag if we don't have one - if (tag == nullptr) - { - m_tag = string_format("~%d~", index); - m_name = string_format("Internal bank #%d", index); - } - else - { - m_tag = tag; - m_name = string_format("Bank '%s'", tag); - } - - if (!m_anonymous && machine().save().registration_allowed()) - machine().save().save_item(&space.device(), "memory", m_tag.c_str(), 0, NAME(m_curentry)); + m_tag = std::move(tag); + m_name = string_format("Bank '%s'", m_tag); + machine().save().save_item(&device, "memory", m_tag.c_str(), 0, NAME(m_curentry)); } @@ -2757,35 +986,6 @@ memory_bank::~memory_bank() //------------------------------------------------- -// references_space - walk the list of references -// to find a match against the provided space -// and read/write -//------------------------------------------------- - -bool memory_bank::references_space(const address_space &space, read_or_write readorwrite) const -{ - for (auto &ref : m_reflist) - if (ref->matches(space, readorwrite)) - return true; - return false; -} - - -//------------------------------------------------- -// add_reference - add a new reference to the -// given space -//------------------------------------------------- - -void memory_bank::add_reference(address_space &space, read_or_write readorwrite) -{ - // if we already have a reference, skip it - if (references_space(space, readorwrite)) - return; - m_reflist.push_back(std::make_unique<bank_reference>(space, readorwrite)); -} - - -//------------------------------------------------- // set_base - set the bank base explicitly //------------------------------------------------- @@ -2801,34 +1001,24 @@ void memory_bank::set_base(void *base) m_curentry = 0; } m_entries[m_curentry] = reinterpret_cast<u8 *>(base); - for(auto cb : m_alloc_notifier) - cb(base); - m_alloc_notifier.clear(); } //------------------------------------------------- -// add_notifier - add a function used to notify when the allocation is done -//------------------------------------------------- -void memory_bank::add_notifier(std::function<void (void *)> cb) -{ - m_alloc_notifier.emplace_back(cb); -} - -//------------------------------------------------- // set_entry - set the base to a pre-configured // entry //------------------------------------------------- void memory_bank::set_entry(int entrynum) { + if(entrynum == -1 && m_entries.empty()) + return; + // validate - if (m_anonymous) - throw emu_fatalerror("memory_bank::set_entry called for anonymous bank"); if (entrynum < 0 || entrynum >= int(m_entries.size())) - throw emu_fatalerror("memory_bank::set_entry called with out-of-range entry %d", entrynum); + throw emu_fatalerror("memory_bank::set_entry called for bank '%s' with out-of-range entry %d", m_tag, entrynum); if (m_entries[entrynum] == nullptr) - throw emu_fatalerror("memory_bank::set_entry called for bank '%s' with invalid bank entry %d", m_tag.c_str(), entrynum); + throw emu_fatalerror("memory_bank::set_entry called for bank '%s' with invalid bank entry %d", m_tag, entrynum); m_curentry = entrynum; } @@ -2864,7 +1054,7 @@ void memory_bank::configure_entries(int startentry, int numentries, void *base, // fill in the requested bank entries for (int entrynum = 0; entrynum < numentries; entrynum ++) - m_entries[entrynum + startentry] = reinterpret_cast<u8 *>(base) + entrynum * stride ; + m_entries[entrynum + startentry] = reinterpret_cast<u8 *>(base) + entrynum * stride; } @@ -2876,9 +1066,9 @@ void memory_bank::configure_entries(int startentry, int numentries, void *base, // memory_region - constructor //------------------------------------------------- -memory_region::memory_region(running_machine &machine, const char *name, u32 length, u8 width, endianness_t endian) +memory_region::memory_region(running_machine &machine, std::string name, u32 length, u8 width, endianness_t endian) : m_machine(machine), - m_name(name), + m_name(std::move(name)), m_buffer(length), m_endianness(endian), m_bitwidth(width * 8), @@ -2886,3 +1076,16 @@ memory_region::memory_region(running_machine &machine, const char *name, u32 len { assert(width == 1 || width == 2 || width == 4 || width == 8); } + +std::string memory_share::compare(u8 width, size_t bytes, endianness_t endianness) const +{ + if (width != m_bitwidth) + return util::string_format("share %s found with unexpected width (expected %d, found %d)", m_name, width, m_bitwidth); + if (bytes != m_bytes) + return util::string_format("share %s found with unexpected size (expected %x, found %x)", m_name, bytes, m_bytes); + if (endianness != m_endianness && m_bitwidth != 8) + return util::string_format("share %s found with unexpected endianness (expected %s, found %s)", m_name, + endianness == ENDIANNESS_LITTLE ? "little" : "big", + m_endianness == ENDIANNESS_LITTLE ? "little" : "big"); + return ""; +} diff --git a/src/emu/emumem.h b/src/emu/emumem.h index 0bd3de898ca..39470a45632 100644 --- a/src/emu/emumem.h +++ b/src/emu/emumem.h @@ -17,6 +17,10 @@ #ifndef MAME_EMU_EMUMEM_H #define MAME_EMU_EMUMEM_H +#include "notifier.h" + +#include <optional> +#include <set> #include <type_traits> using s8 = std::int8_t; @@ -48,6 +52,14 @@ enum class read_or_write }; +//************************************************************************** +// FORWARD DECLARATIONS +//************************************************************************** + +class handler_entry; +template<int Width, int AddrShift> class handler_entry_read_passthrough; +template<int Width, int AddrShift> class handler_entry_write_passthrough; + //************************************************************************** // TYPE DEFINITIONS @@ -59,30 +71,17 @@ using offs_t = u32; // address map constructors are delegates that build up an address_map using address_map_constructor = named_delegate<void (address_map &)>; -// struct with function pointers for accessors; use is generally discouraged unless necessary -struct data_accessors -{ - u8 (*read_byte)(address_space &space, offs_t address); - u16 (*read_word)(address_space &space, offs_t address); - u16 (*read_word_masked)(address_space &space, offs_t address, u16 mask); - u32 (*read_dword)(address_space &space, offs_t address); - u32 (*read_dword_masked)(address_space &space, offs_t address, u32 mask); - u64 (*read_qword)(address_space &space, offs_t address); - u64 (*read_qword_masked)(address_space &space, offs_t address, u64 mask); - - void (*write_byte)(address_space &space, offs_t address, u8 data); - void (*write_word)(address_space &space, offs_t address, u16 data); - void (*write_word_masked)(address_space &space, offs_t address, u16 data, u16 mask); - void (*write_dword)(address_space &space, offs_t address, u32 data); - void (*write_dword_masked)(address_space &space, offs_t address, u32 data, u32 mask); - void (*write_qword)(address_space &space, offs_t address, u64 data); - void (*write_qword_masked)(address_space &space, offs_t address, u64 data, u64 mask); +// a line in the memory structure dump +struct memory_entry_context { + memory_view *view; + bool disabled; + int slot; }; -// a line in the memory structure dump struct memory_entry { offs_t start, end; - class handler_entry *entry; + handler_entry *entry; + std::vector<memory_entry_context> context; }; @@ -154,387 +153,352 @@ using write32smo_delegate = device_delegate<void (u32)>; using write64smo_delegate = device_delegate<void (u64)>; -namespace emu { namespace detail { - -template <typename D, typename T, typename Enable = void> struct rw_device_class { }; - -template <typename D, typename T, typename Ret, typename... Params> -struct rw_device_class<D, Ret (T::*)(Params...), std::enable_if_t<std::is_constructible<D, Ret (T::*)(Params...), const char *, const char *, T *>::value> > { using type = T; }; -template <typename D, typename T, typename Ret, typename... Params> -struct rw_device_class<D, Ret (T::*)(Params...) const, std::enable_if_t<std::is_constructible<D, Ret (T::*)(Params...) const, const char *, const char *, T *>::value> > { using type = T; }; -template <typename D, typename T, typename Ret, typename... Params> -struct rw_device_class<D, Ret (*)(T &, Params...), std::enable_if_t<std::is_constructible<D, Ret (*)(T &, Params...), const char *, const char *, T *>::value> > { using type = T; }; - -template <typename D, typename T> using rw_device_class_t = typename rw_device_class <D, T>::type; - -template <typename T> -inline read8_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<read8_delegate, std::remove_reference_t<T> > *obj) -{ return read8_delegate(func, name, tag, obj); } -template <typename T> -inline read16_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<read16_delegate, std::remove_reference_t<T> > *obj) -{ return read16_delegate(func, name, tag, obj); } -template <typename T> -inline read32_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<read32_delegate, std::remove_reference_t<T> > *obj) -{ return read32_delegate(func, name, tag, obj); } -template <typename T> -inline read64_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<read64_delegate, std::remove_reference_t<T> > *obj) -{ return read64_delegate(func, name, tag, obj); } - -template <typename T> -inline write8_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<write8_delegate, std::remove_reference_t<T> > *obj) -{ return write8_delegate(func, name, tag, obj); } -template <typename T> -inline write16_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<write16_delegate, std::remove_reference_t<T> > *obj) -{ return write16_delegate(func, name, tag, obj); } -template <typename T> -inline write32_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<write32_delegate, std::remove_reference_t<T> > *obj) -{ return write32_delegate(func, name, tag, obj); } -template <typename T> -inline write64_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<write64_delegate, std::remove_reference_t<T> > *obj) -{ return write64_delegate(func, name, tag, obj); } - - -template <typename T> -inline read8m_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<read8m_delegate, std::remove_reference_t<T> > *obj) -{ return read8m_delegate(func, name, tag, obj); } -template <typename T> -inline read16m_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<read16m_delegate, std::remove_reference_t<T> > *obj) -{ return read16m_delegate(func, name, tag, obj); } -template <typename T> -inline read32m_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<read32m_delegate, std::remove_reference_t<T> > *obj) -{ return read32m_delegate(func, name, tag, obj); } -template <typename T> -inline read64m_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<read64m_delegate, std::remove_reference_t<T> > *obj) -{ return read64m_delegate(func, name, tag, obj); } - -template <typename T> -inline write8m_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<write8m_delegate, std::remove_reference_t<T> > *obj) -{ return write8m_delegate(func, name, tag, obj); } -template <typename T> -inline write16m_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<write16m_delegate, std::remove_reference_t<T> > *obj) -{ return write16m_delegate(func, name, tag, obj); } -template <typename T> -inline write32m_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<write32m_delegate, std::remove_reference_t<T> > *obj) -{ return write32m_delegate(func, name, tag, obj); } -template <typename T> -inline write64m_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<write64m_delegate, std::remove_reference_t<T> > *obj) -{ return write64m_delegate(func, name, tag, obj); } - - -template <typename T> -inline read8s_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<read8s_delegate, std::remove_reference_t<T> > *obj) -{ return read8s_delegate(func, name, tag, obj); } -template <typename T> -inline read16s_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<read16s_delegate, std::remove_reference_t<T> > *obj) -{ return read16s_delegate(func, name, tag, obj); } -template <typename T> -inline read32s_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<read32s_delegate, std::remove_reference_t<T> > *obj) -{ return read32s_delegate(func, name, tag, obj); } -template <typename T> -inline read64s_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<read64s_delegate, std::remove_reference_t<T> > *obj) -{ return read64s_delegate(func, name, tag, obj); } - -template <typename T> -inline write8s_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<write8s_delegate, std::remove_reference_t<T> > *obj) -{ return write8s_delegate(func, name, tag, obj); } -template <typename T> -inline write16s_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<write16s_delegate, std::remove_reference_t<T> > *obj) -{ return write16s_delegate(func, name, tag, obj); } -template <typename T> -inline write32s_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<write32s_delegate, std::remove_reference_t<T> > *obj) -{ return write32s_delegate(func, name, tag, obj); } -template <typename T> -inline write64s_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<write64s_delegate, std::remove_reference_t<T> > *obj) -{ return write64s_delegate(func, name, tag, obj); } - - -template <typename T> -inline read8sm_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<read8sm_delegate, std::remove_reference_t<T> > *obj) -{ return read8sm_delegate(func, name, tag, obj); } -template <typename T> -inline read16sm_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<read16sm_delegate, std::remove_reference_t<T> > *obj) -{ return read16sm_delegate(func, name, tag, obj); } -template <typename T> -inline read32sm_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<read32sm_delegate, std::remove_reference_t<T> > *obj) -{ return read32sm_delegate(func, name, tag, obj); } -template <typename T> -inline read64sm_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<read64sm_delegate, std::remove_reference_t<T> > *obj) -{ return read64sm_delegate(func, name, tag, obj); } - -template <typename T> -inline write8sm_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<write8sm_delegate, std::remove_reference_t<T> > *obj) -{ return write8sm_delegate(func, name, tag, obj); } -template <typename T> -inline write16sm_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<write16sm_delegate, std::remove_reference_t<T> > *obj) -{ return write16sm_delegate(func, name, tag, obj); } -template <typename T> -inline write32sm_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<write32sm_delegate, std::remove_reference_t<T> > *obj) -{ return write32sm_delegate(func, name, tag, obj); } -template <typename T> -inline write64sm_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<write64sm_delegate, std::remove_reference_t<T> > *obj) -{ return write64sm_delegate(func, name, tag, obj); } - +// =====================-> Wait states delegates -template <typename T> -inline read8mo_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<read8mo_delegate, std::remove_reference_t<T> > *obj) -{ return read8mo_delegate(func, name, tag, obj); } -template <typename T> -inline read16mo_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<read16mo_delegate, std::remove_reference_t<T> > *obj) -{ return read16mo_delegate(func, name, tag, obj); } -template <typename T> -inline read32mo_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<read32mo_delegate, std::remove_reference_t<T> > *obj) -{ return read32mo_delegate(func, name, tag, obj); } -template <typename T> -inline read64mo_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<read64mo_delegate, std::remove_reference_t<T> > *obj) -{ return read64mo_delegate(func, name, tag, obj); } +using ws_time_delegate = device_delegate<u64 (offs_t, u64)>; +using ws_delay_delegate = device_delegate<u32 (offs_t)>; -template <typename T> -inline write8mo_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<write8mo_delegate, std::remove_reference_t<T> > *obj) -{ return write8mo_delegate(func, name, tag, obj); } -template <typename T> -inline write16mo_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<write16mo_delegate, std::remove_reference_t<T> > *obj) -{ return write16mo_delegate(func, name, tag, obj); } -template <typename T> -inline write32mo_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<write32mo_delegate, std::remove_reference_t<T> > *obj) -{ return write32mo_delegate(func, name, tag, obj); } -template <typename T> -inline write64mo_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<write64mo_delegate, std::remove_reference_t<T> > *obj) -{ return write64mo_delegate(func, name, tag, obj); } +namespace emu::detail { -template <typename T> -inline read8smo_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<read8smo_delegate, std::remove_reference_t<T> > *obj) -{ return read8smo_delegate(func, name, tag, obj); } -template <typename T> -inline read16smo_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<read16smo_delegate, std::remove_reference_t<T> > *obj) -{ return read16smo_delegate(func, name, tag, obj); } -template <typename T> -inline read32smo_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<read32smo_delegate, std::remove_reference_t<T> > *obj) -{ return read32smo_delegate(func, name, tag, obj); } -template <typename T> -inline read64smo_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<read64smo_delegate, std::remove_reference_t<T> > *obj) -{ return read64smo_delegate(func, name, tag, obj); } +// TODO: work out why this doesn't work with clang and/or libc++ void_t but works with this C++14 work-alike +template <typename... T> struct void_wrapper { using type = void; }; +template <typename... T> using void_t = typename void_wrapper<T...>::type; -template <typename T> -inline write8smo_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<write8smo_delegate, std::remove_reference_t<T> > *obj) -{ return write8smo_delegate(func, name, tag, obj); } -template <typename T> -inline write16smo_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<write16smo_delegate, std::remove_reference_t<T> > *obj) -{ return write16smo_delegate(func, name, tag, obj); } -template <typename T> -inline write32smo_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<write32smo_delegate, std::remove_reference_t<T> > *obj) -{ return write32smo_delegate(func, name, tag, obj); } -template <typename T> -inline write64smo_delegate make_delegate(T &&func, const char *name, const char *tag, rw_device_class_t<write64smo_delegate, std::remove_reference_t<T> > *obj) -{ return write64smo_delegate(func, name, tag, obj); } +template <typename D, typename T, typename Enable = void> struct rw_device_class { }; +template <typename D, typename T, typename Ret, typename... Params> +struct rw_device_class<D, Ret (T::*)(Params...), std::enable_if_t<std::is_constructible<D, device_t &, const char *, Ret (T::*)(Params...), const char *>::value> > { using type = T; }; +template <typename D, typename T, typename Ret, typename... Params> +struct rw_device_class<D, Ret (T::*)(Params...) const, std::enable_if_t<std::is_constructible<D, device_t &, const char *, Ret (T::*)(Params...) const, const char *>::value> > { using type = T; }; +template <typename D, typename T, typename Ret, typename... Params> +struct rw_device_class<D, Ret (*)(T &, Params...), std::enable_if_t<std::is_constructible<D, device_t &, const char *, Ret (*)(T &, Params...), const char *>::value> > { using type = T; }; + +template <typename D, typename T> using rw_device_class_t = typename rw_device_class<D, T>::type; + +template <typename T, typename Enable = void> struct rw_delegate_type; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<read8_delegate, std::remove_reference_t<T> > > > { using type = read8_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<read16_delegate, std::remove_reference_t<T> > > > { using type = read16_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<read32_delegate, std::remove_reference_t<T> > > > { using type = read32_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<read64_delegate, std::remove_reference_t<T> > > > { using type = read64_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<read8m_delegate, std::remove_reference_t<T> > > > { using type = read8m_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<read16m_delegate, std::remove_reference_t<T> > > > { using type = read16m_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<read32m_delegate, std::remove_reference_t<T> > > > { using type = read32m_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<read64m_delegate, std::remove_reference_t<T> > > > { using type = read64m_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<read8s_delegate, std::remove_reference_t<T> > > > { using type = read8s_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<read16s_delegate, std::remove_reference_t<T> > > > { using type = read16s_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<read32s_delegate, std::remove_reference_t<T> > > > { using type = read32s_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<read64s_delegate, std::remove_reference_t<T> > > > { using type = read64s_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<read8sm_delegate, std::remove_reference_t<T> > > > { using type = read8sm_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<read16sm_delegate, std::remove_reference_t<T> > > > { using type = read16sm_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<read32sm_delegate, std::remove_reference_t<T> > > > { using type = read32sm_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<read64sm_delegate, std::remove_reference_t<T> > > > { using type = read64sm_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<read8mo_delegate, std::remove_reference_t<T> > > > { using type = read8mo_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<read16mo_delegate, std::remove_reference_t<T> > > > { using type = read16mo_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<read32mo_delegate, std::remove_reference_t<T> > > > { using type = read32mo_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<read64mo_delegate, std::remove_reference_t<T> > > > { using type = read64mo_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<read8smo_delegate, std::remove_reference_t<T> > > > { using type = read8smo_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<read16smo_delegate, std::remove_reference_t<T> > > > { using type = read16smo_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<read32smo_delegate, std::remove_reference_t<T> > > > { using type = read32smo_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<read64smo_delegate, std::remove_reference_t<T> > > > { using type = read64smo_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<write8_delegate, std::remove_reference_t<T> > > > { using type = write8_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<write16_delegate, std::remove_reference_t<T> > > > { using type = write16_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<write32_delegate, std::remove_reference_t<T> > > > { using type = write32_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<write64_delegate, std::remove_reference_t<T> > > > { using type = write64_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<write8m_delegate, std::remove_reference_t<T> > > > { using type = write8m_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<write16m_delegate, std::remove_reference_t<T> > > > { using type = write16m_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<write32m_delegate, std::remove_reference_t<T> > > > { using type = write32m_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<write64m_delegate, std::remove_reference_t<T> > > > { using type = write64m_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<write8s_delegate, std::remove_reference_t<T> > > > { using type = write8s_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<write16s_delegate, std::remove_reference_t<T> > > > { using type = write16s_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<write32s_delegate, std::remove_reference_t<T> > > > { using type = write32s_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<write64s_delegate, std::remove_reference_t<T> > > > { using type = write64s_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<write8sm_delegate, std::remove_reference_t<T> > > > { using type = write8sm_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<write16sm_delegate, std::remove_reference_t<T> > > > { using type = write16sm_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<write32sm_delegate, std::remove_reference_t<T> > > > { using type = write32sm_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<write64sm_delegate, std::remove_reference_t<T> > > > { using type = write64sm_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<write8mo_delegate, std::remove_reference_t<T> > > > { using type = write8mo_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<write16mo_delegate, std::remove_reference_t<T> > > > { using type = write16mo_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<write32mo_delegate, std::remove_reference_t<T> > > > { using type = write32mo_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<write64mo_delegate, std::remove_reference_t<T> > > > { using type = write64mo_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<write8smo_delegate, std::remove_reference_t<T> > > > { using type = write8smo_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<write16smo_delegate, std::remove_reference_t<T> > > > { using type = write16smo_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<write32smo_delegate, std::remove_reference_t<T> > > > { using type = write32smo_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> struct rw_delegate_type<T, void_t<rw_device_class_t<write64smo_delegate, std::remove_reference_t<T> > > > { using type = write64smo_delegate; using device_class = rw_device_class_t<type, std::remove_reference_t<T> >; }; +template <typename T> using rw_delegate_type_t = typename rw_delegate_type<T>::type; +template <typename T> using rw_delegate_device_class_t = typename rw_delegate_type<T>::device_class; template <typename L> -inline std::enable_if_t<std::is_constructible<read8_delegate, L, const char *>::value, read8_delegate> make_lr8_delegate(L l, const char *name) -{ return read8_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<read8_delegate, device_t &, L, const char *>::value, read8_delegate> make_lr8_delegate(device_t &owner, L &&l, const char *name) +{ return read8_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<read8m_delegate, L, const char *>::value, read8m_delegate> make_lr8_delegate(L l, const char *name) -{ return read8m_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<read8m_delegate, device_t &, L, const char *>::value, read8m_delegate> make_lr8_delegate(device_t &owner, L &&l, const char *name) +{ return read8m_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<read8s_delegate, L, const char *>::value, read8s_delegate> make_lr8_delegate(L l, const char *name) -{ return read8s_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<read8s_delegate, device_t &, L, const char *>::value, read8s_delegate> make_lr8_delegate(device_t &owner, L &&l, const char *name) +{ return read8s_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<read8sm_delegate, L, const char *>::value, read8sm_delegate> make_lr8_delegate(L l, const char *name) -{ return read8sm_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<read8sm_delegate, device_t &, L, const char *>::value, read8sm_delegate> make_lr8_delegate(device_t &owner, L &&l, const char *name) +{ return read8sm_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<read8mo_delegate, L, const char *>::value, read8mo_delegate> make_lr8_delegate(L l, const char *name) -{ return read8mo_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<read8mo_delegate, device_t &, L, const char *>::value, read8mo_delegate> make_lr8_delegate(device_t &owner, L &&l, const char *name) +{ return read8mo_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<read8smo_delegate, L, const char *>::value, read8smo_delegate> make_lr8_delegate(L l, const char *name) -{ return read8smo_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<read8smo_delegate, device_t &, L, const char *>::value, read8smo_delegate> make_lr8_delegate(device_t &owner, L &&l, const char *name) +{ return read8smo_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<read16_delegate, L, const char *>::value, read16_delegate> make_lr16_delegate(L l, const char *name) -{ return read16_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<read16_delegate, device_t &, L, const char *>::value, read16_delegate> make_lr16_delegate(device_t &owner, L &&l, const char *name) +{ return read16_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<read16m_delegate, L, const char *>::value, read16m_delegate> make_lr16_delegate(L l, const char *name) -{ return read16m_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<read16m_delegate, device_t &, L, const char *>::value, read16m_delegate> make_lr16_delegate(device_t &owner, L &&l, const char *name) +{ return read16m_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<read16s_delegate, L, const char *>::value, read16s_delegate> make_lr16_delegate(L l, const char *name) -{ return read16s_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<read16s_delegate, device_t &, L, const char *>::value, read16s_delegate> make_lr16_delegate(device_t &owner, L &&l, const char *name) +{ return read16s_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<read16sm_delegate, L, const char *>::value, read16sm_delegate> make_lr16_delegate(L l, const char *name) -{ return read16sm_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<read16sm_delegate, device_t &, L, const char *>::value, read16sm_delegate> make_lr16_delegate(device_t &owner, L &&l, const char *name) +{ return read16sm_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<read16mo_delegate, L, const char *>::value, read16mo_delegate> make_lr16_delegate(L l, const char *name) -{ return read16mo_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<read16mo_delegate, device_t &, L, const char *>::value, read16mo_delegate> make_lr16_delegate(device_t &owner, L &&l, const char *name) +{ return read16mo_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<read16smo_delegate, L, const char *>::value, read16smo_delegate> make_lr16_delegate(L l, const char *name) -{ return read16smo_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<read16smo_delegate, device_t &, L, const char *>::value, read16smo_delegate> make_lr16_delegate(device_t &owner, L &&l, const char *name) +{ return read16smo_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<read32_delegate, L, const char *>::value, read32_delegate> make_lr32_delegate(L l, const char *name) -{ return read32_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<read32_delegate, device_t &, L, const char *>::value, read32_delegate> make_lr32_delegate(device_t &owner, L &&l, const char *name) +{ return read32_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<read32m_delegate, L, const char *>::value, read32m_delegate> make_lr32_delegate(L l, const char *name) -{ return read32m_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<read32m_delegate, device_t &, L, const char *>::value, read32m_delegate> make_lr32_delegate(device_t &owner, L &&l, const char *name) +{ return read32m_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<read32s_delegate, L, const char *>::value, read32s_delegate> make_lr32_delegate(L l, const char *name) -{ return read32s_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<read32s_delegate, device_t &, L, const char *>::value, read32s_delegate> make_lr32_delegate(device_t &owner, L &&l, const char *name) +{ return read32s_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<read32sm_delegate, L, const char *>::value, read32sm_delegate> make_lr32_delegate(L l, const char *name) -{ return read32sm_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<read32sm_delegate, device_t &, L, const char *>::value, read32sm_delegate> make_lr32_delegate(device_t &owner, L &&l, const char *name) +{ return read32sm_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<read32mo_delegate, L, const char *>::value, read32mo_delegate> make_lr32_delegate(L l, const char *name) -{ return read32mo_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<read32mo_delegate, device_t &, L, const char *>::value, read32mo_delegate> make_lr32_delegate(device_t &owner, L &&l, const char *name) +{ return read32mo_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<read32smo_delegate, L, const char *>::value, read32smo_delegate> make_lr32_delegate(L l, const char *name) -{ return read32smo_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<read32smo_delegate, device_t &, L, const char *>::value, read32smo_delegate> make_lr32_delegate(device_t &owner, L &&l, const char *name) +{ return read32smo_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<read64_delegate, L, const char *>::value, read64_delegate> make_lr64_delegate(L l, const char *name) -{ return read64_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<read64_delegate, device_t &, L, const char *>::value, read64_delegate> make_lr64_delegate(device_t &owner, L &&l, const char *name) +{ return read64_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<read64m_delegate, L, const char *>::value, read64m_delegate> make_lr64_delegate(L l, const char *name) -{ return read64m_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<read64m_delegate, device_t &, L, const char *>::value, read64m_delegate> make_lr64_delegate(device_t &owner, L &&l, const char *name) +{ return read64m_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<read64s_delegate, L, const char *>::value, read64s_delegate> make_lr64_delegate(L l, const char *name) -{ return read64s_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<read64s_delegate, device_t &, L, const char *>::value, read64s_delegate> make_lr64_delegate(device_t &owner, L &&l, const char *name) +{ return read64s_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<read64sm_delegate, L, const char *>::value, read64sm_delegate> make_lr64_delegate(L l, const char *name) -{ return read64sm_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<read64sm_delegate, device_t &, L, const char *>::value, read64sm_delegate> make_lr64_delegate(device_t &owner, L &&l, const char *name) +{ return read64sm_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<read64mo_delegate, L, const char *>::value, read64mo_delegate> make_lr64_delegate(L l, const char *name) -{ return read64mo_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<read64mo_delegate, device_t &, L, const char *>::value, read64mo_delegate> make_lr64_delegate(device_t &owner, L &&l, const char *name) +{ return read64mo_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<read64smo_delegate, L, const char *>::value, read64smo_delegate> make_lr64_delegate(L l, const char *name) -{ return read64smo_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<read64smo_delegate, device_t &, L, const char *>::value, read64smo_delegate> make_lr64_delegate(device_t &owner, L &&l, const char *name) +{ return read64smo_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<write8_delegate, L, const char *>::value, write8_delegate> make_lw8_delegate(L l, const char *name) -{ return write8_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<write8_delegate, device_t &, L, const char *>::value, write8_delegate> make_lw8_delegate(device_t &owner, L &&l, const char *name) +{ return write8_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<write8m_delegate, L, const char *>::value, write8m_delegate> make_lw8_delegate(L l, const char *name) -{ return write8m_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<write8m_delegate, device_t &, L, const char *>::value, write8m_delegate> make_lw8_delegate(device_t &owner, L &&l, const char *name) +{ return write8m_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<write8s_delegate, L, const char *>::value, write8s_delegate> make_lw8_delegate(L l, const char *name) -{ return write8s_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<write8s_delegate, device_t &, L, const char *>::value, write8s_delegate> make_lw8_delegate(device_t &owner, L &&l, const char *name) +{ return write8s_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<write8sm_delegate, L, const char *>::value, write8sm_delegate> make_lw8_delegate(L l, const char *name) -{ return write8sm_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<write8sm_delegate, device_t &, L, const char *>::value, write8sm_delegate> make_lw8_delegate(device_t &owner, L &&l, const char *name) +{ return write8sm_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<write8mo_delegate, L, const char *>::value, write8mo_delegate> make_lw8_delegate(L l, const char *name) -{ return write8mo_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<write8mo_delegate, device_t &, L, const char *>::value, write8mo_delegate> make_lw8_delegate(device_t &owner, L &&l, const char *name) +{ return write8mo_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<write8smo_delegate, L, const char *>::value, write8smo_delegate> make_lw8_delegate(L l, const char *name) -{ return write8smo_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<write8smo_delegate, device_t &, L, const char *>::value, write8smo_delegate> make_lw8_delegate(device_t &owner, L &&l, const char *name) +{ return write8smo_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<write16_delegate, L, const char *>::value, write16_delegate> make_lw16_delegate(L l, const char *name) -{ return write16_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<write16_delegate, device_t &, L, const char *>::value, write16_delegate> make_lw16_delegate(device_t &owner, L &&l, const char *name) +{ return write16_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<write16m_delegate, L, const char *>::value, write16m_delegate> make_lw16_delegate(L l, const char *name) -{ return write16m_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<write16m_delegate, device_t &, L, const char *>::value, write16m_delegate> make_lw16_delegate(device_t &owner, L &&l, const char *name) +{ return write16m_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<write16s_delegate, L, const char *>::value, write16s_delegate> make_lw16_delegate(L l, const char *name) -{ return write16s_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<write16s_delegate, device_t &, L, const char *>::value, write16s_delegate> make_lw16_delegate(device_t &owner, L &&l, const char *name) +{ return write16s_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<write16sm_delegate, L, const char *>::value, write16sm_delegate> make_lw16_delegate(L l, const char *name) -{ return write16sm_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<write16sm_delegate, device_t &, L, const char *>::value, write16sm_delegate> make_lw16_delegate(device_t &owner, L &&l, const char *name) +{ return write16sm_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<write16mo_delegate, L, const char *>::value, write16mo_delegate> make_lw16_delegate(L l, const char *name) -{ return write16mo_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<write16mo_delegate, device_t &, L, const char *>::value, write16mo_delegate> make_lw16_delegate(device_t &owner, L &&l, const char *name) +{ return write16mo_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<write16smo_delegate, L, const char *>::value, write16smo_delegate> make_lw16_delegate(L l, const char *name) -{ return write16smo_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<write16smo_delegate, device_t &, L, const char *>::value, write16smo_delegate> make_lw16_delegate(device_t &owner, L &&l, const char *name) +{ return write16smo_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<write32_delegate, L, const char *>::value, write32_delegate> make_lw32_delegate(L l, const char *name) -{ return write32_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<write32_delegate, device_t &, L, const char *>::value, write32_delegate> make_lw32_delegate(device_t &owner, L &&l, const char *name) +{ return write32_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<write32m_delegate, L, const char *>::value, write32m_delegate> make_lw32_delegate(L l, const char *name) -{ return write32m_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<write32m_delegate, device_t &, L, const char *>::value, write32m_delegate> make_lw32_delegate(device_t &owner, L &&l, const char *name) +{ return write32m_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<write32s_delegate, L, const char *>::value, write32s_delegate> make_lw32_delegate(L l, const char *name) -{ return write32s_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<write32s_delegate, device_t &, L, const char *>::value, write32s_delegate> make_lw32_delegate(device_t &owner, L &&l, const char *name) +{ return write32s_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<write32sm_delegate, L, const char *>::value, write32sm_delegate> make_lw32_delegate(L l, const char *name) -{ return write32sm_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<write32sm_delegate, device_t &, L, const char *>::value, write32sm_delegate> make_lw32_delegate(device_t &owner, L &&l, const char *name) +{ return write32sm_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<write32mo_delegate, L, const char *>::value, write32mo_delegate> make_lw32_delegate(L l, const char *name) -{ return write32mo_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<write32mo_delegate, device_t &, L, const char *>::value, write32mo_delegate> make_lw32_delegate(device_t &owner, L &&l, const char *name) +{ return write32mo_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<write32smo_delegate, L, const char *>::value, write32smo_delegate> make_lw32_delegate(L l, const char *name) -{ return write32smo_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<write32smo_delegate, device_t &, L, const char *>::value, write32smo_delegate> make_lw32_delegate(device_t &owner, L &&l, const char *name) +{ return write32smo_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<write64_delegate, L, const char *>::value, write64_delegate> make_lw64_delegate(L l, const char *name) -{ return write64_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<write64_delegate, device_t &, L, const char *>::value, write64_delegate> make_lw64_delegate(device_t &owner, L &&l, const char *name) +{ return write64_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<write64m_delegate, L, const char *>::value, write64m_delegate> make_lw64_delegate(L l, const char *name) -{ return write64m_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<write64m_delegate, device_t &, L, const char *>::value, write64m_delegate> make_lw64_delegate(device_t &owner, L &&l, const char *name) +{ return write64m_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<write64s_delegate, L, const char *>::value, write64s_delegate> make_lw64_delegate(L l, const char *name) -{ return write64s_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<write64s_delegate, device_t &, L, const char *>::value, write64s_delegate> make_lw64_delegate(device_t &owner, L &&l, const char *name) +{ return write64s_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<write64sm_delegate, L, const char *>::value, write64sm_delegate> make_lw64_delegate(L l, const char *name) -{ return write64sm_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<write64sm_delegate, device_t &, L, const char *>::value, write64sm_delegate> make_lw64_delegate(device_t &owner, L &&l, const char *name) +{ return write64sm_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<write64mo_delegate, L, const char *>::value, write64mo_delegate> make_lw64_delegate(L l, const char *name) -{ return write64mo_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<write64mo_delegate, device_t &, L, const char *>::value, write64mo_delegate> make_lw64_delegate(device_t &owner, L &&l, const char *name) +{ return write64mo_delegate(owner, std::forward<L>(l), name); } template <typename L> -inline std::enable_if_t<std::is_constructible<write64smo_delegate, L, const char *>::value, write64smo_delegate> make_lw64_delegate(L l, const char *name) -{ return write64smo_delegate(l, name); } +inline std::enable_if_t<std::is_constructible<write64smo_delegate, device_t &, L, const char *>::value, write64smo_delegate> make_lw64_delegate(device_t &owner, L &&l, const char *name) +{ return write64smo_delegate(owner, std::forward<L>(l), name); } + + +// =====================-> delegate -> Width + +template <typename Delegate> struct handler_width; +template <typename Delegate> inline constexpr int handler_width_v = handler_width<Delegate>::value; +template <> struct handler_width<read8_delegate> { static inline constexpr int value = 0; }; +template <> struct handler_width<read8m_delegate> { static inline constexpr int value = 0; }; +template <> struct handler_width<read8s_delegate> { static inline constexpr int value = 0; }; +template <> struct handler_width<read8sm_delegate> { static inline constexpr int value = 0; }; +template <> struct handler_width<read8mo_delegate> { static inline constexpr int value = 0; }; +template <> struct handler_width<read8smo_delegate> { static inline constexpr int value = 0; }; +template <> struct handler_width<write8_delegate> { static inline constexpr int value = 0; }; +template <> struct handler_width<write8m_delegate> { static inline constexpr int value = 0; }; +template <> struct handler_width<write8s_delegate> { static inline constexpr int value = 0; }; +template <> struct handler_width<write8sm_delegate> { static inline constexpr int value = 0; }; +template <> struct handler_width<write8mo_delegate> { static inline constexpr int value = 0; }; +template <> struct handler_width<write8smo_delegate> { static inline constexpr int value = 0; }; +template <> struct handler_width<read16_delegate> { static inline constexpr int value = 1; }; +template <> struct handler_width<read16m_delegate> { static inline constexpr int value = 1; }; +template <> struct handler_width<read16s_delegate> { static inline constexpr int value = 1; }; +template <> struct handler_width<read16sm_delegate> { static inline constexpr int value = 1; }; +template <> struct handler_width<read16mo_delegate> { static inline constexpr int value = 1; }; +template <> struct handler_width<read16smo_delegate> { static inline constexpr int value = 1; }; +template <> struct handler_width<write16_delegate> { static inline constexpr int value = 1; }; +template <> struct handler_width<write16m_delegate> { static inline constexpr int value = 1; }; +template <> struct handler_width<write16s_delegate> { static inline constexpr int value = 1; }; +template <> struct handler_width<write16sm_delegate> { static inline constexpr int value = 1; }; +template <> struct handler_width<write16mo_delegate> { static inline constexpr int value = 1; }; +template <> struct handler_width<write16smo_delegate> { static inline constexpr int value = 1; }; +template <> struct handler_width<read32_delegate> { static inline constexpr int value = 2; }; +template <> struct handler_width<read32m_delegate> { static inline constexpr int value = 2; }; +template <> struct handler_width<read32s_delegate> { static inline constexpr int value = 2; }; +template <> struct handler_width<read32sm_delegate> { static inline constexpr int value = 2; }; +template <> struct handler_width<read32mo_delegate> { static inline constexpr int value = 2; }; +template <> struct handler_width<read32smo_delegate> { static inline constexpr int value = 2; }; +template <> struct handler_width<write32_delegate> { static inline constexpr int value = 2; }; +template <> struct handler_width<write32m_delegate> { static inline constexpr int value = 2; }; +template <> struct handler_width<write32s_delegate> { static inline constexpr int value = 2; }; +template <> struct handler_width<write32sm_delegate> { static inline constexpr int value = 2; }; +template <> struct handler_width<write32mo_delegate> { static inline constexpr int value = 2; }; +template <> struct handler_width<write32smo_delegate> { static inline constexpr int value = 2; }; +template <> struct handler_width<read64_delegate> { static inline constexpr int value = 3; }; +template <> struct handler_width<read64m_delegate> { static inline constexpr int value = 3; }; +template <> struct handler_width<read64s_delegate> { static inline constexpr int value = 3; }; +template <> struct handler_width<read64sm_delegate> { static inline constexpr int value = 3; }; +template <> struct handler_width<read64mo_delegate> { static inline constexpr int value = 3; }; +template <> struct handler_width<read64smo_delegate> { static inline constexpr int value = 3; }; +template <> struct handler_width<write64_delegate> { static inline constexpr int value = 3; }; +template <> struct handler_width<write64m_delegate> { static inline constexpr int value = 3; }; +template <> struct handler_width<write64s_delegate> { static inline constexpr int value = 3; }; +template <> struct handler_width<write64sm_delegate> { static inline constexpr int value = 3; }; +template <> struct handler_width<write64mo_delegate> { static inline constexpr int value = 3; }; +template <> struct handler_width<write64smo_delegate> { static inline constexpr int value = 3; }; // =====================-> Width -> types -template<int Width> struct handler_entry_size {}; -template<> struct handler_entry_size<0> { using uX = u8; }; -template<> struct handler_entry_size<1> { using uX = u16; }; -template<> struct handler_entry_size<2> { using uX = u32; }; -template<> struct handler_entry_size<3> { using uX = u64; }; +template <int Width> struct handler_entry_size; +template <int Width> using handler_entry_size_t = typename handler_entry_size<Width>::type; +template<> struct handler_entry_size<0> { using type = u8; }; +template<> struct handler_entry_size<1> { using type = u16; }; +template<> struct handler_entry_size<2> { using type = u32; }; +template<> struct handler_entry_size<3> { using type = u64; }; // =====================-> Address segmentation for the search tree +constexpr int handler_entry_dispatch_level(int highbits) +{ + return (highbits > 48) ? 3 : (highbits > 32) ? 2 : (highbits > 14) ? 1 : 0; +} + +constexpr int handler_entry_dispatch_level_to_lowbits(int level, int width, int ashift) +{ + return level == 3 ? 48 : level == 2 ? 32 : level == 1 ? 14 : width + ashift; +} + constexpr int handler_entry_dispatch_lowbits(int highbits, int width, int ashift) { return (highbits > 48) ? 48 : @@ -543,30 +507,74 @@ constexpr int handler_entry_dispatch_lowbits(int highbits, int width, int ashift width + ashift; } -} } // namespace emu::detail + +// =====================-> Passthrough handler management structure + +class memory_passthrough_handler_impl +{ +public: + memory_passthrough_handler_impl(address_space &space) : m_space(space) {} + memory_passthrough_handler_impl(memory_passthrough_handler_impl const &) = delete; + + void remove(); + +private: + address_space &m_space; + std::unordered_set<handler_entry *> m_handlers; + + void add_handler(handler_entry *handler) { m_handlers.insert(handler); } + void remove_handler(handler_entry *handler) { m_handlers.erase(m_handlers.find(handler)); } + + friend address_space; + template<int Width, int AddrShift> friend class ::handler_entry_read_passthrough; + template<int Width, int AddrShift> friend class ::handler_entry_write_passthrough; +}; + +} // namespace emu::detail + + +namespace emu { + +template <typename T> +inline detail::rw_delegate_type_t<T> rw_delegate(device_t &base, char const *tag, T &&func, char const *name) +{ return detail::rw_delegate_type_t<T>(base, tag, std::forward<T>(func), name); } + +template <typename T> +inline detail::rw_delegate_type_t<T> rw_delegate(detail::rw_delegate_device_class_t<T> &object, T &&func, char const *name) +{ return detail::rw_delegate_type_t<T>(object, std::forward<T>(func), name); } + +template <typename D, bool Reqd, typename T> +inline detail::rw_delegate_type_t<T> rw_delegate(device_finder<D, Reqd> const &finder, T &&func, char const *name) +{ return detail::rw_delegate_type_t<T>(finder, std::forward<T>(func), name); } + +} // namespace emu // ======================> memory_units_descritor forwards declaration -template<int Width, int AddrShift, int Endian> class memory_units_descriptor; +template<int Width, int AddrShift> class memory_units_descriptor; // =====================-> The root class of all handlers // Handlers the refcounting as part of the interface - class handler_entry { DISABLE_COPYING(handler_entry); - template<int Width, int AddrShift, endianness_t Endian> friend class address_space_specific; + template<int Level, int Width, int AddrShift, endianness_t Endian> friend class address_space_specific; public: - // Typing flags - static constexpr u32 F_DISPATCH = 0x00000001; // handler that forwards the access to other handlers - static constexpr u32 F_UNITS = 0x00000002; // handler that merges/splits an access among multiple handlers (unitmask support) - static constexpr u32 F_PASSTHROUGH = 0x00000004; // handler that passes through the request to another handler + // Typing flags (low 16 bits are for the user) + static constexpr u32 F_UNMAP = 0x00010000; // the unmapped memory accessed handler + static constexpr u32 F_DISPATCH = 0x00020000; // handler that forwards the access to other handlers + static constexpr u32 F_UNITS = 0x00040000; // handler that merges/splits an access among multiple handlers (unitmask support) + static constexpr u32 F_VIEW = 0x00080000; // handler for a view (kinda like dispatch except not entirely) + static constexpr u32 F_PT_BITS = 24; // position of the 4-bit priority for a passthrough handler. The highest the priority the earlier it is called in the chain. 0 = not passthrough + static constexpr u32 F_PT_REPLACE = 1 << F_PT_BITS; // a passthrough with a odd priority can only happen once in a path + + static constexpr u32 f_pt(u32 priority) { return (priority << F_PT_BITS); } // Start/end of range flags static constexpr u8 START = 1; @@ -594,8 +602,10 @@ public: inline u32 flags() const { return m_flags; } inline bool is_dispatch() const { return m_flags & F_DISPATCH; } + inline bool is_view() const { return m_flags & F_VIEW; } inline bool is_units() const { return m_flags & F_UNITS; } - inline bool is_passthrough() const { return m_flags & F_PASSTHROUGH; } + inline bool is_passthrough() const { return f_get_pt() != 0; } + inline u32 f_get_pt() const { return (m_flags >> F_PT_BITS) & 15; } virtual void dump_map(std::vector<memory_entry> &map) const; @@ -603,6 +613,11 @@ public: virtual void enumerate_references(handler_entry::reflist &refs) const; u32 get_refcount() const { return m_refcount; } + virtual void select_a(int slot); + virtual void select_u(int slot); + + virtual offs_t dispatch_entry(offs_t address) const; + protected: // Address range storage struct range { @@ -632,37 +647,44 @@ protected: // Provides the populate/read/get_ptr/lookup API -template<int Width, int AddrShift, int Endian> class handler_entry_read_passthrough; - -template<int Width, int AddrShift, int Endian> class handler_entry_read : public handler_entry +template<int Width, int AddrShift> class handler_entry_read : public handler_entry { public: - using uX = typename emu::detail::handler_entry_size<Width>::uX; + using uX = emu::detail::handler_entry_size_t<Width>; + + static constexpr u32 NATIVE_MASK = Width + AddrShift >= 0 ? make_bitmask<u32>(Width + AddrShift) : 0; struct mapping { - handler_entry_read<Width, AddrShift, Endian> *original; - handler_entry_read<Width, AddrShift, Endian> *patched; + handler_entry_read<Width, AddrShift> *original; + handler_entry_read<Width, AddrShift> *patched; u8 ukey; }; handler_entry_read(address_space *space, u32 flags) : handler_entry(space, flags) {} ~handler_entry_read() {} - virtual uX read(offs_t offset, uX mem_mask) = 0; + virtual uX read(offs_t offset, uX mem_mask) const = 0; + virtual uX read_interruptible(offs_t offset, uX mem_mask) const = 0; + virtual std::pair<uX, u16> read_flags(offs_t offset, uX mem_mask) const = 0; + virtual u16 lookup_flags(offs_t offset, uX mem_mask) const = 0; virtual void *get_ptr(offs_t offset) const; - virtual void lookup(offs_t address, offs_t &start, offs_t &end, handler_entry_read<Width, AddrShift, Endian> *&handler) const; + virtual void lookup(offs_t address, offs_t &start, offs_t &end, handler_entry_read<Width, AddrShift> *&handler) const; - inline void populate(offs_t start, offs_t end, offs_t mirror, handler_entry_read<Width, AddrShift, Endian> *handler) { + inline void populate(offs_t start, offs_t end, offs_t mirror, handler_entry_read<Width, AddrShift> *handler) { + start &= ~NATIVE_MASK; + end |= NATIVE_MASK; if(mirror) populate_mirror(start, end, start, end, mirror, handler); else populate_nomirror(start, end, start, end, handler); } - virtual void populate_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_read<Width, AddrShift, Endian> *handler); - virtual void populate_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_read<Width, AddrShift, Endian> *handler); + virtual void populate_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_read<Width, AddrShift> *handler); + virtual void populate_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_read<Width, AddrShift> *handler); - inline void populate_mismatched(offs_t start, offs_t end, offs_t mirror, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor) { + void populate_mismatched(offs_t start, offs_t end, offs_t mirror, const memory_units_descriptor<Width, AddrShift> &descriptor) { + start &= ~NATIVE_MASK; + end |= NATIVE_MASK; std::vector<mapping> mappings; if(mirror) populate_mismatched_mirror(start, end, start, end, mirror, descriptor, mappings); @@ -670,10 +692,12 @@ public: populate_mismatched_nomirror(start, end, start, end, descriptor, START|END, mappings); } - virtual void populate_mismatched_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 rkey, std::vector<mapping> &mappings); - virtual void populate_mismatched_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, std::vector<mapping> &mappings); + virtual void populate_mismatched_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, const memory_units_descriptor<Width, AddrShift> &descriptor, u8 rkey, std::vector<mapping> &mappings); + virtual void populate_mismatched_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, const memory_units_descriptor<Width, AddrShift> &descriptor, std::vector<mapping> &mappings); - inline void populate_passthrough(offs_t start, offs_t end, offs_t mirror, handler_entry_read_passthrough<Width, AddrShift, Endian> *handler) { + void populate_passthrough(offs_t start, offs_t end, offs_t mirror, handler_entry_read_passthrough<Width, AddrShift> *handler) { + start &= ~NATIVE_MASK; + end |= NATIVE_MASK; std::vector<mapping> mappings; if(mirror) populate_passthrough_mirror(start, end, start, end, mirror, handler, mappings); @@ -681,49 +705,62 @@ public: populate_passthrough_nomirror(start, end, start, end, handler, mappings); } - virtual void populate_passthrough_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_read_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings); - virtual void populate_passthrough_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_read_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings); + virtual void populate_passthrough_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_read_passthrough<Width, AddrShift> *handler, std::vector<mapping> &mappings); + virtual void populate_passthrough_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_read_passthrough<Width, AddrShift> *handler, std::vector<mapping> &mappings); // Remove a set of passthrough handlers, leaving the lower handler in their place virtual void detach(const std::unordered_set<handler_entry *> &handlers); -}; + // Return the internal structures of the root dispatch + virtual const handler_entry_read<Width, AddrShift> *const *get_dispatch() const; + + virtual void init_handlers(offs_t start_entry, offs_t end_entry, u32 lowbits, offs_t ostart, offs_t oend, handler_entry_read<Width, AddrShift> **dispatch, handler_entry::range *ranges); + virtual handler_entry_read<Width, AddrShift> *dup(); +}; // =====================-> The parent class of all write handlers // Provides the populate/write/get_ptr/lookup API -template<int Width, int AddrShift, int Endian> class handler_entry_write_passthrough; - -template<int Width, int AddrShift, int Endian> class handler_entry_write : public handler_entry +template<int Width, int AddrShift> class handler_entry_write : public handler_entry { public: - using uX = typename emu::detail::handler_entry_size<Width>::uX; + using uX = emu::detail::handler_entry_size_t<Width>; + + static constexpr u32 NATIVE_MASK = Width + AddrShift >= 0 ? make_bitmask<u32>(Width + AddrShift) : 0; struct mapping { - handler_entry_write<Width, AddrShift, Endian> *original; - handler_entry_write<Width, AddrShift, Endian> *patched; + handler_entry_write<Width, AddrShift> *original; + handler_entry_write<Width, AddrShift> *patched; u8 ukey; }; handler_entry_write(address_space *space, u32 flags) : handler_entry(space, flags) {} virtual ~handler_entry_write() {} - virtual void write(offs_t offset, uX data, uX mem_mask) = 0; + virtual void write(offs_t offset, uX data, uX mem_mask) const = 0; + virtual void write_interruptible(offs_t offset, uX data, uX mem_mask) const = 0; + virtual u16 write_flags(offs_t offset, uX data, uX mem_mask) const = 0; + virtual u16 lookup_flags(offs_t offset, uX mem_mask) const = 0; virtual void *get_ptr(offs_t offset) const; - virtual void lookup(offs_t address, offs_t &start, offs_t &end, handler_entry_write<Width, AddrShift, Endian> *&handler) const; + virtual void lookup(offs_t address, offs_t &start, offs_t &end, handler_entry_write<Width, AddrShift> *&handler) const; - inline void populate(offs_t start, offs_t end, offs_t mirror, handler_entry_write<Width, AddrShift, Endian> *handler) { + inline void populate(offs_t start, offs_t end, offs_t mirror, handler_entry_write<Width, AddrShift> *handler) { + start &= ~NATIVE_MASK; + end |= NATIVE_MASK; if(mirror) populate_mirror(start, end, start, end, mirror, handler); else populate_nomirror(start, end, start, end, handler); } - virtual void populate_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_write<Width, AddrShift, Endian> *handler); - virtual void populate_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_write<Width, AddrShift, Endian> *handler); + virtual void populate_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_write<Width, AddrShift> *handler); + virtual void populate_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_write<Width, AddrShift> *handler); + + inline void populate_mismatched(offs_t start, offs_t end, offs_t mirror, const memory_units_descriptor<Width, AddrShift> &descriptor) { + start &= ~NATIVE_MASK; + end |= NATIVE_MASK; - inline void populate_mismatched(offs_t start, offs_t end, offs_t mirror, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor) { std::vector<mapping> mappings; if(mirror) populate_mismatched_mirror(start, end, start, end, mirror, descriptor, mappings); @@ -731,10 +768,12 @@ public: populate_mismatched_nomirror(start, end, start, end, descriptor, START|END, mappings); } - virtual void populate_mismatched_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 rkey, std::vector<mapping> &mappings); - virtual void populate_mismatched_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, std::vector<mapping> &mappings); + virtual void populate_mismatched_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, const memory_units_descriptor<Width, AddrShift> &descriptor, u8 rkey, std::vector<mapping> &mappings); + virtual void populate_mismatched_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, const memory_units_descriptor<Width, AddrShift> &descriptor, std::vector<mapping> &mappings); - inline void populate_passthrough(offs_t start, offs_t end, offs_t mirror, handler_entry_write_passthrough<Width, AddrShift, Endian> *handler) { + inline void populate_passthrough(offs_t start, offs_t end, offs_t mirror, handler_entry_write_passthrough<Width, AddrShift> *handler) { + start &= ~NATIVE_MASK; + end |= NATIVE_MASK; std::vector<mapping> mappings; if(mirror) populate_passthrough_mirror(start, end, start, end, mirror, handler, mappings); @@ -742,36 +781,43 @@ public: populate_passthrough_nomirror(start, end, start, end, handler, mappings); } - virtual void populate_passthrough_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_write_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings); - virtual void populate_passthrough_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_write_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings); + virtual void populate_passthrough_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_write_passthrough<Width, AddrShift> *handler, std::vector<mapping> &mappings); + virtual void populate_passthrough_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_write_passthrough<Width, AddrShift> *handler, std::vector<mapping> &mappings); // Remove a set of passthrough handlers, leaving the lower handler in their place virtual void detach(const std::unordered_set<handler_entry *> &handlers); + + // Return the internal structures of the root dispatch + virtual const handler_entry_write<Width, AddrShift> *const *get_dispatch() const; + + virtual void init_handlers(offs_t start_entry, offs_t end_entry, u32 lowbits, offs_t ostart, offs_t oend, handler_entry_write<Width, AddrShift> **dispatch, handler_entry::range *ranges); + virtual handler_entry_write<Width, AddrShift> *dup(); }; // =====================-> Passthrough handler management structure class memory_passthrough_handler { - template<int Width, int AddrShift, int Endian> friend class handler_entry_read_passthrough; - template<int Width, int AddrShift, int Endian> friend class handler_entry_write_passthrough; - public: - memory_passthrough_handler(address_space &space) : m_space(space) {} + memory_passthrough_handler() : m_impl() {} + memory_passthrough_handler(std::shared_ptr<emu::detail::memory_passthrough_handler_impl> const &impl) : m_impl(impl) {} - inline void remove(); + void remove() + { + auto impl(m_impl.lock()); + if (impl) + impl->remove(); + } private: - address_space &m_space; - std::unordered_set<handler_entry *> m_handlers; + std::weak_ptr<emu::detail::memory_passthrough_handler_impl> m_impl; - void add_handler(handler_entry *handler) { m_handlers.insert(handler); } - void remove_handler(handler_entry *handler) { m_handlers.erase(m_handlers.find(handler)); } + friend class address_space; }; // =====================-> Forward declaration for address_space -template<int Width, int AddrShift, int Endian> class handler_entry_read_unmapped; -template<int Width, int AddrShift, int Endian> class handler_entry_write_unmapped; +template<int Width, int AddrShift> class handler_entry_read_unmapped; +template<int Width, int AddrShift> class handler_entry_write_unmapped; // ======================> address offset -> byte offset @@ -780,17 +826,17 @@ constexpr offs_t memory_offset_to_byte(offs_t offset, int AddrShift) { return Ad // ======================> generic read/write decomposition routines // generic direct read -template<int Width, int AddrShift, int Endian, int TargetWidth, bool Aligned, typename T> typename emu::detail::handler_entry_size<TargetWidth>::uX memory_read_generic(T rop, offs_t address, typename emu::detail::handler_entry_size<TargetWidth>::uX mask) +template<int Width, int AddrShift, endianness_t Endian, int TargetWidth, bool Aligned, typename T> emu::detail::handler_entry_size_t<TargetWidth> memory_read_generic(T rop, offs_t address, emu::detail::handler_entry_size_t<TargetWidth> mask) { - using TargetType = typename emu::detail::handler_entry_size<TargetWidth>::uX; - using NativeType = typename emu::detail::handler_entry_size<Width>::uX; + using TargetType = emu::detail::handler_entry_size_t<TargetWidth>; + using NativeType = emu::detail::handler_entry_size_t<Width>; constexpr u32 TARGET_BYTES = 1 << TargetWidth; constexpr u32 TARGET_BITS = 8 * TARGET_BYTES; constexpr u32 NATIVE_BYTES = 1 << Width; constexpr u32 NATIVE_BITS = 8 * NATIVE_BYTES; constexpr u32 NATIVE_STEP = AddrShift >= 0 ? NATIVE_BYTES << iabs(AddrShift) : NATIVE_BYTES >> iabs(AddrShift); - constexpr u32 NATIVE_MASK = Width + AddrShift >= 0 ? (1 << (Width + AddrShift)) - 1 : 0; + constexpr u32 NATIVE_MASK = Width + AddrShift >= 0 ? make_bitmask<u32>(Width + AddrShift) : 0; // equal to native size and aligned; simple pass-through to the native reader if (NATIVE_BYTES == TARGET_BYTES && (Aligned || (address & NATIVE_MASK) == 0)) @@ -914,9 +960,9 @@ template<int Width, int AddrShift, int Endian, int TargetWidth, bool Aligned, ty } // generic direct write -template<int Width, int AddrShift, int Endian, int TargetWidth, bool Aligned, typename T> void memory_write_generic(T wop, offs_t address, typename emu::detail::handler_entry_size<TargetWidth>::uX data, typename emu::detail::handler_entry_size<TargetWidth>::uX mask) +template<int Width, int AddrShift, endianness_t Endian, int TargetWidth, bool Aligned, typename T> void memory_write_generic(T wop, offs_t address, emu::detail::handler_entry_size_t<TargetWidth> data, emu::detail::handler_entry_size_t<TargetWidth> mask) { - using NativeType = typename emu::detail::handler_entry_size<Width>::uX; + using NativeType = emu::detail::handler_entry_size_t<Width>; constexpr u32 TARGET_BYTES = 1 << TargetWidth; constexpr u32 TARGET_BITS = 8 * TARGET_BYTES; @@ -1037,95 +1083,961 @@ template<int Width, int AddrShift, int Endian, int TargetWidth, bool Aligned, ty } } +// generic direct read with flags +template<int Width, int AddrShift, endianness_t Endian, int TargetWidth, bool Aligned, typename TF> std::pair<emu::detail::handler_entry_size_t<TargetWidth>, u16> memory_read_generic_flags(TF ropf, offs_t address, emu::detail::handler_entry_size_t<TargetWidth> mask) +{ + using TargetType = emu::detail::handler_entry_size_t<TargetWidth>; + using NativeType = emu::detail::handler_entry_size_t<Width>; + + constexpr u32 TARGET_BYTES = 1 << TargetWidth; + constexpr u32 TARGET_BITS = 8 * TARGET_BYTES; + constexpr u32 NATIVE_BYTES = 1 << Width; + constexpr u32 NATIVE_BITS = 8 * NATIVE_BYTES; + constexpr u32 NATIVE_STEP = AddrShift >= 0 ? NATIVE_BYTES << iabs(AddrShift) : NATIVE_BYTES >> iabs(AddrShift); + constexpr u32 NATIVE_MASK = Width + AddrShift >= 0 ? make_bitmask<u32>(Width + AddrShift) : 0; + + // equal to native size and aligned; simple pass-through to the native reader + if (NATIVE_BYTES == TARGET_BYTES && (Aligned || (address & NATIVE_MASK) == 0)) + return ropf(address & ~NATIVE_MASK, mask); + + // if native size is larger, see if we can do a single masked read (guaranteed if we're aligned) + if (NATIVE_BYTES > TARGET_BYTES) + { + u32 offsbits = 8 * (memory_offset_to_byte(address, AddrShift) & (NATIVE_BYTES - (Aligned ? TARGET_BYTES : 1))); + if (Aligned || (offsbits + TARGET_BITS <= NATIVE_BITS)) + { + if (Endian != ENDIANNESS_LITTLE) offsbits = NATIVE_BITS - TARGET_BITS - offsbits; + auto pack = ropf(address & ~NATIVE_MASK, (NativeType)mask << offsbits); + pack.first >>= offsbits; + return pack; + } + } + + // determine our alignment against the native boundaries, and mask the address + u32 offsbits = 8 * (memory_offset_to_byte(address, AddrShift) & (NATIVE_BYTES - 1)); + address &= ~NATIVE_MASK; + + // if we're here, and native size is larger or equal to the target, we need exactly 2 reads + if (NATIVE_BYTES >= TARGET_BYTES) + { + // little-endian case + if (Endian == ENDIANNESS_LITTLE) + { + // read lower bits from lower address + u16 flags = 0; + TargetType result = 0; + NativeType curmask = (NativeType)mask << offsbits; + if (curmask != 0) { auto pack = ropf(address, curmask); result = pack.first >> offsbits; flags = pack.second; } + + // read upper bits from upper address + offsbits = NATIVE_BITS - offsbits; + curmask = mask >> offsbits; + if (curmask != 0) { auto pack = ropf(address + NATIVE_STEP, curmask); result |= pack.first << offsbits; flags |= pack.second; } + return std::pair<NativeType, u16>(result, flags); + } + + // big-endian case + else + { + // left-justify the mask to the target type + constexpr u32 LEFT_JUSTIFY_TARGET_TO_NATIVE_SHIFT = ((NATIVE_BITS >= TARGET_BITS) ? (NATIVE_BITS - TARGET_BITS) : 0); + u16 flags = 0; + NativeType result = 0; + NativeType ljmask = (NativeType)mask << LEFT_JUSTIFY_TARGET_TO_NATIVE_SHIFT; + NativeType curmask = ljmask >> offsbits; + + // read upper bits from lower address + if (curmask != 0) { auto pack = ropf(address, curmask); result = pack.first << offsbits; flags = pack.second; } + offsbits = NATIVE_BITS - offsbits; + + // read lower bits from upper address + curmask = ljmask << offsbits; + if (curmask != 0) { auto pack = ropf(address + NATIVE_STEP, curmask); result |= pack.first >> offsbits; flags |= pack.second; } + + // return the un-justified result + return std::pair<NativeType, u16>(result >> LEFT_JUSTIFY_TARGET_TO_NATIVE_SHIFT, flags); + } + } + + // if we're here, then we have 2 or more reads needed to get our final result + else + { + // compute the maximum number of loops; we do it this way so that there are + // a fixed number of loops for the compiler to unroll if it desires + constexpr u32 MAX_SPLITS_MINUS_ONE = TARGET_BYTES / NATIVE_BYTES - 1; + u16 flags = 0; + TargetType result = 0; + + // little-endian case + if (Endian == ENDIANNESS_LITTLE) + { + // read lowest bits from first address + NativeType curmask = mask << offsbits; + if (curmask != 0) { auto pack = ropf(address, curmask); result = pack.first >> offsbits; flags = pack.second; } + + // read middle bits from subsequent addresses + offsbits = NATIVE_BITS - offsbits; + for (u32 index = 0; index < MAX_SPLITS_MINUS_ONE; index++) + { + address += NATIVE_STEP; + curmask = mask >> offsbits; + if (curmask != 0) { auto pack = ropf(address, curmask); result |= (TargetType)pack.first << offsbits; flags |= pack.second; } + offsbits += NATIVE_BITS; + } + + // if we're not aligned and we still have bits left, read uppermost bits from last address + if (!Aligned && offsbits < TARGET_BITS) + { + curmask = mask >> offsbits; + if (curmask != 0) { auto pack = ropf(address + NATIVE_STEP, curmask); result |= (TargetType)pack.first << offsbits; flags |= pack.second; } + } + } + + // big-endian case + else + { + // read highest bits from first address + offsbits = TARGET_BITS - (NATIVE_BITS - offsbits); + NativeType curmask = mask >> offsbits; + if (curmask != 0) { auto pack = ropf(address, curmask); result = pack.first >> offsbits; flags = pack.second; } + + // read middle bits from subsequent addresses + for (u32 index = 0; index < MAX_SPLITS_MINUS_ONE; index++) + { + offsbits -= NATIVE_BITS; + address += NATIVE_STEP; + curmask = mask >> offsbits; + if (curmask != 0) { auto pack = ropf(address, curmask); result |= (TargetType)pack.first >> offsbits; flags |= pack.second; } + } + + // if we're not aligned and we still have bits left, read lowermost bits from the last address + if (!Aligned && offsbits != 0) + { + offsbits = NATIVE_BITS - offsbits; + curmask = mask << offsbits; + if (curmask != 0) { auto pack = ropf(address + NATIVE_STEP, curmask); result |= (TargetType)pack.first >> offsbits; flags |= pack.second; } + } + } + return std::pair<NativeType, u16>(result, flags); + } +} + +// generic direct write with flags +template<int Width, int AddrShift, endianness_t Endian, int TargetWidth, bool Aligned, typename TF> u16 memory_write_generic_flags(TF wopf, offs_t address, emu::detail::handler_entry_size_t<TargetWidth> data, emu::detail::handler_entry_size_t<TargetWidth> mask) +{ + using NativeType = emu::detail::handler_entry_size_t<Width>; + + constexpr u32 TARGET_BYTES = 1 << TargetWidth; + constexpr u32 TARGET_BITS = 8 * TARGET_BYTES; + constexpr u32 NATIVE_BYTES = 1 << Width; + constexpr u32 NATIVE_BITS = 8 * NATIVE_BYTES; + constexpr u32 NATIVE_STEP = AddrShift >= 0 ? NATIVE_BYTES << iabs(AddrShift) : NATIVE_BYTES >> iabs(AddrShift); + constexpr u32 NATIVE_MASK = Width + AddrShift >= 0 ? (1 << (Width + AddrShift)) - 1 : 0; + + // equal to native size and aligned; simple pass-through to the native writer + if (NATIVE_BYTES == TARGET_BYTES && (Aligned || (address & NATIVE_MASK) == 0)) + return wopf(address & ~NATIVE_MASK, data, mask); + + // if native size is larger, see if we can do a single masked write (guaranteed if we're aligned) + if (NATIVE_BYTES > TARGET_BYTES) + { + u32 offsbits = 8 * (memory_offset_to_byte(address, AddrShift) & (NATIVE_BYTES - (Aligned ? TARGET_BYTES : 1))); + if (Aligned || (offsbits + TARGET_BITS <= NATIVE_BITS)) + { + if (Endian != ENDIANNESS_LITTLE) offsbits = NATIVE_BITS - TARGET_BITS - offsbits; + return wopf(address & ~NATIVE_MASK, (NativeType)data << offsbits, (NativeType)mask << offsbits); + } + } + + // determine our alignment against the native boundaries, and mask the address + u32 offsbits = 8 * (memory_offset_to_byte(address, AddrShift) & (NATIVE_BYTES - 1)); + address &= ~NATIVE_MASK; + + // if we're here, and native size is larger or equal to the target, we need exactly 2 writes + if (NATIVE_BYTES >= TARGET_BYTES) + { + // little-endian case + if (Endian == ENDIANNESS_LITTLE) + { + // write lower bits to lower address + u16 flags = 0; + NativeType curmask = (NativeType)mask << offsbits; + if (curmask != 0) flags |= wopf(address, (NativeType)data << offsbits, curmask); + + // write upper bits to upper address + offsbits = NATIVE_BITS - offsbits; + curmask = mask >> offsbits; + if (curmask != 0) flags |= wopf(address + NATIVE_STEP, data >> offsbits, curmask); + return flags; + } + + // big-endian case + else + { + // left-justify the mask and data to the target type + u16 flags = 0; + constexpr u32 LEFT_JUSTIFY_TARGET_TO_NATIVE_SHIFT = ((NATIVE_BITS >= TARGET_BITS) ? (NATIVE_BITS - TARGET_BITS) : 0); + NativeType ljdata = (NativeType)data << LEFT_JUSTIFY_TARGET_TO_NATIVE_SHIFT; + NativeType ljmask = (NativeType)mask << LEFT_JUSTIFY_TARGET_TO_NATIVE_SHIFT; + // write upper bits to lower address + NativeType curmask = ljmask >> offsbits; + if (curmask != 0) flags |= wopf(address, ljdata >> offsbits, curmask); + // write lower bits to upper address + offsbits = NATIVE_BITS - offsbits; + curmask = ljmask << offsbits; + if (curmask != 0) flags |= wopf(address + NATIVE_STEP, ljdata << offsbits, curmask); + return flags; + } + } + + // if we're here, then we have 2 or more writes needed to get our final result + else + { + // compute the maximum number of loops; we do it this way so that there are + // a fixed number of loops for the compiler to unroll if it desires + constexpr u32 MAX_SPLITS_MINUS_ONE = TARGET_BYTES / NATIVE_BYTES - 1; + u16 flags = 0; + + // little-endian case + if (Endian == ENDIANNESS_LITTLE) + { + // write lowest bits to first address + NativeType curmask = mask << offsbits; + if (curmask != 0) flags |= wopf(address, data << offsbits, curmask); + + // write middle bits to subsequent addresses + offsbits = NATIVE_BITS - offsbits; + for (u32 index = 0; index < MAX_SPLITS_MINUS_ONE; index++) + { + address += NATIVE_STEP; + curmask = mask >> offsbits; + if (curmask != 0) flags |= wopf(address, data >> offsbits, curmask); + offsbits += NATIVE_BITS; + } + + // if we're not aligned and we still have bits left, write uppermost bits to last address + if (!Aligned && offsbits < TARGET_BITS) + { + curmask = mask >> offsbits; + if (curmask != 0) flags |= wopf(address + NATIVE_STEP, data >> offsbits, curmask); + } + } + + // big-endian case + else + { + // write highest bits to first address + offsbits = TARGET_BITS - (NATIVE_BITS - offsbits); + NativeType curmask = mask >> offsbits; + if (curmask != 0) flags |= wopf(address, data >> offsbits, curmask); + + // write middle bits to subsequent addresses + for (u32 index = 0; index < MAX_SPLITS_MINUS_ONE; index++) + { + offsbits -= NATIVE_BITS; + address += NATIVE_STEP; + curmask = mask >> offsbits; + if (curmask != 0) flags |= wopf(address, data >> offsbits, curmask); + } + + // if we're not aligned and we still have bits left, write lowermost bits to the last address + if (!Aligned && offsbits != 0) + { + offsbits = NATIVE_BITS - offsbits; + curmask = mask << offsbits; + if (curmask != 0) flags |= wopf(address + NATIVE_STEP, data << offsbits, curmask); + } + } + return flags; + } +} + +//############################################## +// generic direct read flags lookup +template<int Width, int AddrShift, endianness_t Endian, int TargetWidth, bool Aligned, typename TF> u16 lookup_memory_read_generic_flags(TF lropf, offs_t address, emu::detail::handler_entry_size_t<TargetWidth> mask) +{ + using NativeType = emu::detail::handler_entry_size_t<Width>; + + constexpr u32 TARGET_BYTES = 1 << TargetWidth; + constexpr u32 TARGET_BITS = 8 * TARGET_BYTES; + constexpr u32 NATIVE_BYTES = 1 << Width; + constexpr u32 NATIVE_BITS = 8 * NATIVE_BYTES; + constexpr u32 NATIVE_STEP = AddrShift >= 0 ? NATIVE_BYTES << iabs(AddrShift) : NATIVE_BYTES >> iabs(AddrShift); + constexpr u32 NATIVE_MASK = Width + AddrShift >= 0 ? make_bitmask<u32>(Width + AddrShift) : 0; + + // equal to native size and aligned; simple pass-through to the native flags lookup + if (NATIVE_BYTES == TARGET_BYTES && (Aligned || (address & NATIVE_MASK) == 0)) + return lropf(address & ~NATIVE_MASK, mask); + + // if native size is larger, see if we can do a single masked flags lookup (guaranteed if we're aligned) + if (NATIVE_BYTES > TARGET_BYTES) + { + u32 offsbits = 8 * (memory_offset_to_byte(address, AddrShift) & (NATIVE_BYTES - (Aligned ? TARGET_BYTES : 1))); + if (Aligned || (offsbits + TARGET_BITS <= NATIVE_BITS)) + { + if (Endian != ENDIANNESS_LITTLE) offsbits = NATIVE_BITS - TARGET_BITS - offsbits; + return lropf(address & ~NATIVE_MASK, (NativeType)mask << offsbits); + } + } + + // determine our alignment against the native boundaries, and mask the address + u32 offsbits = 8 * (memory_offset_to_byte(address, AddrShift) & (NATIVE_BYTES - 1)); + address &= ~NATIVE_MASK; + + // if we're here, and native size is larger or equal to the target, we need exactly 2 reads + if (NATIVE_BYTES >= TARGET_BYTES) + { + // little-endian case + if (Endian == ENDIANNESS_LITTLE) + { + // read flags from lower address + u16 flags = 0; + NativeType curmask = (NativeType)mask << offsbits; + if (curmask != 0) flags |= lropf(address, curmask); + + // read flags from upper address + offsbits = NATIVE_BITS - offsbits; + curmask = mask >> offsbits; + if (curmask != 0) flags |= lropf(address + NATIVE_STEP, curmask); + return flags; + } + + // big-endian case + else + { + // left-justify the mask to the target type + constexpr u32 LEFT_JUSTIFY_TARGET_TO_NATIVE_SHIFT = ((NATIVE_BITS >= TARGET_BITS) ? (NATIVE_BITS - TARGET_BITS) : 0); + u16 flags = 0; + NativeType ljmask = (NativeType)mask << LEFT_JUSTIFY_TARGET_TO_NATIVE_SHIFT; + NativeType curmask = ljmask >> offsbits; + + // read flags from lower address + if (curmask != 0) flags |= lropf(address, curmask); + offsbits = NATIVE_BITS - offsbits; + + // read flags from upper address + curmask = ljmask << offsbits; + if (curmask != 0) flags |= lropf(address + NATIVE_STEP, curmask); + + // return the result + return flags; + } + } + + // if we're here, then we have 2 or more reads needed to get our final result + else + { + // compute the maximum number of loops; we do it this way so that there are + // a fixed number of loops for the compiler to unroll if it desires + constexpr u32 MAX_SPLITS_MINUS_ONE = TARGET_BYTES / NATIVE_BYTES - 1; + u16 flags = 0; + + // little-endian case + if (Endian == ENDIANNESS_LITTLE) + { + // read flags from first address + NativeType curmask = mask << offsbits; + if (curmask != 0) flags |= lropf(address, curmask); + + // read flags from subsequent addresses + offsbits = NATIVE_BITS - offsbits; + for (u32 index = 0; index < MAX_SPLITS_MINUS_ONE; index++) + { + address += NATIVE_STEP; + curmask = mask >> offsbits; + if (curmask != 0) flags |= lropf(address, curmask); + offsbits += NATIVE_BITS; + } + + // if we're not aligned and we still have bits left, read flags from last address + if (!Aligned && offsbits < TARGET_BITS) + { + curmask = mask >> offsbits; + if (curmask != 0) flags |= lropf(address + NATIVE_STEP, curmask); + } + } + + // big-endian case + else + { + // read flags from first address + offsbits = TARGET_BITS - (NATIVE_BITS - offsbits); + NativeType curmask = mask >> offsbits; + if (curmask != 0) flags |= lropf(address, curmask); + + // read flags from subsequent addresses + for (u32 index = 0; index < MAX_SPLITS_MINUS_ONE; index++) + { + offsbits -= NATIVE_BITS; + address += NATIVE_STEP; + curmask = mask >> offsbits; + if (curmask != 0) flags |= lropf(address, curmask); + } + + // if we're not aligned and we still have bits left, read flags from the last address + if (!Aligned && offsbits != 0) + { + offsbits = NATIVE_BITS - offsbits; + curmask = mask << offsbits; + if (curmask != 0) flags |= lropf(address + NATIVE_STEP, curmask); + } + } + return flags; + } +} + +// generic direct write flags lookup +template<int Width, int AddrShift, endianness_t Endian, int TargetWidth, bool Aligned, typename TF> u16 lookup_memory_write_generic_flags(TF lwopf, offs_t address, emu::detail::handler_entry_size_t<TargetWidth> mask) +{ + using NativeType = emu::detail::handler_entry_size_t<Width>; + + constexpr u32 TARGET_BYTES = 1 << TargetWidth; + constexpr u32 TARGET_BITS = 8 * TARGET_BYTES; + constexpr u32 NATIVE_BYTES = 1 << Width; + constexpr u32 NATIVE_BITS = 8 * NATIVE_BYTES; + constexpr u32 NATIVE_STEP = AddrShift >= 0 ? NATIVE_BYTES << iabs(AddrShift) : NATIVE_BYTES >> iabs(AddrShift); + constexpr u32 NATIVE_MASK = Width + AddrShift >= 0 ? (1 << (Width + AddrShift)) - 1 : 0; + + // equal to native size and aligned; simple pass-through to the native flags lookup + if (NATIVE_BYTES == TARGET_BYTES && (Aligned || (address & NATIVE_MASK) == 0)) + return lwopf(address & ~NATIVE_MASK, mask); + + // if native size is larger, see if we can do a single masked flags lookup (guaranteed if we're aligned) + if (NATIVE_BYTES > TARGET_BYTES) + { + u32 offsbits = 8 * (memory_offset_to_byte(address, AddrShift) & (NATIVE_BYTES - (Aligned ? TARGET_BYTES : 1))); + if (Aligned || (offsbits + TARGET_BITS <= NATIVE_BITS)) + { + if (Endian != ENDIANNESS_LITTLE) offsbits = NATIVE_BITS - TARGET_BITS - offsbits; + return lwopf(address & ~NATIVE_MASK, (NativeType)mask << offsbits); + } + } + + // determine our alignment against the native boundaries, and mask the address + u32 offsbits = 8 * (memory_offset_to_byte(address, AddrShift) & (NATIVE_BYTES - 1)); + address &= ~NATIVE_MASK; + + // if we're here, and native size is larger or equal to the target, we need exactly 2 lookups + if (NATIVE_BYTES >= TARGET_BYTES) + { + // little-endian case + if (Endian == ENDIANNESS_LITTLE) + { + // lookup flags from lower address + u16 flags = 0; + NativeType curmask = (NativeType)mask << offsbits; + if (curmask != 0) flags |= lwopf(address, curmask); + + // lookup flags from upper address + offsbits = NATIVE_BITS - offsbits; + curmask = mask >> offsbits; + if (curmask != 0) flags |= lwopf(address + NATIVE_STEP, curmask); + return flags; + } + + // big-endian case + else + { + // left-justify the mask to the target type + u16 flags = 0; + constexpr u32 LEFT_JUSTIFY_TARGET_TO_NATIVE_SHIFT = ((NATIVE_BITS >= TARGET_BITS) ? (NATIVE_BITS - TARGET_BITS) : 0); + NativeType ljmask = (NativeType)mask << LEFT_JUSTIFY_TARGET_TO_NATIVE_SHIFT; + + // lookup flags from lower address + NativeType curmask = ljmask >> offsbits; + if (curmask != 0) flags |= lwopf(address, curmask); + + // lookup falgs from upper address + offsbits = NATIVE_BITS - offsbits; + curmask = ljmask << offsbits; + if (curmask != 0) flags |= lwopf(address + NATIVE_STEP, curmask); + return flags; + } + } + + // if we're here, then we have 2 or more lookups needed to get our final result + else + { + // compute the maximum number of loops; we do it this way so that there are + // a fixed number of loops for the compiler to unroll if it desires + constexpr u32 MAX_SPLITS_MINUS_ONE = TARGET_BYTES / NATIVE_BYTES - 1; + u16 flags = 0; + + // little-endian case + if (Endian == ENDIANNESS_LITTLE) + { + // lookup flags from first address + NativeType curmask = mask << offsbits; + if (curmask != 0) flags |= lwopf(address, curmask); + + // lookup flags from subsequent addresses + offsbits = NATIVE_BITS - offsbits; + for (u32 index = 0; index < MAX_SPLITS_MINUS_ONE; index++) + { + address += NATIVE_STEP; + curmask = mask >> offsbits; + if (curmask != 0) flags |= lwopf(address, curmask); + offsbits += NATIVE_BITS; + } + + // if we're not aligned and we still have bits left, lookup flags last address + if (!Aligned && offsbits < TARGET_BITS) + { + curmask = mask >> offsbits; + if (curmask != 0) flags |= lwopf(address + NATIVE_STEP, curmask); + } + } + + // big-endian case + else + { + // lookup flags from first address + offsbits = TARGET_BITS - (NATIVE_BITS - offsbits); + NativeType curmask = mask >> offsbits; + if (curmask != 0) flags |= lwopf(address, curmask); + + // lookup flags from subsequent addresses + for (u32 index = 0; index < MAX_SPLITS_MINUS_ONE; index++) + { + offsbits -= NATIVE_BITS; + address += NATIVE_STEP; + curmask = mask >> offsbits; + if (curmask != 0) flags |= lwopf(address, curmask); + } + + // if we're not aligned and we still have bits left, lookup falgs from the last address + if (!Aligned && offsbits != 0) + { + offsbits = NATIVE_BITS - offsbits; + curmask = mask << offsbits; + if (curmask != 0) flags |= lwopf(address + NATIVE_STEP, curmask); + } + } + return flags; + } +} + +// ======================> Direct dispatching + +template<int Level, int Width, int AddrShift> emu::detail::handler_entry_size_t<Width> dispatch_read(offs_t mask, offs_t offset, emu::detail::handler_entry_size_t<Width> mem_mask, const handler_entry_read<Width, AddrShift> *const *dispatch) +{ + constexpr u32 LOW_BITS = emu::detail::handler_entry_dispatch_level_to_lowbits(Level, Width, AddrShift); + return dispatch[(offset & mask) >> LOW_BITS]->read(offset, mem_mask); +} + + +template<int Level, int Width, int AddrShift> void dispatch_write(offs_t mask, offs_t offset, emu::detail::handler_entry_size_t<Width> data, emu::detail::handler_entry_size_t<Width> mem_mask, const handler_entry_write<Width, AddrShift> *const *dispatch) +{ + constexpr u32 LOW_BITS = emu::detail::handler_entry_dispatch_level_to_lowbits(Level, Width, AddrShift); + return dispatch[(offset & mask) >> LOW_BITS]->write(offset, data, mem_mask); +} + + +template<int Level, int Width, int AddrShift> std::pair<emu::detail::handler_entry_size_t<Width>, u16> dispatch_read_flags(offs_t mask, offs_t offset, emu::detail::handler_entry_size_t<Width> mem_mask, const handler_entry_read<Width, AddrShift> *const *dispatch) +{ + constexpr u32 LOW_BITS = emu::detail::handler_entry_dispatch_level_to_lowbits(Level, Width, AddrShift); + return dispatch[(offset & mask) >> LOW_BITS]->read_flags(offset, mem_mask); +} + + +template<int Level, int Width, int AddrShift> u16 dispatch_write_flags(offs_t mask, offs_t offset, emu::detail::handler_entry_size_t<Width> data, emu::detail::handler_entry_size_t<Width> mem_mask, const handler_entry_write<Width, AddrShift> *const *dispatch) +{ + constexpr u32 LOW_BITS = emu::detail::handler_entry_dispatch_level_to_lowbits(Level, Width, AddrShift); + return dispatch[(offset & mask) >> LOW_BITS]->write_flags(offset, data, mem_mask); +} + +template<int Level, int Width, int AddrShift> u16 dispatch_lookup_read_flags(offs_t mask, offs_t offset, emu::detail::handler_entry_size_t<Width> mem_mask, const handler_entry_read<Width, AddrShift> *const *dispatch) +{ + constexpr u32 LOW_BITS = emu::detail::handler_entry_dispatch_level_to_lowbits(Level, Width, AddrShift); + return dispatch[(offset & mask) >> LOW_BITS]->lookup_flags(offset, mem_mask); +} + + +template<int Level, int Width, int AddrShift> u16 dispatch_lookup_write_flags(offs_t mask, offs_t offset, emu::detail::handler_entry_size_t<Width> mem_mask, const handler_entry_write<Width, AddrShift> *const *dispatch) +{ + constexpr u32 LOW_BITS = emu::detail::handler_entry_dispatch_level_to_lowbits(Level, Width, AddrShift); + return dispatch[(offset & mask) >> LOW_BITS]->lookup_flags(offset, mem_mask); +} + + + +template<int Level, int Width, int AddrShift> emu::detail::handler_entry_size_t<Width> dispatch_read_interruptible(offs_t mask, offs_t offset, emu::detail::handler_entry_size_t<Width> mem_mask, const handler_entry_read<Width, AddrShift> *const *dispatch) +{ + constexpr u32 LOW_BITS = emu::detail::handler_entry_dispatch_level_to_lowbits(Level, Width, AddrShift); + return dispatch[(offset & mask) >> LOW_BITS]->read_interruptible(offset, mem_mask); +} + + +template<int Level, int Width, int AddrShift> void dispatch_write_interruptible(offs_t mask, offs_t offset, emu::detail::handler_entry_size_t<Width> data, emu::detail::handler_entry_size_t<Width> mem_mask, const handler_entry_write<Width, AddrShift> *const *dispatch) +{ + constexpr u32 LOW_BITS = emu::detail::handler_entry_dispatch_level_to_lowbits(Level, Width, AddrShift); + return dispatch[(offset & mask) >> LOW_BITS]->write_interruptible(offset, data, mem_mask); +} + + +namespace emu::detail { + +// ======================> memory_access_specific + +// memory_access_specific does uncached but faster accesses by shortcutting the address_space virtual call + +template<int Level, int Width, int AddrShift, endianness_t Endian> class memory_access_specific +{ + friend class ::address_space; + + using NativeType = emu::detail::handler_entry_size_t<Width>; + static constexpr u32 NATIVE_BYTES = 1 << Width; + static constexpr u32 NATIVE_MASK = (Width + AddrShift >= 0) ? ((1 << (Width + AddrShift)) - 1) : 0; + +public: + // construction/destruction + memory_access_specific() : + m_space(nullptr), + m_addrmask(0), + m_dispatch_read(nullptr), + m_dispatch_write(nullptr) + { + } + + address_space &space() const { + return *m_space; + } + + auto rop() { return [this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }; } + auto ropf() { return [this](offs_t offset, NativeType mask) -> std::pair<NativeType, u16> { return read_native_flags(offset, mask); }; } + auto lropf() { return [this](offs_t offset, NativeType mask) -> u16 { return lookup_read_native_flags(offset, mask); }; } + auto wop() { return [this](offs_t offset, NativeType data, NativeType mask) -> void { write_native(offset, data, mask); }; } + auto wopf() { return [this](offs_t offset, NativeType data, NativeType mask) -> u16 { return write_native_flags(offset, data, mask); }; } + auto lwopf() { return [this](offs_t offset, NativeType mask) -> u16 { return lookup_write_native_flags(offset, mask); }; } + + u8 read_byte(offs_t address) { if constexpr(Width == 0) return read_native(address & ~NATIVE_MASK); else return memory_read_generic<Width, AddrShift, Endian, 0, true>(rop(), address, 0xff); } + u8 read_byte(offs_t address, u8 mask) { return memory_read_generic<Width, AddrShift, Endian, 0, true>(rop(), address, mask); } + u16 read_word(offs_t address) { if constexpr(Width == 1) return read_native(address & ~NATIVE_MASK); else return memory_read_generic<Width, AddrShift, Endian, 1, true>(rop(), address, 0xffff); } + u16 read_word(offs_t address, u16 mask) { return memory_read_generic<Width, AddrShift, Endian, 1, true>(rop(), address, mask); } + u16 read_word_unaligned(offs_t address) { return memory_read_generic<Width, AddrShift, Endian, 1, false>(rop(), address, 0xffff); } + u16 read_word_unaligned(offs_t address, u16 mask) { return memory_read_generic<Width, AddrShift, Endian, 1, false>(rop(), address, mask); } + u32 read_dword(offs_t address) { if constexpr(Width == 2) return read_native(address & ~NATIVE_MASK); else return memory_read_generic<Width, AddrShift, Endian, 2, true>(rop(), address, 0xffffffff); } + u32 read_dword(offs_t address, u32 mask) { return memory_read_generic<Width, AddrShift, Endian, 2, true>(rop(), address, mask); } + u32 read_dword_unaligned(offs_t address) { return memory_read_generic<Width, AddrShift, Endian, 2, false>(rop(), address, 0xffffffff); } + u32 read_dword_unaligned(offs_t address, u32 mask) { return memory_read_generic<Width, AddrShift, Endian, 2, false>(rop(), address, mask); } + u64 read_qword(offs_t address) { if constexpr(Width == 3) return read_native(address & ~NATIVE_MASK); else return memory_read_generic<Width, AddrShift, Endian, 3, true>(rop(), address, 0xffffffffffffffffU); } + u64 read_qword(offs_t address, u64 mask) { return memory_read_generic<Width, AddrShift, Endian, 3, true>(rop(), address, mask); } + u64 read_qword_unaligned(offs_t address) { return memory_read_generic<Width, AddrShift, Endian, 3, false>(rop(), address, 0xffffffffffffffffU); } + u64 read_qword_unaligned(offs_t address, u64 mask) { return memory_read_generic<Width, AddrShift, Endian, 3, false>(rop(), address, mask); } + + void write_byte(offs_t address, u8 data) { if constexpr(Width == 0) write_native(address & ~NATIVE_MASK, data); else memory_write_generic<Width, AddrShift, Endian, 0, true>(wop(), address, data, 0xff); } + void write_byte(offs_t address, u8 data, u8 mask) { memory_write_generic<Width, AddrShift, Endian, 0, true>(wop(), address, data, mask); } + void write_word(offs_t address, u16 data) { if constexpr(Width == 1) write_native(address & ~NATIVE_MASK, data); else memory_write_generic<Width, AddrShift, Endian, 1, true>(wop(), address, data, 0xffff); } + void write_word(offs_t address, u16 data, u16 mask) { memory_write_generic<Width, AddrShift, Endian, 1, true>(wop(), address, data, mask); } + void write_word_unaligned(offs_t address, u16 data) { memory_write_generic<Width, AddrShift, Endian, 1, false>(wop(), address, data, 0xffff); } + void write_word_unaligned(offs_t address, u16 data, u16 mask) { memory_write_generic<Width, AddrShift, Endian, 1, false>(wop(), address, data, mask); } + void write_dword(offs_t address, u32 data) { if constexpr(Width == 2) write_native(address & ~NATIVE_MASK, data); else memory_write_generic<Width, AddrShift, Endian, 2, true>(wop(), address, data, 0xffffffff); } + void write_dword(offs_t address, u32 data, u32 mask) { memory_write_generic<Width, AddrShift, Endian, 2, true>(wop(), address, data, mask); } + void write_dword_unaligned(offs_t address, u32 data) { memory_write_generic<Width, AddrShift, Endian, 2, false>(wop(), address, data, 0xffffffff); } + void write_dword_unaligned(offs_t address, u32 data, u32 mask) { memory_write_generic<Width, AddrShift, Endian, 2, false>(wop(), address, data, mask); } + void write_qword(offs_t address, u64 data) { if constexpr(Width == 3) write_native(address & ~NATIVE_MASK, data); else memory_write_generic<Width, AddrShift, Endian, 3, true>(wop(), address, data, 0xffffffffffffffffU); } + void write_qword(offs_t address, u64 data, u64 mask) { memory_write_generic<Width, AddrShift, Endian, 3, true>(wop(), address, data, mask); } + void write_qword_unaligned(offs_t address, u64 data) { memory_write_generic<Width, AddrShift, Endian, 3, false>(wop(), address, data, 0xffffffffffffffffU); } + void write_qword_unaligned(offs_t address, u64 data, u64 mask) { memory_write_generic<Width, AddrShift, Endian, 3, false>(wop(), address, data, mask); } + + + std::pair<u8, u16> read_byte_flags(offs_t address) { if constexpr(Width == 0) return read_native_flags(address & ~NATIVE_MASK); else return memory_read_generic_flags<Width, AddrShift, Endian, 0, true>(ropf(), address, 0xff); } + std::pair<u8, u16> read_byte_flags(offs_t address, u8 mask) { return memory_read_generic_flags<Width, AddrShift, Endian, 0, true>(ropf(), address, mask); } + std::pair<u16, u16> read_word_flags(offs_t address) { if constexpr(Width == 1) return read_native_flags(address & ~NATIVE_MASK); else return memory_read_generic_flags<Width, AddrShift, Endian, 1, true>(ropf(), address, 0xffff); } + std::pair<u16, u16> read_word_flags(offs_t address, u16 mask) { return memory_read_generic_flags<Width, AddrShift, Endian, 1, true>(ropf(), address, mask); } + std::pair<u16, u16> read_word_unaligned_flags(offs_t address) { return memory_read_generic_flags<Width, AddrShift, Endian, 1, false>(ropf(), address, 0xffff); } + std::pair<u16, u16> read_word_unaligned_flags(offs_t address, u16 mask) { return memory_read_generic_flags<Width, AddrShift, Endian, 1, false>(ropf(), address, mask); } + std::pair<u32, u16> read_dword_flags(offs_t address) { if constexpr(Width == 2) return read_native_flags(address & ~NATIVE_MASK); else return memory_read_generic_flags<Width, AddrShift, Endian, 2, true>(ropf(), address, 0xffffffff); } + std::pair<u32, u16> read_dword_flags(offs_t address, u32 mask) { return memory_read_generic_flags<Width, AddrShift, Endian, 2, true>(ropf(), address, mask); } + std::pair<u32, u16> read_dword_unaligned_flags(offs_t address) { return memory_read_generic_flags<Width, AddrShift, Endian, 2, false>(ropf(), address, 0xffffffff); } + std::pair<u32, u16> read_dword_unaligned_flags(offs_t address, u32 mask) { return memory_read_generic_flags<Width, AddrShift, Endian, 2, false>(ropf(), address, mask); } + std::pair<u64, u16> read_qword_flags(offs_t address) { if constexpr(Width == 3) return read_native_flags(address & ~NATIVE_MASK); else return memory_read_generic_flags<Width, AddrShift, Endian, 3, true>(ropf(), address, 0xffffffffffffffffU); } + std::pair<u64, u16> read_qword_flags(offs_t address, u64 mask) { return memory_read_generic_flags<Width, AddrShift, Endian, 3, true>(ropf(), address, mask); } + std::pair<u64, u16> read_qword_unaligned_flags(offs_t address) { return memory_read_generic_flags<Width, AddrShift, Endian, 3, false>(ropf(), address, 0xffffffffffffffffU); } + std::pair<u64, u16> read_qword_unaligned_flags(offs_t address, u64 mask) { return memory_read_generic_flags<Width, AddrShift, Endian, 3, false>(ropf(), address, mask); } + + u16 write_byte_flags(offs_t address, u8 data) { if constexpr(Width == 0) return write_native_flags(address & ~NATIVE_MASK, data); else return memory_write_generic_flags<Width, AddrShift, Endian, 0, true>(wopf(), address, data, 0xff); } + u16 write_byte_flags(offs_t address, u8 data, u8 mask) { return memory_write_generic_flags<Width, AddrShift, Endian, 0, true>(wopf(), address, data, mask); } + u16 write_word_flags(offs_t address, u16 data) { if constexpr(Width == 1) return write_native_flags(address & ~NATIVE_MASK, data); else return memory_write_generic_flags<Width, AddrShift, Endian, 1, true>(wopf(), address, data, 0xffff); } + u16 write_word_flags(offs_t address, u16 data, u16 mask) { return memory_write_generic_flags<Width, AddrShift, Endian, 1, true>(wopf(), address, data, mask); } + u16 write_word_unaligned_flags(offs_t address, u16 data) { return memory_write_generic_flags<Width, AddrShift, Endian, 1, false>(wopf(), address, data, 0xffff); } + u16 write_word_unaligned_flags(offs_t address, u16 data, u16 mask) { return memory_write_generic_flags<Width, AddrShift, Endian, 1, false>(wopf(), address, data, mask); } + u16 write_dword_flags(offs_t address, u32 data) { if constexpr(Width == 2) return write_native_flags(address & ~NATIVE_MASK, data); else return memory_write_generic_flags<Width, AddrShift, Endian, 2, true>(wopf(), address, data, 0xffffffff); } + u16 write_dword_flags(offs_t address, u32 data, u32 mask) { return memory_write_generic_flags<Width, AddrShift, Endian, 2, true>(wopf(), address, data, mask); } + u16 write_dword_unaligned_flags(offs_t address, u32 data) { return memory_write_generic_flags<Width, AddrShift, Endian, 2, false>(wopf(), address, data, 0xffffffff); } + u16 write_dword_unaligned_flags(offs_t address, u32 data, u32 mask) { return memory_write_generic_flags<Width, AddrShift, Endian, 2, false>(wopf(), address, data, mask); } + u16 write_qword_flags(offs_t address, u64 data) { if constexpr(Width == 3) return write_native_flags(address & ~NATIVE_MASK, data); else return memory_write_generic_flags<Width, AddrShift, Endian, 3, true>(wopf(), address, data, 0xffffffffffffffffU); } + u16 write_qword_flags(offs_t address, u64 data, u64 mask) { return memory_write_generic_flags<Width, AddrShift, Endian, 3, true>(wop(), address, data, mask); } + u16 write_qword_unaligned_flags(offs_t address, u64 data) { return memory_write_generic_flags<Width, AddrShift, Endian, 3, false>(wopf(), address, data, 0xffffffffffffffffU); } + u16 write_qword_unaligned_flags(offs_t address, u64 data, u64 mask) { return memory_write_generic_flags<Width, AddrShift, Endian, 3, false>(wopf(), address, data, mask); } + + u16 lookup_read_byte_flags(offs_t address) { if constexpr(Width == 0) return lookup_read_native_flags(address & ~NATIVE_MASK); else return lookup_memory_read_generic_flags<Width, AddrShift, Endian, 0, true>(lropf(), address, 0xff); } + u16 lookup_read_byte_flags(offs_t address, u8 mask) { return lookup_memory_read_generic_flags<Width, AddrShift, Endian, 0, true>(lropf(), address, mask); } + u16 lookup_read_word_flags(offs_t address) { if constexpr(Width == 1) return lookup_read_native_flags(address & ~NATIVE_MASK); else return lookup_memory_read_generic_flags<Width, AddrShift, Endian, 1, true>(lropf(), address, 0xffff); } + u16 lookup_read_word_flags(offs_t address, u16 mask) { return lookup_memory_read_generic_flags<Width, AddrShift, Endian, 1, true>(lropf(), address, mask); } + u16 lookup_read_word_unaligned_flags(offs_t address) { return lookup_memory_read_generic_flags<Width, AddrShift, Endian, 1, false>(lropf(), address, 0xffff); } + u16 lookup_read_word_unaligned_flags(offs_t address, u16 mask) { return lookup_memory_read_generic_flags<Width, AddrShift, Endian, 1, false>(lropf(), address, mask); } + u16 lookup_read_dword_flags(offs_t address) { if constexpr(Width == 2) return lookup_read_native_flags(address & ~NATIVE_MASK); else return lookup_memory_read_generic_flags<Width, AddrShift, Endian, 2, true>(lropf(), address, 0xffffffff); } + u16 lookup_read_dword_flags(offs_t address, u32 mask) { return lookup_memory_read_generic_flags<Width, AddrShift, Endian, 2, true>(lropf(), address, mask); } + u16 lookup_read_dword_unaligned_flags(offs_t address) { return lookup_memory_read_generic_flags<Width, AddrShift, Endian, 2, false>(lropf(), address, 0xffffffff); } + u16 lookup_read_dword_unaligned_flags(offs_t address, u32 mask) { return lookup_memory_read_generic_flags<Width, AddrShift, Endian, 2, false>(lropf(), address, mask); } + u16 lookup_read_qword_flags(offs_t address) { if constexpr(Width == 3) return lookup_read_native_flags(address & ~NATIVE_MASK); else return lookup_memory_read_generic_flags<Width, AddrShift, Endian, 3, true>(lropf(), address, 0xffffffffffffffffU); } + u16 lookup_read_qword_flags(offs_t address, u64 mask) { return lookup_memory_read_generic_flags<Width, AddrShift, Endian, 3, true>(lropf(), address, mask); } + u16 lookup_read_qword_unaligned_flags(offs_t address) { return lookup_memory_read_generic_flags<Width, AddrShift, Endian, 3, false>(lropf(), address, 0xffffffffffffffffU); } + u16 lookup_read_qword_unaligned_flags(offs_t address, u64 mask) { return lookup_memory_read_generic_flags<Width, AddrShift, Endian, 3, false>(lropf(), address, mask); } + + u16 lookup_write_byte_flags(offs_t address) { if constexpr(Width == 0) return lookup_write_native_flags(address & ~NATIVE_MASK); else return lookup_memory_write_generic_flags<Width, AddrShift, Endian, 0, true>(lwopf(), address, 0xff); } + u16 lookup_write_byte_flags(offs_t address, u8 mask) { return lookup_memory_write_generic_flags<Width, AddrShift, Endian, 0, true>(lwopf(), address, mask); } + u16 lookup_write_word_flags(offs_t address) { if constexpr(Width == 1) return lookup_write_native_flags(address & ~NATIVE_MASK); else return lookup_memory_write_generic_flags<Width, AddrShift, Endian, 1, true>(lwopf(), address, 0xffff); } + u16 lookup_write_word_flags(offs_t address, u16 mask) { return lookup_memory_write_generic_flags<Width, AddrShift, Endian, 1, true>(lwopf(), address, mask); } + u16 lookup_write_word_unaligned_flags(offs_t address) { return lookup_memory_write_generic_flags<Width, AddrShift, Endian, 1, false>(lwopf(), address, 0xffff); } + u16 lookup_write_word_unaligned_flags(offs_t address, u16 mask) { return lookup_memory_write_generic_flags<Width, AddrShift, Endian, 1, false>(lwopf(), address, mask); } + u16 lookup_write_dword_flags(offs_t address) { if constexpr(Width == 2) return lookup_write_native_flags(address & ~NATIVE_MASK); else return lookup_memory_write_generic_flags<Width, AddrShift, Endian, 2, true>(lwopf(), address, 0xffffffff); } + u16 lookup_write_dword_flags(offs_t address, u32 mask) { return lookup_memory_write_generic_flags<Width, AddrShift, Endian, 2, true>(lwopf(), address, mask); } + u16 lookup_write_dword_unaligned_flags(offs_t address) { return lookup_memory_write_generic_flags<Width, AddrShift, Endian, 2, false>(lwopf(), address, 0xffffffff); } + u16 lookup_write_dword_unaligned_flags(offs_t address, u32 mask) { return lookup_memory_write_generic_flags<Width, AddrShift, Endian, 2, false>(lwopf(), address, mask); } + u16 lookup_write_qword_flags(offs_t address) { if constexpr(Width == 3) return lookup_write_native_flags(address & ~NATIVE_MASK); else return lookup_memory_write_generic_flags<Width, AddrShift, Endian, 3, true>(lwopf(), address, 0xffffffffffffffffU); } + u16 lookup_write_qword_flags(offs_t address, u64 mask) { return lookup_memory_write_generic_flags<Width, AddrShift, Endian, 3, true>(wop(), address, mask); } + u16 lookup_write_qword_unaligned_flags(offs_t address) { return lookup_memory_write_generic_flags<Width, AddrShift, Endian, 3, false>(lwopf(), address, 0xffffffffffffffffU); } + u16 lookup_write_qword_unaligned_flags(offs_t address, u64 mask) { return lookup_memory_write_generic_flags<Width, AddrShift, Endian, 3, false>(lwopf(), address, mask); } + + NativeType read_interruptible(offs_t address, NativeType mask = ~NativeType(0)) { + return dispatch_read_interruptible<Level, Width, AddrShift>(~offs_t(0), address & m_addrmask, mask, m_dispatch_read); + } + + void write_interruptible(offs_t address, NativeType data, NativeType mask = ~NativeType(0)) { + dispatch_write_interruptible<Level, Width, AddrShift>(~offs_t(0), address & m_addrmask, data, mask, m_dispatch_write); + } + +private: + address_space * m_space; + + offs_t m_addrmask; // address mask + + const handler_entry_read<Width, AddrShift> *const *m_dispatch_read; + const handler_entry_write<Width, AddrShift> *const *m_dispatch_write; + + NativeType read_native(offs_t address, NativeType mask = ~NativeType(0)) { + return dispatch_read<Level, Width, AddrShift>(~offs_t(0), address & m_addrmask, mask, m_dispatch_read); + } + + void write_native(offs_t address, NativeType data, NativeType mask = ~NativeType(0)) { + dispatch_write<Level, Width, AddrShift>(~offs_t(0), address & m_addrmask, data, mask, m_dispatch_write); + } + + std::pair<NativeType, u16> read_native_flags(offs_t address, NativeType mask = ~NativeType(0)) { + return dispatch_read_flags<Level, Width, AddrShift>(~offs_t(0), address & m_addrmask, mask, m_dispatch_read); + } + + u16 write_native_flags(offs_t address, NativeType data, NativeType mask = ~NativeType(0)) { + return dispatch_write_flags<Level, Width, AddrShift>(~offs_t(0), address & m_addrmask, data, mask, m_dispatch_write); + } + + u16 lookup_read_native_flags(offs_t address, NativeType mask = ~NativeType(0)) { + return dispatch_lookup_read_flags<Level, Width, AddrShift>(~offs_t(0), address & m_addrmask, mask, m_dispatch_read); + } + + u16 lookup_write_native_flags(offs_t address, NativeType mask = ~NativeType(0)) { + return dispatch_lookup_write_flags<Level, Width, AddrShift>(~offs_t(0), address & m_addrmask, mask, m_dispatch_write); + } + + void set(address_space *space, std::pair<const void *, const void *> rw); +}; + + // ======================> memory_access_cache // memory_access_cache contains state data for cached access -template<int Width, int AddrShift, int Endian> class memory_access_cache +template<int Width, int AddrShift, endianness_t Endian> class memory_access_cache { - using NativeType = typename emu::detail::handler_entry_size<Width>::uX; + friend class ::address_space; + + using NativeType = emu::detail::handler_entry_size_t<Width>; static constexpr u32 NATIVE_BYTES = 1 << Width; static constexpr u32 NATIVE_MASK = Width + AddrShift >= 0 ? (1 << (Width + AddrShift)) - 1 : 0; public: // construction/destruction - memory_access_cache(address_space &space, - handler_entry_read <Width, AddrShift, Endian> *root_read, - handler_entry_write<Width, AddrShift, Endian> *root_write); + memory_access_cache() + : m_space(nullptr), + m_addrmask(0), + m_addrstart_r(1), + m_addrend_r(0), + m_addrstart_w(1), + m_addrend_w(0), + m_cache_r(nullptr), + m_cache_w(nullptr), + m_root_read(nullptr), + m_root_write(nullptr) + { + } ~memory_access_cache(); - // getters - address_space &space() const { return m_space; } - // see if an address is within bounds, update it if not void check_address_r(offs_t address) { - if(address >= m_addrstart_r && address <= m_addrend_r) + if(EXPECTED(address >= m_addrstart_r && address <= m_addrend_r)) return; m_root_read->lookup(address, m_addrstart_r, m_addrend_r, m_cache_r); } void check_address_w(offs_t address) { - if(address >= m_addrstart_w && address <= m_addrend_w) + if(EXPECTED(address >= m_addrstart_w && address <= m_addrend_w)) return; m_root_write->lookup(address, m_addrstart_w, m_addrend_w, m_cache_w); } // accessor methods + inline address_space &space() const { + return *m_space; + } + void *read_ptr(offs_t address) { + address &= m_addrmask; check_address_r(address); return m_cache_r->get_ptr(address); } - u8 read_byte(offs_t address) { address &= m_addrmask; return Width == 0 ? read_native(address & ~NATIVE_MASK) : memory_read_generic<Width, AddrShift, Endian, 0, true>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xff); } - u16 read_word(offs_t address) { address &= m_addrmask; return Width == 1 ? read_native(address & ~NATIVE_MASK) : memory_read_generic<Width, AddrShift, Endian, 1, true>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffff); } - u16 read_word(offs_t address, u16 mask) { address &= m_addrmask; return memory_read_generic<Width, AddrShift, Endian, 1, true>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); } - u16 read_word_unaligned(offs_t address) { address &= m_addrmask; return memory_read_generic<Width, AddrShift, Endian, 1, false>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffff); } - u16 read_word_unaligned(offs_t address, u16 mask) { address &= m_addrmask; return memory_read_generic<Width, AddrShift, Endian, 1, false>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); } - u32 read_dword(offs_t address) { address &= m_addrmask; return Width == 2 ? read_native(address & ~NATIVE_MASK) : memory_read_generic<Width, AddrShift, Endian, 2, true>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffffffff); } - u32 read_dword(offs_t address, u32 mask) { address &= m_addrmask; return memory_read_generic<Width, AddrShift, Endian, 2, true>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); } - u32 read_dword_unaligned(offs_t address) { address &= m_addrmask; return memory_read_generic<Width, AddrShift, Endian, 2, false>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffffffff); } - u32 read_dword_unaligned(offs_t address, u32 mask) { address &= m_addrmask; return memory_read_generic<Width, AddrShift, Endian, 2, false>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); } - u64 read_qword(offs_t address) { address &= m_addrmask; return Width == 3 ? read_native(address & ~NATIVE_MASK) : memory_read_generic<Width, AddrShift, Endian, 3, true>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffffffffffffffffU); } - u64 read_qword(offs_t address, u64 mask) { address &= m_addrmask; return memory_read_generic<Width, AddrShift, Endian, 3, true>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); } - u64 read_qword_unaligned(offs_t address) { address &= m_addrmask; return memory_read_generic<Width, AddrShift, Endian, 3, false>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffffffffffffffffU); } - u64 read_qword_unaligned(offs_t address, u64 mask) { address &= m_addrmask; return memory_read_generic<Width, AddrShift, Endian, 3, false>([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); } - - void write_byte(offs_t address, u8 data) { address &= m_addrmask; if (Width == 0) write_native(address & ~NATIVE_MASK, data); else memory_write_generic<Width, AddrShift, Endian, 0, true>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xff); } - void write_word(offs_t address, u16 data) { address &= m_addrmask; if (Width == 1) write_native(address & ~NATIVE_MASK, data); else memory_write_generic<Width, AddrShift, Endian, 1, true>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffff); } - void write_word(offs_t address, u16 data, u16 mask) { address &= m_addrmask; memory_write_generic<Width, AddrShift, Endian, 1, true>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); } - void write_word_unaligned(offs_t address, u16 data) { address &= m_addrmask; memory_write_generic<Width, AddrShift, Endian, 1, false>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffff); } - void write_word_unaligned(offs_t address, u16 data, u16 mask) { address &= m_addrmask; memory_write_generic<Width, AddrShift, Endian, 1, false>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); } - void write_dword(offs_t address, u32 data) { address &= m_addrmask; if (Width == 2) write_native(address & ~NATIVE_MASK, data); else memory_write_generic<Width, AddrShift, Endian, 2, true>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffffffff); } - void write_dword(offs_t address, u32 data, u32 mask) { address &= m_addrmask; memory_write_generic<Width, AddrShift, Endian, 2, true>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); } - void write_dword_unaligned(offs_t address, u32 data) { address &= m_addrmask; memory_write_generic<Width, AddrShift, Endian, 2, false>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffffffff); } - void write_dword_unaligned(offs_t address, u32 data, u32 mask) { address &= m_addrmask; memory_write_generic<Width, AddrShift, Endian, 2, false>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); } - void write_qword(offs_t address, u64 data) { address &= m_addrmask; if (Width == 3) write_native(address & ~NATIVE_MASK, data); else memory_write_generic<Width, AddrShift, Endian, 3, true>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffffffffffffffffU); } - void write_qword(offs_t address, u64 data, u64 mask) { address &= m_addrmask; memory_write_generic<Width, AddrShift, Endian, 3, true>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); } - void write_qword_unaligned(offs_t address, u64 data) { address &= m_addrmask; memory_write_generic<Width, AddrShift, Endian, 3, false>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffffffffffffffffU); } - void write_qword_unaligned(offs_t address, u64 data, u64 mask) { address &= m_addrmask; memory_write_generic<Width, AddrShift, Endian, 3, false>([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); } + auto rop() { return [this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }; } + auto ropf() { return [this](offs_t offset, NativeType mask) -> std::pair<NativeType, u16> { return read_native_flags(offset, mask); }; } + auto lropf() { return [this](offs_t offset, NativeType mask) -> u16 { return lookup_read_native_flags(offset, mask); }; } + auto wop() { return [this](offs_t offset, NativeType data, NativeType mask) -> void { write_native(offset, data, mask); }; } + auto wopf() { return [this](offs_t offset, NativeType data, NativeType mask) -> u16 { return write_native_flags(offset, data, mask); }; } + auto lwopf() { return [this](offs_t offset, NativeType mask) -> u16 { return lookup_write_native_flags(offset, mask); }; } + + u8 read_byte(offs_t address) { if constexpr(Width == 0) return read_native(address & ~NATIVE_MASK); else return memory_read_generic<Width, AddrShift, Endian, 0, true>(rop(), address, 0xff); } + u8 read_byte(offs_t address, u8 mask) { return memory_read_generic<Width, AddrShift, Endian, 0, true>(rop(), address, mask); } + u16 read_word(offs_t address) { if constexpr(Width == 1) return read_native(address & ~NATIVE_MASK); else return memory_read_generic<Width, AddrShift, Endian, 1, true>(rop(), address, 0xffff); } + u16 read_word(offs_t address, u16 mask) { return memory_read_generic<Width, AddrShift, Endian, 1, true>(rop(), address, mask); } + u16 read_word_unaligned(offs_t address) { return memory_read_generic<Width, AddrShift, Endian, 1, false>(rop(), address, 0xffff); } + u16 read_word_unaligned(offs_t address, u16 mask) { return memory_read_generic<Width, AddrShift, Endian, 1, false>(rop(), address, mask); } + u32 read_dword(offs_t address) { if constexpr(Width == 2) return read_native(address & ~NATIVE_MASK); else return memory_read_generic<Width, AddrShift, Endian, 2, true>(rop(), address, 0xffffffff); } + u32 read_dword(offs_t address, u32 mask) { return memory_read_generic<Width, AddrShift, Endian, 2, true>(rop(), address, mask); } + u32 read_dword_unaligned(offs_t address) { return memory_read_generic<Width, AddrShift, Endian, 2, false>(rop(), address, 0xffffffff); } + u32 read_dword_unaligned(offs_t address, u32 mask) { return memory_read_generic<Width, AddrShift, Endian, 2, false>(rop(), address, mask); } + u64 read_qword(offs_t address) { if constexpr(Width == 3) return read_native(address & ~NATIVE_MASK); else return memory_read_generic<Width, AddrShift, Endian, 3, true>(rop(), address, 0xffffffffffffffffU); } + u64 read_qword(offs_t address, u64 mask) { return memory_read_generic<Width, AddrShift, Endian, 3, true>(rop(), address, mask); } + u64 read_qword_unaligned(offs_t address) { return memory_read_generic<Width, AddrShift, Endian, 3, false>(rop(), address, 0xffffffffffffffffU); } + u64 read_qword_unaligned(offs_t address, u64 mask) { return memory_read_generic<Width, AddrShift, Endian, 3, false>(rop(), address, mask); } + + void write_byte(offs_t address, u8 data) { if constexpr(Width == 0) write_native(address & ~NATIVE_MASK, data); else memory_write_generic<Width, AddrShift, Endian, 0, true>(wop(), address, data, 0xff); } + void write_byte(offs_t address, u8 data, u8 mask) { memory_write_generic<Width, AddrShift, Endian, 0, true>(wop(), address, data, mask); } + void write_word(offs_t address, u16 data) { if constexpr(Width == 1) write_native(address & ~NATIVE_MASK, data); else memory_write_generic<Width, AddrShift, Endian, 1, true>(wop(), address, data, 0xffff); } + void write_word(offs_t address, u16 data, u16 mask) { memory_write_generic<Width, AddrShift, Endian, 1, true>(wop(), address, data, mask); } + void write_word_unaligned(offs_t address, u16 data) { memory_write_generic<Width, AddrShift, Endian, 1, false>(wop(), address, data, 0xffff); } + void write_word_unaligned(offs_t address, u16 data, u16 mask) { memory_write_generic<Width, AddrShift, Endian, 1, false>(wop(), address, data, mask); } + void write_dword(offs_t address, u32 data) { if constexpr(Width == 2) write_native(address & ~NATIVE_MASK, data); else memory_write_generic<Width, AddrShift, Endian, 2, true>(wop(), address, data, 0xffffffff); } + void write_dword(offs_t address, u32 data, u32 mask) { memory_write_generic<Width, AddrShift, Endian, 2, true>(wop(), address, data, mask); } + void write_dword_unaligned(offs_t address, u32 data) { memory_write_generic<Width, AddrShift, Endian, 2, false>(wop(), address, data, 0xffffffff); } + void write_dword_unaligned(offs_t address, u32 data, u32 mask) { memory_write_generic<Width, AddrShift, Endian, 2, false>(wop(), address, data, mask); } + void write_qword(offs_t address, u64 data) { if constexpr(Width == 3) write_native(address & ~NATIVE_MASK, data); else memory_write_generic<Width, AddrShift, Endian, 3, true>(wop(), address, data, 0xffffffffffffffffU); } + void write_qword(offs_t address, u64 data, u64 mask) { memory_write_generic<Width, AddrShift, Endian, 3, true>(wop(), address, data, mask); } + void write_qword_unaligned(offs_t address, u64 data) { memory_write_generic<Width, AddrShift, Endian, 3, false>(wop(), address, data, 0xffffffffffffffffU); } + void write_qword_unaligned(offs_t address, u64 data, u64 mask) { memory_write_generic<Width, AddrShift, Endian, 3, false>(wop(), address, data, mask); } + + + std::pair<u8, u16> read_byte_flags(offs_t address) { if constexpr(Width == 0) return read_native_flags(address & ~NATIVE_MASK); else return memory_read_generic_flags<Width, AddrShift, Endian, 0, true>(ropf(), address, 0xff); } + std::pair<u8, u16> read_byte_flags(offs_t address, u8 mask) { return memory_read_generic_flags<Width, AddrShift, Endian, 0, true>(ropf(), address, mask); } + std::pair<u16, u16> read_word_flags(offs_t address) { if constexpr(Width == 1) return read_native_flags(address & ~NATIVE_MASK); else return memory_read_generic_flags<Width, AddrShift, Endian, 1, true>(ropf(), address, 0xffff); } + std::pair<u16, u16> read_word_flags(offs_t address, u16 mask) { return memory_read_generic_flags<Width, AddrShift, Endian, 1, true>(ropf(), address, mask); } + std::pair<u16, u16> read_word_unaligned_flags(offs_t address) { return memory_read_generic_flags<Width, AddrShift, Endian, 1, false>(ropf(), address, 0xffff); } + std::pair<u16, u16> read_word_unaligned_flags(offs_t address, u16 mask) { return memory_read_generic_flags<Width, AddrShift, Endian, 1, false>(ropf(), address, mask); } + std::pair<u32, u16> read_dword_flags(offs_t address) { if constexpr(Width == 2) return read_native_flags(address & ~NATIVE_MASK); else return memory_read_generic_flags<Width, AddrShift, Endian, 2, true>(ropf(), address, 0xffffffff); } + std::pair<u32, u16> read_dword_flags(offs_t address, u32 mask) { return memory_read_generic_flags<Width, AddrShift, Endian, 2, true>(ropf(), address, mask); } + std::pair<u32, u16> read_dword_unaligned_flags(offs_t address) { return memory_read_generic_flags<Width, AddrShift, Endian, 2, false>(ropf(), address, 0xffffffff); } + std::pair<u32, u16> read_dword_unaligned_flags(offs_t address, u32 mask) { return memory_read_generic_flags<Width, AddrShift, Endian, 2, false>(ropf(), address, mask); } + std::pair<u64, u16> read_qword_flags(offs_t address) { if constexpr(Width == 3) return read_native_flags(address & ~NATIVE_MASK); else return memory_read_generic_flags<Width, AddrShift, Endian, 3, true>(ropf(), address, 0xffffffffffffffffU); } + std::pair<u64, u16> read_qword_flags(offs_t address, u64 mask) { return memory_read_generic_flags<Width, AddrShift, Endian, 3, true>(ropf(), address, mask); } + std::pair<u64, u16> read_qword_unaligned_flags(offs_t address) { return memory_read_generic_flags<Width, AddrShift, Endian, 3, false>(ropf(), address, 0xffffffffffffffffU); } + std::pair<u64, u16> read_qword_unaligned_flags(offs_t address, u64 mask) { return memory_read_generic_flags<Width, AddrShift, Endian, 3, false>(ropf(), address, mask); } + + u16 write_byte_flags(offs_t address, u8 data) { if constexpr(Width == 0) return write_native_flags(address & ~NATIVE_MASK, data); else return memory_write_generic_flags<Width, AddrShift, Endian, 0, true>(wopf(), address, data, 0xff); } + u16 write_byte_flags(offs_t address, u8 data, u8 mask) { return memory_write_generic_flags<Width, AddrShift, Endian, 0, true>(wopf(), address, data, mask); } + u16 write_word_flags(offs_t address, u16 data) { if constexpr(Width == 1) return write_native_flags(address & ~NATIVE_MASK, data); else return memory_write_generic_flags<Width, AddrShift, Endian, 1, true>(wopf(), address, data, 0xffff); } + u16 write_word_flags(offs_t address, u16 data, u16 mask) { return memory_write_generic_flags<Width, AddrShift, Endian, 1, true>(wopf(), address, data, mask); } + u16 write_word_unaligned_flags(offs_t address, u16 data) { return memory_write_generic_flags<Width, AddrShift, Endian, 1, false>(wopf(), address, data, 0xffff); } + u16 write_word_unaligned_flags(offs_t address, u16 data, u16 mask) { return memory_write_generic_flags<Width, AddrShift, Endian, 1, false>(wopf(), address, data, mask); } + u16 write_dword_flags(offs_t address, u32 data) { if constexpr(Width == 2) return write_native_flags(address & ~NATIVE_MASK, data); else return memory_write_generic_flags<Width, AddrShift, Endian, 2, true>(wopf(), address, data, 0xffffffff); } + u16 write_dword_flags(offs_t address, u32 data, u32 mask) { return memory_write_generic_flags<Width, AddrShift, Endian, 2, true>(wopf(), address, data, mask); } + u16 write_dword_unaligned_flags(offs_t address, u32 data) { return memory_write_generic_flags<Width, AddrShift, Endian, 2, false>(wopf(), address, data, 0xffffffff); } + u16 write_dword_unaligned_flags(offs_t address, u32 data, u32 mask) { return memory_write_generic_flags<Width, AddrShift, Endian, 2, false>(wopf(), address, data, mask); } + u16 write_qword_flags(offs_t address, u64 data) { if constexpr(Width == 3) return write_native_flags(address & ~NATIVE_MASK, data); else return memory_write_generic_flags<Width, AddrShift, Endian, 3, true>(wopf(), address, data, 0xffffffffffffffffU); } + u16 write_qword_flags(offs_t address, u64 data, u64 mask) { return memory_write_generic_flags<Width, AddrShift, Endian, 3, true>(wop(), address, data, mask); } + u16 write_qword_unaligned_flags(offs_t address, u64 data) { return memory_write_generic_flags<Width, AddrShift, Endian, 3, false>(wopf(), address, data, 0xffffffffffffffffU); } + u16 write_qword_unaligned_flags(offs_t address, u64 data, u64 mask) { return memory_write_generic_flags<Width, AddrShift, Endian, 3, false>(wopf(), address, data, mask); } + + + u16 lookup_read_byte_flags(offs_t address) { if constexpr(Width == 0) return lookup_read_native_flags(address & ~NATIVE_MASK); else return lookup_memory_read_generic_flags<Width, AddrShift, Endian, 0, true>(lropf(), address, 0xff); } + u16 lookup_read_byte_flags(offs_t address, u8 mask) { return lookup_memory_read_generic_flags<Width, AddrShift, Endian, 0, true>(lropf(), address, mask); } + u16 lookup_read_word_flags(offs_t address) { if constexpr(Width == 1) return lookup_read_native_flags(address & ~NATIVE_MASK); else return lookup_memory_read_generic_flags<Width, AddrShift, Endian, 1, true>(lropf(), address, 0xffff); } + u16 lookup_read_word_flags(offs_t address, u16 mask) { return lookup_memory_read_generic_flags<Width, AddrShift, Endian, 1, true>(lropf(), address, mask); } + u16 lookup_read_word_unaligned_flags(offs_t address) { return lookup_memory_read_generic_flags<Width, AddrShift, Endian, 1, false>(lropf(), address, 0xffff); } + u16 lookup_read_word_unaligned_flags(offs_t address, u16 mask) { return lookup_memory_read_generic_flags<Width, AddrShift, Endian, 1, false>(lropf(), address, mask); } + u16 lookup_read_dword_flags(offs_t address) { if constexpr(Width == 2) return lookup_read_native_flags(address & ~NATIVE_MASK); else return lookup_memory_read_generic_flags<Width, AddrShift, Endian, 2, true>(lropf(), address, 0xffffffff); } + u16 lookup_read_dword_flags(offs_t address, u32 mask) { return lookup_memory_read_generic_flags<Width, AddrShift, Endian, 2, true>(lropf(), address, mask); } + u16 lookup_read_dword_unaligned_flags(offs_t address) { return lookup_memory_read_generic_flags<Width, AddrShift, Endian, 2, false>(lropf(), address, 0xffffffff); } + u16 lookup_read_dword_unaligned_flags(offs_t address, u32 mask) { return lookup_memory_read_generic_flags<Width, AddrShift, Endian, 2, false>(lropf(), address, mask); } + u16 lookup_read_qword_flags(offs_t address) { if constexpr(Width == 3) return lookup_read_native_flags(address & ~NATIVE_MASK); else return lookup_memory_read_generic_flags<Width, AddrShift, Endian, 3, true>(lropf(), address, 0xffffffffffffffffU); } + u16 lookup_read_qword_flags(offs_t address, u64 mask) { return lookup_memory_read_generic_flags<Width, AddrShift, Endian, 3, true>(lropf(), address, mask); } + u16 lookup_read_qword_unaligned_flags(offs_t address) { return lookup_memory_read_generic_flags<Width, AddrShift, Endian, 3, false>(lropf(), address, 0xffffffffffffffffU); } + u16 lookup_read_qword_unaligned_flags(offs_t address, u64 mask) { return lookup_memory_read_generic_flags<Width, AddrShift, Endian, 3, false>(lropf(), address, mask); } + + u16 lookup_write_byte_flags(offs_t address) { if constexpr(Width == 0) return lookup_write_native_flags(address & ~NATIVE_MASK); else return lookup_memory_write_generic_flags<Width, AddrShift, Endian, 0, true>(lwopf(), address, 0xff); } + u16 lookup_write_byte_flags(offs_t address, u8 mask) { return lookup_memory_write_generic_flags<Width, AddrShift, Endian, 0, true>(lwopf(), address, mask); } + u16 lookup_write_word_flags(offs_t address) { if constexpr(Width == 1) return lookup_write_native_flags(address & ~NATIVE_MASK); else return lookup_memory_write_generic_flags<Width, AddrShift, Endian, 1, true>(lwopf(), address, 0xffff); } + u16 lookup_write_word_flags(offs_t address, u16 mask) { return lookup_memory_write_generic_flags<Width, AddrShift, Endian, 1, true>(lwopf(), address, mask); } + u16 lookup_write_word_unaligned_flags(offs_t address) { return lookup_memory_write_generic_flags<Width, AddrShift, Endian, 1, false>(lwopf(), address, 0xffff); } + u16 lookup_write_word_unaligned_flags(offs_t address, u16 mask) { return lookup_memory_write_generic_flags<Width, AddrShift, Endian, 1, false>(lwopf(), address, mask); } + u16 lookup_write_dword_flags(offs_t address) { if constexpr(Width == 2) return lookup_write_native_flags(address & ~NATIVE_MASK); else return lookup_memory_write_generic_flags<Width, AddrShift, Endian, 2, true>(lwopf(), address, 0xffffffff); } + u16 lookup_write_dword_flags(offs_t address, u32 mask) { return lookup_memory_write_generic_flags<Width, AddrShift, Endian, 2, true>(lwopf(), address, mask); } + u16 lookup_write_dword_unaligned_flags(offs_t address) { return lookup_memory_write_generic_flags<Width, AddrShift, Endian, 2, false>(lwopf(), address, 0xffffffff); } + u16 lookup_write_dword_unaligned_flags(offs_t address, u32 mask) { return lookup_memory_write_generic_flags<Width, AddrShift, Endian, 2, false>(lwopf(), address, mask); } + u16 lookup_write_qword_flags(offs_t address) { if constexpr(Width == 3) return lookup_write_native_flags(address & ~NATIVE_MASK); else return lookup_memory_write_generic_flags<Width, AddrShift, Endian, 3, true>(lwopf(), address, 0xffffffffffffffffU); } + u16 lookup_write_qword_flags(offs_t address, u64 mask) { return lookup_memory_write_generic_flags<Width, AddrShift, Endian, 3, true>(wop(), address, mask); } + u16 lookup_write_qword_unaligned_flags(offs_t address) { return lookup_memory_write_generic_flags<Width, AddrShift, Endian, 3, false>(lwopf(), address, 0xffffffffffffffffU); } + u16 lookup_write_qword_unaligned_flags(offs_t address, u64 mask) { return lookup_memory_write_generic_flags<Width, AddrShift, Endian, 3, false>(lwopf(), address, mask); } private: - address_space & m_space; - - int m_notifier_id; // id to remove the notifier on destruction + address_space * m_space; offs_t m_addrmask; // address mask offs_t m_addrstart_r; // minimum valid address for reading offs_t m_addrend_r; // maximum valid address for reading offs_t m_addrstart_w; // minimum valid address for writing offs_t m_addrend_w; // maximum valid address for writing - handler_entry_read<Width, AddrShift, Endian> *m_cache_r; // read cache - handler_entry_write<Width, AddrShift, Endian> *m_cache_w; // write cache + handler_entry_read <Width, AddrShift> *m_cache_r; // read cache + handler_entry_write<Width, AddrShift> *m_cache_w; // write cache - handler_entry_read <Width, AddrShift, Endian> *m_root_read; // decode tree roots - handler_entry_write<Width, AddrShift, Endian> *m_root_write; + handler_entry_read <Width, AddrShift> *m_root_read; // decode tree roots + handler_entry_write<Width, AddrShift> *m_root_write; + + util::notifier_subscription m_subscription; NativeType read_native(offs_t address, NativeType mask = ~NativeType(0)); void write_native(offs_t address, NativeType data, NativeType mask = ~NativeType(0)); + std::pair<NativeType, u16> read_native_flags(offs_t address, NativeType mask = ~NativeType(0)); + u16 write_native_flags(offs_t address, NativeType data, NativeType mask = ~NativeType(0)); + u16 lookup_read_native_flags(offs_t address, NativeType mask = ~NativeType(0)); + u16 lookup_write_native_flags(offs_t address, NativeType mask = ~NativeType(0)); + + void set(address_space *space, std::pair<void *, void *> rw); }; +} // namespace emu::detail + + +// ======================> memory_access cache/specific type dispatcher + +template<int HighBits, int Width, int AddrShift, endianness_t Endian> struct memory_access { + static constexpr int Level = emu::detail::handler_entry_dispatch_level(HighBits); + + using cache = emu::detail::memory_access_cache<Width, AddrShift, Endian>; + using specific = emu::detail::memory_access_specific<Level, Width, AddrShift, Endian>; +}; + + // ======================> address_space_config @@ -1177,25 +2089,270 @@ public: // ======================> address_space +class address_space_installer { +public: + const address_space_config &space_config() const { return m_config; } + int data_width() const { return m_config.data_width(); } + int addr_width() const { return m_config.addr_width(); } + int logaddr_width() const { return m_config.logaddr_width(); } + int alignment() const { return m_config.alignment(); } + endianness_t endianness() const { return m_config.endianness(); } + int addr_shift() const { return m_config.addr_shift(); } + bool is_octal() const { return m_config.is_octal(); } + + // address-to-byte conversion helpers + offs_t address_to_byte(offs_t address) const { return m_config.addr2byte(address); } + offs_t address_to_byte_end(offs_t address) const { return m_config.addr2byte_end(address); } + offs_t byte_to_address(offs_t address) const { return m_config.byte2addr(address); } + offs_t byte_to_address_end(offs_t address) const { return m_config.byte2addr_end(address); } + + offs_t addrmask() const { return m_addrmask; } + u8 addrchars() const { return m_addrchars; } + offs_t logaddrmask() const { return m_logaddrmask; } + u8 logaddrchars() const { return m_logaddrchars; } + + // unmap ranges (short form) + void unmap_read(offs_t addrstart, offs_t addrend, offs_t addrmirror = 0, u16 flags = 0) { unmap_generic(addrstart, addrend, addrmirror, flags, read_or_write::READ, false); } + void unmap_write(offs_t addrstart, offs_t addrend, offs_t addrmirror = 0, u16 flags = 0) { unmap_generic(addrstart, addrend, addrmirror, flags, read_or_write::WRITE, false); } + void unmap_readwrite(offs_t addrstart, offs_t addrend, offs_t addrmirror = 0, u16 flags = 0) { unmap_generic(addrstart, addrend, addrmirror, flags, read_or_write::READWRITE, false); } + void nop_read(offs_t addrstart, offs_t addrend, offs_t addrmirror = 0, u16 flags = 0) { unmap_generic(addrstart, addrend, addrmirror, flags, read_or_write::READ, true); } + void nop_write(offs_t addrstart, offs_t addrend, offs_t addrmirror = 0, u16 flags = 0) { unmap_generic(addrstart, addrend, addrmirror, flags, read_or_write::WRITE, true); } + void nop_readwrite(offs_t addrstart, offs_t addrend, offs_t addrmirror = 0, u16 flags = 0) { unmap_generic(addrstart, addrend, addrmirror, flags, read_or_write::READWRITE, true); } + + // install ports, banks, RAM (short form) + void install_read_port(offs_t addrstart, offs_t addrend, const char *rtag) { install_read_port(addrstart, addrend, 0, 0, rtag); } + void install_write_port(offs_t addrstart, offs_t addrend, const char *wtag) { install_write_port(addrstart, addrend, 0, 0, wtag); } + void install_readwrite_port(offs_t addrstart, offs_t addrend, const char *rtag, const char *wtag) { install_readwrite_port(addrstart, addrend, 0, 0, rtag, wtag); } + void install_read_bank(offs_t addrstart, offs_t addrend, memory_bank *bank) { install_read_bank(addrstart, addrend, 0, 0, bank); } + void install_write_bank(offs_t addrstart, offs_t addrend, memory_bank *bank) { install_write_bank(addrstart, addrend, 0, 0, bank); } + void install_readwrite_bank(offs_t addrstart, offs_t addrend, memory_bank *bank) { install_readwrite_bank(addrstart, addrend, 0, 0, bank); } + void install_rom(offs_t addrstart, offs_t addrend, void *baseptr) { install_rom(addrstart, addrend, 0, 0, baseptr); } + void install_writeonly(offs_t addrstart, offs_t addrend, void *baseptr) { install_writeonly(addrstart, addrend, 0, 0, baseptr); } + void install_ram(offs_t addrstart, offs_t addrend, void *baseptr) { install_ram(addrstart, addrend, 0, 0, baseptr); } + + // install ports, banks, RAM (with mirror/mask) + void install_read_port(offs_t addrstart, offs_t addrend, offs_t addrmirror, const char *rtag) { install_readwrite_port(addrstart, addrend, addrmirror, 0, rtag, ""); } + void install_write_port(offs_t addrstart, offs_t addrend, offs_t addrmirror, const char *wtag) { install_readwrite_port(addrstart, addrend, addrmirror, 0, "", wtag); } + void install_readwrite_port(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string rtag, std::string wtag) { install_readwrite_port(addrstart, addrend, addrmirror, 0, rtag, wtag); } + void install_read_bank(offs_t addrstart, offs_t addrend, offs_t addrmirror, memory_bank *bank) { install_bank_generic(addrstart, addrend, addrmirror, 0, bank, nullptr); } + void install_write_bank(offs_t addrstart, offs_t addrend, offs_t addrmirror, memory_bank *bank) { install_bank_generic(addrstart, addrend, addrmirror, 0, nullptr, bank); } + void install_readwrite_bank(offs_t addrstart, offs_t addrend, offs_t addrmirror, memory_bank *bank) { install_bank_generic(addrstart, addrend, addrmirror, 0, bank, bank); } + void install_rom(offs_t addrstart, offs_t addrend, offs_t addrmirror, void *baseptr) { install_ram_generic(addrstart, addrend, addrmirror, 0, read_or_write::READ, baseptr); } + void install_writeonly(offs_t addrstart, offs_t addrend, offs_t addrmirror, void *baseptr) { install_ram_generic(addrstart, addrend, addrmirror, 0, read_or_write::WRITE, baseptr); } + void install_ram(offs_t addrstart, offs_t addrend, offs_t addrmirror, void *baseptr) { install_ram_generic(addrstart, addrend, addrmirror, 0, read_or_write::READWRITE, baseptr); } + + // install ports, banks, RAM (with mirror/mask/flags) + void install_read_port(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, const char *rtag) { install_readwrite_port(addrstart, addrend, addrmirror, flags, rtag, ""); } + void install_write_port(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, const char *wtag) { install_readwrite_port(addrstart, addrend, addrmirror, flags, "", wtag); } + virtual void install_readwrite_port(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, std::string rtag, std::string wtag) = 0; + void install_read_bank(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, memory_bank *bank) { install_bank_generic(addrstart, addrend, addrmirror, flags, bank, nullptr); } + void install_write_bank(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, memory_bank *bank) { install_bank_generic(addrstart, addrend, addrmirror, flags, nullptr, bank); } + void install_readwrite_bank(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, memory_bank *bank) { install_bank_generic(addrstart, addrend, addrmirror, flags, bank, bank); } + void install_rom(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, void *baseptr) { install_ram_generic(addrstart, addrend, addrmirror, flags, read_or_write::READ, baseptr); } + void install_writeonly(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, void *baseptr) { install_ram_generic(addrstart, addrend, addrmirror, flags, read_or_write::WRITE, baseptr); } + void install_ram(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, void *baseptr) { install_ram_generic(addrstart, addrend, addrmirror, flags, read_or_write::READWRITE, baseptr); } + + // install device memory maps + template <typename T> void install_device(offs_t addrstart, offs_t addrend, T &device, void (T::*map)(address_map &map), u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { + address_map_constructor delegate(map, "dynamic_device_install", &device); + install_device_delegate(addrstart, addrend, device, delegate, unitmask, cswidth, flags); + } + + virtual void install_device_delegate(offs_t addrstart, offs_t addrend, device_t &device, address_map_constructor &map, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + + // install taps without mirroring + memory_passthrough_handler install_read_tap(offs_t addrstart, offs_t addrend, std::string name, std::function<void (offs_t offset, u8 &data, u8 mem_mask)> tap, memory_passthrough_handler *mph = nullptr) { return install_read_tap(addrstart, addrend, 0, name, tap, mph); } + memory_passthrough_handler install_read_tap(offs_t addrstart, offs_t addrend, std::string name, std::function<void (offs_t offset, u16 &data, u16 mem_mask)> tap, memory_passthrough_handler *mph = nullptr) { return install_read_tap(addrstart, addrend, 0, name, tap, mph); } + memory_passthrough_handler install_read_tap(offs_t addrstart, offs_t addrend, std::string name, std::function<void (offs_t offset, u32 &data, u32 mem_mask)> tap, memory_passthrough_handler *mph = nullptr) { return install_read_tap(addrstart, addrend, 0, name, tap, mph); } + memory_passthrough_handler install_read_tap(offs_t addrstart, offs_t addrend, std::string name, std::function<void (offs_t offset, u64 &data, u64 mem_mask)> tap, memory_passthrough_handler *mph = nullptr) { return install_read_tap(addrstart, addrend, 0, name, tap, mph); } + memory_passthrough_handler install_write_tap(offs_t addrstart, offs_t addrend, std::string name, std::function<void (offs_t offset, u8 &data, u8 mem_mask)> tap, memory_passthrough_handler *mph = nullptr) { return install_write_tap(addrstart, addrend, 0, name, tap, mph); } + memory_passthrough_handler install_write_tap(offs_t addrstart, offs_t addrend, std::string name, std::function<void (offs_t offset, u16 &data, u16 mem_mask)> tap, memory_passthrough_handler *mph = nullptr) { return install_write_tap(addrstart, addrend, 0, name, tap, mph); } + memory_passthrough_handler install_write_tap(offs_t addrstart, offs_t addrend, std::string name, std::function<void (offs_t offset, u32 &data, u32 mem_mask)> tap, memory_passthrough_handler *mph = nullptr) { return install_write_tap(addrstart, addrend, 0, name, tap, mph); } + memory_passthrough_handler install_write_tap(offs_t addrstart, offs_t addrend, std::string name, std::function<void (offs_t offset, u64 &data, u64 mem_mask)> tap, memory_passthrough_handler *mph = nullptr) { return install_write_tap(addrstart, addrend, 0, name, tap, mph); } + memory_passthrough_handler install_readwrite_tap(offs_t addrstart, offs_t addrend, std::string name, std::function<void (offs_t offset, u8 &data, u8 mem_mask)> tapr, std::function<void (offs_t offset, u8 &data, u8 mem_mask)> tapw, memory_passthrough_handler *mph = nullptr) { return install_readwrite_tap(addrstart, addrend, 0, name, tapr, tapw, mph); } + memory_passthrough_handler install_readwrite_tap(offs_t addrstart, offs_t addrend, std::string name, std::function<void (offs_t offset, u16 &data, u16 mem_mask)> tapr, std::function<void (offs_t offset, u16 &data, u16 mem_mask)> tapw, memory_passthrough_handler *mph = nullptr) { return install_readwrite_tap(addrstart, addrend, 0, name, tapr, tapw, mph); } + memory_passthrough_handler install_readwrite_tap(offs_t addrstart, offs_t addrend, std::string name, std::function<void (offs_t offset, u32 &data, u32 mem_mask)> tapr, std::function<void (offs_t offset, u32 &data, u32 mem_mask)> tapw, memory_passthrough_handler *mph = nullptr) { return install_readwrite_tap(addrstart, addrend, 0, name, tapr, tapw, mph); } + memory_passthrough_handler install_readwrite_tap(offs_t addrstart, offs_t addrend, std::string name, std::function<void (offs_t offset, u64 &data, u64 mem_mask)> tapr, std::function<void (offs_t offset, u64 &data, u64 mem_mask)> tapw, memory_passthrough_handler *mph = nullptr) { return install_readwrite_tap(addrstart, addrend, 0, name, tapr, tapw, mph); } + + // install taps with mirroring + virtual memory_passthrough_handler install_read_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u8 &data, u8 mem_mask)> tap, memory_passthrough_handler *mph = nullptr); + virtual memory_passthrough_handler install_read_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u16 &data, u16 mem_mask)> tap, memory_passthrough_handler *mph = nullptr); + virtual memory_passthrough_handler install_read_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u32 &data, u32 mem_mask)> tap, memory_passthrough_handler *mph = nullptr); + virtual memory_passthrough_handler install_read_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u64 &data, u64 mem_mask)> tap, memory_passthrough_handler *mph = nullptr); + virtual memory_passthrough_handler install_write_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u8 &data, u8 mem_mask)> tap, memory_passthrough_handler *mph = nullptr); + virtual memory_passthrough_handler install_write_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u16 &data, u16 mem_mask)> tap, memory_passthrough_handler *mph = nullptr); + virtual memory_passthrough_handler install_write_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u32 &data, u32 mem_mask)> tap, memory_passthrough_handler *mph = nullptr); + virtual memory_passthrough_handler install_write_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u64 &data, u64 mem_mask)> tap, memory_passthrough_handler *mph = nullptr); + virtual memory_passthrough_handler install_readwrite_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u8 &data, u8 mem_mask)> tapr, std::function<void (offs_t offset, u8 &data, u8 mem_mask)> tapw, memory_passthrough_handler *mph = nullptr); + virtual memory_passthrough_handler install_readwrite_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u16 &data, u16 mem_mask)> tapr, std::function<void (offs_t offset, u16 &data, u16 mem_mask)> tapw, memory_passthrough_handler *mph = nullptr); + virtual memory_passthrough_handler install_readwrite_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u32 &data, u32 mem_mask)> tapr, std::function<void (offs_t offset, u32 &data, u32 mem_mask)> tapw, memory_passthrough_handler *mph = nullptr); + virtual memory_passthrough_handler install_readwrite_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u64 &data, u64 mem_mask)> tapr, std::function<void (offs_t offset, u64 &data, u64 mem_mask)> tapw, memory_passthrough_handler *mph = nullptr); + + // install views + void install_view(offs_t addrstart, offs_t addrend, memory_view &view) { install_view(addrstart, addrend, 0, view); } + virtual void install_view(offs_t addrstart, offs_t addrend, offs_t addrmirror, memory_view &view) = 0; + + // install wait state handlers + void install_read_before_time(offs_t addrstart, offs_t addrend, ws_time_delegate ws) { install_read_before_time(addrstart, addrend, 0, ws); } + virtual void install_read_before_time(offs_t addrstart, offs_t addrend, offs_t addrmirror, ws_time_delegate ws) = 0; + void install_read_before_delay(offs_t addrstart, offs_t addrend, ws_delay_delegate ws) { install_read_before_delay(addrstart, addrend, 0, ws); } + virtual void install_read_before_delay(offs_t addrstart, offs_t addrend, offs_t addrmirror, ws_delay_delegate ws) = 0; + void install_read_after_delay(offs_t addrstart, offs_t addrend, ws_delay_delegate ws) { install_read_after_delay(addrstart, addrend, 0, ws); } + virtual void install_read_after_delay(offs_t addrstart, offs_t addrend, offs_t addrmirror, ws_delay_delegate ws) = 0; + + void install_write_before_time(offs_t addrstart, offs_t addrend, ws_time_delegate ws) { install_write_before_time(addrstart, addrend, 0, ws); } + virtual void install_write_before_time(offs_t addrstart, offs_t addrend, offs_t addrmirror, ws_time_delegate ws) = 0; + void install_write_before_delay(offs_t addrstart, offs_t addrend, ws_delay_delegate ws) { install_write_before_delay(addrstart, addrend, 0, ws); } + virtual void install_write_before_delay(offs_t addrstart, offs_t addrend, offs_t addrmirror, ws_delay_delegate ws) = 0; + void install_write_after_delay(offs_t addrstart, offs_t addrend, ws_delay_delegate ws) { install_write_after_delay(addrstart, addrend, 0, ws); } + virtual void install_write_after_delay(offs_t addrstart, offs_t addrend, offs_t addrmirror, ws_delay_delegate ws) = 0; + + void install_readwrite_before_time(offs_t addrstart, offs_t addrend, ws_time_delegate ws) { install_readwrite_before_time(addrstart, addrend, 0, ws); } + virtual void install_readwrite_before_time(offs_t addrstart, offs_t addrend, offs_t addrmirror, ws_time_delegate ws) = 0; + void install_readwrite_before_delay(offs_t addrstart, offs_t addrend, ws_delay_delegate ws) { install_readwrite_before_delay(addrstart, addrend, 0, ws); } + virtual void install_readwrite_before_delay(offs_t addrstart, offs_t addrend, offs_t addrmirror, ws_delay_delegate ws) = 0; + void install_readwrite_after_delay(offs_t addrstart, offs_t addrend, ws_delay_delegate ws) { install_readwrite_after_delay(addrstart, addrend, 0, ws); } + virtual void install_readwrite_after_delay(offs_t addrstart, offs_t addrend, offs_t addrmirror, ws_delay_delegate ws) = 0; + + // install new-style delegate handlers (short form) + template <typename R> + void install_read_handler(offs_t addrstart, offs_t addrend, R &&rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) + { install_read_handler(addrstart, addrend, 0, 0, 0, std::forward<R>(rhandler), unitmask, cswidth, flags); } + template <typename W> + void install_write_handler(offs_t addrstart, offs_t addrend, W &&whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) + { install_write_handler(addrstart, addrend, 0, 0, 0, std::forward<W>(whandler), unitmask, cswidth, flags); } + template <typename R, typename W> + void install_readwrite_handler(offs_t addrstart, offs_t addrend, R &&rhandler, W &&whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) + { install_readwrite_handler(addrstart, addrend, 0, 0, 0, std::forward<R>(rhandler), std::forward<W>(whandler), unitmask, cswidth, flags); } + + // install new-style delegate handlers (with mirror/mask) + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8m_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8m_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16m_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16m_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32m_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32m_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64m_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64m_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8s_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8s_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16s_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16s_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32s_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32s_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64s_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64s_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8sm_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16sm_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32sm_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64sm_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8mo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16mo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32mo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64mo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8smo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16smo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32smo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64smo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + + template <typename R, typename W> + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, R &&rhandler, W &&whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) + { + static_assert(emu::detail::handler_width_v<std::remove_reference_t<R> > == emu::detail::handler_width_v<std::remove_reference_t<W> >, "handler widths do not match"); + install_read_handler(addrstart, addrend, addrmask, addrmirror, addrselect, std::forward<R>(rhandler), unitmask, cswidth, flags); + install_write_handler(addrstart, addrend, addrmask, addrmirror, addrselect, std::forward<W>(whandler), unitmask, cswidth, flags); + } + +protected: + virtual void unmap_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, read_or_write readorwrite, bool quiet) = 0; + virtual void install_ram_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, read_or_write readorwrite, void *baseptr) = 0; + virtual void install_bank_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, memory_bank *rbank, memory_bank *wbank) = 0; + + void populate_map_entry(const address_map_entry &entry, read_or_write readorwrite); + void adjust_addresses(offs_t &start, offs_t &end, offs_t &mask, offs_t &mirror) { + // adjust start/end/mask values + mask &= m_addrmask; + start &= ~mirror & m_addrmask; + end &= ~mirror & m_addrmask; + } + + void check_optimize_all(const char *function, int width, offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, offs_t &nstart, offs_t &nend, offs_t &nmask, offs_t &nmirror, u64 &nunitmask, int &ncswidth); + void check_optimize_mirror(const char *function, offs_t addrstart, offs_t addrend, offs_t addrmirror, offs_t &nstart, offs_t &nend, offs_t &nmask, offs_t &nmirror); + void check_address(const char *function, offs_t addrstart, offs_t addrend); + + address_space_installer(const address_space_config &config, memory_manager &manager) : + m_config(config), + m_manager(manager), + m_addrmask(make_bitmask<offs_t>(m_config.addr_width())), + m_logaddrmask(make_bitmask<offs_t>(m_config.logaddr_width())), + m_addrchars((m_config.addr_width() + 3) / 4), + m_logaddrchars((m_config.logaddr_width() + 3) / 4) + {} + + const address_space_config &m_config; // configuration of this space + memory_manager & m_manager; // reference to the owning manager + offs_t m_addrmask; // physical address mask + offs_t m_logaddrmask; // logical address mask + u8 m_addrchars; // number of characters to use for physical addresses + u8 m_logaddrchars; // number of characters to use for logical addresses +}; + // address_space holds live information about an address space -class address_space +class address_space : public address_space_installer { friend class memory_bank; friend class memory_block; - template<int Width, int AddrShift, int Endian> friend class handler_entry_read_unmapped; - template<int Width, int AddrShift, int Endian> friend class handler_entry_write_unmapped; - template<int Width, int AddrShift, int Endian> friend class memory_access_cache; - - struct notifier_t { - std::function<void (read_or_write)> m_notifier; - int m_id; - }; + template<int Width, int AddrShift> friend class handler_entry_read_unmapped; + template<int Width, int AddrShift> friend class handler_entry_write_unmapped; protected: // construction/destruction address_space(memory_manager &manager, device_memory_interface &memory, int spacenum); public: + struct specific_access_info + { + struct side + { + void const *const *dispatch; + uintptr_t function; + ptrdiff_t displacement; + bool is_virtual; + }; + + unsigned native_bytes; + unsigned native_mask_bits; + unsigned address_width; + unsigned low_bits; + side read; + side write; + }; + virtual ~address_space(); // getters @@ -1204,27 +2361,40 @@ public: int spacenum() const { return m_spacenum; } address_map *map() const { return m_map.get(); } - template<int Width, int AddrShift, int Endian> memory_access_cache<Width, AddrShift, Endian> *cache() { + template<int Width, int AddrShift, endianness_t Endian> void cache(emu::detail::memory_access_cache<Width, AddrShift, Endian> &v) { if(AddrShift != m_config.addr_shift()) fatalerror("Requesting cache() with address shift %d while the config says %d\n", AddrShift, m_config.addr_shift()); if(8 << Width != m_config.data_width()) fatalerror("Requesting cache() with data width %d while the config says %d\n", 8 << Width, m_config.data_width()); if(Endian != m_config.endianness()) fatalerror("Requesting cache() with endianness %s while the config says %s\n", - endianness_names[Endian], endianness_names[m_config.endianness()]); + util::endian_to_string_view(Endian), util::endian_to_string_view(m_config.endianness())); + + v.set(this, get_cache_info()); + } + + template<int Level, int Width, int AddrShift, endianness_t Endian> void specific(emu::detail::memory_access_specific<Level, Width, AddrShift, Endian> &v) { + if(Level != emu::detail::handler_entry_dispatch_level(m_config.addr_width())) + fatalerror("Requesting specific() with wrong level, bad address width (the config says %d)\n", m_config.addr_width()); + if(AddrShift != m_config.addr_shift()) + fatalerror("Requesting specific() with address shift %d while the config says %d\n", AddrShift, m_config.addr_shift()); + if(8 << Width != m_config.data_width()) + fatalerror("Requesting specific() with data width %d while the config says %d\n", 8 << Width, m_config.data_width()); + if(Endian != m_config.endianness()) + fatalerror("Requesting spefific() with endianness %s while the config says %s\n", + util::endian_to_string_view(Endian), util::endian_to_string_view(m_config.endianness())); - return static_cast<memory_access_cache<Width, AddrShift, Endian> *>(create_cache()); + v.set(this, get_specific_info()); } - int add_change_notifier(std::function<void (read_or_write)> n); - void remove_change_notifier(int id); + util::notifier_subscription add_change_notifier(delegate<void (read_or_write)> &&n); + template <typename T> util::notifier_subscription add_change_notifier(T &&n) { return add_change_notifier(delegate<void (read_or_write)>(std::forward<T>(n))); } void invalidate_caches(read_or_write mode) { if(u32(mode) & ~m_in_notification) { u32 old = m_in_notification; m_in_notification |= u32(mode); - for(const auto &n : m_notifiers) - n.m_notifier(mode); + m_notifiers(mode); m_in_notification = old; } } @@ -1233,19 +2403,11 @@ public: virtual void remove_passthrough(std::unordered_set<handler_entry *> &handlers) = 0; - int data_width() const { return m_config.data_width(); } - int addr_width() const { return m_config.addr_width(); } - int logaddr_width() const { return m_config.logaddr_width(); } - int alignment() const { return m_config.alignment(); } - endianness_t endianness() const { return m_config.endianness(); } - int addr_shift() const { return m_config.addr_shift(); } u64 unmap() const { return m_unmap; } - bool is_octal() const { return m_config.is_octal(); } + void unmap_value_high() { m_unmap = ~0; } - offs_t addrmask() const { return m_addrmask; } - u8 addrchars() const { return m_addrchars; } - offs_t logaddrmask() const { return m_logaddrmask; } - u8 logaddrchars() const { return m_logaddrchars; } + std::shared_ptr<emu::detail::memory_passthrough_handler_impl> make_mph(memory_passthrough_handler *mph); + std::shared_ptr<emu::detail::memory_passthrough_handler_impl> get_default_mpl() { return m_default_mpl; } // debug helpers virtual std::string get_handler_string(read_or_write readorwrite, offs_t byteaddress) const = 0; @@ -1254,12 +2416,13 @@ public: void set_log_unmap(bool log) { m_log_unmap = log; } // general accessors - virtual void accessors(data_accessors &accessors) const = 0; + virtual specific_access_info specific_accessors() const = 0; virtual void *get_read_ptr(offs_t address) const = 0; virtual void *get_write_ptr(offs_t address) const = 0; // read accessors virtual u8 read_byte(offs_t address) = 0; + virtual u8 read_byte(offs_t address, u8 mask) = 0; virtual u16 read_word(offs_t address) = 0; virtual u16 read_word(offs_t address, u16 mask) = 0; virtual u16 read_word_unaligned(offs_t address) = 0; @@ -1275,6 +2438,7 @@ public: // write accessors virtual void write_byte(offs_t address, u8 data) = 0; + virtual void write_byte(offs_t address, u8 data, u8 mask) = 0; virtual void write_word(offs_t address, u16 data) = 0; virtual void write_word(offs_t address, u16 data, u16 mask) = 0; virtual void write_word_unaligned(offs_t address, u16 data) = 0; @@ -1288,282 +2452,33 @@ public: virtual void write_qword_unaligned(offs_t address, u64 data) = 0; virtual void write_qword_unaligned(offs_t address, u64 data, u64 mask) = 0; - // address-to-byte conversion helpers - offs_t address_to_byte(offs_t address) const { return m_config.addr2byte(address); } - offs_t address_to_byte_end(offs_t address) const { return m_config.addr2byte_end(address); } - offs_t byte_to_address(offs_t address) const { return m_config.byte2addr(address); } - offs_t byte_to_address_end(offs_t address) const { return m_config.byte2addr_end(address); } - - // umap ranges (short form) - void unmap_read(offs_t addrstart, offs_t addrend, offs_t addrmirror = 0) { unmap_generic(addrstart, addrend, addrmirror, read_or_write::READ, false); } - void unmap_write(offs_t addrstart, offs_t addrend, offs_t addrmirror = 0) { unmap_generic(addrstart, addrend, addrmirror, read_or_write::WRITE, false); } - void unmap_readwrite(offs_t addrstart, offs_t addrend, offs_t addrmirror = 0) { unmap_generic(addrstart, addrend, addrmirror, read_or_write::READWRITE, false); } - void nop_read(offs_t addrstart, offs_t addrend, offs_t addrmirror = 0) { unmap_generic(addrstart, addrend, addrmirror, read_or_write::READ, true); } - void nop_write(offs_t addrstart, offs_t addrend, offs_t addrmirror = 0) { unmap_generic(addrstart, addrend, addrmirror, read_or_write::WRITE, true); } - void nop_readwrite(offs_t addrstart, offs_t addrend, offs_t addrmirror = 0) { unmap_generic(addrstart, addrend, addrmirror, read_or_write::READWRITE, true); } - - // install ports, banks, RAM (short form) - void install_read_port(offs_t addrstart, offs_t addrend, const char *rtag) { install_read_port(addrstart, addrend, 0, rtag); } - void install_write_port(offs_t addrstart, offs_t addrend, const char *wtag) { install_write_port(addrstart, addrend, 0, wtag); } - void install_readwrite_port(offs_t addrstart, offs_t addrend, const char *rtag, const char *wtag) { install_readwrite_port(addrstart, addrend, 0, rtag, wtag); } - void install_read_bank(offs_t addrstart, offs_t addrend, const char *tag) { install_read_bank(addrstart, addrend, 0, tag); } - void install_write_bank(offs_t addrstart, offs_t addrend, const char *tag) { install_write_bank(addrstart, addrend, 0, tag); } - void install_readwrite_bank(offs_t addrstart, offs_t addrend, const char *tag) { install_readwrite_bank(addrstart, addrend, 0, tag); } - void install_read_bank(offs_t addrstart, offs_t addrend, memory_bank *bank) { install_read_bank(addrstart, addrend, 0, bank); } - void install_write_bank(offs_t addrstart, offs_t addrend, memory_bank *bank) { install_write_bank(addrstart, addrend, 0, bank); } - void install_readwrite_bank(offs_t addrstart, offs_t addrend, memory_bank *bank) { install_readwrite_bank(addrstart, addrend, 0, bank); } - void install_rom(offs_t addrstart, offs_t addrend, void *baseptr = nullptr) { install_rom(addrstart, addrend, 0, baseptr); } - void install_writeonly(offs_t addrstart, offs_t addrend, void *baseptr = nullptr) { install_writeonly(addrstart, addrend, 0, baseptr); } - void install_ram(offs_t addrstart, offs_t addrend, void *baseptr = nullptr) { install_ram(addrstart, addrend, 0, baseptr); } - - // install ports, banks, RAM (with mirror/mask) - void install_read_port(offs_t addrstart, offs_t addrend, offs_t addrmirror, const char *rtag) { install_readwrite_port(addrstart, addrend, addrmirror, rtag, ""); } - void install_write_port(offs_t addrstart, offs_t addrend, offs_t addrmirror, const char *wtag) { install_readwrite_port(addrstart, addrend, addrmirror, "", wtag); } - virtual void install_readwrite_port(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string rtag, std::string wtag) = 0; - void install_read_bank(offs_t addrstart, offs_t addrend, offs_t addrmirror, const char *tag) { install_bank_generic(addrstart, addrend, addrmirror, tag, ""); } - void install_write_bank(offs_t addrstart, offs_t addrend, offs_t addrmirror, const char *tag) { install_bank_generic(addrstart, addrend, addrmirror, "", tag); } - void install_readwrite_bank(offs_t addrstart, offs_t addrend, offs_t addrmirror, const char *tag) { install_bank_generic(addrstart, addrend, addrmirror, tag, tag); } - void install_read_bank(offs_t addrstart, offs_t addrend, offs_t addrmirror, memory_bank *bank) { install_bank_generic(addrstart, addrend, addrmirror, bank, nullptr); } - void install_write_bank(offs_t addrstart, offs_t addrend, offs_t addrmirror, memory_bank *bank) { install_bank_generic(addrstart, addrend, addrmirror, nullptr, bank); } - void install_readwrite_bank(offs_t addrstart, offs_t addrend, offs_t addrmirror, memory_bank *bank) { install_bank_generic(addrstart, addrend, addrmirror, bank, bank); } - void install_rom(offs_t addrstart, offs_t addrend, offs_t addrmirror, void *baseptr = nullptr) { install_ram_generic(addrstart, addrend, addrmirror, read_or_write::READ, baseptr); } - void install_writeonly(offs_t addrstart, offs_t addrend, offs_t addrmirror, void *baseptr = nullptr) { install_ram_generic(addrstart, addrend, addrmirror, read_or_write::WRITE, baseptr); } - void install_ram(offs_t addrstart, offs_t addrend, offs_t addrmirror, void *baseptr = nullptr) { install_ram_generic(addrstart, addrend, addrmirror, read_or_write::READWRITE, baseptr); } - - // install device memory maps - template <typename T> void install_device(offs_t addrstart, offs_t addrend, T &device, void (T::*map)(address_map &map), u64 unitmask = 0, int cswidth = 0) { - address_map_constructor delegate(map, "dynamic_device_install", &device); - install_device_delegate(addrstart, addrend, device, delegate, unitmask, cswidth); - } - - virtual void install_device_delegate(offs_t addrstart, offs_t addrend, device_t &device, address_map_constructor &map, u64 unitmask = 0, int cswidth = 0) = 0; - - // install taps without mirroring - memory_passthrough_handler *install_read_tap(offs_t addrstart, offs_t addrend, std::string name, std::function<void (offs_t offset, u8 &data, u8 mem_mask)> tap, memory_passthrough_handler *mph = nullptr) { return install_read_tap(addrstart, addrend, 0, name, tap, mph); } - memory_passthrough_handler *install_read_tap(offs_t addrstart, offs_t addrend, std::string name, std::function<void (offs_t offset, u16 &data, u16 mem_mask)> tap, memory_passthrough_handler *mph = nullptr) { return install_read_tap(addrstart, addrend, 0, name, tap, mph); } - memory_passthrough_handler *install_read_tap(offs_t addrstart, offs_t addrend, std::string name, std::function<void (offs_t offset, u32 &data, u32 mem_mask)> tap, memory_passthrough_handler *mph = nullptr) { return install_read_tap(addrstart, addrend, 0, name, tap, mph); } - memory_passthrough_handler *install_read_tap(offs_t addrstart, offs_t addrend, std::string name, std::function<void (offs_t offset, u64 &data, u64 mem_mask)> tap, memory_passthrough_handler *mph = nullptr) { return install_read_tap(addrstart, addrend, 0, name, tap, mph); } - memory_passthrough_handler *install_write_tap(offs_t addrstart, offs_t addrend, std::string name, std::function<void (offs_t offset, u8 &data, u8 mem_mask)> tap, memory_passthrough_handler *mph = nullptr) { return install_write_tap(addrstart, addrend, 0, name, tap, mph); } - memory_passthrough_handler *install_write_tap(offs_t addrstart, offs_t addrend, std::string name, std::function<void (offs_t offset, u16 &data, u16 mem_mask)> tap, memory_passthrough_handler *mph = nullptr) { return install_write_tap(addrstart, addrend, 0, name, tap, mph); } - memory_passthrough_handler *install_write_tap(offs_t addrstart, offs_t addrend, std::string name, std::function<void (offs_t offset, u32 &data, u32 mem_mask)> tap, memory_passthrough_handler *mph = nullptr) { return install_write_tap(addrstart, addrend, 0, name, tap, mph); } - memory_passthrough_handler *install_write_tap(offs_t addrstart, offs_t addrend, std::string name, std::function<void (offs_t offset, u64 &data, u64 mem_mask)> tap, memory_passthrough_handler *mph = nullptr) { return install_write_tap(addrstart, addrend, 0, name, tap, mph); } - memory_passthrough_handler *install_readwrite_tap(offs_t addrstart, offs_t addrend, std::string name, std::function<void (offs_t offset, u8 &data, u8 mem_mask)> tapr, std::function<void (offs_t offset, u8 &data, u8 mem_mask)> tapw, memory_passthrough_handler *mph = nullptr) { return install_readwrite_tap(addrstart, addrend, 0, name, tapr, tapw, mph); } - memory_passthrough_handler *install_readwrite_tap(offs_t addrstart, offs_t addrend, std::string name, std::function<void (offs_t offset, u16 &data, u16 mem_mask)> tapr, std::function<void (offs_t offset, u16 &data, u16 mem_mask)> tapw, memory_passthrough_handler *mph = nullptr) { return install_readwrite_tap(addrstart, addrend, 0, name, tapr, tapw, mph); } - memory_passthrough_handler *install_readwrite_tap(offs_t addrstart, offs_t addrend, std::string name, std::function<void (offs_t offset, u32 &data, u32 mem_mask)> tapr, std::function<void (offs_t offset, u32 &data, u32 mem_mask)> tapw, memory_passthrough_handler *mph = nullptr) { return install_readwrite_tap(addrstart, addrend, 0, name, tapr, tapw, mph); } - memory_passthrough_handler *install_readwrite_tap(offs_t addrstart, offs_t addrend, std::string name, std::function<void (offs_t offset, u64 &data, u64 mem_mask)> tapr, std::function<void (offs_t offset, u64 &data, u64 mem_mask)> tapw, memory_passthrough_handler *mph = nullptr) { return install_readwrite_tap(addrstart, addrend, 0, name, tapr, tapw, mph); } - - // install taps with mirroring - virtual memory_passthrough_handler *install_read_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u8 &data, u8 mem_mask)> tap, memory_passthrough_handler *mph = nullptr); - virtual memory_passthrough_handler *install_read_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u16 &data, u16 mem_mask)> tap, memory_passthrough_handler *mph = nullptr); - virtual memory_passthrough_handler *install_read_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u32 &data, u32 mem_mask)> tap, memory_passthrough_handler *mph = nullptr); - virtual memory_passthrough_handler *install_read_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u64 &data, u64 mem_mask)> tap, memory_passthrough_handler *mph = nullptr); - virtual memory_passthrough_handler *install_write_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u8 &data, u8 mem_mask)> tap, memory_passthrough_handler *mph = nullptr); - virtual memory_passthrough_handler *install_write_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u16 &data, u16 mem_mask)> tap, memory_passthrough_handler *mph = nullptr); - virtual memory_passthrough_handler *install_write_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u32 &data, u32 mem_mask)> tap, memory_passthrough_handler *mph = nullptr); - virtual memory_passthrough_handler *install_write_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u64 &data, u64 mem_mask)> tap, memory_passthrough_handler *mph = nullptr); - virtual memory_passthrough_handler *install_readwrite_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u8 &data, u8 mem_mask)> tapr, std::function<void (offs_t offset, u8 &data, u8 mem_mask)> tapw, memory_passthrough_handler *mph = nullptr); - virtual memory_passthrough_handler *install_readwrite_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u16 &data, u16 mem_mask)> tapr, std::function<void (offs_t offset, u16 &data, u16 mem_mask)> tapw, memory_passthrough_handler *mph = nullptr); - virtual memory_passthrough_handler *install_readwrite_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u32 &data, u32 mem_mask)> tapr, std::function<void (offs_t offset, u32 &data, u32 mem_mask)> tapw, memory_passthrough_handler *mph = nullptr); - virtual memory_passthrough_handler *install_readwrite_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, u64 &data, u64 mem_mask)> tapr, std::function<void (offs_t offset, u64 &data, u64 mem_mask)> tapw, memory_passthrough_handler *mph = nullptr); - - - // install new-style delegate handlers (short form) - void install_read_handler(offs_t addrstart, offs_t addrend, read8_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } - void install_write_handler(offs_t addrstart, offs_t addrend, write8_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read8_delegate rhandler, write8_delegate whandler, u64 unitmask = 0, int cswidth = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } - void install_read_handler(offs_t addrstart, offs_t addrend, read16_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } - void install_write_handler(offs_t addrstart, offs_t addrend, write16_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read16_delegate rhandler, write16_delegate whandler, u64 unitmask = 0, int cswidth = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } - void install_read_handler(offs_t addrstart, offs_t addrend, read32_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } - void install_write_handler(offs_t addrstart, offs_t addrend, write32_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read32_delegate rhandler, write32_delegate whandler, u64 unitmask = 0, int cswidth = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } - void install_read_handler(offs_t addrstart, offs_t addrend, read64_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } - void install_write_handler(offs_t addrstart, offs_t addrend, write64_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read64_delegate rhandler, write64_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } - - void install_read_handler(offs_t addrstart, offs_t addrend, read8m_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } - void install_write_handler(offs_t addrstart, offs_t addrend, write8m_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read8m_delegate rhandler, write8m_delegate whandler, u64 unitmask = 0, int cswidth = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } - void install_read_handler(offs_t addrstart, offs_t addrend, read16m_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } - void install_write_handler(offs_t addrstart, offs_t addrend, write16m_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read16m_delegate rhandler, write16m_delegate whandler, u64 unitmask = 0, int cswidth = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } - void install_read_handler(offs_t addrstart, offs_t addrend, read32m_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } - void install_write_handler(offs_t addrstart, offs_t addrend, write32m_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read32m_delegate rhandler, write32m_delegate whandler, u64 unitmask = 0, int cswidth = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } - void install_read_handler(offs_t addrstart, offs_t addrend, read64m_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } - void install_write_handler(offs_t addrstart, offs_t addrend, write64m_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read64m_delegate rhandler, write64m_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } - - void install_read_handler(offs_t addrstart, offs_t addrend, read8s_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } - void install_write_handler(offs_t addrstart, offs_t addrend, write8s_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read8s_delegate rhandler, write8s_delegate whandler, u64 unitmask = 0, int cswidth = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } - void install_read_handler(offs_t addrstart, offs_t addrend, read16s_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } - void install_write_handler(offs_t addrstart, offs_t addrend, write16s_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read16s_delegate rhandler, write16s_delegate whandler, u64 unitmask = 0, int cswidth = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } - void install_read_handler(offs_t addrstart, offs_t addrend, read32s_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } - void install_write_handler(offs_t addrstart, offs_t addrend, write32s_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read32s_delegate rhandler, write32s_delegate whandler, u64 unitmask = 0, int cswidth = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } - void install_read_handler(offs_t addrstart, offs_t addrend, read64s_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } - void install_write_handler(offs_t addrstart, offs_t addrend, write64s_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read64s_delegate rhandler, write64s_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } - - void install_read_handler(offs_t addrstart, offs_t addrend, read8sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } - void install_write_handler(offs_t addrstart, offs_t addrend, write8sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read8sm_delegate rhandler, write8sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } - void install_read_handler(offs_t addrstart, offs_t addrend, read16sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } - void install_write_handler(offs_t addrstart, offs_t addrend, write16sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read16sm_delegate rhandler, write16sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } - void install_read_handler(offs_t addrstart, offs_t addrend, read32sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } - void install_write_handler(offs_t addrstart, offs_t addrend, write32sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read32sm_delegate rhandler, write32sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } - void install_read_handler(offs_t addrstart, offs_t addrend, read64sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } - void install_write_handler(offs_t addrstart, offs_t addrend, write64sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read64sm_delegate rhandler, write64sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } - - void install_read_handler(offs_t addrstart, offs_t addrend, read8mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } - void install_write_handler(offs_t addrstart, offs_t addrend, write8mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read8mo_delegate rhandler, write8mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } - void install_read_handler(offs_t addrstart, offs_t addrend, read16mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } - void install_write_handler(offs_t addrstart, offs_t addrend, write16mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read16mo_delegate rhandler, write16mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } - void install_read_handler(offs_t addrstart, offs_t addrend, read32mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } - void install_write_handler(offs_t addrstart, offs_t addrend, write32mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read32mo_delegate rhandler, write32mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } - void install_read_handler(offs_t addrstart, offs_t addrend, read64mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } - void install_write_handler(offs_t addrstart, offs_t addrend, write64mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read64mo_delegate rhandler, write64mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } - - void install_read_handler(offs_t addrstart, offs_t addrend, read8smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } - void install_write_handler(offs_t addrstart, offs_t addrend, write8smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read8smo_delegate rhandler, write8smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } - void install_read_handler(offs_t addrstart, offs_t addrend, read16smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } - void install_write_handler(offs_t addrstart, offs_t addrend, write16smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read16smo_delegate rhandler, write16smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } - void install_read_handler(offs_t addrstart, offs_t addrend, read32smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } - void install_write_handler(offs_t addrstart, offs_t addrend, write32smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read32smo_delegate rhandler, write32smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } - void install_read_handler(offs_t addrstart, offs_t addrend, read64smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } - void install_write_handler(offs_t addrstart, offs_t addrend, write64smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read64smo_delegate rhandler, write64smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } - - // install new-style delegate handlers (with mirror/mask) - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8_delegate rhandler, write8_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16_delegate rhandler, write16_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32_delegate rhandler, write32_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64_delegate rhandler, write64_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8m_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8m_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8m_delegate rhandler, write8m_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16m_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16m_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16m_delegate rhandler, write16m_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32m_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32m_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32m_delegate rhandler, write32m_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64m_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64m_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64m_delegate rhandler, write64m_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8s_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8s_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8s_delegate rhandler, write8s_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16s_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16s_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16s_delegate rhandler, write16s_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32s_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32s_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32s_delegate rhandler, write32s_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64s_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64s_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64s_delegate rhandler, write64s_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8sm_delegate rhandler, write8sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16sm_delegate rhandler, write16sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32sm_delegate rhandler, write32sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64sm_delegate rhandler, write64sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8mo_delegate rhandler, write8mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16mo_delegate rhandler, write16mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32mo_delegate rhandler, write32mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64mo_delegate rhandler, write64mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8smo_delegate rhandler, write8smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16smo_delegate rhandler, write16smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32smo_delegate rhandler, write32smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64smo_delegate rhandler, write64smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - // setup void prepare_map(); + void prepare_device_map(address_map &map) ATTR_COLD; void populate_from_map(address_map *map = nullptr); - void allocate_memory(); - void locate_memory(); - template<int Width, int AddrShift, int Endian> handler_entry_read_unmapped <Width, AddrShift, Endian> *get_unmap_r() const { return static_cast<handler_entry_read_unmapped <Width, AddrShift, Endian> *>(m_unmap_r); } - template<int Width, int AddrShift, int Endian> handler_entry_write_unmapped<Width, AddrShift, Endian> *get_unmap_w() const { return static_cast<handler_entry_write_unmapped<Width, AddrShift, Endian> *>(m_unmap_w); } + template<int Width, int AddrShift> handler_entry_read_unmapped <Width, AddrShift> *get_unmap_r() const { return static_cast<handler_entry_read_unmapped <Width, AddrShift> *>(m_unmap_r); } + template<int Width, int AddrShift> handler_entry_write_unmapped<Width, AddrShift> *get_unmap_w() const { return static_cast<handler_entry_write_unmapped<Width, AddrShift> *>(m_unmap_w); } + + handler_entry *unmap_r() const { return m_unmap_r; } + handler_entry *unmap_w() const { return m_unmap_w; } + handler_entry *nop_r() const { return m_nop_r; } + handler_entry *nop_w() const { return m_nop_w; } protected: // internal helpers - virtual void *create_cache() = 0; + virtual std::pair<void *, void *> get_cache_info() = 0; + virtual std::pair<const void *, const void *> get_specific_info() = 0; - void populate_map_entry(const address_map_entry &entry, read_or_write readorwrite); - virtual void unmap_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, read_or_write readorwrite, bool quiet) = 0; - virtual void install_ram_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, read_or_write readorwrite, void *baseptr) = 0; - virtual void install_bank_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string rtag, std::string wtag) = 0; - virtual void install_bank_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, memory_bank *rbank, memory_bank *wbank) = 0; - void adjust_addresses(offs_t &start, offs_t &end, offs_t &mask, offs_t &mirror); - void *find_backing_memory(offs_t addrstart, offs_t addrend); - bool needs_backing_store(const address_map_entry &entry); - memory_bank &bank_find_or_allocate(const char *tag, offs_t addrstart, offs_t addrend, offs_t addrmirror, read_or_write readorwrite); - address_map_entry *block_assign_intersecting(offs_t bytestart, offs_t byteend, u8 *base); - void check_optimize_all(const char *function, int width, offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, offs_t &nstart, offs_t &nend, offs_t &nmask, offs_t &nmirror, u64 &nunitmask, int &ncswidth); - void check_optimize_mirror(const char *function, offs_t addrstart, offs_t addrend, offs_t addrmirror, offs_t &nstart, offs_t &nend, offs_t &nmask, offs_t &nmirror); - void check_address(const char *function, offs_t addrstart, offs_t addrend); + void prepare_map_generic(address_map &map, bool allow_alloc) ATTR_COLD; // private state - const address_space_config &m_config; // configuration of this space device_t & m_device; // reference to the owning device std::unique_ptr<address_map> m_map; // original memory map - offs_t m_addrmask; // physical address mask - offs_t m_logaddrmask; // logical address mask u64 m_unmap; // unmapped value int m_spacenum; // address space index bool m_log_unmap; // log unmapped accesses in this space? const char * m_name; // friendly name of the address space - u8 m_addrchars; // number of characters to use for physical addresses - u8 m_logaddrchars; // number of characters to use for logical addresses handler_entry *m_unmap_r; handler_entry *m_unmap_w; @@ -1571,46 +2486,13 @@ protected: handler_entry *m_nop_r; handler_entry *m_nop_w; - std::vector<std::unique_ptr<memory_passthrough_handler>> m_mphs; + std::vector<std::shared_ptr<emu::detail::memory_passthrough_handler_impl>> m_mphs; - std::vector<notifier_t> m_notifiers; // notifier list for address map change - int m_notifier_id; // next notifier id + util::notifier<read_or_write> m_notifiers; // notifier list for address map change u32 m_in_notification; // notification(s) currently being done - memory_manager & m_manager; // reference to the owning manager -}; - - -// ======================> memory_block - -// a memory block is a chunk of RAM associated with a range of memory in a device's address space -class memory_block -{ - DISABLE_COPYING(memory_block); - -public: - // construction/destruction - memory_block(address_space &space, offs_t start, offs_t end, void *memory = nullptr); - ~memory_block(); - - // getters - running_machine &machine() const { return m_machine; } - offs_t addrstart() const { return m_addrstart; } - offs_t addrend() const { return m_addrend; } - u8 *data() const { return m_data; } - // is the given range contained by this memory block? - bool contains(address_space &space, offs_t addrstart, offs_t addrend) const - { - return (&space == &m_space && m_addrstart <= addrstart && m_addrend >= addrend); - } - -private: - // internal state - running_machine & m_machine; // need the machine to free our memory - address_space & m_space; // which address space are we associated with? - offs_t m_addrstart, m_addrend; // start/end for verifying a match - u8 * m_data; // pointer to the data for this block - std::vector<u8> m_allocated; // pointer to the actually allocated block + // passthrough handler used for wait states + std::shared_ptr<emu::detail::memory_passthrough_handler_impl> m_default_mpl; }; @@ -1619,54 +2501,17 @@ private: // a memory bank is a global pointer to memory that can be shared across devices and changed dynamically class memory_bank { - // a bank reference is an entry in a list of address spaces that reference a given bank - class bank_reference - { - public: - // construction/destruction - bank_reference(address_space &space, read_or_write readorwrite) - : m_space(space), - m_readorwrite(readorwrite) { } - - // getters - address_space &space() const { return m_space; } - - // does this reference match the space+read/write combination? - bool matches(const address_space &space, read_or_write readorwrite) const - { - return (&space == &m_space && (readorwrite == read_or_write::READWRITE || readorwrite == m_readorwrite)); - } - - private: - // internal state - address_space & m_space; // address space that references us - read_or_write m_readorwrite; // used for read or write? - - }; - public: // construction/destruction - memory_bank(address_space &space, int index, offs_t start, offs_t end, const char *tag = nullptr); + memory_bank(device_t &device, std::string tag); ~memory_bank(); // getters running_machine &machine() const { return m_machine; } int entry() const { return m_curentry; } - bool anonymous() const { return m_anonymous; } - offs_t addrstart() const { return m_addrstart; } void *base() const { return m_entries.empty() ? nullptr : m_entries[m_curentry]; } - const char *tag() const { return m_tag.c_str(); } - const char *name() const { return m_name.c_str(); } - - // compare a range against our range - bool matches_exactly(offs_t addrstart, offs_t addrend) const { return (m_addrstart == addrstart && m_addrend == addrend); } - bool fully_covers(offs_t addrstart, offs_t addrend) const { return (m_addrstart <= addrstart && m_addrend >= addrend); } - bool is_covered_by(offs_t addrstart, offs_t addrend) const { return (m_addrstart >= addrstart && m_addrend <= addrend); } - bool straddles(offs_t addrstart, offs_t addrend) const { return (m_addrstart < addrend && m_addrend > addrstart); } - - // track and verify address space references to this bank - bool references_space(const address_space &space, read_or_write readorwrite) const; - void add_reference(address_space &space, read_or_write readorwrite); + const std::string &tag() const { return m_tag; } + const std::string &name() const { return m_name; } // set the base explicitly void set_base(void *base); @@ -1675,20 +2520,14 @@ public: void configure_entry(int entrynum, void *base); void configure_entries(int startentry, int numentries, void *base, offs_t stride); void set_entry(int entrynum); - void add_notifier(std::function<void (void *)> cb); private: // internal state running_machine & m_machine; // need the machine to free our memory std::vector<u8 *> m_entries; // the entries - bool m_anonymous; // are we anonymous or explicit? - offs_t m_addrstart; // start offset - offs_t m_addrend; // end offset int m_curentry; // current entry std::string m_name; // friendly name for this bank std::string m_tag; // tag for this bank - std::vector<std::unique_ptr<bank_reference>> m_reflist; // list of address spaces referencing this bank - std::vector<std::function<void (void *)>> m_alloc_notifier; // list of notifier targets when allocating }; @@ -1699,26 +2538,28 @@ class memory_share { public: // construction/destruction - memory_share(u8 width, size_t bytes, endianness_t endianness, void *ptr = nullptr) - : m_ptr(ptr), - m_bytes(bytes), - m_endianness(endianness), - m_bitwidth(width), - m_bytewidth(width <= 8 ? 1 : width <= 16 ? 2 : width <= 32 ? 4 : 8) + memory_share(std::string name, u8 width, size_t bytes, endianness_t endianness, void *ptr) + : m_name(name), + m_ptr(ptr), + m_bytes(bytes), + m_endianness(endianness), + m_bitwidth(width), + m_bytewidth(width <= 8 ? 1 : width <= 16 ? 2 : width <= 32 ? 4 : 8) { } // getters + const std::string &name() const { return m_name; } void *ptr() const { return m_ptr; } size_t bytes() const { return m_bytes; } endianness_t endianness() const { return m_endianness; } u8 bitwidth() const { return m_bitwidth; } u8 bytewidth() const { return m_bytewidth; } - // setters - void set_ptr(void *ptr) { m_ptr = ptr; } + std::string compare(u8 width, size_t bytes, endianness_t endianness) const; private: // internal state + std::string m_name; // share name void * m_ptr; // pointer to the memory backing the region size_t m_bytes; // size of the shared region in bytes endianness_t m_endianness; // endianness of the memory @@ -1734,18 +2575,16 @@ private: class memory_region { DISABLE_COPYING(memory_region); - - friend class memory_manager; public: // construction/destruction - memory_region(running_machine &machine, const char *name, u32 length, u8 width, endianness_t endian); + memory_region(running_machine &machine, std::string name, u32 length, u8 width, endianness_t endian); // getters running_machine &machine() const { return m_machine; } u8 *base() { return (m_buffer.size() > 0) ? &m_buffer[0] : nullptr; } u8 *end() { return base() + m_buffer.size(); } u32 bytes() const { return m_buffer.size(); } - const char *name() const { return m_name.c_str(); } + const std::string &name() const { return m_name; } // flag expansion endianness_t endianness() const { return m_endianness; } @@ -1770,46 +2609,140 @@ private: +// ======================> memory_view + +// a memory view allows switching between submaps in the map +class memory_view +{ + template<int Level, int Width, int AddrShift, endianness_t Endian> friend class address_space_specific; + template<int Level, int Width, int AddrShift> friend class memory_view_entry_specific; + template<int HighBits, int Width, int AddrShift> friend class handler_entry_write_dispatch; + template<int HighBits, int Width, int AddrShift> friend class handler_entry_read_dispatch; + friend class memory_view_entry; + friend class address_map_entry; + friend class address_map; + friend class device_t; + + DISABLE_COPYING(memory_view); + +public: + class memory_view_entry : public address_space_installer { + public: + virtual ~memory_view_entry() = default; + + address_map_entry &operator()(offs_t start, offs_t end); + + virtual void populate_from_map(address_map *map = nullptr) = 0; + + std::string key() const; + + protected: + memory_view &m_view; + std::unique_ptr<address_map> m_map; + int m_id; + + memory_view_entry(const address_space_config &config, memory_manager &manager, memory_view &view, int id); + void prepare_map_generic(address_map &map, bool allow_alloc) ATTR_COLD; + void prepare_device_map(address_map &map) ATTR_COLD; + + void check_range_optimize_all(const char *function, int width, offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, offs_t &nstart, offs_t &nend, offs_t &nmask, offs_t &nmirror, u64 &nunitmask, int &ncswidth); + void check_range_optimize_mirror(const char *function, offs_t addrstart, offs_t addrend, offs_t addrmirror, offs_t &nstart, offs_t &nend, offs_t &nmask, offs_t &nmirror); + void check_range_address(const char *function, offs_t addrstart, offs_t addrend); + }; + + memory_view(device_t &device, std::string name); + ~memory_view(); + + memory_view_entry &operator[](int slot); + + void select(int entry); + void disable(); + bool exists() const { return m_config != nullptr; } + + std::optional<int> entry() const { return m_cur_id == -1 ? std::optional<int>() : m_cur_slot; } + + const std::string &name() const { return m_name; } + +private: + device_t & m_device; + std::string m_name; + std::map<int, int> m_entry_mapping; + std::vector<std::unique_ptr<memory_view_entry>> m_entries; + const address_space_config * m_config; + offs_t m_addrstart; + offs_t m_addrend; + address_space * m_space; + handler_entry * m_handler_read; + handler_entry * m_handler_write; + int m_cur_id; + int m_cur_slot; + std::string m_context; + + void initialize_from_address_map(offs_t addrstart, offs_t addrend, const address_space_config &config); + std::pair<handler_entry *, handler_entry *> make_handlers(address_space &space, offs_t addrstart, offs_t addrend); + void make_subdispatch(std::string context); + int id_to_slot(int id) const; + void register_state(); + void refresh_id(); +}; + + + // ======================> memory_manager // holds internal state for the memory system class memory_manager { friend class address_space; - template<int Width, int AddrShift, endianness_t Endian> friend class address_space_specific; - friend memory_region::memory_region(running_machine &machine, const char *name, u32 length, u8 width, endianness_t endian); + template<int Level, int Width, int AddrShift, endianness_t Endian> friend class address_space_specific; public: // construction/destruction memory_manager(running_machine &machine); + ~memory_manager(); + + // initialize the memory spaces from the memory maps of the devices void initialize(); // getters running_machine &machine() const { return m_machine; } + + // used for the debugger interface memory views const std::unordered_map<std::string, std::unique_ptr<memory_bank>> &banks() const { return m_banklist; } const std::unordered_map<std::string, std::unique_ptr<memory_region>> ®ions() const { return m_regionlist; } const std::unordered_map<std::string, std::unique_ptr<memory_share>> &shares() const { return m_sharelist; } - // regions - memory_region *region_alloc(const char *name, u32 length, u8 width, endianness_t endian); - void region_free(const char *name); - memory_region *region_containing(const void *memory, offs_t bytes) const; + // anonymous memory zones + void *anonymous_alloc(address_space &space, size_t bytes, u8 width, offs_t start, offs_t end, const std::string &key = ""); + + // shares + memory_share *share_alloc(device_t &dev, std::string name, u8 width, size_t bytes, endianness_t endianness); + memory_share *share_find(std::string name); - memory_bank *find(const char *tag) const; - memory_bank *find(address_space &space, offs_t addrstart, offs_t addrend) const; - memory_bank *allocate(address_space &space, offs_t addrstart, offs_t addrend, const char *tag = nullptr); + // banks + memory_bank *bank_alloc(device_t &device, std::string tag); + memory_bank *bank_find(std::string tag); + + // regions + memory_region *region_alloc(std::string name, u32 length, u8 width, endianness_t endian); + memory_region *region_find(std::string name); + void region_free(std::string name); private: - void allocate(device_memory_interface &memory); + struct stdlib_deleter { void operator()(void *p) const { free(p); } }; // internal state running_machine & m_machine; // reference to the machine - bool m_initialized; // have we completed initialization? - std::vector<std::unique_ptr<memory_block>> m_blocklist; // head of the list of memory blocks + std::vector<std::unique_ptr<void, stdlib_deleter>> m_datablocks; // list of memory blocks to free on exit + std::unordered_map<std::string, std::unique_ptr<memory_bank>> m_banklist; // map of banks + std::unordered_map<std::string, std::unique_ptr<memory_share>> m_sharelist; // map of shares + std::unordered_map<std::string, std::unique_ptr<memory_region>> m_regionlist; // map of memory regions - std::unordered_map<std::string, std::unique_ptr<memory_bank>> m_banklist; // data gathered for each bank - std::unordered_map<std::string, std::unique_ptr<memory_share>> m_sharelist; // map for share lookups - std::unordered_map<std::string, std::unique_ptr<memory_region>> m_regionlist; // list of memory regions + // Allocate the address spaces + void allocate(device_memory_interface &memory); + + // Allocate some ram and register it for saving + void *allocate_memory(device_t &dev, int spacenum, std::string name, u8 width, size_t bytes); }; @@ -1818,44 +2751,6 @@ private: // MACROS //************************************************************************** -// space read/write handler function macros -#define READ8_MEMBER(name) u8 name(ATTR_UNUSED address_space &space, ATTR_UNUSED offs_t offset, ATTR_UNUSED u8 mem_mask) -#define WRITE8_MEMBER(name) void name(ATTR_UNUSED address_space &space, ATTR_UNUSED offs_t offset, ATTR_UNUSED u8 data, ATTR_UNUSED u8 mem_mask) -#define READ16_MEMBER(name) u16 name(ATTR_UNUSED address_space &space, ATTR_UNUSED offs_t offset, ATTR_UNUSED u16 mem_mask) -#define WRITE16_MEMBER(name) void name(ATTR_UNUSED address_space &space, ATTR_UNUSED offs_t offset, ATTR_UNUSED u16 data, ATTR_UNUSED u16 mem_mask) -#define READ32_MEMBER(name) u32 name(ATTR_UNUSED address_space &space, ATTR_UNUSED offs_t offset, ATTR_UNUSED u32 mem_mask) -#define WRITE32_MEMBER(name) void name(ATTR_UNUSED address_space &space, ATTR_UNUSED offs_t offset, ATTR_UNUSED u32 data, ATTR_UNUSED u32 mem_mask) -#define READ64_MEMBER(name) u64 name(ATTR_UNUSED address_space &space, ATTR_UNUSED offs_t offset, ATTR_UNUSED u64 mem_mask) -#define WRITE64_MEMBER(name) void name(ATTR_UNUSED address_space &space, ATTR_UNUSED offs_t offset, ATTR_UNUSED u64 data, ATTR_UNUSED u64 mem_mask) - -#define DECLARE_READ8_MEMBER(name) u8 name(ATTR_UNUSED address_space &space, ATTR_UNUSED offs_t offset, ATTR_UNUSED u8 mem_mask = 0xff) -#define DECLARE_WRITE8_MEMBER(name) void name(ATTR_UNUSED address_space &space, ATTR_UNUSED offs_t offset, ATTR_UNUSED u8 data, ATTR_UNUSED u8 mem_mask = 0xff) -#define DECLARE_READ16_MEMBER(name) u16 name(ATTR_UNUSED address_space &space, ATTR_UNUSED offs_t offset, ATTR_UNUSED u16 mem_mask = 0xffff) -#define DECLARE_WRITE16_MEMBER(name) void name(ATTR_UNUSED address_space &space, ATTR_UNUSED offs_t offset, ATTR_UNUSED u16 data, ATTR_UNUSED u16 mem_mask = 0xffff) -#define DECLARE_READ32_MEMBER(name) u32 name(ATTR_UNUSED address_space &space, ATTR_UNUSED offs_t offset, ATTR_UNUSED u32 mem_mask = 0xffffffff) -#define DECLARE_WRITE32_MEMBER(name) void name(ATTR_UNUSED address_space &space, ATTR_UNUSED offs_t offset, ATTR_UNUSED u32 data, ATTR_UNUSED u32 mem_mask = 0xffffffff) -#define DECLARE_READ64_MEMBER(name) u64 name(ATTR_UNUSED address_space &space, ATTR_UNUSED offs_t offset, ATTR_UNUSED u64 mem_mask = 0xffffffffffffffffU) -#define DECLARE_WRITE64_MEMBER(name) void name(ATTR_UNUSED address_space &space, ATTR_UNUSED offs_t offset, ATTR_UNUSED u64 data, ATTR_UNUSED u64 mem_mask = 0xffffffffffffffffU) - -// device delegate macros -#define READ8_DELEGATE(_class, _member) read8_delegate(FUNC(_class::_member), this) -#define WRITE8_DELEGATE(_class, _member) write8_delegate(FUNC(_class::_member), this) -#define READ16_DELEGATE(_class, _member) read16_delegate(FUNC(_class::_member), this) -#define WRITE16_DELEGATE(_class, _member) write16_delegate(FUNC(_class::_member), this) -#define READ32_DELEGATE(_class, _member) read32_delegate(FUNC(_class::_member), this) -#define WRITE32_DELEGATE(_class, _member) write32_delegate(FUNC(_class::_member), this) -#define READ64_DELEGATE(_class, _member) read64_delegate(FUNC(_class::_member), this) -#define WRITE64_DELEGATE(_class, _member) write64_delegate(FUNC(_class::_member), this) - -#define READ8_DEVICE_DELEGATE(_device, _class, _member) read8_delegate(FUNC(_class::_member), (_class *)_device) -#define WRITE8_DEVICE_DELEGATE(_device, _class, _member) write8_delegate(FUNC(_class::_member), (_class *)_device) -#define READ16_DEVICE_DELEGATE(_device, _class, _member) read16_delegate(FUNC(_class::_member), (_class *)_device) -#define WRITE16_DEVICE_DELEGATE(_device, _class, _member) write16_delegate(FUNC(_class::_member), (_class *)_device) -#define READ32_DEVICE_DELEGATE(_device, _class, _member) read32_delegate(FUNC(_class::_member), (_class *)_device) -#define WRITE32_DEVICE_DELEGATE(_device, _class, _member) write32_delegate(FUNC(_class::_member), (_class *)_device) -#define READ64_DEVICE_DELEGATE(_device, _class, _member) read64_delegate(FUNC(_class::_member), (_class *)_device) -#define WRITE64_DEVICE_DELEGATE(_device, _class, _member) write64_delegate(FUNC(_class::_member), (_class *)_device) - // helper macro for merging data with the memory mask #define COMBINE_DATA(varptr) (*(varptr) = (*(varptr) & ~mem_mask) | (data & mem_mask)) @@ -1878,54 +2773,84 @@ private: #define ACCESSING_BITS_32_63 ((mem_mask & 0xffffffff00000000U) != 0) -// macros for accessing bytes and words within larger chunks - -// read/write a byte to a 16-bit space -#define BYTE_XOR_BE(a) ((a) ^ NATIVE_ENDIAN_VALUE_LE_BE(1,0)) -#define BYTE_XOR_LE(a) ((a) ^ NATIVE_ENDIAN_VALUE_LE_BE(0,1)) - -// read/write a byte to a 32-bit space -#define BYTE4_XOR_BE(a) ((a) ^ NATIVE_ENDIAN_VALUE_LE_BE(3,0)) -#define BYTE4_XOR_LE(a) ((a) ^ NATIVE_ENDIAN_VALUE_LE_BE(0,3)) - -// read/write a word to a 32-bit space -#define WORD_XOR_BE(a) ((a) ^ NATIVE_ENDIAN_VALUE_LE_BE(2,0)) -#define WORD_XOR_LE(a) ((a) ^ NATIVE_ENDIAN_VALUE_LE_BE(0,2)) - -// read/write a byte to a 64-bit space -#define BYTE8_XOR_BE(a) ((a) ^ NATIVE_ENDIAN_VALUE_LE_BE(7,0)) -#define BYTE8_XOR_LE(a) ((a) ^ NATIVE_ENDIAN_VALUE_LE_BE(0,7)) - -// read/write a word to a 64-bit space -#define WORD2_XOR_BE(a) ((a) ^ NATIVE_ENDIAN_VALUE_LE_BE(6,0)) -#define WORD2_XOR_LE(a) ((a) ^ NATIVE_ENDIAN_VALUE_LE_BE(0,6)) - -// read/write a dword to a 64-bit space -#define DWORD_XOR_BE(a) ((a) ^ NATIVE_ENDIAN_VALUE_LE_BE(4,0)) -#define DWORD_XOR_LE(a) ((a) ^ NATIVE_ENDIAN_VALUE_LE_BE(0,4)) - - // helpers for checking address alignment #define WORD_ALIGNED(a) (((a) & 1) == 0) #define DWORD_ALIGNED(a) (((a) & 3) == 0) #define QWORD_ALIGNED(a) (((a) & 7) == 0) -template<int Width, int AddrShift, int Endian> typename emu::detail::handler_entry_size<Width>::uX memory_access_cache<Width, AddrShift, Endian>::read_native(offs_t address, typename emu::detail::handler_entry_size<Width>::uX mask) +template<int Width, int AddrShift, endianness_t Endian> +emu::detail::handler_entry_size_t<Width> +emu::detail::memory_access_cache<Width, AddrShift, Endian>:: +read_native(offs_t address, emu::detail::handler_entry_size_t<Width> mask) { + address &= m_addrmask; check_address_r(address); return m_cache_r->read(address, mask); } -template<int Width, int AddrShift, int Endian> void memory_access_cache<Width, AddrShift, Endian>::write_native(offs_t address, typename emu::detail::handler_entry_size<Width>::uX data, typename emu::detail::handler_entry_size<Width>::uX mask) +template<int Width, int AddrShift, endianness_t Endian> +void emu::detail::memory_access_cache<Width, AddrShift, Endian>:: +write_native(offs_t address, emu::detail::handler_entry_size_t<Width> data, emu::detail::handler_entry_size_t<Width> mask) { + address &= m_addrmask; check_address_w(address); m_cache_w->write(address, data, mask); } -void memory_passthrough_handler::remove() +inline void emu::detail::memory_passthrough_handler_impl::remove() { m_space.remove_passthrough(m_handlers); } + +template<int Level, int Width, int AddrShift, endianness_t Endian> +void emu::detail::memory_access_specific<Level, Width, AddrShift, Endian>:: +set(address_space *space, std::pair<const void *, const void *> rw) +{ + m_space = space; + m_addrmask = space->addrmask(); + m_dispatch_read = (const handler_entry_read <Width, AddrShift> *const *)(rw.first); + m_dispatch_write = (const handler_entry_write<Width, AddrShift> *const *)(rw.second); +} + + +template<int Width, int AddrShift, endianness_t Endian> +void emu::detail::memory_access_cache<Width, AddrShift, Endian>:: +set(address_space *space, std::pair<void *, void *> rw) +{ + m_space = space; + m_addrmask = space->addrmask(); + + m_subscription = space->add_change_notifier( + [this] (read_or_write mode) { + if(u32(mode) & u32(read_or_write::READ)) { + m_addrend_r = 0; + m_addrstart_r = 1; + m_cache_r = nullptr; + } + if(u32(mode) & u32(read_or_write::WRITE)) { + m_addrend_w = 0; + m_addrstart_w = 1; + m_cache_w = nullptr; + } + }); + m_root_read = (handler_entry_read <Width, AddrShift> *)(rw.first); + m_root_write = (handler_entry_write<Width, AddrShift> *)(rw.second); + + // Protect against a wandering memset + m_addrstart_r = 1; + m_addrend_r = 0; + m_cache_r = nullptr; + m_addrstart_w = 1; + m_addrend_w = 0; + m_cache_w = nullptr; +} + +template<int Width, int AddrShift, endianness_t Endian> +emu::detail::memory_access_cache<Width, AddrShift, Endian>:: +~memory_access_cache() +{ +} + #endif /* MAME_EMU_EMUMEM_H */ diff --git a/src/emu/emumem_aspace.cpp b/src/emu/emumem_aspace.cpp new file mode 100644 index 00000000000..f93472b110b --- /dev/null +++ b/src/emu/emumem_aspace.cpp @@ -0,0 +1,1283 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles,Olivier Galibert +/*************************************************************************** + + emumem.cpp + + Functions which handle device memory access. + Address-space related functions + +***************************************************************************/ + +#include "emu.h" + +#include "emumem_mud.h" +#include "emumem_hea.h" +#include "emumem_hem.h" +#include "emumem_hedp.h" +#include "emumem_heun.h" +#include "emumem_heu.h" +#include "emumem_hedr.h" +#include "emumem_hedw.h" +#include "emumem_hep.h" +#include "emumem_het.h" +#include "emumem_hws.h" + +#include "emuopts.h" +#include "debug/debugcpu.h" + +#include "mfpresolve.h" + +#include <list> +#include <map> + + +//************************************************************************** +// DEBUGGING +//************************************************************************** + +#define VERBOSE 0 + +#if VERBOSE +template <typename Format, typename... Params> static void VPRINTF(Format &&fmt, Params &&...args) +{ + util::stream_format(std::cerr, std::forward<Format>(fmt), std::forward<Params>(args)...); +} +#else +template <typename Format, typename... Params> static void VPRINTF(Format &&, Params &&...) {} +#endif + +#define VALIDATE_REFCOUNTS 0 + +//************************************************************************** +// CONSTANTS +//************************************************************************** + +using emu::detail::handler_width_v; + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> address_space_specific + +// this is a derived class of address_space with specific width, endianness, and table size +template<int Level, int Width, int AddrShift, endianness_t Endian> +class address_space_specific : public address_space +{ + using uX = emu::detail::handler_entry_size_t<Width>; + using NativeType = uX; + using this_type = address_space_specific<Level, Width, AddrShift, Endian>; + + // constants describing the native size + static constexpr u32 NATIVE_BYTES = 1 << Width; + static constexpr u32 NATIVE_STEP = AddrShift >= 0 ? NATIVE_BYTES << iabs(AddrShift) : NATIVE_BYTES >> iabs(AddrShift); + static constexpr u32 NATIVE_MASK = NATIVE_STEP - 1; + static constexpr u32 NATIVE_BITS = 8 * NATIVE_BYTES; + + static constexpr offs_t offset_to_byte(offs_t offset) { return AddrShift < 0 ? offset << iabs(AddrShift) : offset >> iabs(AddrShift); } + +public: + const handler_entry_read<Width, AddrShift> *const *m_dispatch_read; + const handler_entry_write<Width, AddrShift> *const *m_dispatch_write; + + std::string get_handler_string(read_or_write readorwrite, offs_t byteaddress) const override; + void dump_maps(std::vector<memory_entry> &read_map, std::vector<memory_entry> &write_map) const override; + + void unmap_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, read_or_write readorwrite, bool quiet) override; + void install_ram_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, read_or_write readorwrite, void *baseptr) override; + void install_bank_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, memory_bank *rbank, memory_bank *wbank) override; + void install_readwrite_port(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, std::string rtag, std::string wtag) override; + void install_device_delegate(offs_t addrstart, offs_t addrend, device_t &device, address_map_constructor &map, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override; + void install_view(offs_t addrstart, offs_t addrend, offs_t addrmirror, memory_view &view) override; + + void install_read_before_time(offs_t addrstart, offs_t addrend, offs_t addrmirror, ws_time_delegate ws) override; + void install_read_before_delay(offs_t addrstart, offs_t addrend, offs_t addrmirror, ws_delay_delegate ws) override; + void install_read_after_delay(offs_t addrstart, offs_t addrend, offs_t addrmirror, ws_delay_delegate ws) override; + + void install_write_before_time(offs_t addrstart, offs_t addrend, offs_t addrmirror, ws_time_delegate ws) override; + void install_write_before_delay(offs_t addrstart, offs_t addrend, offs_t addrmirror, ws_delay_delegate ws) override; + void install_write_after_delay(offs_t addrstart, offs_t addrend, offs_t addrmirror, ws_delay_delegate ws) override; + + void install_readwrite_before_time(offs_t addrstart, offs_t addrend, offs_t addrmirror, ws_time_delegate ws) override; + void install_readwrite_before_delay(offs_t addrstart, offs_t addrend, offs_t addrmirror, ws_delay_delegate ws) override; + void install_readwrite_after_delay(offs_t addrstart, offs_t addrend, offs_t addrmirror, ws_delay_delegate ws) override; + + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8m_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8m_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16m_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16m_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32m_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32m_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64m_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64m_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8s_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8s_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16s_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16s_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32s_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32s_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64s_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64s_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8sm_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16sm_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32sm_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64sm_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8mo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16mo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32mo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64mo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8smo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16smo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32smo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64smo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + + using address_space::install_read_tap; + using address_space::install_write_tap; + using address_space::install_readwrite_tap; + + virtual memory_passthrough_handler install_read_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, uX &data, uX mem_mask)> tap, memory_passthrough_handler *mph) override; + virtual memory_passthrough_handler install_write_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, uX &data, uX mem_mask)> tap, memory_passthrough_handler *mph) override; + virtual memory_passthrough_handler install_readwrite_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, uX &data, uX mem_mask)> tapr, std::function<void (offs_t offset, uX &data, uX mem_mask)> tapw, memory_passthrough_handler *mph) override; + + // construction/destruction + address_space_specific(memory_manager &manager, device_memory_interface &memory, int spacenum, int address_width) + : address_space(manager, memory, spacenum) + { + m_unmap_r = new handler_entry_read_unmapped <Width, AddrShift>(this, 0); + m_unmap_w = new handler_entry_write_unmapped<Width, AddrShift>(this, 0); + m_nop_r = new handler_entry_read_nop <Width, AddrShift>(this, 0); + m_nop_w = new handler_entry_write_nop<Width, AddrShift>(this, 0); + + handler_entry::range r{ 0, 0xffffffff >> (32 - address_width) }; + + switch (address_width) { + case 1: m_root_read = new handler_entry_read_dispatch< std::max(1, Width), Width, AddrShift>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch< std::max(1, Width), Width, AddrShift>(this, r, nullptr); break; + case 2: m_root_read = new handler_entry_read_dispatch< std::max(2, Width), Width, AddrShift>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch< std::max(2, Width), Width, AddrShift>(this, r, nullptr); break; + case 3: m_root_read = new handler_entry_read_dispatch< std::max(3, Width), Width, AddrShift>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch< std::max(3, Width), Width, AddrShift>(this, r, nullptr); break; + case 4: m_root_read = new handler_entry_read_dispatch< 4, Width, AddrShift>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch< 4, Width, AddrShift>(this, r, nullptr); break; + case 5: m_root_read = new handler_entry_read_dispatch< 5, Width, AddrShift>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch< 5, Width, AddrShift>(this, r, nullptr); break; + case 6: m_root_read = new handler_entry_read_dispatch< 6, Width, AddrShift>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch< 6, Width, AddrShift>(this, r, nullptr); break; + case 7: m_root_read = new handler_entry_read_dispatch< 7, Width, AddrShift>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch< 7, Width, AddrShift>(this, r, nullptr); break; + case 8: m_root_read = new handler_entry_read_dispatch< 8, Width, AddrShift>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch< 8, Width, AddrShift>(this, r, nullptr); break; + case 9: m_root_read = new handler_entry_read_dispatch< 9, Width, AddrShift>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch< 9, Width, AddrShift>(this, r, nullptr); break; + case 10: m_root_read = new handler_entry_read_dispatch<10, Width, AddrShift>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<10, Width, AddrShift>(this, r, nullptr); break; + case 11: m_root_read = new handler_entry_read_dispatch<11, Width, AddrShift>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<11, Width, AddrShift>(this, r, nullptr); break; + case 12: m_root_read = new handler_entry_read_dispatch<12, Width, AddrShift>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<12, Width, AddrShift>(this, r, nullptr); break; + case 13: m_root_read = new handler_entry_read_dispatch<13, Width, AddrShift>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<13, Width, AddrShift>(this, r, nullptr); break; + case 14: m_root_read = new handler_entry_read_dispatch<14, Width, AddrShift>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<14, Width, AddrShift>(this, r, nullptr); break; + case 15: m_root_read = new handler_entry_read_dispatch<15, Width, AddrShift>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<15, Width, AddrShift>(this, r, nullptr); break; + case 16: m_root_read = new handler_entry_read_dispatch<16, Width, AddrShift>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<16, Width, AddrShift>(this, r, nullptr); break; + case 17: m_root_read = new handler_entry_read_dispatch<17, Width, AddrShift>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<17, Width, AddrShift>(this, r, nullptr); break; + case 18: m_root_read = new handler_entry_read_dispatch<18, Width, AddrShift>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<18, Width, AddrShift>(this, r, nullptr); break; + case 19: m_root_read = new handler_entry_read_dispatch<19, Width, AddrShift>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<19, Width, AddrShift>(this, r, nullptr); break; + case 20: m_root_read = new handler_entry_read_dispatch<20, Width, AddrShift>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<20, Width, AddrShift>(this, r, nullptr); break; + case 21: m_root_read = new handler_entry_read_dispatch<21, Width, AddrShift>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<21, Width, AddrShift>(this, r, nullptr); break; + case 22: m_root_read = new handler_entry_read_dispatch<22, Width, AddrShift>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<22, Width, AddrShift>(this, r, nullptr); break; + case 23: m_root_read = new handler_entry_read_dispatch<23, Width, AddrShift>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<23, Width, AddrShift>(this, r, nullptr); break; + case 24: m_root_read = new handler_entry_read_dispatch<24, Width, AddrShift>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<24, Width, AddrShift>(this, r, nullptr); break; + case 25: m_root_read = new handler_entry_read_dispatch<25, Width, AddrShift>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<25, Width, AddrShift>(this, r, nullptr); break; + case 26: m_root_read = new handler_entry_read_dispatch<26, Width, AddrShift>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<26, Width, AddrShift>(this, r, nullptr); break; + case 27: m_root_read = new handler_entry_read_dispatch<27, Width, AddrShift>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<27, Width, AddrShift>(this, r, nullptr); break; + case 28: m_root_read = new handler_entry_read_dispatch<28, Width, AddrShift>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<28, Width, AddrShift>(this, r, nullptr); break; + case 29: m_root_read = new handler_entry_read_dispatch<29, Width, AddrShift>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<29, Width, AddrShift>(this, r, nullptr); break; + case 30: m_root_read = new handler_entry_read_dispatch<30, Width, AddrShift>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<30, Width, AddrShift>(this, r, nullptr); break; + case 31: m_root_read = new handler_entry_read_dispatch<31, Width, AddrShift>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<31, Width, AddrShift>(this, r, nullptr); break; + case 32: m_root_read = new handler_entry_read_dispatch<32, Width, AddrShift>(this, r, nullptr); m_root_write = new handler_entry_write_dispatch<32, Width, AddrShift>(this, r, nullptr); break; + default: fatalerror("Unhandled address bus width %d\n", address_width); + } + + m_dispatch_read = m_root_read ->get_dispatch(); + m_dispatch_write = m_root_write->get_dispatch(); + } + + virtual ~address_space_specific() { + m_root_read ->unref(); + m_root_write->unref(); + } + + std::pair<void *, void *> get_cache_info() override { + std::pair<void *, void *> rw; + rw.first = m_root_read; + rw.second = m_root_write; + return rw; + } + + std::pair<const void *, const void *> get_specific_info() override { + std::pair<const void *, const void *> rw; + rw.first = m_dispatch_read; + rw.second = m_dispatch_write; + return rw; + } + + void delayed_ref(handler_entry *e) { + e->ref(); + m_delayed_unrefs.insert(e); + } + + void delayed_unref(handler_entry *e) { + m_delayed_unrefs.erase(m_delayed_unrefs.find(e)); + e->unref(); + } + + void validate_reference_counts() const override { + handler_entry::reflist refs; + refs.add(m_root_read); + refs.add(m_root_write); + refs.add(m_unmap_r); + refs.add(m_unmap_w); + refs.add(m_nop_r); + refs.add(m_nop_w); + for(handler_entry *e : m_delayed_unrefs) + refs.add(e); + refs.propagate(); + refs.check(); + } + + virtual void remove_passthrough(std::unordered_set<handler_entry *> &handlers) override { + invalidate_caches(read_or_write::READWRITE); + m_root_read->detach(handlers); + m_root_write->detach(handlers); + } + + // generate specific accessor info + virtual specific_access_info specific_accessors() const override + { + specific_access_info accessors; + + accessors.native_bytes = 1 << Width; + accessors.native_mask_bits = ((Width + AddrShift) >= 0) ? (Width + AddrShift) : 0; + accessors.address_width = addr_width(); + accessors.low_bits = emu::detail::handler_entry_dispatch_level_to_lowbits(Level, Width, AddrShift); + accessors.read.dispatch = reinterpret_cast<void const *const *>(m_dispatch_read); + accessors.write.dispatch = reinterpret_cast<void const *const *>(m_dispatch_write); + + auto readfunc = &handler_entry_read<Width, AddrShift>::read; + auto writefunc = &handler_entry_write<Width, AddrShift>::write; + std::tie(accessors.read.function, accessors.read.displacement, accessors.read.is_virtual) = util::resolve_member_function(readfunc); + std::tie(accessors.write.function, accessors.write.displacement, accessors.write.is_virtual) = util::resolve_member_function(writefunc); + + return accessors; + } + + // return a pointer to the read bank, or nullptr if none + virtual void *get_read_ptr(offs_t address) const override + { + return m_root_read->get_ptr(address); + } + + // return a pointer to the write bank, or nullptr if none + virtual void *get_write_ptr(offs_t address) const override + { + return m_root_write->get_ptr(address); + } + + // native read + NativeType read_native(offs_t offset, NativeType mask) + { + return dispatch_read<Level, Width, AddrShift>(offs_t(-1), offset & m_addrmask, mask, m_dispatch_read); + } + + // mask-less native read + NativeType read_native(offs_t offset) + { + return dispatch_read<Level, Width, AddrShift>(offs_t(-1), offset & m_addrmask, uX(0xffffffffffffffffU), m_dispatch_read); + } + + // native write + void write_native(offs_t offset, NativeType data, NativeType mask) + { + dispatch_write<Level, Width, AddrShift>(offs_t(-1), offset & m_addrmask, data, mask, m_dispatch_write); + } + + // mask-less native write + void write_native(offs_t offset, NativeType data) + { + dispatch_write<Level, Width, AddrShift>(offs_t(-1), offset & m_addrmask, data, uX(0xffffffffffffffffU), m_dispatch_write); + } + + auto rop() { return [this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }; } + auto wop() { return [this](offs_t offset, NativeType data, NativeType mask) -> void { write_native(offset, data, mask); }; } + + // virtual access to these functions + u8 read_byte(offs_t address) override { if constexpr(Width == 0) return read_native(address & ~NATIVE_MASK); else return memory_read_generic<Width, AddrShift, Endian, 0, true>(rop(), address, 0xff); } + u8 read_byte(offs_t address, u8 mask) override { return memory_read_generic<Width, AddrShift, Endian, 0, true>(rop(), address, mask); } + u16 read_word(offs_t address) override { if constexpr(Width == 1) return read_native(address & ~NATIVE_MASK); else return memory_read_generic<Width, AddrShift, Endian, 1, true>(rop(), address, 0xffff); } + u16 read_word(offs_t address, u16 mask) override { return memory_read_generic<Width, AddrShift, Endian, 1, true>(rop(), address, mask); } + u16 read_word_unaligned(offs_t address) override { return memory_read_generic<Width, AddrShift, Endian, 1, false>(rop(), address, 0xffff); } + u16 read_word_unaligned(offs_t address, u16 mask) override { return memory_read_generic<Width, AddrShift, Endian, 1, false>(rop(), address, mask); } + u32 read_dword(offs_t address) override { if constexpr(Width == 2) return read_native(address & ~NATIVE_MASK); else return memory_read_generic<Width, AddrShift, Endian, 2, true>(rop(), address, 0xffffffff); } + u32 read_dword(offs_t address, u32 mask) override { return memory_read_generic<Width, AddrShift, Endian, 2, true>(rop(), address, mask); } + u32 read_dword_unaligned(offs_t address) override { return memory_read_generic<Width, AddrShift, Endian, 2, false>(rop(), address, 0xffffffff); } + u32 read_dword_unaligned(offs_t address, u32 mask) override { return memory_read_generic<Width, AddrShift, Endian, 2, false>(rop(), address, mask); } + u64 read_qword(offs_t address) override { if constexpr(Width == 3) return read_native(address & ~NATIVE_MASK); else return memory_read_generic<Width, AddrShift, Endian, 3, true>(rop(), address, 0xffffffffffffffffU); } + u64 read_qword(offs_t address, u64 mask) override { return memory_read_generic<Width, AddrShift, Endian, 3, true>(rop(), address, mask); } + u64 read_qword_unaligned(offs_t address) override { return memory_read_generic<Width, AddrShift, Endian, 3, false>(rop(), address, 0xffffffffffffffffU); } + u64 read_qword_unaligned(offs_t address, u64 mask) override { return memory_read_generic<Width, AddrShift, Endian, 3, false>(rop(), address, mask); } + + void write_byte(offs_t address, u8 data) override { if constexpr(Width == 0) write_native(address & ~NATIVE_MASK, data); else memory_write_generic<Width, AddrShift, Endian, 0, true>(wop(), address, data, 0xff); } + void write_byte(offs_t address, u8 data, u8 mask) override { memory_write_generic<Width, AddrShift, Endian, 0, true>(wop(), address, data, mask); } + void write_word(offs_t address, u16 data) override { if constexpr(Width == 1) write_native(address & ~NATIVE_MASK, data); else memory_write_generic<Width, AddrShift, Endian, 1, true>(wop(), address, data, 0xffff); } + void write_word(offs_t address, u16 data, u16 mask) override { memory_write_generic<Width, AddrShift, Endian, 1, true>(wop(), address, data, mask); } + void write_word_unaligned(offs_t address, u16 data) override { memory_write_generic<Width, AddrShift, Endian, 1, false>(wop(), address, data, 0xffff); } + void write_word_unaligned(offs_t address, u16 data, u16 mask) override { memory_write_generic<Width, AddrShift, Endian, 1, false>(wop(), address, data, mask); } + void write_dword(offs_t address, u32 data) override { if constexpr(Width == 2) write_native(address & ~NATIVE_MASK, data); else memory_write_generic<Width, AddrShift, Endian, 2, true>(wop(), address, data, 0xffffffff); } + void write_dword(offs_t address, u32 data, u32 mask) override { memory_write_generic<Width, AddrShift, Endian, 2, true>(wop(), address, data, mask); } + void write_dword_unaligned(offs_t address, u32 data) override { memory_write_generic<Width, AddrShift, Endian, 2, false>(wop(), address, data, 0xffffffff); } + void write_dword_unaligned(offs_t address, u32 data, u32 mask) override { memory_write_generic<Width, AddrShift, Endian, 2, false>(wop(), address, data, mask); } + void write_qword(offs_t address, u64 data) override { if constexpr(Width == 3) write_native(address & ~NATIVE_MASK, data); else memory_write_generic<Width, AddrShift, Endian, 3, true>(wop(), address, data, 0xffffffffffffffffU); } + void write_qword(offs_t address, u64 data, u64 mask) override { memory_write_generic<Width, AddrShift, Endian, 3, true>(wop(), address, data, mask); } + void write_qword_unaligned(offs_t address, u64 data) override { memory_write_generic<Width, AddrShift, Endian, 3, false>(wop(), address, data, 0xffffffffffffffffU); } + void write_qword_unaligned(offs_t address, u64 data, u64 mask) override { memory_write_generic<Width, AddrShift, Endian, 3, false>(wop(), address, data, mask); } + + handler_entry_read <Width, AddrShift> *m_root_read; + handler_entry_write<Width, AddrShift> *m_root_write; + + std::unordered_set<handler_entry *> m_delayed_unrefs; + +private: + template<typename READ> + void install_read_handler_impl(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, u16 flags, READ &handler_r) + { + try { handler_r.resolve(); } + catch (const binding_type_exception &) { + osd_printf_error("Binding error while installing read handler %s for range 0x%X-0x%X mask 0x%X mirror 0x%X select 0x%X umask 0x%X\n", handler_r.name(), addrstart, addrend, addrmask, addrmirror, addrselect, unitmask); + throw; + } + install_read_handler_helper<handler_width_v<READ> >(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, handler_r); + } + + template<typename WRITE> + void install_write_handler_impl(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, u16 flags, WRITE &handler_w) + { + try { handler_w.resolve(); } + catch (const binding_type_exception &) { + osd_printf_error("Binding error while installing write handler %s for range 0x%X-0x%X mask 0x%X mirror 0x%X select 0x%X umask 0x%X\n", handler_w.name(), addrstart, addrend, addrmask, addrmirror, addrselect, unitmask); + throw; + } + install_write_handler_helper<handler_width_v<WRITE> >(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, handler_w); + } + + template<int AccessWidth, typename READ> + void install_read_handler_helper(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, u16 flags, const READ &handler_r) + { + if constexpr (Width < AccessWidth) { + fatalerror("install_read_handler: cannot install a %d-wide handler in a %d-wide bus", 8 << AccessWidth, 8 << Width); + } else { + VPRINTF("address_space::install_read_handler(%*x-%*x mask=%*x mirror=%*x, space width=%d, handler width=%d, %s, %*x)\n", + m_addrchars, addrstart, m_addrchars, addrend, + m_addrchars, addrmask, m_addrchars, addrmirror, + 8 << Width, 8 << AccessWidth, + handler_r.name(), data_width() / 4, unitmask); + + offs_t nstart, nend, nmask, nmirror; + u64 nunitmask; + int ncswidth; + check_optimize_all("install_read_handler", 8 << AccessWidth, addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, nstart, nend, nmask, nmirror, nunitmask, ncswidth); + + if constexpr (Width == AccessWidth) { + auto hand_r = new handler_entry_read_delegate<Width, AddrShift, READ>(this, flags, handler_r); + hand_r->set_address_info(nstart, nmask); + m_root_read->populate(nstart, nend, nmirror, hand_r); + } else { + auto hand_r = new handler_entry_read_delegate<AccessWidth, -AccessWidth, READ>(this, flags, handler_r); + memory_units_descriptor<Width, AddrShift> descriptor(AccessWidth, Endian, hand_r, nstart, nend, nmask, nunitmask, ncswidth); + hand_r->set_address_info(descriptor.get_handler_start(), descriptor.get_handler_mask()); + m_root_read->populate_mismatched(nstart, nend, nmirror, descriptor); + hand_r->unref(); + } + invalidate_caches(read_or_write::READ); + } + } + + template<int AccessWidth, typename WRITE> + void install_write_handler_helper(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, u16 flags, + const WRITE &handler_w) + { + if constexpr (Width < AccessWidth) { + fatalerror("install_write_handler: cannot install a %d-wide handler in a %d-wide bus", 8 << AccessWidth, 8 << Width); + } else { + VPRINTF("address_space::install_write_handler(%*x-%*x mask=%*x mirror=%*x, space width=%d, handler width=%d, %s, %*x)\n", + m_addrchars, addrstart, m_addrchars, addrend, + m_addrchars, addrmask, m_addrchars, addrmirror, + 8 << Width, 8 << AccessWidth, + handler_w.name(), data_width() / 4, unitmask); + + offs_t nstart, nend, nmask, nmirror; + u64 nunitmask; + int ncswidth; + check_optimize_all("install_write_handler", 8 << AccessWidth, addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, nstart, nend, nmask, nmirror, nunitmask, ncswidth); + + if constexpr (Width == AccessWidth) { + auto hand_w = new handler_entry_write_delegate<Width, AddrShift, WRITE>(this, flags, handler_w); + hand_w->set_address_info(nstart, nmask); + m_root_write->populate(nstart, nend, nmirror, hand_w); + } else { + auto hand_w = new handler_entry_write_delegate<AccessWidth, -AccessWidth, WRITE>(this, flags, handler_w); + memory_units_descriptor<Width, AddrShift> descriptor(AccessWidth, Endian, hand_w, nstart, nend, nmask, nunitmask, ncswidth); + hand_w->set_address_info(descriptor.get_handler_start(), descriptor.get_handler_mask()); + m_root_write->populate_mismatched(nstart, nend, nmirror, descriptor); + hand_w->unref(); + } + invalidate_caches(read_or_write::WRITE); + } + } +}; + + + +//------------------------------------------------- +// allocate - allocate memory spaces +//------------------------------------------------- + +void memory_manager::allocate(device_memory_interface &memory) +{ + for (int spacenum = 0; spacenum < memory.max_space_count(); ++spacenum) + { + // if there is a configuration for this space, we need an address space + address_space_config const *const spaceconfig = memory.space_config(spacenum); + if (spaceconfig) + { + int level = emu::detail::handler_entry_dispatch_level(spaceconfig->addr_width()); + // allocate one of the appropriate type + switch ((level << 8) | (spaceconfig->endianness() == ENDIANNESS_BIG ? 0x1000 : 0) |spaceconfig->data_width() | (spaceconfig->addr_shift() + 4)) + { + case 0x0000|0x000| 8|(4+1): memory.allocate<address_space_specific<0, 0, 1, ENDIANNESS_LITTLE>>(*this, spacenum); break; + case 0x1000|0x000| 8|(4+1): memory.allocate<address_space_specific<0, 0, 1, ENDIANNESS_BIG >>(*this, spacenum); break; + case 0x0000|0x100| 8|(4+1): memory.allocate<address_space_specific<1, 0, 1, ENDIANNESS_LITTLE>>(*this, spacenum); break; + case 0x1000|0x100| 8|(4+1): memory.allocate<address_space_specific<1, 0, 1, ENDIANNESS_BIG >>(*this, spacenum); break; + + case 0x0000|0x000| 8|(4-0): memory.allocate<address_space_specific<0, 0, 0, ENDIANNESS_LITTLE>>(*this, spacenum); break; + case 0x1000|0x000| 8|(4-0): memory.allocate<address_space_specific<0, 0, 0, ENDIANNESS_BIG >>(*this, spacenum); break; + case 0x0000|0x100| 8|(4-0): memory.allocate<address_space_specific<1, 0, 0, ENDIANNESS_LITTLE>>(*this, spacenum); break; + case 0x1000|0x100| 8|(4-0): memory.allocate<address_space_specific<1, 0, 0, ENDIANNESS_BIG >>(*this, spacenum); break; + + case 0x0000|0x000|16|(4+3): memory.allocate<address_space_specific<0, 1, 3, ENDIANNESS_LITTLE>>(*this, spacenum); break; + case 0x1000|0x000|16|(4+3): memory.allocate<address_space_specific<0, 1, 3, ENDIANNESS_BIG >>(*this, spacenum); break; + case 0x0000|0x100|16|(4+3): memory.allocate<address_space_specific<1, 1, 3, ENDIANNESS_LITTLE>>(*this, spacenum); break; + case 0x1000|0x100|16|(4+3): memory.allocate<address_space_specific<1, 1, 3, ENDIANNESS_BIG >>(*this, spacenum); break; + + case 0x0000|0x000|16|(4-0): memory.allocate<address_space_specific<0, 1, 0, ENDIANNESS_LITTLE>>(*this, spacenum); break; + case 0x1000|0x000|16|(4-0): memory.allocate<address_space_specific<0, 1, 0, ENDIANNESS_BIG >>(*this, spacenum); break; + case 0x0000|0x100|16|(4-0): memory.allocate<address_space_specific<1, 1, 0, ENDIANNESS_LITTLE>>(*this, spacenum); break; + case 0x1000|0x100|16|(4-0): memory.allocate<address_space_specific<1, 1, 0, ENDIANNESS_BIG >>(*this, spacenum); break; + + case 0x0000|0x000|16|(4-1): memory.allocate<address_space_specific<0, 1, -1, ENDIANNESS_LITTLE>>(*this, spacenum); break; + case 0x1000|0x000|16|(4-1): memory.allocate<address_space_specific<0, 1, -1, ENDIANNESS_BIG >>(*this, spacenum); break; + case 0x0000|0x100|16|(4-1): memory.allocate<address_space_specific<1, 1, -1, ENDIANNESS_LITTLE>>(*this, spacenum); break; + case 0x1000|0x100|16|(4-1): memory.allocate<address_space_specific<1, 1, -1, ENDIANNESS_BIG >>(*this, spacenum); break; + + case 0x0000|0x000|32|(4+3): memory.allocate<address_space_specific<0, 2, 3, ENDIANNESS_LITTLE>>(*this, spacenum); break; + case 0x1000|0x000|32|(4+3): memory.allocate<address_space_specific<0, 2, 3, ENDIANNESS_BIG >>(*this, spacenum); break; + case 0x0000|0x100|32|(4+3): memory.allocate<address_space_specific<1, 2, 3, ENDIANNESS_LITTLE>>(*this, spacenum); break; + case 0x1000|0x100|32|(4+3): memory.allocate<address_space_specific<1, 2, 3, ENDIANNESS_BIG >>(*this, spacenum); break; + + case 0x0000|0x000|32|(4-0): memory.allocate<address_space_specific<0, 2, 0, ENDIANNESS_LITTLE>>(*this, spacenum); break; + case 0x1000|0x000|32|(4-0): memory.allocate<address_space_specific<0, 2, 0, ENDIANNESS_BIG >>(*this, spacenum); break; + case 0x0000|0x100|32|(4-0): memory.allocate<address_space_specific<1, 2, 0, ENDIANNESS_LITTLE>>(*this, spacenum); break; + case 0x1000|0x100|32|(4-0): memory.allocate<address_space_specific<1, 2, 0, ENDIANNESS_BIG >>(*this, spacenum); break; + + case 0x0000|0x000|32|(4-1): memory.allocate<address_space_specific<0, 2, -1, ENDIANNESS_LITTLE>>(*this, spacenum); break; + case 0x1000|0x000|32|(4-1): memory.allocate<address_space_specific<0, 2, -1, ENDIANNESS_BIG >>(*this, spacenum); break; + case 0x0000|0x100|32|(4-1): memory.allocate<address_space_specific<1, 2, -1, ENDIANNESS_LITTLE>>(*this, spacenum); break; + case 0x1000|0x100|32|(4-1): memory.allocate<address_space_specific<1, 2, -1, ENDIANNESS_BIG >>(*this, spacenum); break; + + case 0x0000|0x000|32|(4-2): memory.allocate<address_space_specific<0, 2, -2, ENDIANNESS_LITTLE>>(*this, spacenum); break; + case 0x1000|0x000|32|(4-2): memory.allocate<address_space_specific<0, 2, -2, ENDIANNESS_BIG >>(*this, spacenum); break; + case 0x0000|0x100|32|(4-2): memory.allocate<address_space_specific<1, 2, -2, ENDIANNESS_LITTLE>>(*this, spacenum); break; + case 0x1000|0x100|32|(4-2): memory.allocate<address_space_specific<1, 2, -2, ENDIANNESS_BIG >>(*this, spacenum); break; + + case 0x0000|0x000|64|(4-0): memory.allocate<address_space_specific<0, 3, 0, ENDIANNESS_LITTLE>>(*this, spacenum); break; + case 0x1000|0x000|64|(4-0): memory.allocate<address_space_specific<0, 3, 0, ENDIANNESS_BIG >>(*this, spacenum); break; + case 0x0000|0x100|64|(4-0): memory.allocate<address_space_specific<1, 3, 0, ENDIANNESS_LITTLE>>(*this, spacenum); break; + case 0x1000|0x100|64|(4-0): memory.allocate<address_space_specific<1, 3, 0, ENDIANNESS_BIG >>(*this, spacenum); break; + + case 0x0000|0x000|64|(4-1): memory.allocate<address_space_specific<0, 3, -1, ENDIANNESS_LITTLE>>(*this, spacenum); break; + case 0x1000|0x000|64|(4-1): memory.allocate<address_space_specific<0, 3, -1, ENDIANNESS_BIG >>(*this, spacenum); break; + case 0x0000|0x100|64|(4-1): memory.allocate<address_space_specific<1, 3, -1, ENDIANNESS_LITTLE>>(*this, spacenum); break; + case 0x1000|0x100|64|(4-1): memory.allocate<address_space_specific<1, 3, -1, ENDIANNESS_BIG >>(*this, spacenum); break; + + case 0x0000|0x000|64|(4-2): memory.allocate<address_space_specific<0, 3, -2, ENDIANNESS_LITTLE>>(*this, spacenum); break; + case 0x1000|0x000|64|(4-2): memory.allocate<address_space_specific<0, 3, -2, ENDIANNESS_BIG >>(*this, spacenum); break; + case 0x0000|0x100|64|(4-2): memory.allocate<address_space_specific<1, 3, -2, ENDIANNESS_LITTLE>>(*this, spacenum); break; + case 0x1000|0x100|64|(4-2): memory.allocate<address_space_specific<1, 3, -2, ENDIANNESS_BIG >>(*this, spacenum); break; + + case 0x0000|0x000|64|(4-3): memory.allocate<address_space_specific<0, 3, -3, ENDIANNESS_LITTLE>>(*this, spacenum); break; + case 0x1000|0x000|64|(4-3): memory.allocate<address_space_specific<0, 3, -3, ENDIANNESS_BIG >>(*this, spacenum); break; + case 0x0000|0x100|64|(4-3): memory.allocate<address_space_specific<1, 3, -3, ENDIANNESS_LITTLE>>(*this, spacenum); break; + case 0x1000|0x100|64|(4-3): memory.allocate<address_space_specific<1, 3, -3, ENDIANNESS_BIG >>(*this, spacenum); break; + + default: + throw emu_fatalerror("Invalid width %d/shift %d specified for address_space::allocate", spaceconfig->data_width(), spaceconfig->addr_shift()); + } + } + } +} + +//************************************************************************** +// ADDRESS SPACE +//************************************************************************** + +//------------------------------------------------- +// address_space - constructor +//------------------------------------------------- + +address_space::address_space(memory_manager &manager, device_memory_interface &memory, int spacenum) + : address_space_installer(*memory.space_config(spacenum), manager), + m_device(memory.device()), + m_unmap(0), + m_spacenum(spacenum), + m_log_unmap(true), + m_name(memory.space_config(spacenum)->name()), + m_in_notification(0), + m_default_mpl(make_mph(nullptr)) +{ +} + + +//------------------------------------------------- +// ~address_space - destructor +//------------------------------------------------- + +address_space::~address_space() +{ + m_unmap_r->unref(); + m_unmap_w->unref(); + m_nop_r->unref(); + m_nop_w->unref(); +} + + +//------------------------------------------------- +// prepare_map_generic - walk through an address +// map to find implicit memory regions and +// identify shared regions +//------------------------------------------------- + +void address_space::prepare_map_generic(address_map &map, bool allow_alloc) +{ + memory_region *devregion = (m_spacenum == 0) ? m_device.memregion(DEVICE_SELF) : nullptr; + u32 devregionsize = (devregion != nullptr) ? devregion->bytes() : 0; + + // make a pass over the address map, adjusting for the device and getting memory pointers + for (address_map_entry &entry : map.m_entrylist) + { + // computed adjusted addresses first + adjust_addresses(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror); + + // if we have a share entry, add it to our map + if (entry.m_share != nullptr) + { + // if we can't find it, add it to our map if we're allowed to + std::string fulltag = entry.m_devbase.subtag(entry.m_share); + memory_share *share = m_manager.share_find(fulltag); + if (!share) + { + if (!allow_alloc) + fatalerror("Trying to create share '%s' too late\n", fulltag); + VPRINTF("Creating share '%s' of length 0x%X\n", fulltag, entry.m_addrend + 1 - entry.m_addrstart); + share = m_manager.share_alloc(m_device, fulltag, m_config.data_width(), address_to_byte(entry.m_addrend + 1 - entry.m_addrstart), endianness()); + } + else + { + std::string result = share->compare(m_config.data_width(), address_to_byte(entry.m_addrend + 1 - entry.m_addrstart), endianness()); + if (!result.empty()) + fatalerror("%s\n", result); + } + entry.m_memory = share->ptr(); + } + + // if this is a ROM handler without a specified region and not shared, attach it to the implicit region + if (m_spacenum == AS_PROGRAM && entry.m_read.m_type == AMH_ROM && entry.m_region == nullptr && entry.m_share == nullptr) + { + // make sure it fits within the memory region before doing so, however + if (entry.m_addrend < devregionsize) + { + entry.m_region = m_device.tag(); + entry.m_rgnoffs = address_to_byte(entry.m_addrstart); + } + } + + // validate adjusted addresses against implicit regions + if (entry.m_region != nullptr) + { + // determine full tag + std::string fulltag = entry.m_devbase.subtag(entry.m_region); + + // find the region + memory_region *region = m_manager.machine().root_device().memregion(fulltag); + if (region == nullptr) + fatalerror("device '%s' %s space memory map entry %X-%X references nonexistent region \"%s\"\n", m_device.tag(), m_name, entry.m_addrstart, entry.m_addrend, entry.m_region); + + // validate the region + if (entry.m_rgnoffs + m_config.addr2byte(entry.m_addrend - entry.m_addrstart + 1) > region->bytes()) + fatalerror("device '%s' %s space memory map entry %X-%X extends beyond region \"%s\" size (%X)\n", m_device.tag(), m_name, entry.m_addrstart, entry.m_addrend, entry.m_region, region->bytes()); + + if (entry.m_share != nullptr) + fatalerror("device '%s' %s space memory map entry %X-%X has both .region() and .share()\n", m_device.tag(), m_name, entry.m_addrstart, entry.m_addrend); + } + + // convert any region-relative entries to their memory pointers + if (entry.m_region != nullptr) + { + // determine full tag + std::string fulltag = entry.m_devbase.subtag(entry.m_region); + + // set the memory address + entry.m_memory = m_manager.machine().root_device().memregion(fulltag)->base() + entry.m_rgnoffs; + } + + // allocate anonymous ram when needed + if (!entry.m_memory && (entry.m_read.m_type == AMH_RAM || entry.m_write.m_type == AMH_RAM)) + { + if (!allow_alloc) + fatalerror("Trying to create memory in range %X-%X too late\n", entry.m_addrstart, entry.m_addrend); + + entry.m_memory = m_manager.anonymous_alloc(*this, address_to_byte(entry.m_addrend + 1 - entry.m_addrstart), m_config.data_width(), entry.m_addrstart, entry.m_addrend); + } + } +} + + +//------------------------------------------------- +// prepare_map - allocate the address map and +// walk through it to find implicit memory regions +// and identify shared regions +//------------------------------------------------- + +void address_space::prepare_map() +{ + // allocate the address map + m_map = std::make_unique<address_map>(m_device, m_spacenum); + + // merge in the submaps + m_map->import_submaps(m_manager.machine(), m_device.owner() ? *m_device.owner() : m_device, data_width(), endianness(), addr_shift()); + + // extract global parameters specified by the map + m_unmap = (m_map->m_unmapval == 0) ? 0 : ~0; + if (m_map->m_globalmask != 0) + { + if (m_map->m_globalmask & ~m_addrmask) + fatalerror("Can't set a global address mask of %08x on a %d-bits address width bus.\n", m_map->m_globalmask, addr_width()); + + m_addrmask = m_map->m_globalmask; + } + + prepare_map_generic(*m_map, true); +} + +//------------------------------------------------- +// prepare_device_map - check and walk through a +// device-provided to to dynamically install to +// find implicit memory regions and identify +// shared regions +//------------------------------------------------- + +void address_space::prepare_device_map(address_map &map) +{ + // Disable the test for now, some cleanup needed before + if(0) { + // device maps are not supposed to set global parameters + if (map.m_unmapval) + fatalerror("Device maps should not set the unmap value\n"); + + if (map.m_globalmask && map.m_globalmask != m_addrmask) + fatalerror("Device maps should not set the global mask\n"); + } + + prepare_map_generic(map, false); +} + + +//------------------------------------------------- +// populate_from_map - walk the map in reverse +// order and install the appropriate handler for +// each case +//------------------------------------------------- + +void address_space::populate_from_map(address_map *map) +{ + // no map specified, use the space-specific one + if (map == nullptr) + map = m_map.get(); + + // no map, nothing to do + if (map == nullptr) + return; + + // install the handlers, using the original, unadjusted memory map + for (const address_map_entry &entry : map->m_entrylist) { + // map both read and write halves + populate_map_entry(entry, read_or_write::READ); + populate_map_entry(entry, read_or_write::WRITE); + } + + if (VALIDATE_REFCOUNTS) + validate_reference_counts(); +} + +//------------------------------------------------- +// get_handler_string - return a string +// describing the handler at a particular offset +//------------------------------------------------- + +template<int Level, int Width, int AddrShift, endianness_t Endian> std::string address_space_specific<Level, Width, AddrShift, Endian>::get_handler_string(read_or_write readorwrite, offs_t address) const +{ + if (readorwrite == read_or_write::READ) { + offs_t start, end; + handler_entry_read<Width, AddrShift> *handler; + m_root_read->lookup(address, start, end, handler); + return handler->name(); + } else { + offs_t start, end; + handler_entry_write<Width, AddrShift> *handler; + m_root_write->lookup(address, start, end, handler); + return handler->name(); + } +} + +template<int Level, int Width, int AddrShift, endianness_t Endian> void address_space_specific<Level, Width, AddrShift, Endian>::dump_maps(std::vector<memory_entry> &read_map, std::vector<memory_entry> &write_map) const +{ + read_map.clear(); + write_map.clear(); + m_root_read->dump_map(read_map); + m_root_write->dump_map(write_map); +} + + +//************************************************************************** +// DYNAMIC ADDRESS SPACE MAPPING +//************************************************************************** + +//------------------------------------------------- +// unmap - unmap a section of address space +//------------------------------------------------- + +template<int Level, int Width, int AddrShift, endianness_t Endian> void address_space_specific<Level, Width, AddrShift, Endian>::unmap_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, read_or_write readorwrite, bool quiet) +{ + VPRINTF("address_space::unmap(%*x-%*x mirror=%*x, %s, %s)\n", + m_addrchars, addrstart, m_addrchars, addrend, + m_addrchars, addrmirror, + (readorwrite == read_or_write::READ) ? "read" : (readorwrite == read_or_write::WRITE) ? "write" : (readorwrite == read_or_write::READWRITE) ? "read/write" : "??", + quiet ? "quiet" : "normal"); + + offs_t nstart, nend, nmask, nmirror; + check_optimize_mirror("unmap_generic", addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror); + + // read space + if (readorwrite == read_or_write::READ || readorwrite == read_or_write::READWRITE) { + auto handler = flags ? (quiet ? + static_cast<handler_entry_read<Width, AddrShift> *>(new handler_entry_read_nop<Width, AddrShift>(this, flags)) : + static_cast<handler_entry_read<Width, AddrShift> *>(new handler_entry_read_unmapped<Width, AddrShift>(this, flags))) + : (quiet ? static_cast<handler_entry_read<Width, AddrShift> *>(m_nop_r) : + static_cast<handler_entry_read<Width, AddrShift> *>(m_unmap_r)); + handler->ref(); + m_root_read->populate(nstart, nend, nmirror, handler); + } + + // write space + if (readorwrite == read_or_write::WRITE || readorwrite == read_or_write::READWRITE) { + auto handler = flags ? (quiet ? + static_cast<handler_entry_write<Width, AddrShift> *>(new handler_entry_write_nop<Width, AddrShift>(this, flags)) : + static_cast<handler_entry_write<Width, AddrShift> *>(new handler_entry_write_unmapped<Width, AddrShift>(this, flags))) + : (quiet ? static_cast<handler_entry_write<Width, AddrShift> *>(m_nop_w) : + static_cast<handler_entry_write<Width, AddrShift> *>(m_unmap_w)); + handler->ref(); + m_root_write->populate(nstart, nend, nmirror, handler); + } + + invalidate_caches(readorwrite); +} + +//------------------------------------------------- +// install_view - install a view on the bus +//------------------------------------------------- +template<int Level, int Width, int AddrShift, endianness_t Endian> void address_space_specific<Level, Width, AddrShift, Endian>::install_view(offs_t addrstart, offs_t addrend, offs_t addrmirror, memory_view &view) +{ + offs_t nstart, nend, nmask, nmirror; + check_optimize_mirror("install_view", addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror); + + auto handlers = view.make_handlers(*this, addrstart, addrend); + m_root_read ->populate(nstart, nend, nmirror, static_cast<handler_entry_read <Width, AddrShift> *>(handlers.first)); + m_root_write->populate(nstart, nend, nmirror, static_cast<handler_entry_write<Width, AddrShift> *>(handlers.second)); + view.make_subdispatch(""); // Must be called after populate +} + +std::shared_ptr<emu::detail::memory_passthrough_handler_impl> address_space::make_mph(memory_passthrough_handler *mph) +{ + if (mph) + { + auto impl(mph->m_impl.lock()); + if (impl) + { + assert(&impl->m_space == this); + return impl; + } + } + return m_mphs.emplace_back(std::make_shared<emu::detail::memory_passthrough_handler_impl>(*this)); +} + +//------------------------------------------------- +// install_read_tap - install a read tap on the bus +//------------------------------------------------- + +template<int Level, int Width, int AddrShift, endianness_t Endian> memory_passthrough_handler address_space_specific<Level, Width, AddrShift, Endian>::install_read_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, uX &data, uX mem_mask)> tap, memory_passthrough_handler *mph) +{ + offs_t nstart, nend, nmask, nmirror; + check_optimize_mirror("install_read_tap", addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror); + auto impl = make_mph(mph); + + auto handler = new handler_entry_read_tap<Width, AddrShift>(this, *impl, name, tap); + m_root_read->populate_passthrough(nstart, nend, nmirror, handler); + handler->unref(); + + invalidate_caches(read_or_write::READ); + + return impl; +} + +//------------------------------------------------- +// install_write_tap - install a write tap on the bus +//------------------------------------------------- + +template<int Level, int Width, int AddrShift, endianness_t Endian> memory_passthrough_handler address_space_specific<Level, Width, AddrShift, Endian>::install_write_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, uX &data, uX mem_mask)> tap, memory_passthrough_handler *mph) +{ + offs_t nstart, nend, nmask, nmirror; + check_optimize_mirror("install_write_tap", addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror); + auto impl = make_mph(mph); + + auto handler = new handler_entry_write_tap<Width, AddrShift>(this, *impl, name, tap); + m_root_write->populate_passthrough(nstart, nend, nmirror, handler); + handler->unref(); + + invalidate_caches(read_or_write::WRITE); + + return impl; +} +//------------------------------------------------- +// install_write_tap - install a read and a write tap on the bus +//------------------------------------------------- + +template<int Level, int Width, int AddrShift, endianness_t Endian> memory_passthrough_handler address_space_specific<Level, Width, AddrShift, Endian>::install_readwrite_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, uX &data, uX mem_mask)> tapr, std::function<void (offs_t offset, uX &data, uX mem_mask)> tapw, memory_passthrough_handler *mph) +{ + offs_t nstart, nend, nmask, nmirror; + check_optimize_mirror("install_readwrite_tap", addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror); + auto impl = make_mph(mph); + + auto rhandler = new handler_entry_read_tap <Width, AddrShift>(this, *impl, name, tapr); + m_root_read ->populate_passthrough(nstart, nend, nmirror, rhandler); + rhandler->unref(); + + auto whandler = new handler_entry_write_tap<Width, AddrShift>(this, *impl, name, tapw); + m_root_write->populate_passthrough(nstart, nend, nmirror, whandler); + whandler->unref(); + + invalidate_caches(read_or_write::READWRITE); + + return impl; +} + + + + + +//------------------------------------------------- +// install_device_delegate - install the memory map +// of a live device into this address space +//------------------------------------------------- + + template<int Level, int Width, int AddrShift, endianness_t Endian> void address_space_specific<Level, Width, AddrShift, Endian>::install_device_delegate(offs_t addrstart, offs_t addrend, device_t &device, address_map_constructor &delegate, u64 unitmask, int cswidth, u16 flags) +{ + check_address("install_device_delegate", addrstart, addrend); + address_map map(*this, addrstart, addrend, unitmask, cswidth, flags, m_device, delegate); + map.import_submaps(m_manager.machine(), device, data_width(), endianness(), addr_shift()); + prepare_device_map(map); + populate_from_map(&map); +} + + + +//------------------------------------------------- +// install_readwrite_port - install a new I/O port +// handler into this address space +//------------------------------------------------- + + template<int Level, int Width, int AddrShift, endianness_t Endian> void address_space_specific<Level, Width, AddrShift, Endian>::install_readwrite_port(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, std::string rtag, std::string wtag) +{ + VPRINTF("address_space::install_readwrite_port(%*x-%*x mirror=%*x, read=\"%s\" / write=\"%s\")\n", + m_addrchars, addrstart, m_addrchars, addrend, + m_addrchars, addrmirror, + rtag.empty() ? "(none)" : rtag, wtag.empty() ? "(none)" : wtag); + + offs_t nstart, nend, nmask, nmirror; + check_optimize_mirror("install_readwrite_port", addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror); + + // read handler + if (rtag != "") + { + // find the port + ioport_port *port = device().owner()->ioport(rtag); + if (port == nullptr) + throw emu_fatalerror("Attempted to map non-existent port '%s' for read in space %s of device '%s'\n", rtag, m_name, m_device.tag()); + + // map the range and set the ioport + auto hand_r = new handler_entry_read_ioport<Width, AddrShift>(this, flags, port); + m_root_read->populate(nstart, nend, nmirror, hand_r); + } + + if (wtag != "") + { + // find the port + ioport_port *port = device().owner()->ioport(wtag); + if (port == nullptr) + fatalerror("Attempted to map non-existent port '%s' for write in space %s of device '%s'\n", wtag, m_name, m_device.tag()); + + // map the range and set the ioport + auto hand_w = new handler_entry_write_ioport<Width, AddrShift>(this, flags, port); + m_root_write->populate(nstart, nend, nmirror, hand_w); + } + + invalidate_caches(rtag != "" ? wtag != "" ? read_or_write::READWRITE : read_or_write::READ : read_or_write::WRITE); +} + + +//------------------------------------------------- +// install the wait state handlers +//------------------------------------------------- + + +template<int Level, int Width, int AddrShift, endianness_t Endian> void address_space_specific<Level, Width, AddrShift, Endian>::install_read_before_time(offs_t addrstart, offs_t addrend, offs_t addrmirror, ws_time_delegate ws) +{ + auto *cpu = dynamic_cast<cpu_device *>(&m_device); + if (!cpu) + fatalerror("Attempted to a waitstate handler on non-cpu device '%s'\n", m_device.tag()); + if (!cpu->cpu_is_interruptible()) + fatalerror("Attempted to a waitstate handler on non-interruptible cpu device '%s'\n", m_device.tag()); + + VPRINTF("address_space::install_read_before_time(%*x-%*x mirror=%*x ws=%s)\n", + m_addrchars, addrstart, m_addrchars, addrend, + m_addrchars, addrmirror, ws.name()); + + offs_t nstart, nend, nmask, nmirror; + check_optimize_mirror("install_read_before_time", addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror); + + auto hand_r = new handler_entry_read_before_time<Width, AddrShift>(this, *m_default_mpl, ws); + m_root_read->populate_passthrough(nstart, nend, nmirror, hand_r); +} + +template<int Level, int Width, int AddrShift, endianness_t Endian> void address_space_specific<Level, Width, AddrShift, Endian>::install_read_before_delay(offs_t addrstart, offs_t addrend, offs_t addrmirror, ws_delay_delegate ws) +{ + auto *cpu = dynamic_cast<cpu_device *>(&m_device); + if (!cpu) + fatalerror("Attempted to a waitstate handler on non-cpu device '%s'\n", m_device.tag()); + if (!cpu->cpu_is_interruptible()) + fatalerror("Attempted to a waitstate handler on non-interruptible cpu device '%s'\n", m_device.tag()); + + VPRINTF("address_space::install_read_before_delay(%*x-%*x mirror=%*x ws=%s)\n", + m_addrchars, addrstart, m_addrchars, addrend, + m_addrchars, addrmirror, ws.name()); + + offs_t nstart, nend, nmask, nmirror; + check_optimize_mirror("install_read_before_delay", addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror); + + auto hand_r = new handler_entry_read_before_delay<Width, AddrShift>(this, *m_default_mpl, ws); + m_root_read->populate_passthrough(nstart, nend, nmirror, hand_r); +} + +template<int Level, int Width, int AddrShift, endianness_t Endian> void address_space_specific<Level, Width, AddrShift, Endian>::install_read_after_delay(offs_t addrstart, offs_t addrend, offs_t addrmirror, ws_delay_delegate ws) +{ + auto *cpu = dynamic_cast<cpu_device *>(&m_device); + if (!cpu) + fatalerror("Attempted to a waitstate handler on non-cpu device '%s'\n", m_device.tag()); + if (!cpu->cpu_is_interruptible()) + fatalerror("Attempted to a waitstate handler on non-interruptible cpu device '%s'\n", m_device.tag()); + + VPRINTF("address_space::install_read_after_delay(%*x-%*x mirror=%*x ws=%s)\n", + m_addrchars, addrstart, m_addrchars, addrend, + m_addrchars, addrmirror, ws.name()); + + offs_t nstart, nend, nmask, nmirror; + check_optimize_mirror("install_read_after_delay", addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror); + + auto hand_r = new handler_entry_read_after_delay<Width, AddrShift>(this, *m_default_mpl, ws); + m_root_read->populate_passthrough(nstart, nend, nmirror, hand_r); +} + + + +template<int Level, int Width, int AddrShift, endianness_t Endian> void address_space_specific<Level, Width, AddrShift, Endian>::install_write_before_time(offs_t addrstart, offs_t addrend, offs_t addrmirror, ws_time_delegate ws) +{ + auto *cpu = dynamic_cast<cpu_device *>(&m_device); + if (!cpu) + fatalerror("Attempted to a waitstate handler on non-cpu device '%s'\n", m_device.tag()); + if (!cpu->cpu_is_interruptible()) + fatalerror("Attempted to a waitstate handler on non-interruptible cpu device '%s'\n", m_device.tag()); + + VPRINTF("address_space::install_write_before_time(%*x-%*x mirror=%*x ws=%s)\n", + m_addrchars, addrstart, m_addrchars, addrend, + m_addrchars, addrmirror, ws.name()); + + offs_t nstart, nend, nmask, nmirror; + check_optimize_mirror("install_write_before_time", addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror); + + auto hand_w = new handler_entry_write_before_time<Width, AddrShift>(this, *m_default_mpl, ws); + m_root_write->populate_passthrough(nstart, nend, nmirror, hand_w); +} + +template<int Level, int Width, int AddrShift, endianness_t Endian> void address_space_specific<Level, Width, AddrShift, Endian>::install_write_before_delay(offs_t addrstart, offs_t addrend, offs_t addrmirror, ws_delay_delegate ws) +{ + auto *cpu = dynamic_cast<cpu_device *>(&m_device); + if (!cpu) + fatalerror("Attempted to a waitstate handler on non-cpu device '%s'\n", m_device.tag()); + if (!cpu->cpu_is_interruptible()) + fatalerror("Attempted to a waitstate handler on non-interruptible cpu device '%s'\n", m_device.tag()); + + VPRINTF("address_space::install_write_before_delay(%*x-%*x mirror=%*x ws=%s)\n", + m_addrchars, addrstart, m_addrchars, addrend, + m_addrchars, addrmirror, ws.name()); + + offs_t nstart, nend, nmask, nmirror; + check_optimize_mirror("install_write_before_delay", addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror); + + auto hand_w = new handler_entry_write_before_delay<Width, AddrShift>(this, *m_default_mpl, ws); + m_root_write->populate_passthrough(nstart, nend, nmirror, hand_w); +} + +template<int Level, int Width, int AddrShift, endianness_t Endian> void address_space_specific<Level, Width, AddrShift, Endian>::install_write_after_delay(offs_t addrstart, offs_t addrend, offs_t addrmirror, ws_delay_delegate ws) +{ + auto *cpu = dynamic_cast<cpu_device *>(&m_device); + if (!cpu) + fatalerror("Attempted to a waitstate handler on non-cpu device '%s'\n", m_device.tag()); + if (!cpu->cpu_is_interruptible()) + fatalerror("Attempted to a waitstate handler on non-interruptible cpu device '%s'\n", m_device.tag()); + + VPRINTF("address_space::install_write_after_delay(%*x-%*x mirror=%*x ws=%s)\n", + m_addrchars, addrstart, m_addrchars, addrend, + m_addrchars, addrmirror, ws.name()); + + offs_t nstart, nend, nmask, nmirror; + check_optimize_mirror("install_write_after_delay", addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror); + + auto hand_w = new handler_entry_write_after_delay<Width, AddrShift>(this, *m_default_mpl, ws); + m_root_write->populate_passthrough(nstart, nend, nmirror, hand_w); +} + + +template<int Level, int Width, int AddrShift, endianness_t Endian> void address_space_specific<Level, Width, AddrShift, Endian>::install_readwrite_before_time(offs_t addrstart, offs_t addrend, offs_t addrmirror, ws_time_delegate ws) +{ + auto *cpu = dynamic_cast<cpu_device *>(&m_device); + if (!cpu) + fatalerror("Attempted to a waitstate handler on non-cpu device '%s'\n", m_device.tag()); + if (!cpu->cpu_is_interruptible()) + fatalerror("Attempted to a waitstate handler on non-interruptible cpu device '%s'\n", m_device.tag()); + + VPRINTF("address_space::install_readwrite_before_time(%*x-%*x mirror=%*x ws=%s)\n", + m_addrchars, addrstart, m_addrchars, addrend, + m_addrchars, addrmirror, ws.name()); + + offs_t nstart, nend, nmask, nmirror; + check_optimize_mirror("install_readwrite_before_time", addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror); + + auto hand_r = new handler_entry_read_before_time<Width, AddrShift>(this, *m_default_mpl, ws); + m_root_read->populate_passthrough(nstart, nend, nmirror, hand_r); + auto hand_w = new handler_entry_write_before_time<Width, AddrShift>(this, *m_default_mpl, ws); + m_root_write->populate_passthrough(nstart, nend, nmirror, hand_w); +} + +template<int Level, int Width, int AddrShift, endianness_t Endian> void address_space_specific<Level, Width, AddrShift, Endian>::install_readwrite_before_delay(offs_t addrstart, offs_t addrend, offs_t addrmirror, ws_delay_delegate ws) +{ + auto *cpu = dynamic_cast<cpu_device *>(&m_device); + if (!cpu) + fatalerror("Attempted to a waitstate handler on non-cpu device '%s'\n", m_device.tag()); + if (!cpu->cpu_is_interruptible()) + fatalerror("Attempted to a waitstate handler on non-interruptible cpu device '%s'\n", m_device.tag()); + + VPRINTF("address_space::install_readwrite_before_delay(%*x-%*x mirror=%*x ws=%s)\n", + m_addrchars, addrstart, m_addrchars, addrend, + m_addrchars, addrmirror, ws.name()); + + offs_t nstart, nend, nmask, nmirror; + check_optimize_mirror("install_readwrite_before_delay", addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror); + + auto hand_r = new handler_entry_read_before_delay<Width, AddrShift>(this, *m_default_mpl, ws); + m_root_read->populate_passthrough(nstart, nend, nmirror, hand_r); + auto hand_w = new handler_entry_write_before_delay<Width, AddrShift>(this, *m_default_mpl, ws); + m_root_write->populate_passthrough(nstart, nend, nmirror, hand_w); +} + +template<int Level, int Width, int AddrShift, endianness_t Endian> void address_space_specific<Level, Width, AddrShift, Endian>::install_readwrite_after_delay(offs_t addrstart, offs_t addrend, offs_t addrmirror, ws_delay_delegate ws) +{ + auto *cpu = dynamic_cast<cpu_device *>(&m_device); + if (!cpu) + fatalerror("Attempted to a waitstate handler on non-cpu device '%s'\n", m_device.tag()); + if (!cpu->cpu_is_interruptible()) + fatalerror("Attempted to a waitstate handler on non-interruptible cpu device '%s'\n", m_device.tag()); + + VPRINTF("address_space::install_readwrite_after_delay(%*x-%*x mirror=%*x ws=%s)\n", + m_addrchars, addrstart, m_addrchars, addrend, + m_addrchars, addrmirror, ws.name()); + + offs_t nstart, nend, nmask, nmirror; + check_optimize_mirror("install_readwrite_after_delay", addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror); + + auto hand_r = new handler_entry_read_after_delay<Width, AddrShift>(this, *m_default_mpl, ws); + m_root_read->populate_passthrough(nstart, nend, nmirror, hand_r); + auto hand_w = new handler_entry_write_after_delay<Width, AddrShift>(this, *m_default_mpl, ws); + m_root_write->populate_passthrough(nstart, nend, nmirror, hand_w); +} + + + + +//------------------------------------------------- +// install_bank_generic - install a range as +// mapping to a particular bank +//------------------------------------------------- + +template<int Level, int Width, int AddrShift, endianness_t Endian> void address_space_specific<Level, Width, AddrShift, Endian>::install_bank_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, memory_bank *rbank, memory_bank *wbank) +{ + VPRINTF("address_space::install_readwrite_bank(%*x-%*x mirror=%*x, read=\"%s\" / write=\"%s\")\n", + m_addrchars, addrstart, m_addrchars, addrend, + m_addrchars, addrmirror, + (rbank != nullptr) ? rbank->tag() : "(none)", (wbank != nullptr) ? wbank->tag() : "(none)"); + + offs_t nstart, nend, nmask, nmirror; + check_optimize_mirror("install_bank_generic", addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror); + + // map the read bank + if (rbank != nullptr) + { + auto hand_r = new handler_entry_read_memory_bank<Width, AddrShift>(this, flags, *rbank); + hand_r->set_address_info(nstart, nmask); + m_root_read->populate(nstart, nend, nmirror, hand_r); + } + + // map the write bank + if (wbank != nullptr) + { + auto hand_w = new handler_entry_write_memory_bank<Width, AddrShift>(this, flags, *wbank); + hand_w->set_address_info(nstart, nmask); + m_root_write->populate(nstart, nend, nmirror, hand_w); + } + + invalidate_caches(rbank ? wbank ? read_or_write::READWRITE : read_or_write::READ : read_or_write::WRITE); +} + + +//------------------------------------------------- +// install_ram_generic - install a simple fixed +// RAM region into the given address space +//------------------------------------------------- + +template<int Level, int Width, int AddrShift, endianness_t Endian> void address_space_specific<Level, Width, AddrShift, Endian>::install_ram_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, read_or_write readorwrite, void *baseptr) +{ + VPRINTF("address_space::install_ram_generic(%s-%s mirror=%s, %s, %p)\n", + m_addrchars, addrstart, m_addrchars, addrend, + m_addrchars, addrmirror, + (readorwrite == read_or_write::READ) ? "read" : (readorwrite == read_or_write::WRITE) ? "write" : (readorwrite == read_or_write::READWRITE) ? "read/write" : "??", + baseptr); + + offs_t nstart, nend, nmask, nmirror; + check_optimize_mirror("install_ram_generic", addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror); + + // map for read + if (readorwrite == read_or_write::READ || readorwrite == read_or_write::READWRITE) + { + auto hand_r = new handler_entry_read_memory<Width, AddrShift>(this, flags, baseptr); + hand_r->set_address_info(nstart, nmask); + m_root_read->populate(nstart, nend, nmirror, hand_r); + } + + // map for write + if (readorwrite == read_or_write::WRITE || readorwrite == read_or_write::READWRITE) + { + auto hand_w = new handler_entry_write_memory<Width, AddrShift>(this, flags, baseptr); + hand_w->set_address_info(nstart, nmask); + m_root_write->populate(nstart, nend, nmirror, hand_w); + } + + invalidate_caches(readorwrite); +} + +//************************************************************************** +// MEMORY MAPPING HELPERS +//************************************************************************** + +util::notifier_subscription address_space::add_change_notifier(delegate<void (read_or_write)> &&n) +{ + return m_notifiers.subscribe(std::move(n)); +} diff --git a/src/emu/emumem_hea.h b/src/emu/emumem_hea.h index e19a04b4613..9ee6f8c8a1e 100644 --- a/src/emu/emumem_hea.h +++ b/src/emu/emumem_hea.h @@ -5,16 +5,16 @@ // parent class for final handlers which want an address base and a mask -template<int Width, int AddrShift, int Endian> class handler_entry_read_address : public handler_entry_read<Width, AddrShift, Endian> +template<int Width, int AddrShift> class handler_entry_read_address : public handler_entry_read<Width, AddrShift> { public: - using uX = typename emu::detail::handler_entry_size<Width>::uX; + using uX = emu::detail::handler_entry_size_t<Width>; - handler_entry_read_address(address_space *space, u32 flags) : handler_entry_read<Width, AddrShift, Endian>(space, flags) {} + handler_entry_read_address(address_space *space, u32 flags) : handler_entry_read<Width, AddrShift>(space, flags) {} ~handler_entry_read_address() = default; inline void set_address_info(offs_t base, offs_t mask) { - m_address_base = base; + m_address_base = base & ~handler_entry_read<Width, AddrShift>::NATIVE_MASK; m_address_mask = mask; } @@ -22,16 +22,18 @@ protected: offs_t m_address_base, m_address_mask; }; -template<int Width, int AddrShift, int Endian> class handler_entry_write_address : public handler_entry_write<Width, AddrShift, Endian> +template<int Width, int AddrShift> class handler_entry_write_address : public handler_entry_write<Width, AddrShift> { public: - using uX = typename emu::detail::handler_entry_size<Width>::uX; + using uX = emu::detail::handler_entry_size_t<Width>; - handler_entry_write_address(address_space *space, u32 flags) : handler_entry_write<Width, AddrShift, Endian>(space, flags) {} + static constexpr u32 NATIVE_MASK = Width + AddrShift >= 0 ? (1 << (Width + AddrShift)) - 1 : 0; + + handler_entry_write_address(address_space *space, u32 flags) : handler_entry_write<Width, AddrShift>(space, flags) {} ~handler_entry_write_address() = default; inline void set_address_info(offs_t base, offs_t mask) { - m_address_base = base; + m_address_base = base & ~handler_entry_write<Width, AddrShift>::NATIVE_MASK; m_address_mask = mask; } diff --git a/src/emu/emumem_hedp.cpp b/src/emu/emumem_hedp.cpp index ccaa4ed650e..b053f84c592 100644 --- a/src/emu/emumem_hedp.cpp +++ b/src/emu/emumem_hedp.cpp @@ -5,142 +5,173 @@ #include "emumem_hea.h" #include "emumem_hedp.h" -template<int Width, int AddrShift, int Endian, typename READ> template<typename R> +template<int Width, int AddrShift, typename READ> template<typename R> std::enable_if_t<std::is_same<R, read8_delegate>::value || std::is_same<R, read16_delegate>::value || std::is_same<R, read32_delegate>::value || std::is_same<R, read64_delegate>::value, - typename emu::detail::handler_entry_size<Width>::uX> handler_entry_read_delegate<Width, AddrShift, Endian, READ>::read_impl(offs_t offset, uX mem_mask) + emu::detail::handler_entry_size_t<Width> > handler_entry_read_delegate<Width, AddrShift, READ>::read_impl(offs_t offset, uX mem_mask) const { - return m_delegate(*inh::m_space, ((offset - inh::m_address_base) & inh::m_address_mask) >> (Width + AddrShift), mem_mask); + return m_delegate(*this->m_space, ((offset - this->m_address_base) & this->m_address_mask) >> (Width + AddrShift), mem_mask); } -template<int Width, int AddrShift, int Endian, typename READ> template<typename R> +template<int Width, int AddrShift, typename READ> template<typename R> std::enable_if_t<std::is_same<R, read8m_delegate>::value || std::is_same<R, read16m_delegate>::value || std::is_same<R, read32m_delegate>::value || std::is_same<R, read64m_delegate>::value, - typename emu::detail::handler_entry_size<Width>::uX> handler_entry_read_delegate<Width, AddrShift, Endian, READ>::read_impl(offs_t offset, uX mem_mask) + emu::detail::handler_entry_size_t<Width> > handler_entry_read_delegate<Width, AddrShift, READ>::read_impl(offs_t offset, uX mem_mask) const { - return m_delegate(*inh::m_space, ((offset - inh::m_address_base) & inh::m_address_mask) >> (Width + AddrShift)); + return m_delegate(*this->m_space, ((offset - this->m_address_base) & this->m_address_mask) >> (Width + AddrShift)); } -template<int Width, int AddrShift, int Endian, typename READ> template<typename R> +template<int Width, int AddrShift, typename READ> template<typename R> std::enable_if_t<std::is_same<R, read8s_delegate>::value || std::is_same<R, read16s_delegate>::value || std::is_same<R, read32s_delegate>::value || std::is_same<R, read64s_delegate>::value, - typename emu::detail::handler_entry_size<Width>::uX> handler_entry_read_delegate<Width, AddrShift, Endian, READ>::read_impl(offs_t offset, uX mem_mask) + emu::detail::handler_entry_size_t<Width> > handler_entry_read_delegate<Width, AddrShift, READ>::read_impl(offs_t offset, uX mem_mask) const { - return m_delegate(((offset - inh::m_address_base) & inh::m_address_mask) >> (Width + AddrShift), mem_mask); + return m_delegate(((offset - this->m_address_base) & this->m_address_mask) >> (Width + AddrShift), mem_mask); } -template<int Width, int AddrShift, int Endian, typename READ> template<typename R> +template<int Width, int AddrShift, typename READ> template<typename R> std::enable_if_t<std::is_same<R, read8sm_delegate>::value || std::is_same<R, read16sm_delegate>::value || std::is_same<R, read32sm_delegate>::value || std::is_same<R, read64sm_delegate>::value, - typename emu::detail::handler_entry_size<Width>::uX> handler_entry_read_delegate<Width, AddrShift, Endian, READ>::read_impl(offs_t offset, uX mem_mask) + emu::detail::handler_entry_size_t<Width> > handler_entry_read_delegate<Width, AddrShift, READ>::read_impl(offs_t offset, uX mem_mask) const { - return m_delegate(((offset - inh::m_address_base) & inh::m_address_mask) >> (Width + AddrShift)); + return m_delegate(((offset - this->m_address_base) & this->m_address_mask) >> (Width + AddrShift)); } -template<int Width, int AddrShift, int Endian, typename READ> template<typename R> +template<int Width, int AddrShift, typename READ> template<typename R> std::enable_if_t<std::is_same<R, read8mo_delegate>::value || std::is_same<R, read16mo_delegate>::value || std::is_same<R, read32mo_delegate>::value || std::is_same<R, read64mo_delegate>::value, - typename emu::detail::handler_entry_size<Width>::uX> handler_entry_read_delegate<Width, AddrShift, Endian, READ>::read_impl(offs_t offset, uX mem_mask) + emu::detail::handler_entry_size_t<Width> > handler_entry_read_delegate<Width, AddrShift, READ>::read_impl(offs_t offset, uX mem_mask) const { - return m_delegate(*inh::m_space); + return m_delegate(*this->m_space); } -template<int Width, int AddrShift, int Endian, typename READ> template<typename R> +template<int Width, int AddrShift, typename READ> template<typename R> std::enable_if_t<std::is_same<R, read8smo_delegate>::value || std::is_same<R, read16smo_delegate>::value || std::is_same<R, read32smo_delegate>::value || std::is_same<R, read64smo_delegate>::value, - typename emu::detail::handler_entry_size<Width>::uX> handler_entry_read_delegate<Width, AddrShift, Endian, READ>::read_impl(offs_t offset, uX mem_mask) + emu::detail::handler_entry_size_t<Width> > handler_entry_read_delegate<Width, AddrShift, READ>::read_impl(offs_t offset, uX mem_mask) const { return m_delegate(); } -template<int Width, int AddrShift, int Endian, typename READ> typename emu::detail::handler_entry_size<Width>::uX handler_entry_read_delegate<Width, AddrShift, Endian, READ>::read(offs_t offset, uX mem_mask) +template<int Width, int AddrShift, typename READ> emu::detail::handler_entry_size_t<Width> handler_entry_read_delegate<Width, AddrShift, READ>::read(offs_t offset, uX mem_mask) const { return read_impl<READ>(offset, mem_mask); } -template<int Width, int AddrShift, int Endian, typename READ> std::string handler_entry_read_delegate<Width, AddrShift, Endian, READ>::name() const +template<int Width, int AddrShift, typename READ> emu::detail::handler_entry_size_t<Width> handler_entry_read_delegate<Width, AddrShift, READ>::read_interruptible(offs_t offset, uX mem_mask) const +{ + return read_impl<READ>(offset, mem_mask); +} + +template<int Width, int AddrShift, typename READ> std::pair<emu::detail::handler_entry_size_t<Width>, u16> handler_entry_read_delegate<Width, AddrShift, READ>::read_flags(offs_t offset, uX mem_mask) const +{ + return std::pair<uX, u16>(read_impl<READ>(offset, mem_mask), this->m_flags); +} + +template<int Width, int AddrShift, typename READ> u16 handler_entry_read_delegate<Width, AddrShift, READ>::lookup_flags(offs_t offset, uX mem_mask) const +{ + return this->m_flags; +} + +template<int Width, int AddrShift, typename READ> std::string handler_entry_read_delegate<Width, AddrShift, READ>::name() const { return m_delegate.name(); } -template<int Width, int AddrShift, int Endian, typename WRITE> template<typename W> +template<int Width, int AddrShift, typename WRITE> template<typename W> std::enable_if_t<std::is_same<W, write8_delegate>::value || std::is_same<W, write16_delegate>::value || std::is_same<W, write32_delegate>::value || std::is_same<W, write64_delegate>::value, - void> handler_entry_write_delegate<Width, AddrShift, Endian, WRITE>::write_impl(offs_t offset, uX data, uX mem_mask) + void> handler_entry_write_delegate<Width, AddrShift, WRITE>::write_impl(offs_t offset, uX data, uX mem_mask) const { - m_delegate(*inh::m_space, ((offset - inh::m_address_base) & inh::m_address_mask) >> (Width + AddrShift), data, mem_mask); + m_delegate(*this->m_space, ((offset - this->m_address_base) & this->m_address_mask) >> (Width + AddrShift), data, mem_mask); } -template<int Width, int AddrShift, int Endian, typename WRITE> template<typename W> +template<int Width, int AddrShift, typename WRITE> template<typename W> std::enable_if_t<std::is_same<W, write8m_delegate>::value || std::is_same<W, write16m_delegate>::value || std::is_same<W, write32m_delegate>::value || std::is_same<W, write64m_delegate>::value, - void> handler_entry_write_delegate<Width, AddrShift, Endian, WRITE>::write_impl(offs_t offset, uX data, uX mem_mask) + void> handler_entry_write_delegate<Width, AddrShift, WRITE>::write_impl(offs_t offset, uX data, uX mem_mask) const { - m_delegate(*inh::m_space, ((offset - inh::m_address_base) & inh::m_address_mask) >> (Width + AddrShift), data); + m_delegate(*this->m_space, ((offset - this->m_address_base) & this->m_address_mask) >> (Width + AddrShift), data); } -template<int Width, int AddrShift, int Endian, typename WRITE> template<typename W> +template<int Width, int AddrShift, typename WRITE> template<typename W> std::enable_if_t<std::is_same<W, write8s_delegate>::value || std::is_same<W, write16s_delegate>::value || std::is_same<W, write32s_delegate>::value || std::is_same<W, write64s_delegate>::value, - void> handler_entry_write_delegate<Width, AddrShift, Endian, WRITE>::write_impl(offs_t offset, uX data, uX mem_mask) + void> handler_entry_write_delegate<Width, AddrShift, WRITE>::write_impl(offs_t offset, uX data, uX mem_mask) const { - m_delegate(((offset - inh::m_address_base) & inh::m_address_mask) >> (Width + AddrShift), data, mem_mask); + m_delegate(((offset - this->m_address_base) & this->m_address_mask) >> (Width + AddrShift), data, mem_mask); } -template<int Width, int AddrShift, int Endian, typename WRITE> template<typename W> +template<int Width, int AddrShift, typename WRITE> template<typename W> std::enable_if_t<std::is_same<W, write8sm_delegate>::value || std::is_same<W, write16sm_delegate>::value || std::is_same<W, write32sm_delegate>::value || std::is_same<W, write64sm_delegate>::value, - void> handler_entry_write_delegate<Width, AddrShift, Endian, WRITE>::write_impl(offs_t offset, uX data, uX mem_mask) + void> handler_entry_write_delegate<Width, AddrShift, WRITE>::write_impl(offs_t offset, uX data, uX mem_mask) const { - m_delegate(((offset - inh::m_address_base) & inh::m_address_mask) >> (Width + AddrShift), data); + m_delegate(((offset - this->m_address_base) & this->m_address_mask) >> (Width + AddrShift), data); } -template<int Width, int AddrShift, int Endian, typename WRITE> template<typename W> +template<int Width, int AddrShift, typename WRITE> template<typename W> std::enable_if_t<std::is_same<W, write8mo_delegate>::value || std::is_same<W, write16mo_delegate>::value || std::is_same<W, write32mo_delegate>::value || std::is_same<W, write64mo_delegate>::value, - void> handler_entry_write_delegate<Width, AddrShift, Endian, WRITE>::write_impl(offs_t offset, uX data, uX mem_mask) + void> handler_entry_write_delegate<Width, AddrShift, WRITE>::write_impl(offs_t offset, uX data, uX mem_mask) const { - m_delegate(*inh::m_space, data); + m_delegate(*this->m_space, data); } -template<int Width, int AddrShift, int Endian, typename WRITE> template<typename W> +template<int Width, int AddrShift, typename WRITE> template<typename W> std::enable_if_t<std::is_same<W, write8smo_delegate>::value || std::is_same<W, write16smo_delegate>::value || std::is_same<W, write32smo_delegate>::value || std::is_same<W, write64smo_delegate>::value, - void> handler_entry_write_delegate<Width, AddrShift, Endian, WRITE>::write_impl(offs_t offset, uX data, uX mem_mask) + void> handler_entry_write_delegate<Width, AddrShift, WRITE>::write_impl(offs_t offset, uX data, uX mem_mask) const { m_delegate(data); } -template<int Width, int AddrShift, int Endian, typename WRITE> void handler_entry_write_delegate<Width, AddrShift, Endian, WRITE>::write(offs_t offset, uX data, uX mem_mask) +template<int Width, int AddrShift, typename WRITE> void handler_entry_write_delegate<Width, AddrShift, WRITE>::write(offs_t offset, uX data, uX mem_mask) const { write_impl<WRITE>(offset, data, mem_mask); } -template<int Width, int AddrShift, int Endian, typename WRITE> std::string handler_entry_write_delegate<Width, AddrShift, Endian, WRITE>::name() const +template<int Width, int AddrShift, typename WRITE> void handler_entry_write_delegate<Width, AddrShift, WRITE>::write_interruptible(offs_t offset, uX data, uX mem_mask) const +{ + write_impl<WRITE>(offset, data, mem_mask); +} + +template<int Width, int AddrShift, typename WRITE> u16 handler_entry_write_delegate<Width, AddrShift, WRITE>::write_flags(offs_t offset, uX data, uX mem_mask) const +{ + write_impl<WRITE>(offset, data, mem_mask); + return this->m_flags; +} + +template<int Width, int AddrShift, typename WRITE> u16 handler_entry_write_delegate<Width, AddrShift, WRITE>::lookup_flags(offs_t offset, uX mem_mask) const +{ + return this->m_flags; +} + +template<int Width, int AddrShift, typename WRITE> std::string handler_entry_write_delegate<Width, AddrShift, WRITE>::name() const { return m_delegate.name(); } @@ -148,375 +179,252 @@ template<int Width, int AddrShift, int Endian, typename WRITE> std::string handl -template<int Width, int AddrShift, int Endian> typename emu::detail::handler_entry_size<Width>::uX handler_entry_read_ioport<Width, AddrShift, Endian>::read(offs_t offset, uX mem_mask) +template<int Width, int AddrShift> emu::detail::handler_entry_size_t<Width> handler_entry_read_ioport<Width, AddrShift>::read(offs_t offset, uX mem_mask) const { return m_port->read(); } -template<int Width, int AddrShift, int Endian> std::string handler_entry_read_ioport<Width, AddrShift, Endian>::name() const +template<int Width, int AddrShift> emu::detail::handler_entry_size_t<Width> handler_entry_read_ioport<Width, AddrShift>::read_interruptible(offs_t offset, uX mem_mask) const +{ + return m_port->read(); +} + +template<int Width, int AddrShift> std::pair<emu::detail::handler_entry_size_t<Width>, u16> handler_entry_read_ioport<Width, AddrShift>::read_flags(offs_t offset, uX mem_mask) const +{ + return std::pair<uX, u16>(m_port->read(), this->m_flags); +} + +template<int Width, int AddrShift> u16 handler_entry_read_ioport<Width, AddrShift>::lookup_flags(offs_t offset, uX mem_mask) const +{ + return this->m_flags; +} + +template<int Width, int AddrShift> std::string handler_entry_read_ioport<Width, AddrShift>::name() const { return m_port->tag(); } -template<int Width, int AddrShift, int Endian> void handler_entry_write_ioport<Width, AddrShift, Endian>::write(offs_t offset, uX data, uX mem_mask) +template<int Width, int AddrShift> void handler_entry_write_ioport<Width, AddrShift>::write(offs_t offset, uX data, uX mem_mask) const { m_port->write(data, mem_mask); } -template<int Width, int AddrShift, int Endian> std::string handler_entry_write_ioport<Width, AddrShift, Endian>::name() const +template<int Width, int AddrShift> void handler_entry_write_ioport<Width, AddrShift>::write_interruptible(offs_t offset, uX data, uX mem_mask) const +{ + m_port->write(data, mem_mask); +} + +template<int Width, int AddrShift> u16 handler_entry_write_ioport<Width, AddrShift>::write_flags(offs_t offset, uX data, uX mem_mask) const +{ + m_port->write(data, mem_mask); + return this->m_flags; +} + +template<int Width, int AddrShift> u16 handler_entry_write_ioport<Width, AddrShift>::lookup_flags(offs_t offset, uX mem_mask) const +{ + return this->m_flags; +} + +template<int Width, int AddrShift> std::string handler_entry_write_ioport<Width, AddrShift>::name() const { return m_port->tag(); } -template class handler_entry_read_delegate<0, 1, ENDIANNESS_LITTLE, read8_delegate>; -template class handler_entry_read_delegate<0, 1, ENDIANNESS_BIG, read8_delegate>; -template class handler_entry_read_delegate<0, 0, ENDIANNESS_LITTLE, read8_delegate>; -template class handler_entry_read_delegate<0, 0, ENDIANNESS_BIG, read8_delegate>; -template class handler_entry_read_delegate<1, 3, ENDIANNESS_LITTLE, read16_delegate>; -template class handler_entry_read_delegate<1, 3, ENDIANNESS_BIG, read16_delegate>; -template class handler_entry_read_delegate<1, 0, ENDIANNESS_LITTLE, read16_delegate>; -template class handler_entry_read_delegate<1, 0, ENDIANNESS_BIG, read16_delegate>; -template class handler_entry_read_delegate<1, -1, ENDIANNESS_LITTLE, read16_delegate>; -template class handler_entry_read_delegate<1, -1, ENDIANNESS_BIG, read16_delegate>; -template class handler_entry_read_delegate<2, 0, ENDIANNESS_LITTLE, read32_delegate>; -template class handler_entry_read_delegate<2, 0, ENDIANNESS_BIG, read32_delegate>; -template class handler_entry_read_delegate<2, -1, ENDIANNESS_LITTLE, read32_delegate>; -template class handler_entry_read_delegate<2, -1, ENDIANNESS_BIG, read32_delegate>; -template class handler_entry_read_delegate<2, -2, ENDIANNESS_LITTLE, read32_delegate>; -template class handler_entry_read_delegate<2, -2, ENDIANNESS_BIG, read32_delegate>; -template class handler_entry_read_delegate<3, 0, ENDIANNESS_LITTLE, read64_delegate>; -template class handler_entry_read_delegate<3, 0, ENDIANNESS_BIG, read64_delegate>; -template class handler_entry_read_delegate<3, -1, ENDIANNESS_LITTLE, read64_delegate>; -template class handler_entry_read_delegate<3, -1, ENDIANNESS_BIG, read64_delegate>; -template class handler_entry_read_delegate<3, -2, ENDIANNESS_LITTLE, read64_delegate>; -template class handler_entry_read_delegate<3, -2, ENDIANNESS_BIG, read64_delegate>; -template class handler_entry_read_delegate<3, -3, ENDIANNESS_LITTLE, read64_delegate>; -template class handler_entry_read_delegate<3, -3, ENDIANNESS_BIG, read64_delegate>; - -template class handler_entry_read_delegate<0, 1, ENDIANNESS_LITTLE, read8m_delegate>; -template class handler_entry_read_delegate<0, 1, ENDIANNESS_BIG, read8m_delegate>; -template class handler_entry_read_delegate<0, 0, ENDIANNESS_LITTLE, read8m_delegate>; -template class handler_entry_read_delegate<0, 0, ENDIANNESS_BIG, read8m_delegate>; -template class handler_entry_read_delegate<1, 3, ENDIANNESS_LITTLE, read16m_delegate>; -template class handler_entry_read_delegate<1, 3, ENDIANNESS_BIG, read16m_delegate>; -template class handler_entry_read_delegate<1, 0, ENDIANNESS_LITTLE, read16m_delegate>; -template class handler_entry_read_delegate<1, 0, ENDIANNESS_BIG, read16m_delegate>; -template class handler_entry_read_delegate<1, -1, ENDIANNESS_LITTLE, read16m_delegate>; -template class handler_entry_read_delegate<1, -1, ENDIANNESS_BIG, read16m_delegate>; -template class handler_entry_read_delegate<2, 0, ENDIANNESS_LITTLE, read32m_delegate>; -template class handler_entry_read_delegate<2, 0, ENDIANNESS_BIG, read32m_delegate>; -template class handler_entry_read_delegate<2, -1, ENDIANNESS_LITTLE, read32m_delegate>; -template class handler_entry_read_delegate<2, -1, ENDIANNESS_BIG, read32m_delegate>; -template class handler_entry_read_delegate<2, -2, ENDIANNESS_LITTLE, read32m_delegate>; -template class handler_entry_read_delegate<2, -2, ENDIANNESS_BIG, read32m_delegate>; -template class handler_entry_read_delegate<3, 0, ENDIANNESS_LITTLE, read64m_delegate>; -template class handler_entry_read_delegate<3, 0, ENDIANNESS_BIG, read64m_delegate>; -template class handler_entry_read_delegate<3, -1, ENDIANNESS_LITTLE, read64m_delegate>; -template class handler_entry_read_delegate<3, -1, ENDIANNESS_BIG, read64m_delegate>; -template class handler_entry_read_delegate<3, -2, ENDIANNESS_LITTLE, read64m_delegate>; -template class handler_entry_read_delegate<3, -2, ENDIANNESS_BIG, read64m_delegate>; -template class handler_entry_read_delegate<3, -3, ENDIANNESS_LITTLE, read64m_delegate>; -template class handler_entry_read_delegate<3, -3, ENDIANNESS_BIG, read64m_delegate>; - -template class handler_entry_read_delegate<0, 1, ENDIANNESS_LITTLE, read8s_delegate>; -template class handler_entry_read_delegate<0, 1, ENDIANNESS_BIG, read8s_delegate>; -template class handler_entry_read_delegate<0, 0, ENDIANNESS_LITTLE, read8s_delegate>; -template class handler_entry_read_delegate<0, 0, ENDIANNESS_BIG, read8s_delegate>; -template class handler_entry_read_delegate<1, 3, ENDIANNESS_LITTLE, read16s_delegate>; -template class handler_entry_read_delegate<1, 3, ENDIANNESS_BIG, read16s_delegate>; -template class handler_entry_read_delegate<1, 0, ENDIANNESS_LITTLE, read16s_delegate>; -template class handler_entry_read_delegate<1, 0, ENDIANNESS_BIG, read16s_delegate>; -template class handler_entry_read_delegate<1, -1, ENDIANNESS_LITTLE, read16s_delegate>; -template class handler_entry_read_delegate<1, -1, ENDIANNESS_BIG, read16s_delegate>; -template class handler_entry_read_delegate<2, 0, ENDIANNESS_LITTLE, read32s_delegate>; -template class handler_entry_read_delegate<2, 0, ENDIANNESS_BIG, read32s_delegate>; -template class handler_entry_read_delegate<2, -1, ENDIANNESS_LITTLE, read32s_delegate>; -template class handler_entry_read_delegate<2, -1, ENDIANNESS_BIG, read32s_delegate>; -template class handler_entry_read_delegate<2, -2, ENDIANNESS_LITTLE, read32s_delegate>; -template class handler_entry_read_delegate<2, -2, ENDIANNESS_BIG, read32s_delegate>; -template class handler_entry_read_delegate<3, 0, ENDIANNESS_LITTLE, read64s_delegate>; -template class handler_entry_read_delegate<3, 0, ENDIANNESS_BIG, read64s_delegate>; -template class handler_entry_read_delegate<3, -1, ENDIANNESS_LITTLE, read64s_delegate>; -template class handler_entry_read_delegate<3, -1, ENDIANNESS_BIG, read64s_delegate>; -template class handler_entry_read_delegate<3, -2, ENDIANNESS_LITTLE, read64s_delegate>; -template class handler_entry_read_delegate<3, -2, ENDIANNESS_BIG, read64s_delegate>; -template class handler_entry_read_delegate<3, -3, ENDIANNESS_LITTLE, read64s_delegate>; -template class handler_entry_read_delegate<3, -3, ENDIANNESS_BIG, read64s_delegate>; - -template class handler_entry_read_delegate<0, 1, ENDIANNESS_LITTLE, read8sm_delegate>; -template class handler_entry_read_delegate<0, 1, ENDIANNESS_BIG, read8sm_delegate>; -template class handler_entry_read_delegate<0, 0, ENDIANNESS_LITTLE, read8sm_delegate>; -template class handler_entry_read_delegate<0, 0, ENDIANNESS_BIG, read8sm_delegate>; -template class handler_entry_read_delegate<1, 3, ENDIANNESS_LITTLE, read16sm_delegate>; -template class handler_entry_read_delegate<1, 3, ENDIANNESS_BIG, read16sm_delegate>; -template class handler_entry_read_delegate<1, 0, ENDIANNESS_LITTLE, read16sm_delegate>; -template class handler_entry_read_delegate<1, 0, ENDIANNESS_BIG, read16sm_delegate>; -template class handler_entry_read_delegate<1, -1, ENDIANNESS_LITTLE, read16sm_delegate>; -template class handler_entry_read_delegate<1, -1, ENDIANNESS_BIG, read16sm_delegate>; -template class handler_entry_read_delegate<2, 0, ENDIANNESS_LITTLE, read32sm_delegate>; -template class handler_entry_read_delegate<2, 0, ENDIANNESS_BIG, read32sm_delegate>; -template class handler_entry_read_delegate<2, -1, ENDIANNESS_LITTLE, read32sm_delegate>; -template class handler_entry_read_delegate<2, -1, ENDIANNESS_BIG, read32sm_delegate>; -template class handler_entry_read_delegate<2, -2, ENDIANNESS_LITTLE, read32sm_delegate>; -template class handler_entry_read_delegate<2, -2, ENDIANNESS_BIG, read32sm_delegate>; -template class handler_entry_read_delegate<3, 0, ENDIANNESS_LITTLE, read64sm_delegate>; -template class handler_entry_read_delegate<3, 0, ENDIANNESS_BIG, read64sm_delegate>; -template class handler_entry_read_delegate<3, -1, ENDIANNESS_LITTLE, read64sm_delegate>; -template class handler_entry_read_delegate<3, -1, ENDIANNESS_BIG, read64sm_delegate>; -template class handler_entry_read_delegate<3, -2, ENDIANNESS_LITTLE, read64sm_delegate>; -template class handler_entry_read_delegate<3, -2, ENDIANNESS_BIG, read64sm_delegate>; -template class handler_entry_read_delegate<3, -3, ENDIANNESS_LITTLE, read64sm_delegate>; -template class handler_entry_read_delegate<3, -3, ENDIANNESS_BIG, read64sm_delegate>; - -template class handler_entry_read_delegate<0, 1, ENDIANNESS_LITTLE, read8mo_delegate>; -template class handler_entry_read_delegate<0, 1, ENDIANNESS_BIG, read8mo_delegate>; -template class handler_entry_read_delegate<0, 0, ENDIANNESS_LITTLE, read8mo_delegate>; -template class handler_entry_read_delegate<0, 0, ENDIANNESS_BIG, read8mo_delegate>; -template class handler_entry_read_delegate<1, 3, ENDIANNESS_LITTLE, read16mo_delegate>; -template class handler_entry_read_delegate<1, 3, ENDIANNESS_BIG, read16mo_delegate>; -template class handler_entry_read_delegate<1, 0, ENDIANNESS_LITTLE, read16mo_delegate>; -template class handler_entry_read_delegate<1, 0, ENDIANNESS_BIG, read16mo_delegate>; -template class handler_entry_read_delegate<1, -1, ENDIANNESS_LITTLE, read16mo_delegate>; -template class handler_entry_read_delegate<1, -1, ENDIANNESS_BIG, read16mo_delegate>; -template class handler_entry_read_delegate<2, 0, ENDIANNESS_LITTLE, read32mo_delegate>; -template class handler_entry_read_delegate<2, 0, ENDIANNESS_BIG, read32mo_delegate>; -template class handler_entry_read_delegate<2, -1, ENDIANNESS_LITTLE, read32mo_delegate>; -template class handler_entry_read_delegate<2, -1, ENDIANNESS_BIG, read32mo_delegate>; -template class handler_entry_read_delegate<2, -2, ENDIANNESS_LITTLE, read32mo_delegate>; -template class handler_entry_read_delegate<2, -2, ENDIANNESS_BIG, read32mo_delegate>; -template class handler_entry_read_delegate<3, 0, ENDIANNESS_LITTLE, read64mo_delegate>; -template class handler_entry_read_delegate<3, 0, ENDIANNESS_BIG, read64mo_delegate>; -template class handler_entry_read_delegate<3, -1, ENDIANNESS_LITTLE, read64mo_delegate>; -template class handler_entry_read_delegate<3, -1, ENDIANNESS_BIG, read64mo_delegate>; -template class handler_entry_read_delegate<3, -2, ENDIANNESS_LITTLE, read64mo_delegate>; -template class handler_entry_read_delegate<3, -2, ENDIANNESS_BIG, read64mo_delegate>; -template class handler_entry_read_delegate<3, -3, ENDIANNESS_LITTLE, read64mo_delegate>; -template class handler_entry_read_delegate<3, -3, ENDIANNESS_BIG, read64mo_delegate>; - -template class handler_entry_read_delegate<0, 1, ENDIANNESS_LITTLE, read8smo_delegate>; -template class handler_entry_read_delegate<0, 1, ENDIANNESS_BIG, read8smo_delegate>; -template class handler_entry_read_delegate<0, 0, ENDIANNESS_LITTLE, read8smo_delegate>; -template class handler_entry_read_delegate<0, 0, ENDIANNESS_BIG, read8smo_delegate>; -template class handler_entry_read_delegate<1, 3, ENDIANNESS_LITTLE, read16smo_delegate>; -template class handler_entry_read_delegate<1, 3, ENDIANNESS_BIG, read16smo_delegate>; -template class handler_entry_read_delegate<1, 0, ENDIANNESS_LITTLE, read16smo_delegate>; -template class handler_entry_read_delegate<1, 0, ENDIANNESS_BIG, read16smo_delegate>; -template class handler_entry_read_delegate<1, -1, ENDIANNESS_LITTLE, read16smo_delegate>; -template class handler_entry_read_delegate<1, -1, ENDIANNESS_BIG, read16smo_delegate>; -template class handler_entry_read_delegate<2, 0, ENDIANNESS_LITTLE, read32smo_delegate>; -template class handler_entry_read_delegate<2, 0, ENDIANNESS_BIG, read32smo_delegate>; -template class handler_entry_read_delegate<2, -1, ENDIANNESS_LITTLE, read32smo_delegate>; -template class handler_entry_read_delegate<2, -1, ENDIANNESS_BIG, read32smo_delegate>; -template class handler_entry_read_delegate<2, -2, ENDIANNESS_LITTLE, read32smo_delegate>; -template class handler_entry_read_delegate<2, -2, ENDIANNESS_BIG, read32smo_delegate>; -template class handler_entry_read_delegate<3, 0, ENDIANNESS_LITTLE, read64smo_delegate>; -template class handler_entry_read_delegate<3, 0, ENDIANNESS_BIG, read64smo_delegate>; -template class handler_entry_read_delegate<3, -1, ENDIANNESS_LITTLE, read64smo_delegate>; -template class handler_entry_read_delegate<3, -1, ENDIANNESS_BIG, read64smo_delegate>; -template class handler_entry_read_delegate<3, -2, ENDIANNESS_LITTLE, read64smo_delegate>; -template class handler_entry_read_delegate<3, -2, ENDIANNESS_BIG, read64smo_delegate>; -template class handler_entry_read_delegate<3, -3, ENDIANNESS_LITTLE, read64smo_delegate>; -template class handler_entry_read_delegate<3, -3, ENDIANNESS_BIG, read64smo_delegate>; - -template class handler_entry_write_delegate<0, 1, ENDIANNESS_LITTLE, write8_delegate>; -template class handler_entry_write_delegate<0, 1, ENDIANNESS_BIG, write8_delegate>; -template class handler_entry_write_delegate<0, 0, ENDIANNESS_LITTLE, write8_delegate>; -template class handler_entry_write_delegate<0, 0, ENDIANNESS_BIG, write8_delegate>; -template class handler_entry_write_delegate<1, 3, ENDIANNESS_LITTLE, write16_delegate>; -template class handler_entry_write_delegate<1, 3, ENDIANNESS_BIG, write16_delegate>; -template class handler_entry_write_delegate<1, 0, ENDIANNESS_LITTLE, write16_delegate>; -template class handler_entry_write_delegate<1, 0, ENDIANNESS_BIG, write16_delegate>; -template class handler_entry_write_delegate<1, -1, ENDIANNESS_LITTLE, write16_delegate>; -template class handler_entry_write_delegate<1, -1, ENDIANNESS_BIG, write16_delegate>; -template class handler_entry_write_delegate<2, 0, ENDIANNESS_LITTLE, write32_delegate>; -template class handler_entry_write_delegate<2, 0, ENDIANNESS_BIG, write32_delegate>; -template class handler_entry_write_delegate<2, -1, ENDIANNESS_LITTLE, write32_delegate>; -template class handler_entry_write_delegate<2, -1, ENDIANNESS_BIG, write32_delegate>; -template class handler_entry_write_delegate<2, -2, ENDIANNESS_LITTLE, write32_delegate>; -template class handler_entry_write_delegate<2, -2, ENDIANNESS_BIG, write32_delegate>; -template class handler_entry_write_delegate<3, 0, ENDIANNESS_LITTLE, write64_delegate>; -template class handler_entry_write_delegate<3, 0, ENDIANNESS_BIG, write64_delegate>; -template class handler_entry_write_delegate<3, -1, ENDIANNESS_LITTLE, write64_delegate>; -template class handler_entry_write_delegate<3, -1, ENDIANNESS_BIG, write64_delegate>; -template class handler_entry_write_delegate<3, -2, ENDIANNESS_LITTLE, write64_delegate>; -template class handler_entry_write_delegate<3, -2, ENDIANNESS_BIG, write64_delegate>; -template class handler_entry_write_delegate<3, -3, ENDIANNESS_LITTLE, write64_delegate>; -template class handler_entry_write_delegate<3, -3, ENDIANNESS_BIG, write64_delegate>; - -template class handler_entry_write_delegate<0, 1, ENDIANNESS_LITTLE, write8m_delegate>; -template class handler_entry_write_delegate<0, 1, ENDIANNESS_BIG, write8m_delegate>; -template class handler_entry_write_delegate<0, 0, ENDIANNESS_LITTLE, write8m_delegate>; -template class handler_entry_write_delegate<0, 0, ENDIANNESS_BIG, write8m_delegate>; -template class handler_entry_write_delegate<1, 3, ENDIANNESS_LITTLE, write16m_delegate>; -template class handler_entry_write_delegate<1, 3, ENDIANNESS_BIG, write16m_delegate>; -template class handler_entry_write_delegate<1, 0, ENDIANNESS_LITTLE, write16m_delegate>; -template class handler_entry_write_delegate<1, 0, ENDIANNESS_BIG, write16m_delegate>; -template class handler_entry_write_delegate<1, -1, ENDIANNESS_LITTLE, write16m_delegate>; -template class handler_entry_write_delegate<1, -1, ENDIANNESS_BIG, write16m_delegate>; -template class handler_entry_write_delegate<2, 0, ENDIANNESS_LITTLE, write32m_delegate>; -template class handler_entry_write_delegate<2, 0, ENDIANNESS_BIG, write32m_delegate>; -template class handler_entry_write_delegate<2, -1, ENDIANNESS_LITTLE, write32m_delegate>; -template class handler_entry_write_delegate<2, -1, ENDIANNESS_BIG, write32m_delegate>; -template class handler_entry_write_delegate<2, -2, ENDIANNESS_LITTLE, write32m_delegate>; -template class handler_entry_write_delegate<2, -2, ENDIANNESS_BIG, write32m_delegate>; -template class handler_entry_write_delegate<3, 0, ENDIANNESS_LITTLE, write64m_delegate>; -template class handler_entry_write_delegate<3, 0, ENDIANNESS_BIG, write64m_delegate>; -template class handler_entry_write_delegate<3, -1, ENDIANNESS_LITTLE, write64m_delegate>; -template class handler_entry_write_delegate<3, -1, ENDIANNESS_BIG, write64m_delegate>; -template class handler_entry_write_delegate<3, -2, ENDIANNESS_LITTLE, write64m_delegate>; -template class handler_entry_write_delegate<3, -2, ENDIANNESS_BIG, write64m_delegate>; -template class handler_entry_write_delegate<3, -3, ENDIANNESS_LITTLE, write64m_delegate>; -template class handler_entry_write_delegate<3, -3, ENDIANNESS_BIG, write64m_delegate>; - -template class handler_entry_write_delegate<0, 1, ENDIANNESS_LITTLE, write8s_delegate>; -template class handler_entry_write_delegate<0, 1, ENDIANNESS_BIG, write8s_delegate>; -template class handler_entry_write_delegate<0, 0, ENDIANNESS_LITTLE, write8s_delegate>; -template class handler_entry_write_delegate<0, 0, ENDIANNESS_BIG, write8s_delegate>; -template class handler_entry_write_delegate<1, 3, ENDIANNESS_LITTLE, write16s_delegate>; -template class handler_entry_write_delegate<1, 3, ENDIANNESS_BIG, write16s_delegate>; -template class handler_entry_write_delegate<1, 0, ENDIANNESS_LITTLE, write16s_delegate>; -template class handler_entry_write_delegate<1, 0, ENDIANNESS_BIG, write16s_delegate>; -template class handler_entry_write_delegate<1, -1, ENDIANNESS_LITTLE, write16s_delegate>; -template class handler_entry_write_delegate<1, -1, ENDIANNESS_BIG, write16s_delegate>; -template class handler_entry_write_delegate<2, 0, ENDIANNESS_LITTLE, write32s_delegate>; -template class handler_entry_write_delegate<2, 0, ENDIANNESS_BIG, write32s_delegate>; -template class handler_entry_write_delegate<2, -1, ENDIANNESS_LITTLE, write32s_delegate>; -template class handler_entry_write_delegate<2, -1, ENDIANNESS_BIG, write32s_delegate>; -template class handler_entry_write_delegate<2, -2, ENDIANNESS_LITTLE, write32s_delegate>; -template class handler_entry_write_delegate<2, -2, ENDIANNESS_BIG, write32s_delegate>; -template class handler_entry_write_delegate<3, 0, ENDIANNESS_LITTLE, write64s_delegate>; -template class handler_entry_write_delegate<3, 0, ENDIANNESS_BIG, write64s_delegate>; -template class handler_entry_write_delegate<3, -1, ENDIANNESS_LITTLE, write64s_delegate>; -template class handler_entry_write_delegate<3, -1, ENDIANNESS_BIG, write64s_delegate>; -template class handler_entry_write_delegate<3, -2, ENDIANNESS_LITTLE, write64s_delegate>; -template class handler_entry_write_delegate<3, -2, ENDIANNESS_BIG, write64s_delegate>; -template class handler_entry_write_delegate<3, -3, ENDIANNESS_LITTLE, write64s_delegate>; -template class handler_entry_write_delegate<3, -3, ENDIANNESS_BIG, write64s_delegate>; - -template class handler_entry_write_delegate<0, 1, ENDIANNESS_LITTLE, write8sm_delegate>; -template class handler_entry_write_delegate<0, 1, ENDIANNESS_BIG, write8sm_delegate>; -template class handler_entry_write_delegate<0, 0, ENDIANNESS_LITTLE, write8sm_delegate>; -template class handler_entry_write_delegate<0, 0, ENDIANNESS_BIG, write8sm_delegate>; -template class handler_entry_write_delegate<1, 3, ENDIANNESS_LITTLE, write16sm_delegate>; -template class handler_entry_write_delegate<1, 3, ENDIANNESS_BIG, write16sm_delegate>; -template class handler_entry_write_delegate<1, 0, ENDIANNESS_LITTLE, write16sm_delegate>; -template class handler_entry_write_delegate<1, 0, ENDIANNESS_BIG, write16sm_delegate>; -template class handler_entry_write_delegate<1, -1, ENDIANNESS_LITTLE, write16sm_delegate>; -template class handler_entry_write_delegate<1, -1, ENDIANNESS_BIG, write16sm_delegate>; -template class handler_entry_write_delegate<2, 0, ENDIANNESS_LITTLE, write32sm_delegate>; -template class handler_entry_write_delegate<2, 0, ENDIANNESS_BIG, write32sm_delegate>; -template class handler_entry_write_delegate<2, -1, ENDIANNESS_LITTLE, write32sm_delegate>; -template class handler_entry_write_delegate<2, -1, ENDIANNESS_BIG, write32sm_delegate>; -template class handler_entry_write_delegate<2, -2, ENDIANNESS_LITTLE, write32sm_delegate>; -template class handler_entry_write_delegate<2, -2, ENDIANNESS_BIG, write32sm_delegate>; -template class handler_entry_write_delegate<3, 0, ENDIANNESS_LITTLE, write64sm_delegate>; -template class handler_entry_write_delegate<3, 0, ENDIANNESS_BIG, write64sm_delegate>; -template class handler_entry_write_delegate<3, -1, ENDIANNESS_LITTLE, write64sm_delegate>; -template class handler_entry_write_delegate<3, -1, ENDIANNESS_BIG, write64sm_delegate>; -template class handler_entry_write_delegate<3, -2, ENDIANNESS_LITTLE, write64sm_delegate>; -template class handler_entry_write_delegate<3, -2, ENDIANNESS_BIG, write64sm_delegate>; -template class handler_entry_write_delegate<3, -3, ENDIANNESS_LITTLE, write64sm_delegate>; -template class handler_entry_write_delegate<3, -3, ENDIANNESS_BIG, write64sm_delegate>; - -template class handler_entry_write_delegate<0, 1, ENDIANNESS_LITTLE, write8mo_delegate>; -template class handler_entry_write_delegate<0, 1, ENDIANNESS_BIG, write8mo_delegate>; -template class handler_entry_write_delegate<0, 0, ENDIANNESS_LITTLE, write8mo_delegate>; -template class handler_entry_write_delegate<0, 0, ENDIANNESS_BIG, write8mo_delegate>; -template class handler_entry_write_delegate<1, 3, ENDIANNESS_LITTLE, write16mo_delegate>; -template class handler_entry_write_delegate<1, 3, ENDIANNESS_BIG, write16mo_delegate>; -template class handler_entry_write_delegate<1, 0, ENDIANNESS_LITTLE, write16mo_delegate>; -template class handler_entry_write_delegate<1, 0, ENDIANNESS_BIG, write16mo_delegate>; -template class handler_entry_write_delegate<1, -1, ENDIANNESS_LITTLE, write16mo_delegate>; -template class handler_entry_write_delegate<1, -1, ENDIANNESS_BIG, write16mo_delegate>; -template class handler_entry_write_delegate<2, 0, ENDIANNESS_LITTLE, write32mo_delegate>; -template class handler_entry_write_delegate<2, 0, ENDIANNESS_BIG, write32mo_delegate>; -template class handler_entry_write_delegate<2, -1, ENDIANNESS_LITTLE, write32mo_delegate>; -template class handler_entry_write_delegate<2, -1, ENDIANNESS_BIG, write32mo_delegate>; -template class handler_entry_write_delegate<2, -2, ENDIANNESS_LITTLE, write32mo_delegate>; -template class handler_entry_write_delegate<2, -2, ENDIANNESS_BIG, write32mo_delegate>; -template class handler_entry_write_delegate<3, 0, ENDIANNESS_LITTLE, write64mo_delegate>; -template class handler_entry_write_delegate<3, 0, ENDIANNESS_BIG, write64mo_delegate>; -template class handler_entry_write_delegate<3, -1, ENDIANNESS_LITTLE, write64mo_delegate>; -template class handler_entry_write_delegate<3, -1, ENDIANNESS_BIG, write64mo_delegate>; -template class handler_entry_write_delegate<3, -2, ENDIANNESS_LITTLE, write64mo_delegate>; -template class handler_entry_write_delegate<3, -2, ENDIANNESS_BIG, write64mo_delegate>; -template class handler_entry_write_delegate<3, -3, ENDIANNESS_LITTLE, write64mo_delegate>; -template class handler_entry_write_delegate<3, -3, ENDIANNESS_BIG, write64mo_delegate>; - -template class handler_entry_write_delegate<0, 1, ENDIANNESS_LITTLE, write8smo_delegate>; -template class handler_entry_write_delegate<0, 1, ENDIANNESS_BIG, write8smo_delegate>; -template class handler_entry_write_delegate<0, 0, ENDIANNESS_LITTLE, write8smo_delegate>; -template class handler_entry_write_delegate<0, 0, ENDIANNESS_BIG, write8smo_delegate>; -template class handler_entry_write_delegate<1, 3, ENDIANNESS_LITTLE, write16smo_delegate>; -template class handler_entry_write_delegate<1, 3, ENDIANNESS_BIG, write16smo_delegate>; -template class handler_entry_write_delegate<1, 0, ENDIANNESS_LITTLE, write16smo_delegate>; -template class handler_entry_write_delegate<1, 0, ENDIANNESS_BIG, write16smo_delegate>; -template class handler_entry_write_delegate<1, -1, ENDIANNESS_LITTLE, write16smo_delegate>; -template class handler_entry_write_delegate<1, -1, ENDIANNESS_BIG, write16smo_delegate>; -template class handler_entry_write_delegate<2, 0, ENDIANNESS_LITTLE, write32smo_delegate>; -template class handler_entry_write_delegate<2, 0, ENDIANNESS_BIG, write32smo_delegate>; -template class handler_entry_write_delegate<2, -1, ENDIANNESS_LITTLE, write32smo_delegate>; -template class handler_entry_write_delegate<2, -1, ENDIANNESS_BIG, write32smo_delegate>; -template class handler_entry_write_delegate<2, -2, ENDIANNESS_LITTLE, write32smo_delegate>; -template class handler_entry_write_delegate<2, -2, ENDIANNESS_BIG, write32smo_delegate>; -template class handler_entry_write_delegate<3, 0, ENDIANNESS_LITTLE, write64smo_delegate>; -template class handler_entry_write_delegate<3, 0, ENDIANNESS_BIG, write64smo_delegate>; -template class handler_entry_write_delegate<3, -1, ENDIANNESS_LITTLE, write64smo_delegate>; -template class handler_entry_write_delegate<3, -1, ENDIANNESS_BIG, write64smo_delegate>; -template class handler_entry_write_delegate<3, -2, ENDIANNESS_LITTLE, write64smo_delegate>; -template class handler_entry_write_delegate<3, -2, ENDIANNESS_BIG, write64smo_delegate>; -template class handler_entry_write_delegate<3, -3, ENDIANNESS_LITTLE, write64smo_delegate>; -template class handler_entry_write_delegate<3, -3, ENDIANNESS_BIG, write64smo_delegate>; - - -template class handler_entry_read_ioport<0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_read_ioport<0, 1, ENDIANNESS_BIG>; -template class handler_entry_read_ioport<0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_ioport<0, 0, ENDIANNESS_BIG>; -template class handler_entry_read_ioport<1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_read_ioport<1, 3, ENDIANNESS_BIG>; -template class handler_entry_read_ioport<1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_ioport<1, 0, ENDIANNESS_BIG>; -template class handler_entry_read_ioport<1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_ioport<1, -1, ENDIANNESS_BIG>; -template class handler_entry_read_ioport<2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_ioport<2, 0, ENDIANNESS_BIG>; -template class handler_entry_read_ioport<2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_ioport<2, -1, ENDIANNESS_BIG>; -template class handler_entry_read_ioport<2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_ioport<2, -2, ENDIANNESS_BIG>; -template class handler_entry_read_ioport<3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_ioport<3, 0, ENDIANNESS_BIG>; -template class handler_entry_read_ioport<3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_ioport<3, -1, ENDIANNESS_BIG>; -template class handler_entry_read_ioport<3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_ioport<3, -2, ENDIANNESS_BIG>; -template class handler_entry_read_ioport<3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_read_ioport<3, -3, ENDIANNESS_BIG>; - -template class handler_entry_write_ioport<0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_write_ioport<0, 1, ENDIANNESS_BIG>; -template class handler_entry_write_ioport<0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_ioport<0, 0, ENDIANNESS_BIG>; -template class handler_entry_write_ioport<1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_write_ioport<1, 3, ENDIANNESS_BIG>; -template class handler_entry_write_ioport<1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_ioport<1, 0, ENDIANNESS_BIG>; -template class handler_entry_write_ioport<1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_ioport<1, -1, ENDIANNESS_BIG>; -template class handler_entry_write_ioport<2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_ioport<2, 0, ENDIANNESS_BIG>; -template class handler_entry_write_ioport<2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_ioport<2, -1, ENDIANNESS_BIG>; -template class handler_entry_write_ioport<2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_ioport<2, -2, ENDIANNESS_BIG>; -template class handler_entry_write_ioport<3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_ioport<3, 0, ENDIANNESS_BIG>; -template class handler_entry_write_ioport<3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_ioport<3, -1, ENDIANNESS_BIG>; -template class handler_entry_write_ioport<3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_ioport<3, -2, ENDIANNESS_BIG>; -template class handler_entry_write_ioport<3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_write_ioport<3, -3, ENDIANNESS_BIG>; +template class handler_entry_read_delegate<0, 1, read8_delegate>; +template class handler_entry_read_delegate<0, 0, read8_delegate>; +template class handler_entry_read_delegate<1, 3, read16_delegate>; +template class handler_entry_read_delegate<1, 0, read16_delegate>; +template class handler_entry_read_delegate<1, -1, read16_delegate>; +template class handler_entry_read_delegate<2, 3, read32_delegate>; +template class handler_entry_read_delegate<2, 0, read32_delegate>; +template class handler_entry_read_delegate<2, -1, read32_delegate>; +template class handler_entry_read_delegate<2, -2, read32_delegate>; +template class handler_entry_read_delegate<3, 0, read64_delegate>; +template class handler_entry_read_delegate<3, -1, read64_delegate>; +template class handler_entry_read_delegate<3, -2, read64_delegate>; +template class handler_entry_read_delegate<3, -3, read64_delegate>; + +template class handler_entry_read_delegate<0, 1, read8m_delegate>; +template class handler_entry_read_delegate<0, 0, read8m_delegate>; +template class handler_entry_read_delegate<1, 3, read16m_delegate>; +template class handler_entry_read_delegate<1, 0, read16m_delegate>; +template class handler_entry_read_delegate<1, -1, read16m_delegate>; +template class handler_entry_read_delegate<2, 3, read32m_delegate>; +template class handler_entry_read_delegate<2, 0, read32m_delegate>; +template class handler_entry_read_delegate<2, -1, read32m_delegate>; +template class handler_entry_read_delegate<2, -2, read32m_delegate>; +template class handler_entry_read_delegate<3, 0, read64m_delegate>; +template class handler_entry_read_delegate<3, -1, read64m_delegate>; +template class handler_entry_read_delegate<3, -2, read64m_delegate>; +template class handler_entry_read_delegate<3, -3, read64m_delegate>; + +template class handler_entry_read_delegate<0, 1, read8s_delegate>; +template class handler_entry_read_delegate<0, 0, read8s_delegate>; +template class handler_entry_read_delegate<1, 3, read16s_delegate>; +template class handler_entry_read_delegate<1, 0, read16s_delegate>; +template class handler_entry_read_delegate<1, -1, read16s_delegate>; +template class handler_entry_read_delegate<2, 3, read32s_delegate>; +template class handler_entry_read_delegate<2, 0, read32s_delegate>; +template class handler_entry_read_delegate<2, -1, read32s_delegate>; +template class handler_entry_read_delegate<2, -2, read32s_delegate>; +template class handler_entry_read_delegate<3, 0, read64s_delegate>; +template class handler_entry_read_delegate<3, -1, read64s_delegate>; +template class handler_entry_read_delegate<3, -2, read64s_delegate>; +template class handler_entry_read_delegate<3, -3, read64s_delegate>; + +template class handler_entry_read_delegate<0, 1, read8sm_delegate>; +template class handler_entry_read_delegate<0, 0, read8sm_delegate>; +template class handler_entry_read_delegate<1, 3, read16sm_delegate>; +template class handler_entry_read_delegate<1, 0, read16sm_delegate>; +template class handler_entry_read_delegate<1, -1, read16sm_delegate>; +template class handler_entry_read_delegate<2, 3, read32sm_delegate>; +template class handler_entry_read_delegate<2, 0, read32sm_delegate>; +template class handler_entry_read_delegate<2, -1, read32sm_delegate>; +template class handler_entry_read_delegate<2, -2, read32sm_delegate>; +template class handler_entry_read_delegate<3, 0, read64sm_delegate>; +template class handler_entry_read_delegate<3, -1, read64sm_delegate>; +template class handler_entry_read_delegate<3, -2, read64sm_delegate>; +template class handler_entry_read_delegate<3, -3, read64sm_delegate>; + +template class handler_entry_read_delegate<0, 1, read8mo_delegate>; +template class handler_entry_read_delegate<0, 0, read8mo_delegate>; +template class handler_entry_read_delegate<1, 3, read16mo_delegate>; +template class handler_entry_read_delegate<1, 0, read16mo_delegate>; +template class handler_entry_read_delegate<1, -1, read16mo_delegate>; +template class handler_entry_read_delegate<2, 3, read32mo_delegate>; +template class handler_entry_read_delegate<2, 0, read32mo_delegate>; +template class handler_entry_read_delegate<2, -1, read32mo_delegate>; +template class handler_entry_read_delegate<2, -2, read32mo_delegate>; +template class handler_entry_read_delegate<3, 0, read64mo_delegate>; +template class handler_entry_read_delegate<3, -1, read64mo_delegate>; +template class handler_entry_read_delegate<3, -2, read64mo_delegate>; +template class handler_entry_read_delegate<3, -3, read64mo_delegate>; + +template class handler_entry_read_delegate<0, 1, read8smo_delegate>; +template class handler_entry_read_delegate<0, 0, read8smo_delegate>; +template class handler_entry_read_delegate<1, 3, read16smo_delegate>; +template class handler_entry_read_delegate<1, 0, read16smo_delegate>; +template class handler_entry_read_delegate<1, -1, read16smo_delegate>; +template class handler_entry_read_delegate<2, 3, read32smo_delegate>; +template class handler_entry_read_delegate<2, 0, read32smo_delegate>; +template class handler_entry_read_delegate<2, -1, read32smo_delegate>; +template class handler_entry_read_delegate<2, -2, read32smo_delegate>; +template class handler_entry_read_delegate<3, 0, read64smo_delegate>; +template class handler_entry_read_delegate<3, -1, read64smo_delegate>; +template class handler_entry_read_delegate<3, -2, read64smo_delegate>; +template class handler_entry_read_delegate<3, -3, read64smo_delegate>; + +template class handler_entry_write_delegate<0, 1, write8_delegate>; +template class handler_entry_write_delegate<0, 0, write8_delegate>; +template class handler_entry_write_delegate<1, 3, write16_delegate>; +template class handler_entry_write_delegate<1, 0, write16_delegate>; +template class handler_entry_write_delegate<1, -1, write16_delegate>; +template class handler_entry_write_delegate<2, 3, write32_delegate>; +template class handler_entry_write_delegate<2, 0, write32_delegate>; +template class handler_entry_write_delegate<2, -1, write32_delegate>; +template class handler_entry_write_delegate<2, -2, write32_delegate>; +template class handler_entry_write_delegate<3, 0, write64_delegate>; +template class handler_entry_write_delegate<3, -1, write64_delegate>; +template class handler_entry_write_delegate<3, -2, write64_delegate>; +template class handler_entry_write_delegate<3, -3, write64_delegate>; + +template class handler_entry_write_delegate<0, 1, write8m_delegate>; +template class handler_entry_write_delegate<0, 0, write8m_delegate>; +template class handler_entry_write_delegate<1, 3, write16m_delegate>; +template class handler_entry_write_delegate<1, 0, write16m_delegate>; +template class handler_entry_write_delegate<1, -1, write16m_delegate>; +template class handler_entry_write_delegate<2, 3, write32m_delegate>; +template class handler_entry_write_delegate<2, 0, write32m_delegate>; +template class handler_entry_write_delegate<2, -1, write32m_delegate>; +template class handler_entry_write_delegate<2, -2, write32m_delegate>; +template class handler_entry_write_delegate<3, 0, write64m_delegate>; +template class handler_entry_write_delegate<3, -1, write64m_delegate>; +template class handler_entry_write_delegate<3, -2, write64m_delegate>; +template class handler_entry_write_delegate<3, -3, write64m_delegate>; + +template class handler_entry_write_delegate<0, 1, write8s_delegate>; +template class handler_entry_write_delegate<0, 0, write8s_delegate>; +template class handler_entry_write_delegate<1, 3, write16s_delegate>; +template class handler_entry_write_delegate<1, 0, write16s_delegate>; +template class handler_entry_write_delegate<1, -1, write16s_delegate>; +template class handler_entry_write_delegate<2, 3, write32s_delegate>; +template class handler_entry_write_delegate<2, 0, write32s_delegate>; +template class handler_entry_write_delegate<2, -1, write32s_delegate>; +template class handler_entry_write_delegate<2, -2, write32s_delegate>; +template class handler_entry_write_delegate<3, 0, write64s_delegate>; +template class handler_entry_write_delegate<3, -1, write64s_delegate>; +template class handler_entry_write_delegate<3, -2, write64s_delegate>; +template class handler_entry_write_delegate<3, -3, write64s_delegate>; + +template class handler_entry_write_delegate<0, 1, write8sm_delegate>; +template class handler_entry_write_delegate<0, 0, write8sm_delegate>; +template class handler_entry_write_delegate<1, 3, write16sm_delegate>; +template class handler_entry_write_delegate<1, 0, write16sm_delegate>; +template class handler_entry_write_delegate<1, -1, write16sm_delegate>; +template class handler_entry_write_delegate<2, 3, write32sm_delegate>; +template class handler_entry_write_delegate<2, 0, write32sm_delegate>; +template class handler_entry_write_delegate<2, -1, write32sm_delegate>; +template class handler_entry_write_delegate<2, -2, write32sm_delegate>; +template class handler_entry_write_delegate<3, 0, write64sm_delegate>; +template class handler_entry_write_delegate<3, -1, write64sm_delegate>; +template class handler_entry_write_delegate<3, -2, write64sm_delegate>; +template class handler_entry_write_delegate<3, -3, write64sm_delegate>; + +template class handler_entry_write_delegate<0, 1, write8mo_delegate>; +template class handler_entry_write_delegate<0, 0, write8mo_delegate>; +template class handler_entry_write_delegate<1, 3, write16mo_delegate>; +template class handler_entry_write_delegate<1, 0, write16mo_delegate>; +template class handler_entry_write_delegate<1, -1, write16mo_delegate>; +template class handler_entry_write_delegate<2, 3, write32mo_delegate>; +template class handler_entry_write_delegate<2, 0, write32mo_delegate>; +template class handler_entry_write_delegate<2, -1, write32mo_delegate>; +template class handler_entry_write_delegate<2, -2, write32mo_delegate>; +template class handler_entry_write_delegate<3, 0, write64mo_delegate>; +template class handler_entry_write_delegate<3, -1, write64mo_delegate>; +template class handler_entry_write_delegate<3, -2, write64mo_delegate>; +template class handler_entry_write_delegate<3, -3, write64mo_delegate>; + +template class handler_entry_write_delegate<0, 1, write8smo_delegate>; +template class handler_entry_write_delegate<0, 0, write8smo_delegate>; +template class handler_entry_write_delegate<1, 3, write16smo_delegate>; +template class handler_entry_write_delegate<1, 0, write16smo_delegate>; +template class handler_entry_write_delegate<1, -1, write16smo_delegate>; +template class handler_entry_write_delegate<2, 3, write32smo_delegate>; +template class handler_entry_write_delegate<2, 0, write32smo_delegate>; +template class handler_entry_write_delegate<2, -1, write32smo_delegate>; +template class handler_entry_write_delegate<2, -2, write32smo_delegate>; +template class handler_entry_write_delegate<3, 0, write64smo_delegate>; +template class handler_entry_write_delegate<3, -1, write64smo_delegate>; +template class handler_entry_write_delegate<3, -2, write64smo_delegate>; +template class handler_entry_write_delegate<3, -3, write64smo_delegate>; + + +template class handler_entry_read_ioport<0, 1>; +template class handler_entry_read_ioport<0, 0>; +template class handler_entry_read_ioport<1, 3>; +template class handler_entry_read_ioport<1, 0>; +template class handler_entry_read_ioport<1, -1>; +template class handler_entry_read_ioport<2, 3>; +template class handler_entry_read_ioport<2, 0>; +template class handler_entry_read_ioport<2, -1>; +template class handler_entry_read_ioport<2, -2>; +template class handler_entry_read_ioport<3, 0>; +template class handler_entry_read_ioport<3, -1>; +template class handler_entry_read_ioport<3, -2>; +template class handler_entry_read_ioport<3, -3>; + +template class handler_entry_write_ioport<0, 1>; +template class handler_entry_write_ioport<0, 0>; +template class handler_entry_write_ioport<1, 3>; +template class handler_entry_write_ioport<1, 0>; +template class handler_entry_write_ioport<1, -1>; +template class handler_entry_write_ioport<2, 3>; +template class handler_entry_write_ioport<2, 0>; +template class handler_entry_write_ioport<2, -1>; +template class handler_entry_write_ioport<2, -2>; +template class handler_entry_write_ioport<3, 0>; +template class handler_entry_write_ioport<3, -1>; +template class handler_entry_write_ioport<3, -2>; +template class handler_entry_write_ioport<3, -3>; diff --git a/src/emu/emumem_hedp.h b/src/emu/emumem_hedp.h index cba4877b20c..6b8563bd5b5 100644 --- a/src/emu/emumem_hedp.h +++ b/src/emu/emumem_hedp.h @@ -1,20 +1,26 @@ // license:BSD-3-Clause // copyright-holders:Olivier Galibert +#ifndef MAME_EMU_EMUMEM_HEDP_H +#define MAME_EMU_EMUMEM_HEDP_H + +#pragma once // handler_entry_read_delegate/handler_entry_write_delegate -// Executes an access through called a delegate, usually containing a handler or a lambda +// Executes an access through a delegate, usually containing a handler or a lambda -template<int Width, int AddrShift, int Endian, typename READ> class handler_entry_read_delegate : public handler_entry_read_address<Width, AddrShift, Endian> +template<int Width, int AddrShift, typename READ> class handler_entry_read_delegate : public handler_entry_read_address<Width, AddrShift> { public: - using uX = typename emu::detail::handler_entry_size<Width>::uX; - using inh = handler_entry_read_address<Width, AddrShift, Endian>; + using uX = emu::detail::handler_entry_size_t<Width>; - handler_entry_read_delegate(address_space *space, READ delegate) : handler_entry_read_address<Width, AddrShift, Endian>(space, 0), m_delegate(delegate) {} + handler_entry_read_delegate(address_space *space, u16 flags, const READ &delegate) : handler_entry_read_address<Width, AddrShift>(space, flags), m_delegate(delegate) {} ~handler_entry_read_delegate() = default; - uX read(offs_t offset, uX mem_mask) override; + uX read(offs_t offset, uX mem_mask) const override; + uX read_interruptible(offs_t offset, uX mem_mask) const override; + std::pair<uX, u16> read_flags(offs_t offset, uX mem_mask) const override; + u16 lookup_flags(offs_t offset, uX mem_mask) const override; std::string name() const override; @@ -26,54 +32,56 @@ private: std::is_same<R, read16_delegate>::value || std::is_same<R, read32_delegate>::value || std::is_same<R, read64_delegate>::value, - uX> read_impl(offs_t offset, uX mem_mask); + uX> read_impl(offs_t offset, uX mem_mask) const; template<typename R> std::enable_if_t<std::is_same<R, read8m_delegate>::value || std::is_same<R, read16m_delegate>::value || std::is_same<R, read32m_delegate>::value || std::is_same<R, read64m_delegate>::value, - uX> read_impl(offs_t offset, uX mem_mask); + uX> read_impl(offs_t offset, uX mem_mask) const; template<typename R> std::enable_if_t<std::is_same<R, read8s_delegate>::value || std::is_same<R, read16s_delegate>::value || std::is_same<R, read32s_delegate>::value || std::is_same<R, read64s_delegate>::value, - uX> read_impl(offs_t offset, uX mem_mask); + uX> read_impl(offs_t offset, uX mem_mask) const; template<typename R> std::enable_if_t<std::is_same<R, read8sm_delegate>::value || std::is_same<R, read16sm_delegate>::value || std::is_same<R, read32sm_delegate>::value || std::is_same<R, read64sm_delegate>::value, - uX> read_impl(offs_t offset, uX mem_mask); + uX> read_impl(offs_t offset, uX mem_mask) const; template<typename R> std::enable_if_t<std::is_same<R, read8mo_delegate>::value || std::is_same<R, read16mo_delegate>::value || std::is_same<R, read32mo_delegate>::value || std::is_same<R, read64mo_delegate>::value, - uX> read_impl(offs_t offset, uX mem_mask); + uX> read_impl(offs_t offset, uX mem_mask) const; template<typename R> std::enable_if_t<std::is_same<R, read8smo_delegate>::value || std::is_same<R, read16smo_delegate>::value || std::is_same<R, read32smo_delegate>::value || std::is_same<R, read64smo_delegate>::value, - uX> read_impl(offs_t offset, uX mem_mask); + uX> read_impl(offs_t offset, uX mem_mask) const; }; -template<int Width, int AddrShift, int Endian, typename WRITE> class handler_entry_write_delegate : public handler_entry_write_address<Width, AddrShift, Endian> +template<int Width, int AddrShift, typename WRITE> class handler_entry_write_delegate : public handler_entry_write_address<Width, AddrShift> { public: - using uX = typename emu::detail::handler_entry_size<Width>::uX; - using inh = handler_entry_write_address<Width, AddrShift, Endian>; + using uX = emu::detail::handler_entry_size_t<Width>; - handler_entry_write_delegate(address_space *space, WRITE delegate) : handler_entry_write_address<Width, AddrShift, Endian>(space, 0), m_delegate(delegate) {} + handler_entry_write_delegate(address_space *space, u16 flags, const WRITE &delegate) : handler_entry_write_address<Width, AddrShift>(space, flags), m_delegate(delegate) {} ~handler_entry_write_delegate() = default; - void write(offs_t offset, uX data, uX mem_mask) override; + void write(offs_t offset, uX data, uX mem_mask) const override; + void write_interruptible(offs_t offset, uX data, uX mem_mask) const override; + u16 write_flags(offs_t offset, uX data, uX mem_mask) const override; + u16 lookup_flags(offs_t offset, uX mem_mask) const override; std::string name() const override; @@ -85,42 +93,42 @@ private: std::is_same<W, write16_delegate>::value || std::is_same<W, write32_delegate>::value || std::is_same<W, write64_delegate>::value, - void> write_impl(offs_t offset, uX data, uX mem_mask); + void> write_impl(offs_t offset, uX data, uX mem_mask) const; template<typename W> std::enable_if_t<std::is_same<W, write8m_delegate>::value || std::is_same<W, write16m_delegate>::value || std::is_same<W, write32m_delegate>::value || std::is_same<W, write64m_delegate>::value, - void> write_impl(offs_t offset, uX data, uX mem_mask); + void> write_impl(offs_t offset, uX data, uX mem_mask) const; template<typename W> std::enable_if_t<std::is_same<W, write8s_delegate>::value || std::is_same<W, write16s_delegate>::value || std::is_same<W, write32s_delegate>::value || std::is_same<W, write64s_delegate>::value, - void> write_impl(offs_t offset, uX data, uX mem_mask); + void> write_impl(offs_t offset, uX data, uX mem_mask) const; template<typename W> std::enable_if_t<std::is_same<W, write8sm_delegate>::value || std::is_same<W, write16sm_delegate>::value || std::is_same<W, write32sm_delegate>::value || std::is_same<W, write64sm_delegate>::value, - void> write_impl(offs_t offset, uX data, uX mem_mask); + void> write_impl(offs_t offset, uX data, uX mem_mask) const; template<typename W> std::enable_if_t<std::is_same<W, write8mo_delegate>::value || std::is_same<W, write16mo_delegate>::value || std::is_same<W, write32mo_delegate>::value || std::is_same<W, write64mo_delegate>::value, - void> write_impl(offs_t offset, uX data, uX mem_mask); + void> write_impl(offs_t offset, uX data, uX mem_mask) const; template<typename W> std::enable_if_t<std::is_same<W, write8smo_delegate>::value || std::is_same<W, write16smo_delegate>::value || std::is_same<W, write32smo_delegate>::value || std::is_same<W, write64smo_delegate>::value, - void> write_impl(offs_t offset, uX data, uX mem_mask); + void> write_impl(offs_t offset, uX data, uX mem_mask) const; }; @@ -128,16 +136,18 @@ private: // Accesses an ioport -template<int Width, int AddrShift, int Endian> class handler_entry_read_ioport : public handler_entry_read<Width, AddrShift, Endian> +template<int Width, int AddrShift> class handler_entry_read_ioport : public handler_entry_read<Width, AddrShift> { public: - using uX = typename emu::detail::handler_entry_size<Width>::uX; - using inh = handler_entry_read<Width, AddrShift, Endian>; + using uX = emu::detail::handler_entry_size_t<Width>; - handler_entry_read_ioport(address_space *space, ioport_port *port) : handler_entry_read<Width, AddrShift, Endian>(space, 0), m_port(port) {} + handler_entry_read_ioport(address_space *space, u16 flags, ioport_port *port) : handler_entry_read<Width, AddrShift>(space, flags), m_port(port) {} ~handler_entry_read_ioport() = default; - uX read(offs_t offset, uX mem_mask) override; + uX read(offs_t offset, uX mem_mask) const override; + uX read_interruptible(offs_t offset, uX mem_mask) const override; + std::pair<uX, u16> read_flags(offs_t offset, uX mem_mask) const override; + u16 lookup_flags(offs_t offset, uX mem_mask) const override; std::string name() const override; @@ -145,19 +155,23 @@ private: ioport_port *m_port; }; -template<int Width, int AddrShift, int Endian> class handler_entry_write_ioport : public handler_entry_write<Width, AddrShift, Endian> +template<int Width, int AddrShift> class handler_entry_write_ioport : public handler_entry_write<Width, AddrShift> { public: - using uX = typename emu::detail::handler_entry_size<Width>::uX; - using inh = handler_entry_write<Width, AddrShift, Endian>; + using uX = emu::detail::handler_entry_size_t<Width>; - handler_entry_write_ioport(address_space *space, ioport_port *port) : handler_entry_write<Width, AddrShift, Endian>(space, 0), m_port(port) {} + handler_entry_write_ioport(address_space *space, u16 flags, ioport_port *port) : handler_entry_write<Width, AddrShift>(space, flags), m_port(port) {} ~handler_entry_write_ioport() = default; - void write(offs_t offset, uX data, uX mem_mask) override; + void write(offs_t offset, uX data, uX mem_mask) const override; + void write_interruptible(offs_t offset, uX data, uX mem_mask) const override; + u16 write_flags(offs_t offset, uX data, uX mem_mask) const override; + u16 lookup_flags(offs_t offset, uX mem_mask) const override; std::string name() const override; private: ioport_port *m_port; }; + +#endif // MAME_EMU_EMUMEM_HEDP_H diff --git a/src/emu/emumem_hedr.h b/src/emu/emumem_hedr.h index b30dfb95637..e48737f31c9 100644 --- a/src/emu/emumem_hedr.h +++ b/src/emu/emumem_hedr.h @@ -1,41 +1,58 @@ // license:BSD-3-Clause // copyright-holders:Olivier Galibert +#ifndef MAME_EMU_EMUMEM_HEDR_H +#define MAME_EMU_EMUMEM_HEDR_H + +#pragma once // handler_entry_read_dispatch -// dispatches an access among multiple handlers indexed on part of the address +// dispatches an access among multiple handlers indexed on part of the +// address and when appropriate a selected view -template<int HighBits, int Width, int AddrShift, int Endian> class handler_entry_read_dispatch : public handler_entry_read<Width, AddrShift, Endian> +template<int HighBits, int Width, int AddrShift> class handler_entry_read_dispatch : public handler_entry_read<Width, AddrShift> { public: - using uX = typename emu::detail::handler_entry_size<Width>::uX; - using inh = handler_entry_read<Width, AddrShift, Endian>; - using mapping = typename inh::mapping; + using uX = emu::detail::handler_entry_size_t<Width>; + using mapping = typename handler_entry_read<Width, AddrShift>::mapping; - handler_entry_read_dispatch(address_space *space, const handler_entry::range &init, handler_entry_read<Width, AddrShift, Endian> *handler); + handler_entry_read_dispatch(address_space *space, const handler_entry::range &init, handler_entry_read<Width, AddrShift> *handler); + handler_entry_read_dispatch(address_space *space, memory_view &view, offs_t addrstart, offs_t addrend); + handler_entry_read_dispatch(handler_entry_read_dispatch<HighBits, Width, AddrShift> *src); ~handler_entry_read_dispatch(); - uX read(offs_t offset, uX mem_mask) override; + uX read(offs_t offset, uX mem_mask) const override; + uX read_interruptible(offs_t offset, uX mem_mask) const override; + std::pair<uX, u16> read_flags(offs_t offset, uX mem_mask) const override; + u16 lookup_flags(offs_t offset, uX mem_mask) const override; void *get_ptr(offs_t offset) const override; - void lookup(offs_t address, offs_t &start, offs_t &end, handler_entry_read<Width, AddrShift, Endian> *&handler) const override; + void lookup(offs_t address, offs_t &start, offs_t &end, handler_entry_read<Width, AddrShift> *&handler) const override; + offs_t dispatch_entry(offs_t address) const override; void dump_map(std::vector<memory_entry> &map) const override; std::string name() const override; - void populate_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_read<Width, AddrShift, Endian> *handler) override; - void populate_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_read<Width, AddrShift, Endian> *handler) override; - void populate_mismatched_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 rkey, std::vector<mapping> &mappings) override; - void populate_mismatched_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, std::vector<mapping> &mappings) override; - void populate_passthrough_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_read_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings) override; - void populate_passthrough_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_read_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings) override; + void populate_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_read<Width, AddrShift> *handler) override; + void populate_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_read<Width, AddrShift> *handler) override; + void populate_mismatched_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, const memory_units_descriptor<Width, AddrShift> &descriptor, u8 rkey, std::vector<mapping> &mappings) override; + void populate_mismatched_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, const memory_units_descriptor<Width, AddrShift> &descriptor, std::vector<mapping> &mappings) override; + void populate_passthrough_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_read_passthrough<Width, AddrShift> *handler, std::vector<mapping> &mappings) override; + void populate_passthrough_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_read_passthrough<Width, AddrShift> *handler, std::vector<mapping> &mappings) override; void detach(const std::unordered_set<handler_entry *> &handlers) override; void range_cut_before(offs_t address, int start = COUNT); void range_cut_after(offs_t address, int start = -1); void enumerate_references(handler_entry::reflist &refs) const override; -protected: + const handler_entry_read<Width, AddrShift> *const *get_dispatch() const override; + void select_a(int slot) override; + void select_u(int slot) override; + void init_handlers(offs_t start_entry, offs_t end_entry, u32 lowbits, offs_t ostart, offs_t oend, handler_entry_read<Width, AddrShift> **dispatch, handler_entry::range *ranges) override; + handler_entry_read<Width, AddrShift> *dup() override; + +private: + static constexpr int Level = emu::detail::handler_entry_dispatch_level(HighBits); static constexpr u32 LowBits = emu::detail::handler_entry_dispatch_lowbits(HighBits, Width, AddrShift); static constexpr u32 BITCOUNT = HighBits > LowBits ? HighBits - LowBits : 0; static constexpr u32 COUNT = 1 << BITCOUNT; @@ -44,18 +61,49 @@ protected: static constexpr offs_t HIGHMASK = make_bitmask<offs_t>(HighBits) ^ LOWMASK; static constexpr offs_t UPMASK = ~make_bitmask<offs_t>(HighBits); - handler_entry_read<Width, AddrShift, Endian> *m_dispatch[COUNT]; - handler_entry::range m_ranges[COUNT]; + class handler_array : public std::array<handler_entry_read<Width, AddrShift> *, COUNT> + { + public: + using std::array<handler_entry_read<Width, AddrShift> *, COUNT>::array; + handler_array() + { + std::fill(this->begin(), this->end(), nullptr); + } + }; -private: - void populate_nomirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_read<Width, AddrShift, Endian> *handler); - void populate_mirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_read<Width, AddrShift, Endian> *handler); + class range_array : public std::array<handler_entry::range, COUNT> + { + public: + using std::array<handler_entry::range, COUNT>::array; + range_array() + { + std::fill(this->begin(), this->end(), handler_entry::range{ 0, 0 }); + } + }; + + memory_view *m_view; + + std::vector<handler_array> m_dispatch_array; + std::vector<range_array> m_ranges_array; - void populate_mismatched_nomirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 rkey, std::vector<mapping> &mappings); - void populate_mismatched_mirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, std::vector<mapping> &mappings); - void mismatched_patch(const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 rkey, std::vector<mapping> &mappings, handler_entry_read<Width, AddrShift, Endian> *&target); + handler_entry_read<Width, AddrShift> **m_a_dispatch; + handler_entry::range *m_a_ranges; - void populate_passthrough_nomirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_read_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings); - void populate_passthrough_mirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_read_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings); - void passthrough_patch(handler_entry_read_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings, handler_entry_read<Width, AddrShift, Endian> *&target); + handler_entry_read<Width, AddrShift> **m_u_dispatch; + handler_entry::range *m_u_ranges; + + handler_entry::range m_global_range; + + void populate_nomirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_read<Width, AddrShift> *handler); + void populate_mirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_read<Width, AddrShift> *handler); + + void populate_mismatched_nomirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, const memory_units_descriptor<Width, AddrShift> &descriptor, u8 rkey, std::vector<mapping> &mappings); + void populate_mismatched_mirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, const memory_units_descriptor<Width, AddrShift> &descriptor, std::vector<mapping> &mappings); + void mismatched_patch(const memory_units_descriptor<Width, AddrShift> &descriptor, u8 rkey, std::vector<mapping> &mappings, handler_entry_read<Width, AddrShift> *&target); + + void populate_passthrough_nomirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_read_passthrough<Width, AddrShift> *handler, std::vector<mapping> &mappings); + void populate_passthrough_mirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_read_passthrough<Width, AddrShift> *handler, std::vector<mapping> &mappings); + void passthrough_patch(handler_entry_read_passthrough<Width, AddrShift> *handler, std::vector<mapping> &mappings, handler_entry_read<Width, AddrShift> *&target); }; + +#endif // MAME_EMU_EMUMEM_HEDR_H diff --git a/src/emu/emumem_hedr.ipp b/src/emu/emumem_hedr.ipp index af259b33b01..c79691e5e89 100644 --- a/src/emu/emumem_hedr.ipp +++ b/src/emu/emumem_hedr.ipp @@ -1,7 +1,10 @@ // license:BSD-3-Clause // copyright-holders:Olivier Galibert +#ifndef MAME_EMU_EMUMEM_HEDR_IPP +#define MAME_EMU_EMUMEM_HEDR_IPP + +#pragma once -#include "emu.h" #include "emumem_mud.h" #include "emumem_hea.h" #include "emumem_heu.h" @@ -9,173 +12,291 @@ #include "emumem_hep.h" #include "emumem_hedr.h" -template<int HighBits, int Width, int AddrShift, int Endian> handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::handler_entry_read_dispatch(address_space *space, const handler_entry::range &init, handler_entry_read<Width, AddrShift, Endian> *handler) : handler_entry_read<Width, AddrShift, Endian>(space, handler_entry::F_DISPATCH) +template<int HighBits, int Width, int AddrShift> const handler_entry_read<Width, AddrShift> *const *handler_entry_read_dispatch<HighBits, Width, AddrShift>::get_dispatch() const +{ + return m_a_dispatch; +} + +template<int HighBits, int Width, int AddrShift> handler_entry_read_dispatch<HighBits, Width, AddrShift>::handler_entry_read_dispatch(address_space *space, const handler_entry::range &init, handler_entry_read<Width, AddrShift> *handler) : handler_entry_read<Width, AddrShift>(space, handler_entry::F_DISPATCH), m_view(nullptr) { + m_ranges_array.resize(1); + m_dispatch_array.resize(1); + m_a_ranges = m_ranges_array[0].data(); + m_a_dispatch = m_dispatch_array[0].data(); + m_u_ranges = m_ranges_array[0].data(); + m_u_dispatch = m_dispatch_array[0].data(); + m_global_range = init; + if (!handler) - handler = space->get_unmap_r<Width, AddrShift, Endian>(); + handler = space->get_unmap_r<Width, AddrShift>(); handler->ref(COUNT); for(unsigned int i=0; i != COUNT; i++) { - m_dispatch[i] = handler; - m_ranges[i] = init; + m_u_dispatch[i] = handler; + m_u_ranges[i] = init; } } -template<int HighBits, int Width, int AddrShift, int Endian> handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::~handler_entry_read_dispatch() +template<int HighBits, int Width, int AddrShift> handler_entry_read_dispatch<HighBits, Width, AddrShift>::handler_entry_read_dispatch(address_space *space, memory_view &view, offs_t addrstart, offs_t addrend) : handler_entry_read<Width, AddrShift>(space, handler_entry::F_VIEW), m_view(&view), m_a_dispatch(nullptr), m_a_ranges(nullptr), m_u_dispatch(nullptr), m_u_ranges(nullptr) { - for(unsigned int i=0; i != COUNT; i++) - m_dispatch[i]->unref(); + m_ranges_array.resize(1); + m_dispatch_array.resize(1); + m_a_ranges = m_ranges_array[0].data(); + m_a_dispatch = m_dispatch_array[0].data(); + m_u_ranges = m_ranges_array[0].data(); + m_u_dispatch = m_dispatch_array[0].data(); + m_global_range.start = addrstart; + m_global_range.end = addrend; + + auto handler = space->get_unmap_r<Width, AddrShift>(); + handler->ref(COUNT); + for(unsigned int i=0; i != COUNT; i++) { + m_u_dispatch[i] = handler; + m_u_ranges[i].start = addrstart; + m_u_ranges[i].end = addrend; + } } -template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::enumerate_references(handler_entry::reflist &refs) const +template<int HighBits, int Width, int AddrShift> handler_entry_read_dispatch<HighBits, Width, AddrShift>::handler_entry_read_dispatch(handler_entry_read_dispatch<HighBits, Width, AddrShift> *src) : handler_entry_read<Width, AddrShift>(src->m_space, handler_entry::F_DISPATCH), m_view(nullptr) { - for(unsigned int i=0; i != COUNT; i++) - refs.add(m_dispatch[i]); + m_ranges_array.resize(1); + m_dispatch_array.resize(1); + m_a_ranges = m_ranges_array[0].data(); + m_a_dispatch = m_dispatch_array[0].data(); + m_u_ranges = m_ranges_array[0].data(); + m_u_dispatch = m_dispatch_array[0].data(); + m_global_range = src->m_global_range; + + for(unsigned int i=0; i != COUNT; i++) { + m_u_dispatch[i] = src->m_u_dispatch[i]->dup(); + m_u_ranges[i] = src->m_u_ranges[i]; + } } -template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::dump_map(std::vector<memory_entry> &map) const + +template<int HighBits, int Width, int AddrShift> handler_entry_read_dispatch<HighBits, Width, AddrShift>::~handler_entry_read_dispatch() { - offs_t cur = map.empty() ? 0 : map.back().end + 1; - offs_t base = cur & UPMASK; - do { - offs_t entry = (cur >> LowBits) & BITMASK; - if(m_dispatch[entry]->is_dispatch()) - m_dispatch[entry]->dump_map(map); - else - map.emplace_back(memory_entry{ m_ranges[entry].start, m_ranges[entry].end, m_dispatch[entry] }); - cur = map.back().end + 1; - } while(cur && !((cur ^ base) & UPMASK)); + for(auto &d : m_dispatch_array) + for(auto p : d) + if(p) + p->unref(); +} + +template<int HighBits, int Width, int AddrShift> void handler_entry_read_dispatch<HighBits, Width, AddrShift>::enumerate_references(handler_entry::reflist &refs) const +{ + for(auto &d : m_dispatch_array) + for(auto p : d) + if(p) + refs.add(p); +} + +template<int HighBits, int Width, int AddrShift> offs_t handler_entry_read_dispatch<HighBits, Width, AddrShift>::dispatch_entry(offs_t address) const +{ + return (address & HIGHMASK) >> LowBits; +} + +template<int HighBits, int Width, int AddrShift> void handler_entry_read_dispatch<HighBits, Width, AddrShift>::dump_map(std::vector<memory_entry> &map) const +{ + if(m_view) { + offs_t base_cur = map.empty() ? m_view->m_addrstart & HIGHMASK : map.back().end + 1; + for(u32 i = 0; i != m_dispatch_array.size(); i++) { + u32 j = map.size(); + offs_t cur = base_cur; + offs_t end = m_global_range.end + 1; + + do { + offs_t entry = (cur >> LowBits) & BITMASK; + if(m_dispatch_array[i][entry]->is_dispatch() || m_dispatch_array[i][entry]->is_view()) + m_dispatch_array[i][entry]->dump_map(map); + else + map.emplace_back(memory_entry{ m_ranges_array[i][entry].start, m_ranges_array[i][entry].end, m_dispatch_array[i][entry] }); + cur = map.back().end + 1; + } while(cur != end); + if(i == 0) { + for(u32 k = j; k != map.size(); k++) + map[k].context.emplace(map[k].context.begin(), memory_entry_context{ m_view, true, 0 }); + } else { + int slot = m_view->id_to_slot(int(i)-1); + for(u32 k = j; k != map.size(); k++) + map[k].context.emplace(map[k].context.begin(), memory_entry_context{ m_view, false, slot }); + } + } + } else { + offs_t cur = map.empty() ? 0 : map.back().end + 1; + offs_t base = cur & UPMASK; + offs_t end = m_global_range.end + 1; + do { + offs_t entry = (cur >> LowBits) & BITMASK; + if(m_a_dispatch[entry]->is_dispatch() || m_a_dispatch[entry]->is_view()) + m_a_dispatch[entry]->dump_map(map); + else + map.emplace_back(memory_entry{ m_a_ranges[entry].start, m_a_ranges[entry].end, m_a_dispatch[entry] }); + cur = map.back().end + 1; + } while(cur != end && !((cur ^ base) & UPMASK)); + } +} +template<int HighBits, int Width, int AddrShift> emu::detail::handler_entry_size_t<Width> handler_entry_read_dispatch<HighBits, Width, AddrShift>::read(offs_t offset, uX mem_mask) const +{ + return dispatch_read<Level, Width, AddrShift>(HIGHMASK, offset, mem_mask, m_a_dispatch); +} + +template<int HighBits, int Width, int AddrShift> emu::detail::handler_entry_size_t<Width> handler_entry_read_dispatch<HighBits, Width, AddrShift>::read_interruptible(offs_t offset, uX mem_mask) const +{ + return dispatch_read_interruptible<Level, Width, AddrShift>(HIGHMASK, offset, mem_mask, m_a_dispatch); +} + +template<int HighBits, int Width, int AddrShift> std::pair<emu::detail::handler_entry_size_t<Width>, u16> handler_entry_read_dispatch<HighBits, Width, AddrShift>::read_flags(offs_t offset, uX mem_mask) const +{ + return dispatch_read_flags<Level, Width, AddrShift>(HIGHMASK, offset, mem_mask, m_a_dispatch); } -template<int HighBits, int Width, int AddrShift, int Endian> typename emu::detail::handler_entry_size<Width>::uX handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::read(offs_t offset, uX mem_mask) +template<int HighBits, int Width, int AddrShift> u16 handler_entry_read_dispatch<HighBits, Width, AddrShift>::lookup_flags(offs_t offset, uX mem_mask) const { - return m_dispatch[(offset >> LowBits) & BITMASK]->read(offset, mem_mask); + return dispatch_lookup_read_flags<Level, Width, AddrShift>(HIGHMASK, offset, mem_mask, m_a_dispatch); } -template<int HighBits, int Width, int AddrShift, int Endian> void *handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::get_ptr(offs_t offset) const +template<int HighBits, int Width, int AddrShift> void *handler_entry_read_dispatch<HighBits, Width, AddrShift>::get_ptr(offs_t offset) const { - return m_dispatch[(offset >> LowBits) & BITMASK]->get_ptr(offset); + return m_a_dispatch[(offset & HIGHMASK) >> LowBits]->get_ptr(offset); } -template<int HighBits, int Width, int AddrShift, int Endian> std::string handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::name() const +template<int HighBits, int Width, int AddrShift> std::string handler_entry_read_dispatch<HighBits, Width, AddrShift>::name() const { - return "dispatch"; + return m_view ? "view" : "dispatch"; } -template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::lookup(offs_t address, offs_t &start, offs_t &end, handler_entry_read<Width, AddrShift, Endian> *&handler) const +template<int HighBits, int Width, int AddrShift> void handler_entry_read_dispatch<HighBits, Width, AddrShift>::lookup(offs_t address, offs_t &start, offs_t &end, handler_entry_read<Width, AddrShift> *&handler) const { offs_t slot = (address >> LowBits) & BITMASK; - auto h = m_dispatch[slot]; - if(h->is_dispatch()) + auto h = m_a_dispatch[slot]; + if(h->is_dispatch() || h->is_view()) h->lookup(address, start, end, handler); else { - start = m_ranges[slot].start; - end = m_ranges[slot].end; + start = m_a_ranges[slot].start; + end = m_a_ranges[slot].end; handler = h; } } -template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::range_cut_before(offs_t address, int start) +template<int HighBits, int Width, int AddrShift> void handler_entry_read_dispatch<HighBits, Width, AddrShift>::range_cut_before(offs_t address, int start) { - while(--start >= 0) { - if(int(LowBits) > -AddrShift && m_dispatch[start]->is_dispatch()) { - static_cast<handler_entry_read_dispatch<LowBits, Width, AddrShift, Endian> *>(m_dispatch[start])->range_cut_before(address); + while(--start >= 0 && m_u_dispatch[start]) { + if(int(LowBits) > -AddrShift && m_u_dispatch[start]->is_dispatch()) { + static_cast<handler_entry_read_dispatch<LowBits, Width, AddrShift> *>(m_u_dispatch[start])->range_cut_before(address); break; } - if(m_ranges[start].end <= address) + if(m_u_ranges[start].end <= address) break; - m_ranges[start].end = address; + m_u_ranges[start].end = address; } } -template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::range_cut_after(offs_t address, int start) +template<int HighBits, int Width, int AddrShift> void handler_entry_read_dispatch<HighBits, Width, AddrShift>::range_cut_after(offs_t address, int start) { - while(++start < COUNT) { - if(int(LowBits) > -AddrShift && m_dispatch[start]->is_dispatch()) { - static_cast<handler_entry_read_dispatch<LowBits, Width, AddrShift, Endian> *>(m_dispatch[start])->range_cut_after(address); + while(++start < COUNT && m_u_dispatch[start]) { + if(int(LowBits) > -AddrShift && m_u_dispatch[start]->is_dispatch()) { + static_cast<handler_entry_read_dispatch<LowBits, Width, AddrShift> *>(m_u_dispatch[start])->range_cut_after(address); break; } - if(m_ranges[start].start >= address) + if(m_u_ranges[start].start >= address) break; - m_ranges[start].start = address; + m_u_ranges[start].start = address; } } -template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::populate_nomirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_read<Width, AddrShift, Endian> *handler) +template<int HighBits, int Width, int AddrShift> void handler_entry_read_dispatch<HighBits, Width, AddrShift>::populate_nomirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_read<Width, AddrShift> *handler) { - auto cur = m_dispatch[entry]; + auto cur = m_u_dispatch[entry]; if(cur->is_dispatch()) cur->populate_nomirror(start, end, ostart, oend, handler); else { - auto subdispatch = new handler_entry_read_dispatch<LowBits, Width, AddrShift, Endian>(handler_entry::m_space, m_ranges[entry], cur); + auto subdispatch = new handler_entry_read_dispatch<LowBits, Width, AddrShift>(this->m_space, m_u_ranges[entry], cur); cur->unref(); - m_dispatch[entry] = subdispatch; + m_u_dispatch[entry] = subdispatch; subdispatch->populate_nomirror(start, end, ostart, oend, handler); + range_cut_before((entry << LowBits) - 1, entry); + range_cut_after((entry + 1) << LowBits, entry); } } -template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::populate_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_read<Width, AddrShift, Endian> *handler) +template<int HighBits, int Width, int AddrShift> void handler_entry_read_dispatch<HighBits, Width, AddrShift>::populate_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_read<Width, AddrShift> *handler) { - offs_t start_entry = start >> LowBits; - offs_t end_entry = end >> LowBits; + offs_t start_entry = (start & HIGHMASK) >> LowBits; + offs_t end_entry = (end & HIGHMASK) >> LowBits; range_cut_before(ostart-1, start_entry); range_cut_after(oend+1, end_entry); - if(LowBits <= Width + AddrShift) { + if constexpr(LowBits <= Width + AddrShift) { + if(handler->is_view()) { + int delta = dispatch_entry(ostart) - handler->dispatch_entry(ostart); + handler->init_handlers(start >> LowBits, end >> LowBits, LowBits, ostart, oend, m_u_dispatch + delta, m_u_ranges + delta); + } handler->ref(end_entry - start_entry); for(offs_t ent = start_entry; ent <= end_entry; ent++) { - m_dispatch[ent]->unref(); - m_dispatch[ent] = handler; - m_ranges[ent].set(ostart, oend); + m_u_dispatch[ent]->unref(); + m_u_dispatch[ent] = handler; + m_u_ranges[ent].set(ostart, oend); } } else if(start_entry == end_entry) { if(!(start & LOWMASK) && (end & LOWMASK) == LOWMASK) { - m_dispatch[start_entry]->unref(); - m_dispatch[start_entry] = handler; - m_ranges[start_entry].set(ostart, oend); + if(handler->is_view()) { + int delta = dispatch_entry(ostart) - handler->dispatch_entry(ostart); + handler->init_handlers(start >> LowBits, end >> LowBits, LowBits, ostart, oend, m_u_dispatch + delta, m_u_ranges + delta); + } + m_u_dispatch[start_entry]->unref(); + m_u_dispatch[start_entry] = handler; + m_u_ranges[start_entry].set(ostart, oend); } else - populate_nomirror_subdispatch(start_entry, start & LOWMASK, end & LOWMASK, ostart, oend, handler); + populate_nomirror_subdispatch(start_entry, start, end, ostart, oend, handler); } else { if(start & LOWMASK) { - populate_nomirror_subdispatch(start_entry, start & LOWMASK, LOWMASK, ostart, oend, handler); + populate_nomirror_subdispatch(start_entry, start, start | LOWMASK, ostart, oend, handler); start_entry++; + start = (start | LOWMASK) + 1; if(start_entry <= end_entry) handler->ref(); } if((end & LOWMASK) != LOWMASK) { - populate_nomirror_subdispatch(end_entry, 0, end & LOWMASK, ostart, oend, handler); + populate_nomirror_subdispatch(end_entry, end & ~LOWMASK, end, ostart, oend, handler); end_entry--; + end = (end & ~LOWMASK) - 1; if(start_entry <= end_entry) handler->ref(); } if(start_entry <= end_entry) { + if(handler->is_view()) { + int delta = dispatch_entry(ostart) - handler->dispatch_entry(ostart); + handler->init_handlers(start >> LowBits, end >> LowBits, LowBits, ostart, oend, m_u_dispatch + delta, m_u_ranges + delta); + } handler->ref(end_entry - start_entry); for(offs_t ent = start_entry; ent <= end_entry; ent++) { - m_dispatch[ent]->unref(); - m_dispatch[ent] = handler; - m_ranges[ent].set(ostart, oend); + m_u_dispatch[ent]->unref(); + m_u_dispatch[ent] = handler; + m_u_ranges[ent].set(ostart, oend); } } } } -template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::populate_mirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_read<Width, AddrShift, Endian> *handler) + +template<int HighBits, int Width, int AddrShift> void handler_entry_read_dispatch<HighBits, Width, AddrShift>::populate_mirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_read<Width, AddrShift> *handler) { - auto cur = m_dispatch[entry]; + auto cur = m_u_dispatch[entry]; if(cur->is_dispatch()) cur->populate_mirror(start, end, ostart, oend, mirror, handler); else { - auto subdispatch = new handler_entry_read_dispatch<LowBits, Width, AddrShift, Endian>(handler_entry::m_space, m_ranges[entry], cur); + auto subdispatch = new handler_entry_read_dispatch<LowBits, Width, AddrShift>(this->m_space, m_u_ranges[entry], cur); cur->unref(); - m_dispatch[entry] = subdispatch; + m_u_dispatch[entry] = subdispatch; subdispatch->populate_mirror(start, end, ostart, oend, mirror, handler); + range_cut_before((entry << LowBits) - 1, entry); + range_cut_after((entry + 1) << LowBits, entry); } } - -template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::populate_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_read<Width, AddrShift, Endian> *handler) +template<int HighBits, int Width, int AddrShift> void handler_entry_read_dispatch<HighBits, Width, AddrShift>::populate_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_read<Width, AddrShift> *handler) { offs_t hmirror = mirror & HIGHMASK; offs_t lmirror = mirror & LOWMASK; @@ -185,12 +306,10 @@ template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_ offs_t add = 1 + ~hmirror; offs_t offset = 0; offs_t base_entry = start >> LowBits; - start &= LOWMASK; - end &= LOWMASK; do { if(offset) handler->ref(); - populate_mirror_subdispatch(base_entry | (offset >> LowBits), start, end, ostart | offset, oend | offset, lmirror, handler); + populate_mirror_subdispatch(base_entry | (offset >> LowBits), start | offset, end | offset, ostart | offset, oend | offset, lmirror, handler); offset = (offset + add) & hmirror; } while(offset); } else { @@ -206,11 +325,11 @@ template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_ } } -template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::mismatched_patch(const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 rkey, std::vector<mapping> &mappings, handler_entry_read<Width, AddrShift, Endian> *&target) +template<int HighBits, int Width, int AddrShift> void handler_entry_read_dispatch<HighBits, Width, AddrShift>::mismatched_patch(const memory_units_descriptor<Width, AddrShift> &descriptor, u8 rkey, std::vector<mapping> &mappings, handler_entry_read<Width, AddrShift> *&target) { u8 ukey = descriptor.rkey_to_ukey(rkey); - handler_entry_read<Width, AddrShift, Endian> *original = target->is_units() ? target : nullptr; - handler_entry_read<Width, AddrShift, Endian> *replacement = nullptr; + handler_entry_read<Width, AddrShift> *original = target->is_units() ? target : nullptr; + handler_entry_read<Width, AddrShift> *replacement = nullptr; for(const auto &p : mappings) if(p.ukey == ukey && p.original == original) { replacement = p.patched; @@ -218,9 +337,9 @@ template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_ } if(!replacement) { if(original) - replacement = new handler_entry_read_units<Width, AddrShift, Endian>(descriptor, ukey, static_cast<handler_entry_read_units<Width, AddrShift, Endian> *>(original)); + replacement = new handler_entry_read_units<Width, AddrShift>(descriptor, ukey, static_cast<handler_entry_read_units<Width, AddrShift> *>(original)); else - replacement = new handler_entry_read_units<Width, AddrShift, Endian>(descriptor, ukey, inh::m_space); + replacement = new handler_entry_read_units<Width, AddrShift>(descriptor, ukey, this->m_space); mappings.emplace_back(mapping{ original, replacement, ukey }); } else @@ -229,92 +348,91 @@ template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_ target = replacement; } -template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::populate_mismatched_nomirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 rkey, std::vector<mapping> &mappings) +template<int HighBits, int Width, int AddrShift> void handler_entry_read_dispatch<HighBits, Width, AddrShift>::populate_mismatched_nomirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, const memory_units_descriptor<Width, AddrShift> &descriptor, u8 rkey, std::vector<mapping> &mappings) { - auto cur = m_dispatch[entry]; - if(cur->is_dispatch()) + auto cur = m_u_dispatch[entry]; + if(cur->is_dispatch() && !cur->is_view()) cur->populate_mismatched_nomirror(start, end, ostart, oend, descriptor, rkey, mappings); else { - auto subdispatch = new handler_entry_read_dispatch<LowBits, Width, AddrShift, Endian>(handler_entry::m_space, m_ranges[entry], cur); + auto subdispatch = new handler_entry_read_dispatch<LowBits, Width, AddrShift>(this->m_space, m_u_ranges[entry], cur); cur->unref(); - m_dispatch[entry] = subdispatch; + m_u_dispatch[entry] = subdispatch; subdispatch->populate_mismatched_nomirror(start, end, ostart, oend, descriptor, rkey, mappings); } } -template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::populate_mismatched_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 rkey, std::vector<mapping> &mappings) +template<int HighBits, int Width, int AddrShift> void handler_entry_read_dispatch<HighBits, Width, AddrShift>::populate_mismatched_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, const memory_units_descriptor<Width, AddrShift> &descriptor, u8 rkey, std::vector<mapping> &mappings) { - offs_t start_entry = start >> LowBits; - offs_t end_entry = end >> LowBits; + offs_t start_entry = (start & HIGHMASK) >> LowBits; + offs_t end_entry = (end & HIGHMASK) >> LowBits; range_cut_before(ostart-1, start_entry); range_cut_after(oend+1, end_entry); - if(LowBits <= Width + AddrShift) { + if constexpr(LowBits <= Width + AddrShift) { for(offs_t ent = start_entry; ent <= end_entry; ent++) { u8 rkey1 = rkey; if(ent != start_entry) rkey1 &= ~handler_entry::START; if(ent != end_entry) rkey1 &= ~handler_entry::END; - mismatched_patch(descriptor, rkey1, mappings, m_dispatch[ent]); - m_ranges[ent].set(ostart, oend); + mismatched_patch(descriptor, rkey1, mappings, m_u_dispatch[ent]); + m_u_ranges[ent].set(ostart, oend); } } else if(start_entry == end_entry) { if(!(start & LOWMASK) && (end & LOWMASK) == LOWMASK) { - if(m_dispatch[start_entry]->is_dispatch()) - m_dispatch[start_entry]->populate_mismatched_nomirror(start & LOWMASK, end & LOWMASK, ostart, oend, descriptor, rkey, mappings); + if(m_u_dispatch[start_entry]->is_dispatch()) + m_u_dispatch[start_entry]->populate_mismatched_nomirror(start, end, ostart, oend, descriptor, rkey, mappings); else { - mismatched_patch(descriptor, rkey, mappings, m_dispatch[start_entry]); - m_ranges[start_entry].set(ostart, oend); + mismatched_patch(descriptor, rkey, mappings, m_u_dispatch[start_entry]); + m_u_ranges[start_entry].set(ostart, oend); } } else - populate_mismatched_nomirror_subdispatch(start_entry, start & LOWMASK, end & LOWMASK, ostart, oend, descriptor, rkey, mappings); + populate_mismatched_nomirror_subdispatch(start_entry, start, end, ostart, oend, descriptor, rkey, mappings); } else { if(start & LOWMASK) { - populate_mismatched_nomirror_subdispatch(start_entry, start & LOWMASK, LOWMASK, ostart, oend, descriptor, rkey & ~handler_entry::END, mappings); + populate_mismatched_nomirror_subdispatch(start_entry, start, start | LOWMASK, ostart, oend, descriptor, rkey & ~handler_entry::END, mappings); start_entry++; rkey &= ~handler_entry::START; } if((end & LOWMASK) != LOWMASK) { - populate_mismatched_nomirror_subdispatch(end_entry, 0, end & LOWMASK, ostart, oend, descriptor, rkey & ~handler_entry::START, mappings); + populate_mismatched_nomirror_subdispatch(end_entry, end & ~LOWMASK, end, ostart, oend, descriptor, rkey & ~handler_entry::START, mappings); end_entry--; rkey &= ~handler_entry::END; } - if(start_entry <= end_entry) { - for(offs_t ent = start_entry; ent <= end_entry; ent++) { - u8 rkey1 = rkey; - if(ent != start_entry) - rkey1 &= ~handler_entry::START; - if(ent != end_entry) - rkey1 &= ~handler_entry::END; - if(m_dispatch[ent]->is_dispatch()) - m_dispatch[ent]->populate_mismatched_nomirror(start & LOWMASK, end & LOWMASK, ostart, oend, descriptor, rkey1, mappings); - else { - mismatched_patch(descriptor, rkey1, mappings, m_dispatch[ent]); - m_ranges[ent].set(ostart, oend); - } + offs_t base = start & ~LOWMASK; + for(offs_t ent = start_entry; ent <= end_entry; ent++) { + u8 rkey1 = rkey; + if(ent != start_entry) + rkey1 &= ~handler_entry::START; + if(ent != end_entry) + rkey1 &= ~handler_entry::END; + if(m_u_dispatch[ent]->is_dispatch()) + m_u_dispatch[ent]->populate_mismatched_nomirror(base | (ent << LowBits), base | (ent << LowBits) | LOWMASK, ostart, oend, descriptor, rkey1, mappings); + else { + mismatched_patch(descriptor, rkey1, mappings, m_u_dispatch[ent]); + m_u_ranges[ent].set(ostart, oend); } } } } -template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::populate_mismatched_mirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, std::vector<mapping> &mappings) +template<int HighBits, int Width, int AddrShift> void handler_entry_read_dispatch<HighBits, Width, AddrShift>::populate_mismatched_mirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, const memory_units_descriptor<Width, AddrShift> &descriptor, std::vector<mapping> &mappings) { - auto cur = m_dispatch[entry]; + auto cur = m_u_dispatch[entry]; if(cur->is_dispatch()) cur->populate_mismatched_mirror(start, end, ostart, oend, mirror, descriptor, mappings); else { - auto subdispatch = new handler_entry_read_dispatch<LowBits, Width, AddrShift, Endian>(handler_entry::m_space, m_ranges[entry], cur); + auto subdispatch = new handler_entry_read_dispatch<LowBits, Width, AddrShift>(this->m_space, m_u_ranges[entry], cur); cur->unref(); - m_dispatch[entry] = subdispatch; + m_u_dispatch[entry] = subdispatch; subdispatch->populate_mismatched_mirror(start, end, ostart, oend, mirror, descriptor, mappings); } } -template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::populate_mismatched_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, std::vector<mapping> &mappings) +template<int HighBits, int Width, int AddrShift> void handler_entry_read_dispatch<HighBits, Width, AddrShift>::populate_mismatched_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, const memory_units_descriptor<Width, AddrShift> &descriptor, std::vector<mapping> &mappings) { offs_t hmirror = mirror & HIGHMASK; offs_t lmirror = mirror & LOWMASK; @@ -324,8 +442,6 @@ template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_ offs_t add = 1 + ~hmirror; offs_t offset = 0; offs_t base_entry = start >> LowBits; - start &= LOWMASK; - end &= LOWMASK; do { populate_mismatched_mirror_subdispatch(base_entry | (offset >> LowBits), start, end, ostart | offset, oend | offset, lmirror, descriptor, mappings); offset = (offset + add) & hmirror; @@ -341,95 +457,139 @@ template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_ } } - -template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::passthrough_patch(handler_entry_read_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings, handler_entry_read<Width, AddrShift, Endian> *&target) +template<int HighBits, int Width, int AddrShift> void handler_entry_read_dispatch<HighBits, Width, AddrShift>::passthrough_patch(handler_entry_read_passthrough<Width, AddrShift> *handler, std::vector<mapping> &mappings, handler_entry_read<Width, AddrShift> *&target) { - handler_entry_read<Width, AddrShift, Endian> *original = target; - handler_entry_read<Width, AddrShift, Endian> *replacement = nullptr; + // Look in cache first for(const auto &p : mappings) - if(p.original == original) { - replacement = p.patched; - break; + if(p.original == target) { + p.patched->ref(); + target->unref(); + target = p.patched; + return; } - if(!replacement) { - replacement = handler->instantiate(original); + + handler_entry_read<Width, AddrShift> *original = target; + u32 target_priority = original->f_get_pt(); + u32 new_priority = handler->f_get_pt(); + + // 3 cases: new one on top, new one on bottom, or new one replaces old one + + if(!target_priority || new_priority > target_priority || (new_priority == target_priority && !(new_priority & 1))) { + // New one goes over the old one + // previous one is not passthrough (target_priority = 0) + // new one has higher priority + // both have the same priority, and the priority is even which means keep both + + // We instantiate the new one with the old one under it and put it in place and in the cache + + handler_entry_read<Width, AddrShift> *replacement = handler->instantiate(original); mappings.emplace_back(mapping{ original, replacement }); - } else - replacement->ref(); - target->unref(); - target = replacement; + target->unref(); + target = replacement; + + } else if(new_priority == target_priority) { + // New one replaces the old one + // both have the same priority, and the priority is odd which means keep only the new one + + // We instantiate the new one with the old one's subtarget and put it in place and in the cache + + handler_entry_read<Width, AddrShift> *replacement = handler->instantiate(static_cast<handler_entry_read_passthrough<Width, AddrShift> *>(original)->get_subhandler()); + mappings.emplace_back(mapping{ original, replacement }); + target->unref(); + target = replacement; + + } else { + // New one goes under the old one + // both are passthrough and new one has lower priority + // + // This can be recursive, so do the passthrough patch on the subhandler, then instantiate the old one with the result + + handler_entry_read<Width, AddrShift> *recursive = static_cast<handler_entry_read_passthrough<Width, AddrShift> *>(original)->get_subhandler(); + recursive->ref(); + + passthrough_patch(handler, mappings, recursive); + + handler_entry_read<Width, AddrShift> *replacement = static_cast<handler_entry_read_passthrough<Width, AddrShift> *>(original)->instantiate(recursive); + mappings.emplace_back(mapping{ original, replacement }); + target->unref(); + target = replacement; + recursive->unref(); + } } -template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::populate_passthrough_nomirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_read_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings) +template<int HighBits, int Width, int AddrShift> void handler_entry_read_dispatch<HighBits, Width, AddrShift>::populate_passthrough_nomirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_read_passthrough<Width, AddrShift> *handler, std::vector<mapping> &mappings) { - auto cur = m_dispatch[entry]; + auto cur = m_u_dispatch[entry]; if(cur->is_dispatch()) cur->populate_passthrough_nomirror(start, end, ostart, oend, handler, mappings); else { - auto subdispatch = new handler_entry_read_dispatch<LowBits, Width, AddrShift, Endian>(handler_entry::m_space, m_ranges[entry], cur); + auto subdispatch = new handler_entry_read_dispatch<LowBits, Width, AddrShift>(this->m_space, m_u_ranges[entry], cur); cur->unref(); - m_dispatch[entry] = subdispatch; + m_u_dispatch[entry] = subdispatch; subdispatch->populate_passthrough_nomirror(start, end, ostart, oend, handler, mappings); } } -template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::populate_passthrough_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_read_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings) +template<int HighBits, int Width, int AddrShift> void handler_entry_read_dispatch<HighBits, Width, AddrShift>::populate_passthrough_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_read_passthrough<Width, AddrShift> *handler, std::vector<mapping> &mappings) { offs_t start_entry = (start & HIGHMASK) >> LowBits; offs_t end_entry = (end & HIGHMASK) >> LowBits; range_cut_before(ostart-1, start_entry); range_cut_after(oend+1, end_entry); - if(LowBits <= Width + AddrShift) { + if constexpr(LowBits <= Width + AddrShift) { for(offs_t ent = start_entry; ent <= end_entry; ent++) { - passthrough_patch(handler, mappings, m_dispatch[ent]); - m_ranges[ent].intersect(ostart, oend); + passthrough_patch(handler, mappings, m_u_dispatch[ent]); + m_u_ranges[ent].intersect(ostart, oend); } } else if(start_entry == end_entry) { if(!(start & LOWMASK) && (end & LOWMASK) == LOWMASK) { - passthrough_patch(handler, mappings, m_dispatch[start_entry]); - m_ranges[start_entry].intersect(ostart, oend); + if(m_u_dispatch[start_entry]->is_dispatch()) + m_u_dispatch[start_entry]->populate_passthrough_nomirror(start, end, ostart, oend, handler, mappings); + else { + passthrough_patch(handler, mappings, m_u_dispatch[start_entry]); + m_u_ranges[start_entry].intersect(ostart, oend); + } } else - populate_passthrough_nomirror_subdispatch(start_entry, start & LOWMASK, end & LOWMASK, ostart, oend, handler, mappings); + populate_passthrough_nomirror_subdispatch(start_entry, start, end, ostart, oend, handler, mappings); } else { if(start & LOWMASK) { - populate_passthrough_nomirror_subdispatch(start_entry, start & LOWMASK, LOWMASK, ostart, oend, handler, mappings); + populate_passthrough_nomirror_subdispatch(start_entry, start, start | LOWMASK, ostart, oend, handler, mappings); start_entry++; } if((end & LOWMASK) != LOWMASK) { - populate_passthrough_nomirror_subdispatch(end_entry, 0, end & LOWMASK, ostart, oend, handler, mappings); + populate_passthrough_nomirror_subdispatch(end_entry, end & ~LOWMASK, end, ostart, oend, handler, mappings); end_entry--; } - if(start_entry <= end_entry) { - for(offs_t ent = start_entry; ent <= end_entry; ent++) { - if(m_dispatch[ent]->is_dispatch()) - m_dispatch[ent]->populate_passthrough_nomirror(start & LOWMASK, end & LOWMASK, ostart, oend, handler, mappings); - else { - passthrough_patch(handler, mappings, m_dispatch[ent]); - m_ranges[ent].intersect(ostart, oend); - } + offs_t base = start & ~LOWMASK; + for(offs_t ent = start_entry; ent <= end_entry; ent++) { + if(m_u_dispatch[ent]->is_dispatch()) + m_u_dispatch[ent]->populate_passthrough_nomirror(base | (ent << LowBits), base | (ent << LowBits) | LOWMASK, ostart, oend, handler, mappings); + else { + passthrough_patch(handler, mappings, m_u_dispatch[ent]); + m_u_ranges[ent].intersect(ostart, oend); } } } } -template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::populate_passthrough_mirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_read_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings) +template<int HighBits, int Width, int AddrShift> void handler_entry_read_dispatch<HighBits, Width, AddrShift>::populate_passthrough_mirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_read_passthrough<Width, AddrShift> *handler, std::vector<mapping> &mappings) { - auto cur = m_dispatch[entry]; + auto cur = m_u_dispatch[entry]; if(cur->is_dispatch()) cur->populate_passthrough_mirror(start, end, ostart, oend, mirror, handler, mappings); else { - auto subdispatch = new handler_entry_read_dispatch<LowBits, Width, AddrShift, Endian>(handler_entry::m_space, m_ranges[entry], cur); + auto subdispatch = new handler_entry_read_dispatch<LowBits, Width, AddrShift>(this->m_space, m_u_ranges[entry], cur); cur->unref(); - m_dispatch[entry] = subdispatch; + m_u_dispatch[entry] = subdispatch; subdispatch->populate_passthrough_mirror(start, end, ostart, oend, mirror, handler, mappings); } } -template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::populate_passthrough_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_read_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings) +template<int HighBits, int Width, int AddrShift> void handler_entry_read_dispatch<HighBits, Width, AddrShift>::populate_passthrough_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_read_passthrough<Width, AddrShift> *handler, std::vector<mapping> &mappings) { offs_t hmirror = mirror & HIGHMASK; offs_t lmirror = mirror & LOWMASK; @@ -439,10 +599,8 @@ template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_ offs_t add = 1 + ~hmirror; offs_t offset = 0; offs_t base_entry = start >> LowBits; - start &= LOWMASK; - end &= LOWMASK; do { - populate_passthrough_mirror_subdispatch(base_entry | (offset >> LowBits), start, end, ostart | offset, oend | offset, lmirror, handler, mappings); + populate_passthrough_mirror_subdispatch(base_entry | (offset >> LowBits), start | offset, end | offset, ostart | offset, oend | offset, lmirror, handler, mappings); offset = (offset + add) & hmirror; } while(offset); } else { @@ -456,25 +614,126 @@ template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_ } } -template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_read_dispatch<HighBits, Width, AddrShift, Endian>::detach(const std::unordered_set<handler_entry *> &handlers) +template<int HighBits, int Width, int AddrShift> void handler_entry_read_dispatch<HighBits, Width, AddrShift>::detach(const std::unordered_set<handler_entry *> &handlers) { for(unsigned int i=0; i != COUNT; i++) { - if(m_dispatch[i]->is_dispatch()) { - m_dispatch[i]->detach(handlers); + if(m_u_dispatch[i]->is_dispatch()) { + m_u_dispatch[i]->detach(handlers); continue; } - if(!m_dispatch[i]->is_passthrough()) + if(!m_u_dispatch[i]->is_passthrough()) continue; - auto np = static_cast<handler_entry_read_passthrough<Width, AddrShift, Endian> *>(m_dispatch[i]); + auto np = static_cast<handler_entry_read_passthrough<Width, AddrShift> *>(m_u_dispatch[i]); if(handlers.find(np) != handlers.end()) { - m_dispatch[i] = np->get_subhandler(); - m_dispatch[i]->ref(); + m_u_dispatch[i] = np->get_subhandler(); + m_u_dispatch[i]->ref(); np->unref(); } else np->detach(handlers); } } + +template<int HighBits, int Width, int AddrShift> void handler_entry_read_dispatch<HighBits, Width, AddrShift>::init_handlers(offs_t start_entry, offs_t end_entry, u32 lowbits, offs_t ostart, offs_t oend, handler_entry_read<Width, AddrShift> **dispatch, handler_entry::range *ranges) +{ + if(lowbits < LowBits) { + offs_t entry = start_entry >> LowBits; + if(entry != (end_entry >> LowBits)) + fatalerror("Recursive init_handlers spanning multiple entries.\n"); + entry &= BITMASK; + handler_entry_read_dispatch<LowBits, Width, AddrShift> *subdispatch; + if(m_u_dispatch[entry]->flags() & handler_entry::F_DISPATCH) + subdispatch = static_cast<handler_entry_read_dispatch<LowBits, Width, AddrShift> *>(m_u_dispatch[entry]); + + else if(!(m_u_dispatch[entry]->flags() & handler_entry::F_UNMAP)) + fatalerror("Collision on multiple init_handlers calls"); + + else { + m_u_dispatch[entry]->unref(); + m_u_dispatch[entry] = subdispatch = new handler_entry_read_dispatch<LowBits, Width, AddrShift>(this->m_space, m_u_ranges[entry], nullptr); + } + int delta = dispatch_entry(ostart) - subdispatch->dispatch_entry(ostart); + subdispatch->init_handlers(start_entry, end_entry, lowbits, ostart, oend, dispatch + delta, ranges + delta); + + } else if(lowbits != LowBits) { + u32 dt = lowbits - LowBits; + u32 ne = 1 << dt; + u32 ee = end_entry - start_entry; + auto filter = [s = m_global_range.start, e = m_global_range.end] (handler_entry::range r) { r.intersect(s, e); return r; }; + for(offs_t entry = 0; entry <= ee; entry++) { + dispatch[entry]->ref(ne); + u32 e0 = (entry << dt) & BITMASK; + for(offs_t e = 0; e != ne; e++) { + offs_t e1 = e0 | e; + if(!(m_u_dispatch[e1]->flags() & handler_entry::F_UNMAP)) + fatalerror("Collision on multiple init_handlers calls"); + m_u_dispatch[e1]->unref(); + m_u_dispatch[e1] = dispatch[entry]; + m_u_ranges[e1] = filter(ranges[entry]); + } + } + + } else { + auto filter = [s = m_global_range.start, e = m_global_range.end] (handler_entry::range r) { r.intersect(s, e); return r; }; + for(offs_t entry = start_entry & BITMASK; entry <= (end_entry & BITMASK); entry++) { + if(!(m_u_dispatch[entry]->flags() & handler_entry::F_UNMAP)) + fatalerror("Collision on multiple init_handlers calls"); + m_u_dispatch[entry]->unref(); + m_u_dispatch[entry] = dispatch[entry]; + m_u_ranges[entry] = filter(ranges[entry]); + dispatch[entry]->ref(); + } + } +} + +template<int HighBits, int Width, int AddrShift> void handler_entry_read_dispatch<HighBits, Width, AddrShift>::select_a(int id) +{ + u32 i = id+1; + if(i >= m_dispatch_array.size()) + fatalerror("out-of-range view selection."); + + m_a_ranges = m_ranges_array[i].data(); + m_a_dispatch = m_dispatch_array[i].data(); +} + +template<int HighBits, int Width, int AddrShift> void handler_entry_read_dispatch<HighBits, Width, AddrShift>::select_u(int id) +{ + u32 i = id+1; + if(i > m_dispatch_array.size()) + fatalerror("out-of-range view update selection."); + else if(i == m_dispatch_array.size()) { + u32 aid = (handler_array *)(m_a_dispatch) - m_dispatch_array.data(); + + m_dispatch_array.resize(i+1); + m_ranges_array.resize(i+1); + m_a_ranges = m_ranges_array[aid].data(); + m_a_dispatch = m_dispatch_array[aid].data(); + m_u_ranges = m_ranges_array[i].data(); + m_u_dispatch = m_dispatch_array[i].data(); + + for(u32 entry = 0; entry != COUNT; entry++) + if(m_dispatch_array[0][entry]) { + m_u_dispatch[entry] = m_dispatch_array[0][entry]->dup(); + m_u_ranges[entry] = m_ranges_array[0][entry]; + } + + } else { + m_u_ranges = m_ranges_array[i].data(); + m_u_dispatch = m_dispatch_array[i].data(); + } +} + +template<int HighBits, int Width, int AddrShift> handler_entry_read<Width, AddrShift> *handler_entry_read_dispatch<HighBits, Width, AddrShift>::dup() +{ + if(m_view) { + handler_entry::ref(); + return this; + } + + return new handler_entry_read_dispatch<HighBits, Width, AddrShift>(this); +} + +#endif // MAME_EMU_EMUMEM_HEDR_IPP diff --git a/src/emu/emumem_hedr0.cpp b/src/emu/emumem_hedr0.cpp index 92deb472e03..aeddb8dce6f 100644 --- a/src/emu/emumem_hedr0.cpp +++ b/src/emu/emumem_hedr0.cpp @@ -5,184 +5,120 @@ #include "emumem_hedr.ipp" -template class handler_entry_read_dispatch< 1, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 1, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 2, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 2, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 3, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 3, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 4, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 4, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 5, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 5, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 6, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 6, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 7, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 7, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 8, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 8, 0, 1, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch< 0, 0, 1>; +template class handler_entry_read_dispatch< 1, 0, 1>; +template class handler_entry_read_dispatch< 2, 0, 1>; +template class handler_entry_read_dispatch< 3, 0, 1>; +template class handler_entry_read_dispatch< 4, 0, 1>; +template class handler_entry_read_dispatch< 5, 0, 1>; +template class handler_entry_read_dispatch< 6, 0, 1>; +template class handler_entry_read_dispatch< 7, 0, 1>; +template class handler_entry_read_dispatch< 8, 0, 1>; -template class handler_entry_read_dispatch< 1, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 1, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 2, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 2, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 3, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 3, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 4, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 4, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 5, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 5, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 6, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 6, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 7, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 7, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 8, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 8, 0, 0, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch< 0, 0, 0>; +template class handler_entry_read_dispatch< 1, 0, 0>; +template class handler_entry_read_dispatch< 2, 0, 0>; +template class handler_entry_read_dispatch< 3, 0, 0>; +template class handler_entry_read_dispatch< 4, 0, 0>; +template class handler_entry_read_dispatch< 5, 0, 0>; +template class handler_entry_read_dispatch< 6, 0, 0>; +template class handler_entry_read_dispatch< 7, 0, 0>; +template class handler_entry_read_dispatch< 8, 0, 0>; -template class handler_entry_read_dispatch< 1, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 1, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 2, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 2, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 3, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 3, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 4, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 4, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 5, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 5, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 6, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 6, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 7, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 7, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 8, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 8, 1, 3, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch< 0, 1, 3>; +template class handler_entry_read_dispatch< 1, 1, 3>; +template class handler_entry_read_dispatch< 2, 1, 3>; +template class handler_entry_read_dispatch< 3, 1, 3>; +template class handler_entry_read_dispatch< 4, 1, 3>; +template class handler_entry_read_dispatch< 5, 1, 3>; +template class handler_entry_read_dispatch< 6, 1, 3>; +template class handler_entry_read_dispatch< 7, 1, 3>; +template class handler_entry_read_dispatch< 8, 1, 3>; -template class handler_entry_read_dispatch< 1, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 1, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 2, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 2, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 3, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 3, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 4, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 4, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 5, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 5, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 6, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 6, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 7, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 7, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 8, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 8, 1, 0, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch< 1, 1, 0>; +template class handler_entry_read_dispatch< 2, 1, 0>; +template class handler_entry_read_dispatch< 3, 1, 0>; +template class handler_entry_read_dispatch< 4, 1, 0>; +template class handler_entry_read_dispatch< 5, 1, 0>; +template class handler_entry_read_dispatch< 6, 1, 0>; +template class handler_entry_read_dispatch< 7, 1, 0>; +template class handler_entry_read_dispatch< 8, 1, 0>; -template class handler_entry_read_dispatch< 1, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 1, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 2, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 2, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 3, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 3, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 4, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 4, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 5, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 5, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 6, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 6, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 7, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 7, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 8, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 8, 1, -1, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch< 0, 1, -1>; +template class handler_entry_read_dispatch< 1, 1, -1>; +template class handler_entry_read_dispatch< 2, 1, -1>; +template class handler_entry_read_dispatch< 3, 1, -1>; +template class handler_entry_read_dispatch< 4, 1, -1>; +template class handler_entry_read_dispatch< 5, 1, -1>; +template class handler_entry_read_dispatch< 6, 1, -1>; +template class handler_entry_read_dispatch< 7, 1, -1>; +template class handler_entry_read_dispatch< 8, 1, -1>; -template class handler_entry_read_dispatch< 2, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 2, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 3, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 3, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 4, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 4, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 5, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 5, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 6, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 6, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 7, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 7, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 8, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 8, 2, 0, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch< 2, 2, 3>; +template class handler_entry_read_dispatch< 3, 2, 3>; +template class handler_entry_read_dispatch< 4, 2, 3>; +template class handler_entry_read_dispatch< 5, 2, 3>; +template class handler_entry_read_dispatch< 6, 2, 3>; +template class handler_entry_read_dispatch< 7, 2, 3>; +template class handler_entry_read_dispatch< 8, 2, 3>; -template class handler_entry_read_dispatch< 2, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 2, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 3, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 3, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 4, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 4, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 5, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 5, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 6, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 6, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 7, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 7, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 8, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 8, 2, -1, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch< 2, 2, 0>; +template class handler_entry_read_dispatch< 3, 2, 0>; +template class handler_entry_read_dispatch< 4, 2, 0>; +template class handler_entry_read_dispatch< 5, 2, 0>; +template class handler_entry_read_dispatch< 6, 2, 0>; +template class handler_entry_read_dispatch< 7, 2, 0>; +template class handler_entry_read_dispatch< 8, 2, 0>; -template class handler_entry_read_dispatch< 2, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 2, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 3, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 3, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 4, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 4, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 5, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 5, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 6, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 6, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 7, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 7, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 8, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 8, 2, -2, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch< 1, 2, -1>; +template class handler_entry_read_dispatch< 2, 2, -1>; +template class handler_entry_read_dispatch< 3, 2, -1>; +template class handler_entry_read_dispatch< 4, 2, -1>; +template class handler_entry_read_dispatch< 5, 2, -1>; +template class handler_entry_read_dispatch< 6, 2, -1>; +template class handler_entry_read_dispatch< 7, 2, -1>; +template class handler_entry_read_dispatch< 8, 2, -1>; -template class handler_entry_read_dispatch< 3, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 3, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 4, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 4, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 5, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 5, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 6, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 6, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 7, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 7, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 8, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 8, 3, 0, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch< 0, 2, -2>; +template class handler_entry_read_dispatch< 1, 2, -2>; +template class handler_entry_read_dispatch< 2, 2, -2>; +template class handler_entry_read_dispatch< 3, 2, -2>; +template class handler_entry_read_dispatch< 4, 2, -2>; +template class handler_entry_read_dispatch< 5, 2, -2>; +template class handler_entry_read_dispatch< 6, 2, -2>; +template class handler_entry_read_dispatch< 7, 2, -2>; +template class handler_entry_read_dispatch< 8, 2, -2>; -template class handler_entry_read_dispatch< 3, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 3, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 4, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 4, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 5, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 5, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 6, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 6, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 7, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 7, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 8, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 8, 3, -1, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch< 3, 3, 0>; +template class handler_entry_read_dispatch< 4, 3, 0>; +template class handler_entry_read_dispatch< 5, 3, 0>; +template class handler_entry_read_dispatch< 6, 3, 0>; +template class handler_entry_read_dispatch< 7, 3, 0>; +template class handler_entry_read_dispatch< 8, 3, 0>; -template class handler_entry_read_dispatch< 3, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 3, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 4, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 4, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 5, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 5, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 6, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 6, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 7, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 7, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 8, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 8, 3, -2, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch< 2, 3, -1>; +template class handler_entry_read_dispatch< 3, 3, -1>; +template class handler_entry_read_dispatch< 4, 3, -1>; +template class handler_entry_read_dispatch< 5, 3, -1>; +template class handler_entry_read_dispatch< 6, 3, -1>; +template class handler_entry_read_dispatch< 7, 3, -1>; +template class handler_entry_read_dispatch< 8, 3, -1>; -template class handler_entry_read_dispatch< 3, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 3, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 4, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 4, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 5, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 5, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 6, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 6, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 7, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 7, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch< 8, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 8, 3, -3, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch< 1, 3, -2>; +template class handler_entry_read_dispatch< 2, 3, -2>; +template class handler_entry_read_dispatch< 3, 3, -2>; +template class handler_entry_read_dispatch< 4, 3, -2>; +template class handler_entry_read_dispatch< 5, 3, -2>; +template class handler_entry_read_dispatch< 6, 3, -2>; +template class handler_entry_read_dispatch< 7, 3, -2>; +template class handler_entry_read_dispatch< 8, 3, -2>; + +template class handler_entry_read_dispatch< 0, 3, -3>; +template class handler_entry_read_dispatch< 1, 3, -3>; +template class handler_entry_read_dispatch< 2, 3, -3>; +template class handler_entry_read_dispatch< 3, 3, -3>; +template class handler_entry_read_dispatch< 4, 3, -3>; +template class handler_entry_read_dispatch< 5, 3, -3>; +template class handler_entry_read_dispatch< 6, 3, -3>; +template class handler_entry_read_dispatch< 7, 3, -3>; +template class handler_entry_read_dispatch< 8, 3, -3>; diff --git a/src/emu/emumem_hedr1.cpp b/src/emu/emumem_hedr1.cpp index 86aa8452791..d28a67bbe34 100644 --- a/src/emu/emumem_hedr1.cpp +++ b/src/emu/emumem_hedr1.cpp @@ -4,206 +4,119 @@ #include "emu.h" #include "emumem_hedr.ipp" -template class handler_entry_read_dispatch< 9, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 9, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<10, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<10, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<11, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<11, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<12, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<12, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<13, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<13, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<14, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<14, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<15, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<15, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<16, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<16, 0, 1, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch< 9, 0, 1>; +template class handler_entry_read_dispatch<10, 0, 1>; +template class handler_entry_read_dispatch<11, 0, 1>; +template class handler_entry_read_dispatch<12, 0, 1>; +template class handler_entry_read_dispatch<13, 0, 1>; +template class handler_entry_read_dispatch<14, 0, 1>; +template class handler_entry_read_dispatch<15, 0, 1>; +template class handler_entry_read_dispatch<16, 0, 1>; -template class handler_entry_read_dispatch< 9, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 9, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<10, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<10, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<11, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<11, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<12, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<12, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<13, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<13, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<14, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<14, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<15, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<15, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<16, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<16, 0, 0, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch< 9, 0, 0>; +template class handler_entry_read_dispatch<10, 0, 0>; +template class handler_entry_read_dispatch<11, 0, 0>; +template class handler_entry_read_dispatch<12, 0, 0>; +template class handler_entry_read_dispatch<13, 0, 0>; +template class handler_entry_read_dispatch<14, 0, 0>; +template class handler_entry_read_dispatch<15, 0, 0>; +template class handler_entry_read_dispatch<16, 0, 0>; -template class handler_entry_read_dispatch< 9, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 9, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<10, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<10, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<11, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<11, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<12, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<12, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<13, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<13, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<14, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<14, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<15, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<15, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<16, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<16, 1, 3, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch< 9, 1, 3>; +template class handler_entry_read_dispatch<10, 1, 3>; +template class handler_entry_read_dispatch<11, 1, 3>; +template class handler_entry_read_dispatch<12, 1, 3>; +template class handler_entry_read_dispatch<13, 1, 3>; +template class handler_entry_read_dispatch<14, 1, 3>; +template class handler_entry_read_dispatch<15, 1, 3>; +template class handler_entry_read_dispatch<16, 1, 3>; -template class handler_entry_read_dispatch< 9, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 9, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<10, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<10, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<11, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<11, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<12, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<12, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<13, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<13, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<14, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<14, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<15, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<15, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<16, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<16, 1, 0, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch< 9, 1, 0>; +template class handler_entry_read_dispatch<10, 1, 0>; +template class handler_entry_read_dispatch<11, 1, 0>; +template class handler_entry_read_dispatch<12, 1, 0>; +template class handler_entry_read_dispatch<13, 1, 0>; +template class handler_entry_read_dispatch<14, 1, 0>; +template class handler_entry_read_dispatch<15, 1, 0>; +template class handler_entry_read_dispatch<16, 1, 0>; -template class handler_entry_read_dispatch< 9, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 9, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<10, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<10, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<11, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<11, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<12, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<12, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<13, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<13, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<14, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<14, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<15, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<15, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<16, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<16, 1, -1, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch< 9, 1, -1>; +template class handler_entry_read_dispatch<10, 1, -1>; +template class handler_entry_read_dispatch<11, 1, -1>; +template class handler_entry_read_dispatch<12, 1, -1>; +template class handler_entry_read_dispatch<13, 1, -1>; +template class handler_entry_read_dispatch<14, 1, -1>; +template class handler_entry_read_dispatch<15, 1, -1>; +template class handler_entry_read_dispatch<16, 1, -1>; -template class handler_entry_read_dispatch< 9, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 9, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<10, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<10, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<11, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<11, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<12, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<12, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<13, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<13, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<14, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<14, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<15, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<15, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<16, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<16, 2, 0, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch< 9, 2, 3>; +template class handler_entry_read_dispatch<10, 2, 3>; +template class handler_entry_read_dispatch<11, 2, 3>; +template class handler_entry_read_dispatch<12, 2, 3>; +template class handler_entry_read_dispatch<13, 2, 3>; +template class handler_entry_read_dispatch<14, 2, 3>; +template class handler_entry_read_dispatch<15, 2, 3>; +template class handler_entry_read_dispatch<16, 2, 3>; -template class handler_entry_read_dispatch< 9, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 9, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<10, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<10, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<11, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<11, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<12, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<12, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<13, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<13, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<14, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<14, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<15, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<15, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<16, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<16, 2, -1, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch< 9, 2, 0>; +template class handler_entry_read_dispatch<10, 2, 0>; +template class handler_entry_read_dispatch<11, 2, 0>; +template class handler_entry_read_dispatch<12, 2, 0>; +template class handler_entry_read_dispatch<13, 2, 0>; +template class handler_entry_read_dispatch<14, 2, 0>; +template class handler_entry_read_dispatch<15, 2, 0>; +template class handler_entry_read_dispatch<16, 2, 0>; -template class handler_entry_read_dispatch< 9, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 9, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<10, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<10, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<11, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<11, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<12, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<12, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<13, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<13, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<14, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<14, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<15, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<15, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<16, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<16, 2, -2, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch< 9, 2, -1>; +template class handler_entry_read_dispatch<10, 2, -1>; +template class handler_entry_read_dispatch<11, 2, -1>; +template class handler_entry_read_dispatch<12, 2, -1>; +template class handler_entry_read_dispatch<13, 2, -1>; +template class handler_entry_read_dispatch<14, 2, -1>; +template class handler_entry_read_dispatch<15, 2, -1>; +template class handler_entry_read_dispatch<16, 2, -1>; -template class handler_entry_read_dispatch< 9, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 9, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<10, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<10, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<11, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<11, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<12, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<12, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<13, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<13, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<14, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<14, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<15, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<15, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<16, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<16, 3, 0, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch< 9, 2, -2>; +template class handler_entry_read_dispatch<10, 2, -2>; +template class handler_entry_read_dispatch<11, 2, -2>; +template class handler_entry_read_dispatch<12, 2, -2>; +template class handler_entry_read_dispatch<13, 2, -2>; +template class handler_entry_read_dispatch<14, 2, -2>; +template class handler_entry_read_dispatch<15, 2, -2>; +template class handler_entry_read_dispatch<16, 2, -2>; -template class handler_entry_read_dispatch< 9, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 9, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<10, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<10, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<11, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<11, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<12, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<12, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<13, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<13, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<14, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<14, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<15, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<15, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<16, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<16, 3, -1, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch< 9, 3, 0>; +template class handler_entry_read_dispatch<10, 3, 0>; +template class handler_entry_read_dispatch<11, 3, 0>; +template class handler_entry_read_dispatch<12, 3, 0>; +template class handler_entry_read_dispatch<13, 3, 0>; +template class handler_entry_read_dispatch<14, 3, 0>; +template class handler_entry_read_dispatch<15, 3, 0>; +template class handler_entry_read_dispatch<16, 3, 0>; -template class handler_entry_read_dispatch< 9, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 9, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<10, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<10, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<11, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<11, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<12, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<12, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<13, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<13, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<14, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<14, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<15, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<15, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<16, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<16, 3, -2, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch< 9, 3, -1>; +template class handler_entry_read_dispatch<10, 3, -1>; +template class handler_entry_read_dispatch<11, 3, -1>; +template class handler_entry_read_dispatch<12, 3, -1>; +template class handler_entry_read_dispatch<13, 3, -1>; +template class handler_entry_read_dispatch<14, 3, -1>; +template class handler_entry_read_dispatch<15, 3, -1>; +template class handler_entry_read_dispatch<16, 3, -1>; -template class handler_entry_read_dispatch< 9, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch< 9, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<10, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<10, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<11, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<11, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<12, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<12, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<13, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<13, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<14, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<14, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<15, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<15, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<16, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<16, 3, -3, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch< 9, 3, -2>; +template class handler_entry_read_dispatch<10, 3, -2>; +template class handler_entry_read_dispatch<11, 3, -2>; +template class handler_entry_read_dispatch<12, 3, -2>; +template class handler_entry_read_dispatch<13, 3, -2>; +template class handler_entry_read_dispatch<14, 3, -2>; +template class handler_entry_read_dispatch<15, 3, -2>; +template class handler_entry_read_dispatch<16, 3, -2>; + +template class handler_entry_read_dispatch< 9, 3, -3>; +template class handler_entry_read_dispatch<10, 3, -3>; +template class handler_entry_read_dispatch<11, 3, -3>; +template class handler_entry_read_dispatch<12, 3, -3>; +template class handler_entry_read_dispatch<13, 3, -3>; +template class handler_entry_read_dispatch<14, 3, -3>; +template class handler_entry_read_dispatch<15, 3, -3>; +template class handler_entry_read_dispatch<16, 3, -3>; diff --git a/src/emu/emumem_hedr2.cpp b/src/emu/emumem_hedr2.cpp index a59bc746f9e..6a665b14827 100644 --- a/src/emu/emumem_hedr2.cpp +++ b/src/emu/emumem_hedr2.cpp @@ -4,206 +4,119 @@ #include "emu.h" #include "emumem_hedr.ipp" -template class handler_entry_read_dispatch<17, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<17, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<18, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<18, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<19, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<19, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<20, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<20, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<21, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<21, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<22, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<22, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<23, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<23, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<24, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<24, 0, 1, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch<17, 0, 1>; +template class handler_entry_read_dispatch<18, 0, 1>; +template class handler_entry_read_dispatch<19, 0, 1>; +template class handler_entry_read_dispatch<20, 0, 1>; +template class handler_entry_read_dispatch<21, 0, 1>; +template class handler_entry_read_dispatch<22, 0, 1>; +template class handler_entry_read_dispatch<23, 0, 1>; +template class handler_entry_read_dispatch<24, 0, 1>; -template class handler_entry_read_dispatch<17, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<17, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<18, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<18, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<19, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<19, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<20, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<20, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<21, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<21, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<22, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<22, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<23, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<23, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<24, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<24, 0, 0, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch<17, 0, 0>; +template class handler_entry_read_dispatch<18, 0, 0>; +template class handler_entry_read_dispatch<19, 0, 0>; +template class handler_entry_read_dispatch<20, 0, 0>; +template class handler_entry_read_dispatch<21, 0, 0>; +template class handler_entry_read_dispatch<22, 0, 0>; +template class handler_entry_read_dispatch<23, 0, 0>; +template class handler_entry_read_dispatch<24, 0, 0>; -template class handler_entry_read_dispatch<17, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<17, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<18, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<18, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<19, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<19, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<20, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<20, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<21, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<21, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<22, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<22, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<23, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<23, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<24, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<24, 1, 3, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch<17, 1, 3>; +template class handler_entry_read_dispatch<18, 1, 3>; +template class handler_entry_read_dispatch<19, 1, 3>; +template class handler_entry_read_dispatch<20, 1, 3>; +template class handler_entry_read_dispatch<21, 1, 3>; +template class handler_entry_read_dispatch<22, 1, 3>; +template class handler_entry_read_dispatch<23, 1, 3>; +template class handler_entry_read_dispatch<24, 1, 3>; -template class handler_entry_read_dispatch<17, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<17, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<18, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<18, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<19, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<19, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<20, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<20, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<21, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<21, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<22, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<22, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<23, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<23, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<24, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<24, 1, 0, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch<17, 1, 0>; +template class handler_entry_read_dispatch<18, 1, 0>; +template class handler_entry_read_dispatch<19, 1, 0>; +template class handler_entry_read_dispatch<20, 1, 0>; +template class handler_entry_read_dispatch<21, 1, 0>; +template class handler_entry_read_dispatch<22, 1, 0>; +template class handler_entry_read_dispatch<23, 1, 0>; +template class handler_entry_read_dispatch<24, 1, 0>; -template class handler_entry_read_dispatch<17, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<17, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<18, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<18, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<19, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<19, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<20, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<20, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<21, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<21, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<22, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<22, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<23, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<23, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<24, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<24, 1, -1, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch<17, 1, -1>; +template class handler_entry_read_dispatch<18, 1, -1>; +template class handler_entry_read_dispatch<19, 1, -1>; +template class handler_entry_read_dispatch<20, 1, -1>; +template class handler_entry_read_dispatch<21, 1, -1>; +template class handler_entry_read_dispatch<22, 1, -1>; +template class handler_entry_read_dispatch<23, 1, -1>; +template class handler_entry_read_dispatch<24, 1, -1>; -template class handler_entry_read_dispatch<17, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<17, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<18, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<18, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<19, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<19, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<20, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<20, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<21, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<21, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<22, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<22, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<23, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<23, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<24, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<24, 2, 0, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch<17, 2, 3>; +template class handler_entry_read_dispatch<18, 2, 3>; +template class handler_entry_read_dispatch<19, 2, 3>; +template class handler_entry_read_dispatch<20, 2, 3>; +template class handler_entry_read_dispatch<21, 2, 3>; +template class handler_entry_read_dispatch<22, 2, 3>; +template class handler_entry_read_dispatch<23, 2, 3>; +template class handler_entry_read_dispatch<24, 2, 3>; -template class handler_entry_read_dispatch<17, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<17, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<18, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<18, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<19, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<19, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<20, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<20, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<21, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<21, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<22, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<22, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<23, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<23, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<24, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<24, 2, -1, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch<17, 2, 0>; +template class handler_entry_read_dispatch<18, 2, 0>; +template class handler_entry_read_dispatch<19, 2, 0>; +template class handler_entry_read_dispatch<20, 2, 0>; +template class handler_entry_read_dispatch<21, 2, 0>; +template class handler_entry_read_dispatch<22, 2, 0>; +template class handler_entry_read_dispatch<23, 2, 0>; +template class handler_entry_read_dispatch<24, 2, 0>; -template class handler_entry_read_dispatch<17, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<17, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<18, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<18, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<19, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<19, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<20, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<20, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<21, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<21, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<22, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<22, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<23, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<23, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<24, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<24, 2, -2, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch<17, 2, -1>; +template class handler_entry_read_dispatch<18, 2, -1>; +template class handler_entry_read_dispatch<19, 2, -1>; +template class handler_entry_read_dispatch<20, 2, -1>; +template class handler_entry_read_dispatch<21, 2, -1>; +template class handler_entry_read_dispatch<22, 2, -1>; +template class handler_entry_read_dispatch<23, 2, -1>; +template class handler_entry_read_dispatch<24, 2, -1>; -template class handler_entry_read_dispatch<17, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<17, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<18, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<18, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<19, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<19, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<20, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<20, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<21, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<21, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<22, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<22, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<23, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<23, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<24, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<24, 3, 0, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch<17, 2, -2>; +template class handler_entry_read_dispatch<18, 2, -2>; +template class handler_entry_read_dispatch<19, 2, -2>; +template class handler_entry_read_dispatch<20, 2, -2>; +template class handler_entry_read_dispatch<21, 2, -2>; +template class handler_entry_read_dispatch<22, 2, -2>; +template class handler_entry_read_dispatch<23, 2, -2>; +template class handler_entry_read_dispatch<24, 2, -2>; -template class handler_entry_read_dispatch<17, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<17, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<18, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<18, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<19, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<19, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<20, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<20, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<21, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<21, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<22, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<22, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<23, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<23, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<24, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<24, 3, -1, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch<17, 3, 0>; +template class handler_entry_read_dispatch<18, 3, 0>; +template class handler_entry_read_dispatch<19, 3, 0>; +template class handler_entry_read_dispatch<20, 3, 0>; +template class handler_entry_read_dispatch<21, 3, 0>; +template class handler_entry_read_dispatch<22, 3, 0>; +template class handler_entry_read_dispatch<23, 3, 0>; +template class handler_entry_read_dispatch<24, 3, 0>; -template class handler_entry_read_dispatch<17, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<17, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<18, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<18, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<19, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<19, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<20, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<20, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<21, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<21, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<22, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<22, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<23, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<23, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<24, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<24, 3, -2, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch<17, 3, -1>; +template class handler_entry_read_dispatch<18, 3, -1>; +template class handler_entry_read_dispatch<19, 3, -1>; +template class handler_entry_read_dispatch<20, 3, -1>; +template class handler_entry_read_dispatch<21, 3, -1>; +template class handler_entry_read_dispatch<22, 3, -1>; +template class handler_entry_read_dispatch<23, 3, -1>; +template class handler_entry_read_dispatch<24, 3, -1>; -template class handler_entry_read_dispatch<17, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<17, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<18, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<18, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<19, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<19, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<20, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<20, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<21, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<21, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<22, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<22, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<23, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<23, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<24, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<24, 3, -3, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch<17, 3, -2>; +template class handler_entry_read_dispatch<18, 3, -2>; +template class handler_entry_read_dispatch<19, 3, -2>; +template class handler_entry_read_dispatch<20, 3, -2>; +template class handler_entry_read_dispatch<21, 3, -2>; +template class handler_entry_read_dispatch<22, 3, -2>; +template class handler_entry_read_dispatch<23, 3, -2>; +template class handler_entry_read_dispatch<24, 3, -2>; + +template class handler_entry_read_dispatch<17, 3, -3>; +template class handler_entry_read_dispatch<18, 3, -3>; +template class handler_entry_read_dispatch<19, 3, -3>; +template class handler_entry_read_dispatch<20, 3, -3>; +template class handler_entry_read_dispatch<21, 3, -3>; +template class handler_entry_read_dispatch<22, 3, -3>; +template class handler_entry_read_dispatch<23, 3, -3>; +template class handler_entry_read_dispatch<24, 3, -3>; diff --git a/src/emu/emumem_hedr3.cpp b/src/emu/emumem_hedr3.cpp index aa325b5b5a8..190a62f9ce0 100644 --- a/src/emu/emumem_hedr3.cpp +++ b/src/emu/emumem_hedr3.cpp @@ -5,206 +5,119 @@ #include "emumem_hedr.ipp" -template class handler_entry_read_dispatch<25, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<25, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<26, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<26, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<27, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<27, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<28, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<28, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<29, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<29, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<30, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<30, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<31, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<31, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<32, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<32, 0, 1, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch<25, 0, 1>; +template class handler_entry_read_dispatch<26, 0, 1>; +template class handler_entry_read_dispatch<27, 0, 1>; +template class handler_entry_read_dispatch<28, 0, 1>; +template class handler_entry_read_dispatch<29, 0, 1>; +template class handler_entry_read_dispatch<30, 0, 1>; +template class handler_entry_read_dispatch<31, 0, 1>; +template class handler_entry_read_dispatch<32, 0, 1>; -template class handler_entry_read_dispatch<25, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<25, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<26, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<26, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<27, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<27, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<28, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<28, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<29, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<29, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<30, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<30, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<31, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<31, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<32, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<32, 0, 0, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch<25, 0, 0>; +template class handler_entry_read_dispatch<26, 0, 0>; +template class handler_entry_read_dispatch<27, 0, 0>; +template class handler_entry_read_dispatch<28, 0, 0>; +template class handler_entry_read_dispatch<29, 0, 0>; +template class handler_entry_read_dispatch<30, 0, 0>; +template class handler_entry_read_dispatch<31, 0, 0>; +template class handler_entry_read_dispatch<32, 0, 0>; -template class handler_entry_read_dispatch<25, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<25, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<26, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<26, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<27, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<27, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<28, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<28, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<29, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<29, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<31, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<31, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<30, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<30, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<32, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<32, 1, 3, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch<25, 1, 3>; +template class handler_entry_read_dispatch<26, 1, 3>; +template class handler_entry_read_dispatch<27, 1, 3>; +template class handler_entry_read_dispatch<28, 1, 3>; +template class handler_entry_read_dispatch<29, 1, 3>; +template class handler_entry_read_dispatch<30, 1, 3>; +template class handler_entry_read_dispatch<31, 1, 3>; +template class handler_entry_read_dispatch<32, 1, 3>; -template class handler_entry_read_dispatch<25, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<25, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<26, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<26, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<27, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<27, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<28, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<28, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<29, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<29, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<31, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<31, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<30, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<30, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<32, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<32, 1, 0, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch<25, 1, 0>; +template class handler_entry_read_dispatch<26, 1, 0>; +template class handler_entry_read_dispatch<27, 1, 0>; +template class handler_entry_read_dispatch<28, 1, 0>; +template class handler_entry_read_dispatch<29, 1, 0>; +template class handler_entry_read_dispatch<30, 1, 0>; +template class handler_entry_read_dispatch<31, 1, 0>; +template class handler_entry_read_dispatch<32, 1, 0>; -template class handler_entry_read_dispatch<25, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<25, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<26, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<26, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<27, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<27, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<28, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<28, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<29, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<29, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<31, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<31, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<30, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<30, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<32, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<32, 1, -1, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch<25, 1, -1>; +template class handler_entry_read_dispatch<26, 1, -1>; +template class handler_entry_read_dispatch<27, 1, -1>; +template class handler_entry_read_dispatch<28, 1, -1>; +template class handler_entry_read_dispatch<29, 1, -1>; +template class handler_entry_read_dispatch<30, 1, -1>; +template class handler_entry_read_dispatch<31, 1, -1>; +template class handler_entry_read_dispatch<32, 1, -1>; -template class handler_entry_read_dispatch<25, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<25, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<26, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<26, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<27, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<27, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<28, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<28, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<29, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<29, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<31, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<31, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<30, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<30, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<32, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<32, 2, 0, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch<25, 2, 3>; +template class handler_entry_read_dispatch<26, 2, 3>; +template class handler_entry_read_dispatch<27, 2, 3>; +template class handler_entry_read_dispatch<28, 2, 3>; +template class handler_entry_read_dispatch<29, 2, 3>; +template class handler_entry_read_dispatch<30, 2, 3>; +template class handler_entry_read_dispatch<31, 2, 3>; +template class handler_entry_read_dispatch<32, 2, 3>; -template class handler_entry_read_dispatch<25, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<25, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<26, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<26, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<27, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<27, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<28, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<28, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<29, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<29, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<31, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<31, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<30, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<30, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<32, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<32, 2, -1, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch<25, 2, 0>; +template class handler_entry_read_dispatch<26, 2, 0>; +template class handler_entry_read_dispatch<27, 2, 0>; +template class handler_entry_read_dispatch<28, 2, 0>; +template class handler_entry_read_dispatch<29, 2, 0>; +template class handler_entry_read_dispatch<30, 2, 0>; +template class handler_entry_read_dispatch<31, 2, 0>; +template class handler_entry_read_dispatch<32, 2, 0>; -template class handler_entry_read_dispatch<25, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<25, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<26, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<26, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<27, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<27, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<28, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<28, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<29, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<29, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<31, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<31, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<30, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<30, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<32, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<32, 2, -2, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch<25, 2, -1>; +template class handler_entry_read_dispatch<26, 2, -1>; +template class handler_entry_read_dispatch<27, 2, -1>; +template class handler_entry_read_dispatch<28, 2, -1>; +template class handler_entry_read_dispatch<29, 2, -1>; +template class handler_entry_read_dispatch<30, 2, -1>; +template class handler_entry_read_dispatch<31, 2, -1>; +template class handler_entry_read_dispatch<32, 2, -1>; -template class handler_entry_read_dispatch<25, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<25, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<26, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<26, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<27, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<27, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<28, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<28, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<29, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<29, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<31, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<31, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<30, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<30, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<32, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<32, 3, 0, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch<25, 2, -2>; +template class handler_entry_read_dispatch<26, 2, -2>; +template class handler_entry_read_dispatch<27, 2, -2>; +template class handler_entry_read_dispatch<28, 2, -2>; +template class handler_entry_read_dispatch<29, 2, -2>; +template class handler_entry_read_dispatch<30, 2, -2>; +template class handler_entry_read_dispatch<31, 2, -2>; +template class handler_entry_read_dispatch<32, 2, -2>; -template class handler_entry_read_dispatch<25, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<25, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<26, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<26, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<27, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<27, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<28, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<28, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<29, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<29, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<31, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<31, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<30, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<30, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<32, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<32, 3, -1, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch<25, 3, 0>; +template class handler_entry_read_dispatch<26, 3, 0>; +template class handler_entry_read_dispatch<27, 3, 0>; +template class handler_entry_read_dispatch<28, 3, 0>; +template class handler_entry_read_dispatch<29, 3, 0>; +template class handler_entry_read_dispatch<30, 3, 0>; +template class handler_entry_read_dispatch<31, 3, 0>; +template class handler_entry_read_dispatch<32, 3, 0>; -template class handler_entry_read_dispatch<25, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<25, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<26, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<26, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<27, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<27, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<28, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<28, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<29, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<29, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<31, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<31, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<30, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<30, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<32, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<32, 3, -2, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch<25, 3, -1>; +template class handler_entry_read_dispatch<26, 3, -1>; +template class handler_entry_read_dispatch<27, 3, -1>; +template class handler_entry_read_dispatch<28, 3, -1>; +template class handler_entry_read_dispatch<29, 3, -1>; +template class handler_entry_read_dispatch<30, 3, -1>; +template class handler_entry_read_dispatch<31, 3, -1>; +template class handler_entry_read_dispatch<32, 3, -1>; -template class handler_entry_read_dispatch<25, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<25, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<26, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<26, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<27, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<27, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<28, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<28, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<29, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<29, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<31, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<31, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<30, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<30, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_read_dispatch<32, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_read_dispatch<32, 3, -3, ENDIANNESS_BIG>; +template class handler_entry_read_dispatch<25, 3, -2>; +template class handler_entry_read_dispatch<26, 3, -2>; +template class handler_entry_read_dispatch<27, 3, -2>; +template class handler_entry_read_dispatch<28, 3, -2>; +template class handler_entry_read_dispatch<29, 3, -2>; +template class handler_entry_read_dispatch<30, 3, -2>; +template class handler_entry_read_dispatch<31, 3, -2>; +template class handler_entry_read_dispatch<32, 3, -2>; + +template class handler_entry_read_dispatch<25, 3, -3>; +template class handler_entry_read_dispatch<26, 3, -3>; +template class handler_entry_read_dispatch<27, 3, -3>; +template class handler_entry_read_dispatch<28, 3, -3>; +template class handler_entry_read_dispatch<29, 3, -3>; +template class handler_entry_read_dispatch<30, 3, -3>; +template class handler_entry_read_dispatch<31, 3, -3>; +template class handler_entry_read_dispatch<32, 3, -3>; diff --git a/src/emu/emumem_hedw.h b/src/emu/emumem_hedw.h index 72143402c6f..4bd3098a333 100644 --- a/src/emu/emumem_hedw.h +++ b/src/emu/emumem_hedw.h @@ -1,41 +1,58 @@ // license:BSD-3-Clause // copyright-holders:Olivier Galibert +#ifndef MAME_EMU_EMUMEM_HEDW_H +#define MAME_EMU_EMUMEM_HEDW_H + +#pragma once // handler_entry_write_dispatch -// dispatches an access among multiple handlers indexed on part of the address +// dispatches an access among multiple handlers indexed on part of the +// address and when appropriate a selected view -template<int HighBits, int Width, int AddrShift, int Endian> class handler_entry_write_dispatch : public handler_entry_write<Width, AddrShift, Endian> +template<int HighBits, int Width, int AddrShift> class handler_entry_write_dispatch : public handler_entry_write<Width, AddrShift> { public: - using uX = typename emu::detail::handler_entry_size<Width>::uX; - using inh = handler_entry_write<Width, AddrShift, Endian>; - using mapping = typename inh::mapping; + using uX = emu::detail::handler_entry_size_t<Width>; + using mapping = typename handler_entry_write<Width, AddrShift>::mapping; - handler_entry_write_dispatch(address_space *space, const handler_entry::range &init, handler_entry_write<Width, AddrShift, Endian> *handler); + handler_entry_write_dispatch(address_space *space, const handler_entry::range &init, handler_entry_write<Width, AddrShift> *handler); + handler_entry_write_dispatch(address_space *space, memory_view &view, offs_t addrstart, offs_t addrend); + handler_entry_write_dispatch(handler_entry_write_dispatch<HighBits, Width, AddrShift> *src); ~handler_entry_write_dispatch(); - void write(offs_t offset, uX data, uX mem_mask) override; + void write(offs_t offset, uX data, uX mem_mask) const override; + void write_interruptible(offs_t offset, uX data, uX mem_mask) const override; + u16 write_flags(offs_t offset, uX data, uX mem_mask) const override; + u16 lookup_flags(offs_t offset, uX mem_mask) const override; void *get_ptr(offs_t offset) const override; - void lookup(offs_t address, offs_t &start, offs_t &end, handler_entry_write<Width, AddrShift, Endian> *&handler) const override; + void lookup(offs_t address, offs_t &start, offs_t &end, handler_entry_write<Width, AddrShift> *&handler) const override; + offs_t dispatch_entry(offs_t address) const override; void dump_map(std::vector<memory_entry> &map) const override; std::string name() const override; - void populate_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_write<Width, AddrShift, Endian> *handler) override; - void populate_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_write<Width, AddrShift, Endian> *handler) override; - void populate_mismatched_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 rkey, std::vector<mapping> &mappings) override; - void populate_mismatched_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, std::vector<mapping> &mappings) override; - void populate_passthrough_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_write_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings) override; - void populate_passthrough_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_write_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings) override; + void populate_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_write<Width, AddrShift> *handler) override; + void populate_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_write<Width, AddrShift> *handler) override; + void populate_mismatched_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, const memory_units_descriptor<Width, AddrShift> &descriptor, u8 rkey, std::vector<mapping> &mappings) override; + void populate_mismatched_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, const memory_units_descriptor<Width, AddrShift> &descriptor, std::vector<mapping> &mappings) override; + void populate_passthrough_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_write_passthrough<Width, AddrShift> *handler, std::vector<mapping> &mappings) override; + void populate_passthrough_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_write_passthrough<Width, AddrShift> *handler, std::vector<mapping> &mappings) override; void detach(const std::unordered_set<handler_entry *> &handlers) override; void range_cut_before(offs_t address, int start = COUNT); void range_cut_after(offs_t address, int start = -1); void enumerate_references(handler_entry::reflist &refs) const override; -protected: + const handler_entry_write<Width, AddrShift> *const *get_dispatch() const override; + void select_a(int slot) override; + void select_u(int slot) override; + void init_handlers(offs_t start_entry, offs_t end_entry, u32 lowbits, offs_t ostart, offs_t oend, handler_entry_write<Width, AddrShift> **dispatch, handler_entry::range *ranges) override; + handler_entry_write<Width, AddrShift> *dup() override; + +private: + static constexpr int Level = emu::detail::handler_entry_dispatch_level(HighBits); static constexpr u32 LowBits = emu::detail::handler_entry_dispatch_lowbits(HighBits, Width, AddrShift); static constexpr u32 BITCOUNT = HighBits > LowBits ? HighBits - LowBits : 0; static constexpr u32 COUNT = 1 << BITCOUNT; @@ -44,18 +61,49 @@ protected: static constexpr offs_t HIGHMASK = make_bitmask<offs_t>(HighBits) ^ LOWMASK; static constexpr offs_t UPMASK = ~make_bitmask<offs_t>(HighBits); - handler_entry_write<Width, AddrShift, Endian> *m_dispatch[COUNT]; - handler_entry::range m_ranges[COUNT]; + class handler_array : public std::array<handler_entry_write<Width, AddrShift> *, COUNT> + { + public: + using std::array<handler_entry_write<Width, AddrShift> *, COUNT>::array; + handler_array() + { + std::fill(this->begin(), this->end(), nullptr); + } + }; -private: - void populate_nomirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_write<Width, AddrShift, Endian> *handler); - void populate_mirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_write<Width, AddrShift, Endian> *handler); + class range_array : public std::array<handler_entry::range, COUNT> + { + public: + using std::array<handler_entry::range, COUNT>::array; + range_array() + { + std::fill(this->begin(), this->end(), handler_entry::range{ 0, 0 }); + } + }; + + memory_view *m_view; + + std::vector<handler_array> m_dispatch_array; + std::vector<range_array> m_ranges_array; - void populate_mismatched_nomirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 rkey, std::vector<mapping> &mappings); - void populate_mismatched_mirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, std::vector<mapping> &mappings); - void mismatched_patch(const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 rkey, std::vector<mapping> &mappings, handler_entry_write<Width, AddrShift, Endian> *&target); + handler_entry_write<Width, AddrShift> **m_a_dispatch; + handler_entry::range *m_a_ranges; - void populate_passthrough_nomirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_write_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings); - void populate_passthrough_mirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_write_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings); - void passthrough_patch(handler_entry_write_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings, handler_entry_write<Width, AddrShift, Endian> *&target); + handler_entry_write<Width, AddrShift> **m_u_dispatch; + handler_entry::range *m_u_ranges; + + handler_entry::range m_global_range; + + void populate_nomirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_write<Width, AddrShift> *handler); + void populate_mirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_write<Width, AddrShift> *handler); + + void populate_mismatched_nomirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, const memory_units_descriptor<Width, AddrShift> &descriptor, u8 rkey, std::vector<mapping> &mappings); + void populate_mismatched_mirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, const memory_units_descriptor<Width, AddrShift> &descriptor, std::vector<mapping> &mappings); + void mismatched_patch(const memory_units_descriptor<Width, AddrShift> &descriptor, u8 rkey, std::vector<mapping> &mappings, handler_entry_write<Width, AddrShift> *&target); + + void populate_passthrough_nomirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_write_passthrough<Width, AddrShift> *handler, std::vector<mapping> &mappings); + void populate_passthrough_mirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_write_passthrough<Width, AddrShift> *handler, std::vector<mapping> &mappings); + void passthrough_patch(handler_entry_write_passthrough<Width, AddrShift> *handler, std::vector<mapping> &mappings, handler_entry_write<Width, AddrShift> *&target); }; + +#endif // MAME_EMU_EMUMEM_HEDW_H diff --git a/src/emu/emumem_hedw.ipp b/src/emu/emumem_hedw.ipp index 1496521d094..dd6001d4bb0 100644 --- a/src/emu/emumem_hedw.ipp +++ b/src/emu/emumem_hedw.ipp @@ -1,7 +1,10 @@ // license:BSD-3-Clause // copyright-holders:Olivier Galibert +#ifndef MAME_EMU_EMUMEM_HEDW_IPP +#define MAME_EMU_EMUMEM_HEDW_IPP + +#pragma once -#include "emu.h" #include "emumem_mud.h" #include "emumem_hea.h" #include "emumem_heu.h" @@ -9,173 +12,291 @@ #include "emumem_hep.h" #include "emumem_hedw.h" +template<int HighBits, int Width, int AddrShift> const handler_entry_write<Width, AddrShift> *const *handler_entry_write_dispatch<HighBits, Width, AddrShift>::get_dispatch() const +{ + return m_a_dispatch; +} -template<int HighBits, int Width, int AddrShift, int Endian> handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::handler_entry_write_dispatch(address_space *space, const handler_entry::range &init, handler_entry_write<Width, AddrShift, Endian> *handler) : handler_entry_write<Width, AddrShift, Endian>(space, handler_entry::F_DISPATCH) +template<int HighBits, int Width, int AddrShift> handler_entry_write_dispatch<HighBits, Width, AddrShift>::handler_entry_write_dispatch(address_space *space, const handler_entry::range &init, handler_entry_write<Width, AddrShift> *handler) : handler_entry_write<Width, AddrShift>(space, handler_entry::F_DISPATCH), m_view(nullptr) { + m_ranges_array.resize(1); + m_dispatch_array.resize(1); + m_a_ranges = m_ranges_array[0].data(); + m_a_dispatch = m_dispatch_array[0].data(); + m_u_ranges = m_ranges_array[0].data(); + m_u_dispatch = m_dispatch_array[0].data(); + m_global_range = init; + if (!handler) - handler = space->get_unmap_w<Width, AddrShift, Endian>(); + handler = space->get_unmap_w<Width, AddrShift>(); handler->ref(COUNT); for(unsigned int i=0; i != COUNT; i++) { - m_dispatch[i] = handler; - m_ranges[i] = init; + m_u_dispatch[i] = handler; + m_u_ranges[i] = init; } } -template<int HighBits, int Width, int AddrShift, int Endian> handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::~handler_entry_write_dispatch() +template<int HighBits, int Width, int AddrShift> handler_entry_write_dispatch<HighBits, Width, AddrShift>::handler_entry_write_dispatch(address_space *space, memory_view &view, offs_t addrstart, offs_t addrend) : handler_entry_write<Width, AddrShift>(space, handler_entry::F_VIEW), m_view(&view), m_a_dispatch(nullptr), m_a_ranges(nullptr), m_u_dispatch(nullptr), m_u_ranges(nullptr) { - for(unsigned int i=0; i != COUNT; i++) - m_dispatch[i]->unref(); + m_ranges_array.resize(1); + m_dispatch_array.resize(1); + m_a_ranges = m_ranges_array[0].data(); + m_a_dispatch = m_dispatch_array[0].data(); + m_u_ranges = m_ranges_array[0].data(); + m_u_dispatch = m_dispatch_array[0].data(); + m_global_range.start = addrstart; + m_global_range.end = addrend; + + auto handler = space->get_unmap_w<Width, AddrShift>(); + handler->ref(COUNT); + for(unsigned int i=0; i != COUNT; i++) { + m_u_dispatch[i] = handler; + m_u_ranges[i].start = addrstart; + m_u_ranges[i].end = addrend; + } } -template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::enumerate_references(handler_entry::reflist &refs) const +template<int HighBits, int Width, int AddrShift> handler_entry_write_dispatch<HighBits, Width, AddrShift>::handler_entry_write_dispatch(handler_entry_write_dispatch<HighBits, Width, AddrShift> *src) : handler_entry_write<Width, AddrShift>(src->m_space, handler_entry::F_DISPATCH), m_view(nullptr) { - for(unsigned int i=0; i != COUNT; i++) - refs.add(m_dispatch[i]); + m_ranges_array.resize(1); + m_dispatch_array.resize(1); + m_a_ranges = m_ranges_array[0].data(); + m_a_dispatch = m_dispatch_array[0].data(); + m_u_ranges = m_ranges_array[0].data(); + m_u_dispatch = m_dispatch_array[0].data(); + m_global_range = src->m_global_range; + + for(unsigned int i=0; i != COUNT; i++) { + m_u_dispatch[i] = src->m_u_dispatch[i]->dup(); + m_u_ranges[i] = src->m_u_ranges[i]; + } } -template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::dump_map(std::vector<memory_entry> &map) const + +template<int HighBits, int Width, int AddrShift> handler_entry_write_dispatch<HighBits, Width, AddrShift>::~handler_entry_write_dispatch() { - offs_t cur = map.empty() ? 0 : map.back().end + 1; - offs_t base = cur & UPMASK; - do { - offs_t entry = (cur >> LowBits) & BITMASK; - if(m_dispatch[entry]->is_dispatch()) - m_dispatch[entry]->dump_map(map); - else - map.emplace_back(memory_entry{ m_ranges[entry].start, m_ranges[entry].end, m_dispatch[entry] }); - cur = map.back().end + 1; - } while(cur && !((cur ^ base) & UPMASK)); + for(auto &d : m_dispatch_array) + for(auto p : d) + if(p) + p->unref(); +} + +template<int HighBits, int Width, int AddrShift> void handler_entry_write_dispatch<HighBits, Width, AddrShift>::enumerate_references(handler_entry::reflist &refs) const +{ + for(auto &d : m_dispatch_array) + for(auto p : d) + if(p) + refs.add(p); +} + + +template<int HighBits, int Width, int AddrShift> offs_t handler_entry_write_dispatch<HighBits, Width, AddrShift>::dispatch_entry(offs_t address) const +{ + return (address & HIGHMASK) >> LowBits; +} + +template<int HighBits, int Width, int AddrShift> void handler_entry_write_dispatch<HighBits, Width, AddrShift>::dump_map(std::vector<memory_entry> &map) const +{ + if(m_view) { + offs_t base_cur = map.empty() ? m_view->m_addrstart & HIGHMASK : map.back().end + 1; + for(u32 i = 0; i != m_dispatch_array.size(); i++) { + u32 j = map.size(); + offs_t cur = base_cur; + offs_t end = m_global_range.end + 1; + do { + offs_t entry = (cur >> LowBits) & BITMASK; + if(m_dispatch_array[i][entry]->is_dispatch() || m_dispatch_array[i][entry]->is_view()) + m_dispatch_array[i][entry]->dump_map(map); + else + map.emplace_back(memory_entry{ m_ranges_array[i][entry].start, m_ranges_array[i][entry].end, m_dispatch_array[i][entry] }); + cur = map.back().end + 1; + } while(cur != end); + if(i == 0) { + for(u32 k = j; k != map.size(); k++) + map[k].context.emplace(map[k].context.begin(), memory_entry_context{ m_view, true, 0 }); + } else { + int slot = m_view->id_to_slot(int(i)-1); + for(u32 k = j; k != map.size(); k++) + map[k].context.emplace(map[k].context.begin(), memory_entry_context{ m_view, false, slot }); + } + } + } else { + offs_t cur = map.empty() ? 0 : map.back().end + 1; + offs_t base = cur & UPMASK; + offs_t end = m_global_range.end + 1; + do { + offs_t entry = (cur >> LowBits) & BITMASK; + if(m_a_dispatch[entry]->is_dispatch() || m_a_dispatch[entry]->is_view()) + m_a_dispatch[entry]->dump_map(map); + else + map.emplace_back(memory_entry{ m_a_ranges[entry].start, m_a_ranges[entry].end, m_a_dispatch[entry] }); + cur = map.back().end + 1; + } while(cur != end && !((cur ^ base) & UPMASK)); + } +} + +template<int HighBits, int Width, int AddrShift> void handler_entry_write_dispatch<HighBits, Width, AddrShift>::write(offs_t offset, uX data, uX mem_mask) const +{ + dispatch_write<Level, Width, AddrShift>(HIGHMASK, offset, data, mem_mask, m_a_dispatch); +} + +template<int HighBits, int Width, int AddrShift> void handler_entry_write_dispatch<HighBits, Width, AddrShift>::write_interruptible(offs_t offset, uX data, uX mem_mask) const +{ + dispatch_write_interruptible<Level, Width, AddrShift>(HIGHMASK, offset, data, mem_mask, m_a_dispatch); } -template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::write(offs_t offset, uX data, uX mem_mask) +template<int HighBits, int Width, int AddrShift> u16 handler_entry_write_dispatch<HighBits, Width, AddrShift>::write_flags(offs_t offset, uX data, uX mem_mask) const { - m_dispatch[(offset >> LowBits) & BITMASK]->write(offset, data, mem_mask); + return dispatch_write_flags<Level, Width, AddrShift>(HIGHMASK, offset, data, mem_mask, m_a_dispatch); } -template<int HighBits, int Width, int AddrShift, int Endian> void *handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::get_ptr(offs_t offset) const +template<int HighBits, int Width, int AddrShift> u16 handler_entry_write_dispatch<HighBits, Width, AddrShift>::lookup_flags(offs_t offset, uX mem_mask) const { - return m_dispatch[(offset >> LowBits) & BITMASK]->get_ptr(offset); + return dispatch_lookup_write_flags<Level, Width, AddrShift>(HIGHMASK, offset, mem_mask, m_a_dispatch); } -template<int HighBits, int Width, int AddrShift, int Endian> std::string handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::name() const +template<int HighBits, int Width, int AddrShift> void *handler_entry_write_dispatch<HighBits, Width, AddrShift>::get_ptr(offs_t offset) const { - return "dispatch"; + return m_a_dispatch[(offset & HIGHMASK) >> LowBits]->get_ptr(offset); } -template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::lookup(offs_t address, offs_t &start, offs_t &end, handler_entry_write<Width, AddrShift, Endian> *&handler) const +template<int HighBits, int Width, int AddrShift> std::string handler_entry_write_dispatch<HighBits, Width, AddrShift>::name() const +{ + return m_view ? "view" : "dispatch"; +} + +template<int HighBits, int Width, int AddrShift> void handler_entry_write_dispatch<HighBits, Width, AddrShift>::lookup(offs_t address, offs_t &start, offs_t &end, handler_entry_write<Width, AddrShift> *&handler) const { offs_t slot = (address >> LowBits) & BITMASK; - auto h = m_dispatch[slot]; - if(h->is_dispatch()) + auto h = m_a_dispatch[slot]; + if(h->is_dispatch() || h->is_view()) h->lookup(address, start, end, handler); else { - start = m_ranges[slot].start; - end = m_ranges[slot].end; + start = m_a_ranges[slot].start; + end = m_a_ranges[slot].end; handler = h; } } -template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::range_cut_before(offs_t address, int start) +template<int HighBits, int Width, int AddrShift> void handler_entry_write_dispatch<HighBits, Width, AddrShift>::range_cut_before(offs_t address, int start) { - while(--start >= 0) { - if(int(LowBits) > -AddrShift && m_dispatch[start]->is_dispatch()) { - static_cast<handler_entry_write_dispatch<LowBits, Width, AddrShift, Endian> *>(m_dispatch[start])->range_cut_before(address); + while(--start >= 0 && m_u_dispatch[start]) { + if(int(LowBits) > -AddrShift && m_u_dispatch[start]->is_dispatch()) { + static_cast<handler_entry_write_dispatch<LowBits, Width, AddrShift> *>(m_u_dispatch[start])->range_cut_before(address); break; } - if(m_ranges[start].end <= address) + if(m_u_ranges[start].end <= address) break; - m_ranges[start].end = address; + m_u_ranges[start].end = address; } } -template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::range_cut_after(offs_t address, int start) +template<int HighBits, int Width, int AddrShift> void handler_entry_write_dispatch<HighBits, Width, AddrShift>::range_cut_after(offs_t address, int start) { - while(++start < COUNT) { - if(int(LowBits) > -AddrShift && m_dispatch[start]->is_dispatch()) { - static_cast<handler_entry_write_dispatch<LowBits, Width, AddrShift, Endian> *>(m_dispatch[start])->range_cut_after(address); + while(++start < COUNT && m_u_dispatch[start]) { + if(int(LowBits) > -AddrShift && m_u_dispatch[start]->is_dispatch()) { + static_cast<handler_entry_write_dispatch<LowBits, Width, AddrShift> *>(m_u_dispatch[start])->range_cut_after(address); break; } - if(m_ranges[start].start >= address) + if(m_u_ranges[start].start >= address) break; - m_ranges[start].start = address; + m_u_ranges[start].start = address; } } -template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::populate_nomirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_write<Width, AddrShift, Endian> *handler) +template<int HighBits, int Width, int AddrShift> void handler_entry_write_dispatch<HighBits, Width, AddrShift>::populate_nomirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_write<Width, AddrShift> *handler) { - auto cur = m_dispatch[entry]; + auto cur = m_u_dispatch[entry]; if(cur->is_dispatch()) cur->populate_nomirror(start, end, ostart, oend, handler); else { - auto subdispatch = new handler_entry_write_dispatch<LowBits, Width, AddrShift, Endian>(handler_entry::m_space, m_ranges[entry], cur); + auto subdispatch = new handler_entry_write_dispatch<LowBits, Width, AddrShift>(this->m_space, m_u_ranges[entry], cur); cur->unref(); - m_dispatch[entry] = subdispatch; + m_u_dispatch[entry] = subdispatch; subdispatch->populate_nomirror(start, end, ostart, oend, handler); + range_cut_before((entry << LowBits) - 1, entry); + range_cut_after((entry + 1) << LowBits, entry); } } -template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::populate_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_write<Width, AddrShift, Endian> *handler) +template<int HighBits, int Width, int AddrShift> void handler_entry_write_dispatch<HighBits, Width, AddrShift>::populate_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_write<Width, AddrShift> *handler) { offs_t start_entry = (start & HIGHMASK) >> LowBits; offs_t end_entry = (end & HIGHMASK) >> LowBits; range_cut_before(ostart-1, start_entry); range_cut_after(oend+1, end_entry); - if(LowBits <= Width + AddrShift) { + if constexpr(LowBits <= Width + AddrShift) { + if(handler->is_view()) { + int delta = dispatch_entry(ostart) - handler->dispatch_entry(ostart); + handler->init_handlers(start >> LowBits, end >> LowBits, LowBits, ostart, oend, m_u_dispatch + delta, m_u_ranges + delta); + } handler->ref(end_entry - start_entry); for(offs_t ent = start_entry; ent <= end_entry; ent++) { - m_dispatch[ent]->unref(); - m_dispatch[ent] = handler; - m_ranges[ent].set(ostart, oend); + m_u_dispatch[ent]->unref(); + m_u_dispatch[ent] = handler; + m_u_ranges[ent].set(ostart, oend); } } else if(start_entry == end_entry) { if(!(start & LOWMASK) && (end & LOWMASK) == LOWMASK) { - m_dispatch[start_entry]->unref(); - m_dispatch[start_entry] = handler; - m_ranges[start_entry].set(ostart, oend); + if(handler->is_view()) { + int delta = dispatch_entry(ostart) - handler->dispatch_entry(ostart); + handler->init_handlers(start >> LowBits, end >> LowBits, LowBits, ostart, oend, m_u_dispatch + delta, m_u_ranges + delta); + } + m_u_dispatch[start_entry]->unref(); + m_u_dispatch[start_entry] = handler; + m_u_ranges[start_entry].set(ostart, oend); } else - populate_nomirror_subdispatch(start_entry, start & LOWMASK, end & LOWMASK, ostart, oend, handler); + populate_nomirror_subdispatch(start_entry, start, end, ostart, oend, handler); } else { if(start & LOWMASK) { - populate_nomirror_subdispatch(start_entry, start & LOWMASK, LOWMASK, ostart, oend, handler); + populate_nomirror_subdispatch(start_entry, start, start | LOWMASK, ostart, oend, handler); start_entry++; + start = (start | LOWMASK) + 1; if(start_entry <= end_entry) handler->ref(); } if((end & LOWMASK) != LOWMASK) { - populate_nomirror_subdispatch(end_entry, 0, end & LOWMASK, ostart, oend, handler); + populate_nomirror_subdispatch(end_entry, end & ~LOWMASK, end, ostart, oend, handler); end_entry--; + end = (end & ~LOWMASK) - 1; if(start_entry <= end_entry) handler->ref(); } if(start_entry <= end_entry) { + if(handler->is_view()) { + int delta = dispatch_entry(ostart) - handler->dispatch_entry(ostart); + handler->init_handlers(start >> LowBits, end >> LowBits, LowBits, ostart, oend, m_u_dispatch + delta, m_u_ranges + delta); + } handler->ref(end_entry - start_entry); for(offs_t ent = start_entry; ent <= end_entry; ent++) { - m_dispatch[ent]->unref(); - m_dispatch[ent] = handler; - m_ranges[ent].set(ostart, oend); + m_u_dispatch[ent]->unref(); + m_u_dispatch[ent] = handler; + m_u_ranges[ent].set(ostart, oend); } } } } -template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::populate_mirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_write<Width, AddrShift, Endian> *handler) +template<int HighBits, int Width, int AddrShift> void handler_entry_write_dispatch<HighBits, Width, AddrShift>::populate_mirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_write<Width, AddrShift> *handler) { - auto cur = m_dispatch[entry]; + auto cur = m_u_dispatch[entry]; if(cur->is_dispatch()) cur->populate_mirror(start, end, ostart, oend, mirror, handler); else { - auto subdispatch = new handler_entry_write_dispatch<LowBits, Width, AddrShift, Endian>(handler_entry::m_space, m_ranges[entry], cur); + auto subdispatch = new handler_entry_write_dispatch<LowBits, Width, AddrShift>(this->m_space, m_u_ranges[entry], cur); cur->unref(); - m_dispatch[entry] = subdispatch; + m_u_dispatch[entry] = subdispatch; subdispatch->populate_mirror(start, end, ostart, oend, mirror, handler); + range_cut_before((entry << LowBits) - 1, entry); + range_cut_after((entry + 1) << LowBits, entry); } } -template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::populate_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_write<Width, AddrShift, Endian> *handler) +template<int HighBits, int Width, int AddrShift> void handler_entry_write_dispatch<HighBits, Width, AddrShift>::populate_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_write<Width, AddrShift> *handler) { offs_t hmirror = mirror & HIGHMASK; offs_t lmirror = mirror & LOWMASK; @@ -185,12 +306,10 @@ template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_ offs_t add = 1 + ~hmirror; offs_t offset = 0; offs_t base_entry = start >> LowBits; - start &= LOWMASK; - end &= LOWMASK; do { if(offset) handler->ref(); - populate_mirror_subdispatch(base_entry | (offset >> LowBits), start, end, ostart | offset, oend | offset, lmirror, handler); + populate_mirror_subdispatch(base_entry | (offset >> LowBits), start | offset, end | offset, ostart | offset, oend | offset, lmirror, handler); offset = (offset + add) & hmirror; } while(offset); } else { @@ -206,11 +325,11 @@ template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_ } } -template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::mismatched_patch(const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 rkey, std::vector<mapping> &mappings, handler_entry_write<Width, AddrShift, Endian> *&target) +template<int HighBits, int Width, int AddrShift> void handler_entry_write_dispatch<HighBits, Width, AddrShift>::mismatched_patch(const memory_units_descriptor<Width, AddrShift> &descriptor, u8 rkey, std::vector<mapping> &mappings, handler_entry_write<Width, AddrShift> *&target) { u8 ukey = descriptor.rkey_to_ukey(rkey); - handler_entry_write<Width, AddrShift, Endian> *original = target->is_units() ? target : nullptr; - handler_entry_write<Width, AddrShift, Endian> *replacement = nullptr; + handler_entry_write<Width, AddrShift> *original = target->is_units() ? target : nullptr; + handler_entry_write<Width, AddrShift> *replacement = nullptr; for(const auto &p : mappings) if(p.ukey == ukey && p.original == original) { replacement = p.patched; @@ -218,9 +337,9 @@ template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_ } if(!replacement) { if(original) - replacement = new handler_entry_write_units<Width, AddrShift, Endian>(descriptor, ukey, static_cast<handler_entry_write_units<Width, AddrShift, Endian> *>(original)); + replacement = new handler_entry_write_units<Width, AddrShift>(descriptor, ukey, static_cast<handler_entry_write_units<Width, AddrShift> *>(original)); else - replacement = new handler_entry_write_units<Width, AddrShift, Endian>(descriptor, ukey, inh::m_space); + replacement = new handler_entry_write_units<Width, AddrShift>(descriptor, ukey, this->m_space); mappings.emplace_back(mapping{ original, replacement, ukey }); } else @@ -229,92 +348,91 @@ template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_ target = replacement; } -template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::populate_mismatched_nomirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 rkey, std::vector<mapping> &mappings) +template<int HighBits, int Width, int AddrShift> void handler_entry_write_dispatch<HighBits, Width, AddrShift>::populate_mismatched_nomirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, const memory_units_descriptor<Width, AddrShift> &descriptor, u8 rkey, std::vector<mapping> &mappings) { - auto cur = m_dispatch[entry]; + auto cur = m_u_dispatch[entry]; if(cur->is_dispatch()) cur->populate_mismatched_nomirror(start, end, ostart, oend, descriptor, rkey, mappings); else { - auto subdispatch = new handler_entry_write_dispatch<LowBits, Width, AddrShift, Endian>(handler_entry::m_space, m_ranges[entry], cur); + auto subdispatch = new handler_entry_write_dispatch<LowBits, Width, AddrShift>(this->m_space, m_u_ranges[entry], cur); cur->unref(); - m_dispatch[entry] = subdispatch; + m_u_dispatch[entry] = subdispatch; subdispatch->populate_mismatched_nomirror(start, end, ostart, oend, descriptor, rkey, mappings); } } -template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::populate_mismatched_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 rkey, std::vector<mapping> &mappings) +template<int HighBits, int Width, int AddrShift> void handler_entry_write_dispatch<HighBits, Width, AddrShift>::populate_mismatched_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, const memory_units_descriptor<Width, AddrShift> &descriptor, u8 rkey, std::vector<mapping> &mappings) { offs_t start_entry = (start & HIGHMASK) >> LowBits; offs_t end_entry = (end & HIGHMASK) >> LowBits; range_cut_before(ostart-1, start_entry); range_cut_after(oend+1, end_entry); - if(LowBits <= Width + AddrShift) { + if constexpr(LowBits <= Width + AddrShift) { for(offs_t ent = start_entry; ent <= end_entry; ent++) { u8 rkey1 = rkey; if(ent != start_entry) rkey1 &= ~handler_entry::START; if(ent != end_entry) rkey1 &= ~handler_entry::END; - mismatched_patch(descriptor, rkey1, mappings, m_dispatch[ent]); - m_ranges[ent].intersect(ostart, oend); + mismatched_patch(descriptor, rkey1, mappings, m_u_dispatch[ent]); + m_u_ranges[ent].set(ostart, oend); } } else if(start_entry == end_entry) { if(!(start & LOWMASK) && (end & LOWMASK) == LOWMASK) { - if(m_dispatch[start_entry]->is_dispatch()) - m_dispatch[start_entry]->populate_mismatched_nomirror(start & LOWMASK, end & LOWMASK, ostart, oend, descriptor, rkey, mappings); + if(m_u_dispatch[start_entry]->is_dispatch()) + m_u_dispatch[start_entry]->populate_mismatched_nomirror(start, end, ostart, oend, descriptor, rkey, mappings); else { - mismatched_patch(descriptor, rkey, mappings, m_dispatch[start_entry]); - m_ranges[start_entry].intersect(ostart, oend); + mismatched_patch(descriptor, rkey, mappings, m_u_dispatch[start_entry]); + m_u_ranges[start_entry].set(ostart, oend); } } else - populate_mismatched_nomirror_subdispatch(start_entry, start & LOWMASK, end & LOWMASK, ostart, oend, descriptor, rkey, mappings); + populate_mismatched_nomirror_subdispatch(start_entry, start, end, ostart, oend, descriptor, rkey, mappings); } else { if(start & LOWMASK) { - populate_mismatched_nomirror_subdispatch(start_entry, start & LOWMASK, LOWMASK, ostart, oend, descriptor, rkey & ~handler_entry::END, mappings); + populate_mismatched_nomirror_subdispatch(start_entry, start, start | LOWMASK, ostart, oend, descriptor, rkey & ~handler_entry::END, mappings); start_entry++; rkey &= ~handler_entry::START; } if((end & LOWMASK) != LOWMASK) { - populate_mismatched_nomirror_subdispatch(end_entry, 0, end & LOWMASK, ostart, oend, descriptor, rkey & ~handler_entry::START, mappings); + populate_mismatched_nomirror_subdispatch(end_entry, end & ~LOWMASK, end, ostart, oend, descriptor, rkey & ~handler_entry::START, mappings); end_entry--; rkey &= ~handler_entry::END; } - if(start_entry <= end_entry) { - for(offs_t ent = start_entry; ent <= end_entry; ent++) { - u8 rkey1 = rkey; - if(ent != start_entry) - rkey1 &= ~handler_entry::START; - if(ent != end_entry) - rkey1 &= ~handler_entry::END; - if(m_dispatch[ent]->is_dispatch()) - m_dispatch[ent]->populate_mismatched_nomirror(start & LOWMASK, end & LOWMASK, ostart, oend, descriptor, rkey1, mappings); - else { - mismatched_patch(descriptor, rkey1, mappings, m_dispatch[ent]); - m_ranges[ent].intersect(ostart, oend); - } + offs_t base = start & ~LOWMASK; + for(offs_t ent = start_entry; ent <= end_entry; ent++) { + u8 rkey1 = rkey; + if(ent != start_entry) + rkey1 &= ~handler_entry::START; + if(ent != end_entry) + rkey1 &= ~handler_entry::END; + if(m_u_dispatch[ent]->is_dispatch()) + m_u_dispatch[ent]->populate_mismatched_nomirror(base | (ent << LowBits), base | (ent << LowBits) | LOWMASK, ostart, oend, descriptor, rkey1, mappings); + else { + mismatched_patch(descriptor, rkey1, mappings, m_u_dispatch[ent]); + m_u_ranges[ent].set(ostart, oend); } } } } -template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::populate_mismatched_mirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, std::vector<mapping> &mappings) +template<int HighBits, int Width, int AddrShift> void handler_entry_write_dispatch<HighBits, Width, AddrShift>::populate_mismatched_mirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, const memory_units_descriptor<Width, AddrShift> &descriptor, std::vector<mapping> &mappings) { - auto cur = m_dispatch[entry]; + auto cur = m_u_dispatch[entry]; if(cur->is_dispatch()) cur->populate_mismatched_mirror(start, end, ostart, oend, mirror, descriptor, mappings); else { - auto subdispatch = new handler_entry_write_dispatch<LowBits, Width, AddrShift, Endian>(handler_entry::m_space, m_ranges[entry], cur); + auto subdispatch = new handler_entry_write_dispatch<LowBits, Width, AddrShift>(this->m_space, m_u_ranges[entry], cur); cur->unref(); - m_dispatch[entry] = subdispatch; + m_u_dispatch[entry] = subdispatch; subdispatch->populate_mismatched_mirror(start, end, ostart, oend, mirror, descriptor, mappings); } } -template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::populate_mismatched_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, std::vector<mapping> &mappings) +template<int HighBits, int Width, int AddrShift> void handler_entry_write_dispatch<HighBits, Width, AddrShift>::populate_mismatched_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, const memory_units_descriptor<Width, AddrShift> &descriptor, std::vector<mapping> &mappings) { offs_t hmirror = mirror & HIGHMASK; offs_t lmirror = mirror & LOWMASK; @@ -324,8 +442,6 @@ template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_ offs_t add = 1 + ~hmirror; offs_t offset = 0; offs_t base_entry = start >> LowBits; - start &= LOWMASK; - end &= LOWMASK; do { populate_mismatched_mirror_subdispatch(base_entry | (offset >> LowBits), start, end, ostart | offset, oend | offset, lmirror, descriptor, mappings); offset = (offset + add) & hmirror; @@ -342,98 +458,140 @@ template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_ } -template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::passthrough_patch(handler_entry_write_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings, handler_entry_write<Width, AddrShift, Endian> *&target) +template<int HighBits, int Width, int AddrShift> void handler_entry_write_dispatch<HighBits, Width, AddrShift>::passthrough_patch(handler_entry_write_passthrough<Width, AddrShift> *handler, std::vector<mapping> &mappings, handler_entry_write<Width, AddrShift> *&target) { - handler_entry_write<Width, AddrShift, Endian> *original = target; - handler_entry_write<Width, AddrShift, Endian> *replacement = nullptr; + // Look in cache first for(const auto &p : mappings) - if(p.original == original) { - replacement = p.patched; - break; + if(p.original == target) { + p.patched->ref(); + target->unref(); + target = p.patched; + return; } - if(!replacement) { - replacement = handler->instantiate(original); + + handler_entry_write<Width, AddrShift> *original = target; + u32 target_priority = original->f_get_pt(); + u32 new_priority = handler->f_get_pt(); + + // 3 cases: new one on top, new one on bottom, or new one replaces old one + + if(!target_priority || new_priority > target_priority || (new_priority == target_priority && !(new_priority & 1))) { + // New one goes over the old one + // previous one is not passthrough (target_priority = 0) + // new one has higher priority + // both have the same priority, and the priority is even which means keep both + + // We instantiate the new one with the old one under it and put it in place and in the cache + + handler_entry_write<Width, AddrShift> *replacement = handler->instantiate(original); mappings.emplace_back(mapping{ original, replacement }); - } else - replacement->ref(); - target->unref(); - target = replacement; + target->unref(); + target = replacement; + + } else if(new_priority == target_priority) { + // New one replaces the old one + // both have the same priority, and the priority is odd which means keep only the new one + + // We instantiate the new one with the old one's subtarget and put it in place and in the cache + + handler_entry_write<Width, AddrShift> *replacement = handler->instantiate(static_cast<handler_entry_write_passthrough<Width, AddrShift> *>(original)->get_subhandler()); + mappings.emplace_back(mapping{ original, replacement }); + target->unref(); + target = replacement; + + } else { + // New one goes under the old one + // both are passthrough and new one has lower priority + // + // This can be recursive, so do the passthrough patch on the subhandler, then instantiate the old one with the result + + handler_entry_write<Width, AddrShift> *recursive = static_cast<handler_entry_write_passthrough<Width, AddrShift> *>(original)->get_subhandler(); + recursive->ref(); + + passthrough_patch(handler, mappings, recursive); + + handler_entry_write<Width, AddrShift> *replacement = static_cast<handler_entry_write_passthrough<Width, AddrShift> *>(original)->instantiate(recursive); + mappings.emplace_back(mapping{ original, replacement }); + target->unref(); + target = replacement; + recursive->unref(); + } + } -template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::populate_passthrough_nomirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_write_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings) +template<int HighBits, int Width, int AddrShift> void handler_entry_write_dispatch<HighBits, Width, AddrShift>::populate_passthrough_nomirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_write_passthrough<Width, AddrShift> *handler, std::vector<mapping> &mappings) { - auto cur = m_dispatch[entry]; + auto cur = m_u_dispatch[entry]; if(cur->is_dispatch()) cur->populate_passthrough_nomirror(start, end, ostart, oend, handler, mappings); else { - auto subdispatch = new handler_entry_write_dispatch<LowBits, Width, AddrShift, Endian>(handler_entry::m_space, m_ranges[entry], cur); + auto subdispatch = new handler_entry_write_dispatch<LowBits, Width, AddrShift>(this->m_space, m_u_ranges[entry], cur); cur->unref(); - m_dispatch[entry] = subdispatch; + m_u_dispatch[entry] = subdispatch; subdispatch->populate_passthrough_nomirror(start, end, ostart, oend, handler, mappings); } } -template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::populate_passthrough_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_write_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings) +template<int HighBits, int Width, int AddrShift> void handler_entry_write_dispatch<HighBits, Width, AddrShift>::populate_passthrough_nomirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, handler_entry_write_passthrough<Width, AddrShift> *handler, std::vector<mapping> &mappings) { offs_t start_entry = (start & HIGHMASK) >> LowBits; offs_t end_entry = (end & HIGHMASK) >> LowBits; range_cut_before(ostart-1, start_entry); range_cut_after(oend+1, end_entry); - if(LowBits <= Width + AddrShift) { + if constexpr(LowBits <= Width + AddrShift) { for(offs_t ent = start_entry; ent <= end_entry; ent++) { - passthrough_patch(handler, mappings, m_dispatch[ent]); - m_ranges[ent].intersect(ostart, oend); + passthrough_patch(handler, mappings, m_u_dispatch[ent]); + m_u_ranges[ent].intersect(ostart, oend); } } else if(start_entry == end_entry) { if(!(start & LOWMASK) && (end & LOWMASK) == LOWMASK) { - if(m_dispatch[start_entry]->is_dispatch()) - m_dispatch[start_entry]->populate_passthrough_nomirror(start & LOWMASK, end & LOWMASK, ostart, oend, handler, mappings); + if(m_u_dispatch[start_entry]->is_dispatch()) + m_u_dispatch[start_entry]->populate_passthrough_nomirror(start, end, ostart, oend, handler, mappings); else { - passthrough_patch(handler, mappings, m_dispatch[start_entry]); - m_ranges[start_entry].intersect(ostart, oend); + passthrough_patch(handler, mappings, m_u_dispatch[start_entry]); + m_u_ranges[start_entry].intersect(ostart, oend); } } else - populate_passthrough_nomirror_subdispatch(start_entry, start & LOWMASK, end & LOWMASK, ostart, oend, handler, mappings); + populate_passthrough_nomirror_subdispatch(start_entry, start, end, ostart, oend, handler, mappings); } else { if(start & LOWMASK) { - populate_passthrough_nomirror_subdispatch(start_entry, start & LOWMASK, LOWMASK, ostart, oend, handler, mappings); + populate_passthrough_nomirror_subdispatch(start_entry, start, start | LOWMASK, ostart, oend, handler, mappings); start_entry++; } if((end & LOWMASK) != LOWMASK) { - populate_passthrough_nomirror_subdispatch(end_entry, 0, end & LOWMASK, ostart, oend, handler, mappings); + populate_passthrough_nomirror_subdispatch(end_entry, end & ~LOWMASK, end, ostart, oend, handler, mappings); end_entry--; } - if(start_entry <= end_entry) { - for(offs_t ent = start_entry; ent <= end_entry; ent++) { - if(m_dispatch[ent]->is_dispatch()) - m_dispatch[ent]->populate_passthrough_nomirror(start & LOWMASK, end & LOWMASK, ostart, oend, handler, mappings); - else { - passthrough_patch(handler, mappings, m_dispatch[ent]); - m_ranges[ent].intersect(ostart, oend); - } + offs_t base = start & ~LOWMASK; + for(offs_t ent = start_entry; ent <= end_entry; ent++) { + if(m_u_dispatch[ent]->is_dispatch()) + m_u_dispatch[ent]->populate_passthrough_nomirror(base | (ent << LowBits), base | (ent << LowBits) | LOWMASK, ostart, oend, handler, mappings); + else { + passthrough_patch(handler, mappings, m_u_dispatch[ent]); + m_u_ranges[ent].intersect(ostart, oend); } } } } -template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::populate_passthrough_mirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_write_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings) +template<int HighBits, int Width, int AddrShift> void handler_entry_write_dispatch<HighBits, Width, AddrShift>::populate_passthrough_mirror_subdispatch(offs_t entry, offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_write_passthrough<Width, AddrShift> *handler, std::vector<mapping> &mappings) { - auto cur = m_dispatch[entry]; + auto cur = m_u_dispatch[entry]; if(cur->is_dispatch()) cur->populate_passthrough_mirror(start, end, ostart, oend, mirror, handler, mappings); else { - auto subdispatch = new handler_entry_write_dispatch<LowBits, Width, AddrShift, Endian>(handler_entry::m_space, m_ranges[entry], cur); + auto subdispatch = new handler_entry_write_dispatch<LowBits, Width, AddrShift>(this->m_space, m_u_ranges[entry], cur); cur->unref(); - m_dispatch[entry] = subdispatch; + m_u_dispatch[entry] = subdispatch; subdispatch->populate_passthrough_mirror(start, end, ostart, oend, mirror, handler, mappings); } } -template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::populate_passthrough_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_write_passthrough<Width, AddrShift, Endian> *handler, std::vector<mapping> &mappings) +template<int HighBits, int Width, int AddrShift> void handler_entry_write_dispatch<HighBits, Width, AddrShift>::populate_passthrough_mirror(offs_t start, offs_t end, offs_t ostart, offs_t oend, offs_t mirror, handler_entry_write_passthrough<Width, AddrShift> *handler, std::vector<mapping> &mappings) { offs_t hmirror = mirror & HIGHMASK; offs_t lmirror = mirror & LOWMASK; @@ -443,10 +601,8 @@ template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_ offs_t add = 1 + ~hmirror; offs_t offset = 0; offs_t base_entry = start >> LowBits; - start &= LOWMASK; - end &= LOWMASK; do { - populate_passthrough_mirror_subdispatch(base_entry | (offset >> LowBits), start, end, ostart | offset, oend | offset, lmirror, handler, mappings); + populate_passthrough_mirror_subdispatch(base_entry | (offset >> LowBits), start | offset, end | offset, ostart | offset, oend | offset, lmirror, handler, mappings); offset = (offset + add) & hmirror; } while(offset); } else { @@ -460,25 +616,126 @@ template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_ } } -template<int HighBits, int Width, int AddrShift, int Endian> void handler_entry_write_dispatch<HighBits, Width, AddrShift, Endian>::detach(const std::unordered_set<handler_entry *> &handlers) +template<int HighBits, int Width, int AddrShift> void handler_entry_write_dispatch<HighBits, Width, AddrShift>::detach(const std::unordered_set<handler_entry *> &handlers) { for(unsigned int i=0; i != COUNT; i++) { - if(m_dispatch[i]->is_dispatch()) { - m_dispatch[i]->detach(handlers); + if(m_u_dispatch[i]->is_dispatch()) { + m_u_dispatch[i]->detach(handlers); continue; } - if(!m_dispatch[i]->is_passthrough()) + if(!m_u_dispatch[i]->is_passthrough()) continue; - auto np = static_cast<handler_entry_write_passthrough<Width, AddrShift, Endian> *>(m_dispatch[i]); + auto np = static_cast<handler_entry_write_passthrough<Width, AddrShift> *>(m_u_dispatch[i]); if(handlers.find(np) != handlers.end()) { - m_dispatch[i] = np->get_subhandler(); - m_dispatch[i]->ref(); + m_u_dispatch[i] = np->get_subhandler(); + m_u_dispatch[i]->ref(); np->unref(); } else np->detach(handlers); } } + +template<int HighBits, int Width, int AddrShift> void handler_entry_write_dispatch<HighBits, Width, AddrShift>::init_handlers(offs_t start_entry, offs_t end_entry, u32 lowbits, offs_t ostart, offs_t oend, handler_entry_write<Width, AddrShift> **dispatch, handler_entry::range *ranges) +{ + if(lowbits < LowBits) { + offs_t entry = start_entry >> LowBits; + if(entry != (end_entry >> LowBits)) + fatalerror("Recursive init_handlers spanning multiple entries.\n"); + entry &= BITMASK; + handler_entry_write_dispatch<LowBits, Width, AddrShift> *subdispatch; + if(m_u_dispatch[entry]->flags() & handler_entry::F_DISPATCH) + subdispatch = static_cast<handler_entry_write_dispatch<LowBits, Width, AddrShift> *>(m_u_dispatch[entry]); + + else if(!(m_u_dispatch[entry]->flags() & handler_entry::F_UNMAP)) + fatalerror("Collision on multiple init_handlers calls"); + + else { + m_u_dispatch[entry]->unref(); + m_u_dispatch[entry] = subdispatch = new handler_entry_write_dispatch<LowBits, Width, AddrShift>(this->m_space, m_u_ranges[entry], nullptr); + } + int delta = dispatch_entry(ostart) - subdispatch->dispatch_entry(ostart); + subdispatch->init_handlers(start_entry, end_entry, lowbits, ostart, oend, dispatch + delta, ranges + delta); + + } else if(lowbits != LowBits) { + u32 dt = lowbits - LowBits; + u32 ne = 1 << dt; + u32 ee = end_entry - start_entry; + auto filter = [s = m_global_range.start, e = m_global_range.end] (handler_entry::range r) { r.intersect(s, e); return r; }; + for(offs_t entry = 0; entry <= ee; entry++) { + dispatch[entry]->ref(ne); + u32 e0 = (entry << dt) & BITMASK; + for(offs_t e = 0; e != ne; e++) { + offs_t e1 = e0 | e; + if(!(m_u_dispatch[e1]->flags() & handler_entry::F_UNMAP)) + fatalerror("Collision on multiple init_handlers calls"); + m_u_dispatch[e1]->unref(); + m_u_dispatch[e1] = dispatch[entry]; + m_u_ranges[e1] = filter(ranges[entry]); + } + } + + } else { + auto filter = [s = m_global_range.start, e = m_global_range.end] (handler_entry::range r) { r.intersect(s, e); return r; }; + for(offs_t entry = start_entry & BITMASK; entry <= (end_entry & BITMASK); entry++) { + if(!(m_u_dispatch[entry]->flags() & handler_entry::F_UNMAP)) + fatalerror("Collision on multiple init_handlers calls"); + m_u_dispatch[entry]->unref(); + m_u_dispatch[entry] = dispatch[entry]; + m_u_ranges[entry] = filter(ranges[entry]); + dispatch[entry]->ref(); + } + } +} + +template<int HighBits, int Width, int AddrShift> void handler_entry_write_dispatch<HighBits, Width, AddrShift>::select_a(int id) +{ + u32 i = id+1; + if(i >= m_dispatch_array.size()) + fatalerror("out-of-range view selection."); + + m_a_ranges = m_ranges_array[i].data(); + m_a_dispatch = m_dispatch_array[i].data(); +} + +template<int HighBits, int Width, int AddrShift> void handler_entry_write_dispatch<HighBits, Width, AddrShift>::select_u(int id) +{ + u32 i = id+1; + if(i > m_dispatch_array.size()) + fatalerror("out-of-range view update selection."); + else if(i == m_dispatch_array.size()) { + u32 aid = (handler_array *)(m_a_dispatch) - m_dispatch_array.data(); + + m_dispatch_array.resize(i+1); + m_ranges_array.resize(i+1); + m_a_ranges = m_ranges_array[aid].data(); + m_a_dispatch = m_dispatch_array[aid].data(); + m_u_ranges = m_ranges_array[i].data(); + m_u_dispatch = m_dispatch_array[i].data(); + + for(u32 entry = 0; entry != COUNT; entry++) + if(m_dispatch_array[0][entry]) { + m_u_dispatch[entry] = m_dispatch_array[0][entry]->dup(); + m_u_ranges[entry] = m_ranges_array[0][entry]; + } + + } else { + m_u_ranges = m_ranges_array[i].data(); + m_u_dispatch = m_dispatch_array[i].data(); + } +} + +template<int HighBits, int Width, int AddrShift> handler_entry_write<Width, AddrShift> *handler_entry_write_dispatch<HighBits, Width, AddrShift>::dup() +{ + if(m_view) { + handler_entry::ref(); + return this; + } + + return new handler_entry_write_dispatch<HighBits, Width, AddrShift>(this); +} + +#endif // MAME_EMU_EMUMEM_HEDW_IPP diff --git a/src/emu/emumem_hedw0.cpp b/src/emu/emumem_hedw0.cpp index acfbd3362c3..33a8c17e91d 100644 --- a/src/emu/emumem_hedw0.cpp +++ b/src/emu/emumem_hedw0.cpp @@ -5,184 +5,122 @@ #include "emumem_hedw.ipp" -template class handler_entry_write_dispatch< 1, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 1, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 2, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 2, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 3, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 3, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 4, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 4, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 5, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 5, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 6, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 6, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 7, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 7, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 8, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 8, 0, 1, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch< 0, 0, 1>; +template class handler_entry_write_dispatch< 1, 0, 1>; +template class handler_entry_write_dispatch< 2, 0, 1>; +template class handler_entry_write_dispatch< 3, 0, 1>; +template class handler_entry_write_dispatch< 4, 0, 1>; +template class handler_entry_write_dispatch< 5, 0, 1>; +template class handler_entry_write_dispatch< 6, 0, 1>; +template class handler_entry_write_dispatch< 7, 0, 1>; +template class handler_entry_write_dispatch< 8, 0, 1>; -template class handler_entry_write_dispatch< 1, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 1, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 2, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 2, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 3, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 3, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 4, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 4, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 5, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 5, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 6, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 6, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 7, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 7, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 8, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 8, 0, 0, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch< 0, 0, 0>; +template class handler_entry_write_dispatch< 1, 0, 0>; +template class handler_entry_write_dispatch< 2, 0, 0>; +template class handler_entry_write_dispatch< 3, 0, 0>; +template class handler_entry_write_dispatch< 4, 0, 0>; +template class handler_entry_write_dispatch< 5, 0, 0>; +template class handler_entry_write_dispatch< 6, 0, 0>; +template class handler_entry_write_dispatch< 7, 0, 0>; +template class handler_entry_write_dispatch< 8, 0, 0>; -template class handler_entry_write_dispatch< 1, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 1, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 2, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 2, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 3, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 3, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 4, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 4, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 5, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 5, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 6, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 6, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 7, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 7, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 8, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 8, 1, 3, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch< 0, 1, 3>; +template class handler_entry_write_dispatch< 1, 1, 3>; +template class handler_entry_write_dispatch< 2, 1, 3>; +template class handler_entry_write_dispatch< 3, 1, 3>; +template class handler_entry_write_dispatch< 4, 1, 3>; +template class handler_entry_write_dispatch< 5, 1, 3>; +template class handler_entry_write_dispatch< 6, 1, 3>; +template class handler_entry_write_dispatch< 7, 1, 3>; +template class handler_entry_write_dispatch< 8, 1, 3>; -template class handler_entry_write_dispatch< 1, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 1, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 2, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 2, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 3, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 3, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 4, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 4, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 5, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 5, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 6, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 6, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 7, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 7, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 8, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 8, 1, 0, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch< 1, 1, 0>; +template class handler_entry_write_dispatch< 2, 1, 0>; +template class handler_entry_write_dispatch< 3, 1, 0>; +template class handler_entry_write_dispatch< 4, 1, 0>; +template class handler_entry_write_dispatch< 5, 1, 0>; +template class handler_entry_write_dispatch< 6, 1, 0>; +template class handler_entry_write_dispatch< 7, 1, 0>; +template class handler_entry_write_dispatch< 8, 1, 0>; -template class handler_entry_write_dispatch< 1, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 1, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 2, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 2, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 3, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 3, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 4, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 4, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 5, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 5, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 6, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 6, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 7, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 7, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 8, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 8, 1, -1, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch< 0, 1, -1>; +template class handler_entry_write_dispatch< 1, 1, -1>; +template class handler_entry_write_dispatch< 2, 1, -1>; +template class handler_entry_write_dispatch< 3, 1, -1>; +template class handler_entry_write_dispatch< 4, 1, -1>; +template class handler_entry_write_dispatch< 5, 1, -1>; +template class handler_entry_write_dispatch< 6, 1, -1>; +template class handler_entry_write_dispatch< 7, 1, -1>; +template class handler_entry_write_dispatch< 8, 1, -1>; -template class handler_entry_write_dispatch< 2, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 2, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 3, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 3, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 4, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 4, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 5, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 5, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 6, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 6, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 7, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 7, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 8, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 8, 2, 0, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch< 0, 2, 3>; +template class handler_entry_write_dispatch< 1, 2, 3>; +template class handler_entry_write_dispatch< 2, 2, 3>; +template class handler_entry_write_dispatch< 3, 2, 3>; +template class handler_entry_write_dispatch< 4, 2, 3>; +template class handler_entry_write_dispatch< 5, 2, 3>; +template class handler_entry_write_dispatch< 6, 2, 3>; +template class handler_entry_write_dispatch< 7, 2, 3>; +template class handler_entry_write_dispatch< 8, 2, 3>; -template class handler_entry_write_dispatch< 2, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 2, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 3, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 3, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 4, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 4, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 5, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 5, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 6, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 6, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 7, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 7, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 8, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 8, 2, -1, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch< 2, 2, 0>; +template class handler_entry_write_dispatch< 3, 2, 0>; +template class handler_entry_write_dispatch< 4, 2, 0>; +template class handler_entry_write_dispatch< 5, 2, 0>; +template class handler_entry_write_dispatch< 6, 2, 0>; +template class handler_entry_write_dispatch< 7, 2, 0>; +template class handler_entry_write_dispatch< 8, 2, 0>; -template class handler_entry_write_dispatch< 2, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 2, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 3, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 3, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 4, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 4, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 5, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 5, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 6, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 6, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 7, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 7, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 8, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 8, 2, -2, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch< 1, 2, -1>; +template class handler_entry_write_dispatch< 2, 2, -1>; +template class handler_entry_write_dispatch< 3, 2, -1>; +template class handler_entry_write_dispatch< 4, 2, -1>; +template class handler_entry_write_dispatch< 5, 2, -1>; +template class handler_entry_write_dispatch< 6, 2, -1>; +template class handler_entry_write_dispatch< 7, 2, -1>; +template class handler_entry_write_dispatch< 8, 2, -1>; -template class handler_entry_write_dispatch< 3, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 3, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 4, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 4, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 5, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 5, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 6, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 6, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 7, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 7, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 8, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 8, 3, 0, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch< 0, 2, -2>; +template class handler_entry_write_dispatch< 1, 2, -2>; +template class handler_entry_write_dispatch< 2, 2, -2>; +template class handler_entry_write_dispatch< 3, 2, -2>; +template class handler_entry_write_dispatch< 4, 2, -2>; +template class handler_entry_write_dispatch< 5, 2, -2>; +template class handler_entry_write_dispatch< 6, 2, -2>; +template class handler_entry_write_dispatch< 7, 2, -2>; +template class handler_entry_write_dispatch< 8, 2, -2>; -template class handler_entry_write_dispatch< 3, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 3, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 4, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 4, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 5, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 5, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 6, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 6, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 7, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 7, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 8, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 8, 3, -1, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch< 3, 3, 0>; +template class handler_entry_write_dispatch< 4, 3, 0>; +template class handler_entry_write_dispatch< 5, 3, 0>; +template class handler_entry_write_dispatch< 6, 3, 0>; +template class handler_entry_write_dispatch< 7, 3, 0>; +template class handler_entry_write_dispatch< 8, 3, 0>; -template class handler_entry_write_dispatch< 3, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 3, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 4, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 4, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 5, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 5, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 6, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 6, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 7, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 7, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 8, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 8, 3, -2, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch< 2, 3, -1>; +template class handler_entry_write_dispatch< 3, 3, -1>; +template class handler_entry_write_dispatch< 4, 3, -1>; +template class handler_entry_write_dispatch< 5, 3, -1>; +template class handler_entry_write_dispatch< 6, 3, -1>; +template class handler_entry_write_dispatch< 7, 3, -1>; +template class handler_entry_write_dispatch< 8, 3, -1>; -template class handler_entry_write_dispatch< 3, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 3, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 4, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 4, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 5, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 5, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 6, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 6, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 7, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 7, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch< 8, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 8, 3, -3, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch< 1, 3, -2>; +template class handler_entry_write_dispatch< 2, 3, -2>; +template class handler_entry_write_dispatch< 3, 3, -2>; +template class handler_entry_write_dispatch< 4, 3, -2>; +template class handler_entry_write_dispatch< 5, 3, -2>; +template class handler_entry_write_dispatch< 6, 3, -2>; +template class handler_entry_write_dispatch< 7, 3, -2>; +template class handler_entry_write_dispatch< 8, 3, -2>; + +template class handler_entry_write_dispatch< 0, 3, -3>; +template class handler_entry_write_dispatch< 1, 3, -3>; +template class handler_entry_write_dispatch< 2, 3, -3>; +template class handler_entry_write_dispatch< 3, 3, -3>; +template class handler_entry_write_dispatch< 4, 3, -3>; +template class handler_entry_write_dispatch< 5, 3, -3>; +template class handler_entry_write_dispatch< 6, 3, -3>; +template class handler_entry_write_dispatch< 7, 3, -3>; +template class handler_entry_write_dispatch< 8, 3, -3>; diff --git a/src/emu/emumem_hedw1.cpp b/src/emu/emumem_hedw1.cpp index d3d3b9e5f81..ea81f92663b 100644 --- a/src/emu/emumem_hedw1.cpp +++ b/src/emu/emumem_hedw1.cpp @@ -4,206 +4,119 @@ #include "emu.h" #include "emumem_hedw.ipp" -template class handler_entry_write_dispatch< 9, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 9, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<10, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<10, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<11, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<11, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<12, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<12, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<13, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<13, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<14, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<14, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<15, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<15, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<16, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<16, 0, 1, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch< 9, 0, 1>; +template class handler_entry_write_dispatch<10, 0, 1>; +template class handler_entry_write_dispatch<11, 0, 1>; +template class handler_entry_write_dispatch<12, 0, 1>; +template class handler_entry_write_dispatch<13, 0, 1>; +template class handler_entry_write_dispatch<14, 0, 1>; +template class handler_entry_write_dispatch<15, 0, 1>; +template class handler_entry_write_dispatch<16, 0, 1>; -template class handler_entry_write_dispatch< 9, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 9, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<10, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<10, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<11, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<11, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<12, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<12, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<13, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<13, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<14, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<14, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<15, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<15, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<16, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<16, 0, 0, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch< 9, 0, 0>; +template class handler_entry_write_dispatch<10, 0, 0>; +template class handler_entry_write_dispatch<11, 0, 0>; +template class handler_entry_write_dispatch<12, 0, 0>; +template class handler_entry_write_dispatch<13, 0, 0>; +template class handler_entry_write_dispatch<14, 0, 0>; +template class handler_entry_write_dispatch<15, 0, 0>; +template class handler_entry_write_dispatch<16, 0, 0>; -template class handler_entry_write_dispatch< 9, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 9, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<10, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<10, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<11, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<11, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<12, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<12, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<13, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<13, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<14, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<14, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<15, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<15, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<16, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<16, 1, 3, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch< 9, 1, 3>; +template class handler_entry_write_dispatch<10, 1, 3>; +template class handler_entry_write_dispatch<11, 1, 3>; +template class handler_entry_write_dispatch<12, 1, 3>; +template class handler_entry_write_dispatch<13, 1, 3>; +template class handler_entry_write_dispatch<14, 1, 3>; +template class handler_entry_write_dispatch<15, 1, 3>; +template class handler_entry_write_dispatch<16, 1, 3>; -template class handler_entry_write_dispatch< 9, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 9, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<10, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<10, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<11, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<11, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<12, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<12, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<13, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<13, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<14, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<14, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<15, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<15, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<16, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<16, 1, 0, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch< 9, 1, 0>; +template class handler_entry_write_dispatch<10, 1, 0>; +template class handler_entry_write_dispatch<11, 1, 0>; +template class handler_entry_write_dispatch<12, 1, 0>; +template class handler_entry_write_dispatch<13, 1, 0>; +template class handler_entry_write_dispatch<14, 1, 0>; +template class handler_entry_write_dispatch<15, 1, 0>; +template class handler_entry_write_dispatch<16, 1, 0>; -template class handler_entry_write_dispatch< 9, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 9, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<10, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<10, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<11, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<11, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<12, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<12, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<13, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<13, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<14, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<14, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<15, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<15, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<16, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<16, 1, -1, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch< 9, 1, -1>; +template class handler_entry_write_dispatch<10, 1, -1>; +template class handler_entry_write_dispatch<11, 1, -1>; +template class handler_entry_write_dispatch<12, 1, -1>; +template class handler_entry_write_dispatch<13, 1, -1>; +template class handler_entry_write_dispatch<14, 1, -1>; +template class handler_entry_write_dispatch<15, 1, -1>; +template class handler_entry_write_dispatch<16, 1, -1>; -template class handler_entry_write_dispatch< 9, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 9, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<10, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<10, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<11, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<11, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<12, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<12, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<13, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<13, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<14, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<14, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<15, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<15, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<16, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<16, 2, 0, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch< 9, 2, 3>; +template class handler_entry_write_dispatch<10, 2, 3>; +template class handler_entry_write_dispatch<11, 2, 3>; +template class handler_entry_write_dispatch<12, 2, 3>; +template class handler_entry_write_dispatch<13, 2, 3>; +template class handler_entry_write_dispatch<14, 2, 3>; +template class handler_entry_write_dispatch<15, 2, 3>; +template class handler_entry_write_dispatch<16, 2, 3>; -template class handler_entry_write_dispatch< 9, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 9, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<10, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<10, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<11, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<11, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<12, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<12, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<13, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<13, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<14, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<14, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<15, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<15, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<16, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<16, 2, -1, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch< 9, 2, 0>; +template class handler_entry_write_dispatch<10, 2, 0>; +template class handler_entry_write_dispatch<11, 2, 0>; +template class handler_entry_write_dispatch<12, 2, 0>; +template class handler_entry_write_dispatch<13, 2, 0>; +template class handler_entry_write_dispatch<14, 2, 0>; +template class handler_entry_write_dispatch<15, 2, 0>; +template class handler_entry_write_dispatch<16, 2, 0>; -template class handler_entry_write_dispatch< 9, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 9, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<10, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<10, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<11, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<11, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<12, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<12, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<13, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<13, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<14, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<14, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<15, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<15, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<16, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<16, 2, -2, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch< 9, 2, -1>; +template class handler_entry_write_dispatch<10, 2, -1>; +template class handler_entry_write_dispatch<11, 2, -1>; +template class handler_entry_write_dispatch<12, 2, -1>; +template class handler_entry_write_dispatch<13, 2, -1>; +template class handler_entry_write_dispatch<14, 2, -1>; +template class handler_entry_write_dispatch<15, 2, -1>; +template class handler_entry_write_dispatch<16, 2, -1>; -template class handler_entry_write_dispatch< 9, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 9, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<10, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<10, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<11, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<11, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<12, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<12, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<13, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<13, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<14, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<14, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<15, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<15, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<16, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<16, 3, 0, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch< 9, 2, -2>; +template class handler_entry_write_dispatch<10, 2, -2>; +template class handler_entry_write_dispatch<11, 2, -2>; +template class handler_entry_write_dispatch<12, 2, -2>; +template class handler_entry_write_dispatch<13, 2, -2>; +template class handler_entry_write_dispatch<14, 2, -2>; +template class handler_entry_write_dispatch<15, 2, -2>; +template class handler_entry_write_dispatch<16, 2, -2>; -template class handler_entry_write_dispatch< 9, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 9, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<10, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<10, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<11, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<11, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<12, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<12, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<13, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<13, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<14, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<14, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<15, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<15, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<16, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<16, 3, -1, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch< 9, 3, 0>; +template class handler_entry_write_dispatch<10, 3, 0>; +template class handler_entry_write_dispatch<11, 3, 0>; +template class handler_entry_write_dispatch<12, 3, 0>; +template class handler_entry_write_dispatch<13, 3, 0>; +template class handler_entry_write_dispatch<14, 3, 0>; +template class handler_entry_write_dispatch<15, 3, 0>; +template class handler_entry_write_dispatch<16, 3, 0>; -template class handler_entry_write_dispatch< 9, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 9, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<10, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<10, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<11, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<11, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<12, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<12, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<13, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<13, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<14, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<14, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<15, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<15, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<16, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<16, 3, -2, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch< 9, 3, -1>; +template class handler_entry_write_dispatch<10, 3, -1>; +template class handler_entry_write_dispatch<11, 3, -1>; +template class handler_entry_write_dispatch<12, 3, -1>; +template class handler_entry_write_dispatch<13, 3, -1>; +template class handler_entry_write_dispatch<14, 3, -1>; +template class handler_entry_write_dispatch<15, 3, -1>; +template class handler_entry_write_dispatch<16, 3, -1>; -template class handler_entry_write_dispatch< 9, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch< 9, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<10, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<10, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<11, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<11, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<12, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<12, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<13, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<13, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<14, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<14, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<15, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<15, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<16, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<16, 3, -3, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch< 9, 3, -2>; +template class handler_entry_write_dispatch<10, 3, -2>; +template class handler_entry_write_dispatch<11, 3, -2>; +template class handler_entry_write_dispatch<12, 3, -2>; +template class handler_entry_write_dispatch<13, 3, -2>; +template class handler_entry_write_dispatch<14, 3, -2>; +template class handler_entry_write_dispatch<15, 3, -2>; +template class handler_entry_write_dispatch<16, 3, -2>; + +template class handler_entry_write_dispatch< 9, 3, -3>; +template class handler_entry_write_dispatch<10, 3, -3>; +template class handler_entry_write_dispatch<11, 3, -3>; +template class handler_entry_write_dispatch<12, 3, -3>; +template class handler_entry_write_dispatch<13, 3, -3>; +template class handler_entry_write_dispatch<14, 3, -3>; +template class handler_entry_write_dispatch<15, 3, -3>; +template class handler_entry_write_dispatch<16, 3, -3>; diff --git a/src/emu/emumem_hedw2.cpp b/src/emu/emumem_hedw2.cpp index 8e6fc4c4dcf..7ffb7603f01 100644 --- a/src/emu/emumem_hedw2.cpp +++ b/src/emu/emumem_hedw2.cpp @@ -4,206 +4,119 @@ #include "emu.h" #include "emumem_hedw.ipp" -template class handler_entry_write_dispatch<17, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<17, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<18, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<18, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<19, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<19, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<20, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<20, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<21, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<21, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<22, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<22, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<23, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<23, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<24, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<24, 0, 1, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch<17, 0, 1>; +template class handler_entry_write_dispatch<18, 0, 1>; +template class handler_entry_write_dispatch<19, 0, 1>; +template class handler_entry_write_dispatch<20, 0, 1>; +template class handler_entry_write_dispatch<21, 0, 1>; +template class handler_entry_write_dispatch<22, 0, 1>; +template class handler_entry_write_dispatch<23, 0, 1>; +template class handler_entry_write_dispatch<24, 0, 1>; -template class handler_entry_write_dispatch<17, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<17, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<18, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<18, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<19, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<19, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<20, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<20, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<21, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<21, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<22, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<22, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<23, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<23, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<24, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<24, 0, 0, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch<17, 0, 0>; +template class handler_entry_write_dispatch<18, 0, 0>; +template class handler_entry_write_dispatch<19, 0, 0>; +template class handler_entry_write_dispatch<20, 0, 0>; +template class handler_entry_write_dispatch<21, 0, 0>; +template class handler_entry_write_dispatch<22, 0, 0>; +template class handler_entry_write_dispatch<23, 0, 0>; +template class handler_entry_write_dispatch<24, 0, 0>; -template class handler_entry_write_dispatch<17, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<17, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<18, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<18, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<19, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<19, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<20, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<20, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<21, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<21, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<22, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<22, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<23, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<23, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<24, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<24, 1, 3, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch<17, 1, 3>; +template class handler_entry_write_dispatch<18, 1, 3>; +template class handler_entry_write_dispatch<19, 1, 3>; +template class handler_entry_write_dispatch<20, 1, 3>; +template class handler_entry_write_dispatch<21, 1, 3>; +template class handler_entry_write_dispatch<22, 1, 3>; +template class handler_entry_write_dispatch<23, 1, 3>; +template class handler_entry_write_dispatch<24, 1, 3>; -template class handler_entry_write_dispatch<17, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<17, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<18, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<18, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<19, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<19, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<20, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<20, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<21, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<21, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<22, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<22, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<23, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<23, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<24, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<24, 1, 0, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch<17, 1, 0>; +template class handler_entry_write_dispatch<18, 1, 0>; +template class handler_entry_write_dispatch<19, 1, 0>; +template class handler_entry_write_dispatch<20, 1, 0>; +template class handler_entry_write_dispatch<21, 1, 0>; +template class handler_entry_write_dispatch<22, 1, 0>; +template class handler_entry_write_dispatch<23, 1, 0>; +template class handler_entry_write_dispatch<24, 1, 0>; -template class handler_entry_write_dispatch<17, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<17, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<18, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<18, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<19, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<19, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<20, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<20, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<21, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<21, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<22, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<22, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<23, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<23, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<24, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<24, 1, -1, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch<17, 1, -1>; +template class handler_entry_write_dispatch<18, 1, -1>; +template class handler_entry_write_dispatch<19, 1, -1>; +template class handler_entry_write_dispatch<20, 1, -1>; +template class handler_entry_write_dispatch<21, 1, -1>; +template class handler_entry_write_dispatch<22, 1, -1>; +template class handler_entry_write_dispatch<23, 1, -1>; +template class handler_entry_write_dispatch<24, 1, -1>; -template class handler_entry_write_dispatch<17, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<17, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<18, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<18, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<19, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<19, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<20, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<20, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<21, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<21, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<22, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<22, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<23, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<23, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<24, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<24, 2, 0, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch<17, 2, 3>; +template class handler_entry_write_dispatch<18, 2, 3>; +template class handler_entry_write_dispatch<19, 2, 3>; +template class handler_entry_write_dispatch<20, 2, 3>; +template class handler_entry_write_dispatch<21, 2, 3>; +template class handler_entry_write_dispatch<22, 2, 3>; +template class handler_entry_write_dispatch<23, 2, 3>; +template class handler_entry_write_dispatch<24, 2, 3>; -template class handler_entry_write_dispatch<17, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<17, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<18, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<18, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<19, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<19, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<20, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<20, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<21, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<21, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<22, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<22, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<23, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<23, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<24, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<24, 2, -1, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch<17, 2, 0>; +template class handler_entry_write_dispatch<18, 2, 0>; +template class handler_entry_write_dispatch<19, 2, 0>; +template class handler_entry_write_dispatch<20, 2, 0>; +template class handler_entry_write_dispatch<21, 2, 0>; +template class handler_entry_write_dispatch<22, 2, 0>; +template class handler_entry_write_dispatch<23, 2, 0>; +template class handler_entry_write_dispatch<24, 2, 0>; -template class handler_entry_write_dispatch<17, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<17, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<18, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<18, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<19, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<19, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<20, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<20, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<21, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<21, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<22, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<22, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<23, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<23, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<24, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<24, 2, -2, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch<17, 2, -1>; +template class handler_entry_write_dispatch<18, 2, -1>; +template class handler_entry_write_dispatch<19, 2, -1>; +template class handler_entry_write_dispatch<20, 2, -1>; +template class handler_entry_write_dispatch<21, 2, -1>; +template class handler_entry_write_dispatch<22, 2, -1>; +template class handler_entry_write_dispatch<23, 2, -1>; +template class handler_entry_write_dispatch<24, 2, -1>; -template class handler_entry_write_dispatch<17, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<17, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<18, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<18, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<19, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<19, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<20, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<20, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<21, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<21, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<22, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<22, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<23, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<23, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<24, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<24, 3, 0, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch<17, 2, -2>; +template class handler_entry_write_dispatch<18, 2, -2>; +template class handler_entry_write_dispatch<19, 2, -2>; +template class handler_entry_write_dispatch<20, 2, -2>; +template class handler_entry_write_dispatch<21, 2, -2>; +template class handler_entry_write_dispatch<22, 2, -2>; +template class handler_entry_write_dispatch<23, 2, -2>; +template class handler_entry_write_dispatch<24, 2, -2>; -template class handler_entry_write_dispatch<17, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<17, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<18, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<18, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<19, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<19, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<20, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<20, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<21, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<21, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<22, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<22, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<23, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<23, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<24, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<24, 3, -1, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch<17, 3, 0>; +template class handler_entry_write_dispatch<18, 3, 0>; +template class handler_entry_write_dispatch<19, 3, 0>; +template class handler_entry_write_dispatch<20, 3, 0>; +template class handler_entry_write_dispatch<21, 3, 0>; +template class handler_entry_write_dispatch<22, 3, 0>; +template class handler_entry_write_dispatch<23, 3, 0>; +template class handler_entry_write_dispatch<24, 3, 0>; -template class handler_entry_write_dispatch<17, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<17, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<18, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<18, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<19, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<19, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<20, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<20, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<21, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<21, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<22, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<22, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<23, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<23, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<24, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<24, 3, -2, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch<17, 3, -1>; +template class handler_entry_write_dispatch<18, 3, -1>; +template class handler_entry_write_dispatch<19, 3, -1>; +template class handler_entry_write_dispatch<20, 3, -1>; +template class handler_entry_write_dispatch<21, 3, -1>; +template class handler_entry_write_dispatch<22, 3, -1>; +template class handler_entry_write_dispatch<23, 3, -1>; +template class handler_entry_write_dispatch<24, 3, -1>; -template class handler_entry_write_dispatch<17, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<17, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<18, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<18, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<19, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<19, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<20, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<20, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<21, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<21, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<22, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<22, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<23, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<23, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<24, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<24, 3, -3, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch<17, 3, -2>; +template class handler_entry_write_dispatch<18, 3, -2>; +template class handler_entry_write_dispatch<19, 3, -2>; +template class handler_entry_write_dispatch<20, 3, -2>; +template class handler_entry_write_dispatch<21, 3, -2>; +template class handler_entry_write_dispatch<22, 3, -2>; +template class handler_entry_write_dispatch<23, 3, -2>; +template class handler_entry_write_dispatch<24, 3, -2>; + +template class handler_entry_write_dispatch<17, 3, -3>; +template class handler_entry_write_dispatch<18, 3, -3>; +template class handler_entry_write_dispatch<19, 3, -3>; +template class handler_entry_write_dispatch<20, 3, -3>; +template class handler_entry_write_dispatch<21, 3, -3>; +template class handler_entry_write_dispatch<22, 3, -3>; +template class handler_entry_write_dispatch<23, 3, -3>; +template class handler_entry_write_dispatch<24, 3, -3>; diff --git a/src/emu/emumem_hedw3.cpp b/src/emu/emumem_hedw3.cpp index 4a423133483..b0846d8198b 100644 --- a/src/emu/emumem_hedw3.cpp +++ b/src/emu/emumem_hedw3.cpp @@ -5,206 +5,119 @@ #include "emumem_hedw.ipp" -template class handler_entry_write_dispatch<25, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<25, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<26, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<26, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<27, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<27, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<28, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<28, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<29, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<29, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<30, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<30, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<31, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<31, 0, 1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<32, 0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<32, 0, 1, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch<25, 0, 1>; +template class handler_entry_write_dispatch<26, 0, 1>; +template class handler_entry_write_dispatch<27, 0, 1>; +template class handler_entry_write_dispatch<28, 0, 1>; +template class handler_entry_write_dispatch<29, 0, 1>; +template class handler_entry_write_dispatch<30, 0, 1>; +template class handler_entry_write_dispatch<31, 0, 1>; +template class handler_entry_write_dispatch<32, 0, 1>; -template class handler_entry_write_dispatch<25, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<25, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<26, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<26, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<27, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<27, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<28, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<28, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<29, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<29, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<30, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<30, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<31, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<31, 0, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<32, 0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<32, 0, 0, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch<25, 0, 0>; +template class handler_entry_write_dispatch<26, 0, 0>; +template class handler_entry_write_dispatch<27, 0, 0>; +template class handler_entry_write_dispatch<28, 0, 0>; +template class handler_entry_write_dispatch<29, 0, 0>; +template class handler_entry_write_dispatch<30, 0, 0>; +template class handler_entry_write_dispatch<31, 0, 0>; +template class handler_entry_write_dispatch<32, 0, 0>; -template class handler_entry_write_dispatch<25, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<25, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<26, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<26, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<27, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<27, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<28, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<28, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<29, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<29, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<31, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<31, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<30, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<30, 1, 3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<32, 1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<32, 1, 3, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch<25, 1, 3>; +template class handler_entry_write_dispatch<26, 1, 3>; +template class handler_entry_write_dispatch<27, 1, 3>; +template class handler_entry_write_dispatch<28, 1, 3>; +template class handler_entry_write_dispatch<29, 1, 3>; +template class handler_entry_write_dispatch<30, 1, 3>; +template class handler_entry_write_dispatch<31, 1, 3>; +template class handler_entry_write_dispatch<32, 1, 3>; -template class handler_entry_write_dispatch<25, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<25, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<26, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<26, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<27, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<27, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<28, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<28, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<29, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<29, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<31, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<31, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<30, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<30, 1, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<32, 1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<32, 1, 0, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch<25, 1, 0>; +template class handler_entry_write_dispatch<26, 1, 0>; +template class handler_entry_write_dispatch<27, 1, 0>; +template class handler_entry_write_dispatch<28, 1, 0>; +template class handler_entry_write_dispatch<29, 1, 0>; +template class handler_entry_write_dispatch<30, 1, 0>; +template class handler_entry_write_dispatch<31, 1, 0>; +template class handler_entry_write_dispatch<32, 1, 0>; -template class handler_entry_write_dispatch<25, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<25, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<26, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<26, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<27, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<27, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<28, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<28, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<29, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<29, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<31, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<31, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<30, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<30, 1, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<32, 1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<32, 1, -1, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch<25, 1, -1>; +template class handler_entry_write_dispatch<26, 1, -1>; +template class handler_entry_write_dispatch<27, 1, -1>; +template class handler_entry_write_dispatch<28, 1, -1>; +template class handler_entry_write_dispatch<29, 1, -1>; +template class handler_entry_write_dispatch<30, 1, -1>; +template class handler_entry_write_dispatch<31, 1, -1>; +template class handler_entry_write_dispatch<32, 1, -1>; -template class handler_entry_write_dispatch<25, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<25, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<26, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<26, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<27, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<27, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<28, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<28, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<29, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<29, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<31, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<31, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<30, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<30, 2, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<32, 2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<32, 2, 0, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch<25, 2, 3>; +template class handler_entry_write_dispatch<26, 2, 3>; +template class handler_entry_write_dispatch<27, 2, 3>; +template class handler_entry_write_dispatch<28, 2, 3>; +template class handler_entry_write_dispatch<29, 2, 3>; +template class handler_entry_write_dispatch<30, 2, 3>; +template class handler_entry_write_dispatch<31, 2, 3>; +template class handler_entry_write_dispatch<32, 2, 3>; -template class handler_entry_write_dispatch<25, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<25, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<26, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<26, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<27, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<27, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<28, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<28, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<29, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<29, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<31, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<31, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<30, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<30, 2, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<32, 2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<32, 2, -1, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch<25, 2, 0>; +template class handler_entry_write_dispatch<26, 2, 0>; +template class handler_entry_write_dispatch<27, 2, 0>; +template class handler_entry_write_dispatch<28, 2, 0>; +template class handler_entry_write_dispatch<29, 2, 0>; +template class handler_entry_write_dispatch<30, 2, 0>; +template class handler_entry_write_dispatch<31, 2, 0>; +template class handler_entry_write_dispatch<32, 2, 0>; -template class handler_entry_write_dispatch<25, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<25, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<26, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<26, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<27, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<27, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<28, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<28, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<29, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<29, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<31, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<31, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<30, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<30, 2, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<32, 2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<32, 2, -2, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch<25, 2, -1>; +template class handler_entry_write_dispatch<26, 2, -1>; +template class handler_entry_write_dispatch<27, 2, -1>; +template class handler_entry_write_dispatch<28, 2, -1>; +template class handler_entry_write_dispatch<29, 2, -1>; +template class handler_entry_write_dispatch<30, 2, -1>; +template class handler_entry_write_dispatch<31, 2, -1>; +template class handler_entry_write_dispatch<32, 2, -1>; -template class handler_entry_write_dispatch<25, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<25, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<26, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<26, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<27, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<27, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<28, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<28, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<29, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<29, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<31, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<31, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<30, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<30, 3, 0, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<32, 3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<32, 3, 0, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch<25, 2, -2>; +template class handler_entry_write_dispatch<26, 2, -2>; +template class handler_entry_write_dispatch<27, 2, -2>; +template class handler_entry_write_dispatch<28, 2, -2>; +template class handler_entry_write_dispatch<29, 2, -2>; +template class handler_entry_write_dispatch<30, 2, -2>; +template class handler_entry_write_dispatch<31, 2, -2>; +template class handler_entry_write_dispatch<32, 2, -2>; -template class handler_entry_write_dispatch<25, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<25, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<26, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<26, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<27, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<27, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<28, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<28, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<29, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<29, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<31, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<31, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<30, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<30, 3, -1, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<32, 3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<32, 3, -1, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch<25, 3, 0>; +template class handler_entry_write_dispatch<26, 3, 0>; +template class handler_entry_write_dispatch<27, 3, 0>; +template class handler_entry_write_dispatch<28, 3, 0>; +template class handler_entry_write_dispatch<29, 3, 0>; +template class handler_entry_write_dispatch<30, 3, 0>; +template class handler_entry_write_dispatch<31, 3, 0>; +template class handler_entry_write_dispatch<32, 3, 0>; -template class handler_entry_write_dispatch<25, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<25, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<26, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<26, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<27, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<27, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<28, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<28, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<29, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<29, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<31, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<31, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<30, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<30, 3, -2, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<32, 3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<32, 3, -2, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch<25, 3, -1>; +template class handler_entry_write_dispatch<26, 3, -1>; +template class handler_entry_write_dispatch<27, 3, -1>; +template class handler_entry_write_dispatch<28, 3, -1>; +template class handler_entry_write_dispatch<29, 3, -1>; +template class handler_entry_write_dispatch<30, 3, -1>; +template class handler_entry_write_dispatch<31, 3, -1>; +template class handler_entry_write_dispatch<32, 3, -1>; -template class handler_entry_write_dispatch<25, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<25, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<26, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<26, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<27, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<27, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<28, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<28, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<29, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<29, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<31, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<31, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<30, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<30, 3, -3, ENDIANNESS_BIG>; -template class handler_entry_write_dispatch<32, 3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_write_dispatch<32, 3, -3, ENDIANNESS_BIG>; +template class handler_entry_write_dispatch<25, 3, -2>; +template class handler_entry_write_dispatch<26, 3, -2>; +template class handler_entry_write_dispatch<27, 3, -2>; +template class handler_entry_write_dispatch<28, 3, -2>; +template class handler_entry_write_dispatch<29, 3, -2>; +template class handler_entry_write_dispatch<30, 3, -2>; +template class handler_entry_write_dispatch<31, 3, -2>; +template class handler_entry_write_dispatch<32, 3, -2>; + +template class handler_entry_write_dispatch<25, 3, -3>; +template class handler_entry_write_dispatch<26, 3, -3>; +template class handler_entry_write_dispatch<27, 3, -3>; +template class handler_entry_write_dispatch<28, 3, -3>; +template class handler_entry_write_dispatch<29, 3, -3>; +template class handler_entry_write_dispatch<30, 3, -3>; +template class handler_entry_write_dispatch<31, 3, -3>; +template class handler_entry_write_dispatch<32, 3, -3>; diff --git a/src/emu/emumem_hem.cpp b/src/emu/emumem_hem.cpp index 1a2963951f1..e69206d7653 100644 --- a/src/emu/emumem_hem.cpp +++ b/src/emu/emumem_hem.cpp @@ -5,193 +5,227 @@ #include "emumem_hea.h" #include "emumem_hem.h" -template<int Width, int AddrShift, int Endian> typename emu::detail::handler_entry_size<Width>::uX handler_entry_read_memory<Width, AddrShift, Endian>::read(offs_t offset, uX mem_mask) +template<int Width, int AddrShift> emu::detail::handler_entry_size_t<Width> handler_entry_read_memory<Width, AddrShift>::read(offs_t offset, uX mem_mask) const { - return m_base[((offset - inh::m_address_base) & inh::m_address_mask) >> (Width + AddrShift)]; + return m_base[((offset - this->m_address_base) & this->m_address_mask) >> (Width + AddrShift)]; } -template<int Width, int AddrShift, int Endian> void *handler_entry_read_memory<Width, AddrShift, Endian>::get_ptr(offs_t offset) const +template<int Width, int AddrShift> emu::detail::handler_entry_size_t<Width> handler_entry_read_memory<Width, AddrShift>::read_interruptible(offs_t offset, uX mem_mask) const { - return m_base + (((offset - inh::m_address_base) & inh::m_address_mask) >> (Width + AddrShift)); + return m_base[((offset - this->m_address_base) & this->m_address_mask) >> (Width + AddrShift)]; } -template<int Width, int AddrShift, int Endian> std::string handler_entry_read_memory<Width, AddrShift, Endian>::name() const +template<int Width, int AddrShift> std::pair<emu::detail::handler_entry_size_t<Width>, u16> handler_entry_read_memory<Width, AddrShift>::read_flags(offs_t offset, uX mem_mask) const { - return util::string_format("memory@%x", inh::m_address_base); + return std::pair<uX, u16>(m_base[((offset - this->m_address_base) & this->m_address_mask) >> (Width + AddrShift)], this->m_flags); +} + +template<int Width, int AddrShift> u16 handler_entry_read_memory<Width, AddrShift>::lookup_flags(offs_t offset, uX mem_mask) const +{ + return this->m_flags; +} + +template<int Width, int AddrShift> void *handler_entry_read_memory<Width, AddrShift>::get_ptr(offs_t offset) const +{ + return m_base + (((offset - this->m_address_base) & this->m_address_mask) >> (Width + AddrShift)); +} + +template<int Width, int AddrShift> std::string handler_entry_read_memory<Width, AddrShift>::name() const +{ + return util::string_format("memory@%x", this->m_address_base); } -template<int Width, int AddrShift, int Endian> void handler_entry_write_memory<Width, AddrShift, Endian>::write(offs_t offset, uX data, uX mem_mask) +template<int Width, int AddrShift> void handler_entry_write_memory<Width, AddrShift>::write(offs_t offset, uX data, uX mem_mask) const { - offs_t off = ((offset - inh::m_address_base) & inh::m_address_mask) >> (Width + AddrShift); + offs_t off = ((offset - this->m_address_base) & this->m_address_mask) >> (Width + AddrShift); m_base[off] = (m_base[off] & ~mem_mask) | (data & mem_mask); } -template<> void handler_entry_write_memory<0, 0, ENDIANNESS_LITTLE>::write(offs_t offset, u8 data, u8 mem_mask) +template<int Width, int AddrShift> void handler_entry_write_memory<Width, AddrShift>::write_interruptible(offs_t offset, uX data, uX mem_mask) const +{ + offs_t off = ((offset - this->m_address_base) & this->m_address_mask) >> (Width + AddrShift); + m_base[off] = (m_base[off] & ~mem_mask) | (data & mem_mask); +} + +template<int Width, int AddrShift> u16 handler_entry_write_memory<Width, AddrShift>::write_flags(offs_t offset, uX data, uX mem_mask) const +{ + offs_t off = ((offset - this->m_address_base) & this->m_address_mask) >> (Width + AddrShift); + m_base[off] = (m_base[off] & ~mem_mask) | (data & mem_mask); + return this->m_flags; +} + +template<int Width, int AddrShift> u16 handler_entry_write_memory<Width, AddrShift>::lookup_flags(offs_t offset, uX mem_mask) const +{ + return this->m_flags; +} + +template<> void handler_entry_write_memory<0, 0>::write(offs_t offset, u8 data, u8 mem_mask) const +{ + m_base[(offset - this->m_address_base) & this->m_address_mask] = data; +} + +template<> void handler_entry_write_memory<0, 0>::write_interruptible(offs_t offset, u8 data, u8 mem_mask) const { - m_base[(offset - inh::m_address_base) & inh::m_address_mask] = data; + m_base[(offset - this->m_address_base) & this->m_address_mask] = data; } -template<> void handler_entry_write_memory<0, 0, ENDIANNESS_BIG>::write(offs_t offset, u8 data, u8 mem_mask) +template<> u16 handler_entry_write_memory<0, 0>::write_flags(offs_t offset, u8 data, u8 mem_mask) const { - m_base[(offset - inh::m_address_base) & inh::m_address_mask] = data; + m_base[(offset - this->m_address_base) & this->m_address_mask] = data; + return this->m_flags; } -template<int Width, int AddrShift, int Endian> void *handler_entry_write_memory<Width, AddrShift, Endian>::get_ptr(offs_t offset) const +template<int Width, int AddrShift> void *handler_entry_write_memory<Width, AddrShift>::get_ptr(offs_t offset) const { - return m_base + (((offset - inh::m_address_base) & inh::m_address_mask) >> (Width + AddrShift)); + return m_base + (((offset - this->m_address_base) & this->m_address_mask) >> (Width + AddrShift)); } -template<int Width, int AddrShift, int Endian> std::string handler_entry_write_memory<Width, AddrShift, Endian>::name() const +template<int Width, int AddrShift> std::string handler_entry_write_memory<Width, AddrShift>::name() const { - return util::string_format("memory@%x", inh::m_address_base); + return util::string_format("memory@%x", this->m_address_base); } -template<int Width, int AddrShift, int Endian> typename emu::detail::handler_entry_size<Width>::uX handler_entry_read_memory_bank<Width, AddrShift, Endian>::read(offs_t offset, uX mem_mask) +template<int Width, int AddrShift> emu::detail::handler_entry_size_t<Width> handler_entry_read_memory_bank<Width, AddrShift>::read(offs_t offset, uX mem_mask) const { - return static_cast<uX *>(m_bank.base())[((offset - inh::m_address_base) & inh::m_address_mask) >> (Width + AddrShift)]; + return static_cast<uX *>(m_bank.base())[((offset - this->m_address_base) & this->m_address_mask) >> (Width + AddrShift)]; } -template<int Width, int AddrShift, int Endian> void *handler_entry_read_memory_bank<Width, AddrShift, Endian>::get_ptr(offs_t offset) const +template<int Width, int AddrShift> emu::detail::handler_entry_size_t<Width> handler_entry_read_memory_bank<Width, AddrShift>::read_interruptible(offs_t offset, uX mem_mask) const { - return static_cast<uX *>(m_bank.base()) + (((offset - inh::m_address_base) & inh::m_address_mask) >> (Width + AddrShift)); + return static_cast<uX *>(m_bank.base())[((offset - this->m_address_base) & this->m_address_mask) >> (Width + AddrShift)]; } -template<int Width, int AddrShift, int Endian> std::string handler_entry_read_memory_bank<Width, AddrShift, Endian>::name() const +template<int Width, int AddrShift> std::pair<emu::detail::handler_entry_size_t<Width>, u16> handler_entry_read_memory_bank<Width, AddrShift>::read_flags(offs_t offset, uX mem_mask) const +{ + return std::pair<uX, u16>(static_cast<uX *>(m_bank.base())[((offset - this->m_address_base) & this->m_address_mask) >> (Width + AddrShift)], this->m_flags); +} + +template<int Width, int AddrShift> u16 handler_entry_read_memory_bank<Width, AddrShift>::lookup_flags(offs_t offset, uX mem_mask) const +{ + return this->m_flags; +} + +template<int Width, int AddrShift> void *handler_entry_read_memory_bank<Width, AddrShift>::get_ptr(offs_t offset) const +{ + return static_cast<uX *>(m_bank.base()) + (((offset - this->m_address_base) & this->m_address_mask) >> (Width + AddrShift)); +} + +template<int Width, int AddrShift> std::string handler_entry_read_memory_bank<Width, AddrShift>::name() const { return m_bank.name(); } -template<int Width, int AddrShift, int Endian> void handler_entry_write_memory_bank<Width, AddrShift, Endian>::write(offs_t offset, uX data, uX mem_mask) +template<int Width, int AddrShift> void handler_entry_write_memory_bank<Width, AddrShift>::write(offs_t offset, uX data, uX mem_mask) const { - offs_t off = ((offset - inh::m_address_base) & inh::m_address_mask) >> (Width + AddrShift); + offs_t off = ((offset - this->m_address_base) & this->m_address_mask) >> (Width + AddrShift); static_cast<uX *>(m_bank.base())[off] = (static_cast<uX *>(m_bank.base())[off] & ~mem_mask) | (data & mem_mask); } -template<> void handler_entry_write_memory_bank<0, 0, ENDIANNESS_LITTLE>::write(offs_t offset, u8 data, u8 mem_mask) +template<int Width, int AddrShift> void handler_entry_write_memory_bank<Width, AddrShift>::write_interruptible(offs_t offset, uX data, uX mem_mask) const +{ + offs_t off = ((offset - this->m_address_base) & this->m_address_mask) >> (Width + AddrShift); + static_cast<uX *>(m_bank.base())[off] = (static_cast<uX *>(m_bank.base())[off] & ~mem_mask) | (data & mem_mask); +} + +template<int Width, int AddrShift> u16 handler_entry_write_memory_bank<Width, AddrShift>::write_flags(offs_t offset, uX data, uX mem_mask) const +{ + offs_t off = ((offset - this->m_address_base) & this->m_address_mask) >> (Width + AddrShift); + static_cast<uX *>(m_bank.base())[off] = (static_cast<uX *>(m_bank.base())[off] & ~mem_mask) | (data & mem_mask); + return this->m_flags; +} + +template<int Width, int AddrShift> u16 handler_entry_write_memory_bank<Width, AddrShift>::lookup_flags(offs_t offset, uX mem_mask) const +{ + return this->m_flags; +} + +template<> void handler_entry_write_memory_bank<0, 0>::write(offs_t offset, u8 data, u8 mem_mask) const +{ + static_cast<uX *>(m_bank.base())[(offset - this->m_address_base) & this->m_address_mask] = data; +} + +template<> void handler_entry_write_memory_bank<0, 0>::write_interruptible(offs_t offset, u8 data, u8 mem_mask) const { - static_cast<uX *>(m_bank.base())[(offset - inh::m_address_base) & inh::m_address_mask] = data; + static_cast<uX *>(m_bank.base())[(offset - this->m_address_base) & this->m_address_mask] = data; } -template<> void handler_entry_write_memory_bank<0, 0, ENDIANNESS_BIG>::write(offs_t offset, u8 data, u8 mem_mask) +template<> u16 handler_entry_write_memory_bank<0, 0>::write_flags(offs_t offset, u8 data, u8 mem_mask) const { - static_cast<uX *>(m_bank.base())[(offset - inh::m_address_base) & inh::m_address_mask] = data; + static_cast<uX *>(m_bank.base())[(offset - this->m_address_base) & this->m_address_mask] = data; + return this->m_flags; } -template<int Width, int AddrShift, int Endian> void *handler_entry_write_memory_bank<Width, AddrShift, Endian>::get_ptr(offs_t offset) const +template<int Width, int AddrShift> void *handler_entry_write_memory_bank<Width, AddrShift>::get_ptr(offs_t offset) const { - return static_cast<uX *>(m_bank.base()) + (((offset - inh::m_address_base) & inh::m_address_mask) >> (Width + AddrShift)); + return static_cast<uX *>(m_bank.base()) + (((offset - this->m_address_base) & this->m_address_mask) >> (Width + AddrShift)); } -template<int Width, int AddrShift, int Endian> std::string handler_entry_write_memory_bank<Width, AddrShift, Endian>::name() const +template<int Width, int AddrShift> std::string handler_entry_write_memory_bank<Width, AddrShift>::name() const { return m_bank.name(); } -template class handler_entry_read_memory<0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_read_memory<0, 1, ENDIANNESS_BIG>; -template class handler_entry_read_memory<0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_memory<0, 0, ENDIANNESS_BIG>; -template class handler_entry_read_memory<1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_read_memory<1, 3, ENDIANNESS_BIG>; -template class handler_entry_read_memory<1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_memory<1, 0, ENDIANNESS_BIG>; -template class handler_entry_read_memory<1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_memory<1, -1, ENDIANNESS_BIG>; -template class handler_entry_read_memory<2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_memory<2, 0, ENDIANNESS_BIG>; -template class handler_entry_read_memory<2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_memory<2, -1, ENDIANNESS_BIG>; -template class handler_entry_read_memory<2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_memory<2, -2, ENDIANNESS_BIG>; -template class handler_entry_read_memory<3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_memory<3, 0, ENDIANNESS_BIG>; -template class handler_entry_read_memory<3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_memory<3, -1, ENDIANNESS_BIG>; -template class handler_entry_read_memory<3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_memory<3, -2, ENDIANNESS_BIG>; -template class handler_entry_read_memory<3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_read_memory<3, -3, ENDIANNESS_BIG>; - -template class handler_entry_write_memory<0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_write_memory<0, 1, ENDIANNESS_BIG>; -template class handler_entry_write_memory<0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_memory<0, 0, ENDIANNESS_BIG>; -template class handler_entry_write_memory<1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_write_memory<1, 3, ENDIANNESS_BIG>; -template class handler_entry_write_memory<1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_memory<1, 0, ENDIANNESS_BIG>; -template class handler_entry_write_memory<1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_memory<1, -1, ENDIANNESS_BIG>; -template class handler_entry_write_memory<2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_memory<2, 0, ENDIANNESS_BIG>; -template class handler_entry_write_memory<2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_memory<2, -1, ENDIANNESS_BIG>; -template class handler_entry_write_memory<2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_memory<2, -2, ENDIANNESS_BIG>; -template class handler_entry_write_memory<3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_memory<3, 0, ENDIANNESS_BIG>; -template class handler_entry_write_memory<3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_memory<3, -1, ENDIANNESS_BIG>; -template class handler_entry_write_memory<3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_memory<3, -2, ENDIANNESS_BIG>; -template class handler_entry_write_memory<3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_write_memory<3, -3, ENDIANNESS_BIG>; - - -template class handler_entry_read_memory_bank<0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_read_memory_bank<0, 1, ENDIANNESS_BIG>; -template class handler_entry_read_memory_bank<0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_memory_bank<0, 0, ENDIANNESS_BIG>; -template class handler_entry_read_memory_bank<1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_read_memory_bank<1, 3, ENDIANNESS_BIG>; -template class handler_entry_read_memory_bank<1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_memory_bank<1, 0, ENDIANNESS_BIG>; -template class handler_entry_read_memory_bank<1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_memory_bank<1, -1, ENDIANNESS_BIG>; -template class handler_entry_read_memory_bank<2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_memory_bank<2, 0, ENDIANNESS_BIG>; -template class handler_entry_read_memory_bank<2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_memory_bank<2, -1, ENDIANNESS_BIG>; -template class handler_entry_read_memory_bank<2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_memory_bank<2, -2, ENDIANNESS_BIG>; -template class handler_entry_read_memory_bank<3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_memory_bank<3, 0, ENDIANNESS_BIG>; -template class handler_entry_read_memory_bank<3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_memory_bank<3, -1, ENDIANNESS_BIG>; -template class handler_entry_read_memory_bank<3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_memory_bank<3, -2, ENDIANNESS_BIG>; -template class handler_entry_read_memory_bank<3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_read_memory_bank<3, -3, ENDIANNESS_BIG>; - -template class handler_entry_write_memory_bank<0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_write_memory_bank<0, 1, ENDIANNESS_BIG>; -template class handler_entry_write_memory_bank<0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_memory_bank<0, 0, ENDIANNESS_BIG>; -template class handler_entry_write_memory_bank<1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_write_memory_bank<1, 3, ENDIANNESS_BIG>; -template class handler_entry_write_memory_bank<1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_memory_bank<1, 0, ENDIANNESS_BIG>; -template class handler_entry_write_memory_bank<1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_memory_bank<1, -1, ENDIANNESS_BIG>; -template class handler_entry_write_memory_bank<2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_memory_bank<2, 0, ENDIANNESS_BIG>; -template class handler_entry_write_memory_bank<2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_memory_bank<2, -1, ENDIANNESS_BIG>; -template class handler_entry_write_memory_bank<2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_memory_bank<2, -2, ENDIANNESS_BIG>; -template class handler_entry_write_memory_bank<3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_memory_bank<3, 0, ENDIANNESS_BIG>; -template class handler_entry_write_memory_bank<3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_memory_bank<3, -1, ENDIANNESS_BIG>; -template class handler_entry_write_memory_bank<3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_memory_bank<3, -2, ENDIANNESS_BIG>; -template class handler_entry_write_memory_bank<3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_write_memory_bank<3, -3, ENDIANNESS_BIG>; +template class handler_entry_read_memory<0, 1>; +template class handler_entry_read_memory<0, 0>; +template class handler_entry_read_memory<1, 3>; +template class handler_entry_read_memory<1, 0>; +template class handler_entry_read_memory<1, -1>; +template class handler_entry_read_memory<2, 3>; +template class handler_entry_read_memory<2, 0>; +template class handler_entry_read_memory<2, -1>; +template class handler_entry_read_memory<2, -2>; +template class handler_entry_read_memory<3, 0>; +template class handler_entry_read_memory<3, -1>; +template class handler_entry_read_memory<3, -2>; +template class handler_entry_read_memory<3, -3>; + +template class handler_entry_write_memory<0, 1>; +template class handler_entry_write_memory<0, 0>; +template class handler_entry_write_memory<1, 3>; +template class handler_entry_write_memory<1, 0>; +template class handler_entry_write_memory<1, -1>; +template class handler_entry_write_memory<2, 3>; +template class handler_entry_write_memory<2, 0>; +template class handler_entry_write_memory<2, -1>; +template class handler_entry_write_memory<2, -2>; +template class handler_entry_write_memory<3, 0>; +template class handler_entry_write_memory<3, -1>; +template class handler_entry_write_memory<3, -2>; +template class handler_entry_write_memory<3, -3>; + + +template class handler_entry_read_memory_bank<0, 1>; +template class handler_entry_read_memory_bank<0, 0>; +template class handler_entry_read_memory_bank<1, 3>; +template class handler_entry_read_memory_bank<1, 0>; +template class handler_entry_read_memory_bank<1, -1>; +template class handler_entry_read_memory_bank<2, 3>; +template class handler_entry_read_memory_bank<2, 0>; +template class handler_entry_read_memory_bank<2, -1>; +template class handler_entry_read_memory_bank<2, -2>; +template class handler_entry_read_memory_bank<3, 0>; +template class handler_entry_read_memory_bank<3, -1>; +template class handler_entry_read_memory_bank<3, -2>; +template class handler_entry_read_memory_bank<3, -3>; + +template class handler_entry_write_memory_bank<0, 1>; +template class handler_entry_write_memory_bank<0, 0>; +template class handler_entry_write_memory_bank<1, 3>; +template class handler_entry_write_memory_bank<1, 0>; +template class handler_entry_write_memory_bank<1, -1>; +template class handler_entry_write_memory_bank<2, 3>; +template class handler_entry_write_memory_bank<2, 0>; +template class handler_entry_write_memory_bank<2, -1>; +template class handler_entry_write_memory_bank<2, -2>; +template class handler_entry_write_memory_bank<3, 0>; +template class handler_entry_write_memory_bank<3, -1>; +template class handler_entry_write_memory_bank<3, -2>; +template class handler_entry_write_memory_bank<3, -3>; diff --git a/src/emu/emumem_hem.h b/src/emu/emumem_hem.h index 184c752625c..d4c8c145860 100644 --- a/src/emu/emumem_hem.h +++ b/src/emu/emumem_hem.h @@ -1,44 +1,48 @@ // license:BSD-3-Clause // copyright-holders:Olivier Galibert +#ifndef MAME_EMU_EMUMEM_HEM_H +#define MAME_EMU_EMUMEM_HEM_H + +#pragma once // handler_entry_read_memory/handler_entry_write_memory // Accesses fixed memory (non-banked rom or ram) -template<int Width, int AddrShift, int Endian> class handler_entry_read_memory : public handler_entry_read_address<Width, AddrShift, Endian> +template<int Width, int AddrShift> class handler_entry_read_memory : public handler_entry_read_address<Width, AddrShift> { public: - using uX = typename emu::detail::handler_entry_size<Width>::uX; - using inh = handler_entry_read_address<Width, AddrShift, Endian>; + using uX = emu::detail::handler_entry_size_t<Width>; - handler_entry_read_memory(address_space *space) : handler_entry_read_address<Width, AddrShift, Endian>(space, 0) {} + handler_entry_read_memory(address_space *space, u16 flags, void *base) : handler_entry_read_address<Width, AddrShift>(space, flags), m_base(reinterpret_cast<uX *>(base)) {} ~handler_entry_read_memory() = default; - uX read(offs_t offset, uX mem_mask) override; + uX read(offs_t offset, uX mem_mask) const override; + uX read_interruptible(offs_t offset, uX mem_mask) const override; + std::pair<uX, u16> read_flags(offs_t offset, uX mem_mask) const override; + u16 lookup_flags(offs_t offset, uX mem_mask) const override; void *get_ptr(offs_t offset) const override; - inline void set_base(uX *base) { m_base = base; } - std::string name() const override; private: uX *m_base; }; -template<int Width, int AddrShift, int Endian> class handler_entry_write_memory : public handler_entry_write_address<Width, AddrShift, Endian> +template<int Width, int AddrShift> class handler_entry_write_memory : public handler_entry_write_address<Width, AddrShift> { public: - using uX = typename emu::detail::handler_entry_size<Width>::uX; - using inh = handler_entry_write_address<Width, AddrShift, Endian>; + using uX = emu::detail::handler_entry_size_t<Width>; - handler_entry_write_memory(address_space *space) : handler_entry_write_address<Width, AddrShift, Endian>(space, 0) {} + handler_entry_write_memory(address_space *space, u16 flags, void *base) : handler_entry_write_address<Width, AddrShift>(space, flags), m_base(reinterpret_cast<uX *>(base)) {} ~handler_entry_write_memory() = default; - void write(offs_t offset, uX data, uX mem_mask) override; + void write(offs_t offset, uX data, uX mem_mask) const override; + void write_interruptible(offs_t offset, uX data, uX mem_mask) const override; + u16 write_flags(offs_t offset, uX data, uX mem_mask) const override; + u16 lookup_flags(offs_t offset, uX mem_mask) const override; void *get_ptr(offs_t offset) const override; - inline void set_base(uX *base) { m_base = base; } - std::string name() const override; private: @@ -50,16 +54,18 @@ private: // Accesses banked memory, associated to a memory_bank -template<int Width, int AddrShift, int Endian> class handler_entry_read_memory_bank : public handler_entry_read_address<Width, AddrShift, Endian> +template<int Width, int AddrShift> class handler_entry_read_memory_bank : public handler_entry_read_address<Width, AddrShift> { public: - using uX = typename emu::detail::handler_entry_size<Width>::uX; - using inh = handler_entry_read_address<Width, AddrShift, Endian>; + using uX = emu::detail::handler_entry_size_t<Width>; - handler_entry_read_memory_bank(address_space *space, memory_bank &bank) : handler_entry_read_address<Width, AddrShift, Endian>(space, 0), m_bank(bank) {} + handler_entry_read_memory_bank(address_space *space, u16 flags, memory_bank &bank) : handler_entry_read_address<Width, AddrShift>(space, flags), m_bank(bank) {} ~handler_entry_read_memory_bank() = default; - uX read(offs_t offset, uX mem_mask) override; + uX read(offs_t offset, uX mem_mask) const override; + uX read_interruptible(offs_t offset, uX mem_mask) const override; + std::pair<uX, u16> read_flags(offs_t offset, uX mem_mask) const override; + u16 lookup_flags(offs_t offset, uX mem_mask) const override; void *get_ptr(offs_t offset) const override; std::string name() const override; @@ -68,16 +74,18 @@ private: memory_bank &m_bank; }; -template<int Width, int AddrShift, int Endian> class handler_entry_write_memory_bank : public handler_entry_write_address<Width, AddrShift, Endian> +template<int Width, int AddrShift> class handler_entry_write_memory_bank : public handler_entry_write_address<Width, AddrShift> { public: - using uX = typename emu::detail::handler_entry_size<Width>::uX; - using inh = handler_entry_write_address<Width, AddrShift, Endian>; + using uX = emu::detail::handler_entry_size_t<Width>; - handler_entry_write_memory_bank(address_space *space, memory_bank &bank) : handler_entry_write_address<Width, AddrShift, Endian>(space, 0), m_bank(bank) {} + handler_entry_write_memory_bank(address_space *space, u16 flags, memory_bank &bank) : handler_entry_write_address<Width, AddrShift>(space, flags), m_bank(bank) {} ~handler_entry_write_memory_bank() = default; - void write(offs_t offset, uX data, uX mem_mask) override; + void write(offs_t offset, uX data, uX mem_mask) const override; + void write_interruptible(offs_t offset, uX data, uX mem_mask) const override; + u16 write_flags(offs_t offset, uX data, uX mem_mask) const override; + u16 lookup_flags(offs_t offset, uX mem_mask) const override; void *get_ptr(offs_t offset) const override; std::string name() const override; @@ -85,3 +93,5 @@ public: private: memory_bank &m_bank; }; + +#endif // MAME_EMU_EMUMEM_HEM_H diff --git a/src/emu/emumem_hep.cpp b/src/emu/emumem_hep.cpp index f411dcb522c..203ff5d5c9e 100644 --- a/src/emu/emumem_hep.cpp +++ b/src/emu/emumem_hep.cpp @@ -4,7 +4,7 @@ #include "emu.h" #include "emumem_hep.h" -template<int Width, int AddrShift, int Endian> handler_entry_read_passthrough<Width, AddrShift, Endian>::~handler_entry_read_passthrough() +template<int Width, int AddrShift> handler_entry_read_passthrough<Width, AddrShift>::~handler_entry_read_passthrough() { if(m_next) { m_mph.remove_handler(this); @@ -12,11 +12,11 @@ template<int Width, int AddrShift, int Endian> handler_entry_read_passthrough<Wi } } -template<int Width, int AddrShift, int Endian> void handler_entry_read_passthrough<Width, AddrShift, Endian>::detach(const std::unordered_set<handler_entry *> &handlers) +template<int Width, int AddrShift> void handler_entry_read_passthrough<Width, AddrShift>::detach(const std::unordered_set<handler_entry *> &handlers) { if(!m_next->is_passthrough()) return; - auto np = static_cast<handler_entry_read_passthrough<Width, AddrShift, Endian> *>(m_next); + auto np = static_cast<handler_entry_read_passthrough<Width, AddrShift> *>(m_next); if(handlers.find(np) != handlers.end()) { m_next = np->get_subhandler(); @@ -27,7 +27,7 @@ template<int Width, int AddrShift, int Endian> void handler_entry_read_passthrou np->detach(handlers); } -template<int Width, int AddrShift, int Endian> handler_entry_write_passthrough<Width, AddrShift, Endian>::~handler_entry_write_passthrough() +template<int Width, int AddrShift> handler_entry_write_passthrough<Width, AddrShift>::~handler_entry_write_passthrough() { if(m_next) { m_mph.remove_handler(this); @@ -35,11 +35,11 @@ template<int Width, int AddrShift, int Endian> handler_entry_write_passthrough<W } } -template<int Width, int AddrShift, int Endian> void handler_entry_write_passthrough<Width, AddrShift, Endian>::detach(const std::unordered_set<handler_entry *> &handlers) +template<int Width, int AddrShift> void handler_entry_write_passthrough<Width, AddrShift>::detach(const std::unordered_set<handler_entry *> &handlers) { if(!m_next->is_passthrough()) return; - auto np = static_cast<handler_entry_write_passthrough<Width, AddrShift, Endian> *>(m_next); + auto np = static_cast<handler_entry_write_passthrough<Width, AddrShift> *>(m_next); if(handlers.find(np) != handlers.end()) { m_next = np->get_subhandler(); @@ -50,52 +50,30 @@ template<int Width, int AddrShift, int Endian> void handler_entry_write_passthro np->detach(handlers); } -template class handler_entry_read_passthrough<0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_read_passthrough<0, 1, ENDIANNESS_BIG>; -template class handler_entry_read_passthrough<0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_passthrough<0, 0, ENDIANNESS_BIG>; -template class handler_entry_read_passthrough<1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_read_passthrough<1, 3, ENDIANNESS_BIG>; -template class handler_entry_read_passthrough<1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_passthrough<1, 0, ENDIANNESS_BIG>; -template class handler_entry_read_passthrough<1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_passthrough<1, -1, ENDIANNESS_BIG>; -template class handler_entry_read_passthrough<2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_passthrough<2, 0, ENDIANNESS_BIG>; -template class handler_entry_read_passthrough<2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_passthrough<2, -1, ENDIANNESS_BIG>; -template class handler_entry_read_passthrough<2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_passthrough<2, -2, ENDIANNESS_BIG>; -template class handler_entry_read_passthrough<3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_passthrough<3, 0, ENDIANNESS_BIG>; -template class handler_entry_read_passthrough<3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_passthrough<3, -1, ENDIANNESS_BIG>; -template class handler_entry_read_passthrough<3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_passthrough<3, -2, ENDIANNESS_BIG>; -template class handler_entry_read_passthrough<3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_read_passthrough<3, -3, ENDIANNESS_BIG>; +template class handler_entry_read_passthrough<0, 1>; +template class handler_entry_read_passthrough<0, 0>; +template class handler_entry_read_passthrough<1, 3>; +template class handler_entry_read_passthrough<1, 0>; +template class handler_entry_read_passthrough<1, -1>; +template class handler_entry_read_passthrough<2, 3>; +template class handler_entry_read_passthrough<2, 0>; +template class handler_entry_read_passthrough<2, -1>; +template class handler_entry_read_passthrough<2, -2>; +template class handler_entry_read_passthrough<3, 0>; +template class handler_entry_read_passthrough<3, -1>; +template class handler_entry_read_passthrough<3, -2>; +template class handler_entry_read_passthrough<3, -3>; -template class handler_entry_write_passthrough<0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_write_passthrough<0, 1, ENDIANNESS_BIG>; -template class handler_entry_write_passthrough<0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_passthrough<0, 0, ENDIANNESS_BIG>; -template class handler_entry_write_passthrough<1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_write_passthrough<1, 3, ENDIANNESS_BIG>; -template class handler_entry_write_passthrough<1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_passthrough<1, 0, ENDIANNESS_BIG>; -template class handler_entry_write_passthrough<1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_passthrough<1, -1, ENDIANNESS_BIG>; -template class handler_entry_write_passthrough<2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_passthrough<2, 0, ENDIANNESS_BIG>; -template class handler_entry_write_passthrough<2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_passthrough<2, -1, ENDIANNESS_BIG>; -template class handler_entry_write_passthrough<2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_passthrough<2, -2, ENDIANNESS_BIG>; -template class handler_entry_write_passthrough<3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_passthrough<3, 0, ENDIANNESS_BIG>; -template class handler_entry_write_passthrough<3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_passthrough<3, -1, ENDIANNESS_BIG>; -template class handler_entry_write_passthrough<3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_passthrough<3, -2, ENDIANNESS_BIG>; -template class handler_entry_write_passthrough<3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_write_passthrough<3, -3, ENDIANNESS_BIG>; +template class handler_entry_write_passthrough<0, 1>; +template class handler_entry_write_passthrough<0, 0>; +template class handler_entry_write_passthrough<1, 3>; +template class handler_entry_write_passthrough<1, 0>; +template class handler_entry_write_passthrough<1, -1>; +template class handler_entry_write_passthrough<2, 3>; +template class handler_entry_write_passthrough<2, 0>; +template class handler_entry_write_passthrough<2, -1>; +template class handler_entry_write_passthrough<2, -2>; +template class handler_entry_write_passthrough<3, 0>; +template class handler_entry_write_passthrough<3, -1>; +template class handler_entry_write_passthrough<3, -2>; +template class handler_entry_write_passthrough<3, -3>; diff --git a/src/emu/emumem_hep.h b/src/emu/emumem_hep.h index 0b166d77698..bc8616cea2b 100644 --- a/src/emu/emumem_hep.h +++ b/src/emu/emumem_hep.h @@ -5,44 +5,44 @@ // parent class for handlers which want to tap the access and usually pass it on to another handler -template<int Width, int AddrShift, int Endian> class handler_entry_read_passthrough : public handler_entry_read<Width, AddrShift, Endian> +template<int Width, int AddrShift> class handler_entry_read_passthrough : public handler_entry_read<Width, AddrShift> { public: - using uX = typename emu::detail::handler_entry_size<Width>::uX; + using uX = emu::detail::handler_entry_size_t<Width>; - handler_entry_read_passthrough(address_space *space, memory_passthrough_handler &mph) : handler_entry_read<Width, AddrShift, Endian>(space, handler_entry::F_PASSTHROUGH), m_mph(mph), m_next(nullptr) {} + handler_entry_read_passthrough(address_space *space, emu::detail::memory_passthrough_handler_impl &mph, u32 prio) : handler_entry_read<Width, AddrShift>(space, handler_entry::f_pt(prio)), m_mph(mph), m_next(nullptr) {} ~handler_entry_read_passthrough(); - virtual handler_entry_read_passthrough<Width, AddrShift, Endian> *instantiate(handler_entry_read<Width, AddrShift, Endian> *next) const = 0; + virtual handler_entry_read_passthrough<Width, AddrShift> *instantiate(handler_entry_read<Width, AddrShift> *next) const = 0; - handler_entry_read<Width, AddrShift, Endian> *get_subhandler() const { return m_next; } + handler_entry_read<Width, AddrShift> *get_subhandler() const { return m_next; } void detach(const std::unordered_set<handler_entry *> &handlers) override; protected: - memory_passthrough_handler &m_mph; - handler_entry_read<Width, AddrShift, Endian> *m_next; + emu::detail::memory_passthrough_handler_impl &m_mph; + handler_entry_read<Width, AddrShift> *m_next; - handler_entry_read_passthrough(address_space *space, memory_passthrough_handler &mph, handler_entry_read<Width, AddrShift, Endian> *next) : handler_entry_read<Width, AddrShift, Endian>(space, handler_entry::F_PASSTHROUGH), m_mph(mph), m_next(next) { next->ref(); mph.add_handler(this); } + handler_entry_read_passthrough(address_space *space, emu::detail::memory_passthrough_handler_impl &mph, u32 prio, handler_entry_read<Width, AddrShift> *next) : handler_entry_read<Width, AddrShift>(space, handler_entry::f_pt(prio)), m_mph(mph), m_next(next) { next->ref(); mph.add_handler(this); } }; -template<int Width, int AddrShift, int Endian> class handler_entry_write_passthrough : public handler_entry_write<Width, AddrShift, Endian> +template<int Width, int AddrShift> class handler_entry_write_passthrough : public handler_entry_write<Width, AddrShift> { public: - using uX = typename emu::detail::handler_entry_size<Width>::uX; + using uX = emu::detail::handler_entry_size_t<Width>; - handler_entry_write_passthrough(address_space *space, memory_passthrough_handler &mph) : handler_entry_write<Width, AddrShift, Endian>(space, handler_entry::F_PASSTHROUGH), m_mph(mph), m_next(nullptr) {} + handler_entry_write_passthrough(address_space *space, emu::detail::memory_passthrough_handler_impl &mph, u32 prio) : handler_entry_write<Width, AddrShift>(space, handler_entry::f_pt(prio)), m_mph(mph), m_next(nullptr) {} ~handler_entry_write_passthrough(); - virtual handler_entry_write_passthrough<Width, AddrShift, Endian> *instantiate(handler_entry_write<Width, AddrShift, Endian> *next) const = 0; + virtual handler_entry_write_passthrough<Width, AddrShift> *instantiate(handler_entry_write<Width, AddrShift> *next) const = 0; - handler_entry_write<Width, AddrShift, Endian> *get_subhandler() const { return m_next; } + handler_entry_write<Width, AddrShift> *get_subhandler() const { return m_next; } void detach(const std::unordered_set<handler_entry *> &handlers) override; protected: - memory_passthrough_handler &m_mph; - handler_entry_write<Width, AddrShift, Endian> *m_next; + emu::detail::memory_passthrough_handler_impl &m_mph; + handler_entry_write<Width, AddrShift> *m_next; - handler_entry_write_passthrough(address_space *space, memory_passthrough_handler &mph, handler_entry_write<Width, AddrShift, Endian> *next) : handler_entry_write<Width, AddrShift, Endian>(space, handler_entry::F_PASSTHROUGH), m_mph(mph), m_next(next) { next->ref(); mph.add_handler(this); } + handler_entry_write_passthrough(address_space *space, emu::detail::memory_passthrough_handler_impl &mph, u32 prio, handler_entry_write<Width, AddrShift> *next) : handler_entry_write<Width, AddrShift>(space, handler_entry::f_pt(prio)), m_mph(mph), m_next(next) { next->ref(); mph.add_handler(this); } }; diff --git a/src/emu/emumem_het.cpp b/src/emu/emumem_het.cpp index 87f3d35dc10..23349659a48 100644 --- a/src/emu/emumem_het.cpp +++ b/src/emu/emumem_het.cpp @@ -5,89 +5,128 @@ #include "emumem_hep.h" #include "emumem_het.h" -template<int Width, int AddrShift, int Endian> typename emu::detail::handler_entry_size<Width>::uX handler_entry_read_tap<Width, AddrShift, Endian>::read(offs_t offset, uX mem_mask) +template<int Width, int AddrShift> emu::detail::handler_entry_size_t<Width> handler_entry_read_tap<Width, AddrShift>::read(offs_t offset, uX mem_mask) const { - uX data = inh::m_next->read(offset, mem_mask); + this->ref(); + + uX data = this->m_next->read(offset, mem_mask); m_tap(offset, data, mem_mask); + + this->unref(); + return data; +} + +template<int Width, int AddrShift> emu::detail::handler_entry_size_t<Width> handler_entry_read_tap<Width, AddrShift>::read_interruptible(offs_t offset, uX mem_mask) const +{ + this->ref(); + + uX data = this->m_next->read_interruptible(offset, mem_mask); + m_tap(offset, data, mem_mask); + + this->unref(); return data; } -template<int Width, int AddrShift, int Endian> std::string handler_entry_read_tap<Width, AddrShift, Endian>::name() const +template<int Width, int AddrShift> std::pair<emu::detail::handler_entry_size_t<Width>, u16> handler_entry_read_tap<Width, AddrShift>::read_flags(offs_t offset, uX mem_mask) const { - return '(' + m_name + ") " + inh::m_next->name(); + this->ref(); + + auto pack = this->m_next->read_flags(offset, mem_mask); + m_tap(offset, pack.first, mem_mask); + + this->unref(); + return pack; } -template<int Width, int AddrShift, int Endian> handler_entry_read_tap<Width, AddrShift, Endian> *handler_entry_read_tap<Width, AddrShift, Endian>::instantiate(handler_entry_read<Width, AddrShift, Endian> *next) const +template<int Width, int AddrShift> u16 handler_entry_read_tap<Width, AddrShift>::lookup_flags(offs_t offset, uX mem_mask) const { - return new handler_entry_read_tap<Width, AddrShift, Endian>(inh::m_space, inh::m_mph, next, m_name, m_tap); + return this->m_next->lookup_flags(offset, mem_mask); } +template<int Width, int AddrShift> std::string handler_entry_read_tap<Width, AddrShift>::name() const +{ + return '(' + m_name + ") " + this->m_next->name(); +} -template<int Width, int AddrShift, int Endian> void handler_entry_write_tap<Width, AddrShift, Endian>::write(offs_t offset, uX data, uX mem_mask) +template<int Width, int AddrShift> handler_entry_read_tap<Width, AddrShift> *handler_entry_read_tap<Width, AddrShift>::instantiate(handler_entry_read<Width, AddrShift> *next) const { + return new handler_entry_read_tap<Width, AddrShift>(this->m_space, this->m_mph, next, m_name, m_tap); +} + + +template<int Width, int AddrShift> void handler_entry_write_tap<Width, AddrShift>::write(offs_t offset, uX data, uX mem_mask) const +{ + this->ref(); + m_tap(offset, data, mem_mask); - inh::m_next->write(offset, data, mem_mask); + this->m_next->write(offset, data, mem_mask); + + this->unref(); } -template<int Width, int AddrShift, int Endian> std::string handler_entry_write_tap<Width, AddrShift, Endian>::name() const +template<int Width, int AddrShift> void handler_entry_write_tap<Width, AddrShift>::write_interruptible(offs_t offset, uX data, uX mem_mask) const { - return '(' + m_name + ") " + inh::m_next->name(); + this->ref(); + + m_tap(offset, data, mem_mask); + this->m_next->write_interruptible(offset, data, mem_mask); + + this->unref(); } +template<int Width, int AddrShift> u16 handler_entry_write_tap<Width, AddrShift>::write_flags(offs_t offset, uX data, uX mem_mask) const +{ + this->ref(); + + m_tap(offset, data, mem_mask); + u16 flags = this->m_next->write_flags(offset, data, mem_mask); + + this->unref(); + return flags; +} -template<int Width, int AddrShift, int Endian> handler_entry_write_tap<Width, AddrShift, Endian> *handler_entry_write_tap<Width, AddrShift, Endian>::instantiate(handler_entry_write<Width, AddrShift, Endian> *next) const +template<int Width, int AddrShift> u16 handler_entry_write_tap<Width, AddrShift>::lookup_flags(offs_t offset, uX mem_mask) const { - return new handler_entry_write_tap<Width, AddrShift, Endian>(inh::m_space, inh::m_mph, next, m_name, m_tap); + return this->m_next->lookup_flags(offset, mem_mask); } +template<int Width, int AddrShift> std::string handler_entry_write_tap<Width, AddrShift>::name() const +{ + return '(' + m_name + ") " + this->m_next->name(); +} + + +template<int Width, int AddrShift> handler_entry_write_tap<Width, AddrShift> *handler_entry_write_tap<Width, AddrShift>::instantiate(handler_entry_write<Width, AddrShift> *next) const +{ + return new handler_entry_write_tap<Width, AddrShift>(this->m_space, this->m_mph, next, m_name, m_tap); +} + + +template class handler_entry_read_tap<0, 1>; +template class handler_entry_read_tap<0, 0>; +template class handler_entry_read_tap<1, 3>; +template class handler_entry_read_tap<1, 0>; +template class handler_entry_read_tap<1, -1>; +template class handler_entry_read_tap<2, 3>; +template class handler_entry_read_tap<2, 0>; +template class handler_entry_read_tap<2, -1>; +template class handler_entry_read_tap<2, -2>; +template class handler_entry_read_tap<3, 0>; +template class handler_entry_read_tap<3, -1>; +template class handler_entry_read_tap<3, -2>; +template class handler_entry_read_tap<3, -3>; -template class handler_entry_read_tap<0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_read_tap<0, 1, ENDIANNESS_BIG>; -template class handler_entry_read_tap<0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_tap<0, 0, ENDIANNESS_BIG>; -template class handler_entry_read_tap<1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_read_tap<1, 3, ENDIANNESS_BIG>; -template class handler_entry_read_tap<1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_tap<1, 0, ENDIANNESS_BIG>; -template class handler_entry_read_tap<1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_tap<1, -1, ENDIANNESS_BIG>; -template class handler_entry_read_tap<2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_tap<2, 0, ENDIANNESS_BIG>; -template class handler_entry_read_tap<2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_tap<2, -1, ENDIANNESS_BIG>; -template class handler_entry_read_tap<2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_tap<2, -2, ENDIANNESS_BIG>; -template class handler_entry_read_tap<3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_tap<3, 0, ENDIANNESS_BIG>; -template class handler_entry_read_tap<3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_tap<3, -1, ENDIANNESS_BIG>; -template class handler_entry_read_tap<3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_tap<3, -2, ENDIANNESS_BIG>; -template class handler_entry_read_tap<3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_read_tap<3, -3, ENDIANNESS_BIG>; - -template class handler_entry_write_tap<0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_write_tap<0, 1, ENDIANNESS_BIG>; -template class handler_entry_write_tap<0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_tap<0, 0, ENDIANNESS_BIG>; -template class handler_entry_write_tap<1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_write_tap<1, 3, ENDIANNESS_BIG>; -template class handler_entry_write_tap<1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_tap<1, 0, ENDIANNESS_BIG>; -template class handler_entry_write_tap<1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_tap<1, -1, ENDIANNESS_BIG>; -template class handler_entry_write_tap<2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_tap<2, 0, ENDIANNESS_BIG>; -template class handler_entry_write_tap<2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_tap<2, -1, ENDIANNESS_BIG>; -template class handler_entry_write_tap<2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_tap<2, -2, ENDIANNESS_BIG>; -template class handler_entry_write_tap<3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_tap<3, 0, ENDIANNESS_BIG>; -template class handler_entry_write_tap<3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_tap<3, -1, ENDIANNESS_BIG>; -template class handler_entry_write_tap<3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_tap<3, -2, ENDIANNESS_BIG>; -template class handler_entry_write_tap<3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_write_tap<3, -3, ENDIANNESS_BIG>; +template class handler_entry_write_tap<0, 1>; +template class handler_entry_write_tap<0, 0>; +template class handler_entry_write_tap<1, 3>; +template class handler_entry_write_tap<1, 0>; +template class handler_entry_write_tap<1, -1>; +template class handler_entry_write_tap<2, 3>; +template class handler_entry_write_tap<2, 0>; +template class handler_entry_write_tap<2, -1>; +template class handler_entry_write_tap<2, -2>; +template class handler_entry_write_tap<3, 0>; +template class handler_entry_write_tap<3, -1>; +template class handler_entry_write_tap<3, -2>; +template class handler_entry_write_tap<3, -3>; diff --git a/src/emu/emumem_het.h b/src/emu/emumem_het.h index 274aa138403..97650c6cbe3 100644 --- a/src/emu/emumem_het.h +++ b/src/emu/emumem_het.h @@ -1,50 +1,60 @@ // license:BSD-3-Clause // copyright-holders:Olivier Galibert +#ifndef MAME_EMU_EMUMEM_HET_H +#define MAME_EMU_EMUMEM_HET_H + +#pragma once // handler_entry_read_tap/handler_entry_write_tap // handler which tap on a bus access and possibly change the data value through a std::function -template<int Width, int AddrShift, int Endian> class handler_entry_read_tap : public handler_entry_read_passthrough<Width, AddrShift, Endian> +template<int Width, int AddrShift> class handler_entry_read_tap : public handler_entry_read_passthrough<Width, AddrShift> { public: - using uX = typename emu::detail::handler_entry_size<Width>::uX; - using inh = handler_entry_read_passthrough<Width, AddrShift, Endian>; + using uX = emu::detail::handler_entry_size_t<Width>; - handler_entry_read_tap(address_space *space, memory_passthrough_handler &mph, std::string name, std::function<void (offs_t offset, uX &data, uX mem_mask)> tap) : handler_entry_read_passthrough<Width, AddrShift, Endian>(space, mph), m_name(name), m_tap(std::move(tap)) {} + handler_entry_read_tap(address_space *space, emu::detail::memory_passthrough_handler_impl &mph, std::string name, std::function<void (offs_t offset, uX &data, uX mem_mask)> tap) : handler_entry_read_passthrough<Width, AddrShift>(space, mph, 4), m_name(name), m_tap(std::move(tap)) {} ~handler_entry_read_tap() = default; - uX read(offs_t offset, uX mem_mask) override; + uX read(offs_t offset, uX mem_mask) const override; + uX read_interruptible(offs_t offset, uX mem_mask) const override; + std::pair<uX, u16> read_flags(offs_t offset, uX mem_mask) const override; + u16 lookup_flags(offs_t offset, uX mem_mask) const override; std::string name() const override; - handler_entry_read_tap<Width, AddrShift, Endian> *instantiate(handler_entry_read<Width, AddrShift, Endian> *next) const override; + handler_entry_read_tap<Width, AddrShift> *instantiate(handler_entry_read<Width, AddrShift> *next) const override; protected: std::string m_name; std::function<void (offs_t offset, uX &data, uX mem_mask)> m_tap; - handler_entry_read_tap(address_space *space, memory_passthrough_handler &mph, handler_entry_read<Width, AddrShift, Endian> *next, std::string name, std::function<void (offs_t offset, uX &data, uX mem_mask)> tap) : handler_entry_read_passthrough<Width, AddrShift, Endian>(space, mph, next), m_name(name), m_tap(tap) {} + handler_entry_read_tap(address_space *space, emu::detail::memory_passthrough_handler_impl &mph, handler_entry_read<Width, AddrShift> *next, std::string name, std::function<void (offs_t offset, uX &data, uX mem_mask)> tap) : handler_entry_read_passthrough<Width, AddrShift>(space, mph, 4, next), m_name(name), m_tap(tap) {} }; -template<int Width, int AddrShift, int Endian> class handler_entry_write_tap : public handler_entry_write_passthrough<Width, AddrShift, Endian> +template<int Width, int AddrShift> class handler_entry_write_tap : public handler_entry_write_passthrough<Width, AddrShift> { public: - using uX = typename emu::detail::handler_entry_size<Width>::uX; - using inh = handler_entry_write_passthrough<Width, AddrShift, Endian>; + using uX = emu::detail::handler_entry_size_t<Width>; - handler_entry_write_tap(address_space *space, memory_passthrough_handler &mph, std::string name, std::function<void (offs_t offset, uX &data, uX mem_mask)> tap) : handler_entry_write_passthrough<Width, AddrShift, Endian>(space, mph), m_name(name), m_tap(std::move(tap)) {} + handler_entry_write_tap(address_space *space, emu::detail::memory_passthrough_handler_impl &mph, std::string name, std::function<void (offs_t offset, uX &data, uX mem_mask)> tap) : handler_entry_write_passthrough<Width, AddrShift>(space, mph, 4), m_name(name), m_tap(std::move(tap)) {} ~handler_entry_write_tap() = default; - void write(offs_t offset, uX data, uX mem_mask) override; + void write(offs_t offset, uX data, uX mem_mask) const override; + void write_interruptible(offs_t offset, uX data, uX mem_mask) const override; + u16 write_flags(offs_t offset, uX data, uX mem_mask) const override; + u16 lookup_flags(offs_t offset, uX mem_mask) const override; std::string name() const override; - handler_entry_write_tap<Width, AddrShift, Endian> *instantiate(handler_entry_write<Width, AddrShift, Endian> *next) const override; + handler_entry_write_tap<Width, AddrShift> *instantiate(handler_entry_write<Width, AddrShift> *next) const override; protected: std::string m_name; std::function<void (offs_t offset, uX &data, uX mem_mask)> m_tap; - handler_entry_write_tap(address_space *space, memory_passthrough_handler &mph, handler_entry_write<Width, AddrShift, Endian> *next, std::string name, std::function<void (offs_t offset, uX &data, uX mem_mask)> tap) : handler_entry_write_passthrough<Width, AddrShift, Endian>(space, mph, next), m_name(name), m_tap(tap) {} + handler_entry_write_tap(address_space *space, emu::detail::memory_passthrough_handler_impl &mph, handler_entry_write<Width, AddrShift> *next, std::string name, std::function<void (offs_t offset, uX &data, uX mem_mask)> tap) : handler_entry_write_passthrough<Width, AddrShift>(space, mph, 4, next), m_name(name), m_tap(tap) {} }; + +#endif // MAME_EMU_EMUMEM_HET_H diff --git a/src/emu/emumem_heu.cpp b/src/emu/emumem_heu.cpp index 8a62d8aa5e2..ada5d56d63a 100644 --- a/src/emu/emumem_heu.cpp +++ b/src/emu/emumem_heu.cpp @@ -7,8 +7,8 @@ #include "emumem_heu.h" -template<int Width, int AddrShift, int Endian> handler_entry_read_units<Width, AddrShift, Endian>::handler_entry_read_units(const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 ukey, address_space *space) : - handler_entry_read<Width, AddrShift, Endian>(space, inh::F_UNITS), +template<int Width, int AddrShift> handler_entry_read_units<Width, AddrShift>::handler_entry_read_units(const memory_units_descriptor<Width, AddrShift> &descriptor, u8 ukey, address_space *space) : + handler_entry_read<Width, AddrShift>(space, handler_entry_read_units::F_UNITS), m_subunits(0) { const auto &entries = descriptor.get_entries_for_key(ukey); @@ -16,8 +16,8 @@ template<int Width, int AddrShift, int Endian> handler_entry_read_units<Width, A std::sort(m_subunit_infos, m_subunit_infos + m_subunits, [](const subunit_info &a, const subunit_info &b) { return a.m_offset < b.m_offset; }); } -template<int Width, int AddrShift, int Endian> handler_entry_read_units<Width, AddrShift, Endian>::handler_entry_read_units(const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 ukey, const handler_entry_read_units *src) : - handler_entry_read<Width, AddrShift, Endian>(src->m_space, inh::F_UNITS), +template<int Width, int AddrShift> handler_entry_read_units<Width, AddrShift>::handler_entry_read_units(const memory_units_descriptor<Width, AddrShift> &descriptor, u8 ukey, const handler_entry_read_units *src) : + handler_entry_read<Width, AddrShift>(src->m_space, handler_entry_read_units::F_UNITS), m_subunits(0) { uX fullmask = 0; @@ -36,32 +36,49 @@ template<int Width, int AddrShift, int Endian> handler_entry_read_units<Width, A std::sort(m_subunit_infos, m_subunit_infos + m_subunits, [](const subunit_info &a, const subunit_info &b) { return a.m_offset < b.m_offset; }); } -template<int Width, int AddrShift, int Endian> handler_entry_read_units<Width, AddrShift, Endian>::~handler_entry_read_units() +template<int Width, int AddrShift> handler_entry_read_units<Width, AddrShift>::handler_entry_read_units(const handler_entry_read_units *src) : + handler_entry_read<Width, AddrShift>(src->m_space, handler_entry_read_units::F_UNITS), + m_subunits(src->m_subunits) +{ + for(u32 i=0; i != src->m_subunits; i++) { + m_subunit_infos[i] = src->m_subunit_infos[i]; + m_subunit_infos[i].m_handler = static_cast<handler_entry_write<Width, AddrShift> *>(m_subunit_infos[i].m_handler)->dup(); + } +} + +template<int Width, int AddrShift> handler_entry_read_units<Width, AddrShift>::~handler_entry_read_units() { for(u32 i=0; i != m_subunits; i++) m_subunit_infos[i].m_handler->unref(); } -template<int Width, int AddrShift, int Endian> void handler_entry_read_units<Width, AddrShift, Endian>::enumerate_references(handler_entry::reflist &refs) const +template<int Width, int AddrShift> handler_entry_read<Width, AddrShift> *handler_entry_read_units<Width, AddrShift>::dup() +{ + return new handler_entry_read_units<Width, AddrShift>(this); +} + +template<int Width, int AddrShift> void handler_entry_read_units<Width, AddrShift>::enumerate_references(handler_entry::reflist &refs) const { for(u32 i=0; i != m_subunits; i++) refs.add(m_subunit_infos[i].m_handler); } -template<int Width, int AddrShift, int Endian> void handler_entry_read_units<Width, AddrShift, Endian>::fill(const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, const std::vector<typename memory_units_descriptor<Width, AddrShift, Endian>::entry> &entries) +template<int Width, int AddrShift> void handler_entry_read_units<Width, AddrShift>::fill(const memory_units_descriptor<Width, AddrShift> &descriptor, const std::vector<typename memory_units_descriptor<Width, AddrShift>::entry> &entries) { handler_entry *handler = descriptor.get_subunit_handler(); handler->ref(entries.size()); for(const auto &e : entries) - m_subunit_infos[m_subunits++] = subunit_info{ handler, e.m_amask, e.m_dmask, e.m_ashift, e.m_offset, e.m_dshift, descriptor.get_subunit_width(), descriptor.get_subunit_endian() }; - m_unmap = inh::m_space->unmap(); + m_subunit_infos[m_subunits++] = subunit_info{ handler, e.m_amask, e.m_dmask, e.m_ashift, e.m_offset, e.m_dshift, descriptor.get_subunit_width() }; + m_unmap = this->m_space->unmap(); for(int i = 0; i < m_subunits; i++) m_unmap &= ~m_subunit_infos[i].m_dmask; } -template<int Width, int AddrShift, int Endian> typename emu::detail::handler_entry_size<Width>::uX handler_entry_read_units<Width, AddrShift, Endian>::read(offs_t offset, uX mem_mask) +template<int Width, int AddrShift> emu::detail::handler_entry_size_t<Width> handler_entry_read_units<Width, AddrShift>::read(offs_t offset, uX mem_mask) const { + this->ref(); + uX result = m_unmap; for (int index = 0; index < m_subunits; index++) { const subunit_info &si = m_subunit_infos[index]; @@ -69,32 +86,92 @@ template<int Width, int AddrShift, int Endian> typename emu::detail::handler_ent offs_t aoffset = (si.m_ashift >= 0 ? offset >> si.m_ashift : offset << si.m_ashift) + si.m_offset; switch(si.m_width) { case 0: - if(si.m_endian == ENDIANNESS_LITTLE) - result |= uX(static_cast<handler_entry_read<0, 0, ENDIANNESS_LITTLE> *>(si.m_handler)->read(aoffset, mem_mask >> si.m_dshift)) << si.m_dshift; - else - result |= uX(static_cast<handler_entry_read<0, 0, ENDIANNESS_BIG > *>(si.m_handler)->read(aoffset, mem_mask >> si.m_dshift)) << si.m_dshift; + result |= uX(static_cast<handler_entry_read<0, 0> *>(si.m_handler)->read(aoffset, mem_mask >> si.m_dshift)) << si.m_dshift; break; case 1: - if(si.m_endian == ENDIANNESS_LITTLE) - result |= uX(static_cast<handler_entry_read<1, -1, ENDIANNESS_LITTLE> *>(si.m_handler)->read(aoffset, mem_mask >> si.m_dshift)) << si.m_dshift; - else - result |= uX(static_cast<handler_entry_read<1, -1, ENDIANNESS_BIG > *>(si.m_handler)->read(aoffset, mem_mask >> si.m_dshift)) << si.m_dshift; + result |= uX(static_cast<handler_entry_read<1, -1> *>(si.m_handler)->read(aoffset, mem_mask >> si.m_dshift)) << si.m_dshift; break; case 2: - if(si.m_endian == ENDIANNESS_LITTLE) - result |= uX(static_cast<handler_entry_read<2, -2, ENDIANNESS_LITTLE> *>(si.m_handler)->read(aoffset, mem_mask >> si.m_dshift)) << si.m_dshift; - else - result |= uX(static_cast<handler_entry_read<2, -2, ENDIANNESS_BIG > *>(si.m_handler)->read(aoffset, mem_mask >> si.m_dshift)) << si.m_dshift; + result |= uX(static_cast<handler_entry_read<2, -2> *>(si.m_handler)->read(aoffset, mem_mask >> si.m_dshift)) << si.m_dshift; break; default: abort(); } } } + + this->unref(); return result; } -template<int Width, int AddrShift, int Endian> std::string handler_entry_read_units<Width, AddrShift, Endian>::m2r(typename emu::detail::handler_entry_size<Width>::uX mask) +template<int Width, int AddrShift> emu::detail::handler_entry_size_t<Width> handler_entry_read_units<Width, AddrShift>::read_interruptible(offs_t offset, uX mem_mask) const +{ + return read(offset, mem_mask); +} + +template<int Width, int AddrShift> std::pair<emu::detail::handler_entry_size_t<Width>, u16> handler_entry_read_units<Width, AddrShift>::read_flags(offs_t offset, uX mem_mask) const +{ + this->ref(); + + uX result = m_unmap; + u16 flags = 0; + for (int index = 0; index < m_subunits; index++) { + const subunit_info &si = m_subunit_infos[index]; + if (mem_mask & si.m_amask) { + offs_t aoffset = (si.m_ashift >= 0 ? offset >> si.m_ashift : offset << si.m_ashift) + si.m_offset; + std::pair<uX, u16> pack; + switch(si.m_width) { + case 0: + pack = static_cast<handler_entry_read<0, 0> *>(si.m_handler)->read_flags(aoffset, mem_mask >> si.m_dshift); + break; + case 1: + pack = static_cast<handler_entry_read<1, -1> *>(si.m_handler)->read_flags(aoffset, mem_mask >> si.m_dshift); + break; + case 2: + pack = static_cast<handler_entry_read<2, -2> *>(si.m_handler)->read_flags(aoffset, mem_mask >> si.m_dshift); + break; + default: + abort(); + } + result |= uX(pack.first) << si.m_dshift; + flags |= pack.second; + } + } + + this->unref(); + return std::make_pair(result, flags); +} + +template<int Width, int AddrShift> u16 handler_entry_read_units<Width, AddrShift>::lookup_flags(offs_t offset, uX mem_mask) const +{ + this->ref(); + + u16 flags = 0; + for (int index = 0; index < m_subunits; index++) { + const subunit_info &si = m_subunit_infos[index]; + if (mem_mask & si.m_amask) { + offs_t aoffset = (si.m_ashift >= 0 ? offset >> si.m_ashift : offset << si.m_ashift) + si.m_offset; + switch(si.m_width) { + case 0: + flags |= static_cast<handler_entry_read<0, 0> *>(si.m_handler)->lookup_flags(aoffset, mem_mask >> si.m_dshift); + break; + case 1: + flags |= static_cast<handler_entry_read<1, -1> *>(si.m_handler)->lookup_flags(aoffset, mem_mask >> si.m_dshift); + break; + case 2: + flags |= static_cast<handler_entry_read<2, -2> *>(si.m_handler)->lookup_flags(aoffset, mem_mask >> si.m_dshift); + break; + default: + abort(); + } + } + } + + this->unref(); + return flags; +} + +template<int Width, int AddrShift> std::string handler_entry_read_units<Width, AddrShift>::m2r(emu::detail::handler_entry_size_t<Width> mask) { constexpr u32 mbits = 8*sizeof(uX); u32 start, end; @@ -105,7 +182,7 @@ template<int Width, int AddrShift, int Endian> std::string handler_entry_read_un return util::string_format("%d-%d", end, start); } -template<int Width, int AddrShift, int Endian> std::string handler_entry_read_units<Width, AddrShift, Endian>::name() const +template<int Width, int AddrShift> std::string handler_entry_read_units<Width, AddrShift>::name() const { std::string result; @@ -122,8 +199,8 @@ template<int Width, int AddrShift, int Endian> std::string handler_entry_read_un -template<int Width, int AddrShift, int Endian> handler_entry_write_units<Width, AddrShift, Endian>::handler_entry_write_units(const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 ukey, address_space *space) : - handler_entry_write<Width, AddrShift, Endian>(space, inh::F_UNITS), +template<int Width, int AddrShift> handler_entry_write_units<Width, AddrShift>::handler_entry_write_units(const memory_units_descriptor<Width, AddrShift> &descriptor, u8 ukey, address_space *space) : + handler_entry_write<Width, AddrShift>(space, handler_entry_write_units::F_UNITS), m_subunits(0) { const auto &entries = descriptor.get_entries_for_key(ukey); @@ -131,8 +208,8 @@ template<int Width, int AddrShift, int Endian> handler_entry_write_units<Width, std::sort(m_subunit_infos, m_subunit_infos + m_subunits, [](const subunit_info &a, const subunit_info &b) { return a.m_offset < b.m_offset; }); } -template<int Width, int AddrShift, int Endian> handler_entry_write_units<Width, AddrShift, Endian>::handler_entry_write_units(const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 ukey, const handler_entry_write_units<Width, AddrShift, Endian> *src) : - handler_entry_write<Width, AddrShift, Endian>(src->m_space, inh::F_UNITS), +template<int Width, int AddrShift> handler_entry_write_units<Width, AddrShift>::handler_entry_write_units(const memory_units_descriptor<Width, AddrShift> &descriptor, u8 ukey, const handler_entry_write_units<Width, AddrShift> *src) : + handler_entry_write<Width, AddrShift>(src->m_space, handler_entry_write_units::F_UNITS), m_subunits(0) { uX fullmask = 0; @@ -151,60 +228,133 @@ template<int Width, int AddrShift, int Endian> handler_entry_write_units<Width, std::sort(m_subunit_infos, m_subunit_infos + m_subunits, [](const subunit_info &a, const subunit_info &b) { return a.m_offset < b.m_offset; }); } -template<int Width, int AddrShift, int Endian> handler_entry_write_units<Width, AddrShift, Endian>::~handler_entry_write_units() +template<int Width, int AddrShift> handler_entry_write_units<Width, AddrShift>::handler_entry_write_units(const handler_entry_write_units *src) : + handler_entry_write<Width, AddrShift>(src->m_space, handler_entry_write_units::F_UNITS), + m_subunits(src->m_subunits) +{ + for(u32 i=0; i != src->m_subunits; i++) { + m_subunit_infos[i] = src->m_subunit_infos[i]; + m_subunit_infos[i].m_handler = static_cast<handler_entry_write<Width, AddrShift> *>(m_subunit_infos[i].m_handler)->dup(); + } +} + +template<int Width, int AddrShift> handler_entry_write_units<Width, AddrShift>::~handler_entry_write_units() { for(u32 i=0; i != m_subunits; i++) m_subunit_infos[i].m_handler->unref(); } -template<int Width, int AddrShift, int Endian> void handler_entry_write_units<Width, AddrShift, Endian>::enumerate_references(handler_entry::reflist &refs) const +template<int Width, int AddrShift> handler_entry_write<Width, AddrShift> *handler_entry_write_units<Width, AddrShift>::dup() +{ + return new handler_entry_write_units<Width, AddrShift>(this); +} + +template<int Width, int AddrShift> void handler_entry_write_units<Width, AddrShift>::enumerate_references(handler_entry::reflist &refs) const { for(u32 i=0; i != m_subunits; i++) refs.add(m_subunit_infos[i].m_handler); } -template<int Width, int AddrShift, int Endian> void handler_entry_write_units<Width, AddrShift, Endian>::fill(const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, const std::vector<typename memory_units_descriptor<Width, AddrShift, Endian>::entry> &entries) +template<int Width, int AddrShift> void handler_entry_write_units<Width, AddrShift>::fill(const memory_units_descriptor<Width, AddrShift> &descriptor, const std::vector<typename memory_units_descriptor<Width, AddrShift>::entry> &entries) { handler_entry *handler = descriptor.get_subunit_handler(); handler->ref(entries.size()); for(const auto &e : entries) - m_subunit_infos[m_subunits++] = subunit_info{ handler, e.m_amask, e.m_dmask, e.m_ashift, e.m_offset, e.m_dshift, descriptor.get_subunit_width(), descriptor.get_subunit_endian() }; + m_subunit_infos[m_subunits++] = subunit_info{ handler, e.m_amask, e.m_dmask, e.m_ashift, e.m_offset, e.m_dshift, descriptor.get_subunit_width() }; +} + +template<int Width, int AddrShift> void handler_entry_write_units<Width, AddrShift>::write(offs_t offset, uX data, uX mem_mask) const +{ + this->ref(); + + for (int index = 0; index < m_subunits; index++) { + const subunit_info &si = m_subunit_infos[index]; + if (mem_mask & si.m_amask) { + offs_t aoffset = (si.m_ashift >= 0 ? offset >> si.m_ashift : offset << si.m_ashift) + si.m_offset; + switch(si.m_width) { + case 0: + static_cast<handler_entry_write<0, 0> *>(si.m_handler)->write(aoffset, data >> si.m_dshift, mem_mask >> si.m_dshift); + break; + case 1: + static_cast<handler_entry_write<1, -1> *>(si.m_handler)->write(aoffset, data >> si.m_dshift, mem_mask >> si.m_dshift); + break; + case 2: + static_cast<handler_entry_write<2, -2> *>(si.m_handler)->write(aoffset, data >> si.m_dshift, mem_mask >> si.m_dshift); + break; + default: + abort(); + } + } + } + + this->unref(); +} + +template<int Width, int AddrShift> void handler_entry_write_units<Width, AddrShift>::write_interruptible(offs_t offset, uX data, uX mem_mask) const +{ + write(offset, data, mem_mask); } -template<int Width, int AddrShift, int Endian> void handler_entry_write_units<Width, AddrShift, Endian>::write(offs_t offset, uX data, uX mem_mask) +template<int Width, int AddrShift> u16 handler_entry_write_units<Width, AddrShift>::write_flags(offs_t offset, uX data, uX mem_mask) const { + this->ref(); + + u16 flags = 0; for (int index = 0; index < m_subunits; index++) { const subunit_info &si = m_subunit_infos[index]; if (mem_mask & si.m_amask) { offs_t aoffset = (si.m_ashift >= 0 ? offset >> si.m_ashift : offset << si.m_ashift) + si.m_offset; switch(si.m_width) { case 0: - if(si.m_endian == ENDIANNESS_LITTLE) - static_cast<handler_entry_write<0, 0, ENDIANNESS_LITTLE> *>(si.m_handler)->write(aoffset, data >> si.m_dshift, mem_mask >> si.m_dshift); - else - static_cast<handler_entry_write<0, 0, ENDIANNESS_BIG > *>(si.m_handler)->write(aoffset, data >> si.m_dshift, mem_mask >> si.m_dshift); + flags |= static_cast<handler_entry_write<0, 0> *>(si.m_handler)->write_flags(aoffset, data >> si.m_dshift, mem_mask >> si.m_dshift); break; case 1: - if(si.m_endian == ENDIANNESS_LITTLE) - static_cast<handler_entry_write<1, -1, ENDIANNESS_LITTLE> *>(si.m_handler)->write(aoffset, data >> si.m_dshift, mem_mask >> si.m_dshift); - else - static_cast<handler_entry_write<1, -1, ENDIANNESS_BIG > *>(si.m_handler)->write(aoffset, data >> si.m_dshift, mem_mask >> si.m_dshift); + flags |= static_cast<handler_entry_write<1, -1> *>(si.m_handler)->write_flags(aoffset, data >> si.m_dshift, mem_mask >> si.m_dshift); break; case 2: - if(si.m_endian == ENDIANNESS_LITTLE) - static_cast<handler_entry_write<2, -2, ENDIANNESS_LITTLE> *>(si.m_handler)->write(aoffset, data >> si.m_dshift, mem_mask >> si.m_dshift); - else - static_cast<handler_entry_write<2, -2, ENDIANNESS_BIG > *>(si.m_handler)->write(aoffset, data >> si.m_dshift, mem_mask >> si.m_dshift); + flags |= static_cast<handler_entry_write<2, -2> *>(si.m_handler)->write_flags(aoffset, data >> si.m_dshift, mem_mask >> si.m_dshift); break; default: abort(); } } } + + this->unref(); + return flags; +} + +template<int Width, int AddrShift> u16 handler_entry_write_units<Width, AddrShift>::lookup_flags(offs_t offset, uX mem_mask) const +{ + this->ref(); + + u16 flags = 0; + for (int index = 0; index < m_subunits; index++) { + const subunit_info &si = m_subunit_infos[index]; + if (mem_mask & si.m_amask) { + offs_t aoffset = (si.m_ashift >= 0 ? offset >> si.m_ashift : offset << si.m_ashift) + si.m_offset; + switch(si.m_width) { + case 0: + flags |= static_cast<handler_entry_write<0, 0> *>(si.m_handler)->lookup_flags(aoffset, mem_mask >> si.m_dshift); + break; + case 1: + flags |= static_cast<handler_entry_write<1, -1> *>(si.m_handler)->lookup_flags(aoffset, mem_mask >> si.m_dshift); + break; + case 2: + flags |= static_cast<handler_entry_write<2, -2> *>(si.m_handler)->lookup_flags(aoffset, mem_mask >> si.m_dshift); + break; + default: + abort(); + } + } + } + + this->unref(); + return flags; } -template<int Width, int AddrShift, int Endian> std::string handler_entry_write_units<Width, AddrShift, Endian>::m2r(typename emu::detail::handler_entry_size<Width>::uX mask) +template<int Width, int AddrShift> std::string handler_entry_write_units<Width, AddrShift>::m2r(emu::detail::handler_entry_size_t<Width> mask) { constexpr u32 mbits = 8*sizeof(uX); u32 start, end; @@ -215,7 +365,7 @@ template<int Width, int AddrShift, int Endian> std::string handler_entry_write_u return util::string_format("%d-%d", end, start); } -template<int Width, int AddrShift, int Endian> std::string handler_entry_write_units<Width, AddrShift, Endian>::name() const +template<int Width, int AddrShift> std::string handler_entry_write_units<Width, AddrShift>::name() const { std::string result; @@ -230,52 +380,30 @@ template<int Width, int AddrShift, int Endian> std::string handler_entry_write_u } -template class handler_entry_read_units<0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_read_units<0, 1, ENDIANNESS_BIG>; -template class handler_entry_read_units<0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_units<0, 0, ENDIANNESS_BIG>; -template class handler_entry_read_units<1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_read_units<1, 3, ENDIANNESS_BIG>; -template class handler_entry_read_units<1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_units<1, 0, ENDIANNESS_BIG>; -template class handler_entry_read_units<1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_units<1, -1, ENDIANNESS_BIG>; -template class handler_entry_read_units<2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_units<2, 0, ENDIANNESS_BIG>; -template class handler_entry_read_units<2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_units<2, -1, ENDIANNESS_BIG>; -template class handler_entry_read_units<2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_units<2, -2, ENDIANNESS_BIG>; -template class handler_entry_read_units<3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_units<3, 0, ENDIANNESS_BIG>; -template class handler_entry_read_units<3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_units<3, -1, ENDIANNESS_BIG>; -template class handler_entry_read_units<3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_units<3, -2, ENDIANNESS_BIG>; -template class handler_entry_read_units<3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_read_units<3, -3, ENDIANNESS_BIG>; - -template class handler_entry_write_units<0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_write_units<0, 1, ENDIANNESS_BIG>; -template class handler_entry_write_units<0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_units<0, 0, ENDIANNESS_BIG>; -template class handler_entry_write_units<1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_write_units<1, 3, ENDIANNESS_BIG>; -template class handler_entry_write_units<1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_units<1, 0, ENDIANNESS_BIG>; -template class handler_entry_write_units<1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_units<1, -1, ENDIANNESS_BIG>; -template class handler_entry_write_units<2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_units<2, 0, ENDIANNESS_BIG>; -template class handler_entry_write_units<2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_units<2, -1, ENDIANNESS_BIG>; -template class handler_entry_write_units<2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_units<2, -2, ENDIANNESS_BIG>; -template class handler_entry_write_units<3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_units<3, 0, ENDIANNESS_BIG>; -template class handler_entry_write_units<3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_units<3, -1, ENDIANNESS_BIG>; -template class handler_entry_write_units<3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_units<3, -2, ENDIANNESS_BIG>; -template class handler_entry_write_units<3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_write_units<3, -3, ENDIANNESS_BIG>; +template class handler_entry_read_units<0, 1>; +template class handler_entry_read_units<0, 0>; +template class handler_entry_read_units<1, 3>; +template class handler_entry_read_units<1, 0>; +template class handler_entry_read_units<1, -1>; +template class handler_entry_read_units<2, 3>; +template class handler_entry_read_units<2, 0>; +template class handler_entry_read_units<2, -1>; +template class handler_entry_read_units<2, -2>; +template class handler_entry_read_units<3, 0>; +template class handler_entry_read_units<3, -1>; +template class handler_entry_read_units<3, -2>; +template class handler_entry_read_units<3, -3>; + +template class handler_entry_write_units<0, 1>; +template class handler_entry_write_units<0, 0>; +template class handler_entry_write_units<1, 3>; +template class handler_entry_write_units<1, 0>; +template class handler_entry_write_units<1, -1>; +template class handler_entry_write_units<2, 3>; +template class handler_entry_write_units<2, 0>; +template class handler_entry_write_units<2, -1>; +template class handler_entry_write_units<2, -2>; +template class handler_entry_write_units<3, 0>; +template class handler_entry_write_units<3, -1>; +template class handler_entry_write_units<3, -2>; +template class handler_entry_write_units<3, -3>; diff --git a/src/emu/emumem_heu.h b/src/emu/emumem_heu.h index dbc0cf064f7..8573cc4e334 100644 --- a/src/emu/emumem_heu.h +++ b/src/emu/emumem_heu.h @@ -1,25 +1,33 @@ // license:BSD-3-Clause // copyright-holders:Olivier Galibert +#ifndef MAME_EMU_EMUMEM_HEU_H +#define MAME_EMU_EMUMEM_HEU_H + +#pragma once // handler_entry_read_units/handler_entry_write_units // merges/splits an access among multiple handlers (unitmask support) -template<int Width, int AddrShift, int Endian> class handler_entry_read_units : public handler_entry_read<Width, AddrShift, Endian> +template<int Width, int AddrShift> class handler_entry_read_units : public handler_entry_read<Width, AddrShift> { public: - using uX = typename emu::detail::handler_entry_size<Width>::uX; - using inh = handler_entry_read<Width, AddrShift, Endian>; + using uX = emu::detail::handler_entry_size_t<Width>; - handler_entry_read_units(const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 ukey, address_space *space); - handler_entry_read_units(const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 ukey, const handler_entry_read_units *src); + handler_entry_read_units(const memory_units_descriptor<Width, AddrShift> &descriptor, u8 ukey, address_space *space); + handler_entry_read_units(const memory_units_descriptor<Width, AddrShift> &descriptor, u8 ukey, const handler_entry_read_units *src); + handler_entry_read_units(const handler_entry_read_units *src); ~handler_entry_read_units(); - uX read(offs_t offset, uX mem_mask) override; + uX read(offs_t offset, uX mem_mask) const override; + uX read_interruptible(offs_t offset, uX mem_mask) const override; + std::pair<uX, u16> read_flags(offs_t offset, uX mem_mask) const override; + u16 lookup_flags(offs_t offset, uX mem_mask) const override; std::string name() const override; void enumerate_references(handler_entry::reflist &refs) const override; + handler_entry_read<Width, AddrShift> *dup() override; private: static constexpr u32 SUBUNIT_COUNT = 1 << Width; @@ -35,32 +43,35 @@ private: u8 m_dshift; // data shift of the subunit u8 m_width; // access width (0..3) - u8 m_endian; // endianness }; subunit_info m_subunit_infos[SUBUNIT_COUNT]; // subunit information uX m_unmap; // "unmapped" value to add to reads u8 m_subunits; // number of subunits - void fill(const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, const std::vector<typename memory_units_descriptor<Width, AddrShift, Endian>::entry> &entries); + void fill(const memory_units_descriptor<Width, AddrShift> &descriptor, const std::vector<typename memory_units_descriptor<Width, AddrShift>::entry> &entries); static std::string m2r(uX mask); }; -template<int Width, int AddrShift, int Endian> class handler_entry_write_units : public handler_entry_write<Width, AddrShift, Endian> +template<int Width, int AddrShift> class handler_entry_write_units : public handler_entry_write<Width, AddrShift> { public: - using uX = typename emu::detail::handler_entry_size<Width>::uX; - using inh = handler_entry_write<Width, AddrShift, Endian>; + using uX = emu::detail::handler_entry_size_t<Width>; - handler_entry_write_units(const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 ukey, address_space *space); - handler_entry_write_units(const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, u8 ukey, const handler_entry_write_units<Width, AddrShift, Endian> *src); + handler_entry_write_units(const memory_units_descriptor<Width, AddrShift> &descriptor, u8 ukey, address_space *space); + handler_entry_write_units(const memory_units_descriptor<Width, AddrShift> &descriptor, u8 ukey, const handler_entry_write_units<Width, AddrShift> *src); + handler_entry_write_units(const handler_entry_write_units *src); ~handler_entry_write_units(); - void write(offs_t offset, uX data, uX mem_mask) override; + void write(offs_t offset, uX data, uX mem_mask) const override; + void write_interruptible(offs_t offset, uX data, uX mem_mask) const override; + u16 write_flags(offs_t offset, uX data, uX mem_mask) const override; + u16 lookup_flags(offs_t offset, uX mem_mask) const override; std::string name() const override; void enumerate_references(handler_entry::reflist &refs) const override; + handler_entry_write<Width, AddrShift> *dup() override; private: static constexpr u32 SUBUNIT_COUNT = 1 << Width; @@ -76,13 +87,13 @@ private: u8 m_dshift; // data shift of the subunit u8 m_width; // access width (0..3) - u8 m_endian; // endianness }; subunit_info m_subunit_infos[SUBUNIT_COUNT]; // subunit information u8 m_subunits; // number of subunits - void fill(const memory_units_descriptor<Width, AddrShift, Endian> &descriptor, const std::vector<typename memory_units_descriptor<Width, AddrShift, Endian>::entry> &entries); + void fill(const memory_units_descriptor<Width, AddrShift> &descriptor, const std::vector<typename memory_units_descriptor<Width, AddrShift>::entry> &entries); static std::string m2r(uX mask); }; +#endif // MAME_EMU_EMUMEM_HEU_H diff --git a/src/emu/emumem_heun.cpp b/src/emu/emumem_heun.cpp index c25d77bbd33..5157c2c35df 100644 --- a/src/emu/emumem_heun.cpp +++ b/src/emu/emumem_heun.cpp @@ -5,37 +5,110 @@ #include "emumem_hea.h" #include "emumem_heun.h" -template<int Width, int AddrShift, int Endian> typename emu::detail::handler_entry_size<Width>::uX handler_entry_read_unmapped<Width, AddrShift, Endian>::read(offs_t offset, uX mem_mask) +template<int Width, int AddrShift> emu::detail::handler_entry_size_t<Width> handler_entry_read_unmapped<Width, AddrShift>::read(offs_t offset, uX mem_mask) const { - if (inh::m_space->log_unmap() && !inh::m_space->m_manager.machine().side_effects_disabled()) - inh::m_space->device().logerror(inh::m_space->is_octal() + if (this->m_space->log_unmap() && !this->m_space->m_manager.machine().side_effects_disabled()) + this->m_space->device().logerror(this->m_space->is_octal() ? "%s: unmapped %s memory read from %0*o & %0*o\n" : "%s: unmapped %s memory read from %0*X & %0*X\n", - inh::m_space->m_manager.machine().describe_context(), inh::m_space->name(), - inh::m_space->addrchars(), offset, + this->m_space->m_manager.machine().describe_context(), this->m_space->name(), + this->m_space->addrchars(), offset, 2 << Width, mem_mask); - return inh::m_space->unmap(); + return this->m_space->unmap(); } -template<int Width, int AddrShift, int Endian> std::string handler_entry_read_unmapped<Width, AddrShift, Endian>::name() const +template<int Width, int AddrShift> emu::detail::handler_entry_size_t<Width> handler_entry_read_unmapped<Width, AddrShift>::read_interruptible(offs_t offset, uX mem_mask) const +{ + if (this->m_space->log_unmap() && !this->m_space->m_manager.machine().side_effects_disabled()) + this->m_space->device().logerror(this->m_space->is_octal() + ? "%s: unmapped %s memory read from %0*o & %0*o\n" + : "%s: unmapped %s memory read from %0*X & %0*X\n", + this->m_space->m_manager.machine().describe_context(), this->m_space->name(), + this->m_space->addrchars(), offset, + 2 << Width, mem_mask); + return this->m_space->unmap(); +} + +template<int Width, int AddrShift> std::pair<emu::detail::handler_entry_size_t<Width>, u16> handler_entry_read_unmapped<Width, AddrShift>::read_flags(offs_t offset, uX mem_mask) const +{ + if (this->m_space->log_unmap() && !this->m_space->m_manager.machine().side_effects_disabled()) + this->m_space->device().logerror(this->m_space->is_octal() + ? "%s: unmapped %s memory read from %0*o & %0*o\n" + : "%s: unmapped %s memory read from %0*X & %0*X\n", + this->m_space->m_manager.machine().describe_context(), this->m_space->name(), + this->m_space->addrchars(), offset, + 2 << Width, mem_mask); + return std::pair<uX, u16>(this->m_space->unmap(), this->m_flags); +} + +template<int Width, int AddrShift> u16 handler_entry_read_unmapped<Width, AddrShift>::lookup_flags(offs_t offset, uX mem_mask) const +{ + if (this->m_space->log_unmap() && !this->m_space->m_manager.machine().side_effects_disabled()) + this->m_space->device().logerror(this->m_space->is_octal() + ? "%s: unmapped %s memory read flags lookup from %0*o & %0*o\n" + : "%s: unmapped %s memory read flags lookup from %0*X & %0*X\n", + this->m_space->m_manager.machine().describe_context(), this->m_space->name(), + this->m_space->addrchars(), offset, + 2 << Width, mem_mask); + return this->m_flags; +} + +template<int Width, int AddrShift> std::string handler_entry_read_unmapped<Width, AddrShift>::name() const { return "unmapped"; } -template<int Width, int AddrShift, int Endian> void handler_entry_write_unmapped<Width, AddrShift, Endian>::write(offs_t offset, uX data, uX mem_mask) +template<int Width, int AddrShift> void handler_entry_write_unmapped<Width, AddrShift>::write(offs_t offset, uX data, uX mem_mask)const +{ + if (this->m_space->log_unmap() && !this->m_space->m_manager.machine().side_effects_disabled()) + this->m_space->device().logerror(this->m_space->is_octal() + ? "%s: unmapped %s memory write to %0*o = %0*o & %0*o\n" + : "%s: unmapped %s memory write to %0*X = %0*X & %0*X\n", + this->m_space->m_manager.machine().describe_context(), this->m_space->name(), + this->m_space->addrchars(), offset, + 2 << Width, data, + 2 << Width, mem_mask); +} + +template<int Width, int AddrShift> void handler_entry_write_unmapped<Width, AddrShift>::write_interruptible(offs_t offset, uX data, uX mem_mask)const +{ + if (this->m_space->log_unmap() && !this->m_space->m_manager.machine().side_effects_disabled()) + this->m_space->device().logerror(this->m_space->is_octal() + ? "%s: unmapped %s memory write to %0*o = %0*o & %0*o\n" + : "%s: unmapped %s memory write to %0*X = %0*X & %0*X\n", + this->m_space->m_manager.machine().describe_context(), this->m_space->name(), + this->m_space->addrchars(), offset, + 2 << Width, data, + 2 << Width, mem_mask); +} + +template<int Width, int AddrShift> u16 handler_entry_write_unmapped<Width, AddrShift>::write_flags(offs_t offset, uX data, uX mem_mask)const { - if (inh::m_space->log_unmap() && !inh::m_space->m_manager.machine().side_effects_disabled()) - inh::m_space->device().logerror(inh::m_space->is_octal() + if (this->m_space->log_unmap() && !this->m_space->m_manager.machine().side_effects_disabled()) + this->m_space->device().logerror(this->m_space->is_octal() ? "%s: unmapped %s memory write to %0*o = %0*o & %0*o\n" : "%s: unmapped %s memory write to %0*X = %0*X & %0*X\n", - inh::m_space->m_manager.machine().describe_context(), inh::m_space->name(), - inh::m_space->addrchars(), offset, + this->m_space->m_manager.machine().describe_context(), this->m_space->name(), + this->m_space->addrchars(), offset, 2 << Width, data, 2 << Width, mem_mask); + return this->m_flags; } -template<int Width, int AddrShift, int Endian> std::string handler_entry_write_unmapped<Width, AddrShift, Endian>::name() const +template<int Width, int AddrShift> u16 handler_entry_write_unmapped<Width, AddrShift>::lookup_flags(offs_t offset, uX mem_mask)const +{ + if (this->m_space->log_unmap() && !this->m_space->m_manager.machine().side_effects_disabled()) + this->m_space->device().logerror(this->m_space->is_octal() + ? "%s: unmapped %s write flags lookup from %0*o & %0*o\n" + : "%s: unmapped %s write flags lookup from %0*X & %0*X\n", + this->m_space->m_manager.machine().describe_context(), this->m_space->name(), + this->m_space->addrchars(), offset, + 2 << Width, mem_mask); + return this->m_flags; +} + +template<int Width, int AddrShift> std::string handler_entry_write_unmapped<Width, AddrShift>::name() const { return "unmapped"; } @@ -43,124 +116,109 @@ template<int Width, int AddrShift, int Endian> std::string handler_entry_write_u -template<int Width, int AddrShift, int Endian> typename emu::detail::handler_entry_size<Width>::uX handler_entry_read_nop<Width, AddrShift, Endian>::read(offs_t offset, uX mem_mask) +template<int Width, int AddrShift> emu::detail::handler_entry_size_t<Width> handler_entry_read_nop<Width, AddrShift>::read(offs_t offset, uX mem_mask) const +{ + return this->m_space->unmap(); +} + +template<int Width, int AddrShift> emu::detail::handler_entry_size_t<Width> handler_entry_read_nop<Width, AddrShift>::read_interruptible(offs_t offset, uX mem_mask) const +{ + return this->m_space->unmap(); +} + +template<int Width, int AddrShift> std::pair<emu::detail::handler_entry_size_t<Width>, u16> handler_entry_read_nop<Width, AddrShift>::read_flags(offs_t offset, uX mem_mask) const +{ + return std::pair<uX, u16>(this->m_space->unmap(), this->m_flags); +} + +template<int Width, int AddrShift> u16 handler_entry_read_nop<Width, AddrShift>::lookup_flags(offs_t offset, uX mem_mask) const { - return inh::m_space->unmap(); + return this->m_flags; } -template<int Width, int AddrShift, int Endian> std::string handler_entry_read_nop<Width, AddrShift, Endian>::name() const +template<int Width, int AddrShift> std::string handler_entry_read_nop<Width, AddrShift>::name() const { return "nop"; } -template<int Width, int AddrShift, int Endian> void handler_entry_write_nop<Width, AddrShift, Endian>::write(offs_t offset, uX data, uX mem_mask) +template<int Width, int AddrShift> void handler_entry_write_nop<Width, AddrShift>::write(offs_t offset, uX data, uX mem_mask) const +{ +} + +template<int Width, int AddrShift> void handler_entry_write_nop<Width, AddrShift>::write_interruptible(offs_t offset, uX data, uX mem_mask) const +{ +} + +template<int Width, int AddrShift> u16 handler_entry_write_nop<Width, AddrShift>::write_flags(offs_t offset, uX data, uX mem_mask) const +{ + return this->m_flags; +} + +template<int Width, int AddrShift> u16 handler_entry_write_nop<Width, AddrShift>::lookup_flags(offs_t offset, uX mem_mask) const { + return this->m_flags; } -template<int Width, int AddrShift, int Endian> std::string handler_entry_write_nop<Width, AddrShift, Endian>::name() const +template<int Width, int AddrShift> std::string handler_entry_write_nop<Width, AddrShift>::name() const { return "nop"; } -template class handler_entry_read_unmapped<0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_read_unmapped<0, 1, ENDIANNESS_BIG>; -template class handler_entry_read_unmapped<0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_unmapped<0, 0, ENDIANNESS_BIG>; -template class handler_entry_read_unmapped<1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_read_unmapped<1, 3, ENDIANNESS_BIG>; -template class handler_entry_read_unmapped<1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_unmapped<1, 0, ENDIANNESS_BIG>; -template class handler_entry_read_unmapped<1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_unmapped<1, -1, ENDIANNESS_BIG>; -template class handler_entry_read_unmapped<2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_unmapped<2, 0, ENDIANNESS_BIG>; -template class handler_entry_read_unmapped<2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_unmapped<2, -1, ENDIANNESS_BIG>; -template class handler_entry_read_unmapped<2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_unmapped<2, -2, ENDIANNESS_BIG>; -template class handler_entry_read_unmapped<3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_unmapped<3, 0, ENDIANNESS_BIG>; -template class handler_entry_read_unmapped<3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_unmapped<3, -1, ENDIANNESS_BIG>; -template class handler_entry_read_unmapped<3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_unmapped<3, -2, ENDIANNESS_BIG>; -template class handler_entry_read_unmapped<3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_read_unmapped<3, -3, ENDIANNESS_BIG>; - -template class handler_entry_write_unmapped<0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_write_unmapped<0, 1, ENDIANNESS_BIG>; -template class handler_entry_write_unmapped<0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_unmapped<0, 0, ENDIANNESS_BIG>; -template class handler_entry_write_unmapped<1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_write_unmapped<1, 3, ENDIANNESS_BIG>; -template class handler_entry_write_unmapped<1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_unmapped<1, 0, ENDIANNESS_BIG>; -template class handler_entry_write_unmapped<1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_unmapped<1, -1, ENDIANNESS_BIG>; -template class handler_entry_write_unmapped<2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_unmapped<2, 0, ENDIANNESS_BIG>; -template class handler_entry_write_unmapped<2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_unmapped<2, -1, ENDIANNESS_BIG>; -template class handler_entry_write_unmapped<2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_unmapped<2, -2, ENDIANNESS_BIG>; -template class handler_entry_write_unmapped<3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_unmapped<3, 0, ENDIANNESS_BIG>; -template class handler_entry_write_unmapped<3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_unmapped<3, -1, ENDIANNESS_BIG>; -template class handler_entry_write_unmapped<3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_unmapped<3, -2, ENDIANNESS_BIG>; -template class handler_entry_write_unmapped<3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_write_unmapped<3, -3, ENDIANNESS_BIG>; - - -template class handler_entry_read_nop<0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_read_nop<0, 1, ENDIANNESS_BIG>; -template class handler_entry_read_nop<0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_nop<0, 0, ENDIANNESS_BIG>; -template class handler_entry_read_nop<1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_read_nop<1, 3, ENDIANNESS_BIG>; -template class handler_entry_read_nop<1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_nop<1, 0, ENDIANNESS_BIG>; -template class handler_entry_read_nop<1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_nop<1, -1, ENDIANNESS_BIG>; -template class handler_entry_read_nop<2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_nop<2, 0, ENDIANNESS_BIG>; -template class handler_entry_read_nop<2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_nop<2, -1, ENDIANNESS_BIG>; -template class handler_entry_read_nop<2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_nop<2, -2, ENDIANNESS_BIG>; -template class handler_entry_read_nop<3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_read_nop<3, 0, ENDIANNESS_BIG>; -template class handler_entry_read_nop<3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_read_nop<3, -1, ENDIANNESS_BIG>; -template class handler_entry_read_nop<3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_read_nop<3, -2, ENDIANNESS_BIG>; -template class handler_entry_read_nop<3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_read_nop<3, -3, ENDIANNESS_BIG>; - -template class handler_entry_write_nop<0, 1, ENDIANNESS_LITTLE>; -template class handler_entry_write_nop<0, 1, ENDIANNESS_BIG>; -template class handler_entry_write_nop<0, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_nop<0, 0, ENDIANNESS_BIG>; -template class handler_entry_write_nop<1, 3, ENDIANNESS_LITTLE>; -template class handler_entry_write_nop<1, 3, ENDIANNESS_BIG>; -template class handler_entry_write_nop<1, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_nop<1, 0, ENDIANNESS_BIG>; -template class handler_entry_write_nop<1, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_nop<1, -1, ENDIANNESS_BIG>; -template class handler_entry_write_nop<2, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_nop<2, 0, ENDIANNESS_BIG>; -template class handler_entry_write_nop<2, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_nop<2, -1, ENDIANNESS_BIG>; -template class handler_entry_write_nop<2, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_nop<2, -2, ENDIANNESS_BIG>; -template class handler_entry_write_nop<3, 0, ENDIANNESS_LITTLE>; -template class handler_entry_write_nop<3, 0, ENDIANNESS_BIG>; -template class handler_entry_write_nop<3, -1, ENDIANNESS_LITTLE>; -template class handler_entry_write_nop<3, -1, ENDIANNESS_BIG>; -template class handler_entry_write_nop<3, -2, ENDIANNESS_LITTLE>; -template class handler_entry_write_nop<3, -2, ENDIANNESS_BIG>; -template class handler_entry_write_nop<3, -3, ENDIANNESS_LITTLE>; -template class handler_entry_write_nop<3, -3, ENDIANNESS_BIG>; +template class handler_entry_read_unmapped<0, 1>; +template class handler_entry_read_unmapped<0, 0>; +template class handler_entry_read_unmapped<1, 3>; +template class handler_entry_read_unmapped<1, 0>; +template class handler_entry_read_unmapped<1, -1>; +template class handler_entry_read_unmapped<2, 3>; +template class handler_entry_read_unmapped<2, 0>; +template class handler_entry_read_unmapped<2, -1>; +template class handler_entry_read_unmapped<2, -2>; +template class handler_entry_read_unmapped<3, 0>; +template class handler_entry_read_unmapped<3, -1>; +template class handler_entry_read_unmapped<3, -2>; +template class handler_entry_read_unmapped<3, -3>; + +template class handler_entry_write_unmapped<0, 1>; +template class handler_entry_write_unmapped<0, 0>; +template class handler_entry_write_unmapped<1, 3>; +template class handler_entry_write_unmapped<1, 0>; +template class handler_entry_write_unmapped<1, -1>; +template class handler_entry_write_unmapped<2, 3>; +template class handler_entry_write_unmapped<2, 0>; +template class handler_entry_write_unmapped<2, -1>; +template class handler_entry_write_unmapped<2, -2>; +template class handler_entry_write_unmapped<3, 0>; +template class handler_entry_write_unmapped<3, -1>; +template class handler_entry_write_unmapped<3, -2>; +template class handler_entry_write_unmapped<3, -3>; + + +template class handler_entry_read_nop<0, 1>; +template class handler_entry_read_nop<0, 0>; +template class handler_entry_read_nop<1, 3>; +template class handler_entry_read_nop<1, 0>; +template class handler_entry_read_nop<1, -1>; +template class handler_entry_read_nop<2, 3>; +template class handler_entry_read_nop<2, 0>; +template class handler_entry_read_nop<2, -1>; +template class handler_entry_read_nop<2, -2>; +template class handler_entry_read_nop<3, 0>; +template class handler_entry_read_nop<3, -1>; +template class handler_entry_read_nop<3, -2>; +template class handler_entry_read_nop<3, -3>; + +template class handler_entry_write_nop<0, 1>; +template class handler_entry_write_nop<0, 0>; +template class handler_entry_write_nop<1, 3>; +template class handler_entry_write_nop<1, 0>; +template class handler_entry_write_nop<1, -1>; +template class handler_entry_write_nop<2, 3>; +template class handler_entry_write_nop<2, 0>; +template class handler_entry_write_nop<2, -1>; +template class handler_entry_write_nop<2, -2>; +template class handler_entry_write_nop<3, 0>; +template class handler_entry_write_nop<3, -1>; +template class handler_entry_write_nop<3, -2>; +template class handler_entry_write_nop<3, -3>; diff --git a/src/emu/emumem_heun.h b/src/emu/emumem_heun.h index 02a79d825ab..69a97e84f37 100644 --- a/src/emu/emumem_heun.h +++ b/src/emu/emumem_heun.h @@ -1,34 +1,42 @@ // license:BSD-3-Clause // copyright-holders:Olivier Galibert +#ifndef MAME_EMU_EMUMEM_HEUN_H +#define MAME_EMU_EMUMEM_HEUN_H + +#pragma once // handler_entry_read_unmapped/handler_entry_write_unmapped // Logs an unmapped access -template<int Width, int AddrShift, int Endian> class handler_entry_read_unmapped : public handler_entry_read<Width, AddrShift, Endian> +template<int Width, int AddrShift> class handler_entry_read_unmapped : public handler_entry_read<Width, AddrShift> { public: - using uX = typename emu::detail::handler_entry_size<Width>::uX; - using inh = handler_entry_read<Width, AddrShift, Endian>; + using uX = emu::detail::handler_entry_size_t<Width>; - handler_entry_read_unmapped(address_space *space) : handler_entry_read<Width, AddrShift, Endian>(space, 0) {} + handler_entry_read_unmapped(address_space *space, u16 flags) : handler_entry_read<Width, AddrShift>(space, handler_entry::F_UNMAP | flags) {} ~handler_entry_read_unmapped() = default; - uX read(offs_t offset, uX mem_mask) override; + uX read(offs_t offset, uX mem_mask) const override; + uX read_interruptible(offs_t offset, uX mem_mask) const override; + std::pair<uX, u16> read_flags(offs_t offset, uX mem_mask) const override; + u16 lookup_flags(offs_t offset, uX mem_mask) const override; std::string name() const override; }; -template<int Width, int AddrShift, int Endian> class handler_entry_write_unmapped : public handler_entry_write<Width, AddrShift, Endian> +template<int Width, int AddrShift> class handler_entry_write_unmapped : public handler_entry_write<Width, AddrShift> { public: - using uX = typename emu::detail::handler_entry_size<Width>::uX; - using inh = handler_entry_write<Width, AddrShift, Endian>; + using uX = emu::detail::handler_entry_size_t<Width>; - handler_entry_write_unmapped(address_space *space) : handler_entry_write<Width, AddrShift, Endian>(space, 0) {} + handler_entry_write_unmapped(address_space *space, u16 flags) : handler_entry_write<Width, AddrShift>(space, handler_entry::F_UNMAP | flags) {} ~handler_entry_write_unmapped() = default; - void write(offs_t offset, uX data, uX mem_mask) override; + void write(offs_t offset, uX data, uX mem_mask) const override; + void write_interruptible(offs_t offset, uX data, uX mem_mask) const override; + u16 write_flags(offs_t offset, uX data, uX mem_mask) const override; + u16 lookup_flags(offs_t offset, uX mem_mask) const override; std::string name() const override; }; @@ -39,30 +47,36 @@ public: // Drops an unmapped access silently -template<int Width, int AddrShift, int Endian> class handler_entry_read_nop : public handler_entry_read<Width, AddrShift, Endian> +template<int Width, int AddrShift> class handler_entry_read_nop : public handler_entry_read<Width, AddrShift> { public: - using uX = typename emu::detail::handler_entry_size<Width>::uX; - using inh = handler_entry_read<Width, AddrShift, Endian>; + using uX = emu::detail::handler_entry_size_t<Width>; - handler_entry_read_nop(address_space *space) : handler_entry_read<Width, AddrShift, Endian>(space, 0) {} + handler_entry_read_nop(address_space *space, u16 flags) : handler_entry_read<Width, AddrShift>(space, flags) {} ~handler_entry_read_nop() = default; - uX read(offs_t offset, uX mem_mask) override; + uX read(offs_t offset, uX mem_mask) const override; + uX read_interruptible(offs_t offset, uX mem_mask) const override; + std::pair<uX, u16> read_flags(offs_t offset, uX mem_mask) const override; + u16 lookup_flags(offs_t offset, uX mem_mask) const override; std::string name() const override; }; -template<int Width, int AddrShift, int Endian> class handler_entry_write_nop : public handler_entry_write<Width, AddrShift, Endian> +template<int Width, int AddrShift> class handler_entry_write_nop : public handler_entry_write<Width, AddrShift> { public: - using uX = typename emu::detail::handler_entry_size<Width>::uX; - using inh = handler_entry_write<Width, AddrShift, Endian>; + using uX = emu::detail::handler_entry_size_t<Width>; - handler_entry_write_nop(address_space *space) : handler_entry_write<Width, AddrShift, Endian>(space, 0) {} + handler_entry_write_nop(address_space *space, u16 flags) : handler_entry_write<Width, AddrShift>(space, flags) {} ~handler_entry_write_nop() = default; - void write(offs_t offset, uX data, uX mem_mask) override; + void write(offs_t offset, uX data, uX mem_mask) const override; + void write_interruptible(offs_t offset, uX data, uX mem_mask) const override; + u16 write_flags(offs_t offset, uX data, uX mem_mask) const override; + u16 lookup_flags(offs_t offset, uX mem_mask) const override; std::string name() const override; }; + +#endif // MAME_EMU_EMUMEM_HEUN_H diff --git a/src/emu/emumem_hws.cpp b/src/emu/emumem_hws.cpp new file mode 100644 index 00000000000..9aae20d9f4c --- /dev/null +++ b/src/emu/emumem_hws.cpp @@ -0,0 +1,311 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert + +#include "emu.h" +#include "emumem_hep.h" +#include "emumem_hws.h" + +template<int Width, int AddrShift> emu::detail::handler_entry_size_t<Width> handler_entry_read_before_time<Width, AddrShift>::read(offs_t offset, uX mem_mask) const +{ + return this->m_next->read(offset, mem_mask); +} + +template<int Width, int AddrShift> emu::detail::handler_entry_size_t<Width> handler_entry_read_before_time<Width, AddrShift>::read_interruptible(offs_t offset, uX mem_mask) const +{ + u64 tc = m_cpu.total_cycles(); + u64 ac = m_ws(offset, tc); + + if(ac < tc) + ac = tc; + + if(m_cpu.access_before_time(ac, tc)) + return 0; + + return this->m_next->read_interruptible(offset, mem_mask); +} + +template<int Width, int AddrShift> std::pair<emu::detail::handler_entry_size_t<Width>, u16> handler_entry_read_before_time<Width, AddrShift>::read_flags(offs_t offset, uX mem_mask) const +{ + return this->m_next->read_flags(offset, mem_mask); +} + +template<int Width, int AddrShift> u16 handler_entry_read_before_time<Width, AddrShift>::lookup_flags(offs_t offset, uX mem_mask) const +{ + return this->m_next->lookup_flags(offset, mem_mask); +} + +template<int Width, int AddrShift> std::string handler_entry_read_before_time<Width, AddrShift>::name() const +{ + return util::string_format("(bt %s) %s", m_ws.name(), this->m_next->name()); +} + +template<int Width, int AddrShift> handler_entry_read_before_time<Width, AddrShift> *handler_entry_read_before_time<Width, AddrShift>::instantiate(handler_entry_read<Width, AddrShift> *next) const +{ + return new handler_entry_read_before_time<Width, AddrShift>(this->m_space, this->m_mph, next, m_ws); +} + + +template<int Width, int AddrShift> void handler_entry_write_before_time<Width, AddrShift>::write(offs_t offset, uX data, uX mem_mask) const +{ + this->m_next->write(offset, data, mem_mask); +} + +template<int Width, int AddrShift> void handler_entry_write_before_time<Width, AddrShift>::write_interruptible(offs_t offset, uX data, uX mem_mask) const +{ + u64 tc = m_cpu.total_cycles(); + u64 ac = m_ws(offset, tc); + + if(ac < tc) + ac = tc; + + if(m_cpu.access_before_time(ac, tc)) + return; + this->m_next->write_interruptible(offset, data, mem_mask); +} + +template<int Width, int AddrShift> u16 handler_entry_write_before_time<Width, AddrShift>::write_flags(offs_t offset, uX data, uX mem_mask) const +{ + return this->m_next->write_flags(offset, data, mem_mask); +} + +template<int Width, int AddrShift> u16 handler_entry_write_before_time<Width, AddrShift>::lookup_flags(offs_t offset, uX mem_mask) const +{ + return this->m_next->lookup_flags(offset, mem_mask); +} + +template<int Width, int AddrShift> std::string handler_entry_write_before_time<Width, AddrShift>::name() const +{ + return util::string_format("(bt %s) %s", m_ws.name(), this->m_next->name()); +} + + +template<int Width, int AddrShift> handler_entry_write_before_time<Width, AddrShift> *handler_entry_write_before_time<Width, AddrShift>::instantiate(handler_entry_write<Width, AddrShift> *next) const +{ + return new handler_entry_write_before_time<Width, AddrShift>(this->m_space, this->m_mph, next, m_ws); +} + + + +template class handler_entry_read_before_time<0, 1>; +template class handler_entry_read_before_time<0, 0>; +template class handler_entry_read_before_time<1, 3>; +template class handler_entry_read_before_time<1, 0>; +template class handler_entry_read_before_time<1, -1>; +template class handler_entry_read_before_time<2, 3>; +template class handler_entry_read_before_time<2, 0>; +template class handler_entry_read_before_time<2, -1>; +template class handler_entry_read_before_time<2, -2>; +template class handler_entry_read_before_time<3, 0>; +template class handler_entry_read_before_time<3, -1>; +template class handler_entry_read_before_time<3, -2>; +template class handler_entry_read_before_time<3, -3>; + +template class handler_entry_write_before_time<0, 1>; +template class handler_entry_write_before_time<0, 0>; +template class handler_entry_write_before_time<1, 3>; +template class handler_entry_write_before_time<1, 0>; +template class handler_entry_write_before_time<1, -1>; +template class handler_entry_write_before_time<2, 3>; +template class handler_entry_write_before_time<2, 0>; +template class handler_entry_write_before_time<2, -1>; +template class handler_entry_write_before_time<2, -2>; +template class handler_entry_write_before_time<3, 0>; +template class handler_entry_write_before_time<3, -1>; +template class handler_entry_write_before_time<3, -2>; +template class handler_entry_write_before_time<3, -3>; + + + +template<int Width, int AddrShift> emu::detail::handler_entry_size_t<Width> handler_entry_read_before_delay<Width, AddrShift>::read(offs_t offset, uX mem_mask) const +{ + return this->m_next->read(offset, mem_mask); +} + +template<int Width, int AddrShift> emu::detail::handler_entry_size_t<Width> handler_entry_read_before_delay<Width, AddrShift>::read_interruptible(offs_t offset, uX mem_mask) const +{ + if(m_cpu.access_before_delay(m_ws(offset), this)) + return 0; + + return this->m_next->read_interruptible(offset, mem_mask); +} + +template<int Width, int AddrShift> std::pair<emu::detail::handler_entry_size_t<Width>, u16> handler_entry_read_before_delay<Width, AddrShift>::read_flags(offs_t offset, uX mem_mask) const +{ + return this->m_next->read_flags(offset, mem_mask); +} + +template<int Width, int AddrShift> u16 handler_entry_read_before_delay<Width, AddrShift>::lookup_flags(offs_t offset, uX mem_mask) const +{ + return this->m_next->lookup_flags(offset, mem_mask); +} + +template<int Width, int AddrShift> std::string handler_entry_read_before_delay<Width, AddrShift>::name() const +{ + return util::string_format("(bd %s) %s", m_ws.name(), this->m_next->name()); +} + +template<int Width, int AddrShift> handler_entry_read_before_delay<Width, AddrShift> *handler_entry_read_before_delay<Width, AddrShift>::instantiate(handler_entry_read<Width, AddrShift> *next) const +{ + return new handler_entry_read_before_delay<Width, AddrShift>(this->m_space, this->m_mph, next, m_ws); +} + + +template<int Width, int AddrShift> void handler_entry_write_before_delay<Width, AddrShift>::write(offs_t offset, uX data, uX mem_mask) const +{ + this->m_next->write(offset, data, mem_mask); +} + +template<int Width, int AddrShift> void handler_entry_write_before_delay<Width, AddrShift>::write_interruptible(offs_t offset, uX data, uX mem_mask) const +{ + if(m_cpu.access_before_delay(m_ws(offset), this)) + return; + this->m_next->write_interruptible(offset, data, mem_mask); +} + +template<int Width, int AddrShift> u16 handler_entry_write_before_delay<Width, AddrShift>::write_flags(offs_t offset, uX data, uX mem_mask) const +{ + return this->m_next->write_flags(offset, data, mem_mask); +} + +template<int Width, int AddrShift> u16 handler_entry_write_before_delay<Width, AddrShift>::lookup_flags(offs_t offset, uX mem_mask) const +{ + return this->m_next->lookup_flags(offset, mem_mask); +} + +template<int Width, int AddrShift> std::string handler_entry_write_before_delay<Width, AddrShift>::name() const +{ + return util::string_format("(bd %s) %s", m_ws.name(), this->m_next->name()); +} + + +template<int Width, int AddrShift> handler_entry_write_before_delay<Width, AddrShift> *handler_entry_write_before_delay<Width, AddrShift>::instantiate(handler_entry_write<Width, AddrShift> *next) const +{ + return new handler_entry_write_before_delay<Width, AddrShift>(this->m_space, this->m_mph, next, m_ws); +} + + + +template class handler_entry_read_before_delay<0, 1>; +template class handler_entry_read_before_delay<0, 0>; +template class handler_entry_read_before_delay<1, 3>; +template class handler_entry_read_before_delay<1, 0>; +template class handler_entry_read_before_delay<1, -1>; +template class handler_entry_read_before_delay<2, 3>; +template class handler_entry_read_before_delay<2, 0>; +template class handler_entry_read_before_delay<2, -1>; +template class handler_entry_read_before_delay<2, -2>; +template class handler_entry_read_before_delay<3, 0>; +template class handler_entry_read_before_delay<3, -1>; +template class handler_entry_read_before_delay<3, -2>; +template class handler_entry_read_before_delay<3, -3>; + +template class handler_entry_write_before_delay<0, 1>; +template class handler_entry_write_before_delay<0, 0>; +template class handler_entry_write_before_delay<1, 3>; +template class handler_entry_write_before_delay<1, 0>; +template class handler_entry_write_before_delay<1, -1>; +template class handler_entry_write_before_delay<2, 3>; +template class handler_entry_write_before_delay<2, 0>; +template class handler_entry_write_before_delay<2, -1>; +template class handler_entry_write_before_delay<2, -2>; +template class handler_entry_write_before_delay<3, 0>; +template class handler_entry_write_before_delay<3, -1>; +template class handler_entry_write_before_delay<3, -2>; +template class handler_entry_write_before_delay<3, -3>; + + + +template<int Width, int AddrShift> emu::detail::handler_entry_size_t<Width> handler_entry_read_after_delay<Width, AddrShift>::read(offs_t offset, uX mem_mask) const +{ + return this->m_next->read(offset, mem_mask); +} + +template<int Width, int AddrShift> emu::detail::handler_entry_size_t<Width> handler_entry_read_after_delay<Width, AddrShift>::read_interruptible(offs_t offset, uX mem_mask) const +{ + auto r = this->m_next->read_interruptible(offset, mem_mask); + m_cpu.access_after_delay(m_ws(offset)); + return r; +} + +template<int Width, int AddrShift> std::pair<emu::detail::handler_entry_size_t<Width>, u16> handler_entry_read_after_delay<Width, AddrShift>::read_flags(offs_t offset, uX mem_mask) const +{ + return this->m_next->read_flags(offset, mem_mask); +} + +template<int Width, int AddrShift> u16 handler_entry_read_after_delay<Width, AddrShift>::lookup_flags(offs_t offset, uX mem_mask) const +{ + return this->m_next->lookup_flags(offset, mem_mask); +} + +template<int Width, int AddrShift> std::string handler_entry_read_after_delay<Width, AddrShift>::name() const +{ + return util::string_format("(ad %s) %s", m_ws.name(), this->m_next->name()); +} + +template<int Width, int AddrShift> handler_entry_read_after_delay<Width, AddrShift> *handler_entry_read_after_delay<Width, AddrShift>::instantiate(handler_entry_read<Width, AddrShift> *next) const +{ + return new handler_entry_read_after_delay<Width, AddrShift>(this->m_space, this->m_mph, next, m_ws); +} + + +template<int Width, int AddrShift> void handler_entry_write_after_delay<Width, AddrShift>::write(offs_t offset, uX data, uX mem_mask) const +{ + this->m_next->write(offset, data, mem_mask); +} + +template<int Width, int AddrShift> void handler_entry_write_after_delay<Width, AddrShift>::write_interruptible(offs_t offset, uX data, uX mem_mask) const +{ + this->m_next->write_interruptible(offset, data, mem_mask); + m_cpu.access_after_delay(m_ws(offset)); +} + +template<int Width, int AddrShift> u16 handler_entry_write_after_delay<Width, AddrShift>::write_flags(offs_t offset, uX data, uX mem_mask) const +{ + return this->m_next->write_flags(offset, data, mem_mask); +} + +template<int Width, int AddrShift> u16 handler_entry_write_after_delay<Width, AddrShift>::lookup_flags(offs_t offset, uX mem_mask) const +{ + return this->m_next->lookup_flags(offset, mem_mask); +} + +template<int Width, int AddrShift> std::string handler_entry_write_after_delay<Width, AddrShift>::name() const +{ + return util::string_format("(ad %s) %s", m_ws.name(), this->m_next->name()); +} + + +template<int Width, int AddrShift> handler_entry_write_after_delay<Width, AddrShift> *handler_entry_write_after_delay<Width, AddrShift>::instantiate(handler_entry_write<Width, AddrShift> *next) const +{ + return new handler_entry_write_after_delay<Width, AddrShift>(this->m_space, this->m_mph, next, m_ws); +} + + + +template class handler_entry_read_after_delay<0, 1>; +template class handler_entry_read_after_delay<0, 0>; +template class handler_entry_read_after_delay<1, 3>; +template class handler_entry_read_after_delay<1, 0>; +template class handler_entry_read_after_delay<1, -1>; +template class handler_entry_read_after_delay<2, 3>; +template class handler_entry_read_after_delay<2, 0>; +template class handler_entry_read_after_delay<2, -1>; +template class handler_entry_read_after_delay<2, -2>; +template class handler_entry_read_after_delay<3, 0>; +template class handler_entry_read_after_delay<3, -1>; +template class handler_entry_read_after_delay<3, -2>; +template class handler_entry_read_after_delay<3, -3>; + +template class handler_entry_write_after_delay<0, 1>; +template class handler_entry_write_after_delay<0, 0>; +template class handler_entry_write_after_delay<1, 3>; +template class handler_entry_write_after_delay<1, 0>; +template class handler_entry_write_after_delay<1, -1>; +template class handler_entry_write_after_delay<2, 3>; +template class handler_entry_write_after_delay<2, 0>; +template class handler_entry_write_after_delay<2, -1>; +template class handler_entry_write_after_delay<2, -2>; +template class handler_entry_write_after_delay<3, 0>; +template class handler_entry_write_after_delay<3, -1>; +template class handler_entry_write_after_delay<3, -2>; +template class handler_entry_write_after_delay<3, -3>; diff --git a/src/emu/emumem_hws.h b/src/emu/emumem_hws.h new file mode 100644 index 00000000000..baaad6c7a07 --- /dev/null +++ b/src/emu/emumem_hws.h @@ -0,0 +1,162 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert +#ifndef MAME_EMU_EMUMEM_HWS_H +#define MAME_EMU_EMUMEM_HWS_H + +#pragma once + +// handler_entry_read_before_time/handler_entry_write_before_time +// handler_entry_read_before_delay/handler_entry_write_before_delay +// handler_entry_read_after_delay/handler_entry_write_after_delay + +// handlers which handle the different wait state cases + +template<int Width, int AddrShift> class handler_entry_read_before_time : public handler_entry_read_passthrough<Width, AddrShift> +{ +public: + using uX = emu::detail::handler_entry_size_t<Width>; + + handler_entry_read_before_time(address_space *space, emu::detail::memory_passthrough_handler_impl &mph, const ws_time_delegate &ws) : handler_entry_read_passthrough<Width, AddrShift>(space, mph, 13), m_ws(ws), m_cpu(downcast<cpu_device &>(space->device())) {} + ~handler_entry_read_before_time() = default; + + uX read(offs_t offset, uX mem_mask) const override; + uX read_interruptible(offs_t offset, uX mem_mask) const override; + std::pair<uX, u16> read_flags(offs_t offset, uX mem_mask) const override; + u16 lookup_flags(offs_t offset, uX mem_mask) const override; + + std::string name() const override; + + handler_entry_read_before_time<Width, AddrShift> *instantiate(handler_entry_read<Width, AddrShift> *next) const override; + +protected: + ws_time_delegate m_ws; + cpu_device &m_cpu; + + handler_entry_read_before_time(address_space *space, emu::detail::memory_passthrough_handler_impl &mph, handler_entry_read<Width, AddrShift> *next, const ws_time_delegate &ws) : handler_entry_read_passthrough<Width, AddrShift>(space, mph, 13, next), m_ws(ws), m_cpu(downcast<cpu_device &>(space->device())) {} +}; + +template<int Width, int AddrShift> class handler_entry_write_before_time : public handler_entry_write_passthrough<Width, AddrShift> +{ +public: + using uX = emu::detail::handler_entry_size_t<Width>; + + handler_entry_write_before_time(address_space *space, emu::detail::memory_passthrough_handler_impl &mph, const ws_time_delegate &ws) : handler_entry_write_passthrough<Width, AddrShift>(space, mph, 13), m_ws(ws), m_cpu(downcast<cpu_device &>(space->device())) {} + ~handler_entry_write_before_time() = default; + + void write(offs_t offset, uX data, uX mem_mask) const override; + void write_interruptible(offs_t offset, uX data, uX mem_mask) const override; + u16 write_flags(offs_t offset, uX data, uX mem_mask) const override; + u16 lookup_flags(offs_t offset, uX mem_mask) const override; + + std::string name() const override; + + handler_entry_write_before_time<Width, AddrShift> *instantiate(handler_entry_write<Width, AddrShift> *next) const override; + +protected: + ws_time_delegate m_ws; + cpu_device &m_cpu; + + handler_entry_write_before_time(address_space *space, emu::detail::memory_passthrough_handler_impl &mph, handler_entry_write<Width, AddrShift> *next, const ws_time_delegate &ws) : handler_entry_write_passthrough<Width, AddrShift>(space, mph, 13, next), m_ws(ws), m_cpu(downcast<cpu_device &>(space->device())) {} +}; + + + +template<int Width, int AddrShift> class handler_entry_read_before_delay : public handler_entry_read_passthrough<Width, AddrShift> +{ +public: + using uX = emu::detail::handler_entry_size_t<Width>; + + handler_entry_read_before_delay(address_space *space, emu::detail::memory_passthrough_handler_impl &mph, const ws_delay_delegate &ws) : handler_entry_read_passthrough<Width, AddrShift>(space, mph, 13), m_ws(ws), m_cpu(downcast<cpu_device &>(space->device())) {} + ~handler_entry_read_before_delay() = default; + + uX read(offs_t offset, uX mem_mask) const override; + uX read_interruptible(offs_t offset, uX mem_mask) const override; + std::pair<uX, u16> read_flags(offs_t offset, uX mem_mask) const override; + u16 lookup_flags(offs_t offset, uX mem_mask) const override; + + std::string name() const override; + + handler_entry_read_before_delay<Width, AddrShift> *instantiate(handler_entry_read<Width, AddrShift> *next) const override; + +protected: + ws_delay_delegate m_ws; + cpu_device &m_cpu; + + handler_entry_read_before_delay(address_space *space, emu::detail::memory_passthrough_handler_impl &mph, handler_entry_read<Width, AddrShift> *next, const ws_delay_delegate &ws) : handler_entry_read_passthrough<Width, AddrShift>(space, mph, 13, next), m_ws(ws), m_cpu(downcast<cpu_device &>(space->device())) {} +}; + +template<int Width, int AddrShift> class handler_entry_write_before_delay : public handler_entry_write_passthrough<Width, AddrShift> +{ +public: + using uX = emu::detail::handler_entry_size_t<Width>; + + handler_entry_write_before_delay(address_space *space, emu::detail::memory_passthrough_handler_impl &mph, const ws_delay_delegate &ws) : handler_entry_write_passthrough<Width, AddrShift>(space, mph, 11), m_ws(ws), m_cpu(downcast<cpu_device &>(space->device())) {} + ~handler_entry_write_before_delay() = default; + + void write(offs_t offset, uX data, uX mem_mask) const override; + void write_interruptible(offs_t offset, uX data, uX mem_mask) const override; + u16 write_flags(offs_t offset, uX data, uX mem_mask) const override; + u16 lookup_flags(offs_t offset, uX mem_mask) const override; + + std::string name() const override; + + handler_entry_write_before_delay<Width, AddrShift> *instantiate(handler_entry_write<Width, AddrShift> *next) const override; + +protected: + ws_delay_delegate m_ws; + cpu_device &m_cpu; + + handler_entry_write_before_delay(address_space *space, emu::detail::memory_passthrough_handler_impl &mph, handler_entry_write<Width, AddrShift> *next, const ws_delay_delegate &ws) : handler_entry_write_passthrough<Width, AddrShift>(space, mph, 11, next), m_ws(ws), m_cpu(downcast<cpu_device &>(space->device())) {} +}; + + + +template<int Width, int AddrShift> class handler_entry_read_after_delay : public handler_entry_read_passthrough<Width, AddrShift> +{ +public: + using uX = emu::detail::handler_entry_size_t<Width>; + + handler_entry_read_after_delay(address_space *space, emu::detail::memory_passthrough_handler_impl &mph, const ws_delay_delegate &ws) : handler_entry_read_passthrough<Width, AddrShift>(space, mph, 9), m_ws(ws), m_cpu(downcast<cpu_device &>(space->device())) {} + ~handler_entry_read_after_delay() = default; + + uX read(offs_t offset, uX mem_mask) const override; + uX read_interruptible(offs_t offset, uX mem_mask) const override; + std::pair<uX, u16> read_flags(offs_t offset, uX mem_mask) const override; + u16 lookup_flags(offs_t offset, uX mem_mask) const override; + + std::string name() const override; + + handler_entry_read_after_delay<Width, AddrShift> *instantiate(handler_entry_read<Width, AddrShift> *next) const override; + +protected: + ws_delay_delegate m_ws; + cpu_device &m_cpu; + + handler_entry_read_after_delay(address_space *space, emu::detail::memory_passthrough_handler_impl &mph, handler_entry_read<Width, AddrShift> *next, const ws_delay_delegate &ws) : handler_entry_read_passthrough<Width, AddrShift>(space, mph, 9, next), m_ws(ws), m_cpu(downcast<cpu_device &>(space->device())) {} +}; + +template<int Width, int AddrShift> class handler_entry_write_after_delay : public handler_entry_write_passthrough<Width, AddrShift> +{ +public: + using uX = emu::detail::handler_entry_size_t<Width>; + + handler_entry_write_after_delay(address_space *space, emu::detail::memory_passthrough_handler_impl &mph, const ws_delay_delegate &ws) : handler_entry_write_passthrough<Width, AddrShift>(space, mph, 9), m_ws(ws), m_cpu(downcast<cpu_device &>(space->device())) {} + ~handler_entry_write_after_delay() = default; + + void write(offs_t offset, uX data, uX mem_mask) const override; + void write_interruptible(offs_t offset, uX data, uX mem_mask) const override; + u16 write_flags(offs_t offset, uX data, uX mem_mask) const override; + u16 lookup_flags(offs_t offset, uX mem_mask) const override; + + std::string name() const override; + + handler_entry_write_after_delay<Width, AddrShift> *instantiate(handler_entry_write<Width, AddrShift> *next) const override; + +protected: + ws_delay_delegate m_ws; + cpu_device &m_cpu; + + handler_entry_write_after_delay(address_space *space, emu::detail::memory_passthrough_handler_impl &mph, handler_entry_write<Width, AddrShift> *next, const ws_delay_delegate &ws) : handler_entry_write_passthrough<Width, AddrShift>(space, mph, 9, next), m_ws(ws), m_cpu(downcast<cpu_device &>(space->device())) {} +}; + +#endif // MAME_EMU_EMUMEM_HWS_H diff --git a/src/emu/emumem_mud.cpp b/src/emu/emumem_mud.cpp index d1583b58366..4c52b5a1301 100644 --- a/src/emu/emumem_mud.cpp +++ b/src/emu/emumem_mud.cpp @@ -35,22 +35,21 @@ template<> u8 mask_to_ukey<u64>(u64 mask) (mask & 0x00000000000000ff ? 0x01 : 0x00); } -template<int Width, int AddrShift, int Endian> memory_units_descriptor<Width, AddrShift, Endian>::memory_units_descriptor(u8 access_width, u8 access_endian, handler_entry *handler, offs_t addrstart, offs_t addrend, offs_t mask, typename emu::detail::handler_entry_size<Width>::uX unitmask, int cswidth) : m_handler(handler), m_access_width(access_width), m_access_endian(access_endian) +template<int Width, int AddrShift> memory_units_descriptor<Width, AddrShift>::memory_units_descriptor(u8 access_width, endianness_t access_endian, handler_entry *handler, offs_t addrstart, offs_t addrend, offs_t mask, emu::detail::handler_entry_size_t<Width> unitmask, int cswidth) : m_handler(handler), m_access_width(access_width), m_access_endian(access_endian) { - constexpr u32 NATIVE_MASK = Width + AddrShift >= 0 ? make_bitmask<u32>(Width + AddrShift) : 0; u32 bits_per_access = 8 << access_width; + constexpr u32 NATIVE_MASK = Width + AddrShift >= 0 ? make_bitmask<u32>(Width + AddrShift) : 0; // Compute the real base addresses m_addrstart = addrstart & ~NATIVE_MASK; m_addrend = addrend & ~NATIVE_MASK; - // Compute the masks and the keys std::array<uX, 4> umasks; umasks.fill(unitmask); uX smask, emask; - if(Endian == ENDIANNESS_BIG) { + if(access_endian == ENDIANNESS_BIG) { smask = make_bitmask<uX>(8*sizeof(uX) - ((addrstart - m_addrstart) << (3 - AddrShift))); emask = ~make_bitmask<uX>(8*sizeof(uX) - ((addrend - m_addrend + 1) << (3 - AddrShift))); } else { @@ -87,7 +86,7 @@ template<int Width, int AddrShift, int Endian> memory_units_descriptor<Width, Ad generate(m_keymap[i], unitmask, umasks[i], cswidth, bits_per_access, base_shift, shift, active_count); } -template<int Width, int AddrShift, int Endian> void memory_units_descriptor<Width, AddrShift, Endian>::generate(u8 ukey, typename emu::detail::handler_entry_size<Width>::uX gumask, typename emu::detail::handler_entry_size<Width>::uX umask, u32 cswidth, u32 bits_per_access, u8 base_shift, s8 shift, u32 active_count) +template<int Width, int AddrShift> void memory_units_descriptor<Width, AddrShift>::generate(u8 ukey, emu::detail::handler_entry_size_t<Width> gumask, emu::detail::handler_entry_size_t<Width> umask, u32 cswidth, u32 bits_per_access, u8 base_shift, s8 shift, u32 active_count) { auto &entries = m_entries_for_key[ukey]; @@ -104,30 +103,21 @@ template<int Width, int AddrShift, int Endian> void memory_units_descriptor<Widt uX numask = dmask << i; if(umask & numask) { uX amask = csmask << (i & ~(cswidth - 1)); - entries.emplace_back(entry{ amask, numask, shift, u8(i), u8(Endian == ENDIANNESS_BIG ? active_count - 1 - offset : offset) }); + entries.emplace_back(entry{ amask, numask, shift, u8(i), u8(m_access_endian == ENDIANNESS_BIG ? active_count - 1 - offset : offset) }); } if(gumask & numask) offset ++; } } -template class memory_units_descriptor<1, 3, ENDIANNESS_LITTLE>; -template class memory_units_descriptor<1, 3, ENDIANNESS_BIG>; -template class memory_units_descriptor<1, 0, ENDIANNESS_LITTLE>; -template class memory_units_descriptor<1, 0, ENDIANNESS_BIG>; -template class memory_units_descriptor<1, -1, ENDIANNESS_LITTLE>; -template class memory_units_descriptor<1, -1, ENDIANNESS_BIG>; -template class memory_units_descriptor<2, 0, ENDIANNESS_LITTLE>; -template class memory_units_descriptor<2, 0, ENDIANNESS_BIG>; -template class memory_units_descriptor<2, -1, ENDIANNESS_LITTLE>; -template class memory_units_descriptor<2, -1, ENDIANNESS_BIG>; -template class memory_units_descriptor<2, -2, ENDIANNESS_LITTLE>; -template class memory_units_descriptor<2, -2, ENDIANNESS_BIG>; -template class memory_units_descriptor<3, 0, ENDIANNESS_LITTLE>; -template class memory_units_descriptor<3, 0, ENDIANNESS_BIG>; -template class memory_units_descriptor<3, -1, ENDIANNESS_LITTLE>; -template class memory_units_descriptor<3, -1, ENDIANNESS_BIG>; -template class memory_units_descriptor<3, -2, ENDIANNESS_LITTLE>; -template class memory_units_descriptor<3, -2, ENDIANNESS_BIG>; -template class memory_units_descriptor<3, -3, ENDIANNESS_LITTLE>; -template class memory_units_descriptor<3, -3, ENDIANNESS_BIG>; +template class memory_units_descriptor<1, 3>; +template class memory_units_descriptor<1, 0>; +template class memory_units_descriptor<1, -1>; +template class memory_units_descriptor<2, 3>; +template class memory_units_descriptor<2, 0>; +template class memory_units_descriptor<2, -1>; +template class memory_units_descriptor<2, -2>; +template class memory_units_descriptor<3, 0>; +template class memory_units_descriptor<3, -1>; +template class memory_units_descriptor<3, -2>; +template class memory_units_descriptor<3, -3>; diff --git a/src/emu/emumem_mud.h b/src/emu/emumem_mud.h index b5347ee82e2..4a8b6509aa0 100644 --- a/src/emu/emumem_mud.h +++ b/src/emu/emumem_mud.h @@ -3,9 +3,9 @@ // Descriptors for subunit support -template<int Width, int AddrShift, int Endian> class memory_units_descriptor { +template<int Width, int AddrShift> class memory_units_descriptor { public: - using uX = typename emu::detail::handler_entry_size<Width>::uX; + using uX = emu::detail::handler_entry_size_t<Width>; struct entry { uX m_amask; @@ -15,7 +15,7 @@ public: u8 m_offset; }; - memory_units_descriptor(u8 access_width, u8 access_endian, handler_entry *handler, offs_t addrstart, offs_t addrend, offs_t mask, uX unitmask, int cswidth); + memory_units_descriptor(u8 access_width, endianness_t access_endian, handler_entry *handler, offs_t addrstart, offs_t addrend, offs_t mask, uX unitmask, int cswidth); offs_t get_handler_start() const { return m_handler_start; } offs_t get_handler_mask() const { return m_handler_mask; } @@ -24,7 +24,7 @@ public: const std::vector<entry> &get_entries_for_key(u8 key) const { return m_entries_for_key.find(key)->second; } u8 get_subunit_width() const { return m_access_width; } - u8 get_subunit_endian() const { return m_access_endian; } + endianness_t get_subunit_endian() const { return m_access_endian; } void set_subunit_handler(handler_entry *handler) { m_handler = handler; } handler_entry *get_subunit_handler() const { return m_handler; } @@ -35,7 +35,7 @@ private: handler_entry *m_handler; std::array<u8, 4> m_keymap; u8 m_access_width; - u8 m_access_endian; + endianness_t m_access_endian; void generate(u8 ukey, uX gumask, uX umask, u32 cswidth, u32 bits_per_access, u8 base_shift, s8 shift, u32 active_count); }; diff --git a/src/emu/emumem_mview.cpp b/src/emu/emumem_mview.cpp new file mode 100644 index 00000000000..8d16f8f439a --- /dev/null +++ b/src/emu/emumem_mview.cpp @@ -0,0 +1,1112 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles,Olivier Galibert +/*************************************************************************** + + emumem.cpp + + Functions which handle device memory access. + Memory view specific functions + +***************************************************************************/ + +#include "emu.h" +#include <list> +#include <map> +#include "emuopts.h" +#include "debug/debugcpu.h" + +#include "emumem_mud.h" +#include "emumem_hea.h" +#include "emumem_hem.h" +#include "emumem_hedp.h" +#include "emumem_heun.h" +#include "emumem_heu.h" +#include "emumem_hedr.h" +#include "emumem_hedw.h" +#include "emumem_hep.h" +#include "emumem_het.h" +#include "emumem_hws.h" + +#define VERBOSE 0 + +#if VERBOSE +template <typename Format, typename... Params> static void VPRINTF(Format &&fmt, Params &&...args) +{ + util::stream_format(std::cerr, std::forward<Format>(fmt), std::forward<Params>(args)...); +} +#else +template <typename Format, typename... Params> static void VPRINTF(Format &&, Params &&...) {} +#endif + +using emu::detail::handler_width_v; + +address_map_entry &memory_view::memory_view_entry::operator()(offs_t start, offs_t end) +{ + return (*m_map)(start, end); +} + +template<int Level, int Width, int AddrShift> +class memory_view_entry_specific : public memory_view::memory_view_entry +{ + using uX = emu::detail::handler_entry_size_t<Width>; + using NativeType = uX; + + // constants describing the native size + static constexpr u32 NATIVE_BYTES = 1 << Width; + static constexpr u32 NATIVE_STEP = AddrShift >= 0 ? NATIVE_BYTES << iabs(AddrShift) : NATIVE_BYTES >> iabs(AddrShift); + static constexpr u32 NATIVE_MASK = NATIVE_STEP - 1; + static constexpr u32 NATIVE_BITS = 8 * NATIVE_BYTES; + + static constexpr offs_t offset_to_byte(offs_t offset) { return AddrShift < 0 ? offset << iabs(AddrShift) : offset >> iabs(AddrShift); } + +public: + memory_view_entry_specific(const address_space_config &config, memory_manager &manager, memory_view &view, int id) : memory_view_entry(config, manager, view, id) { + } + + virtual ~memory_view_entry_specific() = default; + + handler_entry_read <Width, AddrShift> *r() { return static_cast<handler_entry_read <Width, AddrShift> *>(m_view.m_handler_read); } + handler_entry_write<Width, AddrShift> *w() { return static_cast<handler_entry_write<Width, AddrShift> *>(m_view.m_handler_write); } + + void invalidate_caches(read_or_write readorwrite) { return m_view.m_space->invalidate_caches(readorwrite); } + + virtual void populate_from_map(address_map *map = nullptr) override; + + using address_space_installer::install_read_tap; + using address_space_installer::install_write_tap; + using address_space_installer::install_readwrite_tap; + + virtual memory_passthrough_handler install_read_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, uX &data, uX mem_mask)> tap, memory_passthrough_handler *mph) override; + virtual memory_passthrough_handler install_write_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, uX &data, uX mem_mask)> tap, memory_passthrough_handler *mph) override; + virtual memory_passthrough_handler install_readwrite_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, uX &data, uX mem_mask)> tapr, std::function<void (offs_t offset, uX &data, uX mem_mask)> tapw, memory_passthrough_handler *mph) override; + + virtual void unmap_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, read_or_write readorwrite, bool quiet) override; + virtual void install_ram_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, read_or_write readorwrite, void *baseptr) override; + virtual void install_bank_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, memory_bank *rbank, memory_bank *wbank) override; + virtual void install_view(offs_t addrstart, offs_t addrend, offs_t addrmirror, memory_view &view) override; + virtual void install_readwrite_port(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, std::string rtag, std::string wtag) override; + virtual void install_device_delegate(offs_t addrstart, offs_t addrend, device_t &device, address_map_constructor &map, u64 unitmask, int cswidth, u16 flags) override; + + void install_read_before_time(offs_t addrstart, offs_t addrend, offs_t addrmirror, ws_time_delegate ws) override; + void install_read_before_delay(offs_t addrstart, offs_t addrend, offs_t addrmirror, ws_delay_delegate ws) override; + void install_read_after_delay(offs_t addrstart, offs_t addrend, offs_t addrmirror, ws_delay_delegate ws) override; + + void install_write_before_time(offs_t addrstart, offs_t addrend, offs_t addrmirror, ws_time_delegate ws) override; + void install_write_before_delay(offs_t addrstart, offs_t addrend, offs_t addrmirror, ws_delay_delegate ws) override; + void install_write_after_delay(offs_t addrstart, offs_t addrend, offs_t addrmirror, ws_delay_delegate ws) override; + + void install_readwrite_before_time(offs_t addrstart, offs_t addrend, offs_t addrmirror, ws_time_delegate ws) override; + void install_readwrite_before_delay(offs_t addrstart, offs_t addrend, offs_t addrmirror, ws_delay_delegate ws) override; + void install_readwrite_after_delay(offs_t addrstart, offs_t addrend, offs_t addrmirror, ws_delay_delegate ws) override; + + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8m_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8m_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16m_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16m_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32m_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32m_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64m_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64m_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8s_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8s_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16s_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16s_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32s_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32s_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64s_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64s_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8sm_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16sm_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32sm_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64sm_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8mo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16mo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32mo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64mo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8smo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16smo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32smo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64smo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } + + template<typename READ> + void install_read_handler_impl(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, u16 flags, READ &handler_r) + { + try { handler_r.resolve(); } + catch (const binding_type_exception &) { + osd_printf_error("Binding error while installing read handler %s for range 0x%X-0x%X mask 0x%X mirror 0x%X select 0x%X umask 0x%X\n", handler_r.name(), addrstart, addrend, addrmask, addrmirror, addrselect, unitmask); + throw; + } + install_read_handler_helper<handler_width_v<READ> >(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, handler_r); + } + + template<typename WRITE> + void install_write_handler_impl(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, u16 flags, WRITE &handler_w) + { + try { handler_w.resolve(); } + catch (const binding_type_exception &) { + osd_printf_error("Binding error while installing write handler %s for range 0x%X-0x%X mask 0x%X mirror 0x%X select 0x%X umask 0x%X\n", handler_w.name(), addrstart, addrend, addrmask, addrmirror, addrselect, unitmask); + throw; + } + install_write_handler_helper<handler_width_v<WRITE> >(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, handler_w); + } + + template<int AccessWidth, typename READ> + void install_read_handler_helper(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, u16 flags, const READ &handler_r) + { + if constexpr (Width < AccessWidth) { + fatalerror("install_read_handler: cannot install a %d-wide handler in a %d-wide bus", 8 << AccessWidth, 8 << Width); + } else { + VPRINTF("memory_view::install_read_handler(%*x-%*x mask=%*x mirror=%*x, space width=%d, handler width=%d, %s, %*x)\n", + m_addrchars, addrstart, m_addrchars, addrend, + m_addrchars, addrmask, m_addrchars, addrmirror, + 8 << Width, 8 << AccessWidth, + handler_r.name(), data_width() / 4, unitmask); + + r()->select_u(m_id); + w()->select_u(m_id); + + offs_t nstart, nend, nmask, nmirror; + u64 nunitmask; + int ncswidth; + check_optimize_all("install_read_handler", 8 << AccessWidth, addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, nstart, nend, nmask, nmirror, nunitmask, ncswidth); + + if constexpr (Width == AccessWidth) { + auto hand_r = new handler_entry_read_delegate<Width, AddrShift, READ>(m_view.m_space, flags, handler_r); + hand_r->set_address_info(nstart, nmask); + r()->populate(nstart, nend, nmirror, hand_r); + } else { + auto hand_r = new handler_entry_read_delegate<AccessWidth, -AccessWidth, READ>(m_view.m_space, flags, handler_r); + memory_units_descriptor<Width, AddrShift> descriptor(AccessWidth, endianness(), hand_r, nstart, nend, nmask, nunitmask, ncswidth); + hand_r->set_address_info(descriptor.get_handler_start(), descriptor.get_handler_mask()); + r()->populate_mismatched(nstart, nend, nmirror, descriptor); + hand_r->unref(); + } + invalidate_caches(read_or_write::READ); + } + } + + template<int AccessWidth, typename WRITE> + void install_write_handler_helper(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, u16 flags, + const WRITE &handler_w) + { + if constexpr (Width < AccessWidth) { + fatalerror("install_write_handler: cannot install a %d-wide handler in a %d-wide bus", 8 << AccessWidth, 8 << Width); + } else { + VPRINTF("memory_view::install_write_handler(%*x-%*x mask=%*x mirror=%*x, space width=%d, handler width=%d, %s, %*x)\n", + m_addrchars, addrstart, m_addrchars, addrend, + m_addrchars, addrmask, m_addrchars, addrmirror, + 8 << Width, 8 << AccessWidth, + handler_w.name(), data_width() / 4, unitmask); + + r()->select_u(m_id); + w()->select_u(m_id); + + offs_t nstart, nend, nmask, nmirror; + u64 nunitmask; + int ncswidth; + check_optimize_all("install_write_handler", 8 << AccessWidth, addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, nstart, nend, nmask, nmirror, nunitmask, ncswidth); + + if constexpr (Width == AccessWidth) { + auto hand_w = new handler_entry_write_delegate<Width, AddrShift, WRITE>(m_view.m_space, flags, handler_w); + hand_w->set_address_info(nstart, nmask); + w()->populate(nstart, nend, nmirror, hand_w); + } else { + auto hand_w = new handler_entry_write_delegate<AccessWidth, -AccessWidth, WRITE>(m_view.m_space, flags, handler_w); + memory_units_descriptor<Width, AddrShift> descriptor(AccessWidth, endianness(), hand_w, nstart, nend, nmask, nunitmask, ncswidth); + hand_w->set_address_info(descriptor.get_handler_start(), descriptor.get_handler_mask()); + w()->populate_mismatched(nstart, nend, nmirror, descriptor); + hand_w->unref(); + } + invalidate_caches(read_or_write::WRITE); + } + } +}; + +namespace { + template<int Level, int Width, int AddrShift> memory_view::memory_view_entry *mve_make_1(const address_space_config &config, memory_manager &manager, memory_view &view, int id) { + return new memory_view_entry_specific<Level, Width, AddrShift>(config, manager, view, id); + } + + template<int Width, int AddrShift> memory_view::memory_view_entry *mve_make_2(int Level, const address_space_config &config, memory_manager &manager, memory_view &view, int id) { + switch(Level) { + case 0: return mve_make_1<0, Width, AddrShift>(config, manager, view, id); + case 1: return mve_make_1<1, Width, AddrShift>(config, manager, view, id); + default: abort(); + } + } + + memory_view::memory_view_entry *mve_make(int Level, int Width, int AddrShift, const address_space_config &config, memory_manager &manager, memory_view &view, int id) { + switch (Width | (AddrShift + 4)) { + case 8|(4+1): return mve_make_2<0, 1>(Level, config, manager, view, id); + case 8|(4-0): return mve_make_2<0, 0>(Level, config, manager, view, id); + case 16|(4+3): return mve_make_2<1, 3>(Level, config, manager, view, id); + case 16|(4-0): return mve_make_2<1, 0>(Level, config, manager, view, id); + case 16|(4-1): return mve_make_2<1, -1>(Level, config, manager, view, id); + case 32|(4+3): return mve_make_2<2, 3>(Level, config, manager, view, id); + case 32|(4-0): return mve_make_2<2, 0>(Level, config, manager, view, id); + case 32|(4-1): return mve_make_2<2, -1>(Level, config, manager, view, id); + case 32|(4-2): return mve_make_2<2, -2>(Level, config, manager, view, id); + case 64|(4-0): return mve_make_2<3, 0>(Level, config, manager, view, id); + case 64|(4-1): return mve_make_2<3, -1>(Level, config, manager, view, id); + case 64|(4-2): return mve_make_2<3, -2>(Level, config, manager, view, id); + case 64|(4-3): return mve_make_2<3, -3>(Level, config, manager, view, id); + default: abort(); + } + } +} + +memory_view::memory_view_entry &memory_view::operator[](int slot) +{ + if (!m_config) + fatalerror("A view must be in a map or a space before it can be setup."); + + auto i = m_entry_mapping.find(slot); + if (i == m_entry_mapping.end()) { + memory_view_entry *e; + int id = m_entries.size(); + e = mve_make(emu::detail::handler_entry_dispatch_level(m_config->addr_width()), m_config->data_width(), m_config->addr_shift(), + *m_config, m_device.machine().memory(), *this, id); + m_entries.resize(id+1); + m_entries[id].reset(e); + m_entry_mapping[slot] = id; + if (m_handler_read) { + m_handler_read->select_u(id); + m_handler_write->select_u(id); + } + return *e; + + } else + return *m_entries[i->second]; +} + +memory_view::memory_view_entry::memory_view_entry(const address_space_config &config, memory_manager &manager, memory_view &view, int id) : address_space_installer(config, manager), m_view(view), m_id(id) +{ + m_map = std::make_unique<address_map>(m_view); +} + +void memory_view::memory_view_entry::prepare_map_generic(address_map &map, bool allow_alloc) +{ + memory_region *devregion = (m_view.m_space->spacenum() == 0) ? m_view.m_device.memregion(DEVICE_SELF) : nullptr; + u32 devregionsize = (devregion != nullptr) ? devregion->bytes() : 0; + + // make a pass over the address map, adjusting for the device and getting memory pointers + for (address_map_entry &entry : map.m_entrylist) + { + // computed adjusted addresses first + adjust_addresses(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror); + + // if we have a share entry, add it to our map + if (entry.m_share != nullptr) + { + // if we can't find it, add it to our map if we're allowed to + std::string fulltag = entry.m_devbase.subtag(entry.m_share); + memory_share *share = m_manager.share_find(fulltag); + if (!share) + { + if (!allow_alloc) + fatalerror("Trying to create share '%s' too late\n", fulltag); + VPRINTF("Creating share '%s' of length 0x%X\n", fulltag, entry.m_addrend + 1 - entry.m_addrstart); + share = m_manager.share_alloc(m_view.m_device, fulltag, m_config.data_width(), address_to_byte(entry.m_addrend + 1 - entry.m_addrstart), endianness()); + } + else + { + std::string result = share->compare(m_config.data_width(), address_to_byte(entry.m_addrend + 1 - entry.m_addrstart), endianness()); + if (!result.empty()) + fatalerror("%s\n", result); + } + entry.m_memory = share->ptr(); + } + + // if this is a ROM handler without a specified region and not shared, attach it to the implicit region + if (m_view.m_space->spacenum() == AS_PROGRAM && entry.m_read.m_type == AMH_ROM && entry.m_region == nullptr && entry.m_share == nullptr) + { + // make sure it fits within the memory region before doing so, however + if (entry.m_addrend < devregionsize) + { + entry.m_region = m_view.m_device.tag(); + entry.m_rgnoffs = address_to_byte(entry.m_addrstart); + } + } + + // validate adjusted addresses against implicit regions + if (entry.m_region != nullptr) + { + // determine full tag + std::string fulltag = entry.m_devbase.subtag(entry.m_region); + + // find the region + memory_region *region = m_manager.machine().root_device().memregion(fulltag); + if (region == nullptr) + fatalerror("device '%s' %s space memory map entry %X-%X references nonexistent region \"%s\"\n", m_view.m_device.tag(), m_view.m_space->name(), entry.m_addrstart, entry.m_addrend, entry.m_region); + + // validate the region + if (entry.m_rgnoffs + m_config.addr2byte(entry.m_addrend - entry.m_addrstart + 1) > region->bytes()) + fatalerror("device '%s' %s space memory map entry %X-%X extends beyond region \"%s\" size (%X)\n", m_view.m_device.tag(), m_view.m_space->name(), entry.m_addrstart, entry.m_addrend, entry.m_region, region->bytes()); + + if (entry.m_share != nullptr) + fatalerror("device '%s' %s space memory map entry %X-%X has both .region() and .share()\n", m_view.m_device.tag(), m_view.m_space->name(), entry.m_addrstart, entry.m_addrend); + } + + // convert any region-relative entries to their memory pointers + if (entry.m_region != nullptr) + { + // determine full tag + std::string fulltag = entry.m_devbase.subtag(entry.m_region); + + // set the memory address + entry.m_memory = m_manager.machine().root_device().memregion(fulltag)->base() + entry.m_rgnoffs; + } + + // allocate anonymous ram when needed + if (!entry.m_memory && (entry.m_read.m_type == AMH_RAM || entry.m_write.m_type == AMH_RAM)) + { + if (!allow_alloc) + fatalerror("Trying to create memory in range %X-%X too late\n", entry.m_addrstart, entry.m_addrend); + + entry.m_memory = m_manager.anonymous_alloc(*m_view.m_space, address_to_byte(entry.m_addrend + 1 - entry.m_addrstart), m_config.data_width(), entry.m_addrstart, entry.m_addrend, key()); + } + } +} + +void memory_view::memory_view_entry::prepare_device_map(address_map &map) +{ + // Disable the test for now, some cleanup needed before + if(0) { + // device maps are not supposed to set global parameters + if (map.m_unmapval) + fatalerror("Device maps should not set the unmap value\n"); + + if (map.m_globalmask && map.m_globalmask != m_view.m_space->addrmask()) + fatalerror("Device maps should not set the global mask\n"); + } + + prepare_map_generic(map, false); +} + + +template<int Level, int Width, int AddrShift> void memory_view_entry_specific<Level, Width, AddrShift>::populate_from_map(address_map *map) +{ + // no map specified, use the space-specific one and import the submaps + if (map == nullptr) { + map = m_map.get(); + map->import_submaps(m_manager.machine(), m_view.m_device, data_width(), endianness(), addr_shift()); + } + + prepare_map_generic(*map, true); + + // Force the slot to exist, in case the map is empty + r()->select_u(m_id); + w()->select_u(m_id); + + // install the handlers, using the original, unadjusted memory map + for(const address_map_entry &entry : map->m_entrylist) { + // map both read and write halves + populate_map_entry(entry, read_or_write::READ); + populate_map_entry(entry, read_or_write::WRITE); + } +} + +std::string memory_view::memory_view_entry::key() const +{ + std::string key = m_view.m_context; + if (m_id != -1) + key += util::string_format("%s[%d].", m_view.m_name, m_view.id_to_slot(m_id)); + return key; +} + + +memory_view::memory_view(device_t &device, std::string name) : m_device(device), m_name(name), m_config(nullptr), m_addrstart(0), m_addrend(0), m_space(nullptr), m_handler_read(nullptr), m_handler_write(nullptr), m_cur_id(-1), m_cur_slot(-1) +{ + device.view_register(this); +} + +memory_view::~memory_view() +{ + if (m_handler_read) { + m_handler_read->unref(); + m_handler_write->unref(); + } +} + +void memory_view::register_state() +{ + m_device.machine().save().save_item(&m_device, "view", m_device.subtag(m_name).c_str(), 0, NAME(m_cur_slot)); + m_device.machine().save().save_item(&m_device, "view", m_device.subtag(m_name).c_str(), 0, NAME(m_cur_id)); + m_device.machine().save().register_postload(save_prepost_delegate(FUNC(memory_view::refresh_id), this)); +} + +void memory_view::refresh_id() +{ + if (m_handler_read) { + m_handler_read->select_a(m_cur_id); + m_handler_write->select_a(m_cur_id); + } + + if (m_space) + m_space->invalidate_caches(read_or_write::READWRITE); +} + +void memory_view::disable() +{ + m_cur_slot = -1; + m_cur_id = -1; + refresh_id(); +} + +void memory_view::select(int slot) +{ + auto i = m_entry_mapping.find(slot); + if (i == m_entry_mapping.end()) + fatalerror("memory_view %s: select of unknown slot %d", m_name, slot); + + m_cur_slot = slot; + m_cur_id = i->second; + refresh_id(); +} + +int memory_view::id_to_slot(int id) const +{ + for(const auto &p : m_entry_mapping) + if (p.second == id) + return p.first; + fatalerror("memory_view::id_to_slot on unknown id %d\n", id); +} + +void memory_view::initialize_from_address_map(offs_t addrstart, offs_t addrend, const address_space_config &config) +{ + if (m_config) + fatalerror("A memory_view can be present in only one address map."); + + m_config = &config; + m_addrstart = addrstart; + m_addrend = addrend; +} + +namespace { + template<int Width, int AddrShift> void h_make_1(int HighBits, address_space &space, memory_view &view, offs_t addrstart, offs_t addrend, handler_entry *&r, handler_entry *&w) { + switch(HighBits) { + case 0: r = new handler_entry_read_dispatch<std::max(0, Width), Width, AddrShift>(&space, view, addrstart, addrend); w = new handler_entry_write_dispatch<std::max(0, Width), Width, AddrShift>(&space, view, addrstart, addrend); break; + case 1: r = new handler_entry_read_dispatch<std::max(1, Width), Width, AddrShift>(&space, view, addrstart, addrend); w = new handler_entry_write_dispatch<std::max(1, Width), Width, AddrShift>(&space, view, addrstart, addrend); break; + case 2: r = new handler_entry_read_dispatch<std::max(2, Width), Width, AddrShift>(&space, view, addrstart, addrend); w = new handler_entry_write_dispatch<std::max(2, Width), Width, AddrShift>(&space, view, addrstart, addrend); break; + case 3: r = new handler_entry_read_dispatch<std::max(3, Width), Width, AddrShift>(&space, view, addrstart, addrend); w = new handler_entry_write_dispatch<std::max(3, Width), Width, AddrShift>(&space, view, addrstart, addrend); break; + case 4: r = new handler_entry_read_dispatch< 4, Width, AddrShift>(&space, view, addrstart, addrend); w = new handler_entry_write_dispatch< 4, Width, AddrShift>(&space, view, addrstart, addrend); break; + case 5: r = new handler_entry_read_dispatch< 5, Width, AddrShift>(&space, view, addrstart, addrend); w = new handler_entry_write_dispatch< 5, Width, AddrShift>(&space, view, addrstart, addrend); break; + case 6: r = new handler_entry_read_dispatch< 6, Width, AddrShift>(&space, view, addrstart, addrend); w = new handler_entry_write_dispatch< 6, Width, AddrShift>(&space, view, addrstart, addrend); break; + case 7: r = new handler_entry_read_dispatch< 7, Width, AddrShift>(&space, view, addrstart, addrend); w = new handler_entry_write_dispatch< 7, Width, AddrShift>(&space, view, addrstart, addrend); break; + case 8: r = new handler_entry_read_dispatch< 8, Width, AddrShift>(&space, view, addrstart, addrend); w = new handler_entry_write_dispatch< 8, Width, AddrShift>(&space, view, addrstart, addrend); break; + case 9: r = new handler_entry_read_dispatch< 9, Width, AddrShift>(&space, view, addrstart, addrend); w = new handler_entry_write_dispatch< 9, Width, AddrShift>(&space, view, addrstart, addrend); break; + case 10: r = new handler_entry_read_dispatch<10, Width, AddrShift>(&space, view, addrstart, addrend); w = new handler_entry_write_dispatch<10, Width, AddrShift>(&space, view, addrstart, addrend); break; + case 11: r = new handler_entry_read_dispatch<11, Width, AddrShift>(&space, view, addrstart, addrend); w = new handler_entry_write_dispatch<11, Width, AddrShift>(&space, view, addrstart, addrend); break; + case 12: r = new handler_entry_read_dispatch<12, Width, AddrShift>(&space, view, addrstart, addrend); w = new handler_entry_write_dispatch<12, Width, AddrShift>(&space, view, addrstart, addrend); break; + case 13: r = new handler_entry_read_dispatch<13, Width, AddrShift>(&space, view, addrstart, addrend); w = new handler_entry_write_dispatch<13, Width, AddrShift>(&space, view, addrstart, addrend); break; + case 14: r = new handler_entry_read_dispatch<14, Width, AddrShift>(&space, view, addrstart, addrend); w = new handler_entry_write_dispatch<14, Width, AddrShift>(&space, view, addrstart, addrend); break; + case 15: r = new handler_entry_read_dispatch<15, Width, AddrShift>(&space, view, addrstart, addrend); w = new handler_entry_write_dispatch<15, Width, AddrShift>(&space, view, addrstart, addrend); break; + case 16: r = new handler_entry_read_dispatch<16, Width, AddrShift>(&space, view, addrstart, addrend); w = new handler_entry_write_dispatch<16, Width, AddrShift>(&space, view, addrstart, addrend); break; + case 17: r = new handler_entry_read_dispatch<17, Width, AddrShift>(&space, view, addrstart, addrend); w = new handler_entry_write_dispatch<17, Width, AddrShift>(&space, view, addrstart, addrend); break; + case 18: r = new handler_entry_read_dispatch<18, Width, AddrShift>(&space, view, addrstart, addrend); w = new handler_entry_write_dispatch<18, Width, AddrShift>(&space, view, addrstart, addrend); break; + case 19: r = new handler_entry_read_dispatch<19, Width, AddrShift>(&space, view, addrstart, addrend); w = new handler_entry_write_dispatch<19, Width, AddrShift>(&space, view, addrstart, addrend); break; + case 20: r = new handler_entry_read_dispatch<20, Width, AddrShift>(&space, view, addrstart, addrend); w = new handler_entry_write_dispatch<20, Width, AddrShift>(&space, view, addrstart, addrend); break; + case 21: r = new handler_entry_read_dispatch<21, Width, AddrShift>(&space, view, addrstart, addrend); w = new handler_entry_write_dispatch<21, Width, AddrShift>(&space, view, addrstart, addrend); break; + case 22: r = new handler_entry_read_dispatch<22, Width, AddrShift>(&space, view, addrstart, addrend); w = new handler_entry_write_dispatch<22, Width, AddrShift>(&space, view, addrstart, addrend); break; + case 23: r = new handler_entry_read_dispatch<23, Width, AddrShift>(&space, view, addrstart, addrend); w = new handler_entry_write_dispatch<23, Width, AddrShift>(&space, view, addrstart, addrend); break; + case 24: r = new handler_entry_read_dispatch<24, Width, AddrShift>(&space, view, addrstart, addrend); w = new handler_entry_write_dispatch<24, Width, AddrShift>(&space, view, addrstart, addrend); break; + case 25: r = new handler_entry_read_dispatch<25, Width, AddrShift>(&space, view, addrstart, addrend); w = new handler_entry_write_dispatch<25, Width, AddrShift>(&space, view, addrstart, addrend); break; + case 26: r = new handler_entry_read_dispatch<26, Width, AddrShift>(&space, view, addrstart, addrend); w = new handler_entry_write_dispatch<26, Width, AddrShift>(&space, view, addrstart, addrend); break; + case 27: r = new handler_entry_read_dispatch<27, Width, AddrShift>(&space, view, addrstart, addrend); w = new handler_entry_write_dispatch<27, Width, AddrShift>(&space, view, addrstart, addrend); break; + case 28: r = new handler_entry_read_dispatch<28, Width, AddrShift>(&space, view, addrstart, addrend); w = new handler_entry_write_dispatch<28, Width, AddrShift>(&space, view, addrstart, addrend); break; + case 29: r = new handler_entry_read_dispatch<29, Width, AddrShift>(&space, view, addrstart, addrend); w = new handler_entry_write_dispatch<29, Width, AddrShift>(&space, view, addrstart, addrend); break; + case 30: r = new handler_entry_read_dispatch<30, Width, AddrShift>(&space, view, addrstart, addrend); w = new handler_entry_write_dispatch<30, Width, AddrShift>(&space, view, addrstart, addrend); break; + case 31: r = new handler_entry_read_dispatch<31, Width, AddrShift>(&space, view, addrstart, addrend); w = new handler_entry_write_dispatch<31, Width, AddrShift>(&space, view, addrstart, addrend); break; + case 32: r = new handler_entry_read_dispatch<32, Width, AddrShift>(&space, view, addrstart, addrend); w = new handler_entry_write_dispatch<32, Width, AddrShift>(&space, view, addrstart, addrend); break; + default: abort(); + } + } + + void h_make(int HighBits, int Width, int AddrShift, address_space &space, memory_view &view, offs_t addrstart, offs_t addrend, handler_entry *&r, handler_entry *&w) { + switch (Width | (AddrShift + 4)) { + case 8|(4+1): h_make_1<0, 1>(HighBits, space, view, addrstart, addrend, r, w); break; + case 8|(4-0): h_make_1<0, 0>(HighBits, space, view, addrstart, addrend, r, w); break; + case 16|(4+3): h_make_1<1, 3>(HighBits, space, view, addrstart, addrend, r, w); break; + case 16|(4-0): h_make_1<1, 0>(HighBits, space, view, addrstart, addrend, r, w); break; + case 16|(4-1): h_make_1<1, -1>(HighBits, space, view, addrstart, addrend, r, w); break; + case 32|(4+3): h_make_1<2, 3>(HighBits, space, view, addrstart, addrend, r, w); break; + case 32|(4-0): h_make_1<2, 0>(HighBits, space, view, addrstart, addrend, r, w); break; + case 32|(4-1): h_make_1<2, -1>(HighBits, space, view, addrstart, addrend, r, w); break; + case 32|(4-2): h_make_1<2, -2>(HighBits, space, view, addrstart, addrend, r, w); break; + case 64|(4-0): h_make_1<3, 0>(HighBits, space, view, addrstart, addrend, r, w); break; + case 64|(4-1): h_make_1<3, -1>(HighBits, space, view, addrstart, addrend, r, w); break; + case 64|(4-2): h_make_1<3, -2>(HighBits, space, view, addrstart, addrend, r, w); break; + case 64|(4-3): h_make_1<3, -3>(HighBits, space, view, addrstart, addrend, r, w); break; + default: abort(); + } + } +} + +std::pair<handler_entry *, handler_entry *> memory_view::make_handlers(address_space &space, offs_t addrstart, offs_t addrend) +{ + if (m_space != &space || m_addrstart != addrstart || m_addrend != addrend) { + if (m_space) + fatalerror("A memory_view can be installed only once."); + + if (m_config) { + if (m_addrstart != addrstart || m_addrend != addrend) + fatalerror("A memory_view must be installed at its configuration address."); + } else { + m_config = &space.space_config(); + m_addrstart = addrstart; + m_addrend = addrend; + } + + m_space = &space; + + offs_t span = addrstart ^ addrend; + u32 awidth = 32 - count_leading_zeros_32(span); + + h_make(awidth, m_config->data_width(), m_config->addr_shift(), space, *this, addrstart, addrend, m_handler_read, m_handler_write); + m_handler_read->ref(); + m_handler_write->ref(); + } + + return std::make_pair(m_handler_read, m_handler_write); +} + +void memory_view::make_subdispatch(std::string context) +{ + m_context = context; + for(auto &e : m_entries) + e->populate_from_map(); +} + +void memory_view::memory_view_entry::check_range_optimize_mirror(const char *function, offs_t addrstart, offs_t addrend, offs_t addrmirror, offs_t &nstart, offs_t &nend, offs_t &nmask, offs_t &nmirror) +{ + check_optimize_mirror(function, addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror); + if (nstart < m_view.m_addrstart || (nend | nmirror) > m_view.m_addrend) + fatalerror("%s: The range %x-%x mirror %x, exceeds the view window boundaries %x-%x.\n", function, addrstart, addrend, addrmirror, m_view.m_addrstart, m_view.m_addrend); +} + +void memory_view::memory_view_entry::check_range_optimize_all(const char *function, int width, offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, offs_t &nstart, offs_t &nend, offs_t &nmask, offs_t &nmirror, u64 &nunitmask, int &ncswidth) +{ + check_optimize_all(function, width, addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, nstart, nend, nmask, nmirror, nunitmask, ncswidth); + if (nstart < m_view.m_addrstart || (nend | nmirror | addrselect) > m_view.m_addrend) + fatalerror("%s: The range %x-%x mirror %x select %x, exceeds the view window boundaries %x-%x.\n", function, addrstart, addrend, addrmirror, addrselect, m_view.m_addrstart, m_view.m_addrend); +} + +void memory_view::memory_view_entry::check_range_address(const char *function, offs_t addrstart, offs_t addrend) +{ + check_address(function, addrstart, addrend); + if (addrstart < m_view.m_addrstart || addrend > m_view.m_addrend) + fatalerror("%s: The range %x-%x exceeds the view window boundaries %x-%x.\n", function, addrstart, addrend, m_view.m_addrstart, m_view.m_addrend); +} + +template<int Level, int Width, int AddrShift> void memory_view_entry_specific<Level, Width, AddrShift>::install_ram_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, read_or_write readorwrite, void *baseptr) +{ + VPRINTF("memory_view::install_ram_generic(%s-%s mirror=%s, %s, %p)\n", + m_addrchars, addrstart, m_addrchars, addrend, + m_addrchars, addrmirror, + (readorwrite == read_or_write::READ) ? "read" : (readorwrite == read_or_write::WRITE) ? "write" : (readorwrite == read_or_write::READWRITE) ? "read/write" : "??", + baseptr); + + offs_t nstart, nend, nmask, nmirror; + check_range_optimize_mirror("install_ram_generic", addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror); + + r()->select_u(m_id); + w()->select_u(m_id); + + // map for read + if (readorwrite == read_or_write::READ || readorwrite == read_or_write::READWRITE) + { + auto hand_r = new handler_entry_read_memory<Width, AddrShift>(m_view.m_space, flags, baseptr); + hand_r->set_address_info(nstart, nmask); + r()->populate(nstart, nend, nmirror, hand_r); + } + + // map for write + if (readorwrite == read_or_write::WRITE || readorwrite == read_or_write::READWRITE) + { + auto hand_w = new handler_entry_write_memory<Width, AddrShift>(m_view.m_space, flags, baseptr); + hand_w->set_address_info(nstart, nmask); + w()->populate(nstart, nend, nmirror, hand_w); + } + + invalidate_caches(readorwrite); +} + +template<int Level, int Width, int AddrShift> void memory_view_entry_specific<Level, Width, AddrShift>::unmap_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, read_or_write readorwrite, bool quiet) +{ + VPRINTF("memory_view::unmap(%*x-%*x mirror=%*x, %s, %s)\n", + m_addrchars, addrstart, m_addrchars, addrend, + m_addrchars, addrmirror, + (readorwrite == read_or_write::READ) ? "read" : (readorwrite == read_or_write::WRITE) ? "write" : (readorwrite == read_or_write::READWRITE) ? "read/write" : "??", + quiet ? "quiet" : "normal"); + + offs_t nstart, nend, nmask, nmirror; + check_range_optimize_mirror("unmap_generic", addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror); + + r()->select_u(m_id); + w()->select_u(m_id); + + // read space + if (readorwrite == read_or_write::READ || readorwrite == read_or_write::READWRITE) { + auto handler = flags ? (quiet ? + static_cast<handler_entry_read<Width, AddrShift> *>(new handler_entry_read_nop<Width, AddrShift>(m_view.m_space, flags)) : + static_cast<handler_entry_read<Width, AddrShift> *>(new handler_entry_read_unmapped<Width, AddrShift>(m_view.m_space, flags))) + : (quiet ? + static_cast<handler_entry_read<Width, AddrShift> *>(m_view.m_space->nop_r()) : + static_cast<handler_entry_read<Width, AddrShift> *>(m_view.m_space->unmap_r())); + handler->ref(); + r()->populate(nstart, nend, nmirror, handler); + } + + // write space + if (readorwrite == read_or_write::WRITE || readorwrite == read_or_write::READWRITE) { + auto handler = flags ? (quiet ? + static_cast<handler_entry_write<Width, AddrShift> *>(new handler_entry_write_nop<Width, AddrShift>(m_view.m_space, flags)) : + static_cast<handler_entry_write<Width, AddrShift> *>(new handler_entry_write_unmapped<Width, AddrShift>(m_view.m_space, flags))) + : (quiet ? + static_cast<handler_entry_write<Width, AddrShift> *>(m_view.m_space->nop_w()) : + static_cast<handler_entry_write<Width, AddrShift> *>(m_view.m_space->unmap_w())); + handler->ref(); + w()->populate(nstart, nend, nmirror, handler); + } + + invalidate_caches(readorwrite); +} + +template<int Level, int Width, int AddrShift> void memory_view_entry_specific<Level, Width, AddrShift>::install_view(offs_t addrstart, offs_t addrend, offs_t addrmirror, memory_view &view) +{ + offs_t nstart, nend, nmask, nmirror; + check_range_optimize_mirror("install_view", addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror); + + r()->select_u(m_id); + w()->select_u(m_id); + + auto handlers = view.make_handlers(*m_view.m_space, addrstart, addrend); + r()->populate(nstart, nend, nmirror, static_cast<handler_entry_read <Width, AddrShift> *>(handlers.first)); + w()->populate(nstart, nend, nmirror, static_cast<handler_entry_write<Width, AddrShift> *>(handlers.second)); + view.make_subdispatch(key()); // Must be called after populate +} + +template<int Level, int Width, int AddrShift> memory_passthrough_handler memory_view_entry_specific<Level, Width, AddrShift>::install_read_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, uX &data, uX mem_mask)> tap, memory_passthrough_handler *mph) +{ + offs_t nstart, nend, nmask, nmirror; + check_range_optimize_mirror("install_read_tap", addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror); + auto impl = m_view.m_space->make_mph(mph); + + r()->select_u(m_id); + w()->select_u(m_id); + + auto handler = new handler_entry_read_tap<Width, AddrShift>(m_view.m_space, *impl, name, tap); + r()->populate_passthrough(nstart, nend, nmirror, handler); + handler->unref(); + + invalidate_caches(read_or_write::READ); + + return impl; +} + +template<int Level, int Width, int AddrShift> memory_passthrough_handler memory_view_entry_specific<Level, Width, AddrShift>::install_write_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, uX &data, uX mem_mask)> tap, memory_passthrough_handler *mph) +{ + offs_t nstart, nend, nmask, nmirror; + check_range_optimize_mirror("install_write_tap", addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror); + auto impl = m_view.m_space->make_mph(mph); + + r()->select_u(m_id); + w()->select_u(m_id); + + auto handler = new handler_entry_write_tap<Width, AddrShift>(m_view.m_space, *impl, name, tap); + w()->populate_passthrough(nstart, nend, nmirror, handler); + handler->unref(); + + invalidate_caches(read_or_write::WRITE); + + return impl; +} + +template<int Level, int Width, int AddrShift> memory_passthrough_handler memory_view_entry_specific<Level, Width, AddrShift>::install_readwrite_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function<void (offs_t offset, uX &data, uX mem_mask)> tapr, std::function<void (offs_t offset, uX &data, uX mem_mask)> tapw, memory_passthrough_handler *mph) +{ + offs_t nstart, nend, nmask, nmirror; + check_range_optimize_mirror("install_readwrite_tap", addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror); + auto impl = m_view.m_space->make_mph(mph); + + r()->select_u(m_id); + w()->select_u(m_id); + + auto rhandler = new handler_entry_read_tap <Width, AddrShift>(m_view.m_space, *impl, name, tapr); + r() ->populate_passthrough(nstart, nend, nmirror, rhandler); + rhandler->unref(); + + auto whandler = new handler_entry_write_tap<Width, AddrShift>(m_view.m_space, *impl, name, tapw); + w()->populate_passthrough(nstart, nend, nmirror, whandler); + whandler->unref(); + + invalidate_caches(read_or_write::READWRITE); + + return impl; +} + +template<int Level, int Width, int AddrShift> void memory_view_entry_specific<Level, Width, AddrShift>::install_device_delegate(offs_t addrstart, offs_t addrend, device_t &device, address_map_constructor &delegate, u64 unitmask, int cswidth, u16 flags) +{ + check_range_address("install_device_delegate", addrstart, addrend); + + r()->select_u(m_id); + w()->select_u(m_id); + + address_map map(*m_view.m_space, addrstart, addrend, unitmask, cswidth, flags, m_view.m_device, delegate); + map.import_submaps(m_manager.machine(), device, data_width(), endianness(), addr_shift()); + prepare_device_map(map); + populate_from_map(&map); +} + +template<int Level, int Width, int AddrShift> void memory_view_entry_specific<Level, Width, AddrShift>::install_readwrite_port(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, std::string rtag, std::string wtag) +{ + VPRINTF("memory_view::install_readwrite_port(%*x-%*x mirror=%*x, read=\"%s\" / write=\"%s\")\n", + m_addrchars, addrstart, m_addrchars, addrend, + m_addrchars, addrmirror, + rtag.empty() ? "(none)" : rtag, wtag.empty() ? "(none)" : wtag); + + offs_t nstart, nend, nmask, nmirror; + check_range_optimize_mirror("install_readwrite_port", addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror); + + r()->select_u(m_id); + w()->select_u(m_id); + + // read handler + if (rtag != "") + { + // find the port + ioport_port *port = m_view.m_device.ioport(rtag); + if (port == nullptr) + throw emu_fatalerror("Attempted to map non-existent port '%s' for read in space %s of device '%s'\n", rtag, m_view.m_name, m_view.m_device.tag()); + + // map the range and set the ioport + auto hand_r = new handler_entry_read_ioport<Width, AddrShift>(m_view.m_space, flags, port); + r()->populate(nstart, nend, nmirror, hand_r); + } + + if (wtag != "") + { + // find the port + ioport_port *port = m_view.m_device.ioport(wtag); + if (port == nullptr) + fatalerror("Attempted to map non-existent port '%s' for write in space %s of device '%s'\n", wtag, m_view.m_name, m_view.m_device.tag()); + + // map the range and set the ioport + auto hand_w = new handler_entry_write_ioport<Width, AddrShift>(m_view.m_space, flags, port); + w()->populate(nstart, nend, nmirror, hand_w); + } + + invalidate_caches(rtag != "" ? wtag != "" ? read_or_write::READWRITE : read_or_write::READ : read_or_write::WRITE); +} + +template<int Level, int Width, int AddrShift> void memory_view_entry_specific<Level, Width, AddrShift>::install_read_before_time(offs_t addrstart, offs_t addrend, offs_t addrmirror, ws_time_delegate ws) +{ + auto *cpu = dynamic_cast<cpu_device *>(&m_view.m_device); + if (!cpu) + fatalerror("Attempted to a waitstate handler on non-cpu device '%s'\n", m_view.m_device.tag()); + if (!cpu->cpu_is_interruptible()) + fatalerror("Attempted to a waitstate handler on non-interruptible cpu device '%s'\n", m_view.m_device.tag()); + + VPRINTF("memory_view::install_read_before_time(%*x-%*x mirror=%*x ws=%s)\n", + m_addrchars, addrstart, m_addrchars, addrend, + m_addrchars, addrmirror, ws.name()); + + offs_t nstart, nend, nmask, nmirror; + check_optimize_mirror("install_read_before_time", addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror); + + r()->select_u(m_id); + w()->select_u(m_id); + + auto hand_r = new handler_entry_read_before_time<Width, AddrShift>(m_view.m_space, *m_view.m_space->get_default_mpl(), ws); + r()->populate_passthrough(nstart, nend, nmirror, hand_r); +} + +template<int Level, int Width, int AddrShift> void memory_view_entry_specific<Level, Width, AddrShift>::install_read_before_delay(offs_t addrstart, offs_t addrend, offs_t addrmirror, ws_delay_delegate ws) +{ + auto *cpu = dynamic_cast<cpu_device *>(&m_view.m_device); + if (!cpu) + fatalerror("Attempted to a waitstate handler on non-cpu device '%s'\n", m_view.m_device.tag()); + if (!cpu->cpu_is_interruptible()) + fatalerror("Attempted to a waitstate handler on non-interruptible cpu device '%s'\n", m_view.m_device.tag()); + + VPRINTF("memory_view::install_read_before_delay(%*x-%*x mirror=%*x ws=%s)\n", + m_addrchars, addrstart, m_addrchars, addrend, + m_addrchars, addrmirror, ws.name()); + + offs_t nstart, nend, nmask, nmirror; + check_optimize_mirror("install_read_before_delay", addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror); + + r()->select_u(m_id); + w()->select_u(m_id); + + auto hand_r = new handler_entry_read_before_delay<Width, AddrShift>(m_view.m_space, *m_view.m_space->get_default_mpl(), ws); + r()->populate_passthrough(nstart, nend, nmirror, hand_r); +} + +template<int Level, int Width, int AddrShift> void memory_view_entry_specific<Level, Width, AddrShift>::install_read_after_delay(offs_t addrstart, offs_t addrend, offs_t addrmirror, ws_delay_delegate ws) +{ + auto *cpu = dynamic_cast<cpu_device *>(&m_view.m_device); + if (!cpu) + fatalerror("Attempted to a waitstate handler on non-cpu device '%s'\n", m_view.m_device.tag()); + if (!cpu->cpu_is_interruptible()) + fatalerror("Attempted to a waitstate handler on non-interruptible cpu device '%s'\n", m_view.m_device.tag()); + + VPRINTF("memory_view::install_read_after_delay(%*x-%*x mirror=%*x ws=%s)\n", + m_addrchars, addrstart, m_addrchars, addrend, + m_addrchars, addrmirror, ws.name()); + + offs_t nstart, nend, nmask, nmirror; + check_optimize_mirror("install_read_after_delay", addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror); + + r()->select_u(m_id); + w()->select_u(m_id); + + auto hand_r = new handler_entry_read_after_delay<Width, AddrShift>(m_view.m_space, *m_view.m_space->get_default_mpl(), ws); + r()->populate_passthrough(nstart, nend, nmirror, hand_r); +} + + + +template<int Level, int Width, int AddrShift> void memory_view_entry_specific<Level, Width, AddrShift>::install_write_before_time(offs_t addrstart, offs_t addrend, offs_t addrmirror, ws_time_delegate ws) +{ + auto *cpu = dynamic_cast<cpu_device *>(&m_view.m_device); + if (!cpu) + fatalerror("Attempted to a waitstate handler on non-cpu device '%s'\n", m_view.m_device.tag()); + if (!cpu->cpu_is_interruptible()) + fatalerror("Attempted to a waitstate handler on non-interruptible cpu device '%s'\n", m_view.m_device.tag()); + + VPRINTF("memory_view::install_write_before_time(%*x-%*x mirror=%*x ws=%s)\n", + m_addrchars, addrstart, m_addrchars, addrend, + m_addrchars, addrmirror, ws.name()); + + offs_t nstart, nend, nmask, nmirror; + check_optimize_mirror("install_write_before_time", addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror); + + r()->select_u(m_id); + w()->select_u(m_id); + + auto hand_w = new handler_entry_write_before_time<Width, AddrShift>(m_view.m_space, *m_view.m_space->get_default_mpl(), ws); + w()->populate_passthrough(nstart, nend, nmirror, hand_w); +} + +template<int Level, int Width, int AddrShift> void memory_view_entry_specific<Level, Width, AddrShift>::install_write_before_delay(offs_t addrstart, offs_t addrend, offs_t addrmirror, ws_delay_delegate ws) +{ + auto *cpu = dynamic_cast<cpu_device *>(&m_view.m_device); + if (!cpu) + fatalerror("Attempted to a waitstate handler on non-cpu device '%s'\n", m_view.m_device.tag()); + if (!cpu->cpu_is_interruptible()) + fatalerror("Attempted to a waitstate handler on non-interruptible cpu device '%s'\n", m_view.m_device.tag()); + + VPRINTF("memory_view::install_write_before_delay(%*x-%*x mirror=%*x ws=%s)\n", + m_addrchars, addrstart, m_addrchars, addrend, + m_addrchars, addrmirror, ws.name()); + + offs_t nstart, nend, nmask, nmirror; + check_optimize_mirror("install_write_before_delay", addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror); + + r()->select_u(m_id); + w()->select_u(m_id); + + auto hand_w = new handler_entry_write_before_delay<Width, AddrShift>(m_view.m_space, *m_view.m_space->get_default_mpl(), ws); + w()->populate_passthrough(nstart, nend, nmirror, hand_w); +} + +template<int Level, int Width, int AddrShift> void memory_view_entry_specific<Level, Width, AddrShift>::install_write_after_delay(offs_t addrstart, offs_t addrend, offs_t addrmirror, ws_delay_delegate ws) +{ + auto *cpu = dynamic_cast<cpu_device *>(&m_view.m_device); + if (!cpu) + fatalerror("Attempted to a waitstate handler on non-cpu device '%s'\n", m_view.m_device.tag()); + if (!cpu->cpu_is_interruptible()) + fatalerror("Attempted to a waitstate handler on non-interruptible cpu device '%s'\n", m_view.m_device.tag()); + + VPRINTF("memory_view::install_write_after_delay(%*x-%*x mirror=%*x ws=%s)\n", + m_addrchars, addrstart, m_addrchars, addrend, + m_addrchars, addrmirror, ws.name()); + + offs_t nstart, nend, nmask, nmirror; + check_optimize_mirror("install_write_after_delay", addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror); + + r()->select_u(m_id); + w()->select_u(m_id); + + auto hand_w = new handler_entry_write_after_delay<Width, AddrShift>(m_view.m_space, *m_view.m_space->get_default_mpl(), ws); + w()->populate_passthrough(nstart, nend, nmirror, hand_w); +} + + +template<int Level, int Width, int AddrShift> void memory_view_entry_specific<Level, Width, AddrShift>::install_readwrite_before_time(offs_t addrstart, offs_t addrend, offs_t addrmirror, ws_time_delegate ws) +{ + auto *cpu = dynamic_cast<cpu_device *>(&m_view.m_device); + if (!cpu) + fatalerror("Attempted to a waitstate handler on non-cpu device '%s'\n", m_view.m_device.tag()); + if (!cpu->cpu_is_interruptible()) + fatalerror("Attempted to a waitstate handler on non-interruptible cpu device '%s'\n", m_view.m_device.tag()); + + VPRINTF("memory_view::install_readwrite_before_time(%*x-%*x mirror=%*x ws=%s)\n", + m_addrchars, addrstart, m_addrchars, addrend, + m_addrchars, addrmirror, ws.name()); + + offs_t nstart, nend, nmask, nmirror; + check_optimize_mirror("install_readwrite_before_time", addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror); + + r()->select_u(m_id); + w()->select_u(m_id); + + auto hand_r = new handler_entry_read_before_time<Width, AddrShift>(m_view.m_space, *m_view.m_space->get_default_mpl(), ws); + r()->populate_passthrough(nstart, nend, nmirror, hand_r); + auto hand_w = new handler_entry_write_before_time<Width, AddrShift>(m_view.m_space, *m_view.m_space->get_default_mpl(), ws); + w()->populate_passthrough(nstart, nend, nmirror, hand_w); +} + +template<int Level, int Width, int AddrShift> void memory_view_entry_specific<Level, Width, AddrShift>::install_readwrite_before_delay(offs_t addrstart, offs_t addrend, offs_t addrmirror, ws_delay_delegate ws) +{ + auto *cpu = dynamic_cast<cpu_device *>(&m_view.m_device); + if (!cpu) + fatalerror("Attempted to a waitstate handler on non-cpu device '%s'\n", m_view.m_device.tag()); + if (!cpu->cpu_is_interruptible()) + fatalerror("Attempted to a waitstate handler on non-interruptible cpu device '%s'\n", m_view.m_device.tag()); + + VPRINTF("memory_view::install_readwrite_before_delay(%*x-%*x mirror=%*x ws=%s)\n", + m_addrchars, addrstart, m_addrchars, addrend, + m_addrchars, addrmirror, ws.name()); + + offs_t nstart, nend, nmask, nmirror; + check_optimize_mirror("install_readwrite_before_delay", addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror); + + r()->select_u(m_id); + w()->select_u(m_id); + + auto hand_r = new handler_entry_read_before_delay<Width, AddrShift>(m_view.m_space, *m_view.m_space->get_default_mpl(), ws); + r()->populate_passthrough(nstart, nend, nmirror, hand_r); + auto hand_w = new handler_entry_write_before_delay<Width, AddrShift>(m_view.m_space, *m_view.m_space->get_default_mpl(), ws); + w()->populate_passthrough(nstart, nend, nmirror, hand_w); +} + +template<int Level, int Width, int AddrShift> void memory_view_entry_specific<Level, Width, AddrShift>::install_readwrite_after_delay(offs_t addrstart, offs_t addrend, offs_t addrmirror, ws_delay_delegate ws) +{ + auto *cpu = dynamic_cast<cpu_device *>(&m_view.m_device); + if (!cpu) + fatalerror("Attempted to a waitstate handler on non-cpu device '%s'\n", m_view.m_device.tag()); + if (!cpu->cpu_is_interruptible()) + fatalerror("Attempted to a waitstate handler on non-interruptible cpu device '%s'\n", m_view.m_device.tag()); + + VPRINTF("memory_view::install_readwrite_after_delay(%*x-%*x mirror=%*x ws=%s)\n", + m_addrchars, addrstart, m_addrchars, addrend, + m_addrchars, addrmirror, ws.name()); + + offs_t nstart, nend, nmask, nmirror; + check_optimize_mirror("install_readwrite_after_delay", addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror); + + r()->select_u(m_id); + w()->select_u(m_id); + + auto hand_r = new handler_entry_read_after_delay<Width, AddrShift>(m_view.m_space, *m_view.m_space->get_default_mpl(), ws); + r()->populate_passthrough(nstart, nend, nmirror, hand_r); + auto hand_w = new handler_entry_write_after_delay<Width, AddrShift>(m_view.m_space, *m_view.m_space->get_default_mpl(), ws); + w()->populate_passthrough(nstart, nend, nmirror, hand_w); +} + + +template<int Level, int Width, int AddrShift> void memory_view_entry_specific<Level, Width, AddrShift>::install_bank_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, memory_bank *rbank, memory_bank *wbank) +{ + VPRINTF("memory_view::install_readwrite_bank(%*x-%*x mirror=%*x, read=\"%s\" / write=\"%s\")\n", + m_addrchars, addrstart, m_addrchars, addrend, + m_addrchars, addrmirror, + (rbank != nullptr) ? rbank->tag() : "(none)", (wbank != nullptr) ? wbank->tag() : "(none)"); + + offs_t nstart, nend, nmask, nmirror; + check_range_optimize_mirror("install_bank_generic", addrstart, addrend, addrmirror, nstart, nend, nmask, nmirror); + + r()->select_u(m_id); + w()->select_u(m_id); + + // map the read bank + if (rbank != nullptr) + { + auto hand_r = new handler_entry_read_memory_bank<Width, AddrShift>(m_view.m_space, flags, *rbank); + hand_r->set_address_info(nstart, nmask); + r()->populate(nstart, nend, nmirror, hand_r); + } + + // map the write bank + if (wbank != nullptr) + { + auto hand_w = new handler_entry_write_memory_bank<Width, AddrShift>(m_view.m_space, flags, *wbank); + hand_w->set_address_info(nstart, nmask); + w()->populate(nstart, nend, nmirror, hand_w); + } + + invalidate_caches(rbank ? wbank ? read_or_write::READWRITE : read_or_write::READ : read_or_write::WRITE); +} diff --git a/src/emu/emuopts.cpp b/src/emu/emuopts.cpp index eb7c9bdbf27..18b37734465 100644 --- a/src/emu/emuopts.cpp +++ b/src/emu/emuopts.cpp @@ -10,9 +10,15 @@ #include "emu.h" #include "emuopts.h" + #include "drivenum.h" -#include "softlist_dev.h" #include "hashfile.h" +#include "main.h" +#include "softlist_dev.h" + +// lib/util +#include "corestr.h" +#include "path.h" #include <stack> @@ -24,196 +30,202 @@ const options_entry emu_options::s_option_entries[] = { // unadorned options - only a single one supported at the moment - { OPTION_SYSTEMNAME, nullptr, OPTION_STRING, nullptr }, - { OPTION_SOFTWARENAME, nullptr, OPTION_STRING, nullptr }, + { OPTION_SYSTEMNAME, nullptr, core_options::option_type::STRING, nullptr }, + { OPTION_SOFTWARENAME, nullptr, core_options::option_type::STRING, nullptr }, // config options - { nullptr, nullptr, OPTION_HEADER, "CORE CONFIGURATION OPTIONS" }, - { OPTION_READCONFIG ";rc", "1", OPTION_BOOLEAN, "enable loading of configuration files" }, - { OPTION_WRITECONFIG ";wc", "0", OPTION_BOOLEAN, "write configuration to (driver).ini on exit" }, + { nullptr, nullptr, core_options::option_type::HEADER, "CORE CONFIGURATION OPTIONS" }, + { OPTION_READCONFIG ";rc", "1", core_options::option_type::BOOLEAN, "enable loading of configuration files" }, + { OPTION_WRITECONFIG ";wc", "0", core_options::option_type::BOOLEAN, "write configuration to (driver).ini on exit" }, // search path options - { nullptr, nullptr, OPTION_HEADER, "CORE SEARCH PATH OPTIONS" }, - { OPTION_HOMEPATH, ".", OPTION_STRING, "path to base folder for plugin data (read/write)" }, - { OPTION_MEDIAPATH ";rp;biospath;bp", "roms", OPTION_STRING, "path to ROM sets and hard disk images" }, - { OPTION_HASHPATH ";hash_directory;hash", "hash", OPTION_STRING, "path to software definition files" }, - { OPTION_SAMPLEPATH ";sp", "samples", OPTION_STRING, "path to audio sample sets" }, - { OPTION_ARTPATH, "artwork", OPTION_STRING, "path to artwork files" }, - { OPTION_CTRLRPATH, "ctrlr", OPTION_STRING, "path to controller definitions" }, - { OPTION_INIPATH, ".;ini;ini/presets", OPTION_STRING, "path to ini files" }, - { OPTION_FONTPATH, ".", OPTION_STRING, "path to font files" }, - { OPTION_CHEATPATH, "cheat", OPTION_STRING, "path to cheat files" }, - { OPTION_CROSSHAIRPATH, "crosshair", OPTION_STRING, "path to crosshair files" }, - { OPTION_PLUGINSPATH, "plugins", OPTION_STRING, "path to plugin files" }, - { OPTION_LANGUAGEPATH, "language", OPTION_STRING, "path to UI translation files" }, - { OPTION_SWPATH, "software", OPTION_STRING, "path to loose software" }, + { nullptr, nullptr, core_options::option_type::HEADER, "CORE SEARCH PATH OPTIONS" }, + { OPTION_PLUGINDATAPATH, ".", core_options::option_type::PATH, "path to base folder for plugin data (read/write)" }, + { OPTION_MEDIAPATH ";rp;biospath;bp", "roms", core_options::option_type::MULTIPATH, "path to ROM sets and hard disk images" }, + { OPTION_HASHPATH ";hash_directory;hash", "hash", core_options::option_type::MULTIPATH, "path to software definition files" }, + { OPTION_SAMPLEPATH ";sp", "samples", core_options::option_type::MULTIPATH, "path to audio sample sets" }, + { OPTION_ARTPATH, "artwork", core_options::option_type::MULTIPATH, "path to artwork files" }, + { OPTION_CTRLRPATH, "ctrlr", core_options::option_type::MULTIPATH, "path to controller definitions" }, + { OPTION_INIPATH, ".;ini;ini/presets", core_options::option_type::MULTIPATH, "path to ini files" }, + { OPTION_FONTPATH, ".", core_options::option_type::MULTIPATH, "path to font files" }, + { OPTION_CHEATPATH, "cheat", core_options::option_type::MULTIPATH, "path to cheat files" }, + { OPTION_CROSSHAIRPATH, "crosshair", core_options::option_type::MULTIPATH, "path to crosshair files" }, + { OPTION_PLUGINSPATH, "plugins", core_options::option_type::MULTIPATH, "path to plugin files" }, + { OPTION_LANGUAGEPATH, "language", core_options::option_type::MULTIPATH, "path to UI translation files" }, + { OPTION_SWPATH, "software", core_options::option_type::MULTIPATH, "path to loose software" }, // output directory options - { nullptr, nullptr, OPTION_HEADER, "CORE OUTPUT DIRECTORY OPTIONS" }, - { OPTION_CFG_DIRECTORY, "cfg", OPTION_STRING, "directory to save configurations" }, - { OPTION_NVRAM_DIRECTORY, "nvram", OPTION_STRING, "directory to save NVRAM contents" }, - { OPTION_INPUT_DIRECTORY, "inp", OPTION_STRING, "directory to save input device logs" }, - { OPTION_STATE_DIRECTORY, "sta", OPTION_STRING, "directory to save states" }, - { OPTION_SNAPSHOT_DIRECTORY, "snap", OPTION_STRING, "directory to save/load screenshots" }, - { OPTION_DIFF_DIRECTORY, "diff", OPTION_STRING, "directory to save hard drive image difference files" }, - { OPTION_COMMENT_DIRECTORY, "comments", OPTION_STRING, "directory to save debugger comments" }, + { nullptr, nullptr, core_options::option_type::HEADER, "CORE OUTPUT DIRECTORY OPTIONS" }, + { OPTION_CFG_DIRECTORY, "cfg", core_options::option_type::PATH, "directory to save configurations" }, + { OPTION_NVRAM_DIRECTORY, "nvram", core_options::option_type::PATH, "directory to save NVRAM contents" }, + { OPTION_INPUT_DIRECTORY, "inp", core_options::option_type::PATH, "directory to save input device logs" }, + { OPTION_STATE_DIRECTORY, "sta", core_options::option_type::PATH, "directory to save states" }, + { OPTION_SNAPSHOT_DIRECTORY, "snap", core_options::option_type::PATH, "directory to save/load screenshots" }, + { OPTION_DIFF_DIRECTORY, "diff", core_options::option_type::PATH, "directory to save hard drive image difference files" }, + { OPTION_COMMENT_DIRECTORY, "comments", core_options::option_type::PATH, "directory to save debugger comments" }, + { OPTION_SHARE_DIRECTORY, "share", core_options::option_type::PATH, "directory to share with emulated machines" }, // state/playback options - { nullptr, nullptr, OPTION_HEADER, "CORE STATE/PLAYBACK OPTIONS" }, - { OPTION_STATE, nullptr, OPTION_STRING, "saved state to load" }, - { OPTION_AUTOSAVE, "0", OPTION_BOOLEAN, "automatically restore state on start and save on exit for supported systems" }, - { OPTION_REWIND, "0", OPTION_BOOLEAN, "enable rewind savestates" }, - { OPTION_REWIND_CAPACITY "(1-2048)", "100", OPTION_INTEGER, "rewind buffer size in megabytes" }, - { OPTION_PLAYBACK ";pb", nullptr, OPTION_STRING, "playback an input file" }, - { OPTION_RECORD ";rec", nullptr, OPTION_STRING, "record an input file" }, - { OPTION_RECORD_TIMECODE, "0", OPTION_BOOLEAN, "record an input timecode file (requires -record option)" }, - { OPTION_EXIT_AFTER_PLAYBACK, "0", OPTION_BOOLEAN, "close the program at the end of playback" }, - - { OPTION_MNGWRITE, nullptr, OPTION_STRING, "optional filename to write a MNG movie of the current session" }, - { OPTION_AVIWRITE, nullptr, OPTION_STRING, "optional filename to write an AVI movie of the current session" }, - { OPTION_WAVWRITE, nullptr, OPTION_STRING, "optional filename to write a WAV file of the current session" }, - { OPTION_SNAPNAME, "%g/%i", OPTION_STRING, "override of the default snapshot/movie naming; %g == gamename, %i == index" }, - { OPTION_SNAPSIZE, "auto", OPTION_STRING, "specify snapshot/movie resolution (<width>x<height>) or 'auto' to use minimal size " }, - { OPTION_SNAPVIEW, "internal", OPTION_STRING, "specify snapshot/movie view or 'internal' to use internal pixel-aspect views" }, - { OPTION_SNAPBILINEAR, "1", OPTION_BOOLEAN, "specify if the snapshot/movie should have bilinear filtering applied" }, - { OPTION_STATENAME, "%g", OPTION_STRING, "override of the default state subfolder naming; %g == gamename" }, - { OPTION_BURNIN, "0", OPTION_BOOLEAN, "create burn-in snapshots for each screen" }, + { nullptr, nullptr, core_options::option_type::HEADER, "CORE STATE/PLAYBACK OPTIONS" }, + { OPTION_STATE, nullptr, core_options::option_type::STRING, "saved state to load" }, + { OPTION_AUTOSAVE, "0", core_options::option_type::BOOLEAN, "automatically restore state on start and save on exit for supported systems" }, + { OPTION_REWIND, "0", core_options::option_type::BOOLEAN, "enable rewind savestates" }, + { OPTION_REWIND_CAPACITY "(1-2048)", "100", core_options::option_type::INTEGER, "rewind buffer size in megabytes" }, + { OPTION_PLAYBACK ";pb", nullptr, core_options::option_type::STRING, "playback an input file" }, + { OPTION_RECORD ";rec", nullptr, core_options::option_type::STRING, "record an input file" }, + { OPTION_EXIT_AFTER_PLAYBACK, "0", core_options::option_type::BOOLEAN, "close the program at the end of playback" }, + + { OPTION_MNGWRITE, nullptr, core_options::option_type::PATH, "optional filename to write a MNG movie of the current session" }, + { OPTION_AVIWRITE, nullptr, core_options::option_type::PATH, "optional filename to write an AVI movie of the current session" }, + { OPTION_WAVWRITE, nullptr, core_options::option_type::PATH, "optional filename to write a WAV file of the current session" }, + { OPTION_SNAPNAME, "%g/%i", core_options::option_type::STRING, "override of the default snapshot/movie naming; %g == gamename, %i == index" }, + { OPTION_SNAPSIZE, "auto", core_options::option_type::STRING, "specify snapshot/movie resolution (<width>x<height>) or 'auto' to use minimal size " }, + { OPTION_SNAPVIEW, "auto", core_options::option_type::STRING, "snapshot/movie view - 'auto' for default, or 'native' for per-screen pixel-aspect views" }, + { OPTION_SNAPBILINEAR, "1", core_options::option_type::BOOLEAN, "specify if the snapshot/movie should have bilinear filtering applied" }, + { OPTION_STATENAME, "%g", core_options::option_type::STRING, "override of the default state subfolder naming; %g == gamename" }, + { OPTION_BURNIN, "0", core_options::option_type::BOOLEAN, "create burn-in snapshots for each screen" }, // performance options - { nullptr, nullptr, OPTION_HEADER, "CORE PERFORMANCE OPTIONS" }, - { OPTION_AUTOFRAMESKIP ";afs", "0", OPTION_BOOLEAN, "enable automatic frameskip adjustment to maintain emulation speed" }, - { OPTION_FRAMESKIP ";fs(0-10)", "0", OPTION_INTEGER, "set frameskip to fixed value, 0-10 (autoframeskip must be disabled)" }, - { OPTION_SECONDS_TO_RUN ";str", "0", OPTION_INTEGER, "number of emulated seconds to run before automatically exiting" }, - { OPTION_THROTTLE, "1", OPTION_BOOLEAN, "throttle emulation to keep system running in sync with real time" }, - { OPTION_SLEEP, "1", OPTION_BOOLEAN, "enable sleeping, which gives time back to other applications when idle" }, - { OPTION_SPEED "(0.01-100)", "1.0", OPTION_FLOAT, "controls the speed of gameplay, relative to realtime; smaller numbers are slower" }, - { OPTION_REFRESHSPEED ";rs", "0", OPTION_BOOLEAN, "automatically adjust emulation speed to keep the emulated refresh rate slower than the host screen" }, + { nullptr, nullptr, core_options::option_type::HEADER, "CORE PERFORMANCE OPTIONS" }, + { OPTION_AUTOFRAMESKIP ";afs", "0", core_options::option_type::BOOLEAN, "enable automatic frameskip adjustment to maintain emulation speed" }, + { OPTION_FRAMESKIP ";fs(0-10)", "0", core_options::option_type::INTEGER, "set frameskip to fixed value, 0-10 (upper limit with autoframeskip)" }, + { OPTION_SECONDS_TO_RUN ";str", "0", core_options::option_type::INTEGER, "number of emulated seconds to run before automatically exiting" }, + { OPTION_THROTTLE, "1", core_options::option_type::BOOLEAN, "throttle emulation to keep system running in sync with real time" }, + { OPTION_SLEEP, "1", core_options::option_type::BOOLEAN, "enable sleeping, which gives time back to other applications when idle" }, + { OPTION_SPEED "(0.01-100)", "1.0", core_options::option_type::FLOAT, "controls the speed of gameplay, relative to realtime; smaller numbers are slower" }, + { OPTION_REFRESHSPEED ";rs", "0", core_options::option_type::BOOLEAN, "automatically adjust emulation speed to keep the emulated refresh rate slower than the host screen" }, + { OPTION_LOWLATENCY ";lolat", "0", core_options::option_type::BOOLEAN, "draws new frame before throttling to reduce input latency" }, // render options - { nullptr, nullptr, OPTION_HEADER, "CORE RENDER OPTIONS" }, - { OPTION_KEEPASPECT ";ka", "1", OPTION_BOOLEAN, "maintain aspect ratio when scaling to fill output screen/window" }, - { OPTION_UNEVENSTRETCH ";ues", "1", OPTION_BOOLEAN, "allow non-integer ratios when scaling to fill output screen/window horizontally or vertically" }, - { OPTION_UNEVENSTRETCHX ";uesx", "0", OPTION_BOOLEAN, "allow non-integer ratios when scaling to fill output screen/window horizontally"}, - { OPTION_UNEVENSTRETCHY ";uesy", "0", OPTION_BOOLEAN, "allow non-integer ratios when scaling to fill otuput screen/window vertially"}, - { OPTION_AUTOSTRETCHXY ";asxy", "0", OPTION_BOOLEAN, "automatically apply -unevenstretchx/y based on source native orientation"}, - { OPTION_INTOVERSCAN ";ios", "0", OPTION_BOOLEAN, "allow overscan on integer scaled targets"}, - { OPTION_INTSCALEX ";sx", "0", OPTION_INTEGER, "set horizontal integer scale factor"}, - { OPTION_INTSCALEY ";sy", "0", OPTION_INTEGER, "set vertical integer scale factor"}, + { nullptr, nullptr, core_options::option_type::HEADER, "CORE RENDER OPTIONS" }, + { OPTION_KEEPASPECT ";ka", "1", core_options::option_type::BOOLEAN, "maintain aspect ratio when scaling to fill output screen/window" }, + { OPTION_UNEVENSTRETCH ";ues", "1", core_options::option_type::BOOLEAN, "allow non-integer ratios when scaling to fill output screen/window horizontally or vertically" }, + { OPTION_UNEVENSTRETCHX ";uesx", "0", core_options::option_type::BOOLEAN, "allow non-integer ratios when scaling to fill output screen/window horizontally"}, + { OPTION_UNEVENSTRETCHY ";uesy", "0", core_options::option_type::BOOLEAN, "allow non-integer ratios when scaling to fill otuput screen/window vertially"}, + { OPTION_AUTOSTRETCHXY ";asxy", "0", core_options::option_type::BOOLEAN, "automatically apply -unevenstretchx/y based on source native orientation"}, + { OPTION_INTOVERSCAN ";ios", "0", core_options::option_type::BOOLEAN, "allow overscan on integer scaled targets"}, + { OPTION_INTSCALEX ";sx", "0", core_options::option_type::INTEGER, "set horizontal integer scale factor"}, + { OPTION_INTSCALEY ";sy", "0", core_options::option_type::INTEGER, "set vertical integer scale factor"}, // rotation options - { nullptr, nullptr, OPTION_HEADER, "CORE ROTATION OPTIONS" }, - { OPTION_ROTATE, "1", OPTION_BOOLEAN, "rotate the game screen according to the game's orientation when needed" }, - { OPTION_ROR, "0", OPTION_BOOLEAN, "rotate screen clockwise 90 degrees" }, - { OPTION_ROL, "0", OPTION_BOOLEAN, "rotate screen counterclockwise 90 degrees" }, - { OPTION_AUTOROR, "0", OPTION_BOOLEAN, "automatically rotate screen clockwise 90 degrees if vertical" }, - { OPTION_AUTOROL, "0", OPTION_BOOLEAN, "automatically rotate screen counterclockwise 90 degrees if vertical" }, - { OPTION_FLIPX, "0", OPTION_BOOLEAN, "flip screen left-right" }, - { OPTION_FLIPY, "0", OPTION_BOOLEAN, "flip screen upside-down" }, + { nullptr, nullptr, core_options::option_type::HEADER, "CORE ROTATION OPTIONS" }, + { OPTION_ROTATE, "1", core_options::option_type::BOOLEAN, "rotate the game screen according to the game's orientation when needed" }, + { OPTION_ROR, "0", core_options::option_type::BOOLEAN, "rotate screen clockwise 90 degrees" }, + { OPTION_ROL, "0", core_options::option_type::BOOLEAN, "rotate screen counterclockwise 90 degrees" }, + { OPTION_AUTOROR, "0", core_options::option_type::BOOLEAN, "automatically rotate screen clockwise 90 degrees if vertical" }, + { OPTION_AUTOROL, "0", core_options::option_type::BOOLEAN, "automatically rotate screen counterclockwise 90 degrees if vertical" }, + { OPTION_FLIPX, "0", core_options::option_type::BOOLEAN, "flip screen left-right" }, + { OPTION_FLIPY, "0", core_options::option_type::BOOLEAN, "flip screen upside-down" }, // artwork options - { nullptr, nullptr, OPTION_HEADER, "CORE ARTWORK OPTIONS" }, - { OPTION_ARTWORK_CROP ";artcrop", "0", OPTION_BOOLEAN, "crop artwork so emulated screen image fills output screen/window in one axis" }, - { OPTION_FALLBACK_ARTWORK, nullptr, OPTION_STRING, "fallback artwork if no external artwork or internal driver layout defined" }, - { OPTION_OVERRIDE_ARTWORK, nullptr, OPTION_STRING, "override artwork for external artwork and internal driver layout" }, + { nullptr, nullptr, core_options::option_type::HEADER, "CORE ARTWORK OPTIONS" }, + { OPTION_ARTWORK_CROP ";artcrop", "0", core_options::option_type::BOOLEAN, "crop artwork so emulated screen image fills output screen/window in one axis" }, + { OPTION_FALLBACK_ARTWORK, nullptr, core_options::option_type::STRING, "fallback artwork if no external artwork or internal driver layout defined" }, + { OPTION_OVERRIDE_ARTWORK, nullptr, core_options::option_type::STRING, "override artwork for external artwork and internal driver layout" }, // screen options - { nullptr, nullptr, OPTION_HEADER, "CORE SCREEN OPTIONS" }, - { OPTION_BRIGHTNESS "(0.1-2.0)", "1.0", OPTION_FLOAT, "default game screen brightness correction" }, - { OPTION_CONTRAST "(0.1-2.0)", "1.0", OPTION_FLOAT, "default game screen contrast correction" }, - { OPTION_GAMMA "(0.1-3.0)", "1.0", OPTION_FLOAT, "default game screen gamma correction" }, - { OPTION_PAUSE_BRIGHTNESS "(0.0-1.0)", "0.65", OPTION_FLOAT, "amount to scale the screen brightness when paused" }, - { OPTION_EFFECT, "none", OPTION_STRING, "name of a PNG file to use for visual effects, or 'none'" }, + { nullptr, nullptr, core_options::option_type::HEADER, "CORE SCREEN OPTIONS" }, + { OPTION_BRIGHTNESS "(0.1-2.0)", "1.0", core_options::option_type::FLOAT, "default game screen brightness correction" }, + { OPTION_CONTRAST "(0.1-2.0)", "1.0", core_options::option_type::FLOAT, "default game screen contrast correction" }, + { OPTION_GAMMA "(0.1-3.0)", "1.0", core_options::option_type::FLOAT, "default game screen gamma correction" }, + { OPTION_PAUSE_BRIGHTNESS "(0.0-1.0)", "0.65", core_options::option_type::FLOAT, "amount to scale the screen brightness when paused" }, + { OPTION_EFFECT, "none", core_options::option_type::STRING, "name of a PNG file to use for visual effects, or 'none'" }, // vector options - { nullptr, nullptr, OPTION_HEADER, "CORE VECTOR OPTIONS" }, - { OPTION_BEAM_WIDTH_MIN, "1.0", OPTION_FLOAT, "set vector beam width minimum" }, - { OPTION_BEAM_WIDTH_MAX, "1.0", OPTION_FLOAT, "set vector beam width maximum" }, - { OPTION_BEAM_INTENSITY_WEIGHT, "0", OPTION_FLOAT, "set vector beam intensity weight " }, - { OPTION_FLICKER, "0", OPTION_FLOAT, "set vector flicker effect" }, + { nullptr, nullptr, core_options::option_type::HEADER, "CORE VECTOR OPTIONS" }, + { OPTION_BEAM_WIDTH_MIN, "1.0", core_options::option_type::FLOAT, "set vector beam width minimum" }, + { OPTION_BEAM_WIDTH_MAX, "1.0", core_options::option_type::FLOAT, "set vector beam width maximum" }, + { OPTION_BEAM_DOT_SIZE, "1.0", core_options::option_type::FLOAT, "set vector beam size for dots" }, + { OPTION_BEAM_INTENSITY_WEIGHT, "0", core_options::option_type::FLOAT, "set vector beam intensity weight " }, + { OPTION_FLICKER, "0", core_options::option_type::FLOAT, "set vector flicker effect" }, // sound options - { nullptr, nullptr, OPTION_HEADER, "CORE SOUND OPTIONS" }, - { OPTION_SAMPLERATE ";sr(1000-1000000)", "48000", OPTION_INTEGER, "set sound output sample rate" }, - { OPTION_SAMPLES, "1", OPTION_BOOLEAN, "enable the use of external samples if available" }, - { OPTION_VOLUME ";vol", "0", OPTION_INTEGER, "sound volume in decibels (-32 min, 0 max)" }, + { nullptr, nullptr, core_options::option_type::HEADER, "CORE SOUND OPTIONS" }, + { OPTION_SAMPLERATE ";sr(1000-1000000)", "48000", core_options::option_type::INTEGER, "set sound output sample rate" }, + { OPTION_SAMPLES, "1", core_options::option_type::BOOLEAN, "enable the use of external samples if available" }, + { OPTION_VOLUME ";vol", "0", core_options::option_type::INTEGER, "sound volume in decibels (-32 min, 0 max)" }, + { OPTION_COMPRESSOR, "1", core_options::option_type::BOOLEAN, "enable compressor for sound" }, + { OPTION_SPEAKER_REPORT "(0-4)", "0", core_options::option_type::INTEGER, "print report of speaker ouput maxima (0=none, or 1-4 for more detail)" }, // input options - { nullptr, nullptr, OPTION_HEADER, "CORE INPUT OPTIONS" }, - { OPTION_COIN_LOCKOUT ";coinlock", "1", OPTION_BOOLEAN, "ignore coin inputs if coin lockout ouput is active" }, - { OPTION_CTRLR, nullptr, OPTION_STRING, "preconfigure for specified controller" }, - { OPTION_MOUSE, "0", OPTION_BOOLEAN, "enable mouse input" }, - { OPTION_JOYSTICK ";joy", "1", OPTION_BOOLEAN, "enable joystick input" }, - { OPTION_LIGHTGUN ";gun", "0", OPTION_BOOLEAN, "enable lightgun input" }, - { OPTION_MULTIKEYBOARD ";multikey", "0", OPTION_BOOLEAN, "enable separate input from each keyboard device (if present)" }, - { OPTION_MULTIMOUSE, "0", OPTION_BOOLEAN, "enable separate input from each mouse device (if present)" }, - { OPTION_STEADYKEY ";steady", "0", OPTION_BOOLEAN, "enable steadykey support" }, - { OPTION_UI_ACTIVE, "0", OPTION_BOOLEAN, "enable user interface on top of emulated keyboard (if present)" }, - { OPTION_OFFSCREEN_RELOAD ";reload", "0", OPTION_BOOLEAN, "convert lightgun button 2 into offscreen reload" }, - { OPTION_JOYSTICK_MAP ";joymap", "auto", OPTION_STRING, "explicit joystick map, or auto to auto-select" }, - { OPTION_JOYSTICK_DEADZONE ";joy_deadzone;jdz(0.00-1)", "0.3", OPTION_FLOAT, "center deadzone range for joystick where change is ignored (0.0 center, 1.0 end)" }, - { OPTION_JOYSTICK_SATURATION ";joy_saturation;jsat(0.00-1)", "0.85", OPTION_FLOAT, "end of axis saturation range for joystick where change is ignored (0.0 center, 1.0 end)" }, - { OPTION_NATURAL_KEYBOARD ";nat", "0", OPTION_BOOLEAN, "specifies whether to use a natural keyboard or not" }, - { OPTION_JOYSTICK_CONTRADICTORY ";joy_contradictory","0", OPTION_BOOLEAN, "enable contradictory direction digital joystick input at the same time" }, - { OPTION_COIN_IMPULSE, "0", OPTION_INTEGER, "set coin impulse time (n<0 disable impulse, n==0 obey driver, 0<n set time n)" }, + { nullptr, nullptr, core_options::option_type::HEADER, "CORE INPUT OPTIONS" }, + { OPTION_COIN_LOCKOUT ";coinlock", "1", core_options::option_type::BOOLEAN, "ignore coin inputs if coin lockout output is active" }, + { OPTION_CTRLR, nullptr, core_options::option_type::STRING, "preconfigure for specified controller" }, + { OPTION_MOUSE, "0", core_options::option_type::BOOLEAN, "enable mouse input" }, + { OPTION_JOYSTICK ";joy", "1", core_options::option_type::BOOLEAN, "enable joystick input" }, + { OPTION_LIGHTGUN ";gun", "0", core_options::option_type::BOOLEAN, "enable lightgun input" }, + { OPTION_MULTIKEYBOARD ";multikey", "0", core_options::option_type::BOOLEAN, "enable separate input from each keyboard device (if present)" }, + { OPTION_MULTIMOUSE, "0", core_options::option_type::BOOLEAN, "enable separate input from each mouse device (if present)" }, + { OPTION_STEADYKEY ";steady", "0", core_options::option_type::BOOLEAN, "enable steadykey support" }, + { OPTION_UI_ACTIVE, "0", core_options::option_type::BOOLEAN, "enable user interface on top of emulated keyboard (if present)" }, + { OPTION_OFFSCREEN_RELOAD ";reload", "0", core_options::option_type::BOOLEAN, "convert lightgun button 2 into offscreen reload" }, + { OPTION_JOYSTICK_MAP ";joymap", "auto", core_options::option_type::STRING, "explicit joystick map, or auto to auto-select" }, + { OPTION_JOYSTICK_DEADZONE ";joy_deadzone;jdz(0.00-1)", "0.15", core_options::option_type::FLOAT, "center deadzone range for joystick where change is ignored (0.0 center, 1.0 end)" }, + { OPTION_JOYSTICK_SATURATION ";joy_saturation;jsat(0.00-1)", "0.85", core_options::option_type::FLOAT, "end of axis saturation range for joystick where change is ignored (0.0 center, 1.0 end)" }, + { OPTION_JOYSTICK_THRESHOLD ";joy_threshold;jthresh(0.00-1)", "0.3", core_options::option_type::FLOAT, "threshold for joystick to be considered active as a switch (0.0 center, 1.0 end)" }, + { OPTION_NATURAL_KEYBOARD ";nat", "0", core_options::option_type::BOOLEAN, "specifies whether to use a natural keyboard or not" }, + { OPTION_JOYSTICK_CONTRADICTORY ";joy_contradictory","0", core_options::option_type::BOOLEAN, "enable contradictory direction digital joystick input at the same time" }, + { OPTION_COIN_IMPULSE, "0", core_options::option_type::INTEGER, "set coin impulse time (n<0 disable impulse, n==0 obey driver, 0<n set time n)" }, // input autoenable options - { nullptr, nullptr, OPTION_HEADER, "CORE INPUT AUTOMATIC ENABLE OPTIONS" }, - { OPTION_PADDLE_DEVICE ";paddle", "keyboard", OPTION_STRING, "enable (none|keyboard|mouse|lightgun|joystick) if a paddle control is present" }, - { OPTION_ADSTICK_DEVICE ";adstick", "keyboard", OPTION_STRING, "enable (none|keyboard|mouse|lightgun|joystick) if an analog joystick control is present" }, - { OPTION_PEDAL_DEVICE ";pedal", "keyboard", OPTION_STRING, "enable (none|keyboard|mouse|lightgun|joystick) if a pedal control is present" }, - { OPTION_DIAL_DEVICE ";dial", "keyboard", OPTION_STRING, "enable (none|keyboard|mouse|lightgun|joystick) if a dial control is present" }, - { OPTION_TRACKBALL_DEVICE ";trackball", "keyboard", OPTION_STRING, "enable (none|keyboard|mouse|lightgun|joystick) if a trackball control is present" }, - { OPTION_LIGHTGUN_DEVICE, "keyboard", OPTION_STRING, "enable (none|keyboard|mouse|lightgun|joystick) if a lightgun control is present" }, - { OPTION_POSITIONAL_DEVICE, "keyboard", OPTION_STRING, "enable (none|keyboard|mouse|lightgun|joystick) if a positional control is present" }, - { OPTION_MOUSE_DEVICE, "mouse", OPTION_STRING, "enable (none|keyboard|mouse|lightgun|joystick) if a mouse control is present" }, + { nullptr, nullptr, core_options::option_type::HEADER, "CORE INPUT AUTOMATIC ENABLE OPTIONS" }, + { OPTION_PADDLE_DEVICE ";paddle", "keyboard", core_options::option_type::STRING, "enable (none|keyboard|mouse|lightgun|joystick) if a paddle control is present" }, + { OPTION_ADSTICK_DEVICE ";adstick", "keyboard", core_options::option_type::STRING, "enable (none|keyboard|mouse|lightgun|joystick) if an analog joystick control is present" }, + { OPTION_PEDAL_DEVICE ";pedal", "keyboard", core_options::option_type::STRING, "enable (none|keyboard|mouse|lightgun|joystick) if a pedal control is present" }, + { OPTION_DIAL_DEVICE ";dial", "keyboard", core_options::option_type::STRING, "enable (none|keyboard|mouse|lightgun|joystick) if a dial control is present" }, + { OPTION_TRACKBALL_DEVICE ";trackball", "keyboard", core_options::option_type::STRING, "enable (none|keyboard|mouse|lightgun|joystick) if a trackball control is present" }, + { OPTION_LIGHTGUN_DEVICE, "keyboard", core_options::option_type::STRING, "enable (none|keyboard|mouse|lightgun|joystick) if a lightgun control is present" }, + { OPTION_POSITIONAL_DEVICE, "keyboard", core_options::option_type::STRING, "enable (none|keyboard|mouse|lightgun|joystick) if a positional control is present" }, + { OPTION_MOUSE_DEVICE, "mouse", core_options::option_type::STRING, "enable (none|keyboard|mouse|lightgun|joystick) if a mouse control is present" }, // debugging options - { nullptr, nullptr, OPTION_HEADER, "CORE DEBUGGING OPTIONS" }, - { OPTION_VERBOSE ";v", "0", OPTION_BOOLEAN, "display additional diagnostic information" }, - { OPTION_LOG, "0", OPTION_BOOLEAN, "generate an error.log file" }, - { OPTION_OSLOG, "0", OPTION_BOOLEAN, "output error.log data to system diagnostic output (debugger or standard error)" }, - { OPTION_DEBUG ";d", "0", OPTION_BOOLEAN, "enable/disable debugger" }, - { OPTION_UPDATEINPAUSE, "0", OPTION_BOOLEAN, "keep calling video updates while in pause" }, - { OPTION_DEBUGSCRIPT, nullptr, OPTION_STRING, "script for debugger" }, + { nullptr, nullptr, core_options::option_type::HEADER, "CORE DEBUGGING OPTIONS" }, + { OPTION_VERBOSE ";v", "0", core_options::option_type::BOOLEAN, "display additional diagnostic information" }, + { OPTION_LOG, "0", core_options::option_type::BOOLEAN, "generate an error.log file" }, + { OPTION_OSLOG, "0", core_options::option_type::BOOLEAN, "output error.log data to system diagnostic output (debugger or standard error)" }, + { OPTION_DEBUG ";d", "0", core_options::option_type::BOOLEAN, "enable/disable debugger" }, + { OPTION_UPDATEINPAUSE, "0", core_options::option_type::BOOLEAN, "keep calling video updates while in pause" }, + { OPTION_DEBUGSCRIPT, nullptr, core_options::option_type::PATH, "script for debugger" }, + { OPTION_DEBUGLOG, "0", core_options::option_type::BOOLEAN, "write debug console output to debug.log" }, // comm options - { nullptr, nullptr, OPTION_HEADER, "CORE COMM OPTIONS" }, - { OPTION_COMM_LOCAL_HOST, "0.0.0.0", OPTION_STRING, "local address to bind to" }, - { OPTION_COMM_LOCAL_PORT, "15112", OPTION_STRING, "local port to bind to" }, - { OPTION_COMM_REMOTE_HOST, "127.0.0.1", OPTION_STRING, "remote address to connect to" }, - { OPTION_COMM_REMOTE_PORT, "15112", OPTION_STRING, "remote port to connect to" }, - { OPTION_COMM_FRAME_SYNC, "0", OPTION_BOOLEAN, "sync frames" }, + { nullptr, nullptr, core_options::option_type::HEADER, "CORE COMM OPTIONS" }, + { OPTION_COMM_LOCAL_HOST, "0.0.0.0", core_options::option_type::STRING, "local address to bind to" }, + { OPTION_COMM_LOCAL_PORT, "15112", core_options::option_type::STRING, "local port to bind to" }, + { OPTION_COMM_REMOTE_HOST, "127.0.0.1", core_options::option_type::STRING, "remote address to connect to" }, + { OPTION_COMM_REMOTE_PORT, "15112", core_options::option_type::STRING, "remote port to connect to" }, + { OPTION_COMM_FRAME_SYNC, "0", core_options::option_type::BOOLEAN, "sync frames" }, // misc options - { nullptr, nullptr, OPTION_HEADER, "CORE MISC OPTIONS" }, - { OPTION_DRC, "1", OPTION_BOOLEAN, "enable DRC CPU core if available" }, - { OPTION_DRC_USE_C, "0", OPTION_BOOLEAN, "force DRC to use C backend" }, - { OPTION_DRC_LOG_UML, "0", OPTION_BOOLEAN, "write DRC UML disassembly log" }, - { OPTION_DRC_LOG_NATIVE, "0", OPTION_BOOLEAN, "write DRC native disassembly log" }, - { OPTION_BIOS, nullptr, OPTION_STRING, "select the system BIOS to use" }, - { OPTION_CHEAT ";c", "0", OPTION_BOOLEAN, "enable cheat subsystem" }, - { OPTION_SKIP_GAMEINFO, "0", OPTION_BOOLEAN, "skip displaying the system information screen at startup" }, - { OPTION_UI_FONT, "default", OPTION_STRING, "specify a font to use" }, - { OPTION_UI, "cabinet", OPTION_STRING, "type of UI (simple|cabinet)" }, - { OPTION_RAMSIZE ";ram", nullptr, OPTION_STRING, "size of RAM (if supported by driver)" }, - { OPTION_CONFIRM_QUIT, "0", OPTION_BOOLEAN, "ask for confirmation before exiting" }, - { OPTION_UI_MOUSE, "1", OPTION_BOOLEAN, "display UI mouse cursor" }, - { OPTION_LANGUAGE ";lang", "English", OPTION_STRING, "set UI display language" }, - { OPTION_NVRAM_SAVE ";nvwrite", "1", OPTION_BOOLEAN, "save NVRAM data on exit" }, - - { nullptr, nullptr, OPTION_HEADER, "SCRIPTING OPTIONS" }, - { OPTION_AUTOBOOT_COMMAND ";ab", nullptr, OPTION_STRING, "command to execute after machine boot" }, - { OPTION_AUTOBOOT_DELAY, "0", OPTION_INTEGER, "delay before executing autoboot command (seconds)" }, - { OPTION_AUTOBOOT_SCRIPT ";script", nullptr, OPTION_STRING, "Lua script to execute after machine boot" }, - { OPTION_CONSOLE, "0", OPTION_BOOLEAN, "enable emulator Lua console" }, - { OPTION_PLUGINS, "1", OPTION_BOOLEAN, "enable Lua plugin support" }, - { OPTION_PLUGIN, nullptr, OPTION_STRING, "list of plugins to enable" }, - { OPTION_NO_PLUGIN, nullptr, OPTION_STRING, "list of plugins to disable" }, - - { nullptr, nullptr, OPTION_HEADER, "HTTP SERVER OPTIONS" }, - { OPTION_HTTP, "0", OPTION_BOOLEAN, "enable HTTP server" }, - { OPTION_HTTP_PORT, "8080", OPTION_INTEGER, "HTTP server port" }, - { OPTION_HTTP_ROOT, "web", OPTION_STRING, "HTTP server document root" }, + { nullptr, nullptr, core_options::option_type::HEADER, "CORE MISC OPTIONS" }, + { OPTION_DRC, "1", core_options::option_type::BOOLEAN, "enable DRC CPU core if available" }, + { OPTION_DRC_USE_C, "0", core_options::option_type::BOOLEAN, "force DRC to use C backend" }, + { OPTION_DRC_LOG_UML, "0", core_options::option_type::BOOLEAN, "write DRC UML disassembly log" }, + { OPTION_DRC_LOG_NATIVE, "0", core_options::option_type::BOOLEAN, "write DRC native disassembly log" }, + { OPTION_BIOS, nullptr, core_options::option_type::STRING, "select the system BIOS to use" }, + { OPTION_CHEAT ";c", "0", core_options::option_type::BOOLEAN, "enable cheat subsystem" }, + { OPTION_SKIP_GAMEINFO, "0", core_options::option_type::BOOLEAN, "skip displaying the system information screen at startup" }, + { OPTION_UI_FONT, "default", core_options::option_type::STRING, "specify a font to use" }, + { OPTION_UI, "cabinet", core_options::option_type::STRING, "type of UI (simple|cabinet)" }, + { OPTION_RAMSIZE ";ram", nullptr, core_options::option_type::STRING, "size of RAM (if supported by driver)" }, + { OPTION_CONFIRM_QUIT, "0", core_options::option_type::BOOLEAN, "ask for confirmation before exiting" }, + { OPTION_UI_MOUSE, "1", core_options::option_type::BOOLEAN, "display UI mouse cursor" }, + { OPTION_LANGUAGE ";lang", "", core_options::option_type::STRING, "set UI display language" }, + { OPTION_NVRAM_SAVE ";nvwrite", "1", core_options::option_type::BOOLEAN, "save NVRAM data on exit" }, + + { nullptr, nullptr, core_options::option_type::HEADER, "SCRIPTING OPTIONS" }, + { OPTION_AUTOBOOT_COMMAND ";ab", nullptr, core_options::option_type::STRING, "command to execute after machine boot" }, + { OPTION_AUTOBOOT_DELAY, "0", core_options::option_type::INTEGER, "delay before executing autoboot command (seconds)" }, + { OPTION_AUTOBOOT_SCRIPT ";script", nullptr, core_options::option_type::PATH, "Lua script to execute after machine boot" }, + { OPTION_CONSOLE, "0", core_options::option_type::BOOLEAN, "enable emulator Lua console" }, + { OPTION_PLUGINS, "1", core_options::option_type::BOOLEAN, "enable Lua plugin support" }, + { OPTION_PLUGIN, nullptr, core_options::option_type::STRING, "list of plugins to enable" }, + { OPTION_NO_PLUGIN, nullptr, core_options::option_type::STRING, "list of plugins to disable" }, + + { nullptr, nullptr, core_options::option_type::HEADER, "HTTP SERVER OPTIONS" }, + { OPTION_HTTP, "0", core_options::option_type::BOOLEAN, "enable HTTP server" }, + { OPTION_HTTP_PORT, "8080", core_options::option_type::INTEGER, "HTTP server port" }, + { OPTION_HTTP_ROOT, "web", core_options::option_type::PATH, "HTTP server document root" }, { nullptr } }; @@ -236,7 +248,7 @@ namespace { } - virtual const char *value() const override + virtual const char *value() const noexcept override { // This is returning an empty string instead of nullptr to signify that // specifying the value is a meaningful operation. The option types that @@ -247,7 +259,7 @@ namespace } protected: - virtual void internal_set_value(std::string &&newvalue) override + virtual void internal_set_value(std::string &&newvalue, bool perform_substitutions) override { m_host.set_system_name(std::move(newvalue)); } @@ -267,7 +279,7 @@ namespace } protected: - virtual void internal_set_value(std::string &&newvalue) override + virtual void internal_set_value(std::string &&newvalue, bool perform_substitutions) override { m_host.set_software(std::move(newvalue)); } @@ -286,7 +298,7 @@ namespace { } - virtual const char *value() const override + virtual const char *value() const noexcept override { const char *result = nullptr; if (m_host.specified()) @@ -297,6 +309,7 @@ namespace // happen in practice // // In reality, I want to really return std::optional<std::string> here + // FIXME: the std::string assignment can throw exceptions, and returning std::optional<std::string> also isn't safe in noexcept m_temp = m_host.specified_value(); result = m_temp.c_str(); } @@ -304,7 +317,7 @@ namespace } protected: - virtual void internal_set_value(std::string &&newvalue) override + virtual void internal_set_value(std::string &&newvalue, bool perform_substitutions) override { m_host.specify(std::move(newvalue), false); } @@ -324,13 +337,13 @@ namespace { } - virtual const char *value() const override + virtual const char *value() const noexcept override { return m_host.value().c_str(); } protected: - virtual void internal_set_value(std::string &&newvalue) override + virtual void internal_set_value(std::string &&newvalue, bool perform_substitutions) override { m_host.specify(std::move(newvalue), false); } @@ -483,7 +496,7 @@ void emu_options::set_system_name(std::string &&new_system_name) { // if so, first extract the base name (the reason for this is drag-and-drop on Windows; a side // effect is a command line like 'mame pacman.foo' will work correctly, but so be it) - std::string new_system_base_name = core_filename_extract_base(m_attempted_system_name, true); + std::string new_system_base_name(core_filename_extract_base(m_attempted_system_name, true)); // perform the lookup (and error if it cannot be found) int index = driver_list::find(new_system_base_name.c_str()); @@ -505,17 +518,6 @@ void emu_options::set_system_name(std::string &&new_system_name) //------------------------------------------------- -// set_system_name - called to set the system -// name; will adjust slot/image options as appropriate -//------------------------------------------------- - -void emu_options::set_system_name(const std::string &new_system_name) -{ - set_system_name(std::string(new_system_name)); -} - - -//------------------------------------------------- // update_slot_and_image_options //------------------------------------------------- @@ -531,7 +533,7 @@ void emu_options::update_slot_and_image_options() if (add_and_remove_slot_options()) changed = true; - // second, we perform an analgous operation with m_image_options + // second, we perform an analogous operation with m_image_options if (add_and_remove_image_options()) changed = true; @@ -562,7 +564,7 @@ bool emu_options::add_and_remove_slot_options() // create the configuration machine_config config(*m_system, *this); - for (const device_slot_interface &slot : slot_interface_iterator(config.root_device())) + for (const device_slot_interface &slot : slot_interface_enumerator(config.root_device())) { // come up with the canonical name of the slot const char *slot_option_name = slot.slot_name(); @@ -617,8 +619,8 @@ bool emu_options::add_and_remove_slot_options() //------------------------------------------------- -// add_and_remove_slot_options - add any missing -// and/or purge extraneous slot options +// add_and_remove_image_options - add any missing +// and/or purge extraneous image options //------------------------------------------------- bool emu_options::add_and_remove_image_options() @@ -659,7 +661,7 @@ bool emu_options::add_and_remove_image_options() machine_config config(*m_system, *this); // iterate through all image devices - for (device_image_interface &image : image_interface_iterator(config.root_device())) + for (device_image_interface &image : image_interface_enumerator(config.root_device())) { const std::string &canonical_name(image.canonical_instance_name()); @@ -721,7 +723,7 @@ bool emu_options::add_and_remove_image_options() //------------------------------------------------- // reevaluate_default_card_software - based on recent -// changes in what images are mounted, give drivers +// changes in what images are mounted, give devices // a chance to specify new default slot options //------------------------------------------------- @@ -740,7 +742,7 @@ void emu_options::reevaluate_default_card_software() found = false; // iterate through all slot devices - for (device_slot_interface &slot : slot_interface_iterator(config.root_device())) + for (device_slot_interface &slot : slot_interface_enumerator(config.root_device())) { // retrieve info about the device instance auto &slot_opt(slot_option(slot.slot_name())); @@ -760,6 +762,16 @@ void emu_options::reevaluate_default_card_software() std::string default_card_software = get_default_card_software(slot); if (slot_opt.default_card_software() != default_card_software) { + // HACK: prevent option values set from "XXX_default" in software list entries + // from getting cleared out, using crude heuristic to distinguish these from + // values representing cartridge types and such + if (default_card_software.empty()) + { + auto *opt = slot.option(slot_opt.default_card_software().c_str()); + if (opt && opt->selectable()) + continue; + } + slot_opt.set_default_card_software(std::move(default_card_software)); // calling set_default_card_software() can cause a cascade of slot/image @@ -848,7 +860,8 @@ void emu_options::set_software(std::string &&new_software) } // keep any deferred options for the next round - softlist_opts = std::move(deferred_opts); + softlist_opts.slot = std::move(deferred_opts.slot); + softlist_opts.image = std::move(deferred_opts.image); // do we have any pending options after failing to distribute any? size_t after_size = softlist_opts.slot.size() + softlist_opts.image.size(); @@ -856,6 +869,14 @@ void emu_options::set_software(std::string &&new_software) throw options_error_exception("Could not assign software option"); } + // distribute slot option defaults + for (auto &slot_opt : softlist_opts.slot_defaults) + { + auto iter = m_slot_options.find(slot_opt.first); + if (iter != m_slot_options.end()) + iter->second.set_default_card_software(std::move(slot_opt.second)); + } + // we've succeeded; update the set name m_software_name = std::move(new_software); } @@ -878,9 +899,9 @@ emu_options::software_options emu_options::evaluate_initial_softlist_options(con // and set up a configuration machine_config config(*m_system, *this); - software_list_device_iterator iter(config.root_device()); + software_list_device_enumerator iter(config.root_device()); if (iter.count() == 0) - throw emu_fatalerror(EMU_ERR_FATALERROR, "Error: unknown option: %s\n", software_identifier.c_str()); + throw emu_fatalerror(EMU_ERR_FATALERROR, "Error: unknown option: %s\n", software_identifier); // and finally set up the stack std::stack<std::string> software_identifier_stack; @@ -925,9 +946,12 @@ emu_options::software_options emu_options::evaluate_initial_softlist_options(con // we need to find a mountable image slot, but we need to ensure it is a slot // for which we have not already distributed a part to device_image_interface *image = software_list_device::find_mountable_image( - config, - swpart, - [&results](const device_image_interface &candidate) { return results.image.count(candidate.instance_name()) == 0; }); + config, + swpart, + [&results] (const device_image_interface &candidate) + { + return results.image.count(candidate.instance_name()) == 0; + }); // did we find a slot to put this part into? if (image != nullptr) @@ -953,14 +977,22 @@ emu_options::software_options emu_options::evaluate_initial_softlist_options(con // ... // <sharedfeat name = "ctrl1_default" value = "paddle" /> // </software> - for (const feature_list_item &fi : swinfo->shared_info()) + for (const software_info_item &fi : swinfo->shared_features()) { const std::string default_suffix = "_default"; if (fi.name().size() > default_suffix.size() && fi.name().compare(fi.name().size() - default_suffix.size(), default_suffix.size(), default_suffix) == 0) { std::string slot_name = fi.name().substr(0, fi.name().size() - default_suffix.size()); - results.slot[slot_name] = fi.value(); + + // only add defaults if they exist in this configuration + device_t *device = config.root_device().subdevice(slot_name.c_str()); + if (device) + { + device_slot_interface *intf; + if (device->interface(intf) && intf->option(fi.value().c_str())) + results.slot_defaults[slot_name] = fi.value(); + } } } } @@ -1053,8 +1085,9 @@ image_option &emu_options::image_option(const std::string &device_name) void emu_options::command_argument_processed() { // some command line arguments require that the system name be set, so we can get slot options - if (command_arguments().size() == 1 && !core_iswildstr(command_arguments()[0].c_str()) && - (command() == "listdevices" || (command() == "listslots") || (command() == "listmedia"))) + // FIXME: having this here is a massively leaky abstraction + if (command_arguments().size() == 1 && !core_iswildstr(command_arguments()[0]) && + (command() == "listdevices" || (command() == "listslots") || (command() == "listbios") || (command() == "listmedia") || (command() == "listsoftware"))) { set_system_name(command_arguments()[0]); } @@ -1134,7 +1167,7 @@ void slot_option::specify(std::string &&text, bool peg_priority) // we need to do some elementary parsing here const char *bios_arg = ",bios="; - const size_t pos = text.find(bios_arg); + const std::string::size_type pos = text.find(bios_arg); if (pos != std::string::npos) { m_specified = true; @@ -1159,9 +1192,31 @@ void slot_option::specify(std::string &&text, bool peg_priority) // slot_option::specify //------------------------------------------------- -void slot_option::specify(const std::string &text, bool peg_priority) +void slot_option::specify(std::string_view text, bool peg_priority) { - specify(std::string(text), peg_priority); + // record the old value; we may need to trigger an update + const std::string old_value = value(); + + // we need to do some elementary parsing here + const char *bios_arg = ",bios="; + const std::string_view::size_type pos = text.find(bios_arg); + if (pos != std::string_view::npos) + { + m_specified = true; + m_specified_value = text.substr(0, pos); + m_specified_bios = text.substr(pos + strlen(bios_arg)); + } + else + { + m_specified = true; + m_specified_value = text; + m_specified_bios = ""; + } + + conditionally_peg_priority(m_entry, peg_priority); + + // we may have changed + possibly_changed(old_value); } @@ -1201,8 +1256,8 @@ void slot_option::set_bios(std::string &&text) { if (!m_specified) { - m_specified = true; m_specified_value = value(); + m_specified = true; } m_specified_bios = std::move(text); } @@ -1243,7 +1298,7 @@ image_option::image_option(emu_options &host, const std::string &canonical_insta // image_option::specify //------------------------------------------------- -void image_option::specify(const std::string &value, bool peg_priority) +void image_option::specify(std::string_view value, bool peg_priority) { if (value != m_value) { diff --git a/src/emu/emuopts.h b/src/emu/emuopts.h index 970a90c67da..0a1b1dcf413 100644 --- a/src/emu/emuopts.h +++ b/src/emu/emuopts.h @@ -25,7 +25,7 @@ #define OPTION_WRITECONFIG "writeconfig" // core search path options -#define OPTION_HOMEPATH "homepath" +#define OPTION_PLUGINDATAPATH "homepath" #define OPTION_MEDIAPATH "rompath" #define OPTION_HASHPATH "hashpath" #define OPTION_SAMPLEPATH "samplepath" @@ -47,6 +47,7 @@ #define OPTION_SNAPSHOT_DIRECTORY "snapshot_directory" #define OPTION_DIFF_DIRECTORY "diff_directory" #define OPTION_COMMENT_DIRECTORY "comment_directory" +#define OPTION_SHARE_DIRECTORY "share_directory" // core state/playback options #define OPTION_STATE "state" @@ -55,7 +56,6 @@ #define OPTION_REWIND_CAPACITY "rewind_capacity" #define OPTION_PLAYBACK "playback" #define OPTION_RECORD "record" -#define OPTION_RECORD_TIMECODE "record_timecode" #define OPTION_EXIT_AFTER_PLAYBACK "exit_after_playback" #define OPTION_MNGWRITE "mngwrite" #define OPTION_AVIWRITE "aviwrite" @@ -75,6 +75,7 @@ #define OPTION_SLEEP "sleep" #define OPTION_SPEED "speed" #define OPTION_REFRESHSPEED "refreshspeed" +#define OPTION_LOWLATENCY "lowlatency" // core render options #define OPTION_KEEPASPECT "keepaspect" @@ -110,6 +111,7 @@ // core vector options #define OPTION_BEAM_WIDTH_MIN "beam_width_min" #define OPTION_BEAM_WIDTH_MAX "beam_width_max" +#define OPTION_BEAM_DOT_SIZE "beam_dot_size" #define OPTION_BEAM_INTENSITY_WEIGHT "beam_intensity_weight" #define OPTION_FLICKER "flicker" @@ -117,6 +119,8 @@ #define OPTION_SAMPLERATE "samplerate" #define OPTION_SAMPLES "samples" #define OPTION_VOLUME "volume" +#define OPTION_COMPRESSOR "compressor" +#define OPTION_SPEAKER_REPORT "speaker_report" // core input options #define OPTION_COIN_LOCKOUT "coin_lockout" @@ -132,6 +136,7 @@ #define OPTION_JOYSTICK_MAP "joystick_map" #define OPTION_JOYSTICK_DEADZONE "joystick_deadzone" #define OPTION_JOYSTICK_SATURATION "joystick_saturation" +#define OPTION_JOYSTICK_THRESHOLD "joystick_threshold" #define OPTION_NATURAL_KEYBOARD "natural" #define OPTION_JOYSTICK_CONTRADICTORY "joystick_contradictory" #define OPTION_COIN_IMPULSE "coin_impulse" @@ -153,6 +158,7 @@ #define OPTION_OSLOG "oslog" #define OPTION_UPDATEINPAUSE "update_in_pause" #define OPTION_DEBUGSCRIPT "debugscript" +#define OPTION_DEBUGLOG "debuglog" // core misc options #define OPTION_DRC "drc" @@ -216,8 +222,9 @@ public: core_options::entry::shared_ptr option_entry() const { return m_entry.lock(); } // seters - void specify(const std::string &text, bool peg_priority = true); + void specify(std::string_view text, bool peg_priority = true); void specify(std::string &&text, bool peg_priority = true); + void specify(const char *text, bool peg_priority = true) { specify(std::string_view(text), peg_priority); } void set_bios(std::string &&text); void set_default_card_software(std::string &&s); @@ -250,8 +257,9 @@ public: core_options::entry::shared_ptr option_entry() const { return m_entry.lock(); } // mutators - void specify(const std::string &value, bool peg_priority = true); + void specify(std::string_view value, bool peg_priority = true); void specify(std::string &&value, bool peg_priority = true); + void specify(const char *value, bool peg_priority = true) { specify(std::string_view(value), peg_priority); } // instantiates an option entry (don't call outside of emuopts.cpp) core_options::entry::shared_ptr setup_option_entry(std::vector<std::string> &&names); @@ -287,7 +295,8 @@ public: ~emu_options(); // mutation - void set_system_name(const std::string &new_system_name); + void set_system_name(const char *new_system_name) { set_system_name(std::string(new_system_name)); } + void set_system_name(std::string_view new_system_name) { set_system_name(std::string(new_system_name)); } void set_system_name(std::string &&new_system_name); void set_software(std::string &&new_software); @@ -302,7 +311,7 @@ public: bool write_config() const { return bool_value(OPTION_WRITECONFIG); } // core search path options - const char *home_path() const { return value(OPTION_HOMEPATH); } + const char *plugin_data_path() const { return value(OPTION_PLUGINDATAPATH); } const char *media_path() const { return value(OPTION_MEDIAPATH); } const char *hash_path() const { return value(OPTION_HASHPATH); } const char *sample_path() const { return value(OPTION_SAMPLEPATH); } @@ -324,6 +333,7 @@ public: const char *snapshot_directory() const { return value(OPTION_SNAPSHOT_DIRECTORY); } const char *diff_directory() const { return value(OPTION_DIFF_DIRECTORY); } const char *comment_directory() const { return value(OPTION_COMMENT_DIRECTORY); } + const char *share_directory() const { return value(OPTION_SHARE_DIRECTORY); } // core state/playback options const char *state() const { return value(OPTION_STATE); } @@ -332,7 +342,6 @@ public: int rewind_capacity() const { return int_value(OPTION_REWIND_CAPACITY); } const char *playback() const { return value(OPTION_PLAYBACK); } const char *record() const { return value(OPTION_RECORD); } - bool record_timecode() const { return bool_value(OPTION_RECORD_TIMECODE); } bool exit_after_playback() const { return bool_value(OPTION_EXIT_AFTER_PLAYBACK); } const char *mng_write() const { return value(OPTION_MNGWRITE); } const char *avi_write() const { return value(OPTION_AVIWRITE); } @@ -352,6 +361,7 @@ public: bool sleep() const { return m_sleep; } float speed() const { return float_value(OPTION_SPEED); } bool refresh_speed() const { return m_refresh_speed; } + bool low_latency() const { return bool_value(OPTION_LOWLATENCY); } // core render options bool keep_aspect() const { return bool_value(OPTION_KEEPASPECT); } @@ -387,6 +397,7 @@ public: // core vector options float beam_width_min() const { return float_value(OPTION_BEAM_WIDTH_MIN); } float beam_width_max() const { return float_value(OPTION_BEAM_WIDTH_MAX); } + float beam_dot_size() const { return float_value(OPTION_BEAM_DOT_SIZE); } float beam_intensity_weight() const { return float_value(OPTION_BEAM_INTENSITY_WEIGHT); } float flicker() const { return float_value(OPTION_FLICKER); } @@ -394,6 +405,8 @@ public: int sample_rate() const { return int_value(OPTION_SAMPLERATE); } bool samples() const { return bool_value(OPTION_SAMPLES); } int volume() const { return int_value(OPTION_VOLUME); } + bool compressor() const { return bool_value(OPTION_COMPRESSOR); } + int speaker_report() const { return int_value(OPTION_SPEAKER_REPORT); } // core input options bool coin_lockout() const { return bool_value(OPTION_COIN_LOCKOUT); } @@ -414,6 +427,7 @@ public: const char *joystick_map() const { return value(OPTION_JOYSTICK_MAP); } float joystick_deadzone() const { return float_value(OPTION_JOYSTICK_DEADZONE); } float joystick_saturation() const { return float_value(OPTION_JOYSTICK_SATURATION); } + float joystick_threshold() const { return float_value(OPTION_JOYSTICK_THRESHOLD); } bool steadykey() const { return bool_value(OPTION_STEADYKEY); } bool ui_active() const { return bool_value(OPTION_UI_ACTIVE); } bool offscreen_reload() const { return bool_value(OPTION_OFFSCREEN_RELOAD); } @@ -428,6 +442,7 @@ public: bool oslog() const { return bool_value(OPTION_OSLOG); } const char *debug_script() const { return value(OPTION_DEBUGSCRIPT); } bool update_in_pause() const { return bool_value(OPTION_UPDATEINPAUSE); } + bool debuglog() const { return bool_value(OPTION_DEBUGLOG); } // core misc options bool drc() const { return bool_value(OPTION_DRC); } @@ -489,6 +504,7 @@ private: struct software_options { std::unordered_map<std::string, std::string> slot; + std::unordered_map<std::string, std::string> slot_defaults; std::unordered_map<std::string, std::string> image; }; diff --git a/src/emu/emupal.cpp b/src/emu/emupal.cpp index 6d0955f3484..d8eb5a3e774 100644 --- a/src/emu/emupal.cpp +++ b/src/emu/emupal.cpp @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles /*************************************************************************** - emupal.c + emupal.cpp Palette device. @@ -21,83 +21,82 @@ DEFINE_DEVICE_TYPE(PALETTE, palette_device, "palette", "palette") palette_device::palette_device(const machine_config &mconfig, const char *tag, device_t *owner, init_delegate &&init, u32 entries, u32 indirect) : palette_device(mconfig, tag, owner, 0U) { - set_entries(entries); - set_indirect_entries(indirect); - set_init(std::move(init)); + set_entries(entries, indirect); + m_init = std::move(init); } palette_device::palette_device(const machine_config &mconfig, const char *tag, device_t *owner, black_t, u32 entries) - : palette_device(mconfig, tag, owner, init_delegate(FUNC(palette_device::palette_init_all_black), tag, this), entries) + : palette_device(mconfig, tag, owner, init_delegate(*this, FUNC(palette_device::palette_init_all_black)), entries) { } palette_device::palette_device(const machine_config &mconfig, const char *tag, device_t *owner, mono_t) - : palette_device(mconfig, tag, owner, init_delegate(FUNC(palette_device::palette_init_monochrome), tag, this), 2) + : palette_device(mconfig, tag, owner, init_delegate(*this, FUNC(palette_device::palette_init_monochrome)), 2) { } palette_device::palette_device(const machine_config &mconfig, const char *tag, device_t *owner, mono_inv_t) - : palette_device(mconfig, tag, owner, init_delegate(FUNC(palette_device::palette_init_monochrome_inverted), tag, this), 2) + : palette_device(mconfig, tag, owner, init_delegate(*this, FUNC(palette_device::palette_init_monochrome_inverted)), 2) { } palette_device::palette_device(const machine_config &mconfig, const char *tag, device_t *owner, mono_hi_t) - : palette_device(mconfig, tag, owner, init_delegate(FUNC(palette_device::palette_init_monochrome_highlight), tag, this), 3) + : palette_device(mconfig, tag, owner, init_delegate(*this, FUNC(palette_device::palette_init_monochrome_highlight)), 3) { } palette_device::palette_device(const machine_config &mconfig, const char *tag, device_t *owner, rgb_3b_t) - : palette_device(mconfig, tag, owner, init_delegate(FUNC(palette_device::palette_init_3bit_rgb), tag, this), 8) + : palette_device(mconfig, tag, owner, init_delegate(*this, FUNC(palette_device::palette_init_3bit_rgb)), 8) { } palette_device::palette_device(const machine_config &mconfig, const char *tag, device_t *owner, rbg_3b_t) - : palette_device(mconfig, tag, owner, init_delegate(FUNC(palette_device::palette_init_3bit_rbg), tag, this), 8) + : palette_device(mconfig, tag, owner, init_delegate(*this, FUNC(palette_device::palette_init_3bit_rbg)), 8) { } palette_device::palette_device(const machine_config &mconfig, const char *tag, device_t *owner, grb_3b_t) - : palette_device(mconfig, tag, owner, init_delegate(FUNC(palette_device::palette_init_3bit_grb), tag, this), 8) + : palette_device(mconfig, tag, owner, init_delegate(*this, FUNC(palette_device::palette_init_3bit_grb)), 8) { } palette_device::palette_device(const machine_config &mconfig, const char *tag, device_t *owner, gbr_3b_t) - : palette_device(mconfig, tag, owner, init_delegate(FUNC(palette_device::palette_init_3bit_gbr), tag, this), 8) + : palette_device(mconfig, tag, owner, init_delegate(*this, FUNC(palette_device::palette_init_3bit_gbr)), 8) { } palette_device::palette_device(const machine_config &mconfig, const char *tag, device_t *owner, brg_3b_t) - : palette_device(mconfig, tag, owner, init_delegate(FUNC(palette_device::palette_init_3bit_brg), tag, this), 8) + : palette_device(mconfig, tag, owner, init_delegate(*this, FUNC(palette_device::palette_init_3bit_brg)), 8) { } palette_device::palette_device(const machine_config &mconfig, const char *tag, device_t *owner, bgr_3b_t) - : palette_device(mconfig, tag, owner, init_delegate(FUNC(palette_device::palette_init_3bit_bgr), tag, this), 8) + : palette_device(mconfig, tag, owner, init_delegate(*this, FUNC(palette_device::palette_init_3bit_bgr)), 8) { } palette_device::palette_device(const machine_config &mconfig, const char *tag, device_t *owner, rgb_555_t) - : palette_device(mconfig, tag, owner, init_delegate(FUNC(palette_device::palette_init_rgb_555), tag, this), 32768) + : palette_device(mconfig, tag, owner, init_delegate(*this, FUNC(palette_device::palette_init_rgb_555)), 32768) { } palette_device::palette_device(const machine_config &mconfig, const char *tag, device_t *owner, grb_555_t) - : palette_device(mconfig, tag, owner, init_delegate(FUNC(palette_device::palette_init_grb_555), tag, this), 32768) + : palette_device(mconfig, tag, owner, init_delegate(*this, FUNC(palette_device::palette_init_grb_555)), 32768) { } palette_device::palette_device(const machine_config &mconfig, const char *tag, device_t *owner, bgr_555_t) - : palette_device(mconfig, tag, owner, init_delegate(FUNC(palette_device::palette_init_bgr_555), tag, this), 32768) + : palette_device(mconfig, tag, owner, init_delegate(*this, FUNC(palette_device::palette_init_bgr_555)), 32768) { } palette_device::palette_device(const machine_config &mconfig, const char *tag, device_t *owner, rgb_565_t) - : palette_device(mconfig, tag, owner, init_delegate(FUNC(palette_device::palette_init_rgb_565), tag, this), 65536) + : palette_device(mconfig, tag, owner, init_delegate(*this, FUNC(palette_device::palette_init_rgb_565)), 65536) { } palette_device::palette_device(const machine_config &mconfig, const char *tag, device_t *owner, bgr_565_t) - : palette_device(mconfig, tag, owner, init_delegate(FUNC(palette_device::palette_init_bgr_565), tag, this), 65536) + : palette_device(mconfig, tag, owner, init_delegate(*this, FUNC(palette_device::palette_init_bgr_565)), 65536) { } @@ -114,7 +113,7 @@ palette_device::palette_device(const machine_config &mconfig, const char *tag, d , m_endianness() , m_endianness_supplied(false) , m_prom_region(*this, finder_base::DUMMY_TAG) - , m_init() + , m_init(*this) , m_raw_to_rgb() { } @@ -169,6 +168,12 @@ palette_device &palette_device::set_format(xbgr_333_t, u32 entries) return *this; } +palette_device &palette_device::set_format(xbgr_333_nib_t, u32 entries) +{ + set_format(2, &raw_to_rgb_converter::standard_rgb_decoder<3,3,3, 0,4,8>, entries); + return *this; +} + palette_device &palette_device::set_format(xrgb_444_t, u32 entries) { set_format(2, &raw_to_rgb_converter::standard_rgb_decoder<4,4,4, 8,4,0>, entries); @@ -199,6 +204,12 @@ palette_device &palette_device::set_format(rgbx_444_t, u32 entries) return *this; } +palette_device &palette_device::set_format(grbx_444_t, u32 entries) +{ + set_format(2, &raw_to_rgb_converter::standard_rgb_decoder<4,4,4, 8,12,4>, entries); + return *this; +} + palette_device &palette_device::set_format(gbrx_444_t, u32 entries) { set_format(2, &raw_to_rgb_converter::standard_rgb_decoder<4,4,4, 4,12,8>, entries); @@ -448,6 +459,12 @@ void palette_device::write16_ext(offs_t offset, u16 data, u16 mem_mask) update_for_write(offset * 2, 2); } +void palette_device::write32_ext(offs_t offset, u32 data, u32 mem_mask) +{ + m_paletteram_ext.write32(offset, data, mem_mask); + update_for_write(offset * 4, 4); +} + u8 palette_device::read8_ext(offs_t offset) { return m_paletteram_ext.read8(offset); @@ -458,6 +475,10 @@ u16 palette_device::read16_ext(offs_t offset) return m_paletteram_ext.read16(offset); } +u32 palette_device::read32_ext(offs_t offset) +{ + return m_paletteram_ext.read32(offset); +} //------------------------------------------------- // write_indirect - write a byte to the base @@ -495,7 +516,7 @@ void palette_device::write_indirect_ext(offs_t offset, u8 data) void palette_device::device_start() { // bind the init function - m_init.bind_relative_to(*owner()); + m_init.resolve(); // find the memory, if present const memory_share *share = memshare(tag()); @@ -503,7 +524,7 @@ void palette_device::device_start() { // find the extended (split) memory, if present std::string tag_ext = std::string(tag()).append("_ext"); - const memory_share *share_ext = memshare(tag_ext.c_str()); + const memory_share *share_ext = memshare(tag_ext); // make sure we have specified a format if (m_raw_to_rgb.bytes_per_entry() <= 0) diff --git a/src/emu/emupal.h b/src/emu/emupal.h index acc528110f1..17deb2b0903 100644 --- a/src/emu/emupal.h +++ b/src/emu/emupal.h @@ -95,15 +95,15 @@ #pragma once +#include "memarray.h" +#include <type_traits> +#include <utility> + //************************************************************************** // TYPE DEFINITIONS //************************************************************************** -class palette_device; -typedef device_delegate<void (palette_device &)> palette_init_delegate; - - // ======================> raw_to_rgb_converter class raw_to_rgb_converter @@ -210,11 +210,13 @@ public: enum xrgb_333_t { xRGB_333, xxxxxxxRRRGGGBBB }; enum xrbg_333_t { xRBG_333, xxxxxxxRRRBBBGGG }; enum xbgr_333_t { xBGR_333, xxxxxxxBBBGGGRRR }; + enum xbgr_333_nib_t { xBGR_333_nibble, xxxxxBBBxGGGxRRR }; enum xrgb_444_t { xRGB_444, xxxxRRRRGGGGBBBB }; enum xrbg_444_t { xRBG_444, xxxxRRRRBBBBGGGG }; enum xbrg_444_t { xBRG_444, xxxxBBBBRRRRGGGG }; enum xbgr_444_t { xBGR_444, xxxxBBBBGGGGRRRR }; enum rgbx_444_t { RGBx_444, RRRRGGGGBBBBxxxx }; + enum grbx_444_t { GRBx_444, GGGGRRRRBBBBxxxx }; enum gbrx_444_t { GBRx_444, GGGGBBBBRRRRxxxx }; enum irgb_4444_t { IRGB_4444, IIIIRRRRGGGGBBBB }; enum rgbi_4444_t { RGBI_4444, RRRRGGGGBBBBIIII }; @@ -271,31 +273,22 @@ public: template <typename T> palette_device(const machine_config &mconfig, const char *tag, device_t *owner, rgb_444_prom_t, T &®ion, u32 entries) - : palette_device(mconfig, tag, owner, init_delegate(FUNC(palette_device::palette_init_rgb_444_proms), tag, this), entries) + : palette_device(mconfig, tag, owner, init_delegate(*this, FUNC(palette_device::palette_init_rgb_444_proms)), entries) { set_prom_region(std::forward<T>(region)); } - // FIXME: these should be aware of current device for resolving the tag - template <class FunctionClass> - palette_device(const machine_config &mconfig, const char *tag, device_t *owner, void (FunctionClass::*init)(palette_device &), const char *name, u32 entries = 0U, u32 indirect = 0U) - : palette_device(mconfig, tag, owner, init_delegate(init, name, nullptr, static_cast<FunctionClass *>(nullptr)), entries, indirect) - { } - template <class FunctionClass> - palette_device(const machine_config &mconfig, const char *tag, device_t *owner, void (FunctionClass::*init)(palette_device &) const, const char *name, u32 entries = 0U, u32 indirect = 0U) - : palette_device(mconfig, tag, owner, init_delegate(init, name, nullptr, static_cast<FunctionClass *>(nullptr)), entries, indirect) - { } - template <class FunctionClass> - palette_device(const machine_config &mconfig, const char *tag, device_t *owner, const char *devname, void (FunctionClass::*init)(palette_device &), const char *name, u32 entries = 0U, u32 indirect = 0U) - : palette_device(mconfig, tag, owner, init_delegate(init, name, devname, static_cast<FunctionClass *>(nullptr)), entries, indirect) - { } - template <class FunctionClass> - palette_device(const machine_config &mconfig, const char *tag, device_t *owner, const char *devname, void (FunctionClass::*init)(palette_device &) const, const char *name, u32 entries = 0U, u32 indirect = 0U) - : palette_device(mconfig, tag, owner, init_delegate(init, name, devname, static_cast<FunctionClass *>(nullptr)), entries, indirect) - { } + template <typename F> + palette_device(const machine_config &mconfig, const char *tag, device_t *owner, F &&init, std::enable_if_t<init_delegate::supports_callback<F>::value, const char *> name, u32 entries = 0U, u32 indirect = 0U) + : palette_device(mconfig, tag, owner, 0U) + { set_init(std::forward<F>(init), name).set_entries(entries, indirect); } + template <typename T, typename F> + palette_device(const machine_config &mconfig, const char *tag, device_t *owner, T &&devname, F &&init, std::enable_if_t<init_delegate::supports_callback<F>::value, const char *> name, u32 entries = 0U, u32 indirect = 0U) + : palette_device(mconfig, tag, owner, 0U) + { set_init(std::forward<T>(devname), std::forward<F>(init), name).set_entries(entries, indirect); } // configuration - template <typename Object> void set_init(Object &&init) { m_init = std::forward<Object>(init); } + template <typename... T> palette_device &set_init(T &&... args) { m_init.set(std::forward<T>(args)...); return *this; } palette_device &set_format(raw_to_rgb_converter raw_to_rgb) { m_raw_to_rgb = raw_to_rgb; return *this; } palette_device &set_format(int bytes_per_entry, raw_to_rgb_converter::raw_to_rgb_func func, u32 entries); palette_device &set_format(rgb_332_t, u32 entries); @@ -305,11 +298,13 @@ public: palette_device &set_format(xrgb_333_t, u32 entries); palette_device &set_format(xrbg_333_t, u32 entries); palette_device &set_format(xbgr_333_t, u32 entries); + palette_device &set_format(xbgr_333_nib_t, u32 entries); palette_device &set_format(xrgb_444_t, u32 entries); palette_device &set_format(xrbg_444_t, u32 entries); palette_device &set_format(xbrg_444_t, u32 entries); palette_device &set_format(xbgr_444_t, u32 entries); palette_device &set_format(rgbx_444_t, u32 entries); + palette_device &set_format(grbx_444_t, u32 entries); palette_device &set_format(gbrx_444_t, u32 entries); palette_device &set_format(irgb_4444_t, u32 entries); palette_device &set_format(rgbi_4444_t, u32 entries); @@ -347,28 +342,6 @@ public: palette_device &enable_hilights() { m_enable_hilights = true; return *this; } template <typename T> palette_device &set_prom_region(T &®ion) { m_prom_region.set_tag(std::forward<T>(region)); return *this; } - // FIXME: these should be aware of current device for resolving the tag - template <class FunctionClass> - void set_init(void (FunctionClass::*init)(palette_device &), const char *name) - { - m_init = init_delegate(init, name, nullptr, static_cast<FunctionClass *>(nullptr)); - } - template <class FunctionClass> - void set_init(void (FunctionClass::*init)(palette_device &) const, const char *name) - { - m_init = init_delegate(init, name, nullptr, static_cast<FunctionClass *>(nullptr)); - } - template <class FunctionClass> - void set_init(const char *devname, void (FunctionClass::*init)(palette_device &), const char *name) - { - m_init = init_delegate(init, name, devname, static_cast<FunctionClass *>(nullptr)); - } - template <class FunctionClass> - void set_init(const char *devname, void (FunctionClass::*init)(palette_device &) const, const char *name) - { - m_init = init_delegate(init, name, devname, static_cast<FunctionClass *>(nullptr)); - } - // palette RAM accessors memory_array &basemem() { return m_paletteram; } memory_array &extmem() { return m_paletteram_ext; } @@ -394,20 +367,22 @@ public: void write16(offs_t offset, u16 data, u16 mem_mask = u16(~0)); void write16_ext(offs_t offset, u16 data, u16 mem_mask = u16(~0)); u32 read32(offs_t offset); + u32 read32_ext(offs_t offset); void write32(offs_t offset, u32 data, u32 mem_mask = u32(~0)); + void write32_ext(offs_t offset, u32 data, u32 mem_mask = u32(~0)); // helper to update palette when data changed void update() { if (!m_init.isnull()) m_init(*this); } protected: // device-level overrides - virtual void device_start() override; + virtual void device_start() override ATTR_COLD; // device_palette_interface overrides - virtual u32 palette_entries() const override { return m_entries; } - virtual u32 palette_indirect_entries() const override { return m_indirect_entries; } - virtual bool palette_shadows_enabled() const override { return m_enable_shadows; } - virtual bool palette_hilights_enabled() const override { return m_enable_hilights; } + virtual u32 palette_entries() const noexcept override { return m_entries; } + virtual u32 palette_indirect_entries() const noexcept override { return m_indirect_entries; } + virtual bool palette_shadows_enabled() const noexcept override { return m_enable_shadows; } + virtual bool palette_hilights_enabled() const noexcept override { return m_enable_hilights; } // generic palette init routines void palette_init_all_black(palette_device &palette); diff --git a/src/emu/fileio.cpp b/src/emu/fileio.cpp index bbd1d931f30..c0341b93368 100644 --- a/src/emu/fileio.cpp +++ b/src/emu/fileio.cpp @@ -2,16 +2,41 @@ // copyright-holders:Aaron Giles /*************************************************************************** - fileio.c + fileio.cpp File access functions. ***************************************************************************/ #include "emu.h" -#include "unzip.h" #include "fileio.h" +#include "util/path.h" +#include "util/unzip.h" + +#include <tuple> + +//#define VERBOSE 1 +#define LOG_OUTPUT_FUNC osd_printf_verbose +#include "logmacro.h" + + +template path_iterator::path_iterator(char *&, int); +template path_iterator::path_iterator(char * const &, int); +template path_iterator::path_iterator(char const *&, int); +template path_iterator::path_iterator(char const * const &, int); +template path_iterator::path_iterator(std::vector<std::string> &, int); +template path_iterator::path_iterator(const std::vector<std::string> &, int); + +template emu_file::emu_file(std::string &, u32); +template emu_file::emu_file(const std::string &, u32); +template emu_file::emu_file(char *&, u32); +template emu_file::emu_file(char * const &, u32); +template emu_file::emu_file(char const *&, u32); +template emu_file::emu_file(char const * const &, u32); +template emu_file::emu_file(std::vector<std::string> &, u32); +template emu_file::emu_file(const std::vector<std::string> &, u32); + const u32 OPEN_FLAG_HAS_CRC = 0x10000; @@ -28,6 +53,7 @@ const u32 OPEN_FLAG_HAS_CRC = 0x10000; path_iterator::path_iterator(std::string &&searchpath) : m_searchpath(std::move(searchpath)) , m_current(m_searchpath.cbegin()) + , m_separator(';') // FIXME this should be a macro - UNIX prefers : , m_is_first(true) { } @@ -35,6 +61,7 @@ path_iterator::path_iterator(std::string &&searchpath) path_iterator::path_iterator(std::string const &searchpath) : m_searchpath(searchpath) , m_current(m_searchpath.cbegin()) + , m_separator(';') // FIXME this should be a macro - UNIX prefers : , m_is_first(true) { } @@ -47,6 +74,7 @@ path_iterator::path_iterator(path_iterator &&that) path_iterator::path_iterator(path_iterator const &that) : m_searchpath(that.m_searchpath) , m_current(std::next(m_searchpath.cbegin(), std::distance(that.m_searchpath.cbegin(), that.m_current))) + , m_separator(that.m_separator) , m_is_first(that.m_is_first) { } @@ -61,6 +89,7 @@ path_iterator &path_iterator::operator=(path_iterator &&that) auto const current(std::distance(that.m_searchpath.cbegin(), that.m_current)); m_searchpath = std::move(that.m_searchpath); m_current = std::next(m_searchpath.cbegin(), current); + m_separator = that.m_separator; m_is_first = that.m_is_first; return *this; } @@ -69,6 +98,7 @@ path_iterator &path_iterator::operator=(path_iterator const &that) { m_searchpath = that.m_searchpath; m_current = std::next(m_searchpath.cbegin(), std::distance(that.m_searchpath.cbegin(), that.m_current)); + m_separator = that.m_separator; m_is_first = that.m_is_first; return *this; } @@ -79,28 +109,19 @@ path_iterator &path_iterator::operator=(path_iterator const &that) // multipath sequence //------------------------------------------------- -bool path_iterator::next(std::string &buffer, const char *name) +bool path_iterator::next(std::string &buffer) { // if none left, return false to indicate we are done if (!m_is_first && (m_searchpath.cend() == m_current)) return false; // copy up to the next separator - auto const sep(std::find(m_current, m_searchpath.cend(), ';')); // FIXME this should be a macro - UNIX prefers : + auto const sep(std::find(m_current, m_searchpath.cend(), m_separator)); buffer.assign(m_current, sep); m_current = sep; if (m_searchpath.cend() != m_current) ++m_current; - // append the name if we have one - if (name) - { - // compute the full pathname - if (!buffer.empty() && !util::is_directory_separator(buffer.back())) - buffer.append(PATH_SEPARATOR); - buffer.append(name); - } - // bump the index and return true m_is_first = false; return true; @@ -108,7 +129,7 @@ bool path_iterator::next(std::string &buffer, const char *name) //------------------------------------------------- -// path_iteratr::reset - let's go again +// path_iterator::reset - let's go again //------------------------------------------------- void path_iterator::reset() @@ -128,7 +149,7 @@ void path_iterator::reset() // in the search path //------------------------------------------------- -const osd::directory::entry *file_enumerator::next() +const osd::directory::entry *file_enumerator::next(const char *subdir) { // loop over potentially empty directories while (true) @@ -140,6 +161,10 @@ const osd::directory::entry *file_enumerator::next() if (!m_iterator.next(m_pathbuffer)) return nullptr; + // append the subdir if we have one + if (subdir) + util::path_append(m_pathbuffer, subdir); + // open the path m_curdir = osd::directory::open(m_pathbuffer); } @@ -165,31 +190,31 @@ const osd::directory::entry *file_enumerator::next() //------------------------------------------------- emu_file::emu_file(u32 openflags) - : m_file() - , m_iterator(std::string()) - , m_mediapaths(std::string()) - , m_crc(0) - , m_openflags(openflags) - , m_zipfile(nullptr) - , m_ziplength(0) - , m_remove_on_close(false) - , m_restrict_to_mediapath(false) + : emu_file(path_iterator(std::string()), openflags) { - // sanity check the open flags - if ((m_openflags & OPEN_FLAG_HAS_CRC) && (m_openflags & OPEN_FLAG_WRITE)) - throw emu_fatalerror("Attempted to open a file for write with OPEN_FLAG_HAS_CRC"); + +} + +emu_file::emu_file(path_iterator &&searchpath, u32 openflags) + : emu_file(openflags, EMPTY) +{ + m_iterator.emplace_back(searchpath, std::string()); + m_mediapaths.emplace_back(std::move(searchpath), std::string()); } -emu_file::emu_file(std::string &&searchpath, u32 openflags) - : m_file() - , m_iterator(searchpath) - , m_mediapaths(std::move(searchpath)) +emu_file::emu_file(u32 openflags, empty_t) + : m_filename() + , m_fullpath() + , m_file() + , m_iterator() + , m_mediapaths() + , m_first(true) , m_crc(0) , m_openflags(openflags) , m_zipfile(nullptr) , m_ziplength(0) , m_remove_on_close(false) - , m_restrict_to_mediapath(false) + , m_restrict_to_mediapath(0) { // sanity check the open flags if ((m_openflags & OPEN_FLAG_HAS_CRC) && (m_openflags & OPEN_FLAG_WRITE)) @@ -228,16 +253,16 @@ emu_file::operator util::core_file &() // hash - returns the hash for a file //------------------------------------------------- -util::hash_collection &emu_file::hashes(const char *types) +util::hash_collection &emu_file::hashes(std::string_view types) { // determine the hashes we already have std::string already_have = m_hashes.hash_types(); // determine which hashes we need std::string needed; - for (const char *scan = types; *scan != 0; scan++) - if (already_have.find_first_of(*scan) == -1) - needed.push_back(*scan); + for (char scan : types) + if (already_have.find_first_of(scan) == std::string::npos) + needed.push_back(scan); // if we need nothing, skip it if (needed.empty()) @@ -256,13 +281,14 @@ util::hash_collection &emu_file::hashes(const char *types) return m_hashes; } - // read the data if we can - const u8 *filedata = (const u8 *)m_file->buffer(); - if (filedata == nullptr) + std::uint64_t length; + if (m_file->length(length)) return m_hashes; - // compute the hash - m_hashes.compute(filedata, m_file->size(), needed.c_str()); + // hash the data + std::size_t actual; + (void)m_hashes.compute(*m_file, 0U, length, actual, needed.c_str()); // FIXME: need better interface to report errors + return m_hashes; } @@ -271,93 +297,97 @@ util::hash_collection &emu_file::hashes(const char *types) // open - open a file by searching paths //------------------------------------------------- -osd_file::error emu_file::open(const std::string &name) +std::error_condition emu_file::open(std::string &&name) { // remember the filename and CRC info - m_filename = name; + m_filename = std::move(name); m_crc = 0; m_openflags &= ~OPEN_FLAG_HAS_CRC; // reset the iterator and open_next - m_iterator.reset(); + m_first = true; return open_next(); } -osd_file::error emu_file::open(const std::string &name1, const std::string &name2) -{ - // concatenate the strings and do a standard open - return open(name1 + name2); -} - -osd_file::error emu_file::open(const std::string &name1, const std::string &name2, const std::string &name3) -{ - // concatenate the strings and do a standard open - return open(name1 + name2 + name3); -} - -osd_file::error emu_file::open(const std::string &name1, const std::string &name2, const std::string &name3, const std::string &name4) -{ - // concatenate the strings and do a standard open - return open(name1 + name2 + name3 + name4); -} - -osd_file::error emu_file::open(const std::string &name, u32 crc) +std::error_condition emu_file::open(std::string &&name, u32 crc) { // remember the filename and CRC info - m_filename = name; + m_filename = std::move(name); m_crc = crc; m_openflags |= OPEN_FLAG_HAS_CRC; // reset the iterator and open_next - m_iterator.reset(); + m_first = true; return open_next(); } -osd_file::error emu_file::open(const std::string &name1, const std::string &name2, u32 crc) -{ - // concatenate the strings and do a standard open - return open(name1 + name2, crc); -} - -osd_file::error emu_file::open(const std::string &name1, const std::string &name2, const std::string &name3, u32 crc) -{ - // concatenate the strings and do a standard open - return open(name1 + name2 + name3, crc); -} - -osd_file::error emu_file::open(const std::string &name1, const std::string &name2, const std::string &name3, const std::string &name4, u32 crc) -{ - // concatenate the strings and do a standard open - return open(name1 + name2 + name3 + name4, crc); -} - //------------------------------------------------- // open_next - open the next file that matches // the filename by iterating over paths //------------------------------------------------- -osd_file::error emu_file::open_next() +std::error_condition emu_file::open_next() { // if we're open from a previous attempt, close up now - if (m_file != nullptr) + if (m_file) close(); // loop over paths - osd_file::error filerr = osd_file::error::NOT_FOUND; - while (m_iterator.next(m_fullpath, m_filename.c_str())) + LOG("emu_file: open next '%s'\n", m_filename); + std::error_condition filerr = std::errc::no_such_file_or_directory; + while (filerr) { + if (m_first) + { + m_first = false; + for (searchpath_vector::value_type &i : m_iterator) + { + i.first.reset(); + if (!i.first.next(i.second)) + return filerr; + } + } + else + { + searchpath_vector::iterator i(m_iterator.begin()); + while (i != m_iterator.end()) + { + if (i->first.next(i->second)) + { + LOG("emu_file: next path %d '%s'\n", std::distance(m_iterator.begin(), i), i->second); + for (searchpath_vector::iterator j = m_iterator.begin(); i != j; ++j) + { + j->first.reset(); + j->first.next(j->second); + } + break; + } + ++i; + } + if (m_iterator.end() == i) + return filerr; + } + + // build full path + m_fullpath.clear(); + for (searchpath_vector::value_type const &path : m_iterator) + { + m_fullpath.append(path.second); + if (!m_fullpath.empty() && !util::is_directory_separator(m_fullpath.back())) + m_fullpath.append(PATH_SEPARATOR); + } + m_fullpath.append(m_filename); + // attempt to open the file directly + LOG("emu_file: attempting to open '%s' directly\n", m_fullpath); filerr = util::core_file::open(m_fullpath, m_openflags, m_file); - if (filerr == osd_file::error::NONE) - break; // if we're opening for read-only we have other options - if ((m_openflags & (OPEN_FLAG_READ | OPEN_FLAG_WRITE)) == OPEN_FLAG_READ) + if (filerr && ((m_openflags & (OPEN_FLAG_READ | OPEN_FLAG_WRITE)) == OPEN_FLAG_READ)) { + LOG("emu_file: attempting to open '%s' from archives\n", m_fullpath); filerr = attempt_zipped(); - if (filerr == osd_file::error::NONE) - break; } } return filerr; @@ -369,7 +399,7 @@ osd_file::error emu_file::open_next() // just an array of data in RAM //------------------------------------------------- -osd_file::error emu_file::open_ram(const void *data, u32 length) +std::error_condition emu_file::open_ram(const void *data, u32 length) { // set a fake filename and CRC m_filename = "RAM"; @@ -404,46 +434,32 @@ void emu_file::close() //------------------------------------------------- -// compress - enable/disable streaming file -// compression via zlib; level is 0 to disable -// compression, or up to 9 for max compression -//------------------------------------------------- - -osd_file::error emu_file::compress(int level) -{ - return m_file->compress(level); -} - - -//------------------------------------------------- // compressed_file_ready - ensure our zip is ready // loading if needed //------------------------------------------------- -bool emu_file::compressed_file_ready(void) +std::error_condition emu_file::compressed_file_ready() { // load the ZIP file now if we haven't yet - if (m_zipfile && (load_zipped_file() != osd_file::error::NONE)) - return true; - - return false; + return m_zipfile ? load_zipped_file() : std::error_condition(); } //------------------------------------------------- // seek - seek within a file //------------------------------------------------- -int emu_file::seek(s64 offset, int whence) +std::error_condition emu_file::seek(s64 offset, int whence) { // load the ZIP file now if we haven't yet - if (compressed_file_ready()) - return 1; + std::error_condition err = compressed_file_ready(); + if (err) + return err; // seek if we can if (m_file) return m_file->seek(offset, whence); - return 1; + return std::errc::bad_file_descriptor; // TODO: revisit this error condition } @@ -453,13 +469,15 @@ int emu_file::seek(s64 offset, int whence) u64 emu_file::tell() { + // FIXME: need better interface to report errors // load the ZIP file now if we haven't yet if (compressed_file_ready()) return 0; // tell if we can - if (m_file) - return m_file->tell(); + u64 result; + if (m_file && !m_file->tell(result)) + return result; return 0; } @@ -489,13 +507,15 @@ bool emu_file::eof() u64 emu_file::size() { + // FIXME: need better interface to report errors // use the ZIP length if present - if (m_zipfile != nullptr) + if (m_zipfile) return m_ziplength; // return length if we can - if (m_file) - return m_file->size(); + u64 result; + if (m_file && !m_file->length(result)) + return result; return 0; } @@ -507,15 +527,18 @@ u64 emu_file::size() u32 emu_file::read(void *buffer, u32 length) { + // FIXME: need better interface to report errors // load the ZIP file now if we haven't yet if (compressed_file_ready()) return 0; // read the data if we can + std::error_condition err; + size_t actual = 0; if (m_file) - return m_file->read(buffer, length); + std::tie(err, actual) = util::read(*m_file, buffer, length); - return 0; + return actual; } @@ -579,11 +602,14 @@ char *emu_file::gets(char *s, int n) u32 emu_file::write(const void *buffer, u32 length) { + // FIXME: need better interface to report errors // write the data if we can + std::error_condition err; + size_t actual = 0; if (m_file) - return m_file->write(buffer, length); + std::tie(err, actual) = util::write(*m_file, buffer, length); - return 0; + return actual; } @@ -591,7 +617,7 @@ u32 emu_file::write(const void *buffer, u32 length) // puts - write a line to a text file //------------------------------------------------- -int emu_file::puts(const char *s) +int emu_file::puts(std::string_view s) { // write the data if we can if (m_file) @@ -605,7 +631,7 @@ int emu_file::puts(const char *s) // vfprintf - vfprintf to a text file //------------------------------------------------- -int emu_file::vprintf(util::format_argument_pack<std::ostream> const &args) +int emu_file::vprintf(util::format_argument_pack<char> const &args) { // write the data if we can return m_file ? m_file->vprintf(args) : 0; @@ -629,76 +655,123 @@ void emu_file::flush() // any media path //------------------------------------------------- -bool emu_file::part_of_mediapath(std::string path) +bool emu_file::part_of_mediapath(const std::string &path) { - bool result = false; + if (!m_restrict_to_mediapath) + return true; + + for (size_t i = 0U; (m_mediapaths.size() > i) && ((0 > m_restrict_to_mediapath) || (i < m_restrict_to_mediapath)); i++) + { + m_mediapaths[i].first.reset(); + if (!m_mediapaths[i].first.next(m_mediapaths[i].second)) + return false; + } + std::string mediapath; - m_mediapaths.reset(); - while (m_mediapaths.next(mediapath, nullptr) && !result) { - if (path.compare(mediapath.substr(0, mediapath.length()))) - result = true; + while (true) + { + mediapath.clear(); + for (size_t i = 0U; (m_mediapaths.size() > i) && ((0 > m_restrict_to_mediapath) || (i < m_restrict_to_mediapath)); i++) + { + mediapath.append(m_mediapaths[i].second); + if (!mediapath.empty() && !util::is_directory_separator(mediapath.back())) + mediapath.append(PATH_SEPARATOR); + } + + if (!path.compare(0, mediapath.size(), mediapath)) + { + LOG("emu_file: path '%s' matches media path '%s'\n", path, mediapath); + return true; + } + + size_t i = 0U; + while ((m_mediapaths.size() > i) && ((0 > m_restrict_to_mediapath) || (i < m_restrict_to_mediapath))) + { + if (m_mediapaths[i].first.next(m_mediapaths[i].second)) + { + for (size_t j = 0U; i != j; j++) + { + m_mediapaths[j].first.reset(); + m_mediapaths[j].first.next(m_mediapaths[j].second); + } + break; + } + i++; + } + if ((m_mediapaths.size() == i) || ((0 <= m_restrict_to_mediapath) && (i == m_restrict_to_mediapath))) + { + LOG("emu_file: path '%s' not in media path\n", path); + return false; + } } - return result; } //------------------------------------------------- // attempt_zipped - attempt to open a ZIPped file //------------------------------------------------- -osd_file::error emu_file::attempt_zipped() +std::error_condition emu_file::attempt_zipped() { - typedef util::archive_file::error (*open_func)(const std::string &filename, util::archive_file::ptr &result); + typedef std::error_condition (*open_func)(std::string_view filename, util::archive_file::ptr &result); char const *const suffixes[] = { ".zip", ".7z" }; - open_func const open_funcs[ARRAY_LENGTH(suffixes)] = { &util::archive_file::open_zip, &util::archive_file::open_7z }; + open_func const open_funcs[std::size(suffixes)] = { &util::archive_file::open_zip, &util::archive_file::open_7z }; // loop over archive types std::string const savepath(m_fullpath); std::string filename; - for (unsigned i = 0; i < ARRAY_LENGTH(suffixes); i++, m_fullpath = savepath, filename.clear()) + for (unsigned i = 0; i < std::size(suffixes); i++, m_fullpath = savepath, filename.clear()) { // loop over directory parts up to the start of filename while (1) { - // find the final path separator - auto const dirsep = m_fullpath.find_last_of(PATH_SEPARATOR[0]); - if (dirsep == std::string::npos) + if (!part_of_mediapath(m_fullpath)) break; - if (restrict_to_mediapath() && !part_of_mediapath(m_fullpath)) + // find the final path separator + auto const dirsepiter(std::find_if(m_fullpath.rbegin(), m_fullpath.rend(), util::is_directory_separator)); + if (dirsepiter == m_fullpath.rend()) break; + std::string::size_type const dirsep(std::distance(m_fullpath.begin(), dirsepiter.base()) - 1); // insert the part from the right of the separator into the head of the filename - if (!filename.empty()) filename.insert(0, 1, '/'); + if (!filename.empty()) + filename.insert(0, 1, '/'); filename.insert(0, m_fullpath.substr(dirsep + 1, std::string::npos)); // remove this part of the filename and append an archive extension m_fullpath.resize(dirsep); m_fullpath.append(suffixes[i]); + LOG("emu_file: looking for '%s' in archive '%s'\n", filename, m_fullpath); // attempt to open the archive file util::archive_file::ptr zip; - util::archive_file::error ziperr = open_funcs[i](m_fullpath, zip); + std::error_condition ziperr = open_funcs[i](m_fullpath, zip); // chop the archive suffix back off the filename before continuing m_fullpath = m_fullpath.substr(0, dirsep); // if we failed to open this file, continue scanning - if (ziperr != util::archive_file::error::NONE) + if (ziperr) continue; int header = -1; // see if we can find a file with the right name and (if available) CRC - if (m_openflags & OPEN_FLAG_HAS_CRC) header = zip->search(m_crc, filename, false); - if (header < 0 && (m_openflags & OPEN_FLAG_HAS_CRC)) header = zip->search(m_crc, filename, true); + if (m_openflags & OPEN_FLAG_HAS_CRC) + header = zip->search(m_crc, filename, false); + if (header < 0 && (m_openflags & OPEN_FLAG_HAS_CRC)) + header = zip->search(m_crc, filename, true); // if that failed, look for a file with the right CRC, but the wrong filename - if (header < 0 && (m_openflags & OPEN_FLAG_HAS_CRC)) header = zip->search(m_crc); + if (header < 0 && (m_openflags & OPEN_FLAG_HAS_CRC)) + header = zip->search(m_crc); // if that failed, look for a file with the right name; // reporting a bad checksum is more helpful and less confusing than reporting "ROM not found" - if (header < 0) header = zip->search(filename, false); - if (header < 0) header = zip->search(filename, true); + if (header < 0) + header = zip->search(filename, false); + if (header < 0) + header = zip->search(filename, true); // if we got it, read the data if (header >= 0) @@ -709,14 +782,15 @@ osd_file::error emu_file::attempt_zipped() // build a hash with just the CRC m_hashes.reset(); m_hashes.add_crc(m_zipfile->current_crc()); - return (m_openflags & OPEN_FLAG_NO_PRELOAD) ? osd_file::error::NONE : load_zipped_file(); + m_fullpath = savepath; + return (m_openflags & OPEN_FLAG_NO_PRELOAD) ? std::error_condition() : load_zipped_file(); } // close up the archive file and try the next level zip.reset(); } } - return osd_file::error::NOT_FOUND; + return std::errc::no_such_file_or_directory; } @@ -724,7 +798,7 @@ osd_file::error emu_file::attempt_zipped() // load_zipped_file - load a ZIPped file //------------------------------------------------- -osd_file::error emu_file::load_zipped_file() +std::error_condition emu_file::load_zipped_file() { assert(m_file == nullptr); assert(m_zipdata.empty()); @@ -734,22 +808,22 @@ osd_file::error emu_file::load_zipped_file() m_zipdata.resize(m_ziplength); // read the data into our buffer and return - auto const ziperr = m_zipfile->decompress(&m_zipdata[0], m_zipdata.size()); - if (ziperr != util::archive_file::error::NONE) + auto const ziperr = m_zipfile->decompress(m_zipdata.data(), m_zipdata.size()); + if (ziperr) { m_zipdata.clear(); - return osd_file::error::FAILURE; + return ziperr; } // convert to RAM file - osd_file::error filerr = util::core_file::open_ram(&m_zipdata[0], m_zipdata.size(), m_openflags, m_file); - if (filerr != osd_file::error::NONE) + std::error_condition const filerr = util::core_file::open_ram(m_zipdata.data(), m_zipdata.size(), m_openflags, m_file); + if (filerr) { m_zipdata.clear(); - return osd_file::error::FAILURE; + return filerr; } // close out the ZIP file m_zipfile.reset(); - return osd_file::error::NONE; + return std::error_condition(); } diff --git a/src/emu/fileio.h b/src/emu/fileio.h index 26e9fb4e907..225cebd0a81 100644 --- a/src/emu/fileio.h +++ b/src/emu/fileio.h @@ -16,6 +16,14 @@ #include "corefile.h" #include "hash.h" +#include <iterator> +#include <string> +#include <system_error> +#include <type_traits> +#include <utility> +#include <vector> + + // some systems use macros for getc/putc rather than functions #ifdef getc #undef getc @@ -37,18 +45,50 @@ public: path_iterator(path_iterator &&that); path_iterator(path_iterator const &that); + template <typename T> + path_iterator(T &&searchpath, std::enable_if_t<std::is_constructible<std::string, T>::value, int> = 0) + : path_iterator(std::string(std::forward<T>(searchpath))) + { } + + // TODO: this doesn't work with C arrays (only vector, std::array, etc.) + template <typename T> + path_iterator(T &&paths, std::enable_if_t<std::is_constructible<std::string, typename std::remove_reference_t<T>::value_type>::value, int> = 0) + : path_iterator(concatenate_paths(std::forward<T>(paths))) + { m_separator = '\0'; } + // assignment operators path_iterator &operator=(path_iterator &&that); path_iterator &operator=(path_iterator const &that); // main interface - bool next(std::string &buffer, const char *name = nullptr); + bool next(std::string &buffer); void reset(); private: + // helpers + template <typename T> + static std::string concatenate_paths(T &&paths) + { + std::string result; + auto it(std::begin(paths)); + if (std::end(paths) != it) + { + result.append(*it); + ++it; + } + while (std::end(paths) != it) + { + result.append(1, '\0'); + result.append(*it); + ++it; + } + return result; + } + // internal state std::string m_searchpath; std::string::const_iterator m_current; + char m_separator; bool m_is_first; }; @@ -68,7 +108,7 @@ public: file_enumerator &operator=(file_enumerator const &) = delete; // iterator - const osd::directory::entry *next(); + const osd::directory::entry *next(const char *subdir = nullptr); private: // internal state @@ -83,10 +123,24 @@ private: class emu_file { + enum empty_t { EMPTY }; + using searchpath_vector = std::vector<std::pair<path_iterator, std::string> >; + public: // file open/creation emu_file(u32 openflags); - emu_file(std::string &&searchpath, u32 openflags); + template <typename T> + emu_file(T &&searchpath, std::enable_if_t<std::is_constructible<path_iterator, T>::value, u32> openflags) + : emu_file(path_iterator(std::forward<T>(searchpath)), openflags) + { } + template <typename T, typename U, typename V, typename... W> + emu_file(T &&searchpath, U &&x, V &&y, W &&... z) + : emu_file(0U, EMPTY) + { + m_iterator.reserve(sizeof...(W) + 1); + m_mediapaths.reserve(sizeof...(W) + 1); + set_searchpaths(std::forward<T>(searchpath), std::forward<U>(x), std::forward<V>(y), std::forward<W>(z)...); + } virtual ~emu_file(); // getters @@ -95,33 +149,26 @@ public: const char *filename() const { return m_filename.c_str(); } const char *fullpath() const { return m_fullpath.c_str(); } u32 openflags() const { return m_openflags; } - util::hash_collection &hashes(const char *types); - bool restrict_to_mediapath() const { return m_restrict_to_mediapath; } - bool part_of_mediapath(std::string path); + util::hash_collection &hashes(std::string_view types); // setters void remove_on_close() { m_remove_on_close = true; } void set_openflags(u32 openflags) { assert(!m_file); m_openflags = openflags; } - void set_restrict_to_mediapath(bool rtmp = true) { m_restrict_to_mediapath = rtmp; } + void set_restrict_to_mediapath(int rtmp) { m_restrict_to_mediapath = rtmp; } // open/close - osd_file::error open(const std::string &name); - osd_file::error open(const std::string &name1, const std::string &name2); - osd_file::error open(const std::string &name1, const std::string &name2, const std::string &name3); - osd_file::error open(const std::string &name1, const std::string &name2, const std::string &name3, const std::string &name4); - osd_file::error open(const std::string &name, u32 crc); - osd_file::error open(const std::string &name1, const std::string &name2, u32 crc); - osd_file::error open(const std::string &name1, const std::string &name2, const std::string &name3, u32 crc); - osd_file::error open(const std::string &name1, const std::string &name2, const std::string &name3, const std::string &name4, u32 crc); - osd_file::error open_next(); - osd_file::error open_ram(const void *data, u32 length); + std::error_condition open(std::string &&name); + std::error_condition open(std::string &&name, u32 crc); + std::error_condition open(std::string_view name) { return open(std::string(name)); } + std::error_condition open(std::string_view name, u32 crc) { return open(std::string(name), crc); } + std::error_condition open(const char *name) { return open(std::string(name)); } + std::error_condition open(const char *name, u32 crc) { return open(std::string(name), crc); } + std::error_condition open_next(); + std::error_condition open_ram(const void *data, u32 length); void close(); - // control - osd_file::error compress(int compress); - // position - int seek(s64 offset, int whence); + std::error_condition seek(s64 offset, int whence); u64 tell(); bool eof(); u64 size(); @@ -134,8 +181,8 @@ public: // writing u32 write(const void *buffer, u32 length); - int puts(const char *s); - int vprintf(util::format_argument_pack<std::ostream> const &args); + int puts(std::string_view s); + int vprintf(util::format_argument_pack<char> const &args); template <typename Format, typename... Params> int printf(Format &&fmt, Params &&...args) { return vprintf(util::make_format_argument_pack(std::forward<Format>(fmt), std::forward<Params>(args)...)); @@ -145,28 +192,65 @@ public: void flush(); private: - bool compressed_file_ready(void); + emu_file(u32 openflags, empty_t); + emu_file(path_iterator &&searchpath, u32 openflags); + + template <typename T> + void set_searchpaths(T &&searchpath, u32 openflags) + { + m_iterator.emplace_back(searchpath, ""); + m_mediapaths.emplace_back(std::forward<T>(searchpath), ""); + m_openflags = openflags; + } + template <typename T, typename U, typename V, typename... W> + void set_searchpaths(T &&searchpath, U &&x, V &&y, W &&... z) + { + m_iterator.emplace_back(searchpath, ""); + m_mediapaths.emplace_back(std::forward<T>(searchpath), ""); + set_searchpaths(std::forward<U>(x), std::forward<V>(y), std::forward<W>(z)...); + } + + bool part_of_mediapath(const std::string &path); + std::error_condition compressed_file_ready(); // internal helpers - osd_file::error attempt_zipped(); - osd_file::error load_zipped_file(); + std::error_condition attempt_zipped(); + std::error_condition load_zipped_file(); // internal state std::string m_filename; // original filename provided std::string m_fullpath; // full filename util::core_file::ptr m_file; // core file pointer - path_iterator m_iterator; // iterator for paths - path_iterator m_mediapaths; // media-path iterator + searchpath_vector m_iterator; // iterator for paths + searchpath_vector m_mediapaths; // media-path iterator + bool m_first; // true if this is the start of iteration u32 m_crc; // file's CRC u32 m_openflags; // flags we used for the open util::hash_collection m_hashes; // collection of hashes std::unique_ptr<util::archive_file> m_zipfile; // ZIP file pointer - std::vector<u8> m_zipdata; // ZIP file data - u64 m_ziplength; // ZIP file length + std::vector<u8> m_zipdata; // ZIP file data + u64 m_ziplength; // ZIP file length - bool m_remove_on_close; // flag: remove the file when closing - bool m_restrict_to_mediapath; // flag: restrict to paths inside the media-path + bool m_remove_on_close; // flag: remove the file when closing + int m_restrict_to_mediapath; // flag: restrict to paths inside the media-path }; + +extern template path_iterator::path_iterator(char *&, int); +extern template path_iterator::path_iterator(char * const &, int); +extern template path_iterator::path_iterator(char const *&, int); +extern template path_iterator::path_iterator(char const * const &, int); +extern template path_iterator::path_iterator(std::vector<std::string> &, int); +extern template path_iterator::path_iterator(const std::vector<std::string> &, int); + +extern template emu_file::emu_file(std::string &, u32); +extern template emu_file::emu_file(const std::string &, u32); +extern template emu_file::emu_file(char *&, u32); +extern template emu_file::emu_file(char * const &, u32); +extern template emu_file::emu_file(char const *&, u32); +extern template emu_file::emu_file(char const * const &, u32); +extern template emu_file::emu_file(std::vector<std::string> &, u32); +extern template emu_file::emu_file(const std::vector<std::string> &, u32); + #endif // MAME_EMU_FILEIO_H diff --git a/src/emu/gamedrv.h b/src/emu/gamedrv.h index 834f5b16a43..b3a1087a66a 100644 --- a/src/emu/gamedrv.h +++ b/src/emu/gamedrv.h @@ -16,6 +16,9 @@ #include <type_traits> +/// \defgroup machinedef Machine definition macros + + //************************************************************************** // CONSTANTS //************************************************************************** @@ -27,83 +30,102 @@ struct machine_flags { enum type : u32 { - MASK_ORIENTATION = 0x00000007, - MASK_TYPE = 0x00000038, + MASK_ORIENTATION = 0x0000'0007, - FLIP_X = 0x00000001, - FLIP_Y = 0x00000002, - SWAP_XY = 0x00000004, - ROT0 = 0x00000000, + FLIP_X = 0x0000'0001, + FLIP_Y = 0x0000'0002, + SWAP_XY = 0x0000'0004, + ROT0 = 0x0000'0000, ROT90 = FLIP_X | SWAP_XY, ROT180 = FLIP_X | FLIP_Y, ROT270 = FLIP_Y | SWAP_XY, - TYPE_ARCADE = 0x00000008, // coin-operated machine for public use - TYPE_CONSOLE = 0x00000010, // console system - TYPE_COMPUTER = 0x00000018, // any kind of computer including home computers, minis, calculators, ... - TYPE_OTHER = 0x00000038, // any other emulated system (e.g. clock, satellite receiver, ...) - - NOT_WORKING = 0x00000040, - SUPPORTS_SAVE = 0x00000080, // system supports save states - NO_COCKTAIL = 0x00000100, // screen flip support is missing - IS_BIOS_ROOT = 0x00000200, // this driver entry is a BIOS root - REQUIRES_ARTWORK = 0x00000400, // requires external artwork for key game elements - CLICKABLE_ARTWORK = 0x00000800, // artwork is clickable and requires mouse cursor - UNOFFICIAL = 0x00001000, // unofficial hardware modification - NO_SOUND_HW = 0x00002000, // system has no sound output - MECHANICAL = 0x00004000, // contains mechanical parts (pinball, redemption games, ...) - IS_INCOMPLETE = 0x00008000 // official system with blatantly incomplete hardware/software + NO_COCKTAIL = 0x0000'0040, // screen flip support is missing + IS_BIOS_ROOT = 0x0000'0080, // this driver entry is a BIOS root + REQUIRES_ARTWORK = 0x0000'0100, // requires external artwork for key game elements + UNOFFICIAL = 0x0000'0200, // unofficial hardware modification + NO_SOUND_HW = 0x0000'0400, // system has no sound output + MECHANICAL = 0x0000'0800, // contains mechanical parts (pinball, redemption games, ...) + IS_INCOMPLETE = 0x0000'1000 // official system with blatantly incomplete hardware/software }; }; DECLARE_ENUM_BITWISE_OPERATORS(machine_flags::type); +/// \addtogroup machinedef +/// \{ +/// \name System emulation status constants +/// +/// Constants representing system emulation status flags. May be +/// combined for use as FLAGS arguments to the #GAME, #GAMEL, #CONS, +/// #COMP and #SYST macros. +/// \{ + // flags for machine drivers -constexpr u64 MACHINE_TYPE_ARCADE = machine_flags::TYPE_ARCADE; -constexpr u64 MACHINE_TYPE_CONSOLE = machine_flags::TYPE_CONSOLE; -constexpr u64 MACHINE_TYPE_COMPUTER = machine_flags::TYPE_COMPUTER; -constexpr u64 MACHINE_TYPE_OTHER = machine_flags::TYPE_OTHER; -constexpr u64 MACHINE_NOT_WORKING = machine_flags::NOT_WORKING; -constexpr u64 MACHINE_SUPPORTS_SAVE = machine_flags::SUPPORTS_SAVE; -constexpr u64 MACHINE_NO_COCKTAIL = machine_flags::NO_COCKTAIL; -constexpr u64 MACHINE_IS_BIOS_ROOT = machine_flags::IS_BIOS_ROOT; -constexpr u64 MACHINE_REQUIRES_ARTWORK = machine_flags::REQUIRES_ARTWORK; -constexpr u64 MACHINE_CLICKABLE_ARTWORK = machine_flags::CLICKABLE_ARTWORK; -constexpr u64 MACHINE_UNOFFICIAL = machine_flags::UNOFFICIAL; -constexpr u64 MACHINE_NO_SOUND_HW = machine_flags::NO_SOUND_HW; -constexpr u64 MACHINE_MECHANICAL = machine_flags::MECHANICAL; -constexpr u64 MACHINE_IS_INCOMPLETE = machine_flags::IS_INCOMPLETE; - -// flags taht map to device feature flags -constexpr u64 MACHINE_UNEMULATED_PROTECTION = 0x00000001'00000000; // game's protection not fully emulated -constexpr u64 MACHINE_WRONG_COLORS = 0x00000002'00000000; // colors are totally wrong -constexpr u64 MACHINE_IMPERFECT_COLORS = 0x00000004'00000000; // colors are not 100% accurate, but close -constexpr u64 MACHINE_IMPERFECT_GRAPHICS = 0x00000008'00000000; // graphics are wrong/incomplete -constexpr u64 MACHINE_NO_SOUND = 0x00000010'00000000; // sound is missing -constexpr u64 MACHINE_IMPERFECT_SOUND = 0x00000020'00000000; // sound is known to be wrong -constexpr u64 MACHINE_IMPERFECT_CONTROLS = 0x00000040'00000000; // controls are known to be imperfectly emulated -constexpr u64 MACHINE_NODEVICE_MICROPHONE = 0x00000080'00000000; // any game/system that has unemulated audio capture device -constexpr u64 MACHINE_NODEVICE_PRINTER = 0x00000100'00000000; // any game/system that has unemulated hardcopy output device -constexpr u64 MACHINE_NODEVICE_LAN = 0x00000200'00000000; // any game/system that has unemulated local networking -constexpr u64 MACHINE_IMPERFECT_TIMING = 0x00000400'00000000; // timing is known to be imperfectly emulated - -// useful combinations of flags -constexpr u64 MACHINE_IS_SKELETON = MACHINE_NO_SOUND | MACHINE_NOT_WORKING; // flag combination for skeleton drivers -constexpr u64 MACHINE_IS_SKELETON_MECHANICAL = MACHINE_IS_SKELETON | MACHINE_MECHANICAL | MACHINE_REQUIRES_ARTWORK; // flag combination for skeleton mechanical machines +constexpr u64 MACHINE_NO_COCKTAIL = machine_flags::NO_COCKTAIL; ///< The system supports screen flipping for use in a cocktail cabinet, but this feature is not properly emulated +constexpr u64 MACHINE_IS_BIOS_ROOT = machine_flags::IS_BIOS_ROOT; ///< The system represents an empty system board of some kind - clones are treated as separate systems rather than variants +constexpr u64 MACHINE_REQUIRES_ARTWORK = machine_flags::REQUIRES_ARTWORK; ///< The system requires external artwork for key functionality +constexpr u64 MACHINE_UNOFFICIAL = machine_flags::UNOFFICIAL; ///< The system represents an after-market or end-user modification to a system +constexpr u64 MACHINE_NO_SOUND_HW = machine_flags::NO_SOUND_HW; ///< The system has no sound output capability +constexpr u64 MACHINE_MECHANICAL = machine_flags::MECHANICAL; ///< The system depends on mechanical features for key functionality +constexpr u64 MACHINE_IS_INCOMPLETE = machine_flags::IS_INCOMPLETE; ///< The system represents an incomplete prototype + +// flags that map to device emulation and feature flags +constexpr u64 MACHINE_NOT_WORKING = 0x00000001'00000000; ///< Imperfect emulation prevents using the system as intended +constexpr u64 MACHINE_SUPPORTS_SAVE = 0x00000002'00000000; ///< All devices in the system supports save states (enables auto save feature, and won't show a warning on using save states) +constexpr u64 MACHINE_UNEMULATED_PROTECTION = 0x00000004'00000000; ///< Some form of protection is imperfectly emulated (e.g. copy protection or anti-tampering) +constexpr u64 MACHINE_WRONG_COLORS = 0x00000008'00000000; ///< Colours are completely wrong +constexpr u64 MACHINE_IMPERFECT_COLORS = 0x00000010'00000000; ///< Colours are close but not completely accurate +constexpr u64 MACHINE_IMPERFECT_GRAPHICS = 0x00000020'00000000; ///< Graphics are emulated incorrectly for the system +constexpr u64 MACHINE_NO_SOUND = 0x00000040'00000000; ///< The system has sound output, but it is not emulated +constexpr u64 MACHINE_IMPERFECT_SOUND = 0x00000080'00000000; ///< Sound is known to be imperfectly emulated for the system +constexpr u64 MACHINE_IMPERFECT_CONTROLS = 0x00000100'00000000; ///< Controls or inputs are emulated imperfectly for the system +constexpr u64 MACHINE_NODEVICE_MICROPHONE = 0x00000200'00000000; ///< The system has unemulated audio capture functionality +constexpr u64 MACHINE_NODEVICE_PRINTER = 0x00000400'00000000; ///< The system has unemulated printer functionality +constexpr u64 MACHINE_NODEVICE_LAN = 0x00000800'00000000; ///< The system has unemulated local area networking +constexpr u64 MACHINE_IMPERFECT_TIMING = 0x00001000'00000000; ///< Timing is known to be imperfectly emulated for the system + +/// \} +/// \} //************************************************************************** // TYPE DEFINITIONS //************************************************************************** -// static POD structure describing each game driver entry +/// \brief Static system description +/// +/// A plain data structure providing static information about a system. +/// Used to allow multiple systems to be implemented using a single +/// system device class (an implementation of #driver_device). class game_driver { public: typedef void (*machine_creator_wrapper)(machine_config &, device_t &); typedef void (*driver_init_wrapper)(device_t &); + /// \brief Get emulation flags + /// + /// Converts system flags corresponding to device emulation flags to + /// a device flags type bit field. + /// \param [in] flags A system flags bit field. + /// \return A device flags type bit field corresponding to emulation + /// flags declared in the \p flags argument. + static constexpr device_t::flags_type emulation_flags(u64 flags) + { + return + ((flags & MACHINE_NOT_WORKING) ? device_t::flags::NOT_WORKING : device_t::flags::NONE) | + ((flags & MACHINE_SUPPORTS_SAVE) ? device_t::flags::NONE : device_t::flags::SAVE_UNSUPPORTED); + } + + /// \brief Get unemulated system features + /// + /// Converts system flags corresponding to unemulated device + /// features to a device feature type bit field. + /// \param [in] flags A system flags bit field. + /// \return A device feature type bit field corresponding to + /// unemulated features declared in the \p flags argument. static constexpr device_t::feature_type unemulated_features(u64 flags) { return @@ -114,6 +136,14 @@ public: ((flags & MACHINE_NODEVICE_LAN) ? device_t::feature::LAN : device_t::feature::NONE); } + /// \brief Get imperfectly emulated system features + /// + /// Converts system flags corresponding to imperfectly emulated + /// device features to a device feature type bit field. + /// \param [in] flags A system flags bit field. + /// \return A device feature type bit field corresponding to + /// imperfectly emulated features declared in the \p flags + /// argument. static constexpr device_t::feature_type imperfect_features(u64 flags) { return @@ -126,7 +156,7 @@ public: } device_type type; // static type info for driver class - const char * parent; // if this is a clone, the name of the parent + const char * parent; // name of the parent or BIOS system if applicable const char * year; // year the game was released const char * manufacturer; // manufacturer of the game machine_creator_wrapper machine_creator; // machine driver tokens @@ -136,7 +166,7 @@ public: const char * compatible_with; const internal_layout * default_layout; // default internally defined layout machine_flags::type flags; // orientation and other flags; see defines above - char name[MAX_DRIVER_NAME_CHARS + 1]; // short name of the game + char name[MAX_DRIVER_NAME_CHARS + 1]; // short name of the system }; @@ -151,22 +181,73 @@ public: // static game traits #define GAME_DRIVER_TRAITS(NAME, FULLNAME) \ -namespace { \ - struct GAME_TRAITS_NAME(NAME) { static constexpr char const shortname[] = #NAME, fullname[] = FULLNAME, source[] = __FILE__; }; \ - constexpr char const GAME_TRAITS_NAME(NAME)::shortname[], GAME_TRAITS_NAME(NAME)::fullname[], GAME_TRAITS_NAME(NAME)::source[]; \ -} + namespace { \ + struct GAME_TRAITS_NAME(NAME) { static constexpr char const shortname[] = #NAME, fullname[] = FULLNAME, source[] = __FILE__; }; \ + constexpr char const GAME_TRAITS_NAME(NAME)::shortname[], GAME_TRAITS_NAME(NAME)::fullname[], GAME_TRAITS_NAME(NAME)::source[]; \ + } #define GAME_DRIVER_TYPE(NAME, CLASS, FLAGS) \ -driver_device_creator< \ - CLASS, \ - (GAME_TRAITS_NAME(NAME)::shortname), \ - (GAME_TRAITS_NAME(NAME)::fullname), \ - (GAME_TRAITS_NAME(NAME)::source), \ - game_driver::unemulated_features(FLAGS), \ - game_driver::imperfect_features(FLAGS)> - -// standard GAME() macro -#define GAME(YEAR,NAME,PARENT,MACHINE,INPUT,CLASS,INIT,MONITOR,COMPANY,FULLNAME,FLAGS) \ -GAME_DRIVER_TRAITS(NAME,FULLNAME) \ + emu::detail::driver_tag_struct< \ + CLASS, \ + (GAME_TRAITS_NAME(NAME)::shortname), \ + (GAME_TRAITS_NAME(NAME)::fullname), \ + (GAME_TRAITS_NAME(NAME)::source), \ + game_driver::emulation_flags(FLAGS), \ + game_driver::unemulated_features(FLAGS), \ + game_driver::imperfect_features(FLAGS)>{ } + + +/// \addtogroup machinedef +/// \{ + +/// \brief Define a system +/// +/// Must be used in the global namespace. +/// +/// Creates an appropriately named and populated #game_driver structure +/// describing the system. +/// \param YEAR The year that the system was first made available. Must +/// be a token containing only the digits zero to nine, question mark +/// and plus sign. +/// \param NAME The short name of the system, used for identification, +/// and in filesystem paths for assets and data. Must be a token no +/// longer than sixteen characters, containing only ASCII lowercase +/// letters, digits and underscores. Must be globally unique across +/// systems and devices. +/// \param PARENT Short name of the parent or BIOS system if applicable, +/// or a single digit zero otherwise. +/// \param MACHINE Function used to buid machine configuration for the +/// system. Must be a public member function of the system device +/// class (\p CLASS argument), returning void and taking a reference +/// to a #machine_config object as a parameter. +/// \param INPUT Input port definitions for the root device of the +/// system, usually defined using #INPUT_PORTS_START and associated +/// macros. +/// \param CLASS Class to instantiate as the root device of the system. +/// Must be an implementation of #driver_device. +/// \param INIT Initialisation function called after all child devices +/// have started, but before the driver start functions are called. +/// Often used for tasks like decrypting ROMs. Must be a public +/// member function of the system device class (\p CLASS argument), +/// returning void and accepting no parameters. The function +/// #driver_device::empty_init is supplied for systems that don't need +/// to perform additional tasks. +/// \param MONITOR Screen orientation flags applied to all screens in +/// the system, after the individual screens' orientation flags are +/// applied. Usually one #ROT0, #ROT90, #ROT180 or #ROT270. +/// \param COMPANY Name of the developer or distributor of the system. +/// Must be a string. +/// \param FULLNAME Display name for the system. Must be a string, and +/// must be globally unique across systems and devices. +/// \param FLAGS Bitwise combination of emulation status flags for the +/// system, in addition to flags supplied by the system device class +/// (see #device_t::unemulated_features and +/// #device_t::imperfect_features). It is advisable to supply +/// unemulated and imperfectly emulated feature flags that apply to +/// all systems implemented using the class in the class itself to +/// avoid repetition. +/// \sa GAMEL SYST +#define GAME(YEAR, NAME, PARENT, MACHINE, INPUT, CLASS, INIT, MONITOR, COMPANY, FULLNAME, FLAGS) \ +GAME_DRIVER_TRAITS(NAME, FULLNAME) \ extern game_driver const GAME_NAME(NAME) \ { \ GAME_DRIVER_TYPE(NAME, CLASS, FLAGS), \ @@ -179,52 +260,66 @@ extern game_driver const GAME_NAME(NAME) \ ROM_NAME(NAME), \ nullptr, \ nullptr, \ - machine_flags::type(u32((MONITOR) | (FLAGS) | MACHINE_TYPE_ARCADE)),\ + machine_flags::type(u32((MONITOR) | (FLAGS))), \ #NAME \ }; -// standard macro with additional layout -#define GAMEL(YEAR,NAME,PARENT,MACHINE,INPUT,CLASS,INIT,MONITOR,COMPANY,FULLNAME,FLAGS,LAYOUT) \ -GAME_DRIVER_TRAITS(NAME,FULLNAME) \ -extern game_driver const GAME_NAME(NAME) \ -{ \ - GAME_DRIVER_TYPE(NAME, CLASS, FLAGS), \ - #PARENT, \ - #YEAR, \ - COMPANY, \ - [] (machine_config &config, device_t &owner) { downcast<CLASS &>(owner).MACHINE(config); }, \ - INPUT_PORTS_NAME(INPUT), \ - [] (device_t &owner) { downcast<CLASS &>(owner).INIT(); }, \ - ROM_NAME(NAME), \ - nullptr, \ - &LAYOUT, \ - machine_flags::type(u32((MONITOR) | (FLAGS) | MACHINE_TYPE_ARCADE)),\ - #NAME \ -}; - - -// standard console definition macro -#define CONS(YEAR,NAME,PARENT,COMPAT,MACHINE,INPUT,CLASS,INIT,COMPANY,FULLNAME,FLAGS) \ -GAME_DRIVER_TRAITS(NAME,FULLNAME) \ -extern game_driver const GAME_NAME(NAME) \ -{ \ - GAME_DRIVER_TYPE(NAME, CLASS, FLAGS), \ - #PARENT, \ - #YEAR, \ - COMPANY, \ - [] (machine_config &config, device_t &owner) { downcast<CLASS &>(owner).MACHINE(config); }, \ - INPUT_PORTS_NAME(INPUT), \ - [] (device_t &owner) { downcast<CLASS &>(owner).INIT(); }, \ - ROM_NAME(NAME), \ - #COMPAT, \ - nullptr, \ - machine_flags::type(u32(ROT0 | (FLAGS) | MACHINE_TYPE_CONSOLE)), \ - #NAME \ -}; -// standard computer definition macro -#define COMP(YEAR,NAME,PARENT,COMPAT,MACHINE,INPUT,CLASS,INIT,COMPANY,FULLNAME,FLAGS) \ -GAME_DRIVER_TRAITS(NAME,FULLNAME) \ +/// \brief Define a system with an additional internal layout +/// +/// Equivalent to the #GAME macro, but with the additional ability to +/// supply a system-specific internal artwork layout. Views from the +/// system-specific layout are available in addition to any views from +/// layouts specified in the machine configuration. Must be used in the +/// global namespace. +/// +/// Creates an appropriately named and populated #game_driver structure +/// describing the system. +/// \param YEAR The year that the system was first made available. Must +/// be a token containing only the digits zero to nine, question mark +/// and plus sign. +/// \param NAME The short name of the system, used for identification, +/// and in filesystem paths for assets and data. Must be a token no +/// longer than sixteen characters, containing only ASCII lowercase +/// letters, digits and underscores. Must be globally unique across +/// systems and devices. +/// \param PARENT Short name of the parent or BIOS system if applicable, +/// or a single digit zero otherwise. +/// \param MACHINE Function used to buid machine configuration for the +/// system. Must be a public member function of the system device +/// class (\p CLASS argument), returning void and taking a reference +/// to a #machine_config object as a parameter. +/// \param INPUT Input port definitions for the root device of the +/// system, usually defined using #INPUT_PORTS_START and associated +/// macros. +/// \param CLASS Class to instantiate as the root device of the system. +/// Must be an implementation of #driver_device. +/// \param INIT Initialisation function called after all child devices +/// have started, but before the driver start functions are called. +/// Often used for tasks like decrypting ROMs. Must be a public +/// member function of the system device class (\p CLASS argument), +/// returning void and accepting no parameters. The function +/// #driver_device::empty_init is supplied for systems that don't need +/// to perform additional tasks. +/// \param MONITOR Screen orientation flags applied to all screens in +/// the system, after the individual screens' orientation flags are +/// applied. Usually one #ROT0, #ROT90, #ROT180 or #ROT270. +/// \param COMPANY Name of the developer or distributor of the system. +/// Must be a string. +/// \param FULLNAME Display name for the system. Must be a string, and +/// must be globally unique across systems and devices. +/// \param FLAGS Bitwise combination of emulation status flags for the +/// system, in addition to flags supplied by the system device class +/// (see #device_t::unemulated_features and +/// #device_t::imperfect_features). It is advisable to supply +/// unemulated and imperfectly emulated feature flags that apply to +/// all systems implemented using the class in the class itself to +/// avoid repetition. +/// \param LAYOUT An #internal_layout structure providing additional +/// internal artwork for the system. +/// \sa GAME SYST +#define GAMEL(YEAR, NAME, PARENT, MACHINE, INPUT, CLASS, INIT, MONITOR, COMPANY, FULLNAME, FLAGS, LAYOUT) \ +GAME_DRIVER_TRAITS(NAME, FULLNAME) \ extern game_driver const GAME_NAME(NAME) \ { \ GAME_DRIVER_TYPE(NAME, CLASS, FLAGS), \ @@ -235,30 +330,85 @@ extern game_driver const GAME_NAME(NAME) \ INPUT_PORTS_NAME(INPUT), \ [] (device_t &owner) { downcast<CLASS &>(owner).INIT(); }, \ ROM_NAME(NAME), \ - #COMPAT, \ nullptr, \ - machine_flags::type(u32(ROT0 | (FLAGS) | MACHINE_TYPE_COMPUTER)), \ + &LAYOUT, \ + machine_flags::type(u32((MONITOR) | (FLAGS))), \ #NAME \ }; -// standard system definition macro -#define SYST(YEAR,NAME,PARENT,COMPAT,MACHINE,INPUT,CLASS,INIT,COMPANY,FULLNAME,FLAGS) \ -GAME_DRIVER_TRAITS(NAME,FULLNAME) \ -extern game_driver const GAME_NAME(NAME) \ -{ \ - GAME_DRIVER_TYPE(NAME, CLASS, FLAGS), \ - #PARENT, \ - #YEAR, \ - COMPANY, \ - [] (machine_config &config, device_t &owner) { downcast<CLASS &>(owner).MACHINE(config); }, \ - INPUT_PORTS_NAME(INPUT), \ - [] (device_t &owner) { downcast<CLASS &>(owner).INIT(); }, \ - ROM_NAME(NAME), \ - #COMPAT, \ - nullptr, \ - machine_flags::type(u32(ROT0 | (FLAGS) | MACHINE_TYPE_OTHER)), \ - #NAME \ -}; +/// \brief Define a system with software compatibility grouping +/// +/// Equivalent to the #GAME macro, but allows software-compatible +/// systems to be grouped. +/// +/// Creates an appropriately named and populated #game_driver structure +/// describing the system. +/// \param YEAR The year that the system was first made available. Must +/// be a token containing only the digits zero to nine, question mark +/// and plus sign. +/// \param NAME The short name of the system, used for identification, +/// and in filesystem paths for assets and data. Must be a token no +/// longer than sixteen characters, containing only ASCII lowercase +/// letters, digits and underscores. Must be globally unique across +/// systems and devices. +/// \param PARENT Short name of the parent or BIOS system if applicable, +/// or a single digit zero otherwise. +/// \param COMPAT Short name of a system that this system is compatible +/// with if applicable, or a single digit zero otherwise. +/// \param MACHINE Function used to buid machine configuration for the +/// system. Must be a public member function of the system device +/// class (\p CLASS argument), returning void and taking a reference +/// to a #machine_config object as a parameter. +/// \param INPUT Input port definitions for the root device of the +/// system, usually defined using #INPUT_PORTS_START and associated +/// macros. +/// \param CLASS Class to instantiate as the root device of the system. +/// Must be an implementation of #driver_device. +/// \param INIT Initialisation function called after all child devices +/// have started, but before the driver start functions are called. +/// Often used for tasks like decrypting ROMs. Must be a public +/// member function of the system device class (\p CLASS argument), +/// returning void and accepting no parameters. The function +/// #driver_device::empty_init is supplied for systems that don't need +/// to perform additional tasks. +/// \param COMPANY Name of the developer or distributor of the system. +/// Must be a string. +/// \param FULLNAME Display name for the system. Must be a string, and +/// must be globally unique across systems and devices. +/// \param FLAGS Bitwise combination of emulation status flags for the +/// system, in addition to flags supplied by the system device class +/// (see #device_t::unemulated_features and +/// #device_t::imperfect_features). It is advisable to supply +/// unemulated and imperfectly emulated feature flags that apply to +/// all systems implemented using the class in the class itself to +/// avoid repetition. Screen orientation flags may be included here. +/// \sa GAME GAMEL +#define SYST(YEAR, NAME, PARENT, COMPAT, MACHINE, INPUT, CLASS, INIT, COMPANY, FULLNAME, FLAGS) \ + GAME_DRIVER_TRAITS(NAME, FULLNAME) \ + extern game_driver const GAME_NAME(NAME) \ + { \ + GAME_DRIVER_TYPE(NAME, CLASS, FLAGS), \ + #PARENT, \ + #YEAR, \ + COMPANY, \ + [] (machine_config &config, device_t &owner) { downcast<CLASS &>(owner).MACHINE(config); }, \ + INPUT_PORTS_NAME(INPUT), \ + [] (device_t &owner) { downcast<CLASS &>(owner).INIT(); }, \ + ROM_NAME(NAME), \ + #COMPAT, \ + nullptr, \ + machine_flags::type(u32(ROT0 | (FLAGS))), \ + #NAME \ + }; + + +#define CONS(YEAR, NAME, PARENT, COMPAT, MACHINE, INPUT, CLASS, INIT, COMPANY, FULLNAME, FLAGS) \ + SYST(YEAR, NAME, PARENT, COMPAT, MACHINE, INPUT, CLASS, INIT, COMPANY, FULLNAME, FLAGS) + +#define COMP(YEAR, NAME, PARENT, COMPAT, MACHINE, INPUT, CLASS, INIT, COMPANY, FULLNAME, FLAGS) \ + SYST(YEAR, NAME, PARENT, COMPAT, MACHINE, INPUT, CLASS, INIT, COMPANY, FULLNAME, FLAGS) + +/// \} #endif // MAME_EMU_GAMEDRV_H diff --git a/src/emu/hashfile.cpp b/src/emu/hashfile.cpp index 6ded9a0e397..8436ad339fb 100644 --- a/src/emu/hashfile.cpp +++ b/src/emu/hashfile.cpp @@ -2,7 +2,7 @@ // copyright-holders:Nathan Woods /********************************************************************* - hashfile.c + hashfile.cpp Code for parsing hash info (*.hsi) files @@ -10,12 +10,16 @@ #include "emu.h" #include "hashfile.h" -#include "pool.h" + +#include "drivenum.h" #include "emuopts.h" +#include "fileio.h" + #include "hash.h" -#include "drivenum.h" + #include <pugixml.hpp> + /*------------------------------------------------- hashfile_lookup -------------------------------------------------*/ @@ -24,10 +28,8 @@ static bool read_hash_config(const char *hash_path, const util::hash_collection { /* open a file */ emu_file file(hash_path, OPEN_FLAG_READ); - if (file.open(sysname, ".hsi") != osd_file::error::NONE) - { + if (file.open(std::string(sysname) + ".hsi")) return false; - } pugi::xml_document doc; diff --git a/src/emu/http.cpp b/src/emu/http.cpp index 3a28fa270c0..2a30a13dba3 100644 --- a/src/emu/http.cpp +++ b/src/emu/http.cpp @@ -2,17 +2,22 @@ // copyright-holders:Miodrag Milanovic /*************************************************************************** -http.cpp + http.cpp -HTTP server handling + HTTP server handling ***************************************************************************/ + +#include "emu.h" +#include "http.h" +#include "server_http.hpp" +#include "server_ws.hpp" + #ifdef __sun #define ASIO_DISABLE_DEV_POLL #define ASIO_HAS_EPOLL #endif -#include "emu.h" #include "server_ws_impl.hpp" #include "server_http_impl.hpp" #include <fstream> @@ -126,7 +131,7 @@ public: std::size_t m_path_end; std::size_t m_query_end; - http_request_impl(std::shared_ptr<webpp::Request> request) : m_request(request) { + http_request_impl(std::shared_ptr<webpp::Request> request) : m_request(std::move(request)) { std::size_t len = m_request->path.length(); m_fragment = m_request->path.find('#'); @@ -139,28 +144,28 @@ public: virtual ~http_request_impl() = default; /** Retrieves the requested resource. */ - virtual const std::string get_resource() { + virtual const std::string get_resource() override { // The entire resource: path, query and fragment. return m_request->path; } /** Returns the path part of the requested resource. */ - virtual const std::string get_path() { + virtual const std::string get_path() override { return m_request->path.substr(0, m_path_end); } /** Returns the query part of the requested resource. */ - virtual const std::string get_query() { + virtual const std::string get_query() override { return m_query == std::string::npos ? "" : m_request->path.substr(m_query, m_query_end); } /** Returns the fragment part of the requested resource. */ - virtual const std::string get_fragment() { + virtual const std::string get_fragment() override { return m_fragment == std::string::npos ? "" : m_request->path.substr(m_fragment); } /** Retrieves a header from the HTTP request. */ - virtual const std::string get_header(const std::string &header_name) { + virtual const std::string get_header(const std::string &header_name) override { auto i = m_request->header.find(header_name); if (i != m_request->header.end()) { return (*i).second; @@ -170,7 +175,7 @@ public: } /** Retrieves a header from the HTTP request. */ - virtual const std::list<std::string> get_headers(const std::string &header_name) { + virtual const std::list<std::string> get_headers(const std::string &header_name) override { std::list<std::string> result; auto range = m_request->header.equal_range(header_name); for (auto i = range.first; i != range.second; i++) { @@ -180,7 +185,7 @@ public: } /** Returns the body that was submitted with the HTTP request. */ - virtual const std::string get_body() { + virtual const std::string get_body() override { // TODO(cbrunschen): What to return here - http_server::Request has a 'content' feld that is never filled in! return ""; } @@ -194,28 +199,28 @@ struct http_response_impl : public http_manager::http_response { std::stringstream m_headers; std::stringstream m_body; - http_response_impl(std::shared_ptr<webpp::Response> response) : m_response(response) { } + http_response_impl(std::shared_ptr<webpp::Response> response) : m_response(std::move(response)) { } virtual ~http_response_impl() = default; /** Sets the HTTP status to be returned to the client. */ - virtual void set_status(int status) { + virtual void set_status(int status) override { m_status = status; } /** Sets the HTTP content type to be returned to the client. */ - virtual void set_content_type(const std::string &content_type) { + virtual void set_content_type(const std::string &content_type) override { m_content_type = content_type; } /** Sets the body to be sent to the client. */ - virtual void set_body(const std::string &body) { + virtual void set_body(const std::string &body) override { m_body.str(""); append_body(body); } /** Appends something to the body to be sent to the client. */ - virtual void append_body(const std::string &body) { + virtual void append_body(const std::string &body) override { m_body << body; } @@ -237,10 +242,10 @@ struct websocket_endpoint_impl : public http_manager::websocket_endpoint { http_manager::websocket_close_handler on_close, http_manager::websocket_error_handler on_error) : m_endpoint(endpoint) { - this->on_open = on_open; - this->on_message = on_message; - this->on_close = on_close; - this->on_error = on_error; + this->on_open = std::move(on_open); + this->on_message = std::move(on_message); + this->on_close = std::move(on_close); + this->on_error = std::move(on_error); } }; @@ -253,7 +258,7 @@ struct websocket_connection_impl : public http_manager::websocket_connection { : m_wsserver(server), m_connection(connection) { } /** Sends a message to the client that is connected on the other end of this Websocket connection. */ - virtual void send_message(const std::string &payload, int opcode) { + virtual void send_message(const std::string &payload, int opcode) override { if (auto connection = m_connection.lock()) { std::shared_ptr<webpp::ws_server::SendStream> message_stream = std::make_shared<webpp::ws_server::SendStream>(); (*message_stream) << payload; @@ -262,7 +267,7 @@ struct websocket_connection_impl : public http_manager::websocket_connection { } /** Closes this open Websocket connection. */ - virtual void close() { + virtual void close() override { if (auto connection = m_connection.lock()) { m_wsserver->send_close(connection, 1000 /* normal close */); } @@ -294,8 +299,8 @@ http_manager::http_manager(bool active, short port, const char *root) } // Determine the file extension. - std::size_t last_slash_pos = path.find_last_of("/"); - std::size_t last_dot_pos = path.find_last_of("."); + std::size_t last_slash_pos = path.find_last_of('/'); + std::size_t last_dot_pos = path.find_last_of('.'); std::string extension; if (last_dot_pos != std::string::npos && last_dot_pos > last_slash_pos) { @@ -329,7 +334,7 @@ http_manager::http_manager(bool active, short port, const char *root) }; m_server->on_upgrade = [this](auto socket, auto request) { - auto connection = std::make_shared<webpp::ws_server::Connection>(socket); + auto connection = std::make_shared<webpp::ws_server::Connection>(*m_io_context, socket); connection->method = std::move(request->method); connection->path = std::move(request->path); connection->http_version = std::move(request->http_version); @@ -420,8 +425,8 @@ bool http_manager::read_file(std::ostream &os, const std::string &path) { std::ostringstream full_path; full_path << m_root << path; util::core_file::ptr f; - osd_file::error e = util::core_file::open(full_path.str(), OPEN_FLAG_READ, f); - if (e == osd_file::error::NONE) + std::error_condition const e = util::core_file::open(full_path.str(), OPEN_FLAG_READ, f); + if (!e) { int c; while ((c = f->getc()) >= 0) @@ -429,7 +434,7 @@ bool http_manager::read_file(std::ostream &os, const std::string &path) { os.put(c); } } - return e == osd_file::error::NONE; + return !e; } void http_manager::serve_document(http_request_ptr request, http_response_ptr response, const std::string &filename) { @@ -544,21 +549,21 @@ http_manager::websocket_endpoint_ptr http_manager::add_endpoint(const std::strin auto endpoint_impl = std::make_shared<websocket_endpoint_impl>(endpoint_ptr, on_open, on_message, on_close, on_error); endpoint.on_open = [&, this, endpoint_impl](std::shared_ptr<webpp::Connection> connection) { - this->on_open(endpoint_impl, connection); + this->on_open(endpoint_impl, std::move(connection)); }; endpoint.on_message = [&, this, endpoint_impl](std::shared_ptr<webpp::Connection> connection, std::shared_ptr<webpp::ws_server::Message> message) { std::string payload = message->string(); int opcode = message->fin_rsv_opcode & 0x0f; - this->on_message(endpoint_impl, connection, payload, opcode); + this->on_message(endpoint_impl, std::move(connection), payload, opcode); }; endpoint.on_close = [&, this, endpoint_impl](std::shared_ptr<webpp::Connection> connection, int status, const std::string& reason) { - this->on_close(endpoint_impl, connection, status, reason); + this->on_close(endpoint_impl, std::move(connection), status, reason); }; endpoint.on_error = [&, this, endpoint_impl](std::shared_ptr<webpp::Connection> connection, const std::error_code& error_code) { - this->on_error(endpoint_impl, connection, error_code); + this->on_error(endpoint_impl, std::move(connection), error_code); }; m_endpoints[path] = endpoint_impl; diff --git a/src/emu/http.h b/src/emu/http.h index 6c845757757..e7c26d7980f 100644 --- a/src/emu/http.h +++ b/src/emu/http.h @@ -2,25 +2,20 @@ // copyright-holders:Miodrag Milanovic /*************************************************************************** -http.cpp + http.h -HTTP server handling + HTTP server handling ***************************************************************************/ #pragma once -#ifndef __EMU_H__ -#error Dont include this file directly; include emu.h instead. -#endif - #ifndef MAME_EMU_HTTP_H #define MAME_EMU_HTTP_H +#include <mutex> #include <thread> -#include <time.h> -#include "server_http.hpp" -#include "server_ws.hpp" +#include <ctime> //************************************************************************** // TYPE DEFINITIONS @@ -32,6 +27,13 @@ namespace asio class io_context; } +namespace webpp +{ + class http_server; + class ws_server; + struct Connection; +} + class http_manager { DISABLE_COPYING(http_manager); diff --git a/src/emu/image.cpp b/src/emu/image.cpp index 3013d03f668..52e553953a7 100644 --- a/src/emu/image.cpp +++ b/src/emu/image.cpp @@ -6,18 +6,25 @@ Core image functions and definitions. - ***************************************************************************/ -#include <ctype.h> - #include "emu.h" -#include "emuopts.h" #include "image.h" + #include "config.h" -#include "xmlfile.h" +#include "drivenum.h" +#include "emuopts.h" +#include "fileio.h" +#include "main.h" #include "softlist.h" +// lib/util +#include "corestr.h" +#include "xmlfile.h" +#include "zippath.h" + +#include <cctype> + //************************************************************************** // IMAGE MANAGER @@ -31,8 +38,11 @@ image_manager::image_manager(running_machine &machine) : m_machine(machine) { // make sure that any required devices have been allocated - for (device_image_interface &image : image_interface_iterator(machine.root_device())) + for (device_image_interface &image : image_interface_enumerator(machine.root_device())) { + // see if region-based chds are available + image.check_preset_images(); + // ignore things not user loadable if (!image.user_loadable()) continue; @@ -44,27 +54,32 @@ image_manager::image_manager(running_machine &machine) if (!startup_image.empty()) { // we do have a startup image specified - load it - image_init_result result = image_init_result::FAIL; + std::pair<std::error_condition, std::string> result(image_error::UNSPECIFIED, std::string()); // try as a softlist if (software_name_parse(startup_image)) + { + osd_printf_verbose("%s: attempting to load software item %s\n", image.device().tag(), startup_image); result = image.load_software(startup_image); + } // failing that, try as an image - if (result != image_init_result::PASS) + if (result.first) + { + osd_printf_verbose("%s: attempting to load media image %s\n", image.device().tag(), startup_image); result = image.load(startup_image); + } // failing that, try creating it (if appropriate) - if (result != image_init_result::PASS && image.support_command_line_image_creation()) + if (result.first && image.support_command_line_image_creation()) + { + osd_printf_verbose("%s: attempting to create media image %s\n", image.device().tag(), startup_image); result = image.create(startup_image); + } // did the image load fail? - if (result != image_init_result::PASS) + if (result.first) { - // retrieve image error message - std::string image_err = std::string(image.error()); - std::string startup_image_name = startup_image; - // unload the bad image image.unload(); @@ -73,16 +88,26 @@ image_manager::image_manager(running_machine &machine) if (machine.options().write_config()) write_config(machine.options(), nullptr, &machine.system()); - fatalerror_exitcode(machine, EMU_ERR_DEVICE, "Device %s load (-%s %s) failed: %s", - image.device().name(), - image.instance_name().c_str(), - startup_image_name.c_str(), - image_err.c_str()); + // retrieve image error message + throw emu_fatalerror(EMU_ERR_DEVICE, + !result.second.empty() + ? "Device %1$s load (-%2$s %3$s) failed: %4$s (%5$s:%6$d %7$s)" + : "Device %1$s load (-%2$s %3$s) failed: %7$s (%5$s:%6$d)", + image.device().name(), + image.instance_name(), + startup_image, + result.second, + result.first.category().name(), + result.first.value(), + result.first.message()); } } } - machine.configuration().config_register("image_directories", config_load_delegate(&image_manager::config_load, this), config_save_delegate(&image_manager::config_save, this)); + machine.configuration().config_register( + "image_directories", + configuration_manager::load_delegate(&image_manager::config_load, this), + configuration_manager::save_delegate(&image_manager::config_save, this)); } //------------------------------------------------- @@ -94,16 +119,16 @@ void image_manager::unload_all() // extract the options options_extract(); - for (device_image_interface &image : image_interface_iterator(machine().root_device())) + for (device_image_interface &image : image_interface_enumerator(machine().root_device())) { // unload this image image.unload(); } } -void image_manager::config_load(config_type cfg_type, util::xml::data_node const *parentnode) +void image_manager::config_load(config_type cfg_type, config_level cfg_level, util::xml::data_node const *parentnode) { - if ((cfg_type == config_type::GAME) && (parentnode != nullptr)) + if ((cfg_type == config_type::SYSTEM) && parentnode) { for (util::xml::data_node const *node = parentnode->get_child("device"); node; node = node->get_next_sibling("device")) { @@ -111,13 +136,13 @@ void image_manager::config_load(config_type cfg_type, util::xml::data_node const if ((dev_instance != nullptr) && (dev_instance[0] != '\0')) { - for (device_image_interface &image : image_interface_iterator(machine().root_device())) + for (device_image_interface &image : image_interface_enumerator(machine().root_device())) { if (!strcmp(dev_instance, image.instance_name().c_str())) { const char *const working_directory = node->get_attribute_string("directory", nullptr); if (working_directory != nullptr) - image.set_working_directory(working_directory); + image.set_working_directory(std::string_view(working_directory)); } } } @@ -132,10 +157,10 @@ void image_manager::config_load(config_type cfg_type, util::xml::data_node const void image_manager::config_save(config_type cfg_type, util::xml::data_node *parentnode) { - /* only care about game-specific data */ - if (cfg_type == config_type::GAME) + // only save system-specific data + if (cfg_type == config_type::SYSTEM) { - for (device_image_interface &image : image_interface_iterator(machine().root_device())) + for (device_image_interface &image : image_interface_enumerator(machine().root_device())) { const char *const dev_instance = image.instance_name().c_str(); @@ -156,24 +181,22 @@ void image_manager::config_save(config_type cfg_type, util::xml::data_node *pare int image_manager::write_config(emu_options &options, const char *filename, const game_driver *gamedrv) { - char buffer[128]; - int retval = 1; - - if (gamedrv != nullptr) + std::string buffer; + if (gamedrv) { - sprintf(buffer, "%s.ini", gamedrv->name); - filename = buffer; + buffer.reserve(strlen(gamedrv->name) + 4); + buffer = gamedrv->name; + buffer += ".ini"; + filename = buffer.c_str(); } emu_file file(options.ini_path(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE); - osd_file::error filerr = file.open(filename); - if (filerr == osd_file::error::NONE) - { - std::string inistring = options.output_ini(); - file.puts(inistring.c_str()); - retval = 0; - } - return retval; + std::error_condition const filerr = file.open(filename); + if (filerr) + return 1; + + file.puts(options.output_ini()); + return 0; } /*------------------------------------------------- @@ -183,7 +206,7 @@ int image_manager::write_config(emu_options &options, const char *filename, cons void image_manager::options_extract() { - for (device_image_interface &image : image_interface_iterator(machine().root_device())) + for (device_image_interface &image : image_interface_enumerator(machine().root_device())) { // There are two scenarios where we want to extract the option: // @@ -231,24 +254,107 @@ void image_manager::options_extract() void image_manager::postdevice_init() { /* make sure that any required devices have been allocated */ - for (device_image_interface &image : image_interface_iterator(machine().root_device())) + for (device_image_interface &image : image_interface_enumerator(machine().root_device())) { - image_init_result result = image.finish_load(); + auto [result, image_err] = image.finish_load(); /* did the image load fail? */ - if (result != image_init_result::PASS) + if (result) { /* retrieve image error message */ - std::string image_err = std::string(image.error()); + if (image_err.empty()) + image_err = result.message(); /* unload all images */ unload_all(); - fatalerror_exitcode(machine(), EMU_ERR_DEVICE, "Device %s load failed: %s", - image.device().name(), - image_err.c_str()); + throw emu_fatalerror(EMU_ERR_DEVICE, "Device %s load failed: %s", + image.device().name(), + image_err); } } /* add a callback for when we shut down */ machine().add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(&image_manager::unload_all, this)); } + + +//************************************************************************** +// WORKING DIRECTORIES +//************************************************************************** + +//------------------------------------------------- +// try_change_working_directory - tries to change +// the working directory, but only if the directory +// actually exists +//------------------------------------------------- + +bool image_manager::try_change_working_directory(std::string &working_directory, const std::string &subdir) +{ + bool success = false; + + auto directory = osd::directory::open(working_directory); + if (directory) + { + const osd::directory::entry *entry; + bool done = false; + while (!done && (entry = directory->read()) != nullptr) + { + if (!core_stricmp(subdir.c_str(), entry->name)) + { + done = true; + success = entry->type == osd::directory::entry::entry_type::DIR; + } + } + + directory.reset(); + } + + // did we successfully identify the directory? + if (success) + working_directory = util::zippath_combine(working_directory, subdir); + + return success; +} + + +//------------------------------------------------- +// setup_working_directory - sets up the working +// directory according to a few defaults +//------------------------------------------------- + +std::string image_manager::setup_working_directory() +{ + bool success = false; + // get user-specified directory and make sure it exists + std::string working_directory = machine().options().sw_path(); + // if multipath, get first + size_t i = working_directory.find_first_of(';'); + if (i != std::string::npos) + working_directory.resize(i); + // validate directory + if (!working_directory.empty()) + if (osd::directory::open(working_directory)) + success = true; + + // if not exist, use previous method + if (!success) + { + // first set up the working directory to be the starting directory + osd_get_full_path(working_directory, "."); + // now try browsing down to "software" + if (try_change_working_directory(working_directory, "software")) + success = true; + } + + if (success) + { + // now down to a directory for this computer + int gamedrv = driver_list::find(machine().system()); + while(gamedrv != -1 && !try_change_working_directory(working_directory, driver_list::driver(gamedrv).name)) + { + gamedrv = driver_list::compatible_with(gamedrv); + } + } + + return working_directory; +} diff --git a/src/emu/image.h b/src/emu/image.h index b0cc5f60852..95dca8a92b9 100644 --- a/src/emu/image.h +++ b/src/emu/image.h @@ -26,13 +26,18 @@ public: // getters running_machine &machine() const { return m_machine; } + + std::string setup_working_directory(); + private: - void config_load(config_type cfg_type, util::xml::data_node const *parentnode); + void config_load(config_type cfg_type, config_level cfg_level, util::xml::data_node const *parentnode); void config_save(config_type cfg_type, util::xml::data_node *parentnode); void options_extract(); int write_config(emu_options &options, const char *filename, const game_driver *gamedrv); + bool try_change_working_directory(std::string &working_directory, const std::string &subdir); + // internal state running_machine & m_machine; // reference to our machine }; diff --git a/src/emu/inpttype.h b/src/emu/inpttype.h index 9cf56fd18ee..92d4cbd2be1 100644 --- a/src/emu/inpttype.h +++ b/src/emu/inpttype.h @@ -2,960 +2,355 @@ // copyright-holders:Aaron Giles /*************************************************************************** - inpttype.h + inpttype.ipp - Array of core-defined input types and default mappings. + Core-defined input types. ***************************************************************************/ - - -/*************************************************************************** - BUILT-IN CORE MAPPINGS -***************************************************************************/ - -#define INPUT_PORT_DIGITAL_TYPE(_player,_group,_type,_name,_seq) \ - typelist.append(*global_alloc(input_type_entry(IPT_##_type, IPG_##_group, (_player == 0) ? _player : (_player) - 1, (_player == 0) ? #_type : ("P" #_player "_" #_type), _name, _seq))); - -#define INPUT_PORT_ANALOG_TYPE(_player,_group,_type,_name,_seq,_decseq,_incseq) \ - typelist.append(*global_alloc(input_type_entry(IPT_##_type, IPG_##_group, (_player == 0) ? _player : (_player) - 1, (_player == 0) ? #_type : ("P" #_player "_" #_type), _name, _seq, _decseq, _incseq))); - -/* These input port macros expand to a great deal of code and break compilers */ -#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ > 4 || (__GNUC_MINOR__ == 4 && __GNUC_PATCHLEVEL__ >= 4)))) -#if not(defined(__arm__) || defined(__ARMEL__)) -#pragma GCC push_options -#pragma GCC optimize ("O1") -#endif -#elif defined(_MSC_VER) && !defined(__clang__) -#pragma optimize("", off) -#endif - -/* split up into small functions to be nicer on optimizers */ - -inline void construct_core_types_P1(simple_list<input_type_entry> &typelist) -{ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICK_UP, "P1 Up", input_seq(KEYCODE_UP, input_seq::or_code, JOYCODE_Y_UP_SWITCH_INDEXED(0)) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICK_DOWN, "P1 Down", input_seq(KEYCODE_DOWN, input_seq::or_code, JOYCODE_Y_DOWN_SWITCH_INDEXED(0)) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICK_LEFT, "P1 Left", input_seq(KEYCODE_LEFT, input_seq::or_code, JOYCODE_X_LEFT_SWITCH_INDEXED(0)) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICK_RIGHT, "P1 Right", input_seq(KEYCODE_RIGHT, input_seq::or_code, JOYCODE_X_RIGHT_SWITCH_INDEXED(0)) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICKRIGHT_UP, "P1 Right Stick/Up", input_seq(KEYCODE_I, input_seq::or_code, JOYCODE_BUTTON2_INDEXED(0)) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICKRIGHT_DOWN, "P1 Right Stick/Down", input_seq(KEYCODE_K, input_seq::or_code, JOYCODE_BUTTON3_INDEXED(0)) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICKRIGHT_LEFT, "P1 Right Stick/Left", input_seq(KEYCODE_J, input_seq::or_code, JOYCODE_BUTTON1_INDEXED(0)) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICKRIGHT_RIGHT, "P1 Right Stick/Right", input_seq(KEYCODE_L, input_seq::or_code, JOYCODE_BUTTON4_INDEXED(0)) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICKLEFT_UP, "P1 Left Stick/Up", input_seq(KEYCODE_E, input_seq::or_code, JOYCODE_Y_UP_SWITCH_INDEXED(0)) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICKLEFT_DOWN, "P1 Left Stick/Down", input_seq(KEYCODE_D, input_seq::or_code, JOYCODE_Y_DOWN_SWITCH_INDEXED(0)) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICKLEFT_LEFT, "P1 Left Stick/Left", input_seq(KEYCODE_S, input_seq::or_code, JOYCODE_X_LEFT_SWITCH_INDEXED(0)) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICKLEFT_RIGHT, "P1 Left Stick/Right", input_seq(KEYCODE_F, input_seq::or_code, JOYCODE_X_RIGHT_SWITCH_INDEXED(0)) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON1, "P1 Button 1", input_seq(KEYCODE_LCONTROL, input_seq::or_code, JOYCODE_BUTTON1_INDEXED(0), input_seq::or_code, MOUSECODE_BUTTON1_INDEXED(0), input_seq::or_code, GUNCODE_BUTTON1_INDEXED(0)) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON2, "P1 Button 2", input_seq(KEYCODE_LALT, input_seq::or_code, JOYCODE_BUTTON2_INDEXED(0), input_seq::or_code, MOUSECODE_BUTTON3_INDEXED(0), input_seq::or_code, GUNCODE_BUTTON2_INDEXED(0)) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON3, "P1 Button 3", input_seq(KEYCODE_SPACE, input_seq::or_code, JOYCODE_BUTTON3_INDEXED(0), input_seq::or_code, MOUSECODE_BUTTON2_INDEXED(0)) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON4, "P1 Button 4", input_seq(KEYCODE_LSHIFT, input_seq::or_code, JOYCODE_BUTTON4_INDEXED(0)) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON5, "P1 Button 5", input_seq(KEYCODE_Z, input_seq::or_code, JOYCODE_BUTTON5_INDEXED(0)) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON6, "P1 Button 6", input_seq(KEYCODE_X, input_seq::or_code, JOYCODE_BUTTON6_INDEXED(0)) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON7, "P1 Button 7", input_seq(KEYCODE_C, input_seq::or_code, JOYCODE_BUTTON7_INDEXED(0)) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON8, "P1 Button 8", input_seq(KEYCODE_V, input_seq::or_code, JOYCODE_BUTTON8_INDEXED(0)) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON9, "P1 Button 9", input_seq(KEYCODE_B, input_seq::or_code, JOYCODE_BUTTON9_INDEXED(0)) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON10, "P1 Button 10", input_seq(KEYCODE_N, input_seq::or_code, JOYCODE_BUTTON10_INDEXED(0)) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON11, "P1 Button 11", input_seq(KEYCODE_M, input_seq::or_code, JOYCODE_BUTTON11_INDEXED(0)) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON12, "P1 Button 12", input_seq(KEYCODE_COMMA, input_seq::or_code, JOYCODE_BUTTON12_INDEXED(0)) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON13, "P1 Button 13", input_seq(KEYCODE_STOP, input_seq::or_code, JOYCODE_BUTTON13_INDEXED(0)) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON14, "P1 Button 14", input_seq(KEYCODE_SLASH, input_seq::or_code, JOYCODE_BUTTON14_INDEXED(0)) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON15, "P1 Button 15", input_seq(KEYCODE_RSHIFT, input_seq::or_code, JOYCODE_BUTTON15_INDEXED(0)) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON16, "P1 Button 16", input_seq(JOYCODE_BUTTON16_INDEXED(0)) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, START, "P1 Start", input_seq(KEYCODE_1, input_seq::or_code, JOYCODE_START_INDEXED(0)) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, SELECT, "P1 Select", input_seq(KEYCODE_5, input_seq::or_code, JOYCODE_SELECT_INDEXED(0)) ) -} - -inline void construct_core_types_P1_mahjong(simple_list<input_type_entry> &typelist) -{ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_A, "P1 Mahjong A", input_seq(KEYCODE_A) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_B, "P1 Mahjong B", input_seq(KEYCODE_B) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_C, "P1 Mahjong C", input_seq(KEYCODE_C) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_D, "P1 Mahjong D", input_seq(KEYCODE_D) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_E, "P1 Mahjong E", input_seq(KEYCODE_E) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_F, "P1 Mahjong F", input_seq(KEYCODE_F) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_G, "P1 Mahjong G", input_seq(KEYCODE_G) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_H, "P1 Mahjong H", input_seq(KEYCODE_H) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_I, "P1 Mahjong I", input_seq(KEYCODE_I) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_J, "P1 Mahjong J", input_seq(KEYCODE_J) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_K, "P1 Mahjong K", input_seq(KEYCODE_K) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_L, "P1 Mahjong L", input_seq(KEYCODE_L) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_M, "P1 Mahjong M", input_seq(KEYCODE_M) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_N, "P1 Mahjong N", input_seq(KEYCODE_N) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_O, "P1 Mahjong O", input_seq(KEYCODE_O) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_P, "P1 Mahjong P", input_seq(KEYCODE_COLON) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_Q, "P1 Mahjong Q", input_seq(KEYCODE_Q) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_KAN, "P1 Mahjong Kan", input_seq(KEYCODE_LCONTROL) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_PON, "P1 Mahjong Pon", input_seq(KEYCODE_LALT) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_CHI, "P1 Mahjong Chi", input_seq(KEYCODE_SPACE) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_REACH, "P1 Mahjong Reach", input_seq(KEYCODE_LSHIFT) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_RON, "P1 Mahjong Ron", input_seq(KEYCODE_Z) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_BET, "P1 Mahjong Bet", input_seq(KEYCODE_3) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_LAST_CHANCE, "P1 Mahjong Last Chance", input_seq(KEYCODE_RALT) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_SCORE, "P1 Mahjong Score", input_seq(KEYCODE_RCONTROL) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_DOUBLE_UP, "P1 Mahjong Double Up", input_seq(KEYCODE_RSHIFT) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_FLIP_FLOP, "P1 Mahjong Flip Flop", input_seq(KEYCODE_Y) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_BIG, "P1 Mahjong Big", input_seq(KEYCODE_ENTER) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_SMALL, "P1 Mahjong Small", input_seq(KEYCODE_BACKSPACE) ) -} - -inline void construct_core_types_P1_hanafuda(simple_list<input_type_entry> &typelist) -{ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, HANAFUDA_A, "P1 Hanafuda A/1", input_seq(KEYCODE_A) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, HANAFUDA_B, "P1 Hanafuda B/2", input_seq(KEYCODE_B) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, HANAFUDA_C, "P1 Hanafuda C/3", input_seq(KEYCODE_C) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, HANAFUDA_D, "P1 Hanafuda D/4", input_seq(KEYCODE_D) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, HANAFUDA_E, "P1 Hanafuda E/5", input_seq(KEYCODE_E) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, HANAFUDA_F, "P1 Hanafuda F/6", input_seq(KEYCODE_F) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, HANAFUDA_G, "P1 Hanafuda G/7", input_seq(KEYCODE_G) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, HANAFUDA_H, "P1 Hanafuda H/8", input_seq(KEYCODE_H) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, HANAFUDA_YES, "P1 Hanafuda Yes", input_seq(KEYCODE_M) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, HANAFUDA_NO, "P1 Hanafuda No", input_seq(KEYCODE_N) ) -} - -inline void construct_core_types_gamble(simple_list<input_type_entry> &typelist) -{ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, GAMBLE_HIGH, "High", input_seq(KEYCODE_A) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, GAMBLE_LOW, "Low", input_seq(KEYCODE_S) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, GAMBLE_HALF, "Half Gamble", input_seq(KEYCODE_D) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, GAMBLE_DEAL, "Deal", input_seq(KEYCODE_2) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, GAMBLE_D_UP, "Double Up", input_seq(KEYCODE_3) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, GAMBLE_TAKE, "Take", input_seq(KEYCODE_4) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, GAMBLE_STAND, "Stand", input_seq(KEYCODE_L) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, GAMBLE_BET, "Bet", input_seq(KEYCODE_M) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, GAMBLE_KEYIN, "Key In", input_seq(KEYCODE_Q) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, GAMBLE_KEYOUT, "Key Out", input_seq(KEYCODE_W) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, GAMBLE_PAYOUT, "Payout", input_seq(KEYCODE_I) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, GAMBLE_DOOR, "Door", input_seq(KEYCODE_O) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, GAMBLE_SERVICE, "Service", input_seq(KEYCODE_9) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, GAMBLE_BOOK, "Book-Keeping", input_seq(KEYCODE_0) ) -} - -inline void construct_core_types_poker(simple_list<input_type_entry> &typelist) -{ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, POKER_HOLD1, "Hold 1", input_seq(KEYCODE_Z) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, POKER_HOLD2, "Hold 2", input_seq(KEYCODE_X) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, POKER_HOLD3, "Hold 3", input_seq(KEYCODE_C) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, POKER_HOLD4, "Hold 4", input_seq(KEYCODE_V) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, POKER_HOLD5, "Hold 5", input_seq(KEYCODE_B) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, POKER_CANCEL, "Cancel", input_seq(KEYCODE_N) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, POKER_BET, "Bet", input_seq(KEYCODE_1) ) -} - -inline void construct_core_types_slot(simple_list<input_type_entry> &typelist) -{ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, SLOT_STOP1, "Stop Reel 1", input_seq(KEYCODE_X) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, SLOT_STOP2, "Stop Reel 2", input_seq(KEYCODE_C) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, SLOT_STOP3, "Stop Reel 3", input_seq(KEYCODE_V) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, SLOT_STOP4, "Stop Reel 4", input_seq(KEYCODE_B) ) - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, SLOT_STOP_ALL, "Stop All Reels", input_seq(KEYCODE_Z) ) -} - -inline void construct_core_types_P2(simple_list<input_type_entry> &typelist) -{ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICK_UP, "P2 Up", input_seq(KEYCODE_R, input_seq::or_code, JOYCODE_Y_UP_SWITCH_INDEXED(1)) ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICK_DOWN, "P2 Down", input_seq(KEYCODE_F, input_seq::or_code, JOYCODE_Y_DOWN_SWITCH_INDEXED(1)) ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICK_LEFT, "P2 Left", input_seq(KEYCODE_D, input_seq::or_code, JOYCODE_X_LEFT_SWITCH_INDEXED(1)) ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICK_RIGHT, "P2 Right", input_seq(KEYCODE_G, input_seq::or_code, JOYCODE_X_RIGHT_SWITCH_INDEXED(1)) ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICKRIGHT_UP, "P2 Right Stick/Up", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICKRIGHT_DOWN, "P2 Right Stick/Down", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICKRIGHT_LEFT, "P2 Right Stick/Left", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICKRIGHT_RIGHT, "P2 Right Stick/Right", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICKLEFT_UP, "P2 Left Stick/Up", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICKLEFT_DOWN, "P2 Left Stick/Down", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICKLEFT_LEFT, "P2 Left Stick/Left", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICKLEFT_RIGHT, "P2 Left Stick/Right", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON1, "P2 Button 1", input_seq(KEYCODE_A, input_seq::or_code, JOYCODE_BUTTON1_INDEXED(1), input_seq::or_code, MOUSECODE_BUTTON1_INDEXED(1), input_seq::or_code, GUNCODE_BUTTON1_INDEXED(1)) ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON2, "P2 Button 2", input_seq(KEYCODE_S, input_seq::or_code, JOYCODE_BUTTON2_INDEXED(1), input_seq::or_code, MOUSECODE_BUTTON3_INDEXED(1), input_seq::or_code, GUNCODE_BUTTON2_INDEXED(1)) ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON3, "P2 Button 3", input_seq(KEYCODE_Q, input_seq::or_code, JOYCODE_BUTTON3_INDEXED(1), input_seq::or_code, MOUSECODE_BUTTON2_INDEXED(1)) ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON4, "P2 Button 4", input_seq(KEYCODE_W, input_seq::or_code, JOYCODE_BUTTON4_INDEXED(1)) ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON5, "P2 Button 5", input_seq(KEYCODE_E, input_seq::or_code, JOYCODE_BUTTON5_INDEXED(1)) ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON6, "P2 Button 6", input_seq(JOYCODE_BUTTON6_INDEXED(1)) ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON7, "P2 Button 7", input_seq(JOYCODE_BUTTON7_INDEXED(1)) ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON8, "P2 Button 8", input_seq(JOYCODE_BUTTON8_INDEXED(1)) ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON9, "P2 Button 9", input_seq(JOYCODE_BUTTON9_INDEXED(1)) ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON10, "P2 Button 10", input_seq(JOYCODE_BUTTON10_INDEXED(1)) ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON11, "P2 Button 11", input_seq(JOYCODE_BUTTON11_INDEXED(1)) ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON12, "P2 Button 12", input_seq(JOYCODE_BUTTON12_INDEXED(1)) ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON13, "P2 Button 13", input_seq(JOYCODE_BUTTON13_INDEXED(1)) ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON14, "P2 Button 14", input_seq(JOYCODE_BUTTON14_INDEXED(1)) ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON15, "P2 Button 15", input_seq(JOYCODE_BUTTON15_INDEXED(1)) ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON16, "P2 Button 16", input_seq(JOYCODE_BUTTON16_INDEXED(1)) ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, START, "P2 Start", input_seq(KEYCODE_2, input_seq::or_code, JOYCODE_START_INDEXED(1)) ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, SELECT, "P2 Select", input_seq(KEYCODE_6, input_seq::or_code, JOYCODE_SELECT_INDEXED(1)) ) -} - -inline void construct_core_types_P2_mahjong(simple_list<input_type_entry> &typelist) -{ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_A, "P2 Mahjong A", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_B, "P2 Mahjong B", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_C, "P2 Mahjong C", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_D, "P2 Mahjong D", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_E, "P2 Mahjong E", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_F, "P2 Mahjong F", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_G, "P2 Mahjong G", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_H, "P2 Mahjong H", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_I, "P2 Mahjong I", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_J, "P2 Mahjong J", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_K, "P2 Mahjong K", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_L, "P2 Mahjong L", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_M, "P2 Mahjong M", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_N, "P2 Mahjong N", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_O, "P2 Mahjong O", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_P, "P2 Mahjong P", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_Q, "P2 Mahjong Q", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_KAN, "P2 Mahjong Kan", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_PON, "P2 Mahjong Pon", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_CHI, "P2 Mahjong Chi", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_REACH, "P2 Mahjong Reach", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_RON, "P2 Mahjong Ron", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_BET, "P2 Mahjong Bet", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_LAST_CHANCE, "P2 Mahjong Last Chance", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_SCORE, "P2 Mahjong Score", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_DOUBLE_UP, "P2 Mahjong Double Up", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_FLIP_FLOP, "P2 Mahjong Flip Flop", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_BIG, "P2 Mahjong Big", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_SMALL, "P2 Mahjong Small", input_seq() ) -} - -inline void construct_core_types_P2_hanafuda(simple_list<input_type_entry> &typelist) -{ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, HANAFUDA_A, "P2 Hanafuda A/1", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, HANAFUDA_B, "P2 Hanafuda B/2", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, HANAFUDA_C, "P2 Hanafuda C/3", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, HANAFUDA_D, "P2 Hanafuda D/4", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, HANAFUDA_E, "P2 Hanafuda E/5", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, HANAFUDA_F, "P2 Hanafuda F/6", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, HANAFUDA_G, "P2 Hanafuda G/7", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, HANAFUDA_H, "P2 Hanafuda H/8", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, HANAFUDA_YES, "P2 Hanafuda Yes", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, HANAFUDA_NO, "P2 Hanafuda No", input_seq() ) -} - -inline void construct_core_types_P3(simple_list<input_type_entry> &typelist) -{ - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICK_UP, "P3 Up", input_seq(KEYCODE_I, input_seq::or_code, JOYCODE_Y_UP_SWITCH_INDEXED(2)) ) - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICK_DOWN, "P3 Down", input_seq(KEYCODE_K, input_seq::or_code, JOYCODE_Y_DOWN_SWITCH_INDEXED(2)) ) - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICK_LEFT, "P3 Left", input_seq(KEYCODE_J, input_seq::or_code, JOYCODE_X_LEFT_SWITCH_INDEXED(2)) ) - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICK_RIGHT, "P3 Right", input_seq(KEYCODE_L, input_seq::or_code, JOYCODE_X_RIGHT_SWITCH_INDEXED(2)) ) - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICKRIGHT_UP, "P3 Right Stick/Up", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICKRIGHT_DOWN, "P3 Right Stick/Down", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICKRIGHT_LEFT, "P3 Right Stick/Left", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICKRIGHT_RIGHT, "P3 Right Stick/Right", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICKLEFT_UP, "P3 Left Stick/Up", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICKLEFT_DOWN, "P3 Left Stick/Down", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICKLEFT_LEFT, "P3 Left Stick/Left", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICKLEFT_RIGHT, "P3 Left Stick/Right", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON1, "P3 Button 1", input_seq(KEYCODE_RCONTROL, input_seq::or_code, JOYCODE_BUTTON1_INDEXED(2), input_seq::or_code, GUNCODE_BUTTON1_INDEXED(2)) ) - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON2, "P3 Button 2", input_seq(KEYCODE_RSHIFT, input_seq::or_code, JOYCODE_BUTTON2_INDEXED(2), input_seq::or_code, GUNCODE_BUTTON2_INDEXED(2)) ) - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON3, "P3 Button 3", input_seq(KEYCODE_ENTER, input_seq::or_code, JOYCODE_BUTTON3_INDEXED(2)) ) - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON4, "P3 Button 4", input_seq(JOYCODE_BUTTON4_INDEXED(2)) ) - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON5, "P3 Button 5", input_seq(JOYCODE_BUTTON5_INDEXED(2)) ) - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON6, "P3 Button 6", input_seq(JOYCODE_BUTTON6_INDEXED(2)) ) - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON7, "P3 Button 7", input_seq(JOYCODE_BUTTON7_INDEXED(2)) ) - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON8, "P3 Button 8", input_seq(JOYCODE_BUTTON8_INDEXED(2)) ) - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON9, "P3 Button 9", input_seq(JOYCODE_BUTTON9_INDEXED(2)) ) - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON10, "P3 Button 10", input_seq(JOYCODE_BUTTON10_INDEXED(2)) ) - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON11, "P3 Button 11", input_seq(JOYCODE_BUTTON11_INDEXED(2)) ) - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON12, "P3 Button 12", input_seq(JOYCODE_BUTTON12_INDEXED(2)) ) - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON13, "P3 Button 13", input_seq(JOYCODE_BUTTON13_INDEXED(2)) ) - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON14, "P3 Button 14", input_seq(JOYCODE_BUTTON14_INDEXED(2)) ) - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON15, "P3 Button 15", input_seq(JOYCODE_BUTTON15_INDEXED(2)) ) - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON16, "P3 Button 16", input_seq(JOYCODE_BUTTON16_INDEXED(2)) ) - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, START, "P3 Start", input_seq(KEYCODE_3, input_seq::or_code, JOYCODE_START_INDEXED(2)) ) - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, SELECT, "P3 Select", input_seq(KEYCODE_7, input_seq::or_code, JOYCODE_SELECT_INDEXED(2)) ) -} - -inline void construct_core_types_P4(simple_list<input_type_entry> &typelist) -{ - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICK_UP, "P4 Up", input_seq(KEYCODE_8_PAD, input_seq::or_code, JOYCODE_Y_UP_SWITCH_INDEXED(3)) ) - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICK_DOWN, "P4 Down", input_seq(KEYCODE_2_PAD, input_seq::or_code, JOYCODE_Y_DOWN_SWITCH_INDEXED(3)) ) - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICK_LEFT, "P4 Left", input_seq(KEYCODE_4_PAD, input_seq::or_code, JOYCODE_X_LEFT_SWITCH_INDEXED(3)) ) - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICK_RIGHT, "P4 Right", input_seq(KEYCODE_6_PAD, input_seq::or_code, JOYCODE_X_RIGHT_SWITCH_INDEXED(3)) ) - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICKRIGHT_UP, "P4 Right Stick/Up", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICKRIGHT_DOWN, "P4 Right Stick/Down", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICKRIGHT_LEFT, "P4 Right Stick/Left", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICKRIGHT_RIGHT, "P4 Right Stick/Right", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICKLEFT_UP, "P4 Left Stick/Up", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICKLEFT_DOWN, "P4 Left Stick/Down", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICKLEFT_LEFT, "P4 Left Stick/Left", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICKLEFT_RIGHT, "P4 Left Stick/Right", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON1, "P4 Button 1", input_seq(KEYCODE_0_PAD, input_seq::or_code, JOYCODE_BUTTON1_INDEXED(3)) ) - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON2, "P4 Button 2", input_seq(KEYCODE_DEL_PAD, input_seq::or_code, JOYCODE_BUTTON2_INDEXED(3)) ) - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON3, "P4 Button 3", input_seq(KEYCODE_ENTER_PAD, input_seq::or_code, JOYCODE_BUTTON3_INDEXED(3)) ) - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON4, "P4 Button 4", input_seq(JOYCODE_BUTTON4_INDEXED(3)) ) - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON5, "P4 Button 5", input_seq(JOYCODE_BUTTON5_INDEXED(3)) ) - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON6, "P4 Button 6", input_seq(JOYCODE_BUTTON6_INDEXED(3)) ) - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON7, "P4 Button 7", input_seq(JOYCODE_BUTTON7_INDEXED(3)) ) - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON8, "P4 Button 8", input_seq(JOYCODE_BUTTON8_INDEXED(3)) ) - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON9, "P4 Button 9", input_seq(JOYCODE_BUTTON9_INDEXED(3)) ) - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON10, "P4 Button 10", input_seq(JOYCODE_BUTTON10_INDEXED(3)) ) - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON11, "P4 Button 11", input_seq(JOYCODE_BUTTON11_INDEXED(3)) ) - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON12, "P4 Button 12", input_seq(JOYCODE_BUTTON12_INDEXED(3)) ) - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON13, "P4 Button 13", input_seq(JOYCODE_BUTTON13_INDEXED(3)) ) - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON14, "P4 Button 14", input_seq(JOYCODE_BUTTON14_INDEXED(3)) ) - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON15, "P4 Button 15", input_seq(JOYCODE_BUTTON15_INDEXED(3)) ) - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON16, "P4 Button 16", input_seq(JOYCODE_BUTTON16_INDEXED(3)) ) - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, START, "P4 Start", input_seq(KEYCODE_4, input_seq::or_code, JOYCODE_START_INDEXED(3)) ) - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, SELECT, "P4 Select", input_seq(KEYCODE_8, input_seq::or_code, JOYCODE_SELECT_INDEXED(3)) ) -} - -inline void construct_core_types_P5(simple_list<input_type_entry> &typelist) -{ - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICK_UP, "P5 Up", input_seq(JOYCODE_Y_UP_SWITCH_INDEXED(4)) ) - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICK_DOWN, "P5 Down", input_seq(JOYCODE_Y_DOWN_SWITCH_INDEXED(4)) ) - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICK_LEFT, "P5 Left", input_seq(JOYCODE_X_LEFT_SWITCH_INDEXED(4)) ) - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICK_RIGHT, "P5 Right", input_seq(JOYCODE_X_RIGHT_SWITCH_INDEXED(4)) ) - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICKRIGHT_UP, "P5 Right Stick/Up", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICKRIGHT_DOWN, "P5 Right Stick/Down", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICKRIGHT_LEFT, "P5 Right Stick/Left", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICKRIGHT_RIGHT, "P5 Right Stick/Right", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICKLEFT_UP, "P5 Left Stick/Up", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICKLEFT_DOWN, "P5 Left Stick/Down", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICKLEFT_LEFT, "P5 Left Stick/Left", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICKLEFT_RIGHT, "P5 Left Stick/Right", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON1, "P5 Button 1", input_seq(JOYCODE_BUTTON1_INDEXED(4)) ) - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON2, "P5 Button 2", input_seq(JOYCODE_BUTTON2_INDEXED(4)) ) - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON3, "P5 Button 3", input_seq(JOYCODE_BUTTON3_INDEXED(4)) ) - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON4, "P5 Button 4", input_seq(JOYCODE_BUTTON4_INDEXED(4)) ) - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON5, "P5 Button 5", input_seq(JOYCODE_BUTTON5_INDEXED(4)) ) - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON6, "P5 Button 6", input_seq(JOYCODE_BUTTON6_INDEXED(4)) ) - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON7, "P5 Button 7", input_seq(JOYCODE_BUTTON7_INDEXED(4)) ) - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON8, "P5 Button 8", input_seq(JOYCODE_BUTTON8_INDEXED(4)) ) - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON9, "P5 Button 9", input_seq(JOYCODE_BUTTON9_INDEXED(4)) ) - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON10, "P5 Button 10", input_seq(JOYCODE_BUTTON10_INDEXED(4)) ) - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON11, "P5 Button 11", input_seq(JOYCODE_BUTTON11_INDEXED(4)) ) - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON12, "P5 Button 12", input_seq(JOYCODE_BUTTON12_INDEXED(4)) ) - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON13, "P5 Button 13", input_seq(JOYCODE_BUTTON13_INDEXED(4)) ) - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON14, "P5 Button 14", input_seq(JOYCODE_BUTTON14_INDEXED(4)) ) - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON15, "P5 Button 15", input_seq(JOYCODE_BUTTON15_INDEXED(4)) ) - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON16, "P5 Button 16", input_seq(JOYCODE_BUTTON16_INDEXED(4)) ) - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, START, "P5 Start", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, SELECT, "P5 Select", input_seq() ) -} - -inline void construct_core_types_P6(simple_list<input_type_entry> &typelist) -{ - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICK_UP, "P6 Up", input_seq(JOYCODE_Y_UP_SWITCH_INDEXED(5)) ) - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICK_DOWN, "P6 Down", input_seq(JOYCODE_Y_DOWN_SWITCH_INDEXED(5)) ) - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICK_LEFT, "P6 Left", input_seq(JOYCODE_X_LEFT_SWITCH_INDEXED(5)) ) - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICK_RIGHT, "P6 Right", input_seq(JOYCODE_X_RIGHT_SWITCH_INDEXED(5)) ) - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICKRIGHT_UP, "P6 Right Stick/Up", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICKRIGHT_DOWN, "P6 Right Stick/Down", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICKRIGHT_LEFT, "P6 Right Stick/Left", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICKRIGHT_RIGHT, "P6 Right Stick/Right", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICKLEFT_UP, "P6 Left Stick/Up", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICKLEFT_DOWN, "P6 Left Stick/Down", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICKLEFT_LEFT, "P6 Left Stick/Left", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICKLEFT_RIGHT, "P6 Left Stick/Right", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON1, "P6 Button 1", input_seq(JOYCODE_BUTTON1_INDEXED(5)) ) - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON2, "P6 Button 2", input_seq(JOYCODE_BUTTON2_INDEXED(5)) ) - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON3, "P6 Button 3", input_seq(JOYCODE_BUTTON3_INDEXED(5)) ) - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON4, "P6 Button 4", input_seq(JOYCODE_BUTTON4_INDEXED(5)) ) - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON5, "P6 Button 5", input_seq(JOYCODE_BUTTON5_INDEXED(5)) ) - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON6, "P6 Button 6", input_seq(JOYCODE_BUTTON6_INDEXED(5)) ) - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON7, "P6 Button 7", input_seq(JOYCODE_BUTTON7_INDEXED(5)) ) - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON8, "P6 Button 8", input_seq(JOYCODE_BUTTON8_INDEXED(5)) ) - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON9, "P6 Button 9", input_seq(JOYCODE_BUTTON9_INDEXED(5)) ) - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON10, "P6 Button 10", input_seq(JOYCODE_BUTTON10_INDEXED(5)) ) - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON11, "P6 Button 11", input_seq(JOYCODE_BUTTON11_INDEXED(5)) ) - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON12, "P6 Button 12", input_seq(JOYCODE_BUTTON12_INDEXED(5)) ) - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON13, "P6 Button 13", input_seq(JOYCODE_BUTTON13_INDEXED(5)) ) - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON14, "P6 Button 14", input_seq(JOYCODE_BUTTON14_INDEXED(5)) ) - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON15, "P6 Button 15", input_seq(JOYCODE_BUTTON15_INDEXED(5)) ) - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON16, "P6 Button 16", input_seq(JOYCODE_BUTTON16_INDEXED(5)) ) - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, START, "P6 Start", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, SELECT, "P6 Select", input_seq() ) -} - -inline void construct_core_types_P7(simple_list<input_type_entry> &typelist) -{ - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICK_UP, "P7 Up", input_seq(JOYCODE_Y_UP_SWITCH_INDEXED(6)) ) - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICK_DOWN, "P7 Down", input_seq(JOYCODE_Y_DOWN_SWITCH_INDEXED(6)) ) - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICK_LEFT, "P7 Left", input_seq(JOYCODE_X_LEFT_SWITCH_INDEXED(6)) ) - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICK_RIGHT, "P7 Right", input_seq(JOYCODE_X_RIGHT_SWITCH_INDEXED(6)) ) - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICKRIGHT_UP, "P7 Right Stick/Up", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICKRIGHT_DOWN, "P7 Right Stick/Down", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICKRIGHT_LEFT, "P7 Right Stick/Left", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICKRIGHT_RIGHT, "P7 Right Stick/Right", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICKLEFT_UP, "P7 Left Stick/Up", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICKLEFT_DOWN, "P7 Left Stick/Down", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICKLEFT_LEFT, "P7 Left Stick/Left", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICKLEFT_RIGHT, "P7 Left Stick/Right", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON1, "P7 Button 1", input_seq(JOYCODE_BUTTON1_INDEXED(6)) ) - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON2, "P7 Button 2", input_seq(JOYCODE_BUTTON2_INDEXED(6)) ) - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON3, "P7 Button 3", input_seq(JOYCODE_BUTTON3_INDEXED(6)) ) - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON4, "P7 Button 4", input_seq(JOYCODE_BUTTON4_INDEXED(6)) ) - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON5, "P7 Button 5", input_seq(JOYCODE_BUTTON5_INDEXED(6)) ) - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON6, "P7 Button 6", input_seq(JOYCODE_BUTTON6_INDEXED(6)) ) - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON7, "P7 Button 7", input_seq(JOYCODE_BUTTON7_INDEXED(6)) ) - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON8, "P7 Button 8", input_seq(JOYCODE_BUTTON8_INDEXED(6)) ) - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON9, "P7 Button 9", input_seq(JOYCODE_BUTTON9_INDEXED(6)) ) - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON10, "P7 Button 10", input_seq(JOYCODE_BUTTON10_INDEXED(6)) ) - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON11, "P7 Button 11", input_seq(JOYCODE_BUTTON11_INDEXED(6)) ) - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON12, "P7 Button 12", input_seq(JOYCODE_BUTTON12_INDEXED(6)) ) - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON13, "P7 Button 13", input_seq(JOYCODE_BUTTON13_INDEXED(6)) ) - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON14, "P7 Button 14", input_seq(JOYCODE_BUTTON14_INDEXED(6)) ) - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON15, "P7 Button 15", input_seq(JOYCODE_BUTTON15_INDEXED(6)) ) - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON16, "P7 Button 16", input_seq(JOYCODE_BUTTON16_INDEXED(6)) ) - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, START, "P7 Start", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, SELECT, "P7 Select", input_seq() ) -} - -inline void construct_core_types_P8(simple_list<input_type_entry> &typelist) -{ - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICK_UP, "P8 Up", input_seq(JOYCODE_Y_UP_SWITCH_INDEXED(7)) ) - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICK_DOWN, "P8 Down", input_seq(JOYCODE_Y_DOWN_SWITCH_INDEXED(7)) ) - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICK_LEFT, "P8 Left", input_seq(JOYCODE_X_LEFT_SWITCH_INDEXED(7)) ) - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICK_RIGHT, "P8 Right", input_seq(JOYCODE_X_RIGHT_SWITCH_INDEXED(7)) ) - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICKRIGHT_UP, "P8 Right Stick/Up", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICKRIGHT_DOWN, "P8 Right Stick/Down", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICKRIGHT_LEFT, "P8 Right Stick/Left", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICKRIGHT_RIGHT, "P8 Right Stick/Right", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICKLEFT_UP, "P8 Left Stick/Up", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICKLEFT_DOWN, "P8 Left Stick/Down", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICKLEFT_LEFT, "P8 Left Stick/Left", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICKLEFT_RIGHT, "P8 Left Stick/Right", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON1, "P8 Button 1", input_seq(JOYCODE_BUTTON1_INDEXED(7)) ) - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON2, "P8 Button 2", input_seq(JOYCODE_BUTTON2_INDEXED(7)) ) - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON3, "P8 Button 3", input_seq(JOYCODE_BUTTON3_INDEXED(7)) ) - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON4, "P8 Button 4", input_seq(JOYCODE_BUTTON4_INDEXED(7)) ) - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON5, "P8 Button 5", input_seq(JOYCODE_BUTTON5_INDEXED(7)) ) - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON6, "P8 Button 6", input_seq(JOYCODE_BUTTON6_INDEXED(7)) ) - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON7, "P8 Button 7", input_seq(JOYCODE_BUTTON7_INDEXED(7)) ) - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON8, "P8 Button 8", input_seq(JOYCODE_BUTTON8_INDEXED(7)) ) - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON9, "P8 Button 9", input_seq(JOYCODE_BUTTON9_INDEXED(7)) ) - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON10, "P8 Button 10", input_seq(JOYCODE_BUTTON10_INDEXED(7)) ) - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON11, "P8 Button 11", input_seq(JOYCODE_BUTTON11_INDEXED(7)) ) - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON12, "P8 Button 12", input_seq(JOYCODE_BUTTON12_INDEXED(7)) ) - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON13, "P8 Button 13", input_seq(JOYCODE_BUTTON13_INDEXED(7)) ) - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON14, "P8 Button 14", input_seq(JOYCODE_BUTTON14_INDEXED(7)) ) - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON15, "P8 Button 15", input_seq(JOYCODE_BUTTON15_INDEXED(7)) ) - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON16, "P8 Button 16", input_seq(JOYCODE_BUTTON16_INDEXED(7)) ) - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, START, "P8 Start", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, SELECT, "P8 Select", input_seq() ) -} - -inline void construct_core_types_P9(simple_list<input_type_entry> &typelist) -{ - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICK_UP, "P9 Up", input_seq(JOYCODE_Y_UP_SWITCH_INDEXED(8)) ) - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICK_DOWN, "P9 Down", input_seq(JOYCODE_Y_DOWN_SWITCH_INDEXED(8)) ) - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICK_LEFT, "P9 Left", input_seq(JOYCODE_X_LEFT_SWITCH_INDEXED(8)) ) - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICK_RIGHT, "P9 Right", input_seq(JOYCODE_X_RIGHT_SWITCH_INDEXED(8)) ) - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICKRIGHT_UP, "P9 Right Stick/Up", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICKRIGHT_DOWN, "P9 Right Stick/Down", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICKRIGHT_LEFT, "P9 Right Stick/Left", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICKRIGHT_RIGHT, "P9 Right Stick/Right", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICKLEFT_UP, "P9 Left Stick/Up", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICKLEFT_DOWN, "P9 Left Stick/Down", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICKLEFT_LEFT, "P9 Left Stick/Left", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICKLEFT_RIGHT, "P9 Left Stick/Right", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON1, "P9 Button 1", input_seq(JOYCODE_BUTTON1_INDEXED(8)) ) - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON2, "P9 Button 2", input_seq(JOYCODE_BUTTON2_INDEXED(8)) ) - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON3, "P9 Button 3", input_seq(JOYCODE_BUTTON3_INDEXED(8)) ) - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON4, "P9 Button 4", input_seq(JOYCODE_BUTTON4_INDEXED(8)) ) - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON5, "P9 Button 5", input_seq(JOYCODE_BUTTON5_INDEXED(8)) ) - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON6, "P9 Button 6", input_seq(JOYCODE_BUTTON6_INDEXED(8)) ) - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON7, "P9 Button 7", input_seq(JOYCODE_BUTTON7_INDEXED(8)) ) - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON8, "P9 Button 8", input_seq(JOYCODE_BUTTON8_INDEXED(8)) ) - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON9, "P9 Button 9", input_seq(JOYCODE_BUTTON9_INDEXED(8)) ) - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON10, "P9 Button 10", input_seq(JOYCODE_BUTTON10_INDEXED(8)) ) - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON11, "P9 Button 11", input_seq(JOYCODE_BUTTON11_INDEXED(8)) ) - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON12, "P9 Button 12", input_seq(JOYCODE_BUTTON12_INDEXED(8)) ) - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON13, "P9 Button 13", input_seq(JOYCODE_BUTTON13_INDEXED(8)) ) - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON14, "P9 Button 14", input_seq(JOYCODE_BUTTON14_INDEXED(8)) ) - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON15, "P9 Button 15", input_seq(JOYCODE_BUTTON15_INDEXED(8)) ) - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON16, "P9 Button 16", input_seq(JOYCODE_BUTTON16_INDEXED(8)) ) - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, START, "P9 Start", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, SELECT, "P9 Select", input_seq() ) -} - -inline void construct_core_types_P10(simple_list<input_type_entry> &typelist) -{ - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICK_UP, "P10 Up", input_seq(JOYCODE_Y_UP_SWITCH_INDEXED(9)) ) - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICK_DOWN, "P10 Down", input_seq(JOYCODE_Y_DOWN_SWITCH_INDEXED(9)) ) - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICK_LEFT, "P10 Left", input_seq(JOYCODE_X_LEFT_SWITCH_INDEXED(9)) ) - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICK_RIGHT, "P10 Right", input_seq(JOYCODE_X_RIGHT_SWITCH_INDEXED(9)) ) - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICKRIGHT_UP, "P10 Right Stick/Up", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICKRIGHT_DOWN, "P10 Right Stick/Down", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICKRIGHT_LEFT, "P10 Right Stick/Left", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICKRIGHT_RIGHT, "P10 Right Stick/Right", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICKLEFT_UP, "P10 Left Stick/Up", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICKLEFT_DOWN, "P10 Left Stick/Down", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICKLEFT_LEFT, "P10 Left Stick/Left", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICKLEFT_RIGHT, "P10 Left Stick/Right", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON1, "P10 Button 1", input_seq(JOYCODE_BUTTON1_INDEXED(9)) ) - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON2, "P10 Button 2", input_seq(JOYCODE_BUTTON2_INDEXED(9)) ) - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON3, "P10 Button 3", input_seq(JOYCODE_BUTTON3_INDEXED(9)) ) - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON4, "P10 Button 4", input_seq(JOYCODE_BUTTON4_INDEXED(9)) ) - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON5, "P10 Button 5", input_seq(JOYCODE_BUTTON5_INDEXED(9)) ) - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON6, "P10 Button 6", input_seq(JOYCODE_BUTTON6_INDEXED(9)) ) - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON7, "P10 Button 7", input_seq(JOYCODE_BUTTON7_INDEXED(9)) ) - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON8, "P10 Button 8", input_seq(JOYCODE_BUTTON8_INDEXED(9)) ) - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON9, "P10 Button 9", input_seq(JOYCODE_BUTTON9_INDEXED(9)) ) - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON10, "P10 Button 10", input_seq(JOYCODE_BUTTON10_INDEXED(9)) ) - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON11, "P10 Button 11", input_seq(JOYCODE_BUTTON11_INDEXED(9)) ) - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON12, "P10 Button 12", input_seq(JOYCODE_BUTTON12_INDEXED(9)) ) - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON13, "P10 Button 13", input_seq(JOYCODE_BUTTON13_INDEXED(9)) ) - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON14, "P10 Button 14", input_seq(JOYCODE_BUTTON14_INDEXED(9)) ) - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON15, "P10 Button 15", input_seq(JOYCODE_BUTTON15_INDEXED(9)) ) - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON16, "P10 Button 16", input_seq(JOYCODE_BUTTON16_INDEXED(9)) ) - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, START, "P10 Start", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, SELECT, "P10 Select", input_seq() ) -} - -inline void construct_core_types_start(simple_list<input_type_entry> &typelist) -{ - INPUT_PORT_DIGITAL_TYPE( 0, OTHER, START1, "1 Player Start", input_seq(KEYCODE_1, input_seq::or_code, JOYCODE_START_INDEXED(0)) ) - INPUT_PORT_DIGITAL_TYPE( 0, OTHER, START2, "2 Players Start", input_seq(KEYCODE_2, input_seq::or_code, JOYCODE_START_INDEXED(1)) ) - INPUT_PORT_DIGITAL_TYPE( 0, OTHER, START3, "3 Players Start", input_seq(KEYCODE_3, input_seq::or_code, JOYCODE_START_INDEXED(2)) ) - INPUT_PORT_DIGITAL_TYPE( 0, OTHER, START4, "4 Players Start", input_seq(KEYCODE_4, input_seq::or_code, JOYCODE_START_INDEXED(3)) ) - INPUT_PORT_DIGITAL_TYPE( 0, OTHER, START5, "5 Players Start", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 0, OTHER, START6, "6 Players Start", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 0, OTHER, START7, "7 Players Start", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 0, OTHER, START8, "8 Players Start", input_seq() ) -} - -inline void construct_core_types_coin(simple_list<input_type_entry> &typelist) -{ - INPUT_PORT_DIGITAL_TYPE( 0, OTHER, COIN1, "Coin 1", input_seq(KEYCODE_5, input_seq::or_code, JOYCODE_SELECT_INDEXED(0)) ) - INPUT_PORT_DIGITAL_TYPE( 0, OTHER, COIN2, "Coin 2", input_seq(KEYCODE_6, input_seq::or_code, JOYCODE_SELECT_INDEXED(1)) ) - INPUT_PORT_DIGITAL_TYPE( 0, OTHER, COIN3, "Coin 3", input_seq(KEYCODE_7, input_seq::or_code, JOYCODE_SELECT_INDEXED(2)) ) - INPUT_PORT_DIGITAL_TYPE( 0, OTHER, COIN4, "Coin 4", input_seq(KEYCODE_8, input_seq::or_code, JOYCODE_SELECT_INDEXED(3)) ) - INPUT_PORT_DIGITAL_TYPE( 0, OTHER, COIN5, "Coin 5", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 0, OTHER, COIN6, "Coin 6", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 0, OTHER, COIN7, "Coin 7", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 0, OTHER, COIN8, "Coin 8", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 0, OTHER, COIN9, "Coin 9", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 0, OTHER, COIN10, "Coin 10", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 0, OTHER, COIN11, "Coin 11", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 0, OTHER, COIN12, "Coin 12", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 0, OTHER, BILL1, "Bill 1", input_seq(KEYCODE_BACKSPACE) ) -} - -inline void construct_core_types_service(simple_list<input_type_entry> &typelist) -{ - INPUT_PORT_DIGITAL_TYPE( 0, OTHER, SERVICE1, "Service 1", input_seq(KEYCODE_9) ) - INPUT_PORT_DIGITAL_TYPE( 0, OTHER, SERVICE2, "Service 2", input_seq(KEYCODE_0) ) - INPUT_PORT_DIGITAL_TYPE( 0, OTHER, SERVICE3, "Service 3", input_seq(KEYCODE_MINUS) ) - INPUT_PORT_DIGITAL_TYPE( 0, OTHER, SERVICE4, "Service 4", input_seq(KEYCODE_EQUALS) ) -} - -inline void construct_core_types_tilt(simple_list<input_type_entry> &typelist) -{ - INPUT_PORT_DIGITAL_TYPE( 0, OTHER, TILT1, "Tilt 1", input_seq(KEYCODE_T) ) - INPUT_PORT_DIGITAL_TYPE( 0, OTHER, TILT2, "Tilt 2", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 0, OTHER, TILT3, "Tilt 3", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 0, OTHER, TILT4, "Tilt 4", input_seq() ) -} - -inline void construct_core_types_other(simple_list<input_type_entry> &typelist) -{ - INPUT_PORT_DIGITAL_TYPE( 0, OTHER, POWER_ON, "Power On", input_seq(KEYCODE_F1) ) - INPUT_PORT_DIGITAL_TYPE( 0, OTHER, POWER_OFF, "Power Off", input_seq(KEYCODE_F2) ) - INPUT_PORT_DIGITAL_TYPE( 0, OTHER, SERVICE, "Service", input_seq(KEYCODE_F2) ) - INPUT_PORT_DIGITAL_TYPE( 0, OTHER, TILT, "Tilt", input_seq(KEYCODE_T) ) - INPUT_PORT_DIGITAL_TYPE( 0, OTHER, INTERLOCK, "Door Interlock", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 0, OTHER, MEMORY_RESET, "Memory Reset", input_seq(KEYCODE_F1) ) - INPUT_PORT_DIGITAL_TYPE( 0, OTHER, VOLUME_DOWN, "Volume Down", input_seq(KEYCODE_MINUS) ) - INPUT_PORT_DIGITAL_TYPE( 0, OTHER, VOLUME_UP, "Volume Up", input_seq(KEYCODE_EQUALS) ) -} - -inline void construct_core_types_pedal(simple_list<input_type_entry> &typelist) -{ - INPUT_PORT_ANALOG_TYPE( 1, PLAYER1, PEDAL, "P1 Pedal 1", input_seq(JOYCODE_Z_NEG_ABSOLUTE_INDEXED(0)), input_seq(), input_seq(KEYCODE_LCONTROL, input_seq::or_code, JOYCODE_BUTTON1_INDEXED(0)) ) - INPUT_PORT_ANALOG_TYPE( 2, PLAYER2, PEDAL, "P2 Pedal 1", input_seq(JOYCODE_Z_NEG_ABSOLUTE_INDEXED(1)), input_seq(), input_seq(KEYCODE_A, input_seq::or_code, JOYCODE_BUTTON1_INDEXED(1)) ) - INPUT_PORT_ANALOG_TYPE( 3, PLAYER3, PEDAL, "P3 Pedal 1", input_seq(JOYCODE_Z_NEG_ABSOLUTE_INDEXED(2)), input_seq(), input_seq(KEYCODE_RCONTROL, input_seq::or_code, JOYCODE_BUTTON1_INDEXED(2)) ) - INPUT_PORT_ANALOG_TYPE( 4, PLAYER4, PEDAL, "P4 Pedal 1", input_seq(JOYCODE_Z_NEG_ABSOLUTE_INDEXED(3)), input_seq(), input_seq(KEYCODE_0_PAD, input_seq::or_code, JOYCODE_BUTTON1_INDEXED(3)) ) - INPUT_PORT_ANALOG_TYPE( 5, PLAYER5, PEDAL, "P5 Pedal 1", input_seq(JOYCODE_Z_NEG_ABSOLUTE_INDEXED(4)), input_seq(), input_seq(JOYCODE_BUTTON1_INDEXED(4)) ) - INPUT_PORT_ANALOG_TYPE( 6, PLAYER6, PEDAL, "P6 Pedal 1", input_seq(JOYCODE_Z_NEG_ABSOLUTE_INDEXED(5)), input_seq(), input_seq(JOYCODE_BUTTON1_INDEXED(5)) ) - INPUT_PORT_ANALOG_TYPE( 7, PLAYER7, PEDAL, "P7 Pedal 1", input_seq(JOYCODE_Z_NEG_ABSOLUTE_INDEXED(6)), input_seq(), input_seq(JOYCODE_BUTTON1_INDEXED(6)) ) - INPUT_PORT_ANALOG_TYPE( 8, PLAYER8, PEDAL, "P8 Pedal 1", input_seq(JOYCODE_Z_NEG_ABSOLUTE_INDEXED(7)), input_seq(), input_seq(JOYCODE_BUTTON1_INDEXED(7)) ) - INPUT_PORT_ANALOG_TYPE( 9, PLAYER9, PEDAL, "P9 Pedal 1", input_seq(JOYCODE_Z_NEG_ABSOLUTE_INDEXED(8)), input_seq(), input_seq(JOYCODE_BUTTON1_INDEXED(8)) ) - INPUT_PORT_ANALOG_TYPE( 10, PLAYER10, PEDAL, "P10 Pedal 1", input_seq(JOYCODE_Z_NEG_ABSOLUTE_INDEXED(9)), input_seq(), input_seq(JOYCODE_BUTTON1_INDEXED(9)) ) -} - -inline void construct_core_types_pedal2(simple_list<input_type_entry> &typelist) -{ - INPUT_PORT_ANALOG_TYPE( 1, PLAYER1, PEDAL2, "P1 Pedal 2", input_seq(JOYCODE_W_NEG_ABSOLUTE_INDEXED(0)), input_seq(), input_seq(KEYCODE_LALT, input_seq::or_code, JOYCODE_BUTTON2_INDEXED(0)) ) - INPUT_PORT_ANALOG_TYPE( 2, PLAYER2, PEDAL2, "P2 Pedal 2", input_seq(JOYCODE_W_NEG_ABSOLUTE_INDEXED(1)), input_seq(), input_seq(KEYCODE_S, input_seq::or_code, JOYCODE_BUTTON2_INDEXED(1)) ) - INPUT_PORT_ANALOG_TYPE( 3, PLAYER3, PEDAL2, "P3 Pedal 2", input_seq(JOYCODE_W_NEG_ABSOLUTE_INDEXED(2)), input_seq(), input_seq(KEYCODE_RSHIFT, input_seq::or_code, JOYCODE_BUTTON2_INDEXED(2)) ) - INPUT_PORT_ANALOG_TYPE( 4, PLAYER4, PEDAL2, "P4 Pedal 2", input_seq(JOYCODE_W_NEG_ABSOLUTE_INDEXED(3)), input_seq(), input_seq(KEYCODE_DEL_PAD, input_seq::or_code, JOYCODE_BUTTON2_INDEXED(3)) ) - INPUT_PORT_ANALOG_TYPE( 5, PLAYER5, PEDAL2, "P5 Pedal 2", input_seq(JOYCODE_W_NEG_ABSOLUTE_INDEXED(4)), input_seq(), input_seq(JOYCODE_BUTTON2_INDEXED(4)) ) - INPUT_PORT_ANALOG_TYPE( 6, PLAYER6, PEDAL2, "P6 Pedal 2", input_seq(JOYCODE_W_NEG_ABSOLUTE_INDEXED(5)), input_seq(), input_seq(JOYCODE_BUTTON2_INDEXED(5)) ) - INPUT_PORT_ANALOG_TYPE( 7, PLAYER7, PEDAL2, "P7 Pedal 2", input_seq(JOYCODE_W_NEG_ABSOLUTE_INDEXED(6)), input_seq(), input_seq(JOYCODE_BUTTON2_INDEXED(6)) ) - INPUT_PORT_ANALOG_TYPE( 8, PLAYER8, PEDAL2, "P8 Pedal 2", input_seq(JOYCODE_W_NEG_ABSOLUTE_INDEXED(7)), input_seq(), input_seq(JOYCODE_BUTTON2_INDEXED(7)) ) - INPUT_PORT_ANALOG_TYPE( 9, PLAYER9, PEDAL2, "P9 Pedal 2", input_seq(JOYCODE_W_NEG_ABSOLUTE_INDEXED(8)), input_seq(), input_seq(JOYCODE_BUTTON2_INDEXED(8)) ) - INPUT_PORT_ANALOG_TYPE( 10, PLAYER10, PEDAL2, "P10 Pedal 2", input_seq(JOYCODE_W_NEG_ABSOLUTE_INDEXED(9)), input_seq(), input_seq(JOYCODE_BUTTON2_INDEXED(9)) ) -} - -inline void construct_core_types_pedal3(simple_list<input_type_entry> &typelist) -{ - INPUT_PORT_ANALOG_TYPE( 1, PLAYER1, PEDAL3, "P1 Pedal 3", input_seq(), input_seq(), input_seq(KEYCODE_SPACE, input_seq::or_code, JOYCODE_BUTTON3_INDEXED(0)) ) - INPUT_PORT_ANALOG_TYPE( 2, PLAYER2, PEDAL3, "P2 Pedal 3", input_seq(), input_seq(), input_seq(KEYCODE_Q, input_seq::or_code, JOYCODE_BUTTON3_INDEXED(1)) ) - INPUT_PORT_ANALOG_TYPE( 3, PLAYER3, PEDAL3, "P3 Pedal 3", input_seq(), input_seq(), input_seq(KEYCODE_ENTER, input_seq::or_code, JOYCODE_BUTTON3_INDEXED(2)) ) - INPUT_PORT_ANALOG_TYPE( 4, PLAYER4, PEDAL3, "P4 Pedal 3", input_seq(), input_seq(), input_seq(KEYCODE_ENTER_PAD, input_seq::or_code, JOYCODE_BUTTON3_INDEXED(3)) ) - INPUT_PORT_ANALOG_TYPE( 5, PLAYER5, PEDAL3, "P5 Pedal 3", input_seq(), input_seq(), input_seq(JOYCODE_BUTTON3_INDEXED(4)) ) - INPUT_PORT_ANALOG_TYPE( 6, PLAYER6, PEDAL3, "P6 Pedal 3", input_seq(), input_seq(), input_seq(JOYCODE_BUTTON3_INDEXED(5)) ) - INPUT_PORT_ANALOG_TYPE( 7, PLAYER7, PEDAL3, "P7 Pedal 3", input_seq(), input_seq(), input_seq(JOYCODE_BUTTON3_INDEXED(6)) ) - INPUT_PORT_ANALOG_TYPE( 8, PLAYER8, PEDAL3, "P8 Pedal 3", input_seq(), input_seq(), input_seq(JOYCODE_BUTTON3_INDEXED(7)) ) - INPUT_PORT_ANALOG_TYPE( 9, PLAYER9, PEDAL3, "P9 Pedal 3", input_seq(), input_seq(), input_seq(JOYCODE_BUTTON3_INDEXED(8)) ) - INPUT_PORT_ANALOG_TYPE( 10, PLAYER10, PEDAL3, "P10 Pedal 3", input_seq(), input_seq(), input_seq(JOYCODE_BUTTON3_INDEXED(9)) ) -} - -inline void construct_core_types_paddle(simple_list<input_type_entry> &typelist) -{ - INPUT_PORT_ANALOG_TYPE( 1, PLAYER1, PADDLE, "Paddle", input_seq(JOYCODE_X_INDEXED(0), input_seq::or_code, MOUSECODE_X_INDEXED(0)), input_seq(KEYCODE_LEFT), input_seq(KEYCODE_RIGHT) ) - INPUT_PORT_ANALOG_TYPE( 2, PLAYER2, PADDLE, "Paddle 2", input_seq(JOYCODE_X_INDEXED(1), input_seq::or_code, MOUSECODE_X_INDEXED(1)), input_seq(KEYCODE_D), input_seq(KEYCODE_G) ) - INPUT_PORT_ANALOG_TYPE( 3, PLAYER3, PADDLE, "Paddle 3", input_seq(JOYCODE_X_INDEXED(2), input_seq::or_code, MOUSECODE_X_INDEXED(2)), input_seq(KEYCODE_J), input_seq(KEYCODE_L) ) - INPUT_PORT_ANALOG_TYPE( 4, PLAYER4, PADDLE, "Paddle 4", input_seq(JOYCODE_X_INDEXED(3), input_seq::or_code, MOUSECODE_X_INDEXED(3)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 5, PLAYER5, PADDLE, "Paddle 5", input_seq(JOYCODE_X_INDEXED(4), input_seq::or_code, MOUSECODE_X_INDEXED(4)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 6, PLAYER6, PADDLE, "Paddle 6", input_seq(JOYCODE_X_INDEXED(5), input_seq::or_code, MOUSECODE_X_INDEXED(5)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 7, PLAYER7, PADDLE, "Paddle 7", input_seq(JOYCODE_X_INDEXED(6), input_seq::or_code, MOUSECODE_X_INDEXED(6)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 8, PLAYER8, PADDLE, "Paddle 8", input_seq(JOYCODE_X_INDEXED(7), input_seq::or_code, MOUSECODE_X_INDEXED(7)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 9, PLAYER9, PADDLE, "Paddle 9", input_seq(JOYCODE_X_INDEXED(8), input_seq::or_code, MOUSECODE_X_INDEXED(8)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 10, PLAYER10, PADDLE, "Paddle 10", input_seq(JOYCODE_X_INDEXED(9), input_seq::or_code, MOUSECODE_X_INDEXED(9)), input_seq(), input_seq() ) -} - -inline void construct_core_types_paddle_v(simple_list<input_type_entry> &typelist) -{ - INPUT_PORT_ANALOG_TYPE( 1, PLAYER1, PADDLE_V, "Paddle V", input_seq(JOYCODE_Y_INDEXED(0), input_seq::or_code, MOUSECODE_Y_INDEXED(0)), input_seq(KEYCODE_UP), input_seq(KEYCODE_DOWN) ) - INPUT_PORT_ANALOG_TYPE( 2, PLAYER2, PADDLE_V, "Paddle V 2", input_seq(JOYCODE_Y_INDEXED(1), input_seq::or_code, MOUSECODE_Y_INDEXED(1)), input_seq(KEYCODE_R), input_seq(KEYCODE_F) ) - INPUT_PORT_ANALOG_TYPE( 3, PLAYER3, PADDLE_V, "Paddle V 3", input_seq(JOYCODE_Y_INDEXED(2), input_seq::or_code, MOUSECODE_Y_INDEXED(2)), input_seq(KEYCODE_I), input_seq(KEYCODE_K) ) - INPUT_PORT_ANALOG_TYPE( 4, PLAYER4, PADDLE_V, "Paddle V 4", input_seq(JOYCODE_Y_INDEXED(3), input_seq::or_code, MOUSECODE_Y_INDEXED(3)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 5, PLAYER5, PADDLE_V, "Paddle V 5", input_seq(JOYCODE_Y_INDEXED(4), input_seq::or_code, MOUSECODE_Y_INDEXED(4)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 6, PLAYER6, PADDLE_V, "Paddle V 6", input_seq(JOYCODE_Y_INDEXED(5), input_seq::or_code, MOUSECODE_Y_INDEXED(5)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 7, PLAYER7, PADDLE_V, "Paddle V 7", input_seq(JOYCODE_Y_INDEXED(6), input_seq::or_code, MOUSECODE_Y_INDEXED(6)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 8, PLAYER8, PADDLE_V, "Paddle V 8", input_seq(JOYCODE_Y_INDEXED(7), input_seq::or_code, MOUSECODE_Y_INDEXED(7)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 9, PLAYER9, PADDLE_V, "Paddle V 9", input_seq(JOYCODE_Y_INDEXED(8), input_seq::or_code, MOUSECODE_Y_INDEXED(8)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 10, PLAYER10, PADDLE_V, "Paddle V 10", input_seq(JOYCODE_Y_INDEXED(9), input_seq::or_code, MOUSECODE_Y_INDEXED(9)), input_seq(), input_seq() ) -} - -inline void construct_core_types_positional(simple_list<input_type_entry> &typelist) -{ - INPUT_PORT_ANALOG_TYPE( 1, PLAYER1, POSITIONAL, "Positional", input_seq(MOUSECODE_X_INDEXED(0), input_seq::or_code, JOYCODE_X_INDEXED(0)), input_seq(KEYCODE_LEFT), input_seq(KEYCODE_RIGHT) ) - INPUT_PORT_ANALOG_TYPE( 2, PLAYER2, POSITIONAL, "Positional 2", input_seq(MOUSECODE_X_INDEXED(1), input_seq::or_code, JOYCODE_X_INDEXED(1)), input_seq(KEYCODE_D), input_seq(KEYCODE_G) ) - INPUT_PORT_ANALOG_TYPE( 3, PLAYER3, POSITIONAL, "Positional 3", input_seq(MOUSECODE_X_INDEXED(2), input_seq::or_code, JOYCODE_X_INDEXED(2)), input_seq(KEYCODE_J), input_seq(KEYCODE_L) ) - INPUT_PORT_ANALOG_TYPE( 4, PLAYER4, POSITIONAL, "Positional 4", input_seq(MOUSECODE_X_INDEXED(3), input_seq::or_code, JOYCODE_X_INDEXED(3)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 5, PLAYER5, POSITIONAL, "Positional 5", input_seq(MOUSECODE_X_INDEXED(4), input_seq::or_code, JOYCODE_X_INDEXED(4)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 6, PLAYER6, POSITIONAL, "Positional 6", input_seq(MOUSECODE_X_INDEXED(5), input_seq::or_code, JOYCODE_X_INDEXED(5)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 7, PLAYER7, POSITIONAL, "Positional 7", input_seq(MOUSECODE_X_INDEXED(6), input_seq::or_code, JOYCODE_X_INDEXED(6)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 8, PLAYER8, POSITIONAL, "Positional 8", input_seq(MOUSECODE_X_INDEXED(7), input_seq::or_code, JOYCODE_X_INDEXED(7)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 9, PLAYER9, POSITIONAL, "Positional 9", input_seq(MOUSECODE_X_INDEXED(8), input_seq::or_code, JOYCODE_X_INDEXED(8)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 10, PLAYER10, POSITIONAL, "Positional 10", input_seq(MOUSECODE_X_INDEXED(9), input_seq::or_code, JOYCODE_X_INDEXED(9)), input_seq(), input_seq() ) -} - -inline void construct_core_types_positional_v(simple_list<input_type_entry> &typelist) -{ - INPUT_PORT_ANALOG_TYPE( 1, PLAYER1, POSITIONAL_V, "Positional V", input_seq(MOUSECODE_Y_INDEXED(0), input_seq::or_code, JOYCODE_Y_INDEXED(0)), input_seq(KEYCODE_UP), input_seq(KEYCODE_DOWN) ) - INPUT_PORT_ANALOG_TYPE( 2, PLAYER2, POSITIONAL_V, "Positional V 2", input_seq(MOUSECODE_Y_INDEXED(1), input_seq::or_code, JOYCODE_Y_INDEXED(1)), input_seq(KEYCODE_R), input_seq(KEYCODE_F) ) - INPUT_PORT_ANALOG_TYPE( 3, PLAYER3, POSITIONAL_V, "Positional V 3", input_seq(MOUSECODE_Y_INDEXED(2), input_seq::or_code, JOYCODE_Y_INDEXED(2)), input_seq(KEYCODE_I), input_seq(KEYCODE_K) ) - INPUT_PORT_ANALOG_TYPE( 4, PLAYER4, POSITIONAL_V, "Positional V 4", input_seq(MOUSECODE_Y_INDEXED(3), input_seq::or_code, JOYCODE_Y_INDEXED(3)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 5, PLAYER5, POSITIONAL_V, "Positional V 5", input_seq(MOUSECODE_Y_INDEXED(4), input_seq::or_code, JOYCODE_Y_INDEXED(4)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 6, PLAYER6, POSITIONAL_V, "Positional V 6", input_seq(MOUSECODE_Y_INDEXED(5), input_seq::or_code, JOYCODE_Y_INDEXED(5)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 7, PLAYER7, POSITIONAL_V, "Positional V 7", input_seq(MOUSECODE_Y_INDEXED(6), input_seq::or_code, JOYCODE_Y_INDEXED(6)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 8, PLAYER8, POSITIONAL_V, "Positional V 8", input_seq(MOUSECODE_Y_INDEXED(7), input_seq::or_code, JOYCODE_Y_INDEXED(7)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 9, PLAYER9, POSITIONAL_V, "Positional V 9", input_seq(MOUSECODE_Y_INDEXED(8), input_seq::or_code, JOYCODE_Y_INDEXED(8)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 10, PLAYER10, POSITIONAL_V, "Positional V 10", input_seq(MOUSECODE_Y_INDEXED(9), input_seq::or_code, JOYCODE_Y_INDEXED(9)), input_seq(), input_seq() ) -} - -inline void construct_core_types_dial(simple_list<input_type_entry> &typelist) -{ - INPUT_PORT_ANALOG_TYPE( 1, PLAYER1, DIAL, "Dial", input_seq(MOUSECODE_X_INDEXED(0), input_seq::or_code, JOYCODE_X_INDEXED(0)), input_seq(KEYCODE_LEFT), input_seq(KEYCODE_RIGHT) ) - INPUT_PORT_ANALOG_TYPE( 2, PLAYER2, DIAL, "Dial 2", input_seq(MOUSECODE_X_INDEXED(1), input_seq::or_code, JOYCODE_X_INDEXED(1)), input_seq(KEYCODE_D), input_seq(KEYCODE_G) ) - INPUT_PORT_ANALOG_TYPE( 3, PLAYER3, DIAL, "Dial 3", input_seq(MOUSECODE_X_INDEXED(2), input_seq::or_code, JOYCODE_X_INDEXED(2)), input_seq(KEYCODE_J), input_seq(KEYCODE_L) ) - INPUT_PORT_ANALOG_TYPE( 4, PLAYER4, DIAL, "Dial 4", input_seq(MOUSECODE_X_INDEXED(3), input_seq::or_code, JOYCODE_X_INDEXED(3)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 5, PLAYER5, DIAL, "Dial 5", input_seq(MOUSECODE_X_INDEXED(4), input_seq::or_code, JOYCODE_X_INDEXED(4)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 6, PLAYER6, DIAL, "Dial 6", input_seq(MOUSECODE_X_INDEXED(5), input_seq::or_code, JOYCODE_X_INDEXED(5)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 7, PLAYER7, DIAL, "Dial 7", input_seq(MOUSECODE_X_INDEXED(6), input_seq::or_code, JOYCODE_X_INDEXED(6)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 8, PLAYER8, DIAL, "Dial 8", input_seq(MOUSECODE_X_INDEXED(7), input_seq::or_code, JOYCODE_X_INDEXED(7)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 9, PLAYER9, DIAL, "Dial 9", input_seq(MOUSECODE_X_INDEXED(8), input_seq::or_code, JOYCODE_X_INDEXED(8)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 10, PLAYER10, DIAL, "Dial 10", input_seq(MOUSECODE_X_INDEXED(9), input_seq::or_code, JOYCODE_X_INDEXED(9)), input_seq(), input_seq() ) -} - -inline void construct_core_types_dial_v(simple_list<input_type_entry> &typelist) -{ - INPUT_PORT_ANALOG_TYPE( 1, PLAYER1, DIAL_V, "Dial V", input_seq(MOUSECODE_Y_INDEXED(0), input_seq::or_code, JOYCODE_Y_INDEXED(0)), input_seq(KEYCODE_UP), input_seq(KEYCODE_DOWN) ) - INPUT_PORT_ANALOG_TYPE( 2, PLAYER2, DIAL_V, "Dial V 2", input_seq(MOUSECODE_Y_INDEXED(1), input_seq::or_code, JOYCODE_Y_INDEXED(1)), input_seq(KEYCODE_R), input_seq(KEYCODE_F) ) - INPUT_PORT_ANALOG_TYPE( 3, PLAYER3, DIAL_V, "Dial V 3", input_seq(MOUSECODE_Y_INDEXED(2), input_seq::or_code, JOYCODE_Y_INDEXED(2)), input_seq(KEYCODE_I), input_seq(KEYCODE_K) ) - INPUT_PORT_ANALOG_TYPE( 4, PLAYER4, DIAL_V, "Dial V 4", input_seq(MOUSECODE_Y_INDEXED(3), input_seq::or_code, JOYCODE_Y_INDEXED(3)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 5, PLAYER5, DIAL_V, "Dial V 5", input_seq(MOUSECODE_Y_INDEXED(4), input_seq::or_code, JOYCODE_Y_INDEXED(4)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 6, PLAYER6, DIAL_V, "Dial V 6", input_seq(MOUSECODE_Y_INDEXED(5), input_seq::or_code, JOYCODE_Y_INDEXED(5)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 7, PLAYER7, DIAL_V, "Dial V 7", input_seq(MOUSECODE_Y_INDEXED(6), input_seq::or_code, JOYCODE_Y_INDEXED(6)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 8, PLAYER8, DIAL_V, "Dial V 8", input_seq(MOUSECODE_Y_INDEXED(7), input_seq::or_code, JOYCODE_Y_INDEXED(7)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 9, PLAYER9, DIAL_V, "Dial V 9", input_seq(MOUSECODE_Y_INDEXED(8), input_seq::or_code, JOYCODE_Y_INDEXED(8)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 10, PLAYER10, DIAL_V, "Dial V 10", input_seq(MOUSECODE_Y_INDEXED(9), input_seq::or_code, JOYCODE_Y_INDEXED(9)), input_seq(), input_seq() ) -} - -inline void construct_core_types_trackball_X(simple_list<input_type_entry> &typelist) -{ - INPUT_PORT_ANALOG_TYPE( 1, PLAYER1, TRACKBALL_X, "Track X", input_seq(MOUSECODE_X_INDEXED(0), input_seq::or_code, JOYCODE_X_INDEXED(0)), input_seq(KEYCODE_LEFT), input_seq(KEYCODE_RIGHT) ) - INPUT_PORT_ANALOG_TYPE( 2, PLAYER2, TRACKBALL_X, "Track X 2", input_seq(MOUSECODE_X_INDEXED(1), input_seq::or_code, JOYCODE_X_INDEXED(1)), input_seq(KEYCODE_D), input_seq(KEYCODE_G) ) - INPUT_PORT_ANALOG_TYPE( 3, PLAYER3, TRACKBALL_X, "Track X 3", input_seq(MOUSECODE_X_INDEXED(2), input_seq::or_code, JOYCODE_X_INDEXED(2)), input_seq(KEYCODE_J), input_seq(KEYCODE_L) ) - INPUT_PORT_ANALOG_TYPE( 4, PLAYER4, TRACKBALL_X, "Track X 4", input_seq(MOUSECODE_X_INDEXED(3), input_seq::or_code, JOYCODE_X_INDEXED(3)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 5, PLAYER5, TRACKBALL_X, "Track X 5", input_seq(MOUSECODE_X_INDEXED(4), input_seq::or_code, JOYCODE_X_INDEXED(4)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 6, PLAYER6, TRACKBALL_X, "Track X 6", input_seq(MOUSECODE_X_INDEXED(5), input_seq::or_code, JOYCODE_X_INDEXED(5)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 7, PLAYER7, TRACKBALL_X, "Track X 7", input_seq(MOUSECODE_X_INDEXED(6), input_seq::or_code, JOYCODE_X_INDEXED(6)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 8, PLAYER8, TRACKBALL_X, "Track X 8", input_seq(MOUSECODE_X_INDEXED(7), input_seq::or_code, JOYCODE_X_INDEXED(7)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 9, PLAYER9, TRACKBALL_X, "Track X 9", input_seq(MOUSECODE_X_INDEXED(8), input_seq::or_code, JOYCODE_X_INDEXED(8)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 10, PLAYER10, TRACKBALL_X, "Track X 10", input_seq(MOUSECODE_X_INDEXED(9), input_seq::or_code, JOYCODE_X_INDEXED(9)), input_seq(), input_seq() ) -} - -inline void construct_core_types_trackball_Y(simple_list<input_type_entry> &typelist) -{ - INPUT_PORT_ANALOG_TYPE( 1, PLAYER1, TRACKBALL_Y, "Track Y", input_seq(MOUSECODE_Y_INDEXED(0), input_seq::or_code, JOYCODE_Y_INDEXED(0)), input_seq(KEYCODE_UP), input_seq(KEYCODE_DOWN) ) - INPUT_PORT_ANALOG_TYPE( 2, PLAYER2, TRACKBALL_Y, "Track Y 2", input_seq(MOUSECODE_Y_INDEXED(1), input_seq::or_code, JOYCODE_Y_INDEXED(1)), input_seq(KEYCODE_R), input_seq(KEYCODE_F) ) - INPUT_PORT_ANALOG_TYPE( 3, PLAYER3, TRACKBALL_Y, "Track Y 3", input_seq(MOUSECODE_Y_INDEXED(2), input_seq::or_code, JOYCODE_Y_INDEXED(2)), input_seq(KEYCODE_I), input_seq(KEYCODE_K) ) - INPUT_PORT_ANALOG_TYPE( 4, PLAYER4, TRACKBALL_Y, "Track Y 4", input_seq(MOUSECODE_Y_INDEXED(3), input_seq::or_code, JOYCODE_Y_INDEXED(3)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 5, PLAYER5, TRACKBALL_Y, "Track Y 5", input_seq(MOUSECODE_Y_INDEXED(4), input_seq::or_code, JOYCODE_Y_INDEXED(4)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 6, PLAYER6, TRACKBALL_Y, "Track Y 6", input_seq(MOUSECODE_Y_INDEXED(5), input_seq::or_code, JOYCODE_Y_INDEXED(5)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 7, PLAYER7, TRACKBALL_Y, "Track Y 7", input_seq(MOUSECODE_Y_INDEXED(6), input_seq::or_code, JOYCODE_Y_INDEXED(6)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 8, PLAYER8, TRACKBALL_Y, "Track Y 8", input_seq(MOUSECODE_Y_INDEXED(7), input_seq::or_code, JOYCODE_Y_INDEXED(7)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 9, PLAYER9, TRACKBALL_Y, "Track Y 9", input_seq(MOUSECODE_Y_INDEXED(8), input_seq::or_code, JOYCODE_Y_INDEXED(8)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 10, PLAYER10, TRACKBALL_Y, "Track Y 10", input_seq(MOUSECODE_Y_INDEXED(9), input_seq::or_code, JOYCODE_Y_INDEXED(9)), input_seq(), input_seq() ) -} - -inline void construct_core_types_AD_stick_X(simple_list<input_type_entry> &typelist) -{ - INPUT_PORT_ANALOG_TYPE( 1, PLAYER1, AD_STICK_X, "AD Stick X", input_seq(JOYCODE_X_INDEXED(0), input_seq::or_code, MOUSECODE_X_INDEXED(0)), input_seq(KEYCODE_LEFT), input_seq(KEYCODE_RIGHT) ) - INPUT_PORT_ANALOG_TYPE( 2, PLAYER2, AD_STICK_X, "AD Stick X 2", input_seq(JOYCODE_X_INDEXED(1), input_seq::or_code, MOUSECODE_X_INDEXED(1)), input_seq(KEYCODE_D), input_seq(KEYCODE_G) ) - INPUT_PORT_ANALOG_TYPE( 3, PLAYER3, AD_STICK_X, "AD Stick X 3", input_seq(JOYCODE_X_INDEXED(2), input_seq::or_code, MOUSECODE_X_INDEXED(2)), input_seq(KEYCODE_J), input_seq(KEYCODE_L) ) - INPUT_PORT_ANALOG_TYPE( 4, PLAYER4, AD_STICK_X, "AD Stick X 4", input_seq(JOYCODE_X_INDEXED(3), input_seq::or_code, MOUSECODE_X_INDEXED(3)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 5, PLAYER5, AD_STICK_X, "AD Stick X 5", input_seq(JOYCODE_X_INDEXED(4), input_seq::or_code, MOUSECODE_X_INDEXED(4)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 6, PLAYER6, AD_STICK_X, "AD Stick X 6", input_seq(JOYCODE_X_INDEXED(5), input_seq::or_code, MOUSECODE_X_INDEXED(5)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 7, PLAYER7, AD_STICK_X, "AD Stick X 7", input_seq(JOYCODE_X_INDEXED(6), input_seq::or_code, MOUSECODE_X_INDEXED(6)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 8, PLAYER8, AD_STICK_X, "AD Stick X 8", input_seq(JOYCODE_X_INDEXED(7), input_seq::or_code, MOUSECODE_X_INDEXED(7)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 9, PLAYER9, AD_STICK_X, "AD Stick X 9", input_seq(JOYCODE_X_INDEXED(8), input_seq::or_code, MOUSECODE_X_INDEXED(8)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 10, PLAYER10, AD_STICK_X, "AD Stick X 10", input_seq(JOYCODE_X_INDEXED(9), input_seq::or_code, MOUSECODE_X_INDEXED(9)), input_seq(), input_seq() ) -} - -inline void construct_core_types_AD_stick_Y(simple_list<input_type_entry> &typelist) -{ - INPUT_PORT_ANALOG_TYPE( 1, PLAYER1, AD_STICK_Y, "AD Stick Y", input_seq(JOYCODE_Y_INDEXED(0), input_seq::or_code, MOUSECODE_Y_INDEXED(0)), input_seq(KEYCODE_UP), input_seq(KEYCODE_DOWN) ) - INPUT_PORT_ANALOG_TYPE( 2, PLAYER2, AD_STICK_Y, "AD Stick Y 2", input_seq(JOYCODE_Y_INDEXED(1), input_seq::or_code, MOUSECODE_Y_INDEXED(1)), input_seq(KEYCODE_R), input_seq(KEYCODE_F) ) - INPUT_PORT_ANALOG_TYPE( 3, PLAYER3, AD_STICK_Y, "AD Stick Y 3", input_seq(JOYCODE_Y_INDEXED(2), input_seq::or_code, MOUSECODE_Y_INDEXED(2)), input_seq(KEYCODE_I), input_seq(KEYCODE_K) ) - INPUT_PORT_ANALOG_TYPE( 4, PLAYER4, AD_STICK_Y, "AD Stick Y 4", input_seq(JOYCODE_Y_INDEXED(3), input_seq::or_code, MOUSECODE_Y_INDEXED(3)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 5, PLAYER5, AD_STICK_Y, "AD Stick Y 5", input_seq(JOYCODE_Y_INDEXED(4), input_seq::or_code, MOUSECODE_Y_INDEXED(4)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 6, PLAYER6, AD_STICK_Y, "AD Stick Y 6", input_seq(JOYCODE_Y_INDEXED(5), input_seq::or_code, MOUSECODE_Y_INDEXED(5)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 7, PLAYER7, AD_STICK_Y, "AD Stick Y 7", input_seq(JOYCODE_Y_INDEXED(6), input_seq::or_code, MOUSECODE_Y_INDEXED(6)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 8, PLAYER8, AD_STICK_Y, "AD Stick Y 8", input_seq(JOYCODE_Y_INDEXED(7), input_seq::or_code, MOUSECODE_Y_INDEXED(7)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 9, PLAYER9, AD_STICK_Y, "AD Stick Y 9", input_seq(JOYCODE_Y_INDEXED(8), input_seq::or_code, MOUSECODE_Y_INDEXED(8)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 10, PLAYER10, AD_STICK_Y, "AD Stick Y 10", input_seq(JOYCODE_Y_INDEXED(9), input_seq::or_code, MOUSECODE_Y_INDEXED(9)), input_seq(), input_seq() ) -} - -inline void construct_core_types_AD_stick_Z(simple_list<input_type_entry> &typelist) -{ - INPUT_PORT_ANALOG_TYPE( 1, PLAYER1, AD_STICK_Z, "AD Stick Z", input_seq(JOYCODE_Z_INDEXED(0)), input_seq(KEYCODE_A), input_seq(KEYCODE_Z) ) - INPUT_PORT_ANALOG_TYPE( 2, PLAYER2, AD_STICK_Z, "AD Stick Z 2", input_seq(JOYCODE_Z_INDEXED(1)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 3, PLAYER3, AD_STICK_Z, "AD Stick Z 3", input_seq(JOYCODE_Z_INDEXED(2)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 4, PLAYER4, AD_STICK_Z, "AD Stick Z 4", input_seq(JOYCODE_Z_INDEXED(3)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 5, PLAYER5, AD_STICK_Z, "AD Stick Z 5", input_seq(JOYCODE_Z_INDEXED(4)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 6, PLAYER6, AD_STICK_Z, "AD Stick Z 6", input_seq(JOYCODE_Z_INDEXED(5)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 7, PLAYER7, AD_STICK_Z, "AD Stick Z 7", input_seq(JOYCODE_Z_INDEXED(6)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 8, PLAYER8, AD_STICK_Z, "AD Stick Z 8", input_seq(JOYCODE_Z_INDEXED(7)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 9, PLAYER9, AD_STICK_Z, "AD Stick Z 9", input_seq(JOYCODE_Z_INDEXED(8)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 10, PLAYER10, AD_STICK_Z, "AD Stick Z 10", input_seq(JOYCODE_Z_INDEXED(9)), input_seq(), input_seq() ) -} - -inline void construct_core_types_lightgun_X(simple_list<input_type_entry> &typelist) -{ - INPUT_PORT_ANALOG_TYPE( 1, PLAYER1, LIGHTGUN_X, "Lightgun X", input_seq(GUNCODE_X_INDEXED(0), input_seq::or_code, MOUSECODE_X_INDEXED(0), input_seq::or_code, JOYCODE_X_INDEXED(0)), input_seq(KEYCODE_LEFT), input_seq(KEYCODE_RIGHT) ) - INPUT_PORT_ANALOG_TYPE( 2, PLAYER2, LIGHTGUN_X, "Lightgun X 2", input_seq(GUNCODE_X_INDEXED(1), input_seq::or_code, MOUSECODE_X_INDEXED(1), input_seq::or_code, JOYCODE_X_INDEXED(1)), input_seq(KEYCODE_D), input_seq(KEYCODE_G) ) - INPUT_PORT_ANALOG_TYPE( 3, PLAYER3, LIGHTGUN_X, "Lightgun X 3", input_seq(GUNCODE_X_INDEXED(2), input_seq::or_code, MOUSECODE_X_INDEXED(2), input_seq::or_code, JOYCODE_X_INDEXED(2)), input_seq(KEYCODE_J), input_seq(KEYCODE_L) ) - INPUT_PORT_ANALOG_TYPE( 4, PLAYER4, LIGHTGUN_X, "Lightgun X 4", input_seq(GUNCODE_X_INDEXED(3), input_seq::or_code, MOUSECODE_X_INDEXED(3), input_seq::or_code, JOYCODE_X_INDEXED(3)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 5, PLAYER5, LIGHTGUN_X, "Lightgun X 5", input_seq(GUNCODE_X_INDEXED(4), input_seq::or_code, MOUSECODE_X_INDEXED(4), input_seq::or_code, JOYCODE_X_INDEXED(4)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 6, PLAYER6, LIGHTGUN_X, "Lightgun X 6", input_seq(GUNCODE_X_INDEXED(5), input_seq::or_code, MOUSECODE_X_INDEXED(5), input_seq::or_code, JOYCODE_X_INDEXED(5)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 7, PLAYER7, LIGHTGUN_X, "Lightgun X 7", input_seq(GUNCODE_X_INDEXED(6), input_seq::or_code, MOUSECODE_X_INDEXED(6), input_seq::or_code, JOYCODE_X_INDEXED(6)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 8, PLAYER8, LIGHTGUN_X, "Lightgun X 8", input_seq(GUNCODE_X_INDEXED(7), input_seq::or_code, MOUSECODE_X_INDEXED(7), input_seq::or_code, JOYCODE_X_INDEXED(7)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 9, PLAYER9, LIGHTGUN_X, "Lightgun X 9", input_seq(GUNCODE_X_INDEXED(8), input_seq::or_code, MOUSECODE_X_INDEXED(8), input_seq::or_code, JOYCODE_X_INDEXED(8)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 10, PLAYER10, LIGHTGUN_X, "Lightgun X 10", input_seq(GUNCODE_X_INDEXED(9), input_seq::or_code, MOUSECODE_X_INDEXED(9), input_seq::or_code, JOYCODE_X_INDEXED(9)), input_seq(), input_seq() ) -} - -inline void construct_core_types_lightgun_Y(simple_list<input_type_entry> &typelist) -{ - INPUT_PORT_ANALOG_TYPE( 1, PLAYER1, LIGHTGUN_Y, "Lightgun Y", input_seq(GUNCODE_Y_INDEXED(0), input_seq::or_code, MOUSECODE_Y_INDEXED(0), input_seq::or_code, JOYCODE_Y_INDEXED(0)), input_seq(KEYCODE_UP), input_seq(KEYCODE_DOWN) ) - INPUT_PORT_ANALOG_TYPE( 2, PLAYER2, LIGHTGUN_Y, "Lightgun Y 2", input_seq(GUNCODE_Y_INDEXED(1), input_seq::or_code, MOUSECODE_Y_INDEXED(1), input_seq::or_code, JOYCODE_Y_INDEXED(1)), input_seq(KEYCODE_R), input_seq(KEYCODE_F) ) - INPUT_PORT_ANALOG_TYPE( 3, PLAYER3, LIGHTGUN_Y, "Lightgun Y 3", input_seq(GUNCODE_Y_INDEXED(2), input_seq::or_code, MOUSECODE_Y_INDEXED(2), input_seq::or_code, JOYCODE_Y_INDEXED(2)), input_seq(KEYCODE_I), input_seq(KEYCODE_K) ) - INPUT_PORT_ANALOG_TYPE( 4, PLAYER4, LIGHTGUN_Y, "Lightgun Y 4", input_seq(GUNCODE_Y_INDEXED(3), input_seq::or_code, MOUSECODE_Y_INDEXED(3), input_seq::or_code, JOYCODE_Y_INDEXED(3)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 5, PLAYER5, LIGHTGUN_Y, "Lightgun Y 5", input_seq(GUNCODE_Y_INDEXED(4), input_seq::or_code, MOUSECODE_Y_INDEXED(4), input_seq::or_code, JOYCODE_Y_INDEXED(4)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 6, PLAYER6, LIGHTGUN_Y, "Lightgun Y 6", input_seq(GUNCODE_Y_INDEXED(5), input_seq::or_code, MOUSECODE_Y_INDEXED(5), input_seq::or_code, JOYCODE_Y_INDEXED(5)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 7, PLAYER7, LIGHTGUN_Y, "Lightgun Y 7", input_seq(GUNCODE_Y_INDEXED(6), input_seq::or_code, MOUSECODE_Y_INDEXED(6), input_seq::or_code, JOYCODE_Y_INDEXED(6)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 8, PLAYER8, LIGHTGUN_Y, "Lightgun Y 8", input_seq(GUNCODE_Y_INDEXED(7), input_seq::or_code, MOUSECODE_Y_INDEXED(7), input_seq::or_code, JOYCODE_Y_INDEXED(7)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 9, PLAYER9, LIGHTGUN_Y, "Lightgun Y 9", input_seq(GUNCODE_Y_INDEXED(8), input_seq::or_code, MOUSECODE_Y_INDEXED(8), input_seq::or_code, JOYCODE_Y_INDEXED(8)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 10, PLAYER10, LIGHTGUN_Y, "Lightgun Y 10", input_seq(GUNCODE_Y_INDEXED(9), input_seq::or_code, MOUSECODE_Y_INDEXED(9), input_seq::or_code, JOYCODE_Y_INDEXED(9)), input_seq(), input_seq() ) -} - -inline void construct_core_types_mouse_X(simple_list<input_type_entry> &typelist) -{ - INPUT_PORT_ANALOG_TYPE( 1, PLAYER1, MOUSE_X, "Mouse X", input_seq(MOUSECODE_X_INDEXED(0)), input_seq(KEYCODE_LEFT), input_seq(KEYCODE_RIGHT) ) - INPUT_PORT_ANALOG_TYPE( 2, PLAYER2, MOUSE_X, "Mouse X 2", input_seq(MOUSECODE_X_INDEXED(1)), input_seq(KEYCODE_D), input_seq(KEYCODE_G) ) - INPUT_PORT_ANALOG_TYPE( 3, PLAYER3, MOUSE_X, "Mouse X 3", input_seq(MOUSECODE_X_INDEXED(2)), input_seq(KEYCODE_J), input_seq(KEYCODE_L) ) - INPUT_PORT_ANALOG_TYPE( 4, PLAYER4, MOUSE_X, "Mouse X 4", input_seq(MOUSECODE_X_INDEXED(3)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 5, PLAYER5, MOUSE_X, "Mouse X 5", input_seq(MOUSECODE_X_INDEXED(4)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 6, PLAYER6, MOUSE_X, "Mouse X 6", input_seq(MOUSECODE_X_INDEXED(5)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 7, PLAYER7, MOUSE_X, "Mouse X 7", input_seq(MOUSECODE_X_INDEXED(6)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 8, PLAYER8, MOUSE_X, "Mouse X 8", input_seq(MOUSECODE_X_INDEXED(7)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 9, PLAYER9, MOUSE_X, "Mouse X 9", input_seq(MOUSECODE_X_INDEXED(8)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 10, PLAYER10, MOUSE_X, "Mouse X 10", input_seq(MOUSECODE_X_INDEXED(9)), input_seq(), input_seq() ) -} - -inline void construct_core_types_mouse_Y(simple_list<input_type_entry> &typelist) -{ - INPUT_PORT_ANALOG_TYPE( 1, PLAYER1, MOUSE_Y, "Mouse Y", input_seq(MOUSECODE_Y_INDEXED(0)), input_seq(KEYCODE_UP), input_seq(KEYCODE_DOWN) ) - INPUT_PORT_ANALOG_TYPE( 2, PLAYER2, MOUSE_Y, "Mouse Y 2", input_seq(MOUSECODE_Y_INDEXED(1)), input_seq(KEYCODE_R), input_seq(KEYCODE_F) ) - INPUT_PORT_ANALOG_TYPE( 3, PLAYER3, MOUSE_Y, "Mouse Y 3", input_seq(MOUSECODE_Y_INDEXED(2)), input_seq(KEYCODE_I), input_seq(KEYCODE_K) ) - INPUT_PORT_ANALOG_TYPE( 4, PLAYER4, MOUSE_Y, "Mouse Y 4", input_seq(MOUSECODE_Y_INDEXED(3)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 5, PLAYER5, MOUSE_Y, "Mouse Y 5", input_seq(MOUSECODE_Y_INDEXED(4)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 6, PLAYER6, MOUSE_Y, "Mouse Y 6", input_seq(MOUSECODE_Y_INDEXED(5)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 7, PLAYER7, MOUSE_Y, "Mouse Y 7", input_seq(MOUSECODE_Y_INDEXED(6)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 8, PLAYER8, MOUSE_Y, "Mouse Y 8", input_seq(MOUSECODE_Y_INDEXED(7)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 9, PLAYER9, MOUSE_Y, "Mouse Y 9", input_seq(MOUSECODE_Y_INDEXED(8)), input_seq(), input_seq() ) - INPUT_PORT_ANALOG_TYPE( 10, PLAYER10, MOUSE_Y, "Mouse Y 10", input_seq(MOUSECODE_Y_INDEXED(9)), input_seq(), input_seq() ) -} - -inline void construct_core_types_keypad(simple_list<input_type_entry> &typelist) -{ - INPUT_PORT_DIGITAL_TYPE( 0, OTHER, KEYPAD, "Keypad", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 0, OTHER, KEYBOARD, "Keyboard", input_seq() ) -} - -inline void construct_core_types_UI(simple_list<input_type_entry> &typelist) -{ - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_ON_SCREEN_DISPLAY,"On Screen Display", input_seq(KEYCODE_TILDE, input_seq::not_code, KEYCODE_LSHIFT) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_DEBUG_BREAK, "Break in Debugger", input_seq(KEYCODE_TILDE, input_seq::not_code, KEYCODE_LSHIFT) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_CONFIGURE, "Config Menu", input_seq(KEYCODE_TAB) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_PAUSE, "Pause", input_seq(KEYCODE_P, input_seq::not_code, KEYCODE_LSHIFT, input_seq::not_code, KEYCODE_RSHIFT) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_PAUSE_SINGLE, "Pause - Single Step", input_seq(KEYCODE_P, KEYCODE_LSHIFT) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_REWIND_SINGLE, "Rewind - Single Step", input_seq(KEYCODE_TILDE, KEYCODE_LSHIFT) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_RESET_MACHINE, "Reset Machine", input_seq(KEYCODE_F3, KEYCODE_LSHIFT) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_SOFT_RESET, "Soft Reset", input_seq(KEYCODE_F3, input_seq::not_code, KEYCODE_LSHIFT) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_SHOW_GFX, "Show Gfx", input_seq(KEYCODE_F4) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_FRAMESKIP_DEC, "Frameskip Dec", input_seq(KEYCODE_F8) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_FRAMESKIP_INC, "Frameskip Inc", input_seq(KEYCODE_F9) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_THROTTLE, "Throttle", input_seq(KEYCODE_F10) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_FAST_FORWARD, "Fast Forward", input_seq(KEYCODE_INSERT) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_SHOW_FPS, "Show FPS", input_seq(KEYCODE_F11, input_seq::not_code, KEYCODE_LSHIFT) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_SNAPSHOT, "Save Snapshot", input_seq(KEYCODE_F12, input_seq::not_code, KEYCODE_LSHIFT) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_TIMECODE, "Write current timecode", input_seq(KEYCODE_F12, input_seq::not_code, KEYCODE_LSHIFT) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_RECORD_MNG, "Record MNG", input_seq(KEYCODE_F12, KEYCODE_LSHIFT, input_seq::not_code, KEYCODE_LCONTROL) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_RECORD_AVI, "Record AVI", input_seq(KEYCODE_F12, KEYCODE_LSHIFT, KEYCODE_LCONTROL) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_TOGGLE_CHEAT, "Toggle Cheat", input_seq(KEYCODE_F6) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_TOGGLE_AUTOFIRE, "Toggle Autofire", input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_UP, "UI Up", input_seq(KEYCODE_UP, input_seq::or_code, JOYCODE_Y_UP_SWITCH_INDEXED(0)) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_DOWN, "UI Down", input_seq(KEYCODE_DOWN, input_seq::or_code, JOYCODE_Y_DOWN_SWITCH_INDEXED(0)) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_LEFT, "UI Left", input_seq(KEYCODE_LEFT, input_seq::or_code, JOYCODE_X_LEFT_SWITCH_INDEXED(0)) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_RIGHT, "UI Right", input_seq(KEYCODE_RIGHT, input_seq::or_code, JOYCODE_X_RIGHT_SWITCH_INDEXED(0)) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_HOME, "UI Home", input_seq(KEYCODE_HOME) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_END, "UI End", input_seq(KEYCODE_END) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_PAGE_UP, "UI Page Up", input_seq(KEYCODE_PGUP) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_PAGE_DOWN, "UI Page Down", input_seq(KEYCODE_PGDN) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_FOCUS_NEXT, "UI Focus Next", input_seq(KEYCODE_TAB, input_seq::not_code, KEYCODE_LSHIFT) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_FOCUS_PREV, "UI Focus Previous", input_seq(KEYCODE_TAB, KEYCODE_LSHIFT) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_SELECT, "UI Select", input_seq(KEYCODE_ENTER, input_seq::or_code, JOYCODE_BUTTON1_INDEXED(0), input_seq::or_code, KEYCODE_ENTER_PAD) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_CANCEL, "UI Cancel", input_seq(KEYCODE_ESC) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_DISPLAY_COMMENT, "UI Display Comment", input_seq(KEYCODE_SPACE) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_CLEAR, "UI Clear", input_seq(KEYCODE_DEL) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_ZOOM_IN, "UI Zoom In", input_seq(KEYCODE_EQUALS) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_ZOOM_OUT, "UI Zoom Out", input_seq(KEYCODE_MINUS) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_PREV_GROUP, "UI Previous Group", input_seq(KEYCODE_OPENBRACE) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_NEXT_GROUP, "UI Next Group", input_seq(KEYCODE_CLOSEBRACE) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_ROTATE, "UI Rotate", input_seq(KEYCODE_R) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_SHOW_PROFILER, "Show Profiler", input_seq(KEYCODE_F11, KEYCODE_LSHIFT) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_TOGGLE_UI, "UI Toggle", input_seq(KEYCODE_SCRLOCK, input_seq::not_code, KEYCODE_LSHIFT) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_PASTE, "UI Paste Text", input_seq(KEYCODE_SCRLOCK, KEYCODE_LSHIFT) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_TOGGLE_DEBUG, "Toggle Debugger", input_seq(KEYCODE_F5) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_SAVE_STATE, "Save State", input_seq(KEYCODE_F7, KEYCODE_LSHIFT) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_LOAD_STATE, "Load State", input_seq(KEYCODE_F7, input_seq::not_code, KEYCODE_LSHIFT) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_TAPE_START, "UI (First) Tape Start", input_seq(KEYCODE_F2, input_seq::not_code, KEYCODE_LSHIFT) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_TAPE_STOP, "UI (First) Tape Stop", input_seq(KEYCODE_F2, KEYCODE_LSHIFT) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_DATS, "UI External DAT View", input_seq(KEYCODE_LALT, KEYCODE_D) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_FAVORITES, "UI Add/Remove favorites",input_seq(KEYCODE_LALT, KEYCODE_F) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_EXPORT, "UI Export list", input_seq(KEYCODE_LALT, KEYCODE_E) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_AUDIT_FAST, "UI Audit Unavailable", input_seq(KEYCODE_F1, input_seq::not_code, KEYCODE_LSHIFT) ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_AUDIT_ALL, "UI Audit All", input_seq(KEYCODE_F1, KEYCODE_LSHIFT) ) -} - -inline void construct_core_types_OSD(simple_list<input_type_entry> &typelist) -{ - INPUT_PORT_DIGITAL_TYPE( 0, UI, OSD_1, nullptr, input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, OSD_2, nullptr, input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, OSD_3, nullptr, input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, OSD_4, nullptr, input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, OSD_5, nullptr, input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, OSD_6, nullptr, input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, OSD_7, nullptr, input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, OSD_8, nullptr, input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, OSD_9, nullptr, input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, OSD_10, nullptr, input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, OSD_11, nullptr, input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, OSD_12, nullptr, input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, OSD_13, nullptr, input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, OSD_14, nullptr, input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, OSD_15, nullptr, input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 0, UI, OSD_16, nullptr, input_seq() ) -} - -inline void construct_core_types_invalid(simple_list<input_type_entry> &typelist) -{ - INPUT_PORT_DIGITAL_TYPE( 0, INVALID, UNKNOWN, nullptr, input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 0, INVALID, UNUSED, nullptr, input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 0, INVALID, SPECIAL, nullptr, input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 0, INVALID, OTHER, nullptr, input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 0, INVALID, ADJUSTER, nullptr, input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 0, INVALID, DIPSWITCH, nullptr, input_seq() ) - INPUT_PORT_DIGITAL_TYPE( 0, INVALID, CONFIG, nullptr, input_seq() ) -} - -inline void construct_core_types(simple_list<input_type_entry> &typelist) -{ - construct_core_types_P1(typelist); - construct_core_types_P1_mahjong(typelist); - construct_core_types_P1_hanafuda(typelist); - construct_core_types_gamble(typelist); - construct_core_types_poker(typelist); - construct_core_types_slot(typelist); - construct_core_types_P2(typelist); - construct_core_types_P2_mahjong(typelist); - construct_core_types_P2_hanafuda(typelist); - construct_core_types_P3(typelist); - construct_core_types_P4(typelist); - construct_core_types_P5(typelist); - construct_core_types_P6(typelist); - construct_core_types_P7(typelist); - construct_core_types_P8(typelist); - construct_core_types_P9(typelist); - construct_core_types_P10(typelist); - construct_core_types_start(typelist); - construct_core_types_coin(typelist); - construct_core_types_service(typelist); - construct_core_types_tilt(typelist); - construct_core_types_other(typelist); - construct_core_types_pedal(typelist); - construct_core_types_pedal2(typelist); - construct_core_types_pedal3(typelist); - construct_core_types_paddle(typelist); - construct_core_types_paddle_v(typelist); - construct_core_types_positional(typelist); - construct_core_types_positional_v(typelist); - construct_core_types_dial(typelist); - construct_core_types_dial_v(typelist); - construct_core_types_trackball_X(typelist); - construct_core_types_trackball_Y(typelist); - construct_core_types_AD_stick_X(typelist); - construct_core_types_AD_stick_Y(typelist); - construct_core_types_AD_stick_Z(typelist); - construct_core_types_lightgun_X(typelist); - construct_core_types_lightgun_Y(typelist); - construct_core_types_mouse_X(typelist); - construct_core_types_mouse_Y(typelist); - construct_core_types_keypad(typelist); - construct_core_types_UI(typelist); - construct_core_types_OSD(typelist); - construct_core_types_invalid(typelist); -} -#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ > 4 || (__GNUC_MINOR__ == 4 && __GNUC_PATCHLEVEL__ >= 4)))) -#if not(defined(__arm__) || defined(__ARMEL__)) -#pragma GCC pop_options -#endif -#elif defined(_MSC_VER) && !defined(__clang__) -#pragma optimize("", on) -#endif +#ifndef MAME_EMU_INPTTYPE_H +#define MAME_EMU_INPTTYPE_H + +#pragma once + +#include "interface/inputfwd.h" +#include "osdcomm.h" + + +enum ioport_type : osd::u32 +{ + // pseudo-port types + IPT_INVALID = 0, + IPT_UNUSED, + IPT_END, + IPT_UNKNOWN, + IPT_PORT, + IPT_DIPSWITCH, + IPT_CONFIG, + + // start buttons + IPT_START1, + IPT_START2, + IPT_START3, + IPT_START4, + IPT_START5, + IPT_START6, + IPT_START7, + IPT_START8, + IPT_START9, + IPT_START10, + + // coin slots + IPT_COIN1, + IPT_COIN2, + IPT_COIN3, + IPT_COIN4, + IPT_COIN5, + IPT_COIN6, + IPT_COIN7, + IPT_COIN8, + IPT_COIN9, + IPT_COIN10, + IPT_COIN11, + IPT_COIN12, + IPT_BILL1, + + // service coin + IPT_SERVICE1, + IPT_SERVICE2, + IPT_SERVICE3, + IPT_SERVICE4, + + // tilt inputs + IPT_TILT1, + IPT_TILT2, + IPT_TILT3, + IPT_TILT4, + + // misc other digital inputs + IPT_POWER_ON, + IPT_POWER_OFF, + IPT_SERVICE, + IPT_TILT, + IPT_INTERLOCK, + IPT_MEMORY_RESET, + IPT_VOLUME_UP, + IPT_VOLUME_DOWN, + IPT_START, // use the numbered start button(s) for coin-ops + IPT_SELECT, + IPT_KEYPAD, + IPT_KEYBOARD, + + // digital joystick inputs + IPT_DIGITAL_JOYSTICK_FIRST, + + // use IPT_JOYSTICK for panels where the player has one single joystick + IPT_JOYSTICK_UP, + IPT_JOYSTICK_DOWN, + IPT_JOYSTICK_LEFT, + IPT_JOYSTICK_RIGHT, + + // use IPT_JOYSTICKLEFT and IPT_JOYSTICKRIGHT for dual joystick panels + IPT_JOYSTICKRIGHT_UP, + IPT_JOYSTICKRIGHT_DOWN, + IPT_JOYSTICKRIGHT_LEFT, + IPT_JOYSTICKRIGHT_RIGHT, + IPT_JOYSTICKLEFT_UP, + IPT_JOYSTICKLEFT_DOWN, + IPT_JOYSTICKLEFT_LEFT, + IPT_JOYSTICKLEFT_RIGHT, + + IPT_DIGITAL_JOYSTICK_LAST, + + // action buttons + IPT_BUTTON1, + IPT_BUTTON2, + IPT_BUTTON3, + IPT_BUTTON4, + IPT_BUTTON5, + IPT_BUTTON6, + IPT_BUTTON7, + IPT_BUTTON8, + IPT_BUTTON9, + IPT_BUTTON10, + IPT_BUTTON11, + IPT_BUTTON12, + IPT_BUTTON13, + IPT_BUTTON14, + IPT_BUTTON15, + IPT_BUTTON16, + + // mahjong inputs + IPT_MAHJONG_FIRST, + + IPT_MAHJONG_A, + IPT_MAHJONG_B, + IPT_MAHJONG_C, + IPT_MAHJONG_D, + IPT_MAHJONG_E, + IPT_MAHJONG_F, + IPT_MAHJONG_G, + IPT_MAHJONG_H, + IPT_MAHJONG_I, + IPT_MAHJONG_J, + IPT_MAHJONG_K, + IPT_MAHJONG_L, + IPT_MAHJONG_M, + IPT_MAHJONG_N, + IPT_MAHJONG_O, + IPT_MAHJONG_P, + IPT_MAHJONG_Q, + IPT_MAHJONG_KAN, + IPT_MAHJONG_PON, + IPT_MAHJONG_CHI, + IPT_MAHJONG_REACH, + IPT_MAHJONG_RON, + IPT_MAHJONG_FLIP_FLOP, + IPT_MAHJONG_BET, + IPT_MAHJONG_SCORE, + IPT_MAHJONG_DOUBLE_UP, + IPT_MAHJONG_BIG, + IPT_MAHJONG_SMALL, + IPT_MAHJONG_LAST_CHANCE, + + IPT_MAHJONG_LAST, + + // hanafuda inputs + IPT_HANAFUDA_FIRST, + + IPT_HANAFUDA_A, + IPT_HANAFUDA_B, + IPT_HANAFUDA_C, + IPT_HANAFUDA_D, + IPT_HANAFUDA_E, + IPT_HANAFUDA_F, + IPT_HANAFUDA_G, + IPT_HANAFUDA_H, + IPT_HANAFUDA_YES, + IPT_HANAFUDA_NO, + + IPT_HANAFUDA_LAST, + + // gambling inputs + IPT_GAMBLING_FIRST, + + IPT_GAMBLE_KEYIN, // attendant + IPT_GAMBLE_KEYOUT, // attendant + IPT_GAMBLE_SERVICE, // attendant + IPT_GAMBLE_BOOK, // attendant + IPT_GAMBLE_DOOR, // attendant + // IPT_GAMBLE_DOOR2, // many gambling games have several doors. + // IPT_GAMBLE_DOOR3, + // IPT_GAMBLE_DOOR4, + // IPT_GAMBLE_DOOR5, + + IPT_GAMBLE_PAYOUT, // player + IPT_GAMBLE_BET, // player + IPT_GAMBLE_DEAL, // player + IPT_GAMBLE_STAND, // player + IPT_GAMBLE_TAKE, // player + IPT_GAMBLE_D_UP, // player + IPT_GAMBLE_HALF, // player + IPT_GAMBLE_HIGH, // player + IPT_GAMBLE_LOW, // player + + // poker-specific inputs + IPT_POKER_HOLD1, + IPT_POKER_HOLD2, + IPT_POKER_HOLD3, + IPT_POKER_HOLD4, + IPT_POKER_HOLD5, + IPT_POKER_CANCEL, + + // slot-specific inputs + IPT_SLOT_STOP1, + IPT_SLOT_STOP2, + IPT_SLOT_STOP3, + IPT_SLOT_STOP4, + IPT_SLOT_STOP5, + IPT_SLOT_STOP_ALL, + + IPT_GAMBLING_LAST, + + // analog inputs + IPT_ANALOG_FIRST, + + IPT_ANALOG_ABSOLUTE_FIRST, + + IPT_AD_STICK_X, // absolute // autocenter + IPT_AD_STICK_Y, // absolute // autocenter + IPT_AD_STICK_Z, // absolute // autocenter + IPT_PADDLE, // absolute // autocenter + IPT_PADDLE_V, // absolute // autocenter + IPT_PEDAL, // absolute // autocenter + IPT_PEDAL2, // absolute // autocenter + IPT_PEDAL3, // absolute // autocenter + IPT_LIGHTGUN_X, // absolute + IPT_LIGHTGUN_Y, // absolute + IPT_POSITIONAL, // absolute // autocenter if not wraps + IPT_POSITIONAL_V, // absolute // autocenter if not wraps + + IPT_ANALOG_ABSOLUTE_LAST, + + IPT_DIAL, // relative + IPT_DIAL_V, // relative + IPT_TRACKBALL_X, // relative + IPT_TRACKBALL_Y, // relative + IPT_MOUSE_X, // relative + IPT_MOUSE_Y, // relative + + IPT_ANALOG_LAST, + + // analog adjuster support + IPT_ADJUSTER, + + // the following are special codes for user interface handling - not to be used by drivers! + IPT_UI_FIRST, + + IPT_UI_MENU, + IPT_UI_SELECT, + IPT_UI_BACK, + IPT_UI_CANCEL, + IPT_UI_CLEAR, + IPT_UI_HELP, + IPT_UI_UP, + IPT_UI_DOWN, + IPT_UI_LEFT, + IPT_UI_RIGHT, + IPT_UI_HOME, + IPT_UI_END, + IPT_UI_PAGE_UP, + IPT_UI_PAGE_DOWN, + IPT_UI_PREV_GROUP, + IPT_UI_NEXT_GROUP, + IPT_UI_ON_SCREEN_DISPLAY, + IPT_UI_TOGGLE_UI, + IPT_UI_DEBUG_BREAK, + IPT_UI_PAUSE, + IPT_UI_PAUSE_SINGLE, + IPT_UI_REWIND_SINGLE, + IPT_UI_SAVE_STATE, + IPT_UI_SAVE_STATE_QUICK, + IPT_UI_LOAD_STATE, + IPT_UI_LOAD_STATE_QUICK, + IPT_UI_RESET_MACHINE, + IPT_UI_SOFT_RESET, + IPT_UI_SHOW_GFX, + IPT_UI_FRAMESKIP_DEC, + IPT_UI_FRAMESKIP_INC, + IPT_UI_THROTTLE, + IPT_UI_FAST_FORWARD, + IPT_UI_SHOW_FPS, + IPT_UI_SNAPSHOT, + IPT_UI_RECORD_MNG, + IPT_UI_RECORD_AVI, + IPT_UI_TOGGLE_CHEAT, + IPT_UI_DISPLAY_COMMENT, + IPT_UI_ZOOM_IN, + IPT_UI_ZOOM_OUT, + IPT_UI_ZOOM_DEFAULT, + IPT_UI_ROTATE, + IPT_UI_SHOW_PROFILER, + IPT_UI_RELEASE_POINTER, + IPT_UI_PASTE, + IPT_UI_TAPE_START, + IPT_UI_TAPE_STOP, + IPT_UI_FOCUS_NEXT, + IPT_UI_FOCUS_PREV, + IPT_UI_DATS, + IPT_UI_FAVORITES, + IPT_UI_EXPORT, + IPT_UI_AUDIT, + IPT_UI_MIXER_ADD_FULL, + IPT_UI_MIXER_ADD_CHANNEL, + + // additional OSD-specified UI port types (up to 16) + IPT_OSD_1, + IPT_OSD_2, + IPT_OSD_3, + IPT_OSD_4, + IPT_OSD_5, + IPT_OSD_6, + IPT_OSD_7, + IPT_OSD_8, + IPT_OSD_9, + IPT_OSD_10, + IPT_OSD_11, + IPT_OSD_12, + IPT_OSD_13, + IPT_OSD_14, + IPT_OSD_15, + IPT_OSD_16, + + IPT_UI_LAST, + + IPT_OTHER, // not mapped to standard defaults + + IPT_SPECIAL, // uninterpreted characters + IPT_CUSTOM, // handled by custom code + IPT_OUTPUT, + + IPT_COUNT, + + // aliases for some types + IPT_PADDLE_H = IPT_PADDLE, + IPT_PEDAL1 = IPT_PEDAL, + IPT_POSITIONAL_H = IPT_POSITIONAL, + IPT_DIAL_H = IPT_DIAL +}; + +DECLARE_ENUM_INCDEC_OPERATORS(ioport_type) + + +// sequence types for input_port_seq() call +enum input_seq_type : int +{ + SEQ_TYPE_INVALID = -1, + SEQ_TYPE_STANDARD = 0, + SEQ_TYPE_INCREMENT, + SEQ_TYPE_DECREMENT, + SEQ_TYPE_TOTAL +}; + +DECLARE_ENUM_INCDEC_OPERATORS(input_seq_type) + +#endif // MAME_EMU_INPTTYPE_H diff --git a/src/emu/inpttype.ipp b/src/emu/inpttype.ipp new file mode 100644 index 00000000000..c51afa8ce31 --- /dev/null +++ b/src/emu/inpttype.ipp @@ -0,0 +1,1152 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + inpttype.ipp + + Array of core-defined input types and default mappings. + +***************************************************************************/ + +#include "util/language.h" + + +/*************************************************************************** + BUILT-IN CORE MAPPINGS +***************************************************************************/ + +namespace { + +#define CORE_INPUT_TYPES_P1 \ + CORE_INPUT_TYPES_BEGIN(p1) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICK_UP, N_p("input-name", "%p Up"), input_seq(KEYCODE_UP) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICK_DOWN, N_p("input-name", "%p Down"), input_seq(KEYCODE_DOWN) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICK_LEFT, N_p("input-name", "%p Left"), input_seq(KEYCODE_LEFT) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICK_RIGHT, N_p("input-name", "%p Right"), input_seq(KEYCODE_RIGHT) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICKRIGHT_UP, N_p("input-name", "%p Right Stick/Up"), input_seq(KEYCODE_I) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICKRIGHT_DOWN, N_p("input-name", "%p Right Stick/Down"), input_seq(KEYCODE_K) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICKRIGHT_LEFT, N_p("input-name", "%p Right Stick/Left"), input_seq(KEYCODE_J) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICKRIGHT_RIGHT, N_p("input-name", "%p Right Stick/Right"), input_seq(KEYCODE_L) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICKLEFT_UP, N_p("input-name", "%p Left Stick/Up"), input_seq(KEYCODE_E) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICKLEFT_DOWN, N_p("input-name", "%p Left Stick/Down"), input_seq(KEYCODE_D) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICKLEFT_LEFT, N_p("input-name", "%p Left Stick/Left"), input_seq(KEYCODE_S) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICKLEFT_RIGHT, N_p("input-name", "%p Left Stick/Right"), input_seq(KEYCODE_F) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON1, N_p("input-name", "%p Button 1"), input_seq(KEYCODE_LCONTROL, input_seq::or_code, MOUSECODE_BUTTON1_INDEXED(0), input_seq::or_code, GUNCODE_BUTTON1_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON2, N_p("input-name", "%p Button 2"), input_seq(KEYCODE_LALT, input_seq::or_code, MOUSECODE_BUTTON3_INDEXED(0), input_seq::or_code, GUNCODE_BUTTON2_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON3, N_p("input-name", "%p Button 3"), input_seq(KEYCODE_SPACE, input_seq::or_code, MOUSECODE_BUTTON2_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON4, N_p("input-name", "%p Button 4"), input_seq(KEYCODE_LSHIFT) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON5, N_p("input-name", "%p Button 5"), input_seq(KEYCODE_Z) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON6, N_p("input-name", "%p Button 6"), input_seq(KEYCODE_X) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON7, N_p("input-name", "%p Button 7"), input_seq(KEYCODE_C) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON8, N_p("input-name", "%p Button 8"), input_seq(KEYCODE_V) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON9, N_p("input-name", "%p Button 9"), input_seq(KEYCODE_B) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON10, N_p("input-name", "%p Button 10"), input_seq(KEYCODE_N) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON11, N_p("input-name", "%p Button 11"), input_seq(KEYCODE_M) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON12, N_p("input-name", "%p Button 12"), input_seq(KEYCODE_COMMA) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON13, N_p("input-name", "%p Button 13"), input_seq(KEYCODE_STOP) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON14, N_p("input-name", "%p Button 14"), input_seq(KEYCODE_SLASH) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON15, N_p("input-name", "%p Button 15"), input_seq(KEYCODE_RSHIFT) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON16, N_p("input-name", "%p Button 16"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, START, N_p("input-name", "%p Start"), input_seq(KEYCODE_1) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, SELECT, N_p("input-name", "%p Select"), input_seq(KEYCODE_5) ) \ + CORE_INPUT_TYPES_END() + +#define CORE_INPUT_TYPES_P1_MAHJONG \ + CORE_INPUT_TYPES_BEGIN(p1_mahjong) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_A, N_p("input-name", "%p Mahjong A"), input_seq(KEYCODE_A_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_B, N_p("input-name", "%p Mahjong B"), input_seq(KEYCODE_B_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_C, N_p("input-name", "%p Mahjong C"), input_seq(KEYCODE_C_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_D, N_p("input-name", "%p Mahjong D"), input_seq(KEYCODE_D_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_E, N_p("input-name", "%p Mahjong E"), input_seq(KEYCODE_E_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_F, N_p("input-name", "%p Mahjong F"), input_seq(KEYCODE_F_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_G, N_p("input-name", "%p Mahjong G"), input_seq(KEYCODE_G_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_H, N_p("input-name", "%p Mahjong H"), input_seq(KEYCODE_H_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_I, N_p("input-name", "%p Mahjong I"), input_seq(KEYCODE_I_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_J, N_p("input-name", "%p Mahjong J"), input_seq(KEYCODE_J_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_K, N_p("input-name", "%p Mahjong K"), input_seq(KEYCODE_K_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_L, N_p("input-name", "%p Mahjong L"), input_seq(KEYCODE_L_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_M, N_p("input-name", "%p Mahjong M"), input_seq(KEYCODE_M_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_N, N_p("input-name", "%p Mahjong N"), input_seq(KEYCODE_N_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_O, N_p("input-name", "%p Mahjong O"), input_seq(KEYCODE_O_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_P, N_p("input-name", "%p Mahjong P"), input_seq(KEYCODE_COLON_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_Q, N_p("input-name", "%p Mahjong Q"), input_seq(KEYCODE_Q_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_KAN, N_p("input-name", "%p Mahjong Kan"), input_seq(KEYCODE_LCONTROL_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_PON, N_p("input-name", "%p Mahjong Pon"), input_seq(KEYCODE_LALT_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_CHI, N_p("input-name", "%p Mahjong Chi"), input_seq(KEYCODE_SPACE_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_REACH, N_p("input-name", "%p Mahjong Reach"), input_seq(KEYCODE_LSHIFT_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_RON, N_p("input-name", "%p Mahjong Ron"), input_seq(KEYCODE_Z_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_FLIP_FLOP, N_p("input-name", "%p Mahjong Flip Flop"), input_seq(KEYCODE_Y_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_BET, N_p("input-name", "%p Mahjong Bet"), input_seq(KEYCODE_3_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_SCORE, N_p("input-name", "%p Mahjong Take Score"), input_seq(KEYCODE_RCONTROL_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_DOUBLE_UP, N_p("input-name", "%p Mahjong Double Up"), input_seq(KEYCODE_RSHIFT_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_BIG, N_p("input-name", "%p Mahjong Big"), input_seq(KEYCODE_ENTER_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_SMALL, N_p("input-name", "%p Mahjong Small"), input_seq(KEYCODE_BACKSPACE_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_LAST_CHANCE, N_p("input-name", "%p Mahjong Last Chance"), input_seq(KEYCODE_RALT_INDEXED(0)) ) \ + CORE_INPUT_TYPES_END() + +#define CORE_INPUT_TYPES_P1_HANAFUDA \ + CORE_INPUT_TYPES_BEGIN(p1_hanafuda) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, HANAFUDA_A, N_p("input-name", "%p Hanafuda A/1"), input_seq(KEYCODE_A_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, HANAFUDA_B, N_p("input-name", "%p Hanafuda B/2"), input_seq(KEYCODE_B_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, HANAFUDA_C, N_p("input-name", "%p Hanafuda C/3"), input_seq(KEYCODE_C_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, HANAFUDA_D, N_p("input-name", "%p Hanafuda D/4"), input_seq(KEYCODE_D_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, HANAFUDA_E, N_p("input-name", "%p Hanafuda E/5"), input_seq(KEYCODE_E_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, HANAFUDA_F, N_p("input-name", "%p Hanafuda F/6"), input_seq(KEYCODE_F_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, HANAFUDA_G, N_p("input-name", "%p Hanafuda G/7"), input_seq(KEYCODE_G_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, HANAFUDA_H, N_p("input-name", "%p Hanafuda H/8"), input_seq(KEYCODE_H_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, HANAFUDA_YES, N_p("input-name", "%p Hanafuda Yes"), input_seq(KEYCODE_M_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, HANAFUDA_NO, N_p("input-name", "%p Hanafuda No"), input_seq(KEYCODE_N_INDEXED(0)) ) \ + CORE_INPUT_TYPES_END() + +#define CORE_INPUT_TYPES_GAMBLE \ + CORE_INPUT_TYPES_BEGIN(gamble) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, GAMBLE_KEYIN, N_p("input-name", "Key In"), input_seq(KEYCODE_Q) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, GAMBLE_KEYOUT, N_p("input-name", "Key Out"), input_seq(KEYCODE_W) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, GAMBLE_SERVICE, N_p("input-name", "Service"), input_seq(KEYCODE_9) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, GAMBLE_BOOK, N_p("input-name", "Book-Keeping"), input_seq(KEYCODE_0) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, GAMBLE_DOOR, N_p("input-name", "Door"), input_seq(KEYCODE_O) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, GAMBLE_PAYOUT, N_p("input-name", "Payout"), input_seq(KEYCODE_I) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, GAMBLE_BET, N_p("input-name", "Bet"), input_seq(KEYCODE_M) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, GAMBLE_DEAL, N_p("input-name", "Deal"), input_seq(KEYCODE_2) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, GAMBLE_STAND, N_p("input-name", "Stand"), input_seq(KEYCODE_L) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, GAMBLE_TAKE, N_p("input-name", "Take Score"), input_seq(KEYCODE_4) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, GAMBLE_D_UP, N_p("input-name", "Double Up"), input_seq(KEYCODE_3) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, GAMBLE_HALF, N_p("input-name", "Half Gamble"), input_seq(KEYCODE_D) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, GAMBLE_HIGH, N_p("input-name", "High"), input_seq(KEYCODE_A) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, GAMBLE_LOW, N_p("input-name", "Low"), input_seq(KEYCODE_S) ) \ + CORE_INPUT_TYPES_END() + +#define CORE_INPUT_TYPES_POKER \ + CORE_INPUT_TYPES_BEGIN(poker) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, POKER_HOLD1, N_p("input-name", "Hold 1"), input_seq(KEYCODE_Z) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, POKER_HOLD2, N_p("input-name", "Hold 2"), input_seq(KEYCODE_X) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, POKER_HOLD3, N_p("input-name", "Hold 3"), input_seq(KEYCODE_C) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, POKER_HOLD4, N_p("input-name", "Hold 4"), input_seq(KEYCODE_V) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, POKER_HOLD5, N_p("input-name", "Hold 5"), input_seq(KEYCODE_B) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, POKER_CANCEL, N_p("input-name", "Cancel"), input_seq(KEYCODE_N) ) \ + CORE_INPUT_TYPES_END() + +#define CORE_INPUT_TYPES_SLOT \ + CORE_INPUT_TYPES_BEGIN(slot) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, SLOT_STOP1, N_p("input-name", "Stop Reel 1"), input_seq(KEYCODE_X) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, SLOT_STOP2, N_p("input-name", "Stop Reel 2"), input_seq(KEYCODE_C) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, SLOT_STOP3, N_p("input-name", "Stop Reel 3"), input_seq(KEYCODE_V) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, SLOT_STOP4, N_p("input-name", "Stop Reel 4"), input_seq(KEYCODE_B) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, SLOT_STOP5, N_p("input-name", "Stop Reel 5"), input_seq(KEYCODE_N) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, SLOT_STOP_ALL, N_p("input-name", "Stop All Reels"), input_seq(KEYCODE_Z) ) \ + CORE_INPUT_TYPES_END() + +#define CORE_INPUT_TYPES_P2 \ + CORE_INPUT_TYPES_BEGIN(p2) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICK_UP, N_p("input-name", "%p Up"), input_seq(KEYCODE_R) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICK_DOWN, N_p("input-name", "%p Down"), input_seq(KEYCODE_F) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICK_LEFT, N_p("input-name", "%p Left"), input_seq(KEYCODE_D) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICK_RIGHT, N_p("input-name", "%p Right"), input_seq(KEYCODE_G) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICKRIGHT_UP, N_p("input-name", "%p Right Stick/Up"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICKRIGHT_DOWN, N_p("input-name", "%p Right Stick/Down"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICKRIGHT_LEFT, N_p("input-name", "%p Right Stick/Left"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICKRIGHT_RIGHT, N_p("input-name", "%p Right Stick/Right"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICKLEFT_UP, N_p("input-name", "%p Left Stick/Up"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICKLEFT_DOWN, N_p("input-name", "%p Left Stick/Down"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICKLEFT_LEFT, N_p("input-name", "%p Left Stick/Left"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICKLEFT_RIGHT, N_p("input-name", "%p Left Stick/Right"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON1, N_p("input-name", "%p Button 1"), input_seq(KEYCODE_A, input_seq::or_code, MOUSECODE_BUTTON1_INDEXED(1), input_seq::or_code, GUNCODE_BUTTON1_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON2, N_p("input-name", "%p Button 2"), input_seq(KEYCODE_S, input_seq::or_code, MOUSECODE_BUTTON3_INDEXED(1), input_seq::or_code, GUNCODE_BUTTON2_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON3, N_p("input-name", "%p Button 3"), input_seq(KEYCODE_Q, input_seq::or_code, MOUSECODE_BUTTON2_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON4, N_p("input-name", "%p Button 4"), input_seq(KEYCODE_W) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON5, N_p("input-name", "%p Button 5"), input_seq(KEYCODE_E) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON6, N_p("input-name", "%p Button 6"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON7, N_p("input-name", "%p Button 7"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON8, N_p("input-name", "%p Button 8"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON9, N_p("input-name", "%p Button 9"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON10, N_p("input-name", "%p Button 10"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON11, N_p("input-name", "%p Button 11"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON12, N_p("input-name", "%p Button 12"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON13, N_p("input-name", "%p Button 13"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON14, N_p("input-name", "%p Button 14"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON15, N_p("input-name", "%p Button 15"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON16, N_p("input-name", "%p Button 16"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, START, N_p("input-name", "%p Start"), input_seq(KEYCODE_2) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, SELECT, N_p("input-name", "%p Select"), input_seq(KEYCODE_6) ) \ + CORE_INPUT_TYPES_END() + +#define CORE_INPUT_TYPES_P2_MAHJONG \ + CORE_INPUT_TYPES_BEGIN(p2_mahjong) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_A, N_p("input-name", "%p Mahjong A"), input_seq(KEYCODE_A_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_B, N_p("input-name", "%p Mahjong B"), input_seq(KEYCODE_B_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_C, N_p("input-name", "%p Mahjong C"), input_seq(KEYCODE_C_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_D, N_p("input-name", "%p Mahjong D"), input_seq(KEYCODE_D_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_E, N_p("input-name", "%p Mahjong E"), input_seq(KEYCODE_E_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_F, N_p("input-name", "%p Mahjong F"), input_seq(KEYCODE_F_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_G, N_p("input-name", "%p Mahjong G"), input_seq(KEYCODE_G_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_H, N_p("input-name", "%p Mahjong H"), input_seq(KEYCODE_H_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_I, N_p("input-name", "%p Mahjong I"), input_seq(KEYCODE_I_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_J, N_p("input-name", "%p Mahjong J"), input_seq(KEYCODE_J_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_K, N_p("input-name", "%p Mahjong K"), input_seq(KEYCODE_K_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_L, N_p("input-name", "%p Mahjong L"), input_seq(KEYCODE_L_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_M, N_p("input-name", "%p Mahjong M"), input_seq(KEYCODE_M_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_N, N_p("input-name", "%p Mahjong N"), input_seq(KEYCODE_N_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_O, N_p("input-name", "%p Mahjong O"), input_seq(KEYCODE_O_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_P, N_p("input-name", "%p Mahjong P"), input_seq(KEYCODE_COLON_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_Q, N_p("input-name", "%p Mahjong Q"), input_seq(KEYCODE_Q_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_KAN, N_p("input-name", "%p Mahjong Kan"), input_seq(KEYCODE_LCONTROL_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_PON, N_p("input-name", "%p Mahjong Pon"), input_seq(KEYCODE_LALT_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_CHI, N_p("input-name", "%p Mahjong Chi"), input_seq(KEYCODE_SPACE_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_REACH, N_p("input-name", "%p Mahjong Reach"), input_seq(KEYCODE_LSHIFT_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_RON, N_p("input-name", "%p Mahjong Ron"), input_seq(KEYCODE_Z_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_FLIP_FLOP, N_p("input-name", "%p Mahjong Flip Flop"), input_seq(KEYCODE_Y_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_BET, N_p("input-name", "%p Mahjong Bet"), input_seq(KEYCODE_3_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_SCORE, N_p("input-name", "%p Mahjong Take Score"), input_seq(KEYCODE_RCONTROL_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_DOUBLE_UP, N_p("input-name", "%p Mahjong Double Up"), input_seq(KEYCODE_RSHIFT_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_BIG, N_p("input-name", "%p Mahjong Big"), input_seq(KEYCODE_ENTER_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_SMALL, N_p("input-name", "%p Mahjong Small"), input_seq(KEYCODE_BACKSPACE_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_LAST_CHANCE, N_p("input-name", "%p Mahjong Last Chance"), input_seq(KEYCODE_RALT_INDEXED(1)) ) \ + CORE_INPUT_TYPES_END() + +#define CORE_INPUT_TYPES_P2_HANAFUDA \ + CORE_INPUT_TYPES_BEGIN(p2_hanafuda) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, HANAFUDA_A, N_p("input-name", "%p Hanafuda A/1"), input_seq(KEYCODE_A_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, HANAFUDA_B, N_p("input-name", "%p Hanafuda B/2"), input_seq(KEYCODE_B_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, HANAFUDA_C, N_p("input-name", "%p Hanafuda C/3"), input_seq(KEYCODE_C_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, HANAFUDA_D, N_p("input-name", "%p Hanafuda D/4"), input_seq(KEYCODE_D_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, HANAFUDA_E, N_p("input-name", "%p Hanafuda E/5"), input_seq(KEYCODE_E_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, HANAFUDA_F, N_p("input-name", "%p Hanafuda F/6"), input_seq(KEYCODE_F_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, HANAFUDA_G, N_p("input-name", "%p Hanafuda G/7"), input_seq(KEYCODE_G_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, HANAFUDA_H, N_p("input-name", "%p Hanafuda H/8"), input_seq(KEYCODE_H_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, HANAFUDA_YES, N_p("input-name", "%p Hanafuda Yes"), input_seq(KEYCODE_M_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, HANAFUDA_NO, N_p("input-name", "%p Hanafuda No"), input_seq(KEYCODE_N_INDEXED(1)) ) \ + CORE_INPUT_TYPES_END() + +#define CORE_INPUT_TYPES_P3 \ + CORE_INPUT_TYPES_BEGIN(p3) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICK_UP, N_p("input-name", "%p Up"), input_seq(KEYCODE_I) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICK_DOWN, N_p("input-name", "%p Down"), input_seq(KEYCODE_K) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICK_LEFT, N_p("input-name", "%p Left"), input_seq(KEYCODE_J) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICK_RIGHT, N_p("input-name", "%p Right"), input_seq(KEYCODE_L) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICKRIGHT_UP, N_p("input-name", "%p Right Stick/Up"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICKRIGHT_DOWN, N_p("input-name", "%p Right Stick/Down"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICKRIGHT_LEFT, N_p("input-name", "%p Right Stick/Left"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICKRIGHT_RIGHT, N_p("input-name", "%p Right Stick/Right"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICKLEFT_UP, N_p("input-name", "%p Left Stick/Up"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICKLEFT_DOWN, N_p("input-name", "%p Left Stick/Down"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICKLEFT_LEFT, N_p("input-name", "%p Left Stick/Left"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICKLEFT_RIGHT, N_p("input-name", "%p Left Stick/Right"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON1, N_p("input-name", "%p Button 1"), input_seq(KEYCODE_RCONTROL, input_seq::or_code, GUNCODE_BUTTON1_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON2, N_p("input-name", "%p Button 2"), input_seq(KEYCODE_RSHIFT, input_seq::or_code, GUNCODE_BUTTON2_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON3, N_p("input-name", "%p Button 3"), input_seq(KEYCODE_ENTER) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON4, N_p("input-name", "%p Button 4"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON5, N_p("input-name", "%p Button 5"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON6, N_p("input-name", "%p Button 6"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON7, N_p("input-name", "%p Button 7"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON8, N_p("input-name", "%p Button 8"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON9, N_p("input-name", "%p Button 9"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON10, N_p("input-name", "%p Button 10"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON11, N_p("input-name", "%p Button 11"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON12, N_p("input-name", "%p Button 12"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON13, N_p("input-name", "%p Button 13"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON14, N_p("input-name", "%p Button 14"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON15, N_p("input-name", "%p Button 15"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON16, N_p("input-name", "%p Button 16"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, START, N_p("input-name", "%p Start"), input_seq(KEYCODE_3) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, SELECT, N_p("input-name", "%p Select"), input_seq(KEYCODE_7) ) \ + CORE_INPUT_TYPES_END() + +#define CORE_INPUT_TYPES_P3_MAHJONG \ + CORE_INPUT_TYPES_BEGIN(p3_mahjong) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, MAHJONG_A, N_p("input-name", "%p Mahjong A"), input_seq(KEYCODE_A_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, MAHJONG_B, N_p("input-name", "%p Mahjong B"), input_seq(KEYCODE_B_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, MAHJONG_C, N_p("input-name", "%p Mahjong C"), input_seq(KEYCODE_C_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, MAHJONG_D, N_p("input-name", "%p Mahjong D"), input_seq(KEYCODE_D_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, MAHJONG_E, N_p("input-name", "%p Mahjong E"), input_seq(KEYCODE_E_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, MAHJONG_F, N_p("input-name", "%p Mahjong F"), input_seq(KEYCODE_F_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, MAHJONG_G, N_p("input-name", "%p Mahjong G"), input_seq(KEYCODE_G_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, MAHJONG_H, N_p("input-name", "%p Mahjong H"), input_seq(KEYCODE_H_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, MAHJONG_I, N_p("input-name", "%p Mahjong I"), input_seq(KEYCODE_I_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, MAHJONG_J, N_p("input-name", "%p Mahjong J"), input_seq(KEYCODE_J_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, MAHJONG_K, N_p("input-name", "%p Mahjong K"), input_seq(KEYCODE_K_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, MAHJONG_L, N_p("input-name", "%p Mahjong L"), input_seq(KEYCODE_L_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, MAHJONG_M, N_p("input-name", "%p Mahjong M"), input_seq(KEYCODE_M_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, MAHJONG_N, N_p("input-name", "%p Mahjong N"), input_seq(KEYCODE_N_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, MAHJONG_O, N_p("input-name", "%p Mahjong O"), input_seq(KEYCODE_O_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, MAHJONG_P, N_p("input-name", "%p Mahjong P"), input_seq(KEYCODE_COLON_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, MAHJONG_Q, N_p("input-name", "%p Mahjong Q"), input_seq(KEYCODE_Q_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, MAHJONG_KAN, N_p("input-name", "%p Mahjong Kan"), input_seq(KEYCODE_LCONTROL_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, MAHJONG_PON, N_p("input-name", "%p Mahjong Pon"), input_seq(KEYCODE_LALT_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, MAHJONG_CHI, N_p("input-name", "%p Mahjong Chi"), input_seq(KEYCODE_SPACE_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, MAHJONG_REACH, N_p("input-name", "%p Mahjong Reach"), input_seq(KEYCODE_LSHIFT_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, MAHJONG_RON, N_p("input-name", "%p Mahjong Ron"), input_seq(KEYCODE_Z_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, MAHJONG_FLIP_FLOP, N_p("input-name", "%p Mahjong Flip Flop"), input_seq(KEYCODE_Y_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, MAHJONG_BET, N_p("input-name", "%p Mahjong Bet"), input_seq(KEYCODE_3_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, MAHJONG_SCORE, N_p("input-name", "%p Mahjong Take Score"), input_seq(KEYCODE_RCONTROL_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, MAHJONG_DOUBLE_UP, N_p("input-name", "%p Mahjong Double Up"), input_seq(KEYCODE_RSHIFT_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, MAHJONG_BIG, N_p("input-name", "%p Mahjong Big"), input_seq(KEYCODE_ENTER_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, MAHJONG_SMALL, N_p("input-name", "%p Mahjong Small"), input_seq(KEYCODE_BACKSPACE_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, MAHJONG_LAST_CHANCE, N_p("input-name", "%p Mahjong Last Chance"), input_seq(KEYCODE_RALT_INDEXED(2)) ) \ + CORE_INPUT_TYPES_END() + +#define CORE_INPUT_TYPES_P4 \ + CORE_INPUT_TYPES_BEGIN(p4) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICK_UP, N_p("input-name", "%p Up"), input_seq(KEYCODE_8_PAD) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICK_DOWN, N_p("input-name", "%p Down"), input_seq(KEYCODE_2_PAD) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICK_LEFT, N_p("input-name", "%p Left"), input_seq(KEYCODE_4_PAD) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICK_RIGHT, N_p("input-name", "%p Right"), input_seq(KEYCODE_6_PAD) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICKRIGHT_UP, N_p("input-name", "%p Right Stick/Up"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICKRIGHT_DOWN, N_p("input-name", "%p Right Stick/Down"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICKRIGHT_LEFT, N_p("input-name", "%p Right Stick/Left"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICKRIGHT_RIGHT, N_p("input-name", "%p Right Stick/Right"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICKLEFT_UP, N_p("input-name", "%p Left Stick/Up"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICKLEFT_DOWN, N_p("input-name", "%p Left Stick/Down"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICKLEFT_LEFT, N_p("input-name", "%p Left Stick/Left"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICKLEFT_RIGHT, N_p("input-name", "%p Left Stick/Right"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON1, N_p("input-name", "%p Button 1"), input_seq(KEYCODE_0_PAD) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON2, N_p("input-name", "%p Button 2"), input_seq(KEYCODE_DEL_PAD) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON3, N_p("input-name", "%p Button 3"), input_seq(KEYCODE_ENTER_PAD) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON4, N_p("input-name", "%p Button 4"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON5, N_p("input-name", "%p Button 5"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON6, N_p("input-name", "%p Button 6"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON7, N_p("input-name", "%p Button 7"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON8, N_p("input-name", "%p Button 8"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON9, N_p("input-name", "%p Button 9"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON10, N_p("input-name", "%p Button 10"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON11, N_p("input-name", "%p Button 11"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON12, N_p("input-name", "%p Button 12"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON13, N_p("input-name", "%p Button 13"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON14, N_p("input-name", "%p Button 14"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON15, N_p("input-name", "%p Button 15"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON16, N_p("input-name", "%p Button 16"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, START, N_p("input-name", "%p Start"), input_seq(KEYCODE_4) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, SELECT, N_p("input-name", "%p Select"), input_seq(KEYCODE_8) ) \ + CORE_INPUT_TYPES_END() + +#define CORE_INPUT_TYPES_P4_MAHJONG \ + CORE_INPUT_TYPES_BEGIN(p4_mahjong) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, MAHJONG_A, N_p("input-name", "%p Mahjong A"), input_seq(KEYCODE_A_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, MAHJONG_B, N_p("input-name", "%p Mahjong B"), input_seq(KEYCODE_B_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, MAHJONG_C, N_p("input-name", "%p Mahjong C"), input_seq(KEYCODE_C_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, MAHJONG_D, N_p("input-name", "%p Mahjong D"), input_seq(KEYCODE_D_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, MAHJONG_E, N_p("input-name", "%p Mahjong E"), input_seq(KEYCODE_E_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, MAHJONG_F, N_p("input-name", "%p Mahjong F"), input_seq(KEYCODE_F_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, MAHJONG_G, N_p("input-name", "%p Mahjong G"), input_seq(KEYCODE_G_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, MAHJONG_H, N_p("input-name", "%p Mahjong H"), input_seq(KEYCODE_H_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, MAHJONG_I, N_p("input-name", "%p Mahjong I"), input_seq(KEYCODE_I_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, MAHJONG_J, N_p("input-name", "%p Mahjong J"), input_seq(KEYCODE_J_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, MAHJONG_K, N_p("input-name", "%p Mahjong K"), input_seq(KEYCODE_K_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, MAHJONG_L, N_p("input-name", "%p Mahjong L"), input_seq(KEYCODE_L_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, MAHJONG_M, N_p("input-name", "%p Mahjong M"), input_seq(KEYCODE_M_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, MAHJONG_N, N_p("input-name", "%p Mahjong N"), input_seq(KEYCODE_N_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, MAHJONG_O, N_p("input-name", "%p Mahjong O"), input_seq(KEYCODE_O_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, MAHJONG_P, N_p("input-name", "%p Mahjong P"), input_seq(KEYCODE_COLON_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, MAHJONG_Q, N_p("input-name", "%p Mahjong Q"), input_seq(KEYCODE_Q_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, MAHJONG_KAN, N_p("input-name", "%p Mahjong Kan"), input_seq(KEYCODE_LCONTROL_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, MAHJONG_PON, N_p("input-name", "%p Mahjong Pon"), input_seq(KEYCODE_LALT_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, MAHJONG_CHI, N_p("input-name", "%p Mahjong Chi"), input_seq(KEYCODE_SPACE_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, MAHJONG_REACH, N_p("input-name", "%p Mahjong Reach"), input_seq(KEYCODE_LSHIFT_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, MAHJONG_RON, N_p("input-name", "%p Mahjong Ron"), input_seq(KEYCODE_Z_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, MAHJONG_FLIP_FLOP, N_p("input-name", "%p Mahjong Flip Flop"), input_seq(KEYCODE_Y_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, MAHJONG_BET, N_p("input-name", "%p Mahjong Bet"), input_seq(KEYCODE_3_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, MAHJONG_SCORE, N_p("input-name", "%p Mahjong Take Score"), input_seq(KEYCODE_RCONTROL_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, MAHJONG_DOUBLE_UP, N_p("input-name", "%p Mahjong Double Up"), input_seq(KEYCODE_RSHIFT_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, MAHJONG_BIG, N_p("input-name", "%p Mahjong Big"), input_seq(KEYCODE_ENTER_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, MAHJONG_SMALL, N_p("input-name", "%p Mahjong Small"), input_seq(KEYCODE_BACKSPACE_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, MAHJONG_LAST_CHANCE, N_p("input-name", "%p Mahjong Last Chance"), input_seq(KEYCODE_RALT_INDEXED(3)) ) \ + CORE_INPUT_TYPES_END() + +#define CORE_INPUT_TYPES_P5 \ + CORE_INPUT_TYPES_BEGIN(p5) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICK_UP, N_p("input-name", "%p Up"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICK_DOWN, N_p("input-name", "%p Down"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICK_LEFT, N_p("input-name", "%p Left"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICK_RIGHT, N_p("input-name", "%p Right"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICKRIGHT_UP, N_p("input-name", "%p Right Stick/Up"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICKRIGHT_DOWN, N_p("input-name", "%p Right Stick/Down"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICKRIGHT_LEFT, N_p("input-name", "%p Right Stick/Left"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICKRIGHT_RIGHT, N_p("input-name", "%p Right Stick/Right"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICKLEFT_UP, N_p("input-name", "%p Left Stick/Up"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICKLEFT_DOWN, N_p("input-name", "%p Left Stick/Down"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICKLEFT_LEFT, N_p("input-name", "%p Left Stick/Left"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICKLEFT_RIGHT, N_p("input-name", "%p Left Stick/Right"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON1, N_p("input-name", "%p Button 1"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON2, N_p("input-name", "%p Button 2"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON3, N_p("input-name", "%p Button 3"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON4, N_p("input-name", "%p Button 4"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON5, N_p("input-name", "%p Button 5"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON6, N_p("input-name", "%p Button 6"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON7, N_p("input-name", "%p Button 7"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON8, N_p("input-name", "%p Button 8"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON9, N_p("input-name", "%p Button 9"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON10, N_p("input-name", "%p Button 10"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON11, N_p("input-name", "%p Button 11"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON12, N_p("input-name", "%p Button 12"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON13, N_p("input-name", "%p Button 13"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON14, N_p("input-name", "%p Button 14"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON15, N_p("input-name", "%p Button 15"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON16, N_p("input-name", "%p Button 16"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, START, N_p("input-name", "%p Start"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, SELECT, N_p("input-name", "%p Select"), input_seq() ) \ + CORE_INPUT_TYPES_END() + +#define CORE_INPUT_TYPES_P6 \ + CORE_INPUT_TYPES_BEGIN(p6) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICK_UP, N_p("input-name", "%p Up"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICK_DOWN, N_p("input-name", "%p Down"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICK_LEFT, N_p("input-name", "%p Left"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICK_RIGHT, N_p("input-name", "%p Right"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICKRIGHT_UP, N_p("input-name", "%p Right Stick/Up"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICKRIGHT_DOWN, N_p("input-name", "%p Right Stick/Down"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICKRIGHT_LEFT, N_p("input-name", "%p Right Stick/Left"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICKRIGHT_RIGHT, N_p("input-name", "%p Right Stick/Right"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICKLEFT_UP, N_p("input-name", "%p Left Stick/Up"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICKLEFT_DOWN, N_p("input-name", "%p Left Stick/Down"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICKLEFT_LEFT, N_p("input-name", "%p Left Stick/Left"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICKLEFT_RIGHT, N_p("input-name", "%p Left Stick/Right"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON1, N_p("input-name", "%p Button 1"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON2, N_p("input-name", "%p Button 2"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON3, N_p("input-name", "%p Button 3"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON4, N_p("input-name", "%p Button 4"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON5, N_p("input-name", "%p Button 5"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON6, N_p("input-name", "%p Button 6"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON7, N_p("input-name", "%p Button 7"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON8, N_p("input-name", "%p Button 8"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON9, N_p("input-name", "%p Button 9"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON10, N_p("input-name", "%p Button 10"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON11, N_p("input-name", "%p Button 11"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON12, N_p("input-name", "%p Button 12"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON13, N_p("input-name", "%p Button 13"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON14, N_p("input-name", "%p Button 14"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON15, N_p("input-name", "%p Button 15"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON16, N_p("input-name", "%p Button 16"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, START, N_p("input-name", "%p Start"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, SELECT, N_p("input-name", "%p Select"), input_seq() ) \ + CORE_INPUT_TYPES_END() + +#define CORE_INPUT_TYPES_P7 \ + CORE_INPUT_TYPES_BEGIN(p7) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICK_UP, N_p("input-name", "%p Up"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICK_DOWN, N_p("input-name", "%p Down"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICK_LEFT, N_p("input-name", "%p Left"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICK_RIGHT, N_p("input-name", "%p Right"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICKRIGHT_UP, N_p("input-name", "%p Right Stick/Up"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICKRIGHT_DOWN, N_p("input-name", "%p Right Stick/Down"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICKRIGHT_LEFT, N_p("input-name", "%p Right Stick/Left"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICKRIGHT_RIGHT, N_p("input-name", "%p Right Stick/Right"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICKLEFT_UP, N_p("input-name", "%p Left Stick/Up"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICKLEFT_DOWN, N_p("input-name", "%p Left Stick/Down"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICKLEFT_LEFT, N_p("input-name", "%p Left Stick/Left"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICKLEFT_RIGHT, N_p("input-name", "%p Left Stick/Right"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON1, N_p("input-name", "%p Button 1"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON2, N_p("input-name", "%p Button 2"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON3, N_p("input-name", "%p Button 3"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON4, N_p("input-name", "%p Button 4"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON5, N_p("input-name", "%p Button 5"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON6, N_p("input-name", "%p Button 6"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON7, N_p("input-name", "%p Button 7"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON8, N_p("input-name", "%p Button 8"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON9, N_p("input-name", "%p Button 9"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON10, N_p("input-name", "%p Button 10"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON11, N_p("input-name", "%p Button 11"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON12, N_p("input-name", "%p Button 12"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON13, N_p("input-name", "%p Button 13"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON14, N_p("input-name", "%p Button 14"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON15, N_p("input-name", "%p Button 15"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON16, N_p("input-name", "%p Button 16"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, START, N_p("input-name", "%p Start"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, SELECT, N_p("input-name", "%p Select"), input_seq() ) \ + CORE_INPUT_TYPES_END() + +#define CORE_INPUT_TYPES_P8 \ + CORE_INPUT_TYPES_BEGIN(p8) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICK_UP, N_p("input-name", "%p Up"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICK_DOWN, N_p("input-name", "%p Down"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICK_LEFT, N_p("input-name", "%p Left"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICK_RIGHT, N_p("input-name", "%p Right"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICKRIGHT_UP, N_p("input-name", "%p Right Stick/Up"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICKRIGHT_DOWN, N_p("input-name", "%p Right Stick/Down"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICKRIGHT_LEFT, N_p("input-name", "%p Right Stick/Left"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICKRIGHT_RIGHT, N_p("input-name", "%p Right Stick/Right"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICKLEFT_UP, N_p("input-name", "%p Left Stick/Up"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICKLEFT_DOWN, N_p("input-name", "%p Left Stick/Down"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICKLEFT_LEFT, N_p("input-name", "%p Left Stick/Left"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICKLEFT_RIGHT, N_p("input-name", "%p Left Stick/Right"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON1, N_p("input-name", "%p Button 1"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON2, N_p("input-name", "%p Button 2"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON3, N_p("input-name", "%p Button 3"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON4, N_p("input-name", "%p Button 4"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON5, N_p("input-name", "%p Button 5"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON6, N_p("input-name", "%p Button 6"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON7, N_p("input-name", "%p Button 7"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON8, N_p("input-name", "%p Button 8"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON9, N_p("input-name", "%p Button 9"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON10, N_p("input-name", "%p Button 10"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON11, N_p("input-name", "%p Button 11"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON12, N_p("input-name", "%p Button 12"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON13, N_p("input-name", "%p Button 13"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON14, N_p("input-name", "%p Button 14"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON15, N_p("input-name", "%p Button 15"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON16, N_p("input-name", "%p Button 16"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, START, N_p("input-name", "%p Start"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, SELECT, N_p("input-name", "%p Select"), input_seq() ) \ + CORE_INPUT_TYPES_END() + +#define CORE_INPUT_TYPES_P9 \ + CORE_INPUT_TYPES_BEGIN(p9) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICK_UP, N_p("input-name", "%p Up"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICK_DOWN, N_p("input-name", "%p Down"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICK_LEFT, N_p("input-name", "%p Left"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICK_RIGHT, N_p("input-name", "%p Right"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICKRIGHT_UP, N_p("input-name", "%p Right Stick/Up"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICKRIGHT_DOWN, N_p("input-name", "%p Right Stick/Down"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICKRIGHT_LEFT, N_p("input-name", "%p Right Stick/Left"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICKRIGHT_RIGHT, N_p("input-name", "%p Right Stick/Right"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICKLEFT_UP, N_p("input-name", "%p Left Stick/Up"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICKLEFT_DOWN, N_p("input-name", "%p Left Stick/Down"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICKLEFT_LEFT, N_p("input-name", "%p Left Stick/Left"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICKLEFT_RIGHT, N_p("input-name", "%p Left Stick/Right"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON1, N_p("input-name", "%p Button 1"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON2, N_p("input-name", "%p Button 2"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON3, N_p("input-name", "%p Button 3"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON4, N_p("input-name", "%p Button 4"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON5, N_p("input-name", "%p Button 5"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON6, N_p("input-name", "%p Button 6"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON7, N_p("input-name", "%p Button 7"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON8, N_p("input-name", "%p Button 8"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON9, N_p("input-name", "%p Button 9"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON10, N_p("input-name", "%p Button 10"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON11, N_p("input-name", "%p Button 11"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON12, N_p("input-name", "%p Button 12"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON13, N_p("input-name", "%p Button 13"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON14, N_p("input-name", "%p Button 14"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON15, N_p("input-name", "%p Button 15"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON16, N_p("input-name", "%p Button 16"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, START, N_p("input-name", "%p Start"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, SELECT, N_p("input-name", "%p Select"), input_seq() ) \ + CORE_INPUT_TYPES_END() + +#define CORE_INPUT_TYPES_P10 \ + CORE_INPUT_TYPES_BEGIN(p10) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICK_UP, N_p("input-name", "%p Up"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICK_DOWN, N_p("input-name", "%p Down"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICK_LEFT, N_p("input-name", "%p Left"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICK_RIGHT, N_p("input-name", "%p Right"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICKRIGHT_UP, N_p("input-name", "%p Right Stick/Up"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICKRIGHT_DOWN, N_p("input-name", "%p Right Stick/Down"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICKRIGHT_LEFT, N_p("input-name", "%p Right Stick/Left"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICKRIGHT_RIGHT, N_p("input-name", "%p Right Stick/Right"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICKLEFT_UP, N_p("input-name", "%p Left Stick/Up"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICKLEFT_DOWN, N_p("input-name", "%p Left Stick/Down"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICKLEFT_LEFT, N_p("input-name", "%p Left Stick/Left"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICKLEFT_RIGHT, N_p("input-name", "%p Left Stick/Right"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON1, N_p("input-name", "%p Button 1"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON2, N_p("input-name", "%p Button 2"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON3, N_p("input-name", "%p Button 3"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON4, N_p("input-name", "%p Button 4"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON5, N_p("input-name", "%p Button 5"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON6, N_p("input-name", "%p Button 6"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON7, N_p("input-name", "%p Button 7"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON8, N_p("input-name", "%p Button 8"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON9, N_p("input-name", "%p Button 9"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON10, N_p("input-name", "%p Button 10"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON11, N_p("input-name", "%p Button 11"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON12, N_p("input-name", "%p Button 12"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON13, N_p("input-name", "%p Button 13"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON14, N_p("input-name", "%p Button 14"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON15, N_p("input-name", "%p Button 15"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON16, N_p("input-name", "%p Button 16"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, START, N_p("input-name", "%p Start"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, SELECT, N_p("input-name", "%p Select"), input_seq() ) \ + CORE_INPUT_TYPES_END() + +#define CORE_INPUT_TYPES_START \ + CORE_INPUT_TYPES_BEGIN(start) \ + INPUT_PORT_DIGITAL_TYPE( 0, OTHER, START1, N_p("input-name", "1 Player Start"), input_seq(KEYCODE_1, input_seq::or_code, JOYCODE_START_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, OTHER, START2, N_p("input-name", "2 Players Start"), input_seq(KEYCODE_2, input_seq::or_code, JOYCODE_START_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, OTHER, START3, N_p("input-name", "3 Players Start"), input_seq(KEYCODE_3, input_seq::or_code, JOYCODE_START_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, OTHER, START4, N_p("input-name", "4 Players Start"), input_seq(KEYCODE_4, input_seq::or_code, JOYCODE_START_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, OTHER, START5, N_p("input-name", "5 Players Start"), input_seq(JOYCODE_START_INDEXED(4)) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, OTHER, START6, N_p("input-name", "6 Players Start"), input_seq(JOYCODE_START_INDEXED(5)) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, OTHER, START7, N_p("input-name", "7 Players Start"), input_seq(JOYCODE_START_INDEXED(6)) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, OTHER, START8, N_p("input-name", "8 Players Start"), input_seq(JOYCODE_START_INDEXED(7)) ) \ + CORE_INPUT_TYPES_END() + +#define CORE_INPUT_TYPES_COIN \ + CORE_INPUT_TYPES_BEGIN(coin) \ + INPUT_PORT_DIGITAL_TYPE( 0, OTHER, COIN1, N_p("input-name", "Coin 1"), input_seq(KEYCODE_5, input_seq::or_code, JOYCODE_SELECT_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, OTHER, COIN2, N_p("input-name", "Coin 2"), input_seq(KEYCODE_6, input_seq::or_code, JOYCODE_SELECT_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, OTHER, COIN3, N_p("input-name", "Coin 3"), input_seq(KEYCODE_7, input_seq::or_code, JOYCODE_SELECT_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, OTHER, COIN4, N_p("input-name", "Coin 4"), input_seq(KEYCODE_8, input_seq::or_code, JOYCODE_SELECT_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, OTHER, COIN5, N_p("input-name", "Coin 5"), input_seq(JOYCODE_SELECT_INDEXED(4)) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, OTHER, COIN6, N_p("input-name", "Coin 6"), input_seq(JOYCODE_SELECT_INDEXED(5)) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, OTHER, COIN7, N_p("input-name", "Coin 7"), input_seq(JOYCODE_SELECT_INDEXED(6)) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, OTHER, COIN8, N_p("input-name", "Coin 8"), input_seq(JOYCODE_SELECT_INDEXED(7)) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, OTHER, COIN9, N_p("input-name", "Coin 9"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 0, OTHER, COIN10, N_p("input-name", "Coin 10"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 0, OTHER, COIN11, N_p("input-name", "Coin 11"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 0, OTHER, COIN12, N_p("input-name", "Coin 12"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 0, OTHER, BILL1, N_p("input-name", "Banknote 1"), input_seq(KEYCODE_BACKSPACE) ) \ + CORE_INPUT_TYPES_END() + +#define CORE_INPUT_TYPES_SERVICE \ + CORE_INPUT_TYPES_BEGIN(service) \ + INPUT_PORT_DIGITAL_TYPE( 0, OTHER, SERVICE1, N_p("input-name", "Service 1"), input_seq(KEYCODE_9) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, OTHER, SERVICE2, N_p("input-name", "Service 2"), input_seq(KEYCODE_0) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, OTHER, SERVICE3, N_p("input-name", "Service 3"), input_seq(KEYCODE_MINUS) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, OTHER, SERVICE4, N_p("input-name", "Service 4"), input_seq(KEYCODE_EQUALS) ) \ + CORE_INPUT_TYPES_END() + +#define CORE_INPUT_TYPES_TILT \ + CORE_INPUT_TYPES_BEGIN(tilt) \ + INPUT_PORT_DIGITAL_TYPE( 0, OTHER, TILT1, N_p("input-name", "Tilt 1"), input_seq(KEYCODE_T) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, OTHER, TILT2, N_p("input-name", "Tilt 2"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 0, OTHER, TILT3, N_p("input-name", "Tilt 3"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 0, OTHER, TILT4, N_p("input-name", "Tilt 4"), input_seq() ) \ + CORE_INPUT_TYPES_END() + +#define CORE_INPUT_TYPES_OTHER \ + CORE_INPUT_TYPES_BEGIN(other) \ + INPUT_PORT_DIGITAL_TYPE( 0, OTHER, POWER_ON, N_p("input-name", "Power On"), input_seq(KEYCODE_F1) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, OTHER, POWER_OFF, N_p("input-name", "Power Off"), input_seq(KEYCODE_F2) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, OTHER, SERVICE, N_p("input-name", "Service"), input_seq(KEYCODE_F2) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, OTHER, TILT, N_p("input-name", "Tilt"), input_seq(KEYCODE_T) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, OTHER, INTERLOCK, N_p("input-name", "Door Interlock"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 0, OTHER, MEMORY_RESET, N_p("input-name", "Memory Reset"), input_seq(KEYCODE_F1) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, OTHER, VOLUME_DOWN, N_p("input-name", "Volume Down"), input_seq(KEYCODE_MINUS) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, OTHER, VOLUME_UP, N_p("input-name", "Volume Up"), input_seq(KEYCODE_EQUALS) ) \ + CORE_INPUT_TYPES_END() + +#define CORE_INPUT_TYPES_PEDAL \ + CORE_INPUT_TYPES_BEGIN(pedal) \ + INPUT_PORT_ANALOG_TYPE( 1, PLAYER1, PEDAL, N_p("input-name", "%p Pedal 1"), input_seq(), input_seq(), input_seq(KEYCODE_LCONTROL) ) \ + INPUT_PORT_ANALOG_TYPE( 2, PLAYER2, PEDAL, N_p("input-name", "%p Pedal 1"), input_seq(), input_seq(), input_seq(KEYCODE_A) ) \ + INPUT_PORT_ANALOG_TYPE( 3, PLAYER3, PEDAL, N_p("input-name", "%p Pedal 1"), input_seq(), input_seq(), input_seq(KEYCODE_RCONTROL) ) \ + INPUT_PORT_ANALOG_TYPE( 4, PLAYER4, PEDAL, N_p("input-name", "%p Pedal 1"), input_seq(), input_seq(), input_seq(KEYCODE_0_PAD) ) \ + INPUT_PORT_ANALOG_TYPE( 5, PLAYER5, PEDAL, N_p("input-name", "%p Pedal 1"), input_seq(), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 6, PLAYER6, PEDAL, N_p("input-name", "%p Pedal 1"), input_seq(), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 7, PLAYER7, PEDAL, N_p("input-name", "%p Pedal 1"), input_seq(), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 8, PLAYER8, PEDAL, N_p("input-name", "%p Pedal 1"), input_seq(), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 9, PLAYER9, PEDAL, N_p("input-name", "%p Pedal 1"), input_seq(), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 10, PLAYER10, PEDAL, N_p("input-name", "%p Pedal 1"), input_seq(), input_seq(), input_seq() ) \ + CORE_INPUT_TYPES_END() + +#define CORE_INPUT_TYPES_PEDAL2 \ + CORE_INPUT_TYPES_BEGIN(pedal2) \ + INPUT_PORT_ANALOG_TYPE( 1, PLAYER1, PEDAL2, N_p("input-name", "%p Pedal 2"), input_seq(), input_seq(), input_seq(KEYCODE_LALT) ) \ + INPUT_PORT_ANALOG_TYPE( 2, PLAYER2, PEDAL2, N_p("input-name", "%p Pedal 2"), input_seq(), input_seq(), input_seq(KEYCODE_S) ) \ + INPUT_PORT_ANALOG_TYPE( 3, PLAYER3, PEDAL2, N_p("input-name", "%p Pedal 2"), input_seq(), input_seq(), input_seq(KEYCODE_RSHIFT) ) \ + INPUT_PORT_ANALOG_TYPE( 4, PLAYER4, PEDAL2, N_p("input-name", "%p Pedal 2"), input_seq(), input_seq(), input_seq(KEYCODE_DEL_PAD) ) \ + INPUT_PORT_ANALOG_TYPE( 5, PLAYER5, PEDAL2, N_p("input-name", "%p Pedal 2"), input_seq(), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 6, PLAYER6, PEDAL2, N_p("input-name", "%p Pedal 2"), input_seq(), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 7, PLAYER7, PEDAL2, N_p("input-name", "%p Pedal 2"), input_seq(), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 8, PLAYER8, PEDAL2, N_p("input-name", "%p Pedal 2"), input_seq(), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 9, PLAYER9, PEDAL2, N_p("input-name", "%p Pedal 2"), input_seq(), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 10, PLAYER10, PEDAL2, N_p("input-name", "%p Pedal 2"), input_seq(), input_seq(), input_seq() ) \ + CORE_INPUT_TYPES_END() + +#define CORE_INPUT_TYPES_PEDAL3 \ + CORE_INPUT_TYPES_BEGIN(pedal3) \ + INPUT_PORT_ANALOG_TYPE( 1, PLAYER1, PEDAL3, N_p("input-name", "%p Pedal 3"), input_seq(), input_seq(), input_seq(KEYCODE_SPACE) ) \ + INPUT_PORT_ANALOG_TYPE( 2, PLAYER2, PEDAL3, N_p("input-name", "%p Pedal 3"), input_seq(), input_seq(), input_seq(KEYCODE_Q) ) \ + INPUT_PORT_ANALOG_TYPE( 3, PLAYER3, PEDAL3, N_p("input-name", "%p Pedal 3"), input_seq(), input_seq(), input_seq(KEYCODE_ENTER) ) \ + INPUT_PORT_ANALOG_TYPE( 4, PLAYER4, PEDAL3, N_p("input-name", "%p Pedal 3"), input_seq(), input_seq(), input_seq(KEYCODE_ENTER_PAD) ) \ + INPUT_PORT_ANALOG_TYPE( 5, PLAYER5, PEDAL3, N_p("input-name", "%p Pedal 3"), input_seq(), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 6, PLAYER6, PEDAL3, N_p("input-name", "%p Pedal 3"), input_seq(), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 7, PLAYER7, PEDAL3, N_p("input-name", "%p Pedal 3"), input_seq(), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 8, PLAYER8, PEDAL3, N_p("input-name", "%p Pedal 3"), input_seq(), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 9, PLAYER9, PEDAL3, N_p("input-name", "%p Pedal 3"), input_seq(), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 10, PLAYER10, PEDAL3, N_p("input-name", "%p Pedal 3"), input_seq(), input_seq(), input_seq() ) \ + CORE_INPUT_TYPES_END() + +#define CORE_INPUT_TYPES_PADDLE \ + CORE_INPUT_TYPES_BEGIN(paddle) \ + INPUT_PORT_ANALOG_TYPE( 1, PLAYER1, PADDLE, N_p("input-name", "Paddle"), input_seq(MOUSECODE_X_INDEXED(0)), input_seq(KEYCODE_LEFT), input_seq(KEYCODE_RIGHT) ) \ + INPUT_PORT_ANALOG_TYPE( 2, PLAYER2, PADDLE, N_p("input-name", "Paddle 2"), input_seq(MOUSECODE_X_INDEXED(1)), input_seq(KEYCODE_D), input_seq(KEYCODE_G) ) \ + INPUT_PORT_ANALOG_TYPE( 3, PLAYER3, PADDLE, N_p("input-name", "Paddle 3"), input_seq(MOUSECODE_X_INDEXED(2)), input_seq(KEYCODE_J), input_seq(KEYCODE_L) ) \ + INPUT_PORT_ANALOG_TYPE( 4, PLAYER4, PADDLE, N_p("input-name", "Paddle 4"), input_seq(MOUSECODE_X_INDEXED(3)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 5, PLAYER5, PADDLE, N_p("input-name", "Paddle 5"), input_seq(MOUSECODE_X_INDEXED(4)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 6, PLAYER6, PADDLE, N_p("input-name", "Paddle 6"), input_seq(MOUSECODE_X_INDEXED(5)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 7, PLAYER7, PADDLE, N_p("input-name", "Paddle 7"), input_seq(MOUSECODE_X_INDEXED(6)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 8, PLAYER8, PADDLE, N_p("input-name", "Paddle 8"), input_seq(MOUSECODE_X_INDEXED(7)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 9, PLAYER9, PADDLE, N_p("input-name", "Paddle 9"), input_seq(MOUSECODE_X_INDEXED(8)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 10, PLAYER10, PADDLE, N_p("input-name", "Paddle 10"), input_seq(MOUSECODE_X_INDEXED(9)), input_seq(), input_seq() ) \ + CORE_INPUT_TYPES_END() + +#define CORE_INPUT_TYPES_PADDLE_V \ + CORE_INPUT_TYPES_BEGIN(paddle_v) \ + INPUT_PORT_ANALOG_TYPE( 1, PLAYER1, PADDLE_V, N_p("input-name", "Paddle V"), input_seq(MOUSECODE_Y_INDEXED(0)), input_seq(KEYCODE_UP), input_seq(KEYCODE_DOWN) ) \ + INPUT_PORT_ANALOG_TYPE( 2, PLAYER2, PADDLE_V, N_p("input-name", "Paddle V 2"), input_seq(MOUSECODE_Y_INDEXED(1)), input_seq(KEYCODE_R), input_seq(KEYCODE_F) ) \ + INPUT_PORT_ANALOG_TYPE( 3, PLAYER3, PADDLE_V, N_p("input-name", "Paddle V 3"), input_seq(MOUSECODE_Y_INDEXED(2)), input_seq(KEYCODE_I), input_seq(KEYCODE_K) ) \ + INPUT_PORT_ANALOG_TYPE( 4, PLAYER4, PADDLE_V, N_p("input-name", "Paddle V 4"), input_seq(MOUSECODE_Y_INDEXED(3)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 5, PLAYER5, PADDLE_V, N_p("input-name", "Paddle V 5"), input_seq(MOUSECODE_Y_INDEXED(4)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 6, PLAYER6, PADDLE_V, N_p("input-name", "Paddle V 6"), input_seq(MOUSECODE_Y_INDEXED(5)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 7, PLAYER7, PADDLE_V, N_p("input-name", "Paddle V 7"), input_seq(MOUSECODE_Y_INDEXED(6)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 8, PLAYER8, PADDLE_V, N_p("input-name", "Paddle V 8"), input_seq(MOUSECODE_Y_INDEXED(7)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 9, PLAYER9, PADDLE_V, N_p("input-name", "Paddle V 9"), input_seq(MOUSECODE_Y_INDEXED(8)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 10, PLAYER10, PADDLE_V, N_p("input-name", "Paddle V 10"), input_seq(MOUSECODE_Y_INDEXED(9)), input_seq(), input_seq() ) \ + CORE_INPUT_TYPES_END() + +#define CORE_INPUT_TYPES_POSITIONAL \ + CORE_INPUT_TYPES_BEGIN(positional) \ + INPUT_PORT_ANALOG_TYPE( 1, PLAYER1, POSITIONAL, N_p("input-name", "Positional"), input_seq(MOUSECODE_X_INDEXED(0)), input_seq(KEYCODE_LEFT), input_seq(KEYCODE_RIGHT) ) \ + INPUT_PORT_ANALOG_TYPE( 2, PLAYER2, POSITIONAL, N_p("input-name", "Positional 2"), input_seq(MOUSECODE_X_INDEXED(1)), input_seq(KEYCODE_D), input_seq(KEYCODE_G) ) \ + INPUT_PORT_ANALOG_TYPE( 3, PLAYER3, POSITIONAL, N_p("input-name", "Positional 3"), input_seq(MOUSECODE_X_INDEXED(2)), input_seq(KEYCODE_J), input_seq(KEYCODE_L) ) \ + INPUT_PORT_ANALOG_TYPE( 4, PLAYER4, POSITIONAL, N_p("input-name", "Positional 4"), input_seq(MOUSECODE_X_INDEXED(3)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 5, PLAYER5, POSITIONAL, N_p("input-name", "Positional 5"), input_seq(MOUSECODE_X_INDEXED(4)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 6, PLAYER6, POSITIONAL, N_p("input-name", "Positional 6"), input_seq(MOUSECODE_X_INDEXED(5)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 7, PLAYER7, POSITIONAL, N_p("input-name", "Positional 7"), input_seq(MOUSECODE_X_INDEXED(6)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 8, PLAYER8, POSITIONAL, N_p("input-name", "Positional 8"), input_seq(MOUSECODE_X_INDEXED(7)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 9, PLAYER9, POSITIONAL, N_p("input-name", "Positional 9"), input_seq(MOUSECODE_X_INDEXED(8)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 10, PLAYER10, POSITIONAL, N_p("input-name", "Positional 10"), input_seq(MOUSECODE_X_INDEXED(9)), input_seq(), input_seq() ) \ + CORE_INPUT_TYPES_END() + +#define CORE_INPUT_TYPES_POSITIONAL_V \ + CORE_INPUT_TYPES_BEGIN(positional_v) \ + INPUT_PORT_ANALOG_TYPE( 1, PLAYER1, POSITIONAL_V, N_p("input-name", "Positional V"), input_seq(MOUSECODE_Y_INDEXED(0)), input_seq(KEYCODE_UP), input_seq(KEYCODE_DOWN) ) \ + INPUT_PORT_ANALOG_TYPE( 2, PLAYER2, POSITIONAL_V, N_p("input-name", "Positional V 2"), input_seq(MOUSECODE_Y_INDEXED(1)), input_seq(KEYCODE_R), input_seq(KEYCODE_F) ) \ + INPUT_PORT_ANALOG_TYPE( 3, PLAYER3, POSITIONAL_V, N_p("input-name", "Positional V 3"), input_seq(MOUSECODE_Y_INDEXED(2)), input_seq(KEYCODE_I), input_seq(KEYCODE_K) ) \ + INPUT_PORT_ANALOG_TYPE( 4, PLAYER4, POSITIONAL_V, N_p("input-name", "Positional V 4"), input_seq(MOUSECODE_Y_INDEXED(3)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 5, PLAYER5, POSITIONAL_V, N_p("input-name", "Positional V 5"), input_seq(MOUSECODE_Y_INDEXED(4)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 6, PLAYER6, POSITIONAL_V, N_p("input-name", "Positional V 6"), input_seq(MOUSECODE_Y_INDEXED(5)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 7, PLAYER7, POSITIONAL_V, N_p("input-name", "Positional V 7"), input_seq(MOUSECODE_Y_INDEXED(6)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 8, PLAYER8, POSITIONAL_V, N_p("input-name", "Positional V 8"), input_seq(MOUSECODE_Y_INDEXED(7)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 9, PLAYER9, POSITIONAL_V, N_p("input-name", "Positional V 9"), input_seq(MOUSECODE_Y_INDEXED(8)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 10, PLAYER10, POSITIONAL_V, N_p("input-name", "Positional V 10"), input_seq(MOUSECODE_Y_INDEXED(9)), input_seq(), input_seq() ) \ + CORE_INPUT_TYPES_END() + +#define CORE_INPUT_TYPES_DIAL \ + CORE_INPUT_TYPES_BEGIN(dial) \ + INPUT_PORT_ANALOG_TYPE( 1, PLAYER1, DIAL, N_p("input-name", "Dial"), input_seq(MOUSECODE_X_INDEXED(0)), input_seq(KEYCODE_LEFT), input_seq(KEYCODE_RIGHT) ) \ + INPUT_PORT_ANALOG_TYPE( 2, PLAYER2, DIAL, N_p("input-name", "Dial 2"), input_seq(MOUSECODE_X_INDEXED(1)), input_seq(KEYCODE_D), input_seq(KEYCODE_G) ) \ + INPUT_PORT_ANALOG_TYPE( 3, PLAYER3, DIAL, N_p("input-name", "Dial 3"), input_seq(MOUSECODE_X_INDEXED(2)), input_seq(KEYCODE_J), input_seq(KEYCODE_L) ) \ + INPUT_PORT_ANALOG_TYPE( 4, PLAYER4, DIAL, N_p("input-name", "Dial 4"), input_seq(MOUSECODE_X_INDEXED(3)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 5, PLAYER5, DIAL, N_p("input-name", "Dial 5"), input_seq(MOUSECODE_X_INDEXED(4)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 6, PLAYER6, DIAL, N_p("input-name", "Dial 6"), input_seq(MOUSECODE_X_INDEXED(5)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 7, PLAYER7, DIAL, N_p("input-name", "Dial 7"), input_seq(MOUSECODE_X_INDEXED(6)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 8, PLAYER8, DIAL, N_p("input-name", "Dial 8"), input_seq(MOUSECODE_X_INDEXED(7)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 9, PLAYER9, DIAL, N_p("input-name", "Dial 9"), input_seq(MOUSECODE_X_INDEXED(8)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 10, PLAYER10, DIAL, N_p("input-name", "Dial 10"), input_seq(MOUSECODE_X_INDEXED(9)), input_seq(), input_seq() ) \ + CORE_INPUT_TYPES_END() + +#define CORE_INPUT_TYPES_DIAL_V \ + CORE_INPUT_TYPES_BEGIN(dial_v) \ + INPUT_PORT_ANALOG_TYPE( 1, PLAYER1, DIAL_V, N_p("input-name", "Dial V"), input_seq(MOUSECODE_Y_INDEXED(0)), input_seq(KEYCODE_UP), input_seq(KEYCODE_DOWN) ) \ + INPUT_PORT_ANALOG_TYPE( 2, PLAYER2, DIAL_V, N_p("input-name", "Dial V 2"), input_seq(MOUSECODE_Y_INDEXED(1)), input_seq(KEYCODE_R), input_seq(KEYCODE_F) ) \ + INPUT_PORT_ANALOG_TYPE( 3, PLAYER3, DIAL_V, N_p("input-name", "Dial V 3"), input_seq(MOUSECODE_Y_INDEXED(2)), input_seq(KEYCODE_I), input_seq(KEYCODE_K) ) \ + INPUT_PORT_ANALOG_TYPE( 4, PLAYER4, DIAL_V, N_p("input-name", "Dial V 4"), input_seq(MOUSECODE_Y_INDEXED(3)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 5, PLAYER5, DIAL_V, N_p("input-name", "Dial V 5"), input_seq(MOUSECODE_Y_INDEXED(4)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 6, PLAYER6, DIAL_V, N_p("input-name", "Dial V 6"), input_seq(MOUSECODE_Y_INDEXED(5)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 7, PLAYER7, DIAL_V, N_p("input-name", "Dial V 7"), input_seq(MOUSECODE_Y_INDEXED(6)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 8, PLAYER8, DIAL_V, N_p("input-name", "Dial V 8"), input_seq(MOUSECODE_Y_INDEXED(7)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 9, PLAYER9, DIAL_V, N_p("input-name", "Dial V 9"), input_seq(MOUSECODE_Y_INDEXED(8)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 10, PLAYER10, DIAL_V, N_p("input-name", "Dial V 10"), input_seq(MOUSECODE_Y_INDEXED(9)), input_seq(), input_seq() ) \ + CORE_INPUT_TYPES_END() + +#define CORE_INPUT_TYPES_TRACKBALL_X \ + CORE_INPUT_TYPES_BEGIN(trackball_x) \ + INPUT_PORT_ANALOG_TYPE( 1, PLAYER1, TRACKBALL_X, N_p("input-name", "Trackball X"), input_seq(MOUSECODE_X_INDEXED(0)), input_seq(KEYCODE_LEFT), input_seq(KEYCODE_RIGHT) ) \ + INPUT_PORT_ANALOG_TYPE( 2, PLAYER2, TRACKBALL_X, N_p("input-name", "Trackball X 2"), input_seq(MOUSECODE_X_INDEXED(1)), input_seq(KEYCODE_D), input_seq(KEYCODE_G) ) \ + INPUT_PORT_ANALOG_TYPE( 3, PLAYER3, TRACKBALL_X, N_p("input-name", "Trackball X 3"), input_seq(MOUSECODE_X_INDEXED(2)), input_seq(KEYCODE_J), input_seq(KEYCODE_L) ) \ + INPUT_PORT_ANALOG_TYPE( 4, PLAYER4, TRACKBALL_X, N_p("input-name", "Trackball X 4"), input_seq(MOUSECODE_X_INDEXED(3)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 5, PLAYER5, TRACKBALL_X, N_p("input-name", "Trackball X 5"), input_seq(MOUSECODE_X_INDEXED(4)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 6, PLAYER6, TRACKBALL_X, N_p("input-name", "Trackball X 6"), input_seq(MOUSECODE_X_INDEXED(5)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 7, PLAYER7, TRACKBALL_X, N_p("input-name", "Trackball X 7"), input_seq(MOUSECODE_X_INDEXED(6)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 8, PLAYER8, TRACKBALL_X, N_p("input-name", "Trackball X 8"), input_seq(MOUSECODE_X_INDEXED(7)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 9, PLAYER9, TRACKBALL_X, N_p("input-name", "Trackball X 9"), input_seq(MOUSECODE_X_INDEXED(8)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 10, PLAYER10, TRACKBALL_X, N_p("input-name", "Trackball X 10"), input_seq(MOUSECODE_X_INDEXED(9)), input_seq(), input_seq() ) \ + CORE_INPUT_TYPES_END() + +#define CORE_INPUT_TYPES_TRACKBALL_Y \ + CORE_INPUT_TYPES_BEGIN(trackball_y) \ + INPUT_PORT_ANALOG_TYPE( 1, PLAYER1, TRACKBALL_Y, N_p("input-name", "Trackball Y"), input_seq(MOUSECODE_Y_INDEXED(0)), input_seq(KEYCODE_UP), input_seq(KEYCODE_DOWN) ) \ + INPUT_PORT_ANALOG_TYPE( 2, PLAYER2, TRACKBALL_Y, N_p("input-name", "Trackball Y 2"), input_seq(MOUSECODE_Y_INDEXED(1)), input_seq(KEYCODE_R), input_seq(KEYCODE_F) ) \ + INPUT_PORT_ANALOG_TYPE( 3, PLAYER3, TRACKBALL_Y, N_p("input-name", "Trackball Y 3"), input_seq(MOUSECODE_Y_INDEXED(2)), input_seq(KEYCODE_I), input_seq(KEYCODE_K) ) \ + INPUT_PORT_ANALOG_TYPE( 4, PLAYER4, TRACKBALL_Y, N_p("input-name", "Trackball Y 4"), input_seq(MOUSECODE_Y_INDEXED(3)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 5, PLAYER5, TRACKBALL_Y, N_p("input-name", "Trackball Y 5"), input_seq(MOUSECODE_Y_INDEXED(4)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 6, PLAYER6, TRACKBALL_Y, N_p("input-name", "Trackball Y 6"), input_seq(MOUSECODE_Y_INDEXED(5)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 7, PLAYER7, TRACKBALL_Y, N_p("input-name", "Trackball Y 7"), input_seq(MOUSECODE_Y_INDEXED(6)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 8, PLAYER8, TRACKBALL_Y, N_p("input-name", "Trackball Y 8"), input_seq(MOUSECODE_Y_INDEXED(7)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 9, PLAYER9, TRACKBALL_Y, N_p("input-name", "Trackball Y 9"), input_seq(MOUSECODE_Y_INDEXED(8)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 10, PLAYER10, TRACKBALL_Y, N_p("input-name", "Trackball Y 10"), input_seq(MOUSECODE_Y_INDEXED(9)), input_seq(), input_seq() ) \ + CORE_INPUT_TYPES_END() + +#define CORE_INPUT_TYPES_AD_STICK_X \ + CORE_INPUT_TYPES_BEGIN(ad_stick_x) \ + INPUT_PORT_ANALOG_TYPE( 1, PLAYER1, AD_STICK_X, N_p("input-name", "AD Stick X"), input_seq(MOUSECODE_X_INDEXED(0)), input_seq(KEYCODE_LEFT), input_seq(KEYCODE_RIGHT) ) \ + INPUT_PORT_ANALOG_TYPE( 2, PLAYER2, AD_STICK_X, N_p("input-name", "AD Stick X 2"), input_seq(MOUSECODE_X_INDEXED(1)), input_seq(KEYCODE_D), input_seq(KEYCODE_G) ) \ + INPUT_PORT_ANALOG_TYPE( 3, PLAYER3, AD_STICK_X, N_p("input-name", "AD Stick X 3"), input_seq(MOUSECODE_X_INDEXED(2)), input_seq(KEYCODE_J), input_seq(KEYCODE_L) ) \ + INPUT_PORT_ANALOG_TYPE( 4, PLAYER4, AD_STICK_X, N_p("input-name", "AD Stick X 4"), input_seq(MOUSECODE_X_INDEXED(3)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 5, PLAYER5, AD_STICK_X, N_p("input-name", "AD Stick X 5"), input_seq(MOUSECODE_X_INDEXED(4)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 6, PLAYER6, AD_STICK_X, N_p("input-name", "AD Stick X 6"), input_seq(MOUSECODE_X_INDEXED(5)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 7, PLAYER7, AD_STICK_X, N_p("input-name", "AD Stick X 7"), input_seq(MOUSECODE_X_INDEXED(6)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 8, PLAYER8, AD_STICK_X, N_p("input-name", "AD Stick X 8"), input_seq(MOUSECODE_X_INDEXED(7)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 9, PLAYER9, AD_STICK_X, N_p("input-name", "AD Stick X 9"), input_seq(MOUSECODE_X_INDEXED(8)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 10, PLAYER10, AD_STICK_X, N_p("input-name", "AD Stick X 10"), input_seq(MOUSECODE_X_INDEXED(9)), input_seq(), input_seq() ) \ + CORE_INPUT_TYPES_END() + +#define CORE_INPUT_TYPES_AD_STICK_Y \ + CORE_INPUT_TYPES_BEGIN(ad_stick_y) \ + INPUT_PORT_ANALOG_TYPE( 1, PLAYER1, AD_STICK_Y, N_p("input-name", "AD Stick Y"), input_seq(MOUSECODE_Y_INDEXED(0)), input_seq(KEYCODE_UP), input_seq(KEYCODE_DOWN) ) \ + INPUT_PORT_ANALOG_TYPE( 2, PLAYER2, AD_STICK_Y, N_p("input-name", "AD Stick Y 2"), input_seq(MOUSECODE_Y_INDEXED(1)), input_seq(KEYCODE_R), input_seq(KEYCODE_F) ) \ + INPUT_PORT_ANALOG_TYPE( 3, PLAYER3, AD_STICK_Y, N_p("input-name", "AD Stick Y 3"), input_seq(MOUSECODE_Y_INDEXED(2)), input_seq(KEYCODE_I), input_seq(KEYCODE_K) ) \ + INPUT_PORT_ANALOG_TYPE( 4, PLAYER4, AD_STICK_Y, N_p("input-name", "AD Stick Y 4"), input_seq(MOUSECODE_Y_INDEXED(3)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 5, PLAYER5, AD_STICK_Y, N_p("input-name", "AD Stick Y 5"), input_seq(MOUSECODE_Y_INDEXED(4)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 6, PLAYER6, AD_STICK_Y, N_p("input-name", "AD Stick Y 6"), input_seq(MOUSECODE_Y_INDEXED(5)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 7, PLAYER7, AD_STICK_Y, N_p("input-name", "AD Stick Y 7"), input_seq(MOUSECODE_Y_INDEXED(6)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 8, PLAYER8, AD_STICK_Y, N_p("input-name", "AD Stick Y 8"), input_seq(MOUSECODE_Y_INDEXED(7)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 9, PLAYER9, AD_STICK_Y, N_p("input-name", "AD Stick Y 9"), input_seq(MOUSECODE_Y_INDEXED(8)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 10, PLAYER10, AD_STICK_Y, N_p("input-name", "AD Stick Y 10"), input_seq(MOUSECODE_Y_INDEXED(9)), input_seq(), input_seq() ) \ + CORE_INPUT_TYPES_END() + +#define CORE_INPUT_TYPES_AD_STICK_Z \ + CORE_INPUT_TYPES_BEGIN(ad_stick_z) \ + INPUT_PORT_ANALOG_TYPE( 1, PLAYER1, AD_STICK_Z, N_p("input-name", "AD Stick Z"), input_seq(), input_seq(KEYCODE_A), input_seq(KEYCODE_Z) ) \ + INPUT_PORT_ANALOG_TYPE( 2, PLAYER2, AD_STICK_Z, N_p("input-name", "AD Stick Z 2"), input_seq(), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 3, PLAYER3, AD_STICK_Z, N_p("input-name", "AD Stick Z 3"), input_seq(), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 4, PLAYER4, AD_STICK_Z, N_p("input-name", "AD Stick Z 4"), input_seq(), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 5, PLAYER5, AD_STICK_Z, N_p("input-name", "AD Stick Z 5"), input_seq(), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 6, PLAYER6, AD_STICK_Z, N_p("input-name", "AD Stick Z 6"), input_seq(), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 7, PLAYER7, AD_STICK_Z, N_p("input-name", "AD Stick Z 7"), input_seq(), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 8, PLAYER8, AD_STICK_Z, N_p("input-name", "AD Stick Z 8"), input_seq(), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 9, PLAYER9, AD_STICK_Z, N_p("input-name", "AD Stick Z 9"), input_seq(), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 10, PLAYER10, AD_STICK_Z, N_p("input-name", "AD Stick Z 10"), input_seq(), input_seq(), input_seq() ) \ + CORE_INPUT_TYPES_END() + +#define CORE_INPUT_TYPES_LIGHTGUN_X \ + CORE_INPUT_TYPES_BEGIN(lightgun_x) \ + INPUT_PORT_ANALOG_TYPE( 1, PLAYER1, LIGHTGUN_X, N_p("input-name", "Lightgun X"), input_seq(GUNCODE_X_INDEXED(0), input_seq::or_code, MOUSECODE_X_INDEXED(0)), input_seq(KEYCODE_LEFT), input_seq(KEYCODE_RIGHT) ) \ + INPUT_PORT_ANALOG_TYPE( 2, PLAYER2, LIGHTGUN_X, N_p("input-name", "Lightgun X 2"), input_seq(GUNCODE_X_INDEXED(1), input_seq::or_code, MOUSECODE_X_INDEXED(1)), input_seq(KEYCODE_D), input_seq(KEYCODE_G) ) \ + INPUT_PORT_ANALOG_TYPE( 3, PLAYER3, LIGHTGUN_X, N_p("input-name", "Lightgun X 3"), input_seq(GUNCODE_X_INDEXED(2), input_seq::or_code, MOUSECODE_X_INDEXED(2)), input_seq(KEYCODE_J), input_seq(KEYCODE_L) ) \ + INPUT_PORT_ANALOG_TYPE( 4, PLAYER4, LIGHTGUN_X, N_p("input-name", "Lightgun X 4"), input_seq(GUNCODE_X_INDEXED(3), input_seq::or_code, MOUSECODE_X_INDEXED(3)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 5, PLAYER5, LIGHTGUN_X, N_p("input-name", "Lightgun X 5"), input_seq(GUNCODE_X_INDEXED(4), input_seq::or_code, MOUSECODE_X_INDEXED(4)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 6, PLAYER6, LIGHTGUN_X, N_p("input-name", "Lightgun X 6"), input_seq(GUNCODE_X_INDEXED(5), input_seq::or_code, MOUSECODE_X_INDEXED(5)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 7, PLAYER7, LIGHTGUN_X, N_p("input-name", "Lightgun X 7"), input_seq(GUNCODE_X_INDEXED(6), input_seq::or_code, MOUSECODE_X_INDEXED(6)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 8, PLAYER8, LIGHTGUN_X, N_p("input-name", "Lightgun X 8"), input_seq(GUNCODE_X_INDEXED(7), input_seq::or_code, MOUSECODE_X_INDEXED(7)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 9, PLAYER9, LIGHTGUN_X, N_p("input-name", "Lightgun X 9"), input_seq(GUNCODE_X_INDEXED(8), input_seq::or_code, MOUSECODE_X_INDEXED(8)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 10, PLAYER10, LIGHTGUN_X, N_p("input-name", "Lightgun X 10"), input_seq(GUNCODE_X_INDEXED(9), input_seq::or_code, MOUSECODE_X_INDEXED(9)), input_seq(), input_seq() ) \ + CORE_INPUT_TYPES_END() + +#define CORE_INPUT_TYPES_LIGHTGUN_Y \ + CORE_INPUT_TYPES_BEGIN(lightgun_y) \ + INPUT_PORT_ANALOG_TYPE( 1, PLAYER1, LIGHTGUN_Y, N_p("input-name", "Lightgun Y"), input_seq(GUNCODE_Y_INDEXED(0), input_seq::or_code, MOUSECODE_Y_INDEXED(0)), input_seq(KEYCODE_UP), input_seq(KEYCODE_DOWN) ) \ + INPUT_PORT_ANALOG_TYPE( 2, PLAYER2, LIGHTGUN_Y, N_p("input-name", "Lightgun Y 2"), input_seq(GUNCODE_Y_INDEXED(1), input_seq::or_code, MOUSECODE_Y_INDEXED(1)), input_seq(KEYCODE_R), input_seq(KEYCODE_F) ) \ + INPUT_PORT_ANALOG_TYPE( 3, PLAYER3, LIGHTGUN_Y, N_p("input-name", "Lightgun Y 3"), input_seq(GUNCODE_Y_INDEXED(2), input_seq::or_code, MOUSECODE_Y_INDEXED(2)), input_seq(KEYCODE_I), input_seq(KEYCODE_K) ) \ + INPUT_PORT_ANALOG_TYPE( 4, PLAYER4, LIGHTGUN_Y, N_p("input-name", "Lightgun Y 4"), input_seq(GUNCODE_Y_INDEXED(3), input_seq::or_code, MOUSECODE_Y_INDEXED(3)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 5, PLAYER5, LIGHTGUN_Y, N_p("input-name", "Lightgun Y 5"), input_seq(GUNCODE_Y_INDEXED(4), input_seq::or_code, MOUSECODE_Y_INDEXED(4)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 6, PLAYER6, LIGHTGUN_Y, N_p("input-name", "Lightgun Y 6"), input_seq(GUNCODE_Y_INDEXED(5), input_seq::or_code, MOUSECODE_Y_INDEXED(5)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 7, PLAYER7, LIGHTGUN_Y, N_p("input-name", "Lightgun Y 7"), input_seq(GUNCODE_Y_INDEXED(6), input_seq::or_code, MOUSECODE_Y_INDEXED(6)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 8, PLAYER8, LIGHTGUN_Y, N_p("input-name", "Lightgun Y 8"), input_seq(GUNCODE_Y_INDEXED(7), input_seq::or_code, MOUSECODE_Y_INDEXED(7)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 9, PLAYER9, LIGHTGUN_Y, N_p("input-name", "Lightgun Y 9"), input_seq(GUNCODE_Y_INDEXED(8), input_seq::or_code, MOUSECODE_Y_INDEXED(8)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 10, PLAYER10, LIGHTGUN_Y, N_p("input-name", "Lightgun Y 10"), input_seq(GUNCODE_Y_INDEXED(9), input_seq::or_code, MOUSECODE_Y_INDEXED(9)), input_seq(), input_seq() ) \ + CORE_INPUT_TYPES_END() + +#define CORE_INPUT_TYPES_MOUSE_X \ + CORE_INPUT_TYPES_BEGIN(mouse_x) \ + INPUT_PORT_ANALOG_TYPE( 1, PLAYER1, MOUSE_X, N_p("input-name", "Mouse X"), input_seq(MOUSECODE_X_INDEXED(0)), input_seq(KEYCODE_LEFT), input_seq(KEYCODE_RIGHT) ) \ + INPUT_PORT_ANALOG_TYPE( 2, PLAYER2, MOUSE_X, N_p("input-name", "Mouse X 2"), input_seq(MOUSECODE_X_INDEXED(1)), input_seq(KEYCODE_D), input_seq(KEYCODE_G) ) \ + INPUT_PORT_ANALOG_TYPE( 3, PLAYER3, MOUSE_X, N_p("input-name", "Mouse X 3"), input_seq(MOUSECODE_X_INDEXED(2)), input_seq(KEYCODE_J), input_seq(KEYCODE_L) ) \ + INPUT_PORT_ANALOG_TYPE( 4, PLAYER4, MOUSE_X, N_p("input-name", "Mouse X 4"), input_seq(MOUSECODE_X_INDEXED(3)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 5, PLAYER5, MOUSE_X, N_p("input-name", "Mouse X 5"), input_seq(MOUSECODE_X_INDEXED(4)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 6, PLAYER6, MOUSE_X, N_p("input-name", "Mouse X 6"), input_seq(MOUSECODE_X_INDEXED(5)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 7, PLAYER7, MOUSE_X, N_p("input-name", "Mouse X 7"), input_seq(MOUSECODE_X_INDEXED(6)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 8, PLAYER8, MOUSE_X, N_p("input-name", "Mouse X 8"), input_seq(MOUSECODE_X_INDEXED(7)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 9, PLAYER9, MOUSE_X, N_p("input-name", "Mouse X 9"), input_seq(MOUSECODE_X_INDEXED(8)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 10, PLAYER10, MOUSE_X, N_p("input-name", "Mouse X 10"), input_seq(MOUSECODE_X_INDEXED(9)), input_seq(), input_seq() ) \ + CORE_INPUT_TYPES_END() + +#define CORE_INPUT_TYPES_MOUSE_Y \ + CORE_INPUT_TYPES_BEGIN(mouse_y) \ + INPUT_PORT_ANALOG_TYPE( 1, PLAYER1, MOUSE_Y, N_p("input-name", "Mouse Y"), input_seq(MOUSECODE_Y_INDEXED(0)), input_seq(KEYCODE_UP), input_seq(KEYCODE_DOWN) ) \ + INPUT_PORT_ANALOG_TYPE( 2, PLAYER2, MOUSE_Y, N_p("input-name", "Mouse Y 2"), input_seq(MOUSECODE_Y_INDEXED(1)), input_seq(KEYCODE_R), input_seq(KEYCODE_F) ) \ + INPUT_PORT_ANALOG_TYPE( 3, PLAYER3, MOUSE_Y, N_p("input-name", "Mouse Y 3"), input_seq(MOUSECODE_Y_INDEXED(2)), input_seq(KEYCODE_I), input_seq(KEYCODE_K) ) \ + INPUT_PORT_ANALOG_TYPE( 4, PLAYER4, MOUSE_Y, N_p("input-name", "Mouse Y 4"), input_seq(MOUSECODE_Y_INDEXED(3)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 5, PLAYER5, MOUSE_Y, N_p("input-name", "Mouse Y 5"), input_seq(MOUSECODE_Y_INDEXED(4)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 6, PLAYER6, MOUSE_Y, N_p("input-name", "Mouse Y 6"), input_seq(MOUSECODE_Y_INDEXED(5)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 7, PLAYER7, MOUSE_Y, N_p("input-name", "Mouse Y 7"), input_seq(MOUSECODE_Y_INDEXED(6)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 8, PLAYER8, MOUSE_Y, N_p("input-name", "Mouse Y 8"), input_seq(MOUSECODE_Y_INDEXED(7)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 9, PLAYER9, MOUSE_Y, N_p("input-name", "Mouse Y 9"), input_seq(MOUSECODE_Y_INDEXED(8)), input_seq(), input_seq() ) \ + INPUT_PORT_ANALOG_TYPE( 10, PLAYER10, MOUSE_Y, N_p("input-name", "Mouse Y 10"), input_seq(MOUSECODE_Y_INDEXED(9)), input_seq(), input_seq() ) \ + CORE_INPUT_TYPES_END() + +#define CORE_INPUT_TYPES_KEYPAD \ + CORE_INPUT_TYPES_BEGIN(keypad) \ + INPUT_PORT_DIGITAL_TYPE( 0, OTHER, KEYPAD, N_p("input-name", "Keypad"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 0, OTHER, KEYBOARD, N_p("input-name", "Keyboard"), input_seq() ) \ + CORE_INPUT_TYPES_END() + +#define CORE_INPUT_TYPES_UI \ + CORE_INPUT_TYPES_BEGIN(ui) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_MENU, N_p("input-name", "Show/Hide Menu"), input_seq(KEYCODE_TAB) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_SELECT, N_p("input-name", "UI Select"), input_seq(KEYCODE_ENTER, input_seq::not_code, KEYCODE_LALT, input_seq::not_code, KEYCODE_RALT, input_seq::or_code, KEYCODE_ENTER_PAD) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_BACK, N_p("input-name", "UI Back"), input_seq(KEYCODE_ESC) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_CANCEL, N_p("input-name", "UI Cancel"), input_seq(KEYCODE_ESC) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_CLEAR, N_p("input-name", "UI Clear"), input_seq(KEYCODE_DEL) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_HELP, N_p("input-name", "UI Help"), input_seq(KEYCODE_F1) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_UP, N_p("input-name", "UI Up"), input_seq(KEYCODE_UP) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_DOWN, N_p("input-name", "UI Down"), input_seq(KEYCODE_DOWN) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_LEFT, N_p("input-name", "UI Left"), input_seq(KEYCODE_LEFT) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_RIGHT, N_p("input-name", "UI Right"), input_seq(KEYCODE_RIGHT) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_HOME, N_p("input-name", "UI Home"), input_seq(KEYCODE_HOME) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_END, N_p("input-name", "UI End"), input_seq(KEYCODE_END) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_PAGE_UP, N_p("input-name", "UI Page Up"), input_seq(KEYCODE_PGUP) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_PAGE_DOWN, N_p("input-name", "UI Page Down"), input_seq(KEYCODE_PGDN) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_PREV_GROUP, N_p("input-name", "UI Previous Group"), input_seq(KEYCODE_OPENBRACE) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_NEXT_GROUP, N_p("input-name", "UI Next Group"), input_seq(KEYCODE_CLOSEBRACE) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_ON_SCREEN_DISPLAY, N_p("input-name", "On Screen Display"), input_seq(KEYCODE_TILDE) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_TOGGLE_UI, N_p("input-name", "Toggle UI Controls"), input_seq(KEYCODE_SCRLOCK, input_seq::not_code, KEYCODE_LSHIFT, input_seq::not_code, KEYCODE_RSHIFT) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_DEBUG_BREAK, N_p("input-name", "Break in Debugger"), input_seq(KEYCODE_TILDE) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_PAUSE, N_p("input-name", "Pause"), input_seq(KEYCODE_F5, input_seq::not_code, KEYCODE_LSHIFT, input_seq::not_code, KEYCODE_RSHIFT) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_PAUSE_SINGLE, N_p("input-name", "Pause - Single Step"), input_seq(KEYCODE_F5, KEYCODE_LSHIFT, input_seq::or_code, KEYCODE_F5, KEYCODE_RSHIFT) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_REWIND_SINGLE, N_p("input-name", "Rewind - Single Step"), input_seq(KEYCODE_F4, KEYCODE_LSHIFT, input_seq::or_code, KEYCODE_F4, KEYCODE_RSHIFT) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_SAVE_STATE, N_p("input-name", "Save State"), input_seq(KEYCODE_F6, input_seq::not_code, KEYCODE_LSHIFT, input_seq::not_code, KEYCODE_RSHIFT) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_SAVE_STATE_QUICK, N_p("input-name", "Quick Save State"), input_seq(KEYCODE_F6, KEYCODE_LSHIFT, input_seq::or_code, KEYCODE_F6, KEYCODE_RSHIFT) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_LOAD_STATE, N_p("input-name", "Load State"), input_seq(KEYCODE_F7, input_seq::not_code, KEYCODE_LSHIFT, input_seq::not_code, KEYCODE_RSHIFT) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_LOAD_STATE_QUICK, N_p("input-name", "Quick Load State"), input_seq(KEYCODE_F7, KEYCODE_LSHIFT, input_seq::or_code, KEYCODE_F7, KEYCODE_RSHIFT) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_RESET_MACHINE, N_p("input-name", "Reset Machine"), input_seq(KEYCODE_F3, KEYCODE_LSHIFT, input_seq::or_code, KEYCODE_F3, KEYCODE_RSHIFT) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_SOFT_RESET, N_p("input-name", "Soft Reset"), input_seq(KEYCODE_F3, input_seq::not_code, KEYCODE_LSHIFT, input_seq::not_code, KEYCODE_RSHIFT) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_SHOW_GFX, N_p("input-name", "Show Decoded Graphics"), input_seq(KEYCODE_F4, input_seq::not_code, KEYCODE_LSHIFT, input_seq::not_code, KEYCODE_RSHIFT) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_FRAMESKIP_DEC, N_p("input-name", "Frameskip Dec"), input_seq(KEYCODE_F8, input_seq::not_code, KEYCODE_LSHIFT, input_seq::not_code, KEYCODE_RSHIFT) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_FRAMESKIP_INC, N_p("input-name", "Frameskip Inc"), input_seq(KEYCODE_F9) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_THROTTLE, N_p("input-name", "Throttle"), input_seq(KEYCODE_F10) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_FAST_FORWARD, N_p("input-name", "Fast Forward"), input_seq(KEYCODE_INSERT) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_SHOW_FPS, N_p("input-name", "Show FPS"), input_seq(KEYCODE_F11, input_seq::not_code, KEYCODE_LSHIFT) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_SNAPSHOT, N_p("input-name", "Save Snapshot"), input_seq(KEYCODE_F12, input_seq::not_code, KEYCODE_LSHIFT) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_RECORD_MNG, N_p("input-name", "Record MNG"), input_seq(KEYCODE_F12, KEYCODE_LSHIFT, input_seq::not_code, KEYCODE_LCONTROL) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_RECORD_AVI, N_p("input-name", "Record AVI"), input_seq(KEYCODE_F12, KEYCODE_LSHIFT, KEYCODE_LCONTROL) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_TOGGLE_CHEAT, N_p("input-name", "Toggle Cheat"), input_seq(KEYCODE_F8, KEYCODE_LSHIFT, input_seq::or_code, KEYCODE_F8, KEYCODE_RSHIFT) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_DISPLAY_COMMENT, N_p("input-name", "UI Display Comment"), input_seq(KEYCODE_SPACE) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_ZOOM_IN, N_p("input-name", "UI Zoom In"), input_seq(KEYCODE_EQUALS) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_ZOOM_OUT, N_p("input-name", "UI Zoom Out"), input_seq(KEYCODE_MINUS) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_ZOOM_DEFAULT, N_p("input-name", "UI Default Zoom"), input_seq(KEYCODE_0) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_ROTATE, N_p("input-name", "UI Rotate"), input_seq(KEYCODE_R) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_SHOW_PROFILER, N_p("input-name", "Show Profiler"), input_seq(KEYCODE_F11, KEYCODE_LSHIFT) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_RELEASE_POINTER, N_p("input-name", "UI Release Pointer"), input_seq(KEYCODE_RCONTROL, KEYCODE_RALT) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_PASTE, N_p("input-name", "UI Paste Text"), input_seq(KEYCODE_SCRLOCK, KEYCODE_LSHIFT, input_seq::or_code, KEYCODE_SCRLOCK, KEYCODE_RSHIFT) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_TAPE_START, N_p("input-name", "UI (First) Tape Start"), input_seq(KEYCODE_F2, input_seq::not_code, KEYCODE_LSHIFT, input_seq::not_code, KEYCODE_RSHIFT) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_TAPE_STOP, N_p("input-name", "UI (First) Tape Stop"), input_seq(KEYCODE_F2, KEYCODE_LSHIFT) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_FOCUS_NEXT, N_p("input-name", "UI Focus Next"), input_seq(KEYCODE_TAB, input_seq::not_code, KEYCODE_LSHIFT, input_seq::not_code, KEYCODE_RSHIFT) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_FOCUS_PREV, N_p("input-name", "UI Focus Previous"), input_seq(KEYCODE_TAB, KEYCODE_LSHIFT, input_seq::or_code, KEYCODE_TAB, KEYCODE_RSHIFT) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_DATS, N_p("input-name", "UI External DAT View"), input_seq(KEYCODE_LALT, KEYCODE_D) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_FAVORITES, N_p("input-name", "UI Add/Remove Favorite"), input_seq(KEYCODE_LALT, KEYCODE_F) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_EXPORT, N_p("input-name", "UI Export List"), input_seq(KEYCODE_LALT, KEYCODE_E) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_AUDIT, N_p("input-name", "UI Audit Media"), input_seq(KEYCODE_F1, input_seq::not_code, KEYCODE_LSHIFT, input_seq::not_code, KEYCODE_RSHIFT) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_MIXER_ADD_FULL, N_p("input-name", "UI Audio add full mapping"), input_seq(KEYCODE_F) ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_MIXER_ADD_CHANNEL, N_p("input-name", "UI Audio add channel mapping"), input_seq(KEYCODE_C) ) \ + CORE_INPUT_TYPES_END() + +#define CORE_INPUT_TYPES_OSD \ + CORE_INPUT_TYPES_BEGIN(osd) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, OSD_1, nullptr, input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, OSD_2, nullptr, input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, OSD_3, nullptr, input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, OSD_4, nullptr, input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, OSD_5, nullptr, input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, OSD_6, nullptr, input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, OSD_7, nullptr, input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, OSD_8, nullptr, input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, OSD_9, nullptr, input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, OSD_10, nullptr, input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, OSD_11, nullptr, input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, OSD_12, nullptr, input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, OSD_13, nullptr, input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, OSD_14, nullptr, input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, OSD_15, nullptr, input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 0, UI, OSD_16, nullptr, input_seq() ) \ + CORE_INPUT_TYPES_END() + +#define CORE_INPUT_TYPES_INVALID \ + CORE_INPUT_TYPES_BEGIN(invalid) \ + INPUT_PORT_DIGITAL_TYPE( 0, INVALID, UNKNOWN, nullptr, input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 0, INVALID, UNUSED, nullptr, input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 0, INVALID, SPECIAL, nullptr, input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 0, INVALID, OTHER, nullptr, input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 0, INVALID, ADJUSTER, nullptr, input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 0, INVALID, DIPSWITCH, nullptr, input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 0, INVALID, CONFIG, nullptr, input_seq() ) \ + CORE_INPUT_TYPES_END() + + +ATTR_COLD void emplace_core_digital_type(std::vector<input_type_entry> &typelist, ioport_type type, ioport_group group, int player, const char *token, const char *name, input_seq seq) +{ + typelist.emplace_back(type, group, (player == 0) ? player : (player - 1), token, name, seq); +} + +ATTR_COLD void emplace_core_analog_type(std::vector<input_type_entry> &typelist, ioport_type type, ioport_group group, int player, const char *token, const char *name, input_seq seq, input_seq decseq, input_seq incseq) +{ + typelist.emplace_back(type, group, (player == 0) ? player : (player - 1), token, name, seq, decseq, incseq); +} + + +// instantiate the contruct functions +#define CORE_INPUT_TYPES_BEGIN(_name) \ + ATTR_COLD inline void emplace_core_types_##_name(std::vector<input_type_entry> &typelist) \ + { +#define INPUT_PORT_DIGITAL_TYPE(_player, _group, _type, _name, _seq) \ + emplace_core_digital_type(typelist, IPT_##_type, IPG_##_group, _player, (_player == 0) ? #_type : ("P" #_player "_" #_type), _name, _seq); +#define INPUT_PORT_ANALOG_TYPE(_player, _group, _type, _name, _seq, _decseq, _incseq) \ + emplace_core_analog_type(typelist, IPT_##_type, IPG_##_group, _player, (_player == 0) ? #_type : ("P" #_player "_" #_type), _name, _seq, _decseq, _incseq); +#define CORE_INPUT_TYPES_END() \ + } +CORE_INPUT_TYPES_P1 +CORE_INPUT_TYPES_P1_MAHJONG +CORE_INPUT_TYPES_P1_HANAFUDA +CORE_INPUT_TYPES_GAMBLE +CORE_INPUT_TYPES_POKER +CORE_INPUT_TYPES_SLOT +CORE_INPUT_TYPES_P2 +CORE_INPUT_TYPES_P2_MAHJONG +CORE_INPUT_TYPES_P2_HANAFUDA +CORE_INPUT_TYPES_P3 +CORE_INPUT_TYPES_P3_MAHJONG +CORE_INPUT_TYPES_P4 +CORE_INPUT_TYPES_P4_MAHJONG +CORE_INPUT_TYPES_P5 +CORE_INPUT_TYPES_P6 +CORE_INPUT_TYPES_P7 +CORE_INPUT_TYPES_P8 +CORE_INPUT_TYPES_P9 +CORE_INPUT_TYPES_P10 +CORE_INPUT_TYPES_START +CORE_INPUT_TYPES_COIN +CORE_INPUT_TYPES_SERVICE +CORE_INPUT_TYPES_TILT +CORE_INPUT_TYPES_OTHER +CORE_INPUT_TYPES_PEDAL +CORE_INPUT_TYPES_PEDAL2 +CORE_INPUT_TYPES_PEDAL3 +CORE_INPUT_TYPES_PADDLE +CORE_INPUT_TYPES_PADDLE_V +CORE_INPUT_TYPES_POSITIONAL +CORE_INPUT_TYPES_POSITIONAL_V +CORE_INPUT_TYPES_DIAL +CORE_INPUT_TYPES_DIAL_V +CORE_INPUT_TYPES_TRACKBALL_X +CORE_INPUT_TYPES_TRACKBALL_Y +CORE_INPUT_TYPES_AD_STICK_X +CORE_INPUT_TYPES_AD_STICK_Y +CORE_INPUT_TYPES_AD_STICK_Z +CORE_INPUT_TYPES_LIGHTGUN_X +CORE_INPUT_TYPES_LIGHTGUN_Y +CORE_INPUT_TYPES_MOUSE_X +CORE_INPUT_TYPES_MOUSE_Y +CORE_INPUT_TYPES_KEYPAD +CORE_INPUT_TYPES_UI +CORE_INPUT_TYPES_OSD +CORE_INPUT_TYPES_INVALID +#undef CORE_INPUT_TYPES_BEGIN +#undef INPUT_PORT_DIGITAL_TYPE +#undef INPUT_PORT_ANALOG_TYPE +#undef CORE_INPUT_TYPES_END + + +// make a count function so we don't have to reallocate the vector +#define CORE_INPUT_TYPES_BEGIN(_name) +#define INPUT_PORT_DIGITAL_TYPE(_player, _group, _type, _name, _seq) + 1 +#define INPUT_PORT_ANALOG_TYPE(_player, _group, _type, _name, _seq, _decseq, _incseq) + 1 +#define CORE_INPUT_TYPES_END() +constexpr size_t core_input_types_count() +{ + return 0 + CORE_INPUT_TYPES_P1 + CORE_INPUT_TYPES_P1_MAHJONG + CORE_INPUT_TYPES_P1_HANAFUDA + CORE_INPUT_TYPES_GAMBLE + CORE_INPUT_TYPES_POKER + CORE_INPUT_TYPES_SLOT + CORE_INPUT_TYPES_P2 + CORE_INPUT_TYPES_P2_MAHJONG + CORE_INPUT_TYPES_P2_HANAFUDA + CORE_INPUT_TYPES_P3 + CORE_INPUT_TYPES_P3_MAHJONG + CORE_INPUT_TYPES_P4 + CORE_INPUT_TYPES_P4_MAHJONG + CORE_INPUT_TYPES_P5 + CORE_INPUT_TYPES_P6 + CORE_INPUT_TYPES_P7 + CORE_INPUT_TYPES_P8 + CORE_INPUT_TYPES_P9 + CORE_INPUT_TYPES_P10 + CORE_INPUT_TYPES_START + CORE_INPUT_TYPES_COIN + CORE_INPUT_TYPES_SERVICE + CORE_INPUT_TYPES_TILT + CORE_INPUT_TYPES_OTHER + CORE_INPUT_TYPES_PEDAL + CORE_INPUT_TYPES_PEDAL2 + CORE_INPUT_TYPES_PEDAL3 + CORE_INPUT_TYPES_PADDLE + CORE_INPUT_TYPES_PADDLE_V + CORE_INPUT_TYPES_POSITIONAL + CORE_INPUT_TYPES_POSITIONAL_V + CORE_INPUT_TYPES_DIAL + CORE_INPUT_TYPES_DIAL_V + CORE_INPUT_TYPES_TRACKBALL_X + CORE_INPUT_TYPES_TRACKBALL_Y + CORE_INPUT_TYPES_AD_STICK_X + CORE_INPUT_TYPES_AD_STICK_Y + CORE_INPUT_TYPES_AD_STICK_Z + CORE_INPUT_TYPES_LIGHTGUN_X + CORE_INPUT_TYPES_LIGHTGUN_Y + CORE_INPUT_TYPES_MOUSE_X + CORE_INPUT_TYPES_MOUSE_Y + CORE_INPUT_TYPES_KEYPAD + CORE_INPUT_TYPES_UI + CORE_INPUT_TYPES_OSD + CORE_INPUT_TYPES_INVALID + ; +} +#undef CORE_INPUT_TYPES_BEGIN +#undef INPUT_PORT_DIGITAL_TYPE +#undef INPUT_PORT_ANALOG_TYPE +#undef CORE_INPUT_TYPES_END + + +ATTR_COLD inline void emplace_core_types(std::vector<input_type_entry> &typelist) +{ + typelist.reserve(core_input_types_count()); + + emplace_core_types_p1(typelist); + emplace_core_types_p1_mahjong(typelist); + emplace_core_types_p1_hanafuda(typelist); + emplace_core_types_gamble(typelist); + emplace_core_types_poker(typelist); + emplace_core_types_slot(typelist); + emplace_core_types_p2(typelist); + emplace_core_types_p2_mahjong(typelist); + emplace_core_types_p2_hanafuda(typelist); + emplace_core_types_p3(typelist); + emplace_core_types_p3_mahjong(typelist); + emplace_core_types_p4(typelist); + emplace_core_types_p4_mahjong(typelist); + emplace_core_types_p5(typelist); + emplace_core_types_p6(typelist); + emplace_core_types_p7(typelist); + emplace_core_types_p8(typelist); + emplace_core_types_p9(typelist); + emplace_core_types_p10(typelist); + emplace_core_types_start(typelist); + emplace_core_types_coin(typelist); + emplace_core_types_service(typelist); + emplace_core_types_tilt(typelist); + emplace_core_types_other(typelist); + emplace_core_types_pedal(typelist); + emplace_core_types_pedal2(typelist); + emplace_core_types_pedal3(typelist); + emplace_core_types_paddle(typelist); + emplace_core_types_paddle_v(typelist); + emplace_core_types_positional(typelist); + emplace_core_types_positional_v(typelist); + emplace_core_types_dial(typelist); + emplace_core_types_dial_v(typelist); + emplace_core_types_trackball_x(typelist); + emplace_core_types_trackball_y(typelist); + emplace_core_types_ad_stick_x(typelist); + emplace_core_types_ad_stick_y(typelist); + emplace_core_types_ad_stick_z(typelist); + emplace_core_types_lightgun_x(typelist); + emplace_core_types_lightgun_y(typelist); + emplace_core_types_mouse_x(typelist); + emplace_core_types_mouse_y(typelist); + emplace_core_types_keypad(typelist); + emplace_core_types_ui(typelist); + emplace_core_types_osd(typelist); + emplace_core_types_invalid(typelist); +} + +} // anonymous namespace diff --git a/src/emu/input.cpp b/src/emu/input.cpp index 0265b021e9a..5eeee806f9d 100644 --- a/src/emu/input.cpp +++ b/src/emu/input.cpp @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles /*************************************************************************** - input.c + input.cpp Handle input from the user. @@ -20,25 +20,11 @@ #include "emu.h" #include "inputdev.h" +#include "corestr.h" -//************************************************************************** -// CONSTANTS -//************************************************************************** - -// invalid memory value for axis polling -const s32 INVALID_AXIS_VALUE = 0x7fffffff; - -// additional expanded input codes for sequences -const input_code input_seq::end_code(DEVICE_CLASS_INTERNAL, 0, ITEM_CLASS_INVALID, ITEM_MODIFIER_NONE, ITEM_ID_SEQ_END); -const input_code input_seq::default_code(DEVICE_CLASS_INTERNAL, 0, ITEM_CLASS_INVALID, ITEM_MODIFIER_NONE, ITEM_ID_SEQ_DEFAULT); -const input_code input_seq::not_code(DEVICE_CLASS_INTERNAL, 0, ITEM_CLASS_INVALID, ITEM_MODIFIER_NONE, ITEM_ID_SEQ_NOT); -const input_code input_seq::or_code(DEVICE_CLASS_INTERNAL, 0, ITEM_CLASS_INVALID, ITEM_MODIFIER_NONE, ITEM_ID_SEQ_OR); - -// constant sequences -const input_seq input_seq::empty_seq; - +namespace { //************************************************************************** // TYPE DEFINITIONS @@ -49,24 +35,26 @@ const input_seq input_seq::empty_seq; // simple class to match codes to strings struct code_string_table { - u32 operator[](const char *string) const + static inline constexpr u32 SENTINEL = ~u32(0); + + u32 operator[](std::string_view string) const { - for (const code_string_table *current = this; current->m_code != ~0; current++) - if (strcmp(current->m_string, string) == 0) + for (const code_string_table *current = this; current->m_code != SENTINEL; current++) + if (current->m_string == string) return current->m_code; - return ~0; + return SENTINEL; } const char *operator[](u32 code) const { - for (const code_string_table *current = this; current->m_code != ~0; current++) + for (const code_string_table *current = this; current->m_code != SENTINEL; current++) if (current->m_code == code) return current->m_string; return nullptr; } - u32 m_code; - const char * m_string; + u32 m_code; + const char * m_string; }; @@ -76,60 +64,62 @@ struct code_string_table //************************************************************************** // token strings for device classes -static const code_string_table devclass_token_table[] = +const code_string_table devclass_token_table[] = { - { DEVICE_CLASS_KEYBOARD, "KEYCODE" }, - { DEVICE_CLASS_MOUSE, "MOUSECODE" }, - { DEVICE_CLASS_LIGHTGUN, "GUNCODE" }, - { DEVICE_CLASS_JOYSTICK, "JOYCODE" }, - { ~0U, "UNKCODE" } + { DEVICE_CLASS_KEYBOARD, "KEYCODE" }, + { DEVICE_CLASS_MOUSE, "MOUSECODE" }, + { DEVICE_CLASS_LIGHTGUN, "GUNCODE" }, + { DEVICE_CLASS_JOYSTICK, "JOYCODE" }, + { code_string_table::SENTINEL, "UNKCODE" } }; // friendly strings for device classes -static const code_string_table devclass_string_table[] = +const code_string_table devclass_string_table[] = { - { DEVICE_CLASS_KEYBOARD, "Kbd" }, - { DEVICE_CLASS_MOUSE, "Mouse" }, - { DEVICE_CLASS_LIGHTGUN, "Gun" }, - { DEVICE_CLASS_JOYSTICK, "Joy" }, - { ~0U, "Unk" } + { DEVICE_CLASS_KEYBOARD, "Kbd" }, + { DEVICE_CLASS_MOUSE, "Mouse" }, + { DEVICE_CLASS_LIGHTGUN, "Gun" }, + { DEVICE_CLASS_JOYSTICK, "Joy" }, + { code_string_table::SENTINEL, "Unk" } }; // token strings for item modifiers -static const code_string_table modifier_token_table[] = +const code_string_table modifier_token_table[] = { - { ITEM_MODIFIER_POS, "POS" }, - { ITEM_MODIFIER_NEG, "NEG" }, - { ITEM_MODIFIER_LEFT, "LEFT" }, - { ITEM_MODIFIER_RIGHT, "RIGHT" }, - { ITEM_MODIFIER_UP, "UP" }, - { ITEM_MODIFIER_DOWN, "DOWN" }, - { ~0U, "" } + { ITEM_MODIFIER_REVERSE, "REVERSE" }, + { ITEM_MODIFIER_POS, "POS" }, + { ITEM_MODIFIER_NEG, "NEG" }, + { ITEM_MODIFIER_LEFT, "LEFT" }, + { ITEM_MODIFIER_RIGHT, "RIGHT" }, + { ITEM_MODIFIER_UP, "UP" }, + { ITEM_MODIFIER_DOWN, "DOWN" }, + { code_string_table::SENTINEL, "" } }; // friendly strings for item modifiers -static const code_string_table modifier_string_table[] = +const code_string_table modifier_string_table[] = { - { ITEM_MODIFIER_POS, "+" }, - { ITEM_MODIFIER_NEG, "-" }, - { ITEM_MODIFIER_LEFT, "Left" }, - { ITEM_MODIFIER_RIGHT, "Right" }, - { ITEM_MODIFIER_UP, "Up" }, - { ITEM_MODIFIER_DOWN, "Down" }, - { ~0U, "" } + { ITEM_MODIFIER_REVERSE, "Reverse" }, + { ITEM_MODIFIER_POS, "+" }, + { ITEM_MODIFIER_NEG, "-" }, + { ITEM_MODIFIER_LEFT, "Left" }, + { ITEM_MODIFIER_RIGHT, "Right" }, + { ITEM_MODIFIER_UP, "Up" }, + { ITEM_MODIFIER_DOWN, "Down" }, + { code_string_table::SENTINEL, "" } }; // token strings for item classes -static const code_string_table itemclass_token_table[] = +const code_string_table itemclass_token_table[] = { - { ITEM_CLASS_SWITCH, "SWITCH" }, - { ITEM_CLASS_ABSOLUTE, "ABSOLUTE" }, - { ITEM_CLASS_RELATIVE, "RELATIVE" }, - { ~0U, "" } + { ITEM_CLASS_SWITCH, "SWITCH" }, + { ITEM_CLASS_ABSOLUTE, "ABSOLUTE" }, + { ITEM_CLASS_RELATIVE, "RELATIVE" }, + { code_string_table::SENTINEL, "" } }; // token strings for standard item ids -static const code_string_table itemid_token_table[] = +const code_string_table itemid_token_table[] = { // standard keyboard codes { ITEM_ID_A, "A" }, @@ -363,188 +353,50 @@ static const code_string_table itemid_token_table[] = { ITEM_ID_ADD_RELATIVE15,"ADDREL15" }, { ITEM_ID_ADD_RELATIVE16,"ADDREL16" }, - { ~0U, nullptr } + { code_string_table::SENTINEL, nullptr } }; //************************************************************************** -// INPUT SEQ +// UTILITY FUNCTIONS //************************************************************************** -//------------------------------------------------- -// operator+= - append a code to the end of an -// input sequence -//------------------------------------------------- - -input_seq &input_seq::operator+=(input_code code) +inline void accumulate_axis_value( + s32 &result, + input_item_class &resultclass, + input_item_class &resultclasszero, + s32 value, + input_item_class valueclass, + input_item_class valueclasszero) { - // if not enough room, return false - int curlength = length(); - if (curlength < ARRAY_LENGTH(m_code) - 1) + if (!value) { - m_code[curlength++] = code; - m_code[curlength] = end_code; + // track the highest-priority zero + if ((ITEM_CLASS_ABSOLUTE == valueclasszero) || (ITEM_CLASS_INVALID == resultclasszero)) + resultclasszero = valueclasszero; } - return *this; -} - - -//------------------------------------------------- -// operator|= - append a code to a sequence; if -// the sequence is non-empty, insert an OR -// before the new code -//------------------------------------------------- - -input_seq &input_seq::operator|=(input_code code) -{ - // overwrite end/default with the new code - if (m_code[0] == end_code || m_code[0] == default_code) - m_code[0] = code; - - // otherwise, append an OR token and then the new code - else + else if (ITEM_CLASS_ABSOLUTE == valueclass) { - *this += or_code; - *this += code; + // absolute values override relative values + if (ITEM_CLASS_ABSOLUTE == resultclass) + result += value; + else + result = value; + resultclass = ITEM_CLASS_ABSOLUTE; } - return *this; -} - - -//------------------------------------------------- -// length - return the length of the sequence -//------------------------------------------------- - -int input_seq::length() const -{ - // find the end token; error if none found - for (int seqnum = 0; seqnum < ARRAY_LENGTH(m_code); seqnum++) - if (m_code[seqnum] == end_code) - return seqnum; - return ARRAY_LENGTH(m_code); -} - - -//------------------------------------------------- -// is_valid - return true if a given sequence is -// valid -//------------------------------------------------- - -bool input_seq::is_valid() const -{ - // "default" can only be of length 1 - if (m_code[0] == default_code) - return (length() == 1); - - // scan the sequence for valid codes - input_item_class lastclass = ITEM_CLASS_INVALID; - input_code lastcode = INPUT_CODE_INVALID; - int positive_code_count = 0; - for (auto code : m_code) + else if (ITEM_CLASS_RELATIVE == valueclass) { - // invalid codes are never permitted - - if (code == INPUT_CODE_INVALID) - return false; - - // if we hit an OR or the end, validate the previous chunk - if (code == or_code || code == end_code) - { - // must be at least one positive code - if (positive_code_count == 0) - return false; - - // last code must not have been an internal code - if (lastcode.internal()) - return false; - - // if this is the end, we're ok - if (code == end_code) - return true; - - // reset the state for the next chunk - positive_code_count = 0; - lastclass = ITEM_CLASS_INVALID; - } - - // if we hit a NOT, make sure we don't have a double - else if (code == not_code) + // relative values accumulate + if (resultclass != ITEM_CLASS_ABSOLUTE) { - if (lastcode == not_code) - return false; + result += value; + resultclass = ITEM_CLASS_RELATIVE; } - - // anything else - else - { - // count positive codes - if (lastcode != not_code) - positive_code_count++; - - // non-switch items can't have a NOT - input_item_class itemclass = code.item_class(); - if (itemclass != ITEM_CLASS_SWITCH && lastcode == not_code) - return false; - - // absolute/relative items must all be the same class - if ((lastclass == ITEM_CLASS_ABSOLUTE && itemclass != ITEM_CLASS_ABSOLUTE) || - (lastclass == ITEM_CLASS_RELATIVE && itemclass != ITEM_CLASS_RELATIVE)) - return false; - } - - // remember the last code - lastcode = code; } - - // if we got here, we were missing an END token; fail - return false; -} - - -//------------------------------------------------- -// set - directly set up to the first 7 codes -//------------------------------------------------- - -void input_seq::set(input_code code0, input_code code1, input_code code2, input_code code3, input_code code4, input_code code5, input_code code6) -{ - m_code[0] = code0; - m_code[1] = code1; - m_code[2] = code2; - m_code[3] = code3; - m_code[4] = code4; - m_code[5] = code5; - m_code[6] = code6; - for (int codenum = 7; codenum < ARRAY_LENGTH(m_code); codenum++) - m_code[codenum] = end_code; -} - - -//------------------------------------------------- -// backspace - "backspace" over the last entry in -// a sequence -//------------------------------------------------- - -void input_seq::backspace() -{ - // if we have at least one entry, remove it - int curlength = length(); - if (curlength > 0) - m_code[curlength - 1] = end_code; } - -//------------------------------------------------- -// replace - replace all instances of oldcode -// with newcode in a sequence -//------------------------------------------------- - -void input_seq::replace(input_code oldcode, input_code newcode) -{ - for (auto & elem : m_code) - if (elem == oldcode) - elem = newcode; -} +} // anonymous namespace @@ -556,10 +408,7 @@ void input_seq::replace(input_code oldcode, input_code newcode) // input_manager - constructor //------------------------------------------------- -input_manager::input_manager(running_machine &machine) - : m_machine(machine), - m_poll_seq_last_ticks(0), - m_poll_seq_class(ITEM_CLASS_SWITCH) +input_manager::input_manager(running_machine &machine) : m_machine(machine) { // reset code memory reset_memory(); @@ -590,72 +439,89 @@ input_manager::~input_manager() //------------------------------------------------- +// class_enabled - return whether input device +// class is enabled +//------------------------------------------------- + +bool input_manager::class_enabled(input_device_class devclass) const +{ + assert(devclass >= DEVICE_CLASS_FIRST_VALID && devclass <= DEVICE_CLASS_LAST_VALID); + return m_class[devclass]->enabled(); +} + + +//------------------------------------------------- +// add_device - add a representation of a host +// input device +//------------------------------------------------- + +osd::input_device &input_manager::add_device(input_device_class devclass, std::string_view name, std::string_view id, void *internal) +{ + return device_class(devclass).add_device(name, id, internal); +} + + +//------------------------------------------------- // code_value - return the value of a given // input code //------------------------------------------------- s32 input_manager::code_value(input_code code) { - g_profiler.start(PROFILER_INPUT); - s32 result = 0; - - // dummy loop to allow clean early exits - do + auto profile = g_profiler.start(PROFILER_INPUT); + + // return 0 for any invalid devices + input_device *const device = device_from_code(code); + if (!device) + return 0; + + // also return 0 if the device class is disabled + input_class &devclass = *m_class[code.device_class()]; + if (!devclass.enabled()) + return 0; + + // if this is not a multi device, only return data for item 0 and iterate over all + int startindex = code.device_index(); + int stopindex = startindex; + if (!devclass.multi()) { - // return 0 for any invalid devices - input_device *device = device_from_code(code); - if (device == nullptr) - break; - - // also return 0 if the device class is disabled - input_class &devclass = *m_class[code.device_class()]; - if (!devclass.enabled()) - break; - - // if this is not a multi device, only return data for item 0 and iterate over all - int startindex = code.device_index(); - int stopindex = startindex; - if (!devclass.multi()) - { - if (startindex != 0) - break; - stopindex = devclass.maxindex(); - } + if (startindex != 0) + return 0; + stopindex = devclass.maxindex(); + } - // iterate over all device indices - input_item_class targetclass = code.item_class(); - for (int curindex = startindex; curindex <= stopindex; curindex++) + // iterate over all device indices + s32 result = 0; + input_item_class targetclass = code.item_class(); + for (int curindex = startindex; curindex <= stopindex; curindex++) + { + // lookup the item for the appropriate index + code.set_device_index(curindex); + input_device_item *const item = item_from_code(code); + if (item) { - // lookup the item for the appropriate index - code.set_device_index(curindex); - input_device_item *item = item_from_code(code); - if (item == nullptr) - continue; - // process items according to their native type switch (targetclass) { - case ITEM_CLASS_ABSOLUTE: - if (result == 0) - result = item->read_as_absolute(code.item_modifier()); - break; + case ITEM_CLASS_ABSOLUTE: + if (result == 0) + result = item->read_as_absolute(code.item_modifier()); + break; - case ITEM_CLASS_RELATIVE: - result += item->read_as_relative(code.item_modifier()); - break; + case ITEM_CLASS_RELATIVE: + result += item->read_as_relative(code.item_modifier()); + break; - case ITEM_CLASS_SWITCH: - result |= item->read_as_switch(code.item_modifier()); - break; + case ITEM_CLASS_SWITCH: + result |= item->read_as_switch(code.item_modifier()); + break; - default: - break; + default: + break; } } - } while (0); + } - // stop the profiler before exiting - g_profiler.stop(); return result; } @@ -671,13 +537,13 @@ bool input_manager::code_pressed_once(input_code code) // look for the code in the memory bool curvalue = code_pressed(code); int empty = -1; - for (int memnum = 0; memnum < ARRAY_LENGTH(m_switch_memory); memnum++) + for (int memnum = 0; memnum < std::size(m_switch_memory); memnum++) { // were we previous pressed on the last time through here? if (m_switch_memory[memnum] == code) { // if no longer pressed, clear entry - if (curvalue == false) + if (!curvalue) m_switch_memory[memnum] = INPUT_CODE_INVALID; // always return false @@ -689,11 +555,11 @@ bool input_manager::code_pressed_once(input_code code) empty = memnum; } - // if we get here, we were not previously pressed; if still not pressed, return 0 - if (curvalue == false) + // if we get here, we were not previously pressed; if still not pressed, return false + if (!curvalue) return false; - // otherwise, add ourself to the memory and return 1 + // otherwise, add the code to the memory and return true assert(empty != -1); if (empty != -1) m_switch_memory[empty] = code; @@ -702,225 +568,6 @@ bool input_manager::code_pressed_once(input_code code) //------------------------------------------------- -// reset_polling - reset memories in preparation -// for polling -//------------------------------------------------- - -void input_manager::reset_polling() -{ - // reset switch memory - reset_memory(); - - // iterate over device classes and devices - for (input_device_class devclass = DEVICE_CLASS_FIRST_VALID; devclass <= DEVICE_CLASS_LAST_VALID; ++devclass) - for (int devnum = 0; devnum <= m_class[devclass]->maxindex(); devnum++) - { - // fetch the device; ignore if nullptr - input_device *device = m_class[devclass]->device(devnum); - if (device == nullptr) - continue; - - // iterate over items within each device - for (input_item_id itemid = ITEM_ID_FIRST_VALID; itemid <= device->maxitem(); ++itemid) - { - // for any non-switch items, set memory equal to the current value - input_device_item *item = device->item(itemid); - if (item != nullptr && item->itemclass() != ITEM_CLASS_SWITCH) - item->set_memory(code_value(item->code())); - } - } -} - - -//------------------------------------------------- -// poll_switches - poll for any input -//------------------------------------------------- - -input_code input_manager::poll_switches() -{ - // iterate over device classes and devices - for (input_device_class devclass = DEVICE_CLASS_FIRST_VALID; devclass <= DEVICE_CLASS_LAST_VALID; ++devclass) - for (int devnum = 0; devnum <= m_class[devclass]->maxindex(); devnum++) - { - // fetch the device; ignore if nullptr - input_device *device = m_class[devclass]->device(devnum); - if (device == nullptr) - continue; - - // iterate over items within each device - for (input_item_id itemid = ITEM_ID_FIRST_VALID; itemid <= device->maxitem(); ++itemid) - { - input_device_item *item = device->item(itemid); - if (item != nullptr) - { - input_code code = item->code(); - - // if the item is natively a switch, poll it - if (item->itemclass() == ITEM_CLASS_SWITCH) - { - if (code_pressed_once(code)) - return code; - else - continue; - } - - // skip if there is not enough axis movement - if (!code_check_axis(*item, code)) - continue; - - // otherwise, poll axes digitally - code.set_item_class(ITEM_CLASS_SWITCH); - - // if this is a joystick X axis, check with left/right modifiers - if (devclass == DEVICE_CLASS_JOYSTICK && code.item_id() == ITEM_ID_XAXIS) - { - code.set_item_modifier(ITEM_MODIFIER_LEFT); - if (code_pressed_once(code)) - return code; - code.set_item_modifier(ITEM_MODIFIER_RIGHT); - if (code_pressed_once(code)) - return code; - } - - // if this is a joystick Y axis, check with up/down modifiers - else if (devclass == DEVICE_CLASS_JOYSTICK && code.item_id() == ITEM_ID_YAXIS) - { - code.set_item_modifier(ITEM_MODIFIER_UP); - if (code_pressed_once(code)) - return code; - code.set_item_modifier(ITEM_MODIFIER_DOWN); - if (code_pressed_once(code)) - return code; - } - - // any other axis, check with pos/neg modifiers - else - { - code.set_item_modifier(ITEM_MODIFIER_POS); - if (code_pressed_once(code)) - return code; - code.set_item_modifier(ITEM_MODIFIER_NEG); - if (code_pressed_once(code)) - return code; - } - } - } - } - - // if nothing, return an invalid code - return INPUT_CODE_INVALID; -} - - -//------------------------------------------------- -// poll_keyboard_switches - poll for any -// keyboard-specific input -//------------------------------------------------- - -input_code input_manager::poll_keyboard_switches() -{ - // iterate over devices within each class - input_class &keyboard_class = device_class(DEVICE_CLASS_KEYBOARD); - for (int devnum = 0; devnum < keyboard_class.maxindex(); devnum++) - { - // fetch the device; ignore if nullptr - input_device *device = keyboard_class.device(devnum); - if (device == nullptr) - continue; - - // iterate over items within each device - for (input_item_id itemid = ITEM_ID_FIRST_VALID; itemid <= device->maxitem(); ++itemid) - { - input_device_item *item = device->item(itemid); - if (item != nullptr && item->itemclass() == ITEM_CLASS_SWITCH) - { - input_code code = item->code(); - if (code_pressed_once(code)) - return code; - } - } - } - - // if nothing, return an invalid code - return INPUT_CODE_INVALID; -} - - -//------------------------------------------------- -// code_check_axis - see if axis has moved far -// enough to trigger a read when polling -//------------------------------------------------- - -bool input_manager::code_check_axis(input_device_item &item, input_code code) -{ - // if we've already reported this one, don't bother - if (item.memory() == INVALID_AXIS_VALUE) - return false; - - // ignore min/max for lightguns - // so the selection will not be affected by a gun going out of range - s32 curval = code_value(code); - if (code.device_class() == DEVICE_CLASS_LIGHTGUN && - (code.item_id() == ITEM_ID_XAXIS || code.item_id() == ITEM_ID_YAXIS) && - (curval == INPUT_ABSOLUTE_MAX || curval == INPUT_ABSOLUTE_MIN)) - return false; - - // compute the diff against memory - s32 diff = curval - item.memory(); - if (diff < 0) - diff = -diff; - - // for absolute axes, look for 25% of maximum - if (item.itemclass() == ITEM_CLASS_ABSOLUTE && diff > (INPUT_ABSOLUTE_MAX - INPUT_ABSOLUTE_MIN) / 4) - { - item.set_memory(INVALID_AXIS_VALUE); - return true; - } - - // for relative axes, look for ~20 pixels movement - if (item.itemclass() == ITEM_CLASS_RELATIVE && diff > 20 * INPUT_RELATIVE_PER_PIXEL) - { - item.set_memory(INVALID_AXIS_VALUE); - return true; - } - return false; -} - - -//------------------------------------------------- -// poll_axes - poll for any input -//------------------------------------------------- - -input_code input_manager::poll_axes() -{ - // iterate over device classes and devices - for (input_device_class devclass = DEVICE_CLASS_FIRST_VALID; devclass <= DEVICE_CLASS_LAST_VALID; ++devclass) - for (int devnum = 0; devnum <= m_class[devclass]->maxindex(); devnum++) - { - // fetch the device; ignore if nullptr - input_device *device = m_class[devclass]->device(devnum); - if (device == nullptr) - continue; - - // iterate over items within each device - for (input_item_id itemid = ITEM_ID_FIRST_VALID; itemid <= device->maxitem(); ++itemid) - { - input_device_item *item = device->item(itemid); - if (item != nullptr && item->itemclass() != ITEM_CLASS_SWITCH) - { - input_code code = item->code(); - if (code_check_axis(*item, code)) - return code; - } - } - } - - // if nothing, return an invalid code - return INPUT_CODE_INVALID; -} - - -//------------------------------------------------- // device_from_code - given an input_code return // a pointer to the associated device //------------------------------------------------- @@ -999,50 +646,44 @@ input_code input_manager::code_from_itemid(input_item_id itemid) const std::string input_manager::code_name(input_code code) const { // if nothing there, return an empty string - input_device_item *item = item_from_code(code); - if (item == nullptr) + input_device_item const *const item = item_from_code(code); + if (!item) return std::string(); - // determine the devclass part - const char *devclass = (*devclass_string_table)[code.device_class()]; - - // determine the devindex part - std::string devindex = string_format("%d", code.device_index() + 1); - - // if we're unifying all devices, don't display a number - if (!m_class[code.device_class()]->multi()) - devindex.clear(); + std::string str; // keyboard 0 doesn't show a class or index if it is the only one - input_device_class device_class = item->device().devclass(); - if (device_class == DEVICE_CLASS_KEYBOARD && m_class[device_class]->maxindex() == 0) + input_device_class const device_class = item->device().devclass(); + if ((device_class != DEVICE_CLASS_KEYBOARD) || (m_class[device_class]->maxindex() > 0)) { - devclass = ""; - devindex.clear(); - } - - // devcode part comes from the item name - const char *devcode = item->name(); + // determine the devclass part + str = (*devclass_string_table)[code.device_class()]; - // determine the modifier part - const char *modifier = (*modifier_string_table)[code.item_modifier()]; - - // devcode is redundant with joystick switch left/right/up/down - if (device_class == DEVICE_CLASS_JOYSTICK && code.item_class() == ITEM_CLASS_SWITCH) - if (code.item_modifier() >= ITEM_MODIFIER_LEFT && code.item_modifier() <= ITEM_MODIFIER_DOWN) - devcode = ""; + // if we're unifying all devices, don't display a number + if (m_class[code.device_class()]->multi()) + str.append(util::string_format(" %d ", code.device_index() + 1)); + else + str.append(" "); + } - // concatenate the strings - std::string str(devclass); - if (!devindex.empty()) - str.append(" ").append(devindex); - if (devcode[0] != 0) - str.append(" ").append(devcode); - if (modifier != nullptr) - str.append(" ").append(modifier); + // append item name - redundant with joystick switch left/right/up/down + bool const joydir = + (device_class == DEVICE_CLASS_JOYSTICK) && + (code.item_class() == ITEM_CLASS_SWITCH) && + (code.item_modifier() >= ITEM_MODIFIER_LEFT) && + (code.item_modifier() <= ITEM_MODIFIER_DOWN); + if (joydir) + { + str.append((*modifier_string_table)[code.item_modifier()]); + } + else + { + str.append(item->name()); + char const *const modifier = (*modifier_string_table)[code.item_modifier()]; + if (modifier && *modifier) + str.append(" ").append(modifier); + } - // delete any leading spaces - strtrimspace(str); return str; } @@ -1053,8 +694,12 @@ std::string input_manager::code_name(input_code code) const std::string input_manager::code_to_token(input_code code) const { + using namespace std::literals; + // determine the devclass part const char *devclass = (*devclass_token_table)[code.device_class()]; + if (!devclass) + return "INVALID"; // determine the devindex part; keyboard 0 doesn't show an index std::string devindex = string_format("%d", code.device_index() + 1); @@ -1063,25 +708,25 @@ std::string input_manager::code_to_token(input_code code) const // determine the itemid part; look up in the table if we don't have a token input_device_item *item = item_from_code(code); - const char *devcode = (item != nullptr) ? item->token() : "UNKNOWN"; + std::string_view devcode = item ? item->token() : "UNKNOWN"sv; // determine the modifier part const char *modifier = (*modifier_token_table)[code.item_modifier()]; // determine the itemclass part; if we match the native class, we don't include this const char *itemclass = ""; - if (item == nullptr || item->itemclass() != code.item_class()) + if (!item || (item->itemclass() != code.item_class())) itemclass = (*itemclass_token_table)[code.item_class()]; // concatenate the strings std::string str(devclass); if (!devindex.empty()) str.append("_").append(devindex); - if (devcode[0] != 0) + if (!devcode.empty()) str.append("_").append(devcode); if (modifier != nullptr) str.append("_").append(modifier); - if (itemclass[0] != 0) + if (itemclass != nullptr && itemclass[0] != 0) str.append("_").append(itemclass); return str; } @@ -1092,27 +737,27 @@ std::string input_manager::code_to_token(input_code code) const // token //------------------------------------------------- -input_code input_manager::code_from_token(const char *_token) +input_code input_manager::code_from_token(std::string_view _token) { // copy the token and break it into pieces std::string token[6]; - int numtokens; - for (numtokens = 0; numtokens < ARRAY_LENGTH(token); ) + int numtokens = 0; + while (numtokens < std::size(token)) { // make a token up to the next underscore - char *score = (char *)strchr(_token, '_'); - token[numtokens++].assign(_token, (score == nullptr) ? strlen(_token) : (score - _token)); + std::string_view::size_type score = _token.find('_'); + token[numtokens++].assign(_token, 0, (std::string_view::npos == score) ? _token.length() : score); // if we hit the end, we're done, else advance our pointer - if (score == nullptr) + if (std::string_view::npos == score) break; - _token = score + 1; + _token.remove_prefix(score + 1); } // first token should be the devclass int curtok = 0; - input_device_class devclass = input_device_class((*devclass_token_table)[token[curtok++].c_str()]); - if (devclass == ~input_device_class(0)) + input_device_class const devclass = input_device_class((*devclass_token_table)[token[curtok++]]); + if (devclass == input_device_class(code_string_table::SENTINEL)) return INPUT_CODE_INVALID; // second token might be index; look for number @@ -1126,27 +771,29 @@ input_code input_manager::code_from_token(const char *_token) return INPUT_CODE_INVALID; // next token is the item ID - input_item_id itemid = input_item_id((*itemid_token_table)[token[curtok].c_str()]); - bool standard = (itemid != ~input_item_id(0)); + input_item_id itemid = input_item_id((*itemid_token_table)[token[curtok]]); + bool const standard = (itemid != input_item_id(code_string_table::SENTINEL)); - // if we're a standard code, default the itemclass based on it input_item_class itemclass = ITEM_CLASS_INVALID; if (standard) + { + // if we're a standard code, default the itemclass based on it itemclass = m_class[devclass]->standard_item_class(itemid); - - // otherwise, keep parsing + } else { + // otherwise, keep parsing + // if this is an invalid device, we have nothing to look up input_device *device = m_class[devclass]->device(devindex); - if (device == nullptr) + if (!device) return INPUT_CODE_INVALID; // if not a standard code, look it up in the device specific codes for (itemid = ITEM_ID_FIRST_VALID; itemid <= device->maxitem(); ++itemid) { input_device_item *item = device->item(itemid); - if (item != nullptr && token[curtok].compare(item->token()) == 0) + if (item && !token[curtok].compare(item->token())) { // take the itemclass from the item itemclass = item->itemclass(); @@ -1164,8 +811,8 @@ input_code input_manager::code_from_token(const char *_token) input_item_modifier modifier = ITEM_MODIFIER_NONE; if (curtok < numtokens) { - modifier = input_item_modifier((*modifier_token_table)[token[curtok].c_str()]); - if (modifier != ~input_item_modifier(0)) + modifier = input_item_modifier((*modifier_token_table)[token[curtok]]); + if (modifier != input_item_modifier(code_string_table::SENTINEL)) curtok++; else modifier = ITEM_MODIFIER_NONE; @@ -1174,8 +821,8 @@ input_code input_manager::code_from_token(const char *_token) // if we have another token, it is the item class if (curtok < numtokens) { - u32 temp = (*itemclass_token_table)[token[curtok].c_str()]; - if (temp != ~0) + u32 const temp = (*itemclass_token_table)[token[curtok]]; + if (temp != code_string_table::SENTINEL) { curtok++; itemclass = input_item_class(temp); @@ -1198,7 +845,7 @@ input_code input_manager::code_from_token(const char *_token) const char *input_manager::standard_token(input_item_id itemid) const { - return itemid <= ITEM_ID_MAXIMUM ? (*itemid_token_table)[itemid] : nullptr; + return (itemid <= ITEM_ID_MAXIMUM) ? (*itemid_token_table)[itemid] : nullptr; } @@ -1215,14 +862,16 @@ bool input_manager::seq_pressed(const input_seq &seq) bool first = true; for (int codenum = 0; ; codenum++) { - // handle NOT input_code code = seq[codenum]; if (code == input_seq::not_code) + { + // handle NOT invert = true; - - // handle OR and END + } else if (code == input_seq::or_code || code == input_seq::end_code) { + // handle OR and END + // if we have a positive result from the previous set, we're done if (result || code == input_seq::end_code) break; @@ -1232,10 +881,10 @@ bool input_manager::seq_pressed(const input_seq &seq) invert = false; first = true; } - - // handle everything else as a series of ANDs else { + // handle everything else as a series of ANDs + // if this is the first in the sequence, result is set equal if (first) result = code_pressed(code) ^ invert; @@ -1261,233 +910,117 @@ bool input_manager::seq_pressed(const input_seq &seq) s32 input_manager::seq_axis_value(const input_seq &seq, input_item_class &itemclass) { - // start with no valid classes - input_item_class itemclasszero = ITEM_CLASS_INVALID; + // start with zero result and no valid classes + s32 result = 0; itemclass = ITEM_CLASS_INVALID; + input_item_class itemclasszero = ITEM_CLASS_INVALID; // iterate over all of the codes - s32 result = 0; + s32 groupval = 0; + input_item_class groupclass = ITEM_CLASS_INVALID; + input_item_class groupclasszero = ITEM_CLASS_INVALID; bool invert = false; bool enable = true; for (int codenum = 0; ; codenum++) { - // handle NOT - input_code code = seq[codenum]; + input_code const code = seq[codenum]; if (code == input_seq::not_code) + { + // handle NOT - invert the next code invert = true; - - // handle OR and END - else if (code == input_seq::or_code || code == input_seq::end_code) + } + else if (code == input_seq::end_code) { - // if we have a positive result from the previous set, we're done - if (itemclass != ITEM_CLASS_INVALID || code == input_seq::end_code) - break; - - // otherwise, reset our state - result = 0; + // handle END - commit group and break out of loop + accumulate_axis_value( + result, itemclass, itemclasszero, + groupval, groupclass, groupclasszero); + break; + } + else if (code == input_seq::or_code) + { + // handle OR - commit group and reset for the next group + accumulate_axis_value( + result, itemclass, itemclasszero, + groupval, groupclass, groupclasszero); + groupval = 0; + groupclasszero = ITEM_CLASS_INVALID; + groupclass = ITEM_CLASS_INVALID; invert = false; enable = true; } - - // handle everything else only if we're still enabled else if (enable) { + // handle everything else only if we're still enabled + input_item_class const codeclass = code.item_class(); + // switch codes serve as enables - if (code.item_class() == ITEM_CLASS_SWITCH) + if (ITEM_CLASS_SWITCH == codeclass) { // AND against previous digital codes if (enable) - enable &= code_pressed(code) ^ invert; + { + enable = code_pressed(code) ^ invert; + if (!enable) + { + // clear current group if enable became false - only way out is an OR code + groupval = 0; + groupclasszero = ITEM_CLASS_INVALID; + groupclass = ITEM_CLASS_INVALID; + } + } } - - // non-switch codes are analog values else { - s32 value = code_value(code); - - // if we got a 0 value, don't do anything except remember the first type - if (value == 0) - { - if (itemclasszero == ITEM_CLASS_INVALID) - itemclasszero = code.item_class(); - } - - // non-zero absolute values stick - else if (code.item_class() == ITEM_CLASS_ABSOLUTE) - { - itemclass = ITEM_CLASS_ABSOLUTE; - result = value; - } - - // non-zero relative values accumulate - else if (code.item_class() == ITEM_CLASS_RELATIVE) - { - itemclass = ITEM_CLASS_RELATIVE; - result += value; - } + // non-switch codes are analog values + accumulate_axis_value( + groupval, groupclass, groupclasszero, + code_value(code), codeclass, codeclass); } - // clear the invert flag + // clear the invert flag - it only applies to one item invert = false; } } - // if the caller wants to know the type, provide it - if (result == 0) + // saturate mixed absolute values, report neutral type + if (ITEM_CLASS_ABSOLUTE == itemclass) + result = std::clamp(result, osd::input_device::ABSOLUTE_MIN, osd::input_device::ABSOLUTE_MAX); + else if (ITEM_CLASS_INVALID == itemclass) itemclass = itemclasszero; return result; } //------------------------------------------------- -// seq_poll_start - begin polling for a new -// sequence of the given itemclass -//------------------------------------------------- - -void input_manager::seq_poll_start(input_item_class itemclass, const input_seq *startseq) -{ - assert(itemclass == ITEM_CLASS_SWITCH || itemclass == ITEM_CLASS_ABSOLUTE || itemclass == ITEM_CLASS_RELATIVE); - - // reset the recording count and the clock - m_poll_seq_last_ticks = 0; - m_poll_seq_class = itemclass; - m_poll_seq.reset(); - - // grab the starting sequence to append to, and append an OR - if (startseq != nullptr) - { - m_poll_seq = *startseq; - if (m_poll_seq.length() > 0) - m_poll_seq += input_seq::or_code; - } - - // flush out any goobers - reset_polling(); - input_code dummycode = KEYCODE_ENTER; - while (dummycode != INPUT_CODE_INVALID) - dummycode = (m_poll_seq_class == ITEM_CLASS_SWITCH) ? poll_switches() : poll_axes(); -} - - -//------------------------------------------------- -// input_seq_poll - continue polling -//------------------------------------------------- - -bool input_manager::seq_poll() -{ - int curlen = m_poll_seq.length(); - input_code lastcode = m_poll_seq[curlen - 1]; - - // switch case: see if we have a new code to process - input_code newcode; - if (m_poll_seq_class == ITEM_CLASS_SWITCH) - { - newcode = poll_switches(); - if (newcode != INPUT_CODE_INVALID) - { - // if code is duplicate, toggle the NOT state on the code - if (curlen > 0 && newcode == lastcode) - { - // back up over the existing code - m_poll_seq.backspace(); - - // if there was a NOT preceding it, delete it as well, otherwise append a fresh one - if (m_poll_seq[curlen - 2] == input_seq::not_code) - m_poll_seq.backspace(); - else - m_poll_seq += input_seq::not_code; - } - } - } - - // absolute/relative case: see if we have an analog change of sufficient amount - else - { - bool has_or = false; - if (lastcode == input_seq::or_code) - { - lastcode = m_poll_seq[curlen - 2]; - has_or = true; - } - newcode = poll_axes(); - - // if the last code doesn't match absolute/relative of this code, ignore the new one - if ((lastcode.item_class() == ITEM_CLASS_ABSOLUTE && newcode.item_class() != ITEM_CLASS_ABSOLUTE) || - (lastcode.item_class() == ITEM_CLASS_RELATIVE && newcode.item_class() != ITEM_CLASS_RELATIVE)) - newcode = INPUT_CODE_INVALID; - - // if the new code is valid, check for half-axis toggles on absolute controls - if (newcode != INPUT_CODE_INVALID && curlen > 0 && newcode.item_class() == ITEM_CLASS_ABSOLUTE) - { - input_code last_nomodifier = lastcode; - last_nomodifier.set_item_modifier(ITEM_MODIFIER_NONE); - if (newcode == last_nomodifier) - { - // increment the modifier, wrapping back to none - switch (lastcode.item_modifier()) - { - case ITEM_MODIFIER_NONE: newcode.set_item_modifier(ITEM_MODIFIER_POS); break; - case ITEM_MODIFIER_POS: newcode.set_item_modifier(ITEM_MODIFIER_NEG); break; - default: - case ITEM_MODIFIER_NEG: newcode.set_item_modifier(ITEM_MODIFIER_NONE); break; - } - - // back up over the previous code so we can re-append - if (has_or) - m_poll_seq.backspace(); - m_poll_seq.backspace(); - } - } - } - - // if we got a new code to append it, append it and reset the timer - if (newcode != INPUT_CODE_INVALID) - { - m_poll_seq += newcode; - m_poll_seq_last_ticks = osd_ticks(); - } - - // if we're recorded at least one item and 2/3 of a second has passed, we're done - if (m_poll_seq_last_ticks != 0 && osd_ticks() > m_poll_seq_last_ticks + osd_ticks_per_second() * 2 / 3) - { - // if the final result is invalid, reset to nothing - if (!m_poll_seq.is_valid()) - m_poll_seq.reset(); - - // return true to indicate that we are finished - return true; - } - - // return false to indicate we are still polling - return false; -} - - -//------------------------------------------------- // seq_clean - clean the sequence, removing // any invalid bits //------------------------------------------------- input_seq input_manager::seq_clean(const input_seq &seq) const { + // make a copy of our sequence, removing any invalid bits + input_seq clean_codes; int clean_index = 0; for (int codenum = 0; seq[codenum] != input_seq::end_code; codenum++) { // if this is a code item which is not valid, don't copy it and remove any preceding ORs/NOTs input_code code = seq[codenum]; - if (!code.internal() && code_name(code).empty()) + if (!code.internal() && (((code.device_index() > 0) && !m_class[code.device_class()]->multi()) || !item_from_code(code))) { - while (clean_index > 0 && seq[clean_index - 1].internal()) + while (clean_index > 0 && clean_codes[clean_index - 1].internal()) + { + clean_codes.backspace(); clean_index--; + } } - else if (clean_index > 0 || !code.internal()) + else if (clean_index > 0 || !code.internal() || code == input_seq::not_code) + { + clean_codes += code; clean_index++; + } } - - input_seq cleaned_seq; - for (int i = 0; i < clean_index; i++) - cleaned_seq += seq[i]; - return cleaned_seq; + return clean_codes; } @@ -1503,7 +1036,7 @@ std::string input_manager::seq_name(const input_seq &seq) const // special case: empty if (cleaned_seq[0] == input_seq::end_code) - return std::string((seq.length() == 0) ? "None" : "n/a"); + return std::string(seq.empty() ? "None" : "n/a"); // start with an empty buffer std::string str; @@ -1569,53 +1102,52 @@ std::string input_manager::seq_to_tokens(const input_seq &seq) const // of a sequence //------------------------------------------------- -void input_manager::seq_from_tokens(input_seq &seq, const char *string) +void input_manager::seq_from_tokens(input_seq &seq, std::string_view string) { // start with a blank sequence seq.reset(); // loop until we're done - std::vector<char> strcopy(string, string + std::strlen(string) + 1); - char *str = &strcopy[0]; unsigned operators = 0; while (1) { // trim any leading spaces - while (*str != 0 && isspace(u8(*str))) - str++; + while (!string.empty() && isspace(u8(string[0]))) + string.remove_prefix(1); // bail if we're done - if (*str == 0) + if (string.empty()) return; // find the end of the token and make it upper-case along the way - char *strtemp; - for (strtemp = str; *strtemp != 0 && !isspace(u8(*strtemp)); strtemp++) - *strtemp = toupper(u8(*strtemp)); - char origspace = *strtemp; - *strtemp = 0; + std::string token; + while (!string.empty() && !isspace(u8(string[0]))) + { + token.push_back(toupper(u8(string[0]))); + string.remove_prefix(1); + } // look for common stuff input_code code; bool is_operator; - if (strcmp(str, "OR") == 0) + if (token == "OR") { code = input_seq::or_code; is_operator = true; } - else if (strcmp(str, "NOT") == 0) + else if (token == "NOT") { code = input_seq::not_code; is_operator = true; } - else if (strcmp(str, "DEFAULT") == 0) + else if (token == "DEFAULT") { code = input_seq::default_code; is_operator = false; } else { - code = code_from_token(str); + code = code_from_token(token); is_operator = false; } @@ -1629,7 +1161,7 @@ void input_manager::seq_from_tokens(input_seq &seq, const char *string) { if (code.device_class() < DEVICE_CLASS_FIRST_VALID) { - osd_printf_warning("Input: Dropping invalid input token %s\n", str); + osd_printf_warning("Input: Dropping invalid input token %s\n", token); while (operators) { seq.backspace(); @@ -1644,9 +1176,9 @@ void input_manager::seq_from_tokens(input_seq &seq, const char *string) } // advance - if (origspace == 0) + if (string.empty()) return; - str = strtemp + 1; + string.remove_prefix(1); } } @@ -1655,37 +1187,34 @@ void input_manager::seq_from_tokens(input_seq &seq, const char *string) // controller based on device map table //------------------------------------------------- -bool input_manager::map_device_to_controller(const devicemap_table_type *devicemap_table) +bool input_manager::map_device_to_controller(const devicemap_table &table) { - if (nullptr == devicemap_table) - return true; - - for (devicemap_table_type::const_iterator it = devicemap_table->begin(); it != devicemap_table->end(); it++) + for (const auto &it : table) { - const char *deviceid = it->first.c_str(); - const char *controllername = it->second.c_str(); + std::string_view deviceid = it.first; + std::string_view controllername = it.second; // tokenize the controller name into device class and index (i.e. controller name should be of the form "GUNCODE_1") std::string token[2]; - int numtokens; - const char *_token = controllername; - for (numtokens = 0; numtokens < ARRAY_LENGTH(token); ) + int numtokens = 0; + std::string_view _token = controllername; + while (numtokens < std::size(token)) { // make a token up to the next underscore - char *score = (char *)strchr(_token, '_'); - token[numtokens++].assign(_token, (score == nullptr) ? strlen(_token) : (score - _token)); + std::string_view::size_type score = _token.find('_'); + token[numtokens++].assign(_token, 0, (std::string_view::npos == score) ? _token.length() : score); // if we hit the end, we're done, else advance our pointer - if (score == nullptr) + if (std::string_view::npos == score) break; - _token = score + 1; + _token.remove_prefix(score + 1); } if (2 != numtokens) return false; // first token should be the devclass - input_device_class devclass = input_device_class((*devclass_token_table)[strmakeupper(token[0]).c_str()]); - if (devclass == ~input_device_class(0)) + input_device_class const devclass = input_device_class((*devclass_token_table)[strmakeupper(token[0])]); + if (devclass == input_device_class(code_string_table::SENTINEL)) return false; // second token should be the devindex @@ -1702,11 +1231,11 @@ bool input_manager::map_device_to_controller(const devicemap_table_type *devicem for (int devnum = 0; devnum <= input_devclass->maxindex(); devnum++) { input_device *device = input_devclass->device(devnum); - if (device != nullptr && device->match_device_id(deviceid)) + if (device && device->match_device_id(deviceid)) { // remap devindex input_devclass->remap_device_index(device->devindex(), devindex); - osd_printf_verbose("Input: Remapped %s #%d: %s (device id: %s)\n", input_devclass->name(), devindex, device->name(), device->id()); + osd_printf_verbose("Input: Remapped %s #%d: %s (device id: %s)\n", input_devclass->name(), devindex + 1, device->name(), device->id()); break; } diff --git a/src/emu/input.h b/src/emu/input.h index 3ca97ae1e99..7c00a590a8d 100644 --- a/src/emu/input.h +++ b/src/emu/input.h @@ -17,439 +17,44 @@ #ifndef MAME_EMU_INPUT_H #define MAME_EMU_INPUT_H +#include "interface/inputcode.h" +#include "interface/inputman.h" +#include "interface/inputseq.h" -//************************************************************************** -// CONSTANTS -//************************************************************************** - -// relative devices return ~512 units per onscreen pixel -constexpr s32 INPUT_RELATIVE_PER_PIXEL = 512; - -// absolute devices return values between -65536 and +65536 -constexpr s32 INPUT_ABSOLUTE_MIN = -65536; -constexpr s32 INPUT_ABSOLUTE_MAX = 65536; - -// maximum number of axis/buttons/hats with ITEM_IDs for use by osd layer -constexpr int INPUT_MAX_AXIS = 8; -constexpr int INPUT_MAX_BUTTONS = 32; -constexpr int INPUT_MAX_HATS = 4; -constexpr int INPUT_MAX_ADD_SWITCH = 16; -constexpr int INPUT_MAX_ADD_ABSOLUTE = 16; -constexpr int INPUT_MAX_ADD_RELATIVE = 16; - - -// device classes -enum input_device_class -{ - DEVICE_CLASS_INVALID, - DEVICE_CLASS_FIRST_VALID, - DEVICE_CLASS_KEYBOARD = DEVICE_CLASS_FIRST_VALID, - DEVICE_CLASS_MOUSE, - DEVICE_CLASS_LIGHTGUN, - DEVICE_CLASS_JOYSTICK, - DEVICE_CLASS_LAST_VALID = DEVICE_CLASS_JOYSTICK, - DEVICE_CLASS_INTERNAL, - DEVICE_CLASS_MAXIMUM -}; -DECLARE_ENUM_INCDEC_OPERATORS(input_device_class) - - -// device index -constexpr int DEVICE_INDEX_MAXIMUM = 0xff; - - -// input item classes -enum input_item_class -{ - ITEM_CLASS_INVALID, - ITEM_CLASS_SWITCH, - ITEM_CLASS_ABSOLUTE, - ITEM_CLASS_RELATIVE, - ITEM_CLASS_MAXIMUM -}; - - -// input item modifiers -enum input_item_modifier -{ - ITEM_MODIFIER_NONE, - ITEM_MODIFIER_POS, - ITEM_MODIFIER_NEG, - ITEM_MODIFIER_LEFT, - ITEM_MODIFIER_RIGHT, - ITEM_MODIFIER_UP, - ITEM_MODIFIER_DOWN, - ITEM_MODIFIER_MAXIMUM -}; - - -// standard item IDs -enum input_item_id -{ - ITEM_ID_INVALID, - ITEM_ID_FIRST_VALID, - - // standard keyboard IDs - ITEM_ID_A = ITEM_ID_FIRST_VALID, - ITEM_ID_B, - ITEM_ID_C, - ITEM_ID_D, - ITEM_ID_E, - ITEM_ID_F, - ITEM_ID_G, - ITEM_ID_H, - ITEM_ID_I, - ITEM_ID_J, - ITEM_ID_K, - ITEM_ID_L, - ITEM_ID_M, - ITEM_ID_N, - ITEM_ID_O, - ITEM_ID_P, - ITEM_ID_Q, - ITEM_ID_R, - ITEM_ID_S, - ITEM_ID_T, - ITEM_ID_U, - ITEM_ID_V, - ITEM_ID_W, - ITEM_ID_X, - ITEM_ID_Y, - ITEM_ID_Z, - ITEM_ID_0, - ITEM_ID_1, - ITEM_ID_2, - ITEM_ID_3, - ITEM_ID_4, - ITEM_ID_5, - ITEM_ID_6, - ITEM_ID_7, - ITEM_ID_8, - ITEM_ID_9, - ITEM_ID_F1, - ITEM_ID_F2, - ITEM_ID_F3, - ITEM_ID_F4, - ITEM_ID_F5, - ITEM_ID_F6, - ITEM_ID_F7, - ITEM_ID_F8, - ITEM_ID_F9, - ITEM_ID_F10, - ITEM_ID_F11, - ITEM_ID_F12, - ITEM_ID_F13, - ITEM_ID_F14, - ITEM_ID_F15, - ITEM_ID_F16, - ITEM_ID_F17, - ITEM_ID_F18, - ITEM_ID_F19, - ITEM_ID_F20, - ITEM_ID_ESC, - ITEM_ID_TILDE, - ITEM_ID_MINUS, - ITEM_ID_EQUALS, - ITEM_ID_BACKSPACE, - ITEM_ID_TAB, - ITEM_ID_OPENBRACE, - ITEM_ID_CLOSEBRACE, - ITEM_ID_ENTER, - ITEM_ID_COLON, - ITEM_ID_QUOTE, - ITEM_ID_BACKSLASH, - ITEM_ID_BACKSLASH2, - ITEM_ID_COMMA, - ITEM_ID_STOP, - ITEM_ID_SLASH, - ITEM_ID_SPACE, - ITEM_ID_INSERT, - ITEM_ID_DEL, - ITEM_ID_HOME, - ITEM_ID_END, - ITEM_ID_PGUP, - ITEM_ID_PGDN, - ITEM_ID_LEFT, - ITEM_ID_RIGHT, - ITEM_ID_UP, - ITEM_ID_DOWN, - ITEM_ID_0_PAD, - ITEM_ID_1_PAD, - ITEM_ID_2_PAD, - ITEM_ID_3_PAD, - ITEM_ID_4_PAD, - ITEM_ID_5_PAD, - ITEM_ID_6_PAD, - ITEM_ID_7_PAD, - ITEM_ID_8_PAD, - ITEM_ID_9_PAD, - ITEM_ID_SLASH_PAD, - ITEM_ID_ASTERISK, - ITEM_ID_MINUS_PAD, - ITEM_ID_PLUS_PAD, - ITEM_ID_DEL_PAD, - ITEM_ID_ENTER_PAD, - ITEM_ID_BS_PAD, - ITEM_ID_TAB_PAD, - ITEM_ID_00_PAD, - ITEM_ID_000_PAD, - ITEM_ID_COMMA_PAD, - ITEM_ID_EQUALS_PAD, - ITEM_ID_PRTSCR, - ITEM_ID_PAUSE, - ITEM_ID_LSHIFT, - ITEM_ID_RSHIFT, - ITEM_ID_LCONTROL, - ITEM_ID_RCONTROL, - ITEM_ID_LALT, - ITEM_ID_RALT, - ITEM_ID_SCRLOCK, - ITEM_ID_NUMLOCK, - ITEM_ID_CAPSLOCK, - ITEM_ID_LWIN, - ITEM_ID_RWIN, - ITEM_ID_MENU, - ITEM_ID_CANCEL, - - // standard mouse/joystick/gun IDs - ITEM_ID_XAXIS, - ITEM_ID_YAXIS, - ITEM_ID_ZAXIS, - ITEM_ID_RXAXIS, - ITEM_ID_RYAXIS, - ITEM_ID_RZAXIS, - ITEM_ID_SLIDER1, - ITEM_ID_SLIDER2, - ITEM_ID_BUTTON1, - ITEM_ID_BUTTON2, - ITEM_ID_BUTTON3, - ITEM_ID_BUTTON4, - ITEM_ID_BUTTON5, - ITEM_ID_BUTTON6, - ITEM_ID_BUTTON7, - ITEM_ID_BUTTON8, - ITEM_ID_BUTTON9, - ITEM_ID_BUTTON10, - ITEM_ID_BUTTON11, - ITEM_ID_BUTTON12, - ITEM_ID_BUTTON13, - ITEM_ID_BUTTON14, - ITEM_ID_BUTTON15, - ITEM_ID_BUTTON16, - ITEM_ID_BUTTON17, - ITEM_ID_BUTTON18, - ITEM_ID_BUTTON19, - ITEM_ID_BUTTON20, - ITEM_ID_BUTTON21, - ITEM_ID_BUTTON22, - ITEM_ID_BUTTON23, - ITEM_ID_BUTTON24, - ITEM_ID_BUTTON25, - ITEM_ID_BUTTON26, - ITEM_ID_BUTTON27, - ITEM_ID_BUTTON28, - ITEM_ID_BUTTON29, - ITEM_ID_BUTTON30, - ITEM_ID_BUTTON31, - ITEM_ID_BUTTON32, - ITEM_ID_START, - ITEM_ID_SELECT, - - // Hats - ITEM_ID_HAT1UP, - ITEM_ID_HAT1DOWN, - ITEM_ID_HAT1LEFT, - ITEM_ID_HAT1RIGHT, - ITEM_ID_HAT2UP, - ITEM_ID_HAT2DOWN, - ITEM_ID_HAT2LEFT, - ITEM_ID_HAT2RIGHT, - ITEM_ID_HAT3UP, - ITEM_ID_HAT3DOWN, - ITEM_ID_HAT3LEFT, - ITEM_ID_HAT3RIGHT, - ITEM_ID_HAT4UP, - ITEM_ID_HAT4DOWN, - ITEM_ID_HAT4LEFT, - ITEM_ID_HAT4RIGHT, - - // Additional IDs - ITEM_ID_ADD_SWITCH1, - ITEM_ID_ADD_SWITCH2, - ITEM_ID_ADD_SWITCH3, - ITEM_ID_ADD_SWITCH4, - ITEM_ID_ADD_SWITCH5, - ITEM_ID_ADD_SWITCH6, - ITEM_ID_ADD_SWITCH7, - ITEM_ID_ADD_SWITCH8, - ITEM_ID_ADD_SWITCH9, - ITEM_ID_ADD_SWITCH10, - ITEM_ID_ADD_SWITCH11, - ITEM_ID_ADD_SWITCH12, - ITEM_ID_ADD_SWITCH13, - ITEM_ID_ADD_SWITCH14, - ITEM_ID_ADD_SWITCH15, - ITEM_ID_ADD_SWITCH16, - - ITEM_ID_ADD_ABSOLUTE1, - ITEM_ID_ADD_ABSOLUTE2, - ITEM_ID_ADD_ABSOLUTE3, - ITEM_ID_ADD_ABSOLUTE4, - ITEM_ID_ADD_ABSOLUTE5, - ITEM_ID_ADD_ABSOLUTE6, - ITEM_ID_ADD_ABSOLUTE7, - ITEM_ID_ADD_ABSOLUTE8, - ITEM_ID_ADD_ABSOLUTE9, - ITEM_ID_ADD_ABSOLUTE10, - ITEM_ID_ADD_ABSOLUTE11, - ITEM_ID_ADD_ABSOLUTE12, - ITEM_ID_ADD_ABSOLUTE13, - ITEM_ID_ADD_ABSOLUTE14, - ITEM_ID_ADD_ABSOLUTE15, - ITEM_ID_ADD_ABSOLUTE16, - - ITEM_ID_ADD_RELATIVE1, - ITEM_ID_ADD_RELATIVE2, - ITEM_ID_ADD_RELATIVE3, - ITEM_ID_ADD_RELATIVE4, - ITEM_ID_ADD_RELATIVE5, - ITEM_ID_ADD_RELATIVE6, - ITEM_ID_ADD_RELATIVE7, - ITEM_ID_ADD_RELATIVE8, - ITEM_ID_ADD_RELATIVE9, - ITEM_ID_ADD_RELATIVE10, - ITEM_ID_ADD_RELATIVE11, - ITEM_ID_ADD_RELATIVE12, - ITEM_ID_ADD_RELATIVE13, - ITEM_ID_ADD_RELATIVE14, - ITEM_ID_ADD_RELATIVE15, - ITEM_ID_ADD_RELATIVE16, - - // generic other IDs - ITEM_ID_OTHER_SWITCH, - ITEM_ID_OTHER_AXIS_ABSOLUTE, - ITEM_ID_OTHER_AXIS_RELATIVE, - ITEM_ID_MAXIMUM, - - // internal codes for sequences - ITEM_ID_SEQ_END, - ITEM_ID_SEQ_DEFAULT, - ITEM_ID_SEQ_NOT, - ITEM_ID_SEQ_OR, - - // absolute maximum ID - ITEM_ID_ABSOLUTE_MAXIMUM = 0xfff -}; -DECLARE_ENUM_INCDEC_OPERATORS(input_item_id) - +#include <algorithm> +#include <array> +#include <cassert> +#include <iterator> +#include <map> +#include <memory> +#include <string> +#include <utility> //************************************************************************** // TYPE DEFINITIONS //************************************************************************** -// controller alias table typedef -typedef std::map<std::string, std::string> devicemap_table_type; - -// ======================> input_code - -// a combined code that describes a particular input on a particular device -class input_code -{ -public: - // construction/destruction - input_code(input_device_class devclass = DEVICE_CLASS_INVALID, int devindex = 0, input_item_class itemclass = ITEM_CLASS_INVALID, input_item_modifier modifier = ITEM_MODIFIER_NONE, input_item_id itemid = ITEM_ID_INVALID) - : m_internal(((devclass & 0xf) << 28) | ((devindex & 0xff) << 20) | ((itemclass & 0xf) << 16) | ((modifier & 0xf) << 12) | (itemid & 0xfff)) - { - assert(devclass >= 0 && devclass < DEVICE_CLASS_MAXIMUM); - assert(devindex >= 0 && devindex < DEVICE_INDEX_MAXIMUM); - assert(itemclass >= 0 && itemclass < ITEM_CLASS_MAXIMUM); - assert(modifier >= 0 && modifier < ITEM_MODIFIER_MAXIMUM); - assert(itemid >= 0 && itemid < ITEM_ID_ABSOLUTE_MAXIMUM); - } - input_code(const input_code &src) - : m_internal(src.m_internal) { } - - // operators - bool operator==(const input_code &rhs) const { return m_internal == rhs.m_internal; } - bool operator!=(const input_code &rhs) const { return m_internal != rhs.m_internal; } - - // getters - bool internal() const { return device_class() == DEVICE_CLASS_INTERNAL; } - input_device_class device_class() const { return input_device_class((m_internal >> 28) & 0xf); } - int device_index() const { return ((m_internal >> 20) & 0xff); } - input_item_class item_class() const { return input_item_class((m_internal >> 16) & 0xf); } - input_item_modifier item_modifier() const { return input_item_modifier((m_internal >> 12) & 0xf); } - input_item_id item_id() const { return input_item_id(m_internal & 0xfff); } - - // setters - void set_device_class(input_device_class devclass) { assert(devclass >= 0 && devclass <= 0xf); m_internal = (m_internal & ~(0xf << 28)) | ((devclass & 0xf) << 28); } - void set_device_index(int devindex) { assert(devindex >= 0 && devindex <= 0xff); m_internal = (m_internal & ~(0xff << 20)) | ((devindex & 0xff) << 20); } - void set_item_class(input_item_class itemclass) { assert(itemclass >= 0 && itemclass <= 0xf); m_internal = (m_internal & ~(0xf << 16)) | ((itemclass & 0xf) << 16); } - void set_item_modifier(input_item_modifier modifier) { assert(modifier >= 0 && modifier <= 0xf); m_internal = (m_internal & ~(0xf << 12)) | ((modifier & 0xf) << 12); } - void set_item_id(input_item_id itemid) { assert(itemid >= 0 && itemid <= 0xfff); m_internal = (m_internal & ~0xfff) | (itemid & 0xfff); } - -private: - // internal state - u32 m_internal; -}; - - -// ======================> input_seq - -// a sequence of input_codes, supporting AND/OR and inversion -class input_seq -{ -public: - // construction/destruction - input_seq(input_code code0 = input_seq::end_code, input_code code1 = input_seq::end_code, input_code code2 = input_seq::end_code, input_code code3 = input_seq::end_code, input_code code4 = input_seq::end_code, input_code code5 = input_seq::end_code, input_code code6 = input_seq::end_code) - { set(code0, code1, code2, code3, code4, code5, code6); } - input_seq(const input_seq &rhs) { memcpy(m_code, rhs.m_code, sizeof(m_code)); } - - // operators - bool operator==(const input_seq &rhs) const { return (memcmp(m_code, rhs.m_code, sizeof(m_code)) == 0); } - bool operator!=(const input_seq &rhs) const { return (memcmp(m_code, rhs.m_code, sizeof(m_code)) != 0); } - input_code operator[](int index) const { return (index >= 0 && index < ARRAY_LENGTH(m_code)) ? m_code[index] : input_seq::end_code; } - input_seq &operator+=(input_code code); - input_seq &operator|=(input_code code); - - // getters - int length() const; - bool is_valid() const; - bool is_default() const { return m_code[0] == default_code; } - - // setters - void set(input_code code0 = input_seq::end_code, input_code code1 = input_seq::end_code, input_code code2 = input_seq::end_code, input_code code3 = input_seq::end_code, input_code code4 = input_seq::end_code, input_code code5 = input_seq::end_code, input_code code6 = input_seq::end_code); - void reset() { set(); } - void set_default() { set(default_code); } - void backspace(); - void replace(input_code oldcode, input_code newcode); - - // constant codes used in sequences - static const input_code end_code; - static const input_code default_code; - static const input_code not_code; - static const input_code or_code; - - // constant sequences - static const input_seq empty_seq; - -private: - // internal state - input_code m_code[16]; -}; +using osd::input_seq; // too much trouble to qualify this everywhere // ======================> input_manager // global machine-level information about devices -class input_manager +class input_manager : public osd::input_manager { public: + // controller alias table typedef + using devicemap_table = std::map<std::string, std::string>; + // construction/destruction input_manager(running_machine &machine); ~input_manager(); + // OSD interface + virtual bool class_enabled(input_device_class devclass) const override; + virtual osd::input_device &add_device(input_device_class devclass, std::string_view name, std::string_view id, void *internal) override; + // getters running_machine &machine() const { return m_machine; } input_class &device_class(input_device_class devclass) { assert(devclass >= DEVICE_CLASS_FIRST_VALID && devclass <= DEVICE_CLASS_LAST_VALID); return *m_class[devclass]; } @@ -459,43 +64,31 @@ public: bool code_pressed(input_code code) { return code_value(code) != 0; } bool code_pressed_once(input_code code); - // input code polling - void reset_polling(); - input_code poll_axes(); - input_code poll_switches(); - input_code poll_keyboard_switches(); - // input code helpers input_device *device_from_code(input_code code) const; input_device_item *item_from_code(input_code code) const; input_code code_from_itemid(input_item_id itemid) const; std::string code_name(input_code code) const; std::string code_to_token(input_code code) const; - input_code code_from_token(const char *_token); + input_code code_from_token(std::string_view _token); const char *standard_token(input_item_id itemid) const; // input sequence readers bool seq_pressed(const input_seq &seq); s32 seq_axis_value(const input_seq &seq, input_item_class &itemclass); - // input sequence polling - void seq_poll_start(input_item_class itemclass, const input_seq *startseq = nullptr); - bool seq_poll(); - const input_seq &seq_poll_final() const { return m_poll_seq; } - // input sequence helpers input_seq seq_clean(const input_seq &seq) const; std::string seq_name(const input_seq &seq) const; std::string seq_to_tokens(const input_seq &seq) const; - void seq_from_tokens(input_seq &seq, const char *_token); + void seq_from_tokens(input_seq &seq, std::string_view _token); // misc - bool map_device_to_controller(const devicemap_table_type *devicemap_table = nullptr); + bool map_device_to_controller(const devicemap_table &table); private: // internal helpers void reset_memory(); - bool code_check_axis(input_device_item &item, input_code code); // internal state running_machine & m_machine; @@ -503,474 +96,7 @@ private: // classes std::array<std::unique_ptr<input_class>, DEVICE_CLASS_MAXIMUM> m_class; - - // sequence polling state - input_seq m_poll_seq; - osd_ticks_t m_poll_seq_last_ticks; - input_item_class m_poll_seq_class; }; - -//************************************************************************** -// MACROS -//************************************************************************** - -// invalid codes -#define INPUT_CODE_INVALID input_code() - -// keyboard codes -#define KEYCODE_A_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_A) -#define KEYCODE_B_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_B) -#define KEYCODE_C_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_C) -#define KEYCODE_D_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_D) -#define KEYCODE_E_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_E) -#define KEYCODE_F_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_F) -#define KEYCODE_G_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_G) -#define KEYCODE_H_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_H) -#define KEYCODE_I_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_I) -#define KEYCODE_J_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_J) -#define KEYCODE_K_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_K) -#define KEYCODE_L_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_L) -#define KEYCODE_M_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_M) -#define KEYCODE_N_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_N) -#define KEYCODE_O_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_O) -#define KEYCODE_P_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_P) -#define KEYCODE_Q_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_Q) -#define KEYCODE_R_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_R) -#define KEYCODE_S_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_S) -#define KEYCODE_T_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_T) -#define KEYCODE_U_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_U) -#define KEYCODE_V_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_V) -#define KEYCODE_W_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_W) -#define KEYCODE_X_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_X) -#define KEYCODE_Y_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_Y) -#define KEYCODE_Z_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_Z) -#define KEYCODE_0_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_0) -#define KEYCODE_1_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_1) -#define KEYCODE_2_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_2) -#define KEYCODE_3_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_3) -#define KEYCODE_4_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_4) -#define KEYCODE_5_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_5) -#define KEYCODE_6_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_6) -#define KEYCODE_7_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_7) -#define KEYCODE_8_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_8) -#define KEYCODE_9_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_9) -#define KEYCODE_F1_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_F1) -#define KEYCODE_F2_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_F2) -#define KEYCODE_F3_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_F3) -#define KEYCODE_F4_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_F4) -#define KEYCODE_F5_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_F5) -#define KEYCODE_F6_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_F6) -#define KEYCODE_F7_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_F7) -#define KEYCODE_F8_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_F8) -#define KEYCODE_F9_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_F9) -#define KEYCODE_F10_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_F10) -#define KEYCODE_F11_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_F11) -#define KEYCODE_F12_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_F12) -#define KEYCODE_F13_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_F13) -#define KEYCODE_F14_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_F14) -#define KEYCODE_F15_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_F15) -#define KEYCODE_F16_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_F16) -#define KEYCODE_F17_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_F17) -#define KEYCODE_F18_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_F18) -#define KEYCODE_F19_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_F19) -#define KEYCODE_F20_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_F20) -#define KEYCODE_ESC_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_ESC) -#define KEYCODE_TILDE_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_TILDE) -#define KEYCODE_MINUS_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_MINUS) -#define KEYCODE_EQUALS_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_EQUALS) -#define KEYCODE_BACKSPACE_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BACKSPACE) -#define KEYCODE_TAB_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_TAB) -#define KEYCODE_OPENBRACE_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_OPENBRACE) -#define KEYCODE_CLOSEBRACE_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_CLOSEBRACE) -#define KEYCODE_ENTER_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_ENTER) -#define KEYCODE_COLON_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_COLON) -#define KEYCODE_QUOTE_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_QUOTE) -#define KEYCODE_BACKSLASH_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BACKSLASH) -#define KEYCODE_BACKSLASH2_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BACKSLASH2) -#define KEYCODE_COMMA_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_COMMA) -#define KEYCODE_STOP_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_STOP) -#define KEYCODE_SLASH_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_SLASH) -#define KEYCODE_SPACE_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_SPACE) -#define KEYCODE_INSERT_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_INSERT) -#define KEYCODE_DEL_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_DEL) -#define KEYCODE_HOME_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_HOME) -#define KEYCODE_END_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_END) -#define KEYCODE_PGUP_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_PGUP) -#define KEYCODE_PGDN_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_PGDN) -#define KEYCODE_LEFT_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_LEFT) -#define KEYCODE_RIGHT_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_RIGHT) -#define KEYCODE_UP_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_UP) -#define KEYCODE_DOWN_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_DOWN) -#define KEYCODE_0_PAD_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_0_PAD) -#define KEYCODE_1_PAD_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_1_PAD) -#define KEYCODE_2_PAD_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_2_PAD) -#define KEYCODE_3_PAD_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_3_PAD) -#define KEYCODE_4_PAD_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_4_PAD) -#define KEYCODE_5_PAD_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_5_PAD) -#define KEYCODE_6_PAD_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_6_PAD) -#define KEYCODE_7_PAD_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_7_PAD) -#define KEYCODE_8_PAD_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_8_PAD) -#define KEYCODE_9_PAD_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_9_PAD) -#define KEYCODE_SLASH_PAD_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_SLASH_PAD) -#define KEYCODE_ASTERISK_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_ASTERISK) -#define KEYCODE_MINUS_PAD_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_MINUS_PAD) -#define KEYCODE_PLUS_PAD_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_PLUS_PAD) -#define KEYCODE_DEL_PAD_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_DEL_PAD) -#define KEYCODE_ENTER_PAD_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_ENTER_PAD) -#define KEYCODE_BS_PAD_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BS_PAD) -#define KEYCODE_TAB_PAD_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_TAB_PAD) -#define KEYCODE_00_PAD_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_00_PAD) -#define KEYCODE_000_PAD_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_000_PAD) -#define KEYCODE_COMMA_PAD_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_COMMA_PAD) -#define KEYCODE_EQUALS_PAD_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_EQUALS_PAD) -#define KEYCODE_PRTSCR_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_PRTSCR) -#define KEYCODE_PAUSE_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_PAUSE) -#define KEYCODE_LSHIFT_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_LSHIFT) -#define KEYCODE_RSHIFT_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_RSHIFT) -#define KEYCODE_LCONTROL_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_LCONTROL) -#define KEYCODE_RCONTROL_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_RCONTROL) -#define KEYCODE_LALT_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_LALT) -#define KEYCODE_RALT_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_RALT) -#define KEYCODE_SCRLOCK_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_SCRLOCK) -#define KEYCODE_NUMLOCK_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_NUMLOCK) -#define KEYCODE_CAPSLOCK_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_CAPSLOCK) -#define KEYCODE_LWIN_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_LWIN) -#define KEYCODE_RWIN_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_RWIN) -#define KEYCODE_MENU_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_MENU) -#define KEYCODE_CANCEL_INDEXED(n) input_code(DEVICE_CLASS_KEYBOARD, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_CANCEL) - -#define KEYCODE_A KEYCODE_A_INDEXED(0) -#define KEYCODE_B KEYCODE_B_INDEXED(0) -#define KEYCODE_C KEYCODE_C_INDEXED(0) -#define KEYCODE_D KEYCODE_D_INDEXED(0) -#define KEYCODE_E KEYCODE_E_INDEXED(0) -#define KEYCODE_F KEYCODE_F_INDEXED(0) -#define KEYCODE_G KEYCODE_G_INDEXED(0) -#define KEYCODE_H KEYCODE_H_INDEXED(0) -#define KEYCODE_I KEYCODE_I_INDEXED(0) -#define KEYCODE_J KEYCODE_J_INDEXED(0) -#define KEYCODE_K KEYCODE_K_INDEXED(0) -#define KEYCODE_L KEYCODE_L_INDEXED(0) -#define KEYCODE_M KEYCODE_M_INDEXED(0) -#define KEYCODE_N KEYCODE_N_INDEXED(0) -#define KEYCODE_O KEYCODE_O_INDEXED(0) -#define KEYCODE_P KEYCODE_P_INDEXED(0) -#define KEYCODE_Q KEYCODE_Q_INDEXED(0) -#define KEYCODE_R KEYCODE_R_INDEXED(0) -#define KEYCODE_S KEYCODE_S_INDEXED(0) -#define KEYCODE_T KEYCODE_T_INDEXED(0) -#define KEYCODE_U KEYCODE_U_INDEXED(0) -#define KEYCODE_V KEYCODE_V_INDEXED(0) -#define KEYCODE_W KEYCODE_W_INDEXED(0) -#define KEYCODE_X KEYCODE_X_INDEXED(0) -#define KEYCODE_Y KEYCODE_Y_INDEXED(0) -#define KEYCODE_Z KEYCODE_Z_INDEXED(0) -#define KEYCODE_0 KEYCODE_0_INDEXED(0) -#define KEYCODE_1 KEYCODE_1_INDEXED(0) -#define KEYCODE_2 KEYCODE_2_INDEXED(0) -#define KEYCODE_3 KEYCODE_3_INDEXED(0) -#define KEYCODE_4 KEYCODE_4_INDEXED(0) -#define KEYCODE_5 KEYCODE_5_INDEXED(0) -#define KEYCODE_6 KEYCODE_6_INDEXED(0) -#define KEYCODE_7 KEYCODE_7_INDEXED(0) -#define KEYCODE_8 KEYCODE_8_INDEXED(0) -#define KEYCODE_9 KEYCODE_9_INDEXED(0) -#define KEYCODE_F1 KEYCODE_F1_INDEXED(0) -#define KEYCODE_F2 KEYCODE_F2_INDEXED(0) -#define KEYCODE_F3 KEYCODE_F3_INDEXED(0) -#define KEYCODE_F4 KEYCODE_F4_INDEXED(0) -#define KEYCODE_F5 KEYCODE_F5_INDEXED(0) -#define KEYCODE_F6 KEYCODE_F6_INDEXED(0) -#define KEYCODE_F7 KEYCODE_F7_INDEXED(0) -#define KEYCODE_F8 KEYCODE_F8_INDEXED(0) -#define KEYCODE_F9 KEYCODE_F9_INDEXED(0) -#define KEYCODE_F10 KEYCODE_F10_INDEXED(0) -#define KEYCODE_F11 KEYCODE_F11_INDEXED(0) -#define KEYCODE_F12 KEYCODE_F12_INDEXED(0) -#define KEYCODE_F13 KEYCODE_F13_INDEXED(0) -#define KEYCODE_F14 KEYCODE_F14_INDEXED(0) -#define KEYCODE_F15 KEYCODE_F15_INDEXED(0) -#define KEYCODE_F16 KEYCODE_F16_INDEXED(0) -#define KEYCODE_F17 KEYCODE_F17_INDEXED(0) -#define KEYCODE_F18 KEYCODE_F18_INDEXED(0) -#define KEYCODE_F19 KEYCODE_F19_INDEXED(0) -#define KEYCODE_F20 KEYCODE_F20_INDEXED(0) -#define KEYCODE_ESC KEYCODE_ESC_INDEXED(0) -#define KEYCODE_TILDE KEYCODE_TILDE_INDEXED(0) -#define KEYCODE_MINUS KEYCODE_MINUS_INDEXED(0) -#define KEYCODE_EQUALS KEYCODE_EQUALS_INDEXED(0) -#define KEYCODE_BACKSPACE KEYCODE_BACKSPACE_INDEXED(0) -#define KEYCODE_TAB KEYCODE_TAB_INDEXED(0) -#define KEYCODE_OPENBRACE KEYCODE_OPENBRACE_INDEXED(0) -#define KEYCODE_CLOSEBRACE KEYCODE_CLOSEBRACE_INDEXED(0) -#define KEYCODE_ENTER KEYCODE_ENTER_INDEXED(0) -#define KEYCODE_COLON KEYCODE_COLON_INDEXED(0) -#define KEYCODE_QUOTE KEYCODE_QUOTE_INDEXED(0) -#define KEYCODE_BACKSLASH KEYCODE_BACKSLASH_INDEXED(0) -#define KEYCODE_BACKSLASH2 KEYCODE_BACKSLASH2_INDEXED(0) -#define KEYCODE_COMMA KEYCODE_COMMA_INDEXED(0) -#define KEYCODE_STOP KEYCODE_STOP_INDEXED(0) -#define KEYCODE_SLASH KEYCODE_SLASH_INDEXED(0) -#define KEYCODE_SPACE KEYCODE_SPACE_INDEXED(0) -#define KEYCODE_INSERT KEYCODE_INSERT_INDEXED(0) -#define KEYCODE_DEL KEYCODE_DEL_INDEXED(0) -#define KEYCODE_HOME KEYCODE_HOME_INDEXED(0) -#define KEYCODE_END KEYCODE_END_INDEXED(0) -#define KEYCODE_PGUP KEYCODE_PGUP_INDEXED(0) -#define KEYCODE_PGDN KEYCODE_PGDN_INDEXED(0) -#define KEYCODE_LEFT KEYCODE_LEFT_INDEXED(0) -#define KEYCODE_RIGHT KEYCODE_RIGHT_INDEXED(0) -#define KEYCODE_UP KEYCODE_UP_INDEXED(0) -#define KEYCODE_DOWN KEYCODE_DOWN_INDEXED(0) -#define KEYCODE_0_PAD KEYCODE_0_PAD_INDEXED(0) -#define KEYCODE_1_PAD KEYCODE_1_PAD_INDEXED(0) -#define KEYCODE_2_PAD KEYCODE_2_PAD_INDEXED(0) -#define KEYCODE_3_PAD KEYCODE_3_PAD_INDEXED(0) -#define KEYCODE_4_PAD KEYCODE_4_PAD_INDEXED(0) -#define KEYCODE_5_PAD KEYCODE_5_PAD_INDEXED(0) -#define KEYCODE_6_PAD KEYCODE_6_PAD_INDEXED(0) -#define KEYCODE_7_PAD KEYCODE_7_PAD_INDEXED(0) -#define KEYCODE_8_PAD KEYCODE_8_PAD_INDEXED(0) -#define KEYCODE_9_PAD KEYCODE_9_PAD_INDEXED(0) -#define KEYCODE_SLASH_PAD KEYCODE_SLASH_PAD_INDEXED(0) -#define KEYCODE_ASTERISK KEYCODE_ASTERISK_INDEXED(0) -#define KEYCODE_MINUS_PAD KEYCODE_MINUS_PAD_INDEXED(0) -#define KEYCODE_PLUS_PAD KEYCODE_PLUS_PAD_INDEXED(0) -#define KEYCODE_DEL_PAD KEYCODE_DEL_PAD_INDEXED(0) -#define KEYCODE_ENTER_PAD KEYCODE_ENTER_PAD_INDEXED(0) -#define KEYCODE_BS_PAD KEYCODE_BS_PAD_INDEXED(0) -#define KEYCODE_TAB_PAD KEYCODE_TAB_PAD_INDEXED(0) -#define KEYCODE_00_PAD KEYCODE_00_PAD_INDEXED(0) -#define KEYCODE_000_PAD KEYCODE_000_PAD_INDEXED(0) -#define KEYCODE_COMMA_PAD KEYCODE_COMMA_PAD_INDEXED(0) -#define KEYCODE_EQUALS_PAD KEYCODE_EQUALS_PAD_INDEXED(0) -#define KEYCODE_PRTSCR KEYCODE_PRTSCR_INDEXED(0) -#define KEYCODE_PAUSE KEYCODE_PAUSE_INDEXED(0) -#define KEYCODE_LSHIFT KEYCODE_LSHIFT_INDEXED(0) -#define KEYCODE_RSHIFT KEYCODE_RSHIFT_INDEXED(0) -#define KEYCODE_LCONTROL KEYCODE_LCONTROL_INDEXED(0) -#define KEYCODE_RCONTROL KEYCODE_RCONTROL_INDEXED(0) -#define KEYCODE_LALT KEYCODE_LALT_INDEXED(0) -#define KEYCODE_RALT KEYCODE_RALT_INDEXED(0) -#define KEYCODE_SCRLOCK KEYCODE_SCRLOCK_INDEXED(0) -#define KEYCODE_NUMLOCK KEYCODE_NUMLOCK_INDEXED(0) -#define KEYCODE_CAPSLOCK KEYCODE_CAPSLOCK_INDEXED(0) -#define KEYCODE_LWIN KEYCODE_LWIN_INDEXED(0) -#define KEYCODE_RWIN KEYCODE_RWIN_INDEXED(0) -#define KEYCODE_MENU KEYCODE_MENU_INDEXED(0) -#define KEYCODE_CANCEL KEYCODE_CANCEL_INDEXED(0) - -// mouse axes as relative devices -#define MOUSECODE_X_INDEXED(n) input_code(DEVICE_CLASS_MOUSE, n, ITEM_CLASS_RELATIVE, ITEM_MODIFIER_NONE, ITEM_ID_XAXIS) -#define MOUSECODE_Y_INDEXED(n) input_code(DEVICE_CLASS_MOUSE, n, ITEM_CLASS_RELATIVE, ITEM_MODIFIER_NONE, ITEM_ID_YAXIS) -#define MOUSECODE_Z_INDEXED(n) input_code(DEVICE_CLASS_MOUSE, n, ITEM_CLASS_RELATIVE, ITEM_MODIFIER_NONE, ITEM_ID_ZAXIS) - -#define MOUSECODE_X MOUSECODE_X_INDEXED(0) -#define MOUSECODE_Y MOUSECODE_Y_INDEXED(0) -#define MOUSECODE_Z MOUSECODE_Z_INDEXED(0) - -// mouse axes as switches in +/- direction -#define MOUSECODE_X_POS_SWITCH_INDEXED(n) input_code(DEVICE_CLASS_MOUSE, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_POS, ITEM_ID_XAXIS) -#define MOUSECODE_X_NEG_SWITCH_INDEXED(n) input_code(DEVICE_CLASS_MOUSE, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, ITEM_ID_XAXIS) -#define MOUSECODE_Y_POS_SWITCH_INDEXED(n) input_code(DEVICE_CLASS_MOUSE, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_POS, ITEM_ID_YAXIS) -#define MOUSECODE_Y_NEG_SWITCH_INDEXED(n) input_code(DEVICE_CLASS_MOUSE, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, ITEM_ID_YAXIS) -#define MOUSECODE_Z_POS_SWITCH_INDEXED(n) input_code(DEVICE_CLASS_MOUSE, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_POS, ITEM_ID_ZAXIS) -#define MOUSECODE_Z_NEG_SWITCH_INDEXED(n) input_code(DEVICE_CLASS_MOUSE, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, ITEM_ID_ZAXIS) - -#define MOUSECODE_X_POS_SWITCH MOUSECODE_X_POS_SWITCH_INDEXED(0) -#define MOUSECODE_X_NEG_SWITCH MOUSECODE_X_NEG_SWITCH_INDEXED(0) -#define MOUSECODE_Y_POS_SWITCH MOUSECODE_Y_POS_SWITCH_INDEXED(0) -#define MOUSECODE_Y_NEG_SWITCH MOUSECODE_Y_NEG_SWITCH_INDEXED(0) -#define MOUSECODE_Z_POS_SWITCH MOUSECODE_Z_POS_SWITCH_INDEXED(0) -#define MOUSECODE_Z_NEG_SWITCH MOUSECODE_Z_NEG_SWITCH_INDEXED(0) - -// mouse buttons -#define MOUSECODE_BUTTON1_INDEXED(n) input_code(DEVICE_CLASS_MOUSE, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON1) -#define MOUSECODE_BUTTON2_INDEXED(n) input_code(DEVICE_CLASS_MOUSE, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON2) -#define MOUSECODE_BUTTON3_INDEXED(n) input_code(DEVICE_CLASS_MOUSE, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON3) -#define MOUSECODE_BUTTON4_INDEXED(n) input_code(DEVICE_CLASS_MOUSE, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON4) -#define MOUSECODE_BUTTON5_INDEXED(n) input_code(DEVICE_CLASS_MOUSE, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON5) -#define MOUSECODE_BUTTON6_INDEXED(n) input_code(DEVICE_CLASS_MOUSE, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON6) -#define MOUSECODE_BUTTON7_INDEXED(n) input_code(DEVICE_CLASS_MOUSE, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON7) -#define MOUSECODE_BUTTON8_INDEXED(n) input_code(DEVICE_CLASS_MOUSE, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON8) - -#define MOUSECODE_BUTTON1 MOUSECODE_BUTTON1_INDEXED(0) -#define MOUSECODE_BUTTON2 MOUSECODE_BUTTON2_INDEXED(0) -#define MOUSECODE_BUTTON3 MOUSECODE_BUTTON3_INDEXED(0) -#define MOUSECODE_BUTTON4 MOUSECODE_BUTTON4_INDEXED(0) -#define MOUSECODE_BUTTON5 MOUSECODE_BUTTON5_INDEXED(0) -#define MOUSECODE_BUTTON6 MOUSECODE_BUTTON6_INDEXED(0) -#define MOUSECODE_BUTTON7 MOUSECODE_BUTTON7_INDEXED(0) -#define MOUSECODE_BUTTON8 MOUSECODE_BUTTON8_INDEXED(0) - -// gun axes as absolute devices -#define GUNCODE_X_INDEXED(n) input_code(DEVICE_CLASS_LIGHTGUN, n, ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NONE, ITEM_ID_XAXIS) -#define GUNCODE_Y_INDEXED(n) input_code(DEVICE_CLASS_LIGHTGUN, n, ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NONE, ITEM_ID_YAXIS) - -#define GUNCODE_X GUNCODE_X_INDEXED(0) -#define GUNCODE_Y GUNCODE_Y_INDEXED(0) - -// gun buttons -#define GUNCODE_BUTTON1_INDEXED(n) input_code(DEVICE_CLASS_LIGHTGUN, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON1) -#define GUNCODE_BUTTON2_INDEXED(n) input_code(DEVICE_CLASS_LIGHTGUN, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON2) -#define GUNCODE_BUTTON3_INDEXED(n) input_code(DEVICE_CLASS_LIGHTGUN, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON3) -#define GUNCODE_BUTTON4_INDEXED(n) input_code(DEVICE_CLASS_LIGHTGUN, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON4) -#define GUNCODE_BUTTON5_INDEXED(n) input_code(DEVICE_CLASS_LIGHTGUN, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON5) -#define GUNCODE_BUTTON6_INDEXED(n) input_code(DEVICE_CLASS_LIGHTGUN, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON6) -#define GUNCODE_BUTTON7_INDEXED(n) input_code(DEVICE_CLASS_LIGHTGUN, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON7) -#define GUNCODE_BUTTON8_INDEXED(n) input_code(DEVICE_CLASS_LIGHTGUN, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON8) - -#define GUNCODE_BUTTON1 GUNCODE_BUTTON1_INDEXED(0) -#define GUNCODE_BUTTON2 GUNCODE_BUTTON2_INDEXED(0) -#define GUNCODE_BUTTON3 GUNCODE_BUTTON3_INDEXED(0) -#define GUNCODE_BUTTON4 GUNCODE_BUTTON4_INDEXED(0) -#define GUNCODE_BUTTON5 GUNCODE_BUTTON5_INDEXED(0) -#define GUNCODE_BUTTON6 GUNCODE_BUTTON6_INDEXED(0) -#define GUNCODE_BUTTON7 GUNCODE_BUTTON7_INDEXED(0) -#define GUNCODE_BUTTON8 GUNCODE_BUTTON8_INDEXED(0) - -// joystick axes as absolute devices -#define JOYCODE_X_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NONE, ITEM_ID_XAXIS) -#define JOYCODE_Y_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NONE, ITEM_ID_YAXIS) -#define JOYCODE_Z_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NONE, ITEM_ID_ZAXIS) -#define JOYCODE_U_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NONE, ITEM_ID_RXAXIS) -#define JOYCODE_V_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NONE, ITEM_ID_RYAXIS) -#define JOYCODE_W_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NONE, ITEM_ID_RZAXIS) - -#define JOYCODE_X JOYCODE_X_INDEXED(0) -#define JOYCODE_Y JOYCODE_Y_INDEXED(0) -#define JOYCODE_Z JOYCODE_Z_INDEXED(0) -#define JOYCODE_U JOYCODE_U_INDEXED(0) -#define JOYCODE_V JOYCODE_V_INDEXED(0) -#define JOYCODE_W JOYCODE_W_INDEXED(0) - -// joystick axes as absolute half-axes -#define JOYCODE_X_POS_ABSOLUTE_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_POS, ITEM_ID_XAXIS) -#define JOYCODE_X_NEG_ABSOLUTE_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NEG, ITEM_ID_XAXIS) -#define JOYCODE_Y_POS_ABSOLUTE_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_POS, ITEM_ID_YAXIS) -#define JOYCODE_Y_NEG_ABSOLUTE_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NEG, ITEM_ID_YAXIS) -#define JOYCODE_Z_POS_ABSOLUTE_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_POS, ITEM_ID_ZAXIS) -#define JOYCODE_Z_NEG_ABSOLUTE_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NEG, ITEM_ID_ZAXIS) -#define JOYCODE_U_POS_ABSOLUTE_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_POS, ITEM_ID_RXAXIS) -#define JOYCODE_U_NEG_ABSOLUTE_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NEG, ITEM_ID_RXAXIS) -#define JOYCODE_V_POS_ABSOLUTE_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_POS, ITEM_ID_RYAXIS) -#define JOYCODE_V_NEG_ABSOLUTE_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NEG, ITEM_ID_RYAXIS) -#define JOYCODE_W_POS_ABSOLUTE_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_POS, ITEM_ID_RZAXIS) -#define JOYCODE_W_NEG_ABSOLUTE_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NEG, ITEM_ID_RZAXIS) - -#define JOYCODE_X_POS_ABSOLUTE JOYCODE_X_POS_ABSOLUTE_INDEXED(0) -#define JOYCODE_X_NEG_ABSOLUTE JOYCODE_X_NEG_ABSOLUTE_INDEXED(0) -#define JOYCODE_Y_POS_ABSOLUTE JOYCODE_Y_POS_ABSOLUTE_INDEXED(0) -#define JOYCODE_Y_NEG_ABSOLUTE JOYCODE_Y_NEG_ABSOLUTE_INDEXED(0) -#define JOYCODE_Z_POS_ABSOLUTE JOYCODE_Z_POS_ABSOLUTE_INDEXED(0) -#define JOYCODE_Z_NEG_ABSOLUTE JOYCODE_Z_NEG_ABSOLUTE_INDEXED(0) -#define JOYCODE_U_POS_ABSOLUTE JOYCODE_U_POS_ABSOLUTE_INDEXED(0) -#define JOYCODE_U_NEG_ABSOLUTE JOYCODE_U_NEG_ABSOLUTE_INDEXED(0) -#define JOYCODE_V_POS_ABSOLUTE JOYCODE_V_POS_ABSOLUTE_INDEXED(0) -#define JOYCODE_V_NEG_ABSOLUTE JOYCODE_V_NEG_ABSOLUTE_INDEXED(0) -#define JOYCODE_W_POS_ABSOLUTE JOYCODE_W_POS_ABSOLUTE_INDEXED(0) -#define JOYCODE_W_NEG_ABSOLUTE JOYCODE_W_NEG_ABSOLUTE_INDEXED(0) - -// joystick axes as switches; X/Y are specially handled for left/right/up/down mapping -#define JOYCODE_X_LEFT_SWITCH_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_LEFT, ITEM_ID_XAXIS) -#define JOYCODE_X_RIGHT_SWITCH_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_RIGHT, ITEM_ID_XAXIS) -#define JOYCODE_Y_UP_SWITCH_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_UP, ITEM_ID_YAXIS) -#define JOYCODE_Y_DOWN_SWITCH_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_DOWN, ITEM_ID_YAXIS) -#define JOYCODE_Z_POS_SWITCH_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_POS, ITEM_ID_ZAXIS) -#define JOYCODE_Z_NEG_SWITCH_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, ITEM_ID_ZAXIS) -#define JOYCODE_U_POS_SWITCH_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_POS, ITEM_ID_RXAXIS) -#define JOYCODE_U_NEG_SWITCH_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, ITEM_ID_RXAXIS) -#define JOYCODE_V_POS_SWITCH_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_POS, ITEM_ID_RYAXIS) -#define JOYCODE_V_NEG_SWITCH_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, ITEM_ID_RYAXIS) -#define JOYCODE_W_POS_SWITCH_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_POS, ITEM_ID_RZAXIS) -#define JOYCODE_W_NEG_SWITCH_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, ITEM_ID_RZAXIS) - -#define JOYCODE_X_LEFT_SWITCH JOYCODE_X_LEFT_SWITCH_INDEXED(0) -#define JOYCODE_X_RIGHT_SWITCH JOYCODE_X_RIGHT_SWITCH_INDEXED(0) -#define JOYCODE_Y_UP_SWITCH JOYCODE_Y_UP_SWITCH_INDEXED(0) -#define JOYCODE_Y_DOWN_SWITCH JOYCODE_Y_DOWN_SWITCH_INDEXED(0) -#define JOYCODE_Z_POS_SWITCH JOYCODE_Z_POS_SWITCH_INDEXED(0) -#define JOYCODE_Z_NEG_SWITCH JOYCODE_Z_NEG_SWITCH_INDEXED(0) -#define JOYCODE_U_POS_SWITCH JOYCODE_U_POS_SWITCH_INDEXED(0) -#define JOYCODE_U_NEG_SWITCH JOYCODE_U_NEG_SWITCH_INDEXED(0) -#define JOYCODE_V_POS_SWITCH JOYCODE_V_POS_SWITCH_INDEXED(0) -#define JOYCODE_V_NEG_SWITCH JOYCODE_V_NEG_SWITCH_INDEXED(0) -#define JOYCODE_W_POS_SWITCH JOYCODE_W_POS_SWITCH_INDEXED(0) -#define JOYCODE_W_NEG_SWITCH JOYCODE_W_NEG_SWITCH_INDEXED(0) - -// joystick buttons -#define JOYCODE_BUTTON1_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON1) -#define JOYCODE_BUTTON2_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON2) -#define JOYCODE_BUTTON3_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON3) -#define JOYCODE_BUTTON4_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON4) -#define JOYCODE_BUTTON5_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON5) -#define JOYCODE_BUTTON6_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON6) -#define JOYCODE_BUTTON7_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON7) -#define JOYCODE_BUTTON8_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON8) -#define JOYCODE_BUTTON9_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON9) -#define JOYCODE_BUTTON10_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON10) -#define JOYCODE_BUTTON11_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON11) -#define JOYCODE_BUTTON12_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON12) -#define JOYCODE_BUTTON13_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON13) -#define JOYCODE_BUTTON14_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON14) -#define JOYCODE_BUTTON15_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON15) -#define JOYCODE_BUTTON16_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON16) -#define JOYCODE_BUTTON17_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON17) -#define JOYCODE_BUTTON18_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON18) -#define JOYCODE_BUTTON19_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON19) -#define JOYCODE_BUTTON20_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON20) -#define JOYCODE_BUTTON21_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON21) -#define JOYCODE_BUTTON22_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON22) -#define JOYCODE_BUTTON23_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON23) -#define JOYCODE_BUTTON24_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON24) -#define JOYCODE_BUTTON25_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON25) -#define JOYCODE_BUTTON26_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON26) -#define JOYCODE_BUTTON27_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON27) -#define JOYCODE_BUTTON28_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON28) -#define JOYCODE_BUTTON29_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON29) -#define JOYCODE_BUTTON30_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON30) -#define JOYCODE_BUTTON31_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON31) -#define JOYCODE_BUTTON32_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_BUTTON32) -#define JOYCODE_START_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_START) -#define JOYCODE_SELECT_INDEXED(n) input_code(DEVICE_CLASS_JOYSTICK, n, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, ITEM_ID_SELECT) - -#define JOYCODE_BUTTON1 JOYCODE_BUTTON1_INDEXED(0) -#define JOYCODE_BUTTON2 JOYCODE_BUTTON2_INDEXED(0) -#define JOYCODE_BUTTON3 JOYCODE_BUTTON3_INDEXED(0) -#define JOYCODE_BUTTON4 JOYCODE_BUTTON4_INDEXED(0) -#define JOYCODE_BUTTON5 JOYCODE_BUTTON5_INDEXED(0) -#define JOYCODE_BUTTON6 JOYCODE_BUTTON6_INDEXED(0) -#define JOYCODE_BUTTON7 JOYCODE_BUTTON7_INDEXED(0) -#define JOYCODE_BUTTON8 JOYCODE_BUTTON8_INDEXED(0) -#define JOYCODE_BUTTON9 JOYCODE_BUTTON9_INDEXED(0) -#define JOYCODE_BUTTON10 JOYCODE_BUTTON10_INDEXED(0) -#define JOYCODE_BUTTON11 JOYCODE_BUTTON11_INDEXED(0) -#define JOYCODE_BUTTON12 JOYCODE_BUTTON12_INDEXED(0) -#define JOYCODE_BUTTON13 JOYCODE_BUTTON13_INDEXED(0) -#define JOYCODE_BUTTON14 JOYCODE_BUTTON14_INDEXED(0) -#define JOYCODE_BUTTON15 JOYCODE_BUTTON15_INDEXED(0) -#define JOYCODE_BUTTON16 JOYCODE_BUTTON16_INDEXED(0) -#define JOYCODE_BUTTON17 JOYCODE_BUTTON17_INDEXED(0) -#define JOYCODE_BUTTON18 JOYCODE_BUTTON18_INDEXED(0) -#define JOYCODE_BUTTON19 JOYCODE_BUTTON19_INDEXED(0) -#define JOYCODE_BUTTON20 JOYCODE_BUTTON20_INDEXED(0) -#define JOYCODE_BUTTON21 JOYCODE_BUTTON21_INDEXED(0) -#define JOYCODE_BUTTON22 JOYCODE_BUTTON22_INDEXED(0) -#define JOYCODE_BUTTON23 JOYCODE_BUTTON23_INDEXED(0) -#define JOYCODE_BUTTON24 JOYCODE_BUTTON24_INDEXED(0) -#define JOYCODE_BUTTON25 JOYCODE_BUTTON25_INDEXED(0) -#define JOYCODE_BUTTON26 JOYCODE_BUTTON26_INDEXED(0) -#define JOYCODE_BUTTON27 JOYCODE_BUTTON27_INDEXED(0) -#define JOYCODE_BUTTON28 JOYCODE_BUTTON28_INDEXED(0) -#define JOYCODE_BUTTON29 JOYCODE_BUTTON29_INDEXED(0) -#define JOYCODE_BUTTON30 JOYCODE_BUTTON30_INDEXED(0) -#define JOYCODE_BUTTON31 JOYCODE_BUTTON31_INDEXED(0) -#define JOYCODE_BUTTON32 JOYCODE_BUTTON32_INDEXED(0) -#define JOYCODE_START JOYCODE_START_INDEXED(0) -#define JOYCODE_SELECT JOYCODE_SELECT_INDEXED(0) - - - #endif // MAME_EMU_INPUT_H diff --git a/src/emu/inputdev.cpp b/src/emu/inputdev.cpp index a9e8fee7491..a9caaf7788d 100644 --- a/src/emu/inputdev.cpp +++ b/src/emu/inputdev.cpp @@ -11,9 +11,12 @@ #include "emu.h" #include "inputdev.h" +#include "corestr.h" #include "emuopts.h" +namespace { + //************************************************************************** // TYPE DEFINITIONS //************************************************************************** @@ -25,12 +28,19 @@ class input_device_switch_item : public input_device_item { public: // construction/destruction - input_device_switch_item(input_device &device, const char *name, void *internal, input_item_id itemid, item_get_state_func getstate); + input_device_switch_item( + input_device &device, + std::string_view name, + std::string_view tokenhint, + void *internal, + input_item_id itemid, + item_get_state_func getstate); // readers virtual s32 read_as_switch(input_item_modifier modifier) override; virtual s32 read_as_relative(input_item_modifier modifier) override; virtual s32 read_as_absolute(input_item_modifier modifier) override; + virtual bool item_check_axis(input_item_modifier modifiers, s32 memory) override; // steadykey helper bool steadykey_changed(); @@ -50,12 +60,19 @@ class input_device_relative_item : public input_device_item { public: // construction/destruction - input_device_relative_item(input_device &device, const char *name, void *internal, input_item_id itemid, item_get_state_func getstate); + input_device_relative_item( + input_device &device, + std::string_view name, + std::string_view tokenhint, + void *internal, + input_item_id itemid, + item_get_state_func getstate); // readers virtual s32 read_as_switch(input_item_modifier modifier) override; virtual s32 read_as_relative(input_item_modifier modifier) override; virtual s32 read_as_absolute(input_item_modifier modifier) override; + virtual bool item_check_axis(input_item_modifier modifier, s32 memory) override; }; @@ -66,14 +83,23 @@ class input_device_absolute_item : public input_device_item { public: // construction/destruction - input_device_absolute_item(input_device &device, const char *name, void *internal, input_item_id itemid, item_get_state_func getstate); + input_device_absolute_item( + input_device &device, + std::string_view name, + std::string_view tokenhint, + void *internal, + input_item_id itemid, + item_get_state_func getstate); // readers virtual s32 read_as_switch(input_item_modifier modifier) override; virtual s32 read_as_relative(input_item_modifier modifier) override; virtual s32 read_as_absolute(input_item_modifier modifier) override; + virtual bool item_check_axis(input_item_modifier modifier, s32 memory) override; }; +} // anonymous namespace + //************************************************************************** // GLOBAL VARIABLES @@ -225,8 +251,8 @@ std::string joystick_map::to_string() const u8 joystick_map::update(s32 xaxisval, s32 yaxisval) { // now map the X and Y axes to a 9x9 grid using the raw values - xaxisval = ((xaxisval - INPUT_ABSOLUTE_MIN) * 9) / (INPUT_ABSOLUTE_MAX - INPUT_ABSOLUTE_MIN + 1); - yaxisval = ((yaxisval - INPUT_ABSOLUTE_MIN) * 9) / (INPUT_ABSOLUTE_MAX - INPUT_ABSOLUTE_MIN + 1); + xaxisval = ((xaxisval - osd::input_device::ABSOLUTE_MIN) * 9) / (osd::input_device::ABSOLUTE_MAX - osd::input_device::ABSOLUTE_MIN + 1); + yaxisval = ((yaxisval - osd::input_device::ABSOLUTE_MIN) * 9) / (osd::input_device::ABSOLUTE_MAX - osd::input_device::ABSOLUTE_MIN + 1); u8 mapval = m_map[yaxisval][xaxisval]; // handle stickiness @@ -249,15 +275,16 @@ u8 joystick_map::update(s32 xaxisval, s32 yaxisval) // input_device - constructor //------------------------------------------------- -input_device::input_device(input_manager &manager, const char *name, const char *id, void *internal) - : m_manager(manager), - m_name(name), - m_id(id), - m_devindex(-1), - m_maxitem(input_item_id(0)), - m_internal(internal), - m_steadykey_enabled(manager.machine().options().steadykey()), - m_lightgun_reload_button(manager.machine().options().offscreen_reload()) +input_device::input_device(input_manager &manager, std::string_view name, std::string_view id, void *internal) + : m_manager(manager) + , m_name(name) + , m_id(id) + , m_devindex(-1) + , m_maxitem(input_item_id(0)) + , m_internal(internal) + , m_threshold(std::max<s32>(s32(manager.machine().options().joystick_threshold() * osd::input_device::ABSOLUTE_MAX), 1)) + , m_steadykey_enabled(manager.machine().options().steadykey()) + , m_lightgun_reload_button(manager.machine().options().offscreen_reload()) { } @@ -275,11 +302,15 @@ input_device::~input_device() // add_item - add a new item to an input device //------------------------------------------------- -input_item_id input_device::add_item(const char *name, input_item_id itemid, item_get_state_func getstate, void *internal) +input_item_id input_device::add_item( + std::string_view name, + std::string_view tokenhint, + input_item_id itemid, + item_get_state_func getstate, + void *internal) { if (machine().phase() != machine_phase::INIT) throw emu_fatalerror("Can only call input_device::add_item at init time!"); - assert(name != nullptr); assert(itemid > ITEM_ID_INVALID && itemid < ITEM_ID_MAXIMUM); assert(getstate != nullptr); @@ -298,15 +329,15 @@ input_item_id input_device::add_item(const char *name, input_item_id itemid, ite switch (m_manager.device_class(devclass()).standard_item_class(originalid)) { case ITEM_CLASS_SWITCH: - m_item[itemid] = std::make_unique<input_device_switch_item>(*this, name, internal, itemid, getstate); + m_item[itemid] = std::make_unique<input_device_switch_item>(*this, name, tokenhint, internal, itemid, getstate); break; case ITEM_CLASS_RELATIVE: - m_item[itemid] = std::make_unique<input_device_relative_item>(*this, name, internal, itemid, getstate); + m_item[itemid] = std::make_unique<input_device_relative_item>(*this, name, tokenhint, internal, itemid, getstate); break; case ITEM_CLASS_ABSOLUTE: - m_item[itemid] = std::make_unique<input_device_absolute_item>(*this, name, internal, itemid, getstate); + m_item[itemid] = std::make_unique<input_device_absolute_item>(*this, name, tokenhint, internal, itemid, getstate); break; default: @@ -321,17 +352,25 @@ input_item_id input_device::add_item(const char *name, input_item_id itemid, ite //------------------------------------------------- +// set_default_assignments - set additional input +// assignments suitable for device +//------------------------------------------------- + +void input_device::set_default_assignments(assignment_vector &&assignments) +{ + m_default_assignments = std::move(assignments); +} + + +//------------------------------------------------- // match_device_id - match device id via // substring search //------------------------------------------------- -bool input_device::match_device_id(const char *deviceid) +bool input_device::match_device_id(std::string_view deviceid) const { - std::string deviceidupper(deviceid); - std::string idupper(m_id); - - strmakeupper(deviceidupper); - strmakeupper(idupper); + std::string deviceidupper(strmakeupper(deviceid)); + std::string idupper(strmakeupper(m_id)); return std::string::npos == idupper.find(deviceidupper) ? false : true; } @@ -345,7 +384,7 @@ bool input_device::match_device_id(const char *deviceid) // input_device_keyboard - constructor //------------------------------------------------- -input_device_keyboard::input_device_keyboard(input_manager &manager, const char *_name, const char *_id, void *_internal) +input_device_keyboard::input_device_keyboard(input_manager &manager, std::string_view _name, std::string_view _id, void *_internal) : input_device(manager, _name, _id, _internal) { } @@ -386,7 +425,7 @@ void input_device_keyboard::apply_steadykey() const // input_device_mouse - constructor //------------------------------------------------- -input_device_mouse::input_device_mouse(input_manager &manager, const char *_name, const char *_id, void *_internal) +input_device_mouse::input_device_mouse(input_manager &manager, std::string_view _name, std::string_view _id, void *_internal) : input_device(manager, _name, _id, _internal) { } @@ -396,7 +435,7 @@ input_device_mouse::input_device_mouse(input_manager &manager, const char *_name // input_device_lightgun - constructor //------------------------------------------------- -input_device_lightgun::input_device_lightgun(input_manager &manager, const char *_name, const char *_id, void *_internal) +input_device_lightgun::input_device_lightgun(input_manager &manager, std::string_view _name, std::string_view _id, void *_internal) : input_device(manager, _name, _id, _internal) { } @@ -406,10 +445,11 @@ input_device_lightgun::input_device_lightgun(input_manager &manager, const char // input_device_joystick - constructor //------------------------------------------------- -input_device_joystick::input_device_joystick(input_manager &manager, const char *_name, const char *_id, void *_internal) - : input_device(manager, _name, _id, _internal), - m_joystick_deadzone(s32(manager.machine().options().joystick_deadzone() * INPUT_ABSOLUTE_MAX)), - m_joystick_saturation(s32(manager.machine().options().joystick_saturation() * INPUT_ABSOLUTE_MAX)) +input_device_joystick::input_device_joystick(input_manager &manager, std::string_view _name, std::string_view _id, void *_internal) + : input_device(manager, _name, _id, _internal) + , m_deadzone(s32(manager.machine().options().joystick_deadzone() * osd::input_device::ABSOLUTE_MAX)) + , m_saturation(s32(manager.machine().options().joystick_saturation() * osd::input_device::ABSOLUTE_MAX)) + , m_range(m_saturation - m_deadzone) { // get the default joystick map const char *mapstring = machine().options().joystick_map(); @@ -423,7 +463,7 @@ input_device_joystick::input_device_joystick(input_manager &manager, const char m_joymap.parse(input_class_joystick::map_8way); } else if (mapstring != input_class_joystick::map_8way) - osd_printf_verbose("Input: Default joystick map = %s\n", m_joymap.to_string().c_str()); + osd_printf_verbose("Input: Default joystick map = %s\n", m_joymap.to_string()); } @@ -436,24 +476,16 @@ input_device_joystick::input_device_joystick(input_manager &manager, const char s32 input_device_joystick::adjust_absolute_value(s32 result) const { // properties are symmetric - bool negative = false; - if (result < 0) - { - negative = true; + bool const negative = result < 0; + if (negative) result = -result; - } - // if in the deadzone, return 0 - if (result < m_joystick_deadzone) + if (result < m_deadzone) // if in the deadzone, return 0 result = 0; - - // if saturated, return the max - else if (result > m_joystick_saturation) - result = INPUT_ABSOLUTE_MAX; - - // otherwise, scale - else - result = s64(result - m_joystick_deadzone) * s64(INPUT_ABSOLUTE_MAX) / s64(m_joystick_saturation - m_joystick_deadzone); + else if (result >= m_saturation) // if saturated, return the max + result = osd::input_device::ABSOLUTE_MAX; + else // otherwise, scale + result = s64(result - m_deadzone) * s64(osd::input_device::ABSOLUTE_MAX) / m_range; // re-apply sign and return return negative ? -result : result; @@ -493,18 +525,16 @@ input_class::~input_class() // add_device - add a new input device //------------------------------------------------- -input_device *input_class::add_device(const char *name, const char *id, void *internal) +input_device &input_class::add_device(std::string_view name, std::string_view id, void *internal) { if (machine().phase() != machine_phase::INIT) throw emu_fatalerror("Can only call input_class::add_device at init time!"); - assert(name != nullptr); - assert(id != nullptr); // allocate a new device and add it to the index return add_device(make_device(name, id, internal)); } -input_device *input_class::add_device(std::unique_ptr<input_device> &&new_device) +input_device &input_class::add_device(std::unique_ptr<input_device> &&new_device) { assert(new_device->devclass() == m_devclass); @@ -517,12 +547,12 @@ input_device *input_class::add_device(std::unique_ptr<input_device> &&new_device m_maxindex = std::max(m_maxindex, devindex); if (new_device->id()[0] == 0) - osd_printf_verbose("Input: Adding %s #%d: %s\n", m_name, devindex, new_device->name()); + osd_printf_verbose("Input: Adding %s #%d: %s\n", m_name, devindex + 1, new_device->name()); else - osd_printf_verbose("Input: Adding %s #%d: %s (device id: %s)\n", m_name, devindex, new_device->name(), new_device->id()); + osd_printf_verbose("Input: Adding %s #%d: %s (device id: %s)\n", m_name, devindex + 1, new_device->name(), new_device->id()); m_device[devindex] = std::move(new_device); - return m_device[devindex].get(); + return *m_device[devindex]; } throw emu_fatalerror("Input: Too many %s devices\n", m_name); @@ -536,17 +566,21 @@ input_device *input_class::add_device(std::unique_ptr<input_device> &&new_device input_item_class input_class::standard_item_class(input_item_id itemid) const { - // most everything standard is a switch, apart from the axes if (itemid == ITEM_ID_OTHER_SWITCH || itemid < ITEM_ID_XAXIS || (itemid > ITEM_ID_SLIDER2 && itemid < ITEM_ID_ADD_ABSOLUTE1)) + { + // most everything standard is a switch, apart from the axes return ITEM_CLASS_SWITCH; - - // standard mouse axes are relative + } else if (m_devclass == DEVICE_CLASS_MOUSE || itemid == ITEM_ID_OTHER_AXIS_RELATIVE || (itemid >= ITEM_ID_ADD_RELATIVE1 && itemid <= ITEM_ID_ADD_RELATIVE16)) + { + // standard mouse axes are relative return ITEM_CLASS_RELATIVE; - - // all other standard axes are absolute + } else + { + // all other standard axes are absolute return ITEM_CLASS_ABSOLUTE; + } } @@ -570,8 +604,7 @@ void input_class::remap_device_index(int oldindex, int newindex) if (nullptr != m_device[newindex].get()) m_device[newindex]->set_devindex(newindex); - // update the maximum index found, since newindex may - // exceed current m_maxindex + // update the maximum index found, since newindex may exceed current m_maxindex m_maxindex = std::max(m_maxindex, newindex); } @@ -648,7 +681,7 @@ bool input_class_joystick::set_global_joystick_map(const char *mapstring) if (!map.parse(mapstring)) return false; - osd_printf_verbose("Input: Changing default joystick map = %s\n", map.to_string().c_str()); + osd_printf_verbose("Input: Changing default joystick map = %s\n", map.to_string()); // iterate over joysticks and set the map for (int joynum = 0; joynum <= maxindex(); joynum++) @@ -666,25 +699,37 @@ bool input_class_joystick::set_global_joystick_map(const char *mapstring) // input_device_item - constructor //------------------------------------------------- -input_device_item::input_device_item(input_device &device, const char *name, void *internal, input_item_id itemid, item_get_state_func getstate, input_item_class itemclass) - : m_device(device), - m_name(name), - m_internal(internal), - m_itemid(itemid), - m_itemclass(itemclass), - m_getstate(getstate), - m_current(0), - m_memory(0) -{ - // use a standard token name for know item IDs +input_device_item::input_device_item( + input_device &device, + std::string_view name, + std::string_view tokenhint, + void *internal, + input_item_id itemid, + item_get_state_func getstate, + input_item_class itemclass) + : m_device(device) + , m_name(name) + , m_internal(internal) + , m_itemid(itemid) + , m_itemclass(itemclass) + , m_getstate(getstate) + , m_current(0) +{ const char *standard_token = manager().standard_token(itemid); - if (standard_token != nullptr) - m_token.assign(standard_token); - - // otherwise, create a tokenized name - else { - m_token.assign(name); - strmakeupper(m_token); + if (standard_token) + { + // use a standard token name for known item IDs + m_token = standard_token; + } + else if (!tokenhint.empty()) + { + // fall back to token hint if supplied + m_token = tokenhint; + } + else + { + // otherwise, create a tokenized name + m_token = strmakeupper(name); strdelchr(m_token, ' '); strdelchr(m_token, '_'); } @@ -700,6 +745,17 @@ input_device_item::~input_device_item() } +//------------------------------------------------- +// check_axis - see if axis has moved far enough +// to trigger a read when polling +//------------------------------------------------- + +bool input_device_item::check_axis(input_item_modifier modifier, s32 memory) +{ + // use osd::INVALID_AXIS_VALUE as a short-circuit + return (memory != osd::input_device::INVALID_AXIS_VALUE) && item_check_axis(modifier, memory); +} + //************************************************************************** // INPUT DEVICE SWITCH ITEM @@ -709,10 +765,16 @@ input_device_item::~input_device_item() // input_device_switch_item - constructor //------------------------------------------------- -input_device_switch_item::input_device_switch_item(input_device &device, const char *name, void *internal, input_item_id itemid, item_get_state_func getstate) - : input_device_item(device, name, internal, itemid, getstate, ITEM_CLASS_SWITCH), - m_steadykey(0), - m_oldkey(0) +input_device_switch_item::input_device_switch_item( + input_device &device, + std::string_view name, + std::string_view tokenhint, + void *internal, + input_item_id itemid, + item_get_state_func getstate) + : input_device_item(device, name, tokenhint, internal, itemid, getstate, ITEM_CLASS_SWITCH) + , m_steadykey(0) + , m_oldkey(0) { } @@ -775,6 +837,17 @@ s32 input_device_switch_item::read_as_absolute(input_item_modifier modifier) //------------------------------------------------- +// item_check_axis - see if axis has moved far +// enough to trigger a read when polling +//------------------------------------------------- + +bool input_device_switch_item::item_check_axis(input_item_modifier modifier, s32 memory) +{ + return false; +} + + +//------------------------------------------------- // steadykey_changed - update for steadykey // behavior, returning true if the current state // has changed since the last call @@ -803,8 +876,14 @@ bool input_device_switch_item::steadykey_changed() // input_device_relative_item - constructor //------------------------------------------------- -input_device_relative_item::input_device_relative_item(input_device &device, const char *name, void *internal, input_item_id itemid, item_get_state_func getstate) - : input_device_item(device, name, internal, itemid, getstate, ITEM_CLASS_RELATIVE) +input_device_relative_item::input_device_relative_item( + input_device &device, + std::string_view name, + std::string_view tokenhint, + void *internal, + input_item_id itemid, + item_get_state_func getstate) + : input_device_item(device, name, tokenhint, internal, itemid, getstate, ITEM_CLASS_RELATIVE) { } @@ -817,13 +896,20 @@ input_device_relative_item::input_device_relative_item(input_device &device, con s32 input_device_relative_item::read_as_switch(input_item_modifier modifier) { // process according to modifiers - if (modifier == ITEM_MODIFIER_POS || modifier == ITEM_MODIFIER_RIGHT || modifier == ITEM_MODIFIER_DOWN) - return (update_value() > 0); - else if (modifier == ITEM_MODIFIER_NEG || modifier == ITEM_MODIFIER_LEFT || modifier == ITEM_MODIFIER_UP) - return (update_value() < 0); - + switch (modifier) + { + case ITEM_MODIFIER_POS: + case ITEM_MODIFIER_RIGHT: + case ITEM_MODIFIER_DOWN: + return update_value() > 0; + case ITEM_MODIFIER_NEG: + case ITEM_MODIFIER_LEFT: + case ITEM_MODIFIER_UP: + return update_value() < 0; // all other cases just return 0 - return 0; + default: + return 0; + } } @@ -835,7 +921,10 @@ s32 input_device_relative_item::read_as_switch(input_item_modifier modifier) s32 input_device_relative_item::read_as_relative(input_item_modifier modifier) { // just return directly - return update_value(); + if (ITEM_MODIFIER_REVERSE == modifier) + return -update_value(); + else + return update_value(); } @@ -851,6 +940,20 @@ s32 input_device_relative_item::read_as_absolute(input_item_modifier modifier) } +//------------------------------------------------- +// item_check_axis - see if axis has moved far +// enough to trigger a read when polling +//------------------------------------------------- + +bool input_device_relative_item::item_check_axis(input_item_modifier modifier, s32 memory) +{ + const s32 curval = read_as_relative(modifier); + + // for relative axes, look for ~20 pixels movement + return std::abs(curval - memory) > (20 * osd::input_device::RELATIVE_PER_PIXEL); +} + + //************************************************************************** // INPUT DEVICE ABSOLUTE ITEM @@ -860,8 +963,14 @@ s32 input_device_relative_item::read_as_absolute(input_item_modifier modifier) // input_device_absolute_item - constructor //------------------------------------------------- -input_device_absolute_item::input_device_absolute_item(input_device &device, const char *name, void *internal, input_item_id itemid, item_get_state_func getstate) - : input_device_item(device, name, internal, itemid, getstate, ITEM_CLASS_ABSOLUTE) +input_device_absolute_item::input_device_absolute_item( + input_device &device, + std::string_view name, + std::string_view tokenhint, + void *internal, + input_item_id itemid, + item_get_state_func getstate) + : input_device_item(device, name, tokenhint, internal, itemid, getstate, ITEM_CLASS_ABSOLUTE) { } @@ -874,8 +983,7 @@ input_device_absolute_item::input_device_absolute_item(input_device &device, con s32 input_device_absolute_item::read_as_switch(input_item_modifier modifier) { // start with the current value - s32 result = m_device.adjust_absolute(update_value()); - assert(result >= INPUT_ABSOLUTE_MIN && result <= INPUT_ABSOLUTE_MAX); + s32 const result = update_value(); // left/right/up/down: if this is a joystick, fetch the paired X/Y axis values and convert if (m_device.devclass() == DEVICE_CLASS_JOYSTICK && modifier >= ITEM_MODIFIER_LEFT && modifier <= ITEM_MODIFIER_DOWN) @@ -893,18 +1001,24 @@ s32 input_device_absolute_item::read_as_switch(input_item_modifier modifier) // now map the X and Y axes to a 9x9 grid using the raw values joystick_map &joymap = downcast<input_device_joystick &>(m_device).joymap(); - return (joymap.update(xaxis_item->current(), yaxis_item->current()) >> (modifier - ITEM_MODIFIER_LEFT)) & 1; + return BIT(joymap.update(xaxis_item->current(), yaxis_item->current()), modifier - ITEM_MODIFIER_LEFT); } } - // positive/negative: true if past the deadzone in either direction - if (modifier == ITEM_MODIFIER_POS || modifier == ITEM_MODIFIER_RIGHT || modifier == ITEM_MODIFIER_DOWN) - return (result > 0); - else if (modifier == ITEM_MODIFIER_NEG || modifier == ITEM_MODIFIER_LEFT || modifier == ITEM_MODIFIER_UP) - return (result < 0); - - // all other cases just return 0 - return 0; + // positive/negative: true if past the threshold in either direction, otherwise zero + switch (modifier) + { + case ITEM_MODIFIER_POS: + case ITEM_MODIFIER_RIGHT: + case ITEM_MODIFIER_DOWN: + return result >= m_device.threshold(); + case ITEM_MODIFIER_NEG: + case ITEM_MODIFIER_LEFT: + case ITEM_MODIFIER_UP: + return -result >= m_device.threshold(); + default: + return 0; + } } @@ -930,7 +1044,7 @@ s32 input_device_absolute_item::read_as_absolute(input_item_modifier modifier) { // start with the current value s32 result = m_device.adjust_absolute(update_value()); - assert(result >= INPUT_ABSOLUTE_MIN && result <= INPUT_ABSOLUTE_MAX); + assert(result >= osd::input_device::ABSOLUTE_MIN && result <= osd::input_device::ABSOLUTE_MAX); // if we're doing a lightgun reload hack, override the value if (m_device.devclass() == DEVICE_CLASS_LIGHTGUN && m_device.lightgun_reload_button()) @@ -938,13 +1052,34 @@ s32 input_device_absolute_item::read_as_absolute(input_item_modifier modifier) // if it is pressed, return (min,max) input_device_item *button2_item = m_device.item(ITEM_ID_BUTTON2); if (button2_item != nullptr && button2_item->update_value()) - result = (m_itemid == ITEM_ID_XAXIS) ? INPUT_ABSOLUTE_MIN : INPUT_ABSOLUTE_MAX; + result = (m_itemid == ITEM_ID_XAXIS) ? osd::input_device::ABSOLUTE_MIN : osd::input_device::ABSOLUTE_MAX; } // positive/negative: scale to full axis - if (modifier == ITEM_MODIFIER_POS) - result = std::max(result, 0) * 2 + INPUT_ABSOLUTE_MIN; - if (modifier == ITEM_MODIFIER_NEG) - result = std::max(-result, 0) * 2 + INPUT_ABSOLUTE_MIN; + if (modifier == ITEM_MODIFIER_REVERSE) + result = -result; + else if (modifier == ITEM_MODIFIER_POS) + result = std::max(result, 0) * 2 + osd::input_device::ABSOLUTE_MIN; + else if (modifier == ITEM_MODIFIER_NEG) + result = std::max(-result, 0) * 2 + osd::input_device::ABSOLUTE_MIN; return result; } + + +//------------------------------------------------- +// item_check_axis - see if axis has moved far +// enough to trigger a read when polling +//------------------------------------------------- + +bool input_device_absolute_item::item_check_axis(input_item_modifier modifier, s32 memory) +{ + // ignore min/max for lightguns + // so the selection will not be affected by a gun going out of range + const s32 curval = read_as_absolute(modifier); + if (m_device.devclass() == DEVICE_CLASS_LIGHTGUN && + (curval == osd::input_device::ABSOLUTE_MAX || curval == osd::input_device::ABSOLUTE_MIN)) + return false; + + // for absolute axes, look for 25% of maximum + return std::abs(curval - memory) > ((osd::input_device::ABSOLUTE_MAX - osd::input_device::ABSOLUTE_MIN) / 4); +} diff --git a/src/emu/inputdev.h b/src/emu/inputdev.h index f6f0027b325..8e914094610 100644 --- a/src/emu/inputdev.h +++ b/src/emu/inputdev.h @@ -13,14 +13,13 @@ #pragma once +#include "interface/inputdev.h" + //************************************************************************** // TYPE DEFINITIONS //************************************************************************** -// callback for getting the value of an item on a device -typedef s32 (*item_get_state_func)(void *device_internal, void *item_internal); - // ======================> joystick_map // a 9x9 joystick map @@ -72,36 +71,44 @@ private: // a single item on an input device class input_device_item { -protected: - // construction/destruction - input_device_item(input_device &device, const char *name, void *internal, input_item_id itemid, item_get_state_func getstate, input_item_class itemclass); - public: + using item_get_state_func = osd::input_device::item_get_state_func; + virtual ~input_device_item(); // getters input_device &device() const { return m_device; } input_manager &manager() const; running_machine &machine() const; - const char *name() const { return m_name.c_str(); } + const std::string &name() const { return m_name; } void *internal() const { return m_internal; } input_item_id itemid() const { return m_itemid; } input_item_class itemclass() const { return m_itemclass; } input_code code() const; - const char *token() const { return m_token.c_str(); } + const std::string &token() const { return m_token; } s32 current() const { return m_current; } - s32 memory() const { return m_memory; } // helpers s32 update_value(); - void set_memory(s32 value) { m_memory = value; } + bool check_axis(input_item_modifier modifier, s32 memory); // readers virtual s32 read_as_switch(input_item_modifier modifier) = 0; virtual s32 read_as_relative(input_item_modifier modifier) = 0; virtual s32 read_as_absolute(input_item_modifier modifier) = 0; + virtual bool item_check_axis(input_item_modifier modifier, s32 memory) = 0; protected: + // construction/destruction + input_device_item( + input_device &device, + std::string_view name, + std::string_view tokenhint, + void *internal, + input_item_id itemid, + item_get_state_func getstate, + input_item_class itemclass); + // internal state input_device & m_device; // reference to our owning device std::string m_name; // string name of item @@ -113,44 +120,51 @@ protected: // live state s32 m_current; // current raw value - s32 m_memory; // "memory" value, to remember where we started during polling }; // ======================> input_device // a logical device of a given class that can provide input -class input_device +class input_device : public osd::input_device { friend class input_class; public: // construction/destruction - input_device(input_manager &manager, const char *_name, const char *_id, void *_internal); + input_device(input_manager &manager, std::string_view _name, std::string_view _id, void *_internal); virtual ~input_device(); // getters input_manager &manager() const { return m_manager; } running_machine &machine() const { return m_manager.machine(); } input_device_class devclass() const { return device_class(); } - const char *name() const { return m_name.c_str(); } - const char *id() const { return m_id.c_str(); } + const std::string &name() const { return m_name; } + const std::string &id() const { return m_id; } int devindex() const { return m_devindex; } input_device_item *item(input_item_id index) const { return m_item[index].get(); } + const assignment_vector &default_assignments() const { return m_default_assignments; } input_item_id maxitem() const { return m_maxitem; } void *internal() const { return m_internal; } + s32 threshold() const { return m_threshold; } bool steadykey_enabled() const { return m_steadykey_enabled; } bool lightgun_reload_button() const { return m_lightgun_reload_button; } // setters void set_devindex(int devindex) { m_devindex = devindex; } - // item management - input_item_id add_item(const char *name, input_item_id itemid, item_get_state_func getstate, void *internal = nullptr); + // interface for host input device + virtual input_item_id add_item( + std::string_view name, + std::string_view tokenhint, + input_item_id itemid, + item_get_state_func getstate, + void *internal) override; + virtual void set_default_assignments(assignment_vector &&assignments) override; // helpers s32 adjust_absolute(s32 value) const { return adjust_absolute_value(value); } - bool match_device_id(const char *deviceid); + bool match_device_id(std::string_view deviceid) const; protected: // specific overrides @@ -164,11 +178,13 @@ private: std::string m_id; // id of device int m_devindex; // device index of this device std::unique_ptr<input_device_item> m_item[ITEM_ID_ABSOLUTE_MAXIMUM+1]; // array of pointers to items + assignment_vector m_default_assignments; // additional assignments input_item_id m_maxitem; // maximum item index - void * m_internal; // internal callback pointer + void *const m_internal; // internal callback pointer - bool m_steadykey_enabled; // steadykey enabled for keyboards - bool m_lightgun_reload_button; // lightgun reload hack + s32 const m_threshold; // threshold for treating absolute axis as active + bool const m_steadykey_enabled; // steadykey enabled for keyboards + bool const m_lightgun_reload_button; // lightgun reload hack }; @@ -178,7 +194,7 @@ class input_device_keyboard : public input_device { public: // construction/destruction - input_device_keyboard(input_manager &manager, const char *_name, const char *_id, void *_internal); + input_device_keyboard(input_manager &manager, std::string_view _name, std::string_view _id, void *_internal); // helpers void apply_steadykey() const; @@ -195,7 +211,7 @@ class input_device_mouse : public input_device { public: // construction/destruction - input_device_mouse(input_manager &manager, const char *_name, const char *_id, void *_internal); + input_device_mouse(input_manager &manager, std::string_view _name, std::string_view _id, void *_internal); protected: // specific overrides @@ -209,7 +225,7 @@ class input_device_lightgun : public input_device { public: // construction/destruction - input_device_lightgun(input_manager &manager, const char *_name, const char *_id, void *_internal); + input_device_lightgun(input_manager &manager, std::string_view _name, std::string_view _id, void *_internal); protected: // specific overrides @@ -224,7 +240,7 @@ class input_device_joystick : public input_device { public: // construction/destruction - input_device_joystick(input_manager &manager, const char *_name, const char *_id, void *_internal); + input_device_joystick(input_manager &manager, std::string_view _name, std::string_view _id, void *_internal); // getters joystick_map &joymap() { return m_joymap; } @@ -239,9 +255,10 @@ protected: private: // joystick information - joystick_map m_joymap; // joystick map for this device - s32 m_joystick_deadzone; // deadzone for joystick - s32 m_joystick_saturation; // saturation position for joystick + joystick_map m_joymap; // joystick map for this device + s32 const m_deadzone; // deadzone for joystick + s32 const m_saturation; // saturation position for joystick + s64 const m_range; // difference between saturation and deadzone }; @@ -270,8 +287,7 @@ public: void set_multi(bool multi = true) { m_multi = multi; } // device management - input_device *add_device(const char *name, const char *id, void *internal = nullptr); - input_device *add_device(std::unique_ptr<input_device> &&new_device); + input_device &add_device(std::string_view name, std::string_view id, void *internal = nullptr); // misc helpers input_item_class standard_item_class(input_item_id itemid) const; @@ -279,10 +295,11 @@ public: protected: // specific overrides - virtual std::unique_ptr<input_device> make_device(const char *name, const char *id, void *internal) = 0; + virtual std::unique_ptr<input_device> make_device(std::string_view name, std::string_view id, void *internal) = 0; private: - // indexing helpers + // internal helpers + input_device &add_device(std::unique_ptr<input_device> &&new_device); int newindex(input_device &device); // internal state @@ -307,7 +324,7 @@ public: protected: // specific overrides - virtual std::unique_ptr<input_device> make_device(const char *name, const char *id, void *internal) override + virtual std::unique_ptr<input_device> make_device(std::string_view name, std::string_view id, void *internal) override { return std::make_unique<input_device_keyboard>(manager(), name, id, internal); } @@ -329,7 +346,7 @@ public: protected: // specific overrides - virtual std::unique_ptr<input_device> make_device(const char *name, const char *id, void *internal) override + virtual std::unique_ptr<input_device> make_device(std::string_view name, std::string_view id, void *internal) override { return std::make_unique<input_device_mouse>(manager(), name, id, internal); } @@ -347,7 +364,7 @@ public: protected: // specific overrides - virtual std::unique_ptr<input_device> make_device(const char *name, const char *id, void *internal) override + virtual std::unique_ptr<input_device> make_device(std::string_view name, std::string_view id, void *internal) override { return std::make_unique<input_device_lightgun>(manager(), name, id, internal); } @@ -372,7 +389,7 @@ public: protected: // specific overrides - virtual std::unique_ptr<input_device> make_device(const char *name, const char *id, void *internal) override + virtual std::unique_ptr<input_device> make_device(std::string_view name, std::string_view id, void *internal) override { return std::make_unique<input_device_joystick>(manager(), name, id, internal); } diff --git a/src/emu/ioport.cpp b/src/emu/ioport.cpp index e74502439fe..712df85db29 100644 --- a/src/emu/ioport.cpp +++ b/src/emu/ioport.cpp @@ -2,110 +2,41 @@ // copyright-holders:Aaron Giles /*************************************************************************** - ioport.c + ioport.cpp Input/output port handling. -**************************************************************************** - - Theory of operation - - ------------ - OSD controls - ------------ - - There are three types of controls that the OSD can provide as potential - input devices: digital controls, absolute analog controls, and relative - analog controls. - - Digital controls have only two states: on or off. They are generally - mapped to buttons and digital joystick directions (like a gamepad or a - joystick hat). The OSD layer must return either 0 (off) or 1 (on) for - these types of controls. - - Absolute analog controls are analog in the sense that they return a - range of values depending on how much a given control is moved, but they - are physically bounded. This means that there is a minimum and maximum - limit to how far the control can be moved. They are generally mapped to - analog joystick axes, lightguns, most PC steering wheels, and pedals. - The OSD layer must determine the minimum and maximum range of each - analog device and scale that to a value between -65536 and +65536 - representing the position of the control. -65536 generally refers to - the topmost or leftmost position, while +65536 refers to the bottommost - or rightmost position. Note that pedals are a special case here, the - OSD layer needs to return half axis as full -65536 to + 65536 range. - - Relative analog controls are analog as well, but are not physically - bounded. They can be moved continually in one direction without limit. - They are generally mapped to trackballs and mice. Because they are - unbounded, the OSD layer can only return delta values since the last - read. Because of this, it is difficult to scale appropriately. For - MAME's purposes, when mapping a mouse devices to a relative analog - control, one pixel of movement should correspond to 512 units. Other - analog control types should be scaled to return values of a similar - magnitude. Like absolute analog controls, negative values refer to - upward or leftward movement, while positive values refer to downward - or rightward movement. - - ------------- - Game controls - ------------- - - Similarly, the types of controls used by arcade games fall into the same - three categories: digital, absolute analog, and relative analog. The - tricky part is how to map any arbitrary type of OSD control to an - arbitrary type of game control. - - Digital controls: used for game buttons and standard 4/8-way joysticks, - as well as many other types of game controls. Mapping an OSD digital - control to a game's OSD control is trivial. For OSD analog controls, - the MAME core does not directly support mapping any OSD analog devices - to digital controls. However, the OSD layer is free to enumerate digital - equivalents for analog devices. For example, each analog axis in the - Windows OSD code enumerates to two digital controls, one for the - negative direction (up/left) and one for the position direction - (down/right). When these "digital" inputs are queried, the OSD layer - checks the axis position against the center, adding in a dead zone, - and returns 0 or 1 to indicate its position. - - Absolute analog controls: used for analog joysticks, lightguns, pedals, - and wheel controls. Mapping an OSD absolute analog control to this type - is easy. OSD relative analog controls can be mapped here as well by - accumulating the deltas and bounding the results. OSD digital controls - are mapped to these types of controls in pairs, one for a decrement and - one for an increment, but apart from that, operate the same as the OSD - relative analog controls by accumulating deltas and applying bounds. - The speed of the digital delta is user-configurable per analog input. - In addition, most absolute analog control types have an autocentering - feature that is activated when using the digital increment/decrement - sequences, which returns the control back to the center at a user- - controllable speed if no digital sequences are pressed. - - Relative analog controls: used for trackballs and dial controls. Again, - mapping an OSD relative analog control to this type is straightforward. - OSD absolute analog controls can't map directly to these, but if the OSD - layer provides a digital equivalent for each direction, it can be done. - OSD digital controls map just like they do for absolute analog controls, - except that the accumulated deltas are not bounded, but rather wrap. - ***************************************************************************/ #include "emu.h" -#include "emuopts.h" + #include "config.h" -#include "xmlfile.h" -#include "profiler.h" -#include "ui/uimain.h" +#include "emuopts.h" +#include "fileio.h" #include "inputdev.h" +#include "main.h" #include "natkeyboard.h" +#include "profiler.h" + +#include "ui/uimain.h" + +#include "util/corestr.h" +#include "util/ioprocsfilter.h" +#include "util/language.h" +#include "util/multibyte.h" +#include "util/unicode.h" +#include "util/xmlfile.h" #include "osdepend.h" -#include <ctype.h> -#include <time.h> +#include <algorithm> +#include <cctype> +#include <ctime> +#include <sstream> namespace { + // temporary: set this to 1 to enable the originally defined behavior that // a field specified via PORT_MODIFY which intersects a previously-defined // field completely wipes out the previous definition @@ -156,6 +87,22 @@ inline s32 apply_scale(s32 value, s64 scale) return (s64(value) * scale) / (1 << 24); } +//------------------------------------------------- +// compute_shift -- get shift required to right- +// align an I/O port field value +//------------------------------------------------- + +inline u8 compute_shift(ioport_value mask) +{ + u8 result = 0U; + while (mask && !BIT(mask, 0)) + { + mask >>= 1; + ++result; + } + return result; +} + //************************************************************************** @@ -188,6 +135,7 @@ const struct { INPUT_STRING_3C_1C, "3 Coins/1 Credit" }, { INPUT_STRING_8C_3C, "8 Coins/3 Credits" }, { INPUT_STRING_4C_2C, "4 Coins/2 Credits" }, + { INPUT_STRING_5C_2C, "5 Coins/2 Credits" }, { INPUT_STRING_2C_1C, "2 Coins/1 Credit" }, { INPUT_STRING_5C_3C, "5 Coins/3 Credits" }, { INPUT_STRING_3C_2C, "3 Coins/2 Credits" }, @@ -196,6 +144,7 @@ const struct { INPUT_STRING_3C_3C, "3 Coins/3 Credits" }, { INPUT_STRING_2C_2C, "2 Coins/2 Credits" }, { INPUT_STRING_1C_1C, "1 Coin/1 Credit" }, + { INPUT_STRING_3C_5C, "3 Coins/5 Credits" }, { INPUT_STRING_4C_5C, "4 Coins/5 Credits" }, { INPUT_STRING_3C_4C, "3 Coins/4 Credits" }, { INPUT_STRING_2C_3C, "2 Coins/3 Credits" }, @@ -289,7 +238,158 @@ const struct { INPUT_STRING_None, "None" }, }; -} // TODO: anonymous namespace +const char *const input_gm_notes_names[128] = { + "C-1", "C-1#", "D-1", "D-1#", "E-1", "F-1", "F-1#", "G-1", "G-1#", "A-1", "A-1#", "B-1", + "C0", "C0#", "D0", "D0#", "E0", "F0", "F0#", "G0", "G0#", "A0", "A0#", "B0", + "C1", "C1#", "D1", "D1#", "E1", "F1", "F1#", "G1", "G1#", "A1", "A1#", "B1", + "C2", "C2#", "D2", "D2#", "E2", "F2", "F2#", "G2", "G2#", "A2", "A2#", "B2", + "C3", "C3#", "D3", "D3#", "E3", "F3", "F3#", "G3", "G3#", "A3", "A3#", "B3", + "C4", "C4#", "D4", "D4#", "E4", "F4", "F4#", "G4", "G4#", "A4", "A4#", "B4", + "C5", "C5#", "D5", "D5#", "E5", "F5", "F5#", "G5", "G5#", "A5", "A5#", "B5", + "C6", "C6#", "D6", "D6#", "E6", "F6", "F6#", "G6", "G6#", "A6", "A6#", "B6", + "C7", "C7#", "D7", "D7#", "E7", "F7", "F7#", "G7", "G7#", "A7", "A7#", "B7", + "C8", "C8#", "D8", "D8#", "E8", "F8", "F8#", "G8", "G8#", "A8", "A8#", "B8", + "C9", "C9#", "D9", "D9#", "E9", "F9", "F9#", "G9" +}; + +inline bool input_seq_good(running_machine &machine, input_seq const &seq) +{ + if (INPUT_CODE_INVALID == seq[0]) + return false; + else if (seq.empty()) + return true; + else + return input_seq::end_code != machine.input().seq_clean(seq)[0]; +} + + +std::string substitute_player(std::string_view name, u8 player) +{ + using util::lang_translate; + + std::string result; + while (!name.empty()) + { + auto const found = name.find('%'); + if ((std::string_view::npos == found) || (name.length() == found + 1)) + { + result.append(name); + break; + } + switch (name[found + 1]) + { + case '%': + result.append(name.substr(0, found + 1)); + break; + case 'p': + result.append(name.substr(0, found)); + result.append(util::string_format(_("input-name", "P%1$u"), player + 1)); + break; + default: + result.append(name.substr(0, found + 2)); + } + name.remove_prefix(found + 2); + } + return result; +} + + + +// ======================> inp_header + +// header at the front of INP files +class inp_header +{ +public: + // parameters + static constexpr unsigned MAJVERSION = 3; + static constexpr unsigned MINVERSION = 0; + + bool read(emu_file &f) + { + return f.read(m_data, sizeof(m_data)) == sizeof(m_data); + } + bool write(emu_file &f) const + { + return f.write(m_data, sizeof(m_data)) == sizeof(m_data); + } + + bool check_magic() const + { + return 0 == std::memcmp(MAGIC, m_data + OFFS_MAGIC, OFFS_BASETIME - OFFS_MAGIC); + } + u64 get_basetime() const + { + return get_u64le(m_data + OFFS_BASETIME); + } + unsigned get_majversion() const + { + return m_data[OFFS_MAJVERSION]; + } + unsigned get_minversion() const + { + return m_data[OFFS_MINVERSION]; + } + std::string get_sysname() const + { + return get_string<OFFS_SYSNAME, OFFS_APPDESC>(); + } + std::string get_appdesc() const + { + return get_string<OFFS_APPDESC, OFFS_END>(); + } + + void set_magic() + { + std::memcpy(m_data + OFFS_MAGIC, MAGIC, OFFS_BASETIME - OFFS_MAGIC); + } + void set_basetime(u64 time) + { + put_u64le(m_data + OFFS_BASETIME, time); + } + void set_version() + { + m_data[OFFS_MAJVERSION] = MAJVERSION; + m_data[OFFS_MINVERSION] = MINVERSION; + } + void set_sysname(std::string const &name) + { + set_string<OFFS_SYSNAME, OFFS_APPDESC>(name); + } + void set_appdesc(std::string const &desc) + { + set_string<OFFS_APPDESC, OFFS_END>(desc); + } + +private: + template <std::size_t BEGIN, std::size_t END> void set_string(std::string const &str) + { + std::size_t const used = (std::min<std::size_t>)(str.size() + 1, END - BEGIN); + std::memcpy(m_data + BEGIN, str.c_str(), used); + if ((END - BEGIN) > used) + std::memset(m_data + BEGIN + used, 0, (END - BEGIN) - used); + } + template <std::size_t BEGIN, std::size_t END> std::string get_string() const + { + char const *const begin = reinterpret_cast<char const *>(m_data + BEGIN); + return std::string(begin, std::find(begin, reinterpret_cast<char const *>(m_data + END), '\0')); + } + + static constexpr std::size_t OFFS_MAGIC = 0x00; // 0x08 bytes + static constexpr std::size_t OFFS_BASETIME = 0x08; // 0x08 bytes (little-endian binary integer) + static constexpr std::size_t OFFS_MAJVERSION = 0x10; // 0x01 bytes (binary integer) + static constexpr std::size_t OFFS_MINVERSION = 0x11; // 0x01 bytes (binary integer) + // 0x02 bytes reserved + static constexpr std::size_t OFFS_SYSNAME = 0x14; // 0x0c bytes (ASCII) + static constexpr std::size_t OFFS_APPDESC = 0x20; // 0x20 bytes (ASCII) + static constexpr std::size_t OFFS_END = 0x40; + + static u8 const MAGIC[OFFS_BASETIME - OFFS_MAGIC]; + + u8 m_data[OFFS_END]; +}; + +} // anonymous namespace // XML attributes for the different types @@ -304,7 +404,7 @@ u8 const inp_header::MAGIC[inp_header::OFFS_BASETIME - inp_header::OFFS_MAGIC] = // BUILT-IN CORE MAPPINGS //************************************************************************** -#include "inpttype.h" +#include "inpttype.ipp" @@ -321,16 +421,13 @@ u8 const inp_header::MAGIC[inp_header::OFFS_BASETIME - inp_header::OFFS_MAGIC] = // to the current list //------------------------------------------------- -void ioport_list::append(device_t &device, std::string &errorbuf) +void ioport_list::append(device_t &device, std::ostream &errorbuf) { // no constructor, no list ioport_constructor constructor = device.input_ports(); - if (constructor == nullptr) + if (!constructor) return; - // reset error buffer - errorbuf.clear(); - // detokenize into the list (*constructor)(device, *this, errorbuf); @@ -349,24 +446,22 @@ void ioport_list::append(device_t &device, std::string &errorbuf) // input_type_entry - constructors //------------------------------------------------- -input_type_entry::input_type_entry(ioport_type type, ioport_group group, int player, const char *token, const char *name, input_seq standard) - : m_next(nullptr), - m_type(type), - m_group(group), - m_player(player), - m_token(token), - m_name(name) +input_type_entry::input_type_entry(ioport_type type, ioport_group group, int player, const char *token, const char *name, input_seq standard) noexcept : + m_type(type), + m_group(group), + m_player(player), + m_token(token), + m_name(name) { m_defseq[SEQ_TYPE_STANDARD] = m_seq[SEQ_TYPE_STANDARD] = standard; } -input_type_entry::input_type_entry(ioport_type type, ioport_group group, int player, const char *token, const char *name, input_seq standard, input_seq decrement, input_seq increment) - : m_next(nullptr), - m_type(type), - m_group(group), - m_player(player), - m_token(token), - m_name(name) +input_type_entry::input_type_entry(ioport_type type, ioport_group group, int player, const char *token, const char *name, input_seq standard, input_seq decrement, input_seq increment) noexcept : + m_type(type), + m_group(group), + m_player(player), + m_token(token), + m_name(name) { m_defseq[SEQ_TYPE_STANDARD] = m_seq[SEQ_TYPE_STANDARD] = standard; m_defseq[SEQ_TYPE_INCREMENT] = m_seq[SEQ_TYPE_INCREMENT] = increment; @@ -375,11 +470,41 @@ input_type_entry::input_type_entry(ioport_type type, ioport_group group, int pla //------------------------------------------------- +// name - gets the display name for the input +// type +//------------------------------------------------- + +std::string input_type_entry::name() const +{ + using util::lang_translate; + + if (!m_name) + return std::string(); + else if ((group() < IPG_PLAYER1) || (group() > IPG_PLAYER10)) + return _("input-name", m_name); + else + return substitute_player(_("input-name", m_name), player()); +} + + +//------------------------------------------------- +// replace_code - replace all instances of +// oldcodewith newcode in all sequences +//------------------------------------------------- + +void input_type_entry::replace_code(input_code oldcode, input_code newcode) noexcept +{ + for (input_seq &seq : m_seq) + seq.replace(oldcode, newcode); +} + + +//------------------------------------------------- // configure_osd - set the token and name of an // OSD entry //------------------------------------------------- -void input_type_entry::configure_osd(const char *token, const char *name) +void input_type_entry::configure_osd(const char *token, const char *name) noexcept { assert(m_type >= IPT_OSD_1 && m_type <= IPT_OSD_16); m_token = token; @@ -392,10 +517,9 @@ void input_type_entry::configure_osd(const char *token, const char *name) // from the default //------------------------------------------------- -void input_type_entry::restore_default_seq() +void input_type_entry::restore_default_seq() noexcept { - for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype) - m_seq[seqtype] = defseq(seqtype); + m_seq = m_defseq; } @@ -407,13 +531,12 @@ void input_type_entry::restore_default_seq() // digital_joystick - constructor //------------------------------------------------- -digital_joystick::digital_joystick(int player, int number) - : m_next(nullptr), - m_player(player), - m_number(number), - m_current(0), - m_current4way(0), - m_previous(0) +digital_joystick::digital_joystick(int player, int number) : + m_player(player), + m_number(number), + m_current(0), + m_current4way(0), + m_previous(0) { } @@ -426,7 +549,7 @@ digital_joystick::digital_joystick(int player, int number) digital_joystick::direction_t digital_joystick::add_axis(ioport_field &field) { direction_t direction = direction_t((field.type() - (IPT_DIGITAL_JOYSTICK_FIRST + 1)) % 4); - m_field[direction].append(*global_alloc(simple_list_wrapper<ioport_field>(&field))); + m_field[direction].emplace_front(field); return direction; } @@ -446,10 +569,10 @@ void digital_joystick::frame_update() // read all the associated ports running_machine *machine = nullptr; for (direction_t direction = JOYDIR_UP; direction < JOYDIR_COUNT; ++direction) - for (const simple_list_wrapper<ioport_field> &i : m_field[direction]) + for (const std::reference_wrapper<ioport_field> &i : m_field[direction]) { - machine = &i.object()->machine(); - if (machine->input().seq_pressed(i.object()->seq(SEQ_TYPE_STANDARD))) + machine = &i.get().machine(); + if (machine->input().seq_pressed(i.get().seq(SEQ_TYPE_STANDARD))) m_current |= 1 << direction; } @@ -496,6 +619,8 @@ void digital_joystick::frame_update() m_current4way &= ~(UP_BIT | DOWN_BIT); } } + else + m_current4way &= m_current; } @@ -515,7 +640,7 @@ bool ioport_condition::eval() const return true; // otherwise, read the referenced port and switch off the condition type - ioport_value condvalue = m_port->read(); + ioport_value const condvalue = m_port->read(); switch (m_condition) { case ALWAYS: return true; @@ -550,11 +675,10 @@ void ioport_condition::initialize(device_t &device) // ioport_setting - constructor //------------------------------------------------- -ioport_setting::ioport_setting(ioport_field &field, ioport_value _value, const char *_name) - : m_next(nullptr), - m_field(field), - m_value(_value), - m_name(_name) +ioport_setting::ioport_setting(ioport_field &field, ioport_value _value, const char *_name) : + m_field(field), + m_value(_value), + m_name(_name) { } @@ -568,11 +692,10 @@ ioport_setting::ioport_setting(ioport_field &field, ioport_value _value, const c // ioport_diplocation - constructor //------------------------------------------------- -ioport_diplocation::ioport_diplocation(const char *name, u8 swnum, bool invert) - : m_next(nullptr), - m_name(name), - m_number(swnum), - m_invert(invert) +ioport_diplocation::ioport_diplocation(std::string_view name, u8 swnum, bool invert) : + m_name(name), + m_number(swnum), + m_invert(invert) { } @@ -586,38 +709,41 @@ ioport_diplocation::ioport_diplocation(const char *name, u8 swnum, bool invert) // ioport_field - constructor //------------------------------------------------- -ioport_field::ioport_field(ioport_port &port, ioport_type type, ioport_value defvalue, ioport_value maskbits, const char *name) - : m_next(nullptr), - m_port(port), - m_modcount(port.modcount()), - m_mask(maskbits), - m_defvalue(defvalue & maskbits), - m_type(type), - m_player(0), - m_flags(0), - m_impulse(0), - m_name(name), - m_write_param(0), - m_digital_value(false), - m_min(0), - m_max(maskbits), - m_sensitivity(0), - m_delta(0), - m_centerdelta(0), - m_crosshair_axis(CROSSHAIR_AXIS_NONE), - m_crosshair_scale(1.0), - m_crosshair_offset(0), - m_crosshair_altaxis(0), - m_full_turn_count(0), - m_remap_table(nullptr), - m_way(0) +ioport_field::ioport_field(ioport_port &port, ioport_type type, ioport_value defvalue, ioport_value maskbits, const char *name) : + m_next(nullptr), + m_port(port), + m_modcount(port.modcount()), + m_mask(maskbits), + m_defvalue(defvalue & maskbits), + m_type(type), + m_player(0), + m_flags(0), + m_impulse(0), + m_name(name), + m_read(port.device()), + m_write(port.device()), + m_write_param(0), + m_digital_value(false), + m_min(0), + m_max(maskbits), + m_sensitivity(0), + m_delta(0), + m_centerdelta(0), + m_crosshair_axis(CROSSHAIR_AXIS_NONE), + m_crosshair_scale(1.0), + m_crosshair_offset(0), + m_crosshair_altaxis(0), + m_crosshair_mapper(port.device()), + m_full_turn_count(0), + m_remap_table(nullptr), + m_way(0) { // reset sequences and chars for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype) m_seq[seqtype].set_default(); - for (int i = 0; i < ARRAY_LENGTH(m_chars); i++) - std::fill(std::begin(m_chars[i]), std::end(m_chars[i]), char32_t(0)); + for (auto &chars : m_chars) + std::fill(std::begin(chars), std::end(chars), UCHAR_INVALID); // for DIP switches and configs, look for a default value from the owner if (type == IPT_DIPSWITCH || type == IPT_CONFIG) @@ -635,18 +761,39 @@ ioport_field::ioport_field(ioport_port &port, ioport_type type, ioport_value def } } + +//------------------------------------------------- +// ~ioport_field - destructor +//------------------------------------------------- + +ioport_field::~ioport_field() +{ +} + + +//------------------------------------------------- +// set_value - programmatically set field value +//------------------------------------------------- + void ioport_field::set_value(ioport_value value) { - m_digital_value = value != 0; + if (is_analog()) + live().analog->set_value(s32(value)); + else + m_digital_value = value != 0; } //------------------------------------------------- -// ~ioport_field - destructor +// clear_value - clear programmatic override //------------------------------------------------- -ioport_field::~ioport_field() +void ioport_field::clear_value() { + if (is_analog()) + live().analog->clear_value(); + else + m_digital_value = false; } @@ -655,16 +802,25 @@ ioport_field::~ioport_field() // field (this must never return nullptr) //------------------------------------------------- -const char *ioport_field::name() const +std::string ioport_field::name() const { - // if we have a non-default name, use that - if (m_live != nullptr && !m_live->name.empty()) - return m_live->name.c_str(); - if (m_name != nullptr) + using util::lang_translate; + + // if we have an overridden name, use that + if (m_live && !m_live->name.empty()) + return m_live->name; + + // if no specific name, use the generic name for the type + if (!m_name) + return manager().type_name(m_type, m_player); + + // return name for non-controller fields as-is + ioport_group const group = manager().type_group(m_type, m_player); + if ((group < IPG_PLAYER1) || (group > IPG_PLAYER10)) return m_name; - // otherwise, return the name associated with the type - return manager().type_name(m_type, m_player); + // substitute the player number in if necessary + return substitute_player(m_name, m_player); } @@ -673,18 +829,14 @@ const char *ioport_field::name() const // given input field //------------------------------------------------- -const input_seq &ioport_field::seq(input_seq_type seqtype) const +const input_seq &ioport_field::seq(input_seq_type seqtype) const noexcept { - // if no live state, return default - if (m_live == nullptr) - return defseq(seqtype); - - // if the sequence is the special default code, return the expanded default value - if (m_live->seq[seqtype].is_default()) - return manager().type_seq(m_type, m_player, seqtype); + // if the sequence is not the special default code, return it + if (m_live && !m_live->seq[seqtype].is_default()) + return m_live->seq[seqtype]; - // otherwise, return the sequence as-is - return m_live->seq[seqtype]; + // otherwise return the default sequence + return defseq(seqtype); } @@ -693,7 +845,7 @@ const input_seq &ioport_field::seq(input_seq_type seqtype) const // the given input field //------------------------------------------------- -const input_seq &ioport_field::defseq(input_seq_type seqtype) const +const input_seq &ioport_field::defseq(input_seq_type seqtype) const noexcept { // if the sequence is the special default code, return the expanded default value if (m_seq[seqtype].is_default()) @@ -711,14 +863,8 @@ const input_seq &ioport_field::defseq(input_seq_type seqtype) const void ioport_field::set_defseq(input_seq_type seqtype, const input_seq &newseq) { - const bool was_changed = seq(seqtype) != defseq(seqtype); - // set the new sequence m_seq[seqtype] = newseq; - - // also update live state unless previously customized - if (m_live != nullptr && !was_changed) - m_live->seq[seqtype] = newseq; } @@ -727,11 +873,11 @@ void ioport_field::set_defseq(input_seq_type seqtype, const input_seq &newseq) // field //------------------------------------------------- -ioport_type_class ioport_field::type_class() const +ioport_type_class ioport_field::type_class() const noexcept { // inputs associated with specific players - ioport_group group = manager().type_group(m_type, m_player); - if (group >= IPG_PLAYER1 && group <= IPG_PLAYER10) + ioport_group const group = manager().type_group(m_type, m_player); + if ((group >= IPG_PLAYER1) && (group <= IPG_PLAYER10)) return INPUT_CLASS_CONTROLLER; // keys (names derived from character codes) @@ -739,7 +885,7 @@ ioport_type_class ioport_field::type_class() const return INPUT_CLASS_KEYBOARD; // configuration settings (specific names required) - if (m_type == IPT_CONFIG) + if (m_type == IPT_CONFIG || m_type == IPT_ADJUSTER) return INPUT_CLASS_CONFIG; // DIP switches (specific names required) @@ -762,14 +908,11 @@ ioport_type_class ioport_field::type_class() const std::vector<char32_t> ioport_field::keyboard_codes(int which) const { - if (which >= ARRAY_LENGTH(m_chars)) + if (which >= std::size(m_chars)) throw emu_fatalerror("Tried to access keyboard_code with out-of-range index %d\n", which); - std::vector<char32_t> result; - for (int i = 0; i < ARRAY_LENGTH(m_chars[which]) && m_chars[which][i] != 0; i++) - result.push_back(m_chars[which][i]); - - return result; + auto &chars = m_chars[which]; + return std::vector<char32_t>(std::begin(chars), std::find(std::begin(chars), std::end(chars), UCHAR_INVALID)); } @@ -856,33 +999,35 @@ std::string ioport_field::key_name(int which) const // settings for the given input field //------------------------------------------------- -void ioport_field::get_user_settings(user_settings &settings) +void ioport_field::get_user_settings(user_settings &settings) const { // zap the entire structure - memset(&settings, 0, sizeof(settings)); + settings = user_settings(); // copy the basics for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype) + { settings.seq[seqtype] = seq(seqtype); + if (m_live) + settings.cfg[seqtype] = m_live->cfg[seqtype]; + } // if there's a list of settings or we're an adjuster, copy the current value if (!m_settinglist.empty() || m_type == IPT_ADJUSTER) settings.value = m_live->value; - // if there's analog data, extract the analog settings if (m_live->analog != nullptr) { + // if there's analog data, extract the analog settings settings.sensitivity = m_live->analog->sensitivity(); settings.delta = m_live->analog->delta(); settings.centerdelta = m_live->analog->centerdelta(); settings.reverse = m_live->analog->reverse(); } - - // non-analog settings else { + // non-analog settings settings.toggle = m_live->toggle; - settings.autofire = m_live->autofire; } } @@ -897,31 +1042,29 @@ void ioport_field::set_user_settings(const user_settings &settings) // copy the basics for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype) { - const input_seq &defseq = manager().type_seq(m_type, m_player, input_seq_type(seqtype)); - if (defseq == settings.seq[seqtype]) + if (settings.seq[seqtype].is_default()) m_live->seq[seqtype].set_default(); else m_live->seq[seqtype] = settings.seq[seqtype]; + m_live->cfg[seqtype] = settings.cfg[seqtype]; } // if there's a list of settings or we're an adjuster, copy the current value if (!m_settinglist.empty() || m_type == IPT_ADJUSTER) m_live->value = settings.value; - // if there's analog data, extract the analog settings - if (m_live->analog != nullptr) + if (m_live->analog) { + // if there's analog data, extract the analog settings m_live->analog->m_sensitivity = settings.sensitivity; m_live->analog->m_delta = settings.delta; m_live->analog->m_centerdelta = settings.centerdelta; m_live->analog->m_reverse = settings.reverse; } - - // non-analog settings else { + // non-analog settings m_live->toggle = settings.toggle; - m_live->autofire = settings.autofire; } } @@ -937,7 +1080,7 @@ const char *ioport_field::setting_name() const assert(!m_settinglist.empty()); // scan the list of settings looking for a match on the current value - for (ioport_setting &setting : m_settinglist) + for (ioport_setting const &setting : m_settinglist) if (setting.enabled()) if (setting.value() == m_live->value) return setting.name(); @@ -957,7 +1100,7 @@ bool ioport_field::has_previous_setting() const assert(!m_settinglist.empty()); // scan the list of settings looking for a match on the current value - for (ioport_setting &setting : m_settinglist) + for (ioport_setting const &setting : m_settinglist) if (setting.enabled()) return (setting.value() != m_live->value); @@ -976,30 +1119,32 @@ void ioport_field::select_previous_setting() assert(!m_settinglist.empty()); // scan the list of settings looking for a match on the current value - ioport_setting *prevsetting = nullptr; + auto prevsetting = m_settinglist.end(); bool found_match = false; - for (ioport_setting &setting : m_settinglist) - if (setting.enabled()) + for (auto setting = m_settinglist.begin(); m_settinglist.end() != setting; ++setting) + { + if (setting->enabled()) { - if (setting.value() == m_live->value) + if (setting->value() == m_live->value) { found_match = true; - if (prevsetting != nullptr) + if (m_settinglist.end() != prevsetting) break; } - prevsetting = &setting; + prevsetting = setting; } + } // if we didn't find a matching value, select the first if (!found_match) { - for (prevsetting = m_settinglist.first(); prevsetting != nullptr; prevsetting = prevsetting->next()) - if (prevsetting->enabled()) - break; + prevsetting = m_settinglist.begin(); + while ((m_settinglist.end() != prevsetting) && !prevsetting->enabled()) + ++prevsetting; } // update the value to the previous one - if (prevsetting != nullptr) + if (m_settinglist.end() != prevsetting) m_live->value = prevsetting->value(); } @@ -1016,7 +1161,8 @@ bool ioport_field::has_next_setting() const // scan the list of settings looking for a match on the current value bool found = false; - for (ioport_setting &setting : m_settinglist) + for (ioport_setting const &setting : m_settinglist) + { if (setting.enabled()) { if (found) @@ -1024,7 +1170,7 @@ bool ioport_field::has_next_setting() const if (setting.value() == m_live->value) found = true; } - + } return false; } @@ -1040,27 +1186,29 @@ void ioport_field::select_next_setting() assert(!m_settinglist.empty()); // scan the list of settings looking for a match on the current value - ioport_setting *nextsetting = nullptr; - ioport_setting *setting; - for (setting = m_settinglist.first(); setting != nullptr; setting = setting->next()) - if (setting->enabled()) - if (setting->value() == m_live->value) - break; + auto setting = m_settinglist.begin(); + while ((m_settinglist.end() != setting) && (!setting->enabled() || (setting->value() != m_live->value))) + ++setting; // if we found one, scan forward for the next valid one - if (setting != nullptr) - for (nextsetting = setting->next(); nextsetting != nullptr; nextsetting = nextsetting->next()) - if (nextsetting->enabled()) - break; + auto nextsetting = setting; + if (m_settinglist.end() != nextsetting) + { + ++nextsetting; + while ((m_settinglist.end() != nextsetting) && !nextsetting->enabled()) + ++nextsetting; + } // if we hit the end, search from the beginning - if (nextsetting == nullptr) - for (nextsetting = m_settinglist.first(); nextsetting != nullptr; nextsetting = nextsetting->next()) - if (nextsetting->enabled()) - break; + if (m_settinglist.end() == nextsetting) + { + nextsetting = m_settinglist.begin(); + while ((m_settinglist.end() != nextsetting) && !nextsetting->enabled()) + ++nextsetting; + } // update the value to the previous one - if (nextsetting != nullptr) + if (m_settinglist.end() != nextsetting) m_live->value = nextsetting->value(); } @@ -1098,19 +1246,6 @@ void ioport_field::frame_update(ioport_value &result) // if the state changed, look for switch down/switch up bool curstate = m_digital_value || machine().input().seq_pressed(seq()); - if (m_live->autofire && !machine().ioport().get_autofire_toggle()) - { - if (curstate) - { - if (m_live->autopressed > machine().ioport().get_autofire_delay()) - m_live->autopressed = 0; - else if (m_live->autopressed > machine().ioport().get_autofire_delay() / 2) - curstate = false; - m_live->autopressed++; - } - else - m_live->autopressed = 0; - } bool changed = false; if (curstate != m_live->last) { @@ -1139,7 +1274,7 @@ void ioport_field::frame_update(ioport_value &result) // toggle controls: flip the toggle state or advance to the next setting if (m_live->toggle) { - if (m_settinglist.count() == 0) + if (m_settinglist.empty()) m_live->value ^= m_mask; else select_next_setting(); @@ -1192,17 +1327,17 @@ void ioport_field::frame_update(ioport_value &result) //------------------------------------------------- -// crosshair_position - compute the crosshair +// crosshair_read - compute the crosshair // position //------------------------------------------------- -void ioport_field::crosshair_position(float &x, float &y, bool &gotx, bool &goty) +float ioport_field::crosshair_read() const { - double value = m_live->analog->crosshair_read(); + float value = m_live->analog->crosshair_read(); // apply the scale and offset if (m_crosshair_scale < 0) - value = -(1.0 - value) * m_crosshair_scale; + value = -(1.0f - value) * m_crosshair_scale; else value *= m_crosshair_scale; value += m_crosshair_offset; @@ -1211,29 +1346,7 @@ void ioport_field::crosshair_position(float &x, float &y, bool &gotx, bool &goty if (!m_crosshair_mapper.isnull()) value = m_crosshair_mapper(value); - // handle X axis - if (m_crosshair_axis == CROSSHAIR_AXIS_X) - { - x = value; - gotx = true; - if (m_crosshair_altaxis != 0) - { - y = m_crosshair_altaxis; - goty = true; - } - } - - // handle Y axis - else - { - y = value; - goty = true; - if (m_crosshair_altaxis != 0) - { - x = m_crosshair_altaxis; - gotx = true; - } - } + return value; } @@ -1243,82 +1356,85 @@ void ioport_field::crosshair_position(float &x, float &y, bool &gotx, bool &goty // descriptions //------------------------------------------------- -void ioport_field::expand_diplocation(const char *location, std::string &errorbuf) +void ioport_field::expand_diplocation(const char *location, std::ostream &errorbuf) { // if nothing present, bail - if (location == nullptr) + if (!location) return; - m_diploclist.reset(); + m_diploclist.clear(); // parse the string - std::string name; // Don't move this variable inside the loop, lastname's lifetime depends on it being outside - const char *lastname = nullptr; + std::string_view lastname; const char *curentry = location; int entries = 0; - while (*curentry != 0) + while (*curentry) { // find the end of this entry const char *comma = strchr(curentry, ','); - if (comma == nullptr) + if (!comma) comma = curentry + strlen(curentry); // extract it to tempbuf - std::string tempstr; - tempstr.assign(curentry, comma - curentry); + std::string_view tempstr(curentry, comma - curentry); // first extract the switch name if present - const char *number = tempstr.c_str(); - const char *colon = strchr(tempstr.c_str(), ':'); + std::string_view::size_type number = 0; + std::string_view::size_type const colon = tempstr.find(':'); - // allocate and copy the name if it is present - if (colon != nullptr) + std::string_view name; + if (colon != std::string_view::npos) { - lastname = name.assign(number, colon - number).c_str(); + // allocate and copy the name if it is present + lastname = tempstr.substr(0, colon); number = colon + 1; + if (lastname.empty()) + { + util::stream_format(errorbuf, "Switch location '%s' has empty switch name!\n", location); + lastname = "UNK"; + } + name = lastname; } - - // otherwise, just copy the last name else { - if (lastname == nullptr) + // otherwise, just copy the last name + if (lastname.empty()) { - errorbuf.append(string_format("Switch location '%s' missing switch name!\n", location)); - lastname = (char *)"UNK"; + util::stream_format(errorbuf, "Switch location '%s' missing switch name!\n", location); + lastname = "UNK"; } - name.assign(lastname); + name = lastname; } // if the number is preceded by a '!' it's active high - bool invert = false; - if (*number == '!') - { - invert = true; - number++; - } + bool const invert = tempstr[number] == '!'; + if (invert) + ++number; // now scan the switch number int swnum = -1; - if (sscanf(number, "%d", &swnum) != 1) - errorbuf.append(string_format("Switch location '%s' has invalid format!\n", location)); + if (sscanf(&tempstr[number], "%d", &swnum) != 1) + util::stream_format(errorbuf, "Switch location '%s' has invalid format!\n", location); + else if (0 >= swnum) + util::stream_format(errorbuf, "Switch location '%s' has switch number that is not positive!\n", location); // allocate a new entry - m_diploclist.append(*global_alloc(ioport_diplocation(name.c_str(), swnum, invert))); + if (0 < swnum) + m_diploclist.emplace_back(name, swnum, invert); entries++; // advance to the next item curentry = comma; - if (*curentry != 0) + if (*curentry) curentry++; } // then verify the number of bits in the mask matches - ioport_value temp; - int bits; - for (bits = 0, temp = m_mask; temp != 0 && bits < 32; bits++) - temp &= temp - 1; - if (bits != entries) - errorbuf.append(string_format("Switch location '%s' does not describe enough bits for mask %X\n", location, m_mask)); + int const bits = population_count_32(m_mask); + if (bits > entries) + util::stream_format(errorbuf, "Switch location '%s' does not describe enough bits for mask %X\n", location, m_mask); + else if (bits < entries) + util::stream_format(errorbuf, "Switch location '%s' describes too many bits for mask %X\n", location, m_mask); } @@ -1329,9 +1445,9 @@ void ioport_field::expand_diplocation(const char *location, std::string &errorbu void ioport_field::init_live_state(analog_field *analog) { // resolve callbacks - m_read.bind_relative_to(device()); - m_write.bind_relative_to(device()); - m_crosshair_mapper.bind_relative_to(device()); + m_read.resolve(); + m_write.resolve(); + m_crosshair_mapper.resolve(); // allocate live state m_live = std::make_unique<ioport_field_live>(*this, analog); @@ -1352,17 +1468,15 @@ void ioport_field::init_live_state(analog_field *analog) // ioport_field_live - constructor //------------------------------------------------- -ioport_field_live::ioport_field_live(ioport_field &field, analog_field *analog) - : analog(analog), - joystick(nullptr), - value(field.defvalue()), - impulse(0), - last(0), - toggle(field.toggle()), - joydir(digital_joystick::JOYDIR_COUNT), - autofire(false), - autopressed(0), - lockout(false) +ioport_field_live::ioport_field_live(ioport_field &field, analog_field *analog) : + analog(analog), + joystick(nullptr), + value(field.defvalue()), + impulse(0), + last(0), + toggle(field.toggle()), + joydir(digital_joystick::JOYDIR_COUNT), + lockout(false) { // fill in the basic values for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype) @@ -1379,7 +1493,7 @@ ioport_field_live::ioport_field_live(ioport_field &field, analog_field *analog) if (field.type_class() == INPUT_CLASS_KEYBOARD && field.specific_name() == nullptr) { // loop through each character on the field - for (int which = 0; which < 4; which++) + for (int which = 0; which < (1 << (UCHAR_SHIFT_END - UCHAR_SHIFT_BEGIN + 1)); which++) { std::vector<char32_t> const codes = field.keyboard_codes(which); if (codes.empty()) @@ -1387,12 +1501,16 @@ ioport_field_live::ioport_field_live(ioport_field &field, analog_field *analog) name.append(string_format("%-*s ", std::max(SPACE_COUNT - 1, 0), field.key_name(which))); } - // trim extra spaces - strtrimspace(name); - // special case if (name.empty()) name.assign("Unnamed Key"); + else + { + // trim extra spaces + auto pos = name.find_last_not_of(' '); + assert(pos < name.size()); + name.erase(pos + 1); + } } } @@ -1406,12 +1524,12 @@ ioport_field_live::ioport_field_live(ioport_field &field, analog_field *analog) // ioport_port - constructor //------------------------------------------------- -ioport_port::ioport_port(device_t &owner, const char *tag) - : m_next(nullptr), - m_device(owner), - m_tag(tag), - m_modcount(0), - m_active(0) +ioport_port::ioport_port(device_t &owner, const char *tag) : + m_next(nullptr), + m_device(owner), + m_tag(tag), + m_modcount(0), + m_active(0) { } @@ -1513,7 +1631,7 @@ void ioport_port::frame_update() m_live->digital = 0; // now loop back and modify based on the inputs - for (ioport_field &field : fields()) + for (ioport_field &field : m_fieldlist) field.frame_update(m_live->digital); } @@ -1523,7 +1641,7 @@ void ioport_port::frame_update() // wholly overlapped by other fields //------------------------------------------------- -void ioport_port::collapse_fields(std::string &errorbuf) +void ioport_port::collapse_fields(std::ostream &errorbuf) { ioport_value maskbits = 0; int lastmodcount = -1; @@ -1552,13 +1670,13 @@ void ioport_port::collapse_fields(std::string &errorbuf) // for errors //------------------------------------------------- -void ioport_port::insert_field(ioport_field &newfield, ioport_value &disallowedbits, std::string &errorbuf) +void ioport_port::insert_field(ioport_field &newfield, ioport_value &disallowedbits, std::ostream &errorbuf) { // verify against the disallowed bits, but only if we are condition-free if (newfield.condition().none()) { if ((newfield.mask() & disallowedbits) != 0) - errorbuf.append(string_format("INPUT_TOKEN_FIELD specifies duplicate port bits (port=%s mask=%X)\n", tag(), newfield.mask())); + util::stream_format(errorbuf, "INPUT_TOKEN_FIELD specifies duplicate port bits (port=%s mask=%X)\n", tag(), newfield.mask()); disallowedbits |= newfield.mask(); } @@ -1567,14 +1685,14 @@ void ioport_port::insert_field(ioport_field &newfield, ioport_value &disallowedb for (ioport_field *field = m_fieldlist.first(); field != nullptr; field = nextfield) { nextfield = field->next(); - if ((field->mask() & newfield.mask()) != 0 && + if ((field->mask() & newfield.mask()) && (newfield.condition().none() || field->condition().none() || field->condition() == newfield.condition())) { // reduce the mask of the field we found field->reduce_mask(newfield.mask()); // if the new entry fully overrides the previous one, we nuke - if (INPUT_PORT_OVERRIDE_FULLY_NUKES_PREVIOUS || field->mask() == 0) + if (!field->mask() || (INPUT_PORT_OVERRIDE_FULLY_NUKES_PREVIOUS && (field->type() != IPT_UNUSED) && (field->type() != IPT_UNKNOWN))) m_fieldlist.remove(*field); } } @@ -1630,10 +1748,10 @@ void ioport_port::update_defvalue(bool flush_defaults) // ioport_port_live - constructor //------------------------------------------------- -ioport_port_live::ioport_port_live(ioport_port &port) - : defvalue(0), - digital(0), - outputvalue(0) +ioport_port_live::ioport_port_live(ioport_port &port) : + defvalue(0), + digital(0), + outputvalue(0) { // iterate over fields for (ioport_field &field : port.fields()) @@ -1641,15 +1759,15 @@ ioport_port_live::ioport_port_live(ioport_port &port) // allocate analog state if it's analog analog_field *analog = nullptr; if (field.is_analog()) - analog = &analoglist.append(*global_alloc(analog_field(field))); + analog = &analoglist.emplace_back(field); // allocate a dynamic field for reading if (field.has_dynamic_read()) - readlist.append(*global_alloc(dynamic_field(field))); + readlist.emplace_back(field); // allocate a dynamic field for writing if (field.has_dynamic_write()) - writelist.append(*global_alloc(dynamic_field(field))); + writelist.emplace_back(field); // let the field initialize its live state field.init_live_state(analog); @@ -1666,22 +1784,18 @@ ioport_port_live::ioport_port_live(ioport_port &port) // ioport_manager - constructor //------------------------------------------------- -ioport_manager::ioport_manager(running_machine &machine) - : m_machine(machine), - m_safe_to_read(false), - m_last_frame_time(attotime::zero), - m_last_delta_nsec(0), - m_record_file(machine.options().input_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS), - m_playback_file(machine.options().input_directory(), OPEN_FLAG_READ), - m_playback_accumulated_speed(0), - m_playback_accumulated_frames(0), - m_timecode_file(machine.options().input_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS), - m_timecode_count(0), - m_timecode_last_time(attotime::zero), - m_autofire_toggle(false), - m_autofire_delay(3) // 1 seems too fast for a bunch of games +ioport_manager::ioport_manager(running_machine &machine) : + m_machine(machine), + m_safe_to_read(false), + m_last_frame_time(attotime::zero), + m_last_delta_nsec(0), + m_playback_accumulated_speed(0), + m_playback_accumulated_frames(0), + m_deselected_card_config(), + m_applied_device_defaults(false) { - memset(m_type_to_entry, 0, sizeof(m_type_to_entry)); + for (auto &entries : m_type_to_entry) + std::fill(std::begin(entries), std::end(entries), nullptr); } @@ -1700,13 +1814,18 @@ time_t ioport_manager::initialize() init_port_types(); // if we have a token list, proceed - device_iterator iter(machine().root_device()); - for (device_t &device : iter) + device_enumerator iter(machine().root_device()); { - std::string errors; - m_portlist.append(device, errors); - if (!errors.empty()) - osd_printf_error("Input port errors:\n%s", errors.c_str()); + std::ostringstream errors; + for (device_t &device : iter) + { + m_portlist.append(device, errors); + if (errors.tellp()) + { + osd_printf_error("Input port errors:\n%s", std::move(errors).str()); + errors.str(""); + } + } } // renumber player numbers for controller ports @@ -1719,11 +1838,14 @@ time_t ioport_manager::initialize() if (&port.second->device() == &device) { for (ioport_field &field : port.second->fields()) - if (field.type_class()==INPUT_CLASS_CONTROLLER) + { + if (field.type_class() == INPUT_CLASS_CONTROLLER) { - if (players < field.player() + 1) players = field.player() + 1; + if (players < field.player() + 1) + players = field.player() + 1; field.set_player(field.player() + player_offset); } + } } } player_offset += players; @@ -1734,20 +1856,20 @@ time_t ioport_manager::initialize() port.second->init_live_state(); // handle autoselection of devices - init_autoselect_devices(IPT_AD_STICK_X, IPT_AD_STICK_Y, IPT_AD_STICK_Z, OPTION_ADSTICK_DEVICE, "analog joystick"); - init_autoselect_devices(IPT_PADDLE, IPT_PADDLE_V, 0, OPTION_PADDLE_DEVICE, "paddle"); - init_autoselect_devices(IPT_PEDAL, IPT_PEDAL2, IPT_PEDAL3, OPTION_PEDAL_DEVICE, "pedal"); - init_autoselect_devices(IPT_LIGHTGUN_X, IPT_LIGHTGUN_Y, 0, OPTION_LIGHTGUN_DEVICE, "lightgun"); - init_autoselect_devices(IPT_POSITIONAL, IPT_POSITIONAL_V, 0, OPTION_POSITIONAL_DEVICE, "positional"); - init_autoselect_devices(IPT_DIAL, IPT_DIAL_V, 0, OPTION_DIAL_DEVICE, "dial"); - init_autoselect_devices(IPT_TRACKBALL_X, IPT_TRACKBALL_Y, 0, OPTION_TRACKBALL_DEVICE, "trackball"); - init_autoselect_devices(IPT_MOUSE_X, IPT_MOUSE_Y, 0, OPTION_MOUSE_DEVICE, "mouse"); + init_autoselect_devices({ IPT_AD_STICK_X, IPT_AD_STICK_Y, IPT_AD_STICK_Z }, OPTION_ADSTICK_DEVICE, "analog joystick"); + init_autoselect_devices({ IPT_PADDLE, IPT_PADDLE_V }, OPTION_PADDLE_DEVICE, "paddle"); + init_autoselect_devices({ IPT_PEDAL, IPT_PEDAL2, IPT_PEDAL3 }, OPTION_PEDAL_DEVICE, "pedal"); + init_autoselect_devices({ IPT_LIGHTGUN_X, IPT_LIGHTGUN_Y }, OPTION_LIGHTGUN_DEVICE, "lightgun"); + init_autoselect_devices({ IPT_POSITIONAL, IPT_POSITIONAL_V }, OPTION_POSITIONAL_DEVICE, "positional"); + init_autoselect_devices({ IPT_DIAL, IPT_DIAL_V }, OPTION_DIAL_DEVICE, "dial"); + init_autoselect_devices({ IPT_TRACKBALL_X, IPT_TRACKBALL_Y }, OPTION_TRACKBALL_DEVICE, "trackball"); + init_autoselect_devices({ IPT_MOUSE_X, IPT_MOUSE_Y }, OPTION_MOUSE_DEVICE, "mouse"); // look for 4-way diagonal joysticks and change the default map if we find any const char *joystick_map_default = machine().options().joystick_map(); if (joystick_map_default[0] == 0 || strcmp(joystick_map_default, "auto") == 0) for (auto &port : m_portlist) - for (ioport_field &field : port.second->fields()) + for (ioport_field const &field : port.second->fields()) if (field.live().joystick != nullptr && field.rotated()) { input_class_joystick &devclass = downcast<input_class_joystick &>(machine().input().device_class(DEVICE_CLASS_JOYSTICK)); @@ -1755,16 +1877,15 @@ time_t ioport_manager::initialize() break; } - // initialize natural keyboard - m_natkeyboard = std::make_unique<natural_keyboard>(machine()); - // register callbacks for when we load configurations - machine().configuration().config_register("input", config_load_delegate(&ioport_manager::load_config, this), config_save_delegate(&ioport_manager::save_config, this)); + machine().configuration().config_register( + "input", + configuration_manager::load_delegate(&ioport_manager::load_config, this), + configuration_manager::save_delegate(&ioport_manager::save_config, this)); // open playback and record files if specified time_t basetime = playback_init(); record_init(); - timecode_init(); return basetime; } @@ -1777,7 +1898,7 @@ time_t ioport_manager::initialize() void ioport_manager::init_port_types() { // convert the array into a list of type states that can be modified - construct_core_types(m_typelist); + emplace_core_types(m_typelist); // ask the OSD to customize the list machine().osd().customize_input_type_list(m_typelist); @@ -1800,39 +1921,71 @@ void ioport_manager::init_port_types() // in and the corresponding option //------------------------------------------------- -void ioport_manager::init_autoselect_devices(int type1, int type2, int type3, const char *option, const char *ananame) +void ioport_manager::init_autoselect_devices(std::initializer_list<ioport_type> types, std::string_view option, std::string_view ananame) { + static std::pair<char const *, char const *> const CLASS_OPTIONS[] = { + { "mouse", OPTION_MOUSE }, + { "joystick", OPTION_JOYSTICK }, + { "lightgun", OPTION_LIGHTGUN } }; + // if nothing specified, ignore the option - const char *stemp = machine().options().value(option); - if (stemp[0] == 0 || strcmp(stemp, "none") == 0) + auto const autooption = machine().options().get_entry(option); + char const *const autoclass = autooption->value(); + if (!autoclass || !*autoclass || !std::strcmp(autoclass, "none")) return; - // extract valid strings + // if the device class is enabled anyway or disabled at a higher priority level, do nothing + auto const classname = std::find_if( + std::begin(CLASS_OPTIONS), + std::end(CLASS_OPTIONS), + [&autoclass] (auto const &x) { return !std::strcmp(autoclass, x.first); }); + if (std::end(CLASS_OPTIONS) != classname) + { + if (machine().options().bool_value(classname->second)) + return; + + auto const classoption = machine().options().get_entry(classname->second); + if (classoption->priority() > autooption->priority()) + { + osd_printf_verbose("Input: Won't autoenable %s in presence of a %s as it's disabled at a higher priority\n", autoclass, ananame); + return; + } + } + + // find matching device class input_class *autoenable_class = nullptr; for (input_device_class devclass = DEVICE_CLASS_FIRST_VALID; devclass <= DEVICE_CLASS_LAST_VALID; ++devclass) - if (strcmp(stemp, machine().input().device_class(devclass).name()) == 0) + { + if (!std::strcmp(autoclass, machine().input().device_class(devclass).name())) { autoenable_class = &machine().input().device_class(devclass); break; } - if (autoenable_class == nullptr) + } + if (!autoenable_class) { - osd_printf_error("Invalid %s value %s; reverting to keyboard\n", option, stemp); + osd_printf_error("Invalid %s value %s; reverting to keyboard\n", option, autoclass); autoenable_class = &machine().input().device_class(DEVICE_CLASS_KEYBOARD); } - // only scan the list if we haven't already enabled this class of control - if (!autoenable_class->enabled()) - for (auto &port : m_portlist) - for (ioport_field &field : port.second->fields()) + // nothing to do if the class is already enabled + if (autoenable_class->enabled()) + return; - // if this port type is in use, apply the autoselect criteria - if ((type1 != 0 && field.type() == type1) || (type2 != 0 && field.type() == type2) || (type3 != 0 && field.type() == type3)) - { - osd_printf_verbose("Input: Autoenabling %s due to presence of a %s\n", autoenable_class->name(), ananame); - autoenable_class->enable(); - break; - } + // scan the port list + for (auto &port : m_portlist) + { + for (ioport_field const &field : port.second->fields()) + { + // if this port type is in use, apply the autoselect criteria + if (std::find(std::begin(types), std::end(types), field.type()) != std::end(types)) + { + osd_printf_verbose("Input: Autoenabling %s due to presence of a %s\n", autoenable_class->name(), ananame); + autoenable_class->enable(); + return; + } + } + } } @@ -1846,7 +1999,6 @@ void ioport_manager::exit() // close any playback or recording files playback_end(); record_end(); - timecode_end(); } @@ -1864,15 +2016,21 @@ ioport_manager::~ioport_manager() // type/player //------------------------------------------------- -const char *ioport_manager::type_name(ioport_type type, u8 player) +std::string ioport_manager::type_name(ioport_type type, u8 player) const { + using util::lang_translate; + // if we have a machine, use the live state and quick lookup - input_type_entry *entry = m_type_to_entry[type][player]; - if (entry != nullptr && entry->name() != nullptr) - return entry->name(); + input_type_entry const *const entry = m_type_to_entry[type][player]; + if (entry) + { + std::string name = entry->name(); + if (!name.empty()) + return name; + } // if we find nothing, return a default string (not a null pointer) - return "???"; + return _("input-name", "???"); } @@ -1881,7 +2039,7 @@ const char *ioport_manager::type_name(ioport_type type, u8 player) // type/player //------------------------------------------------- -ioport_group ioport_manager::type_group(ioport_type type, int player) +ioport_group ioport_manager::type_group(ioport_type type, int player) const noexcept { input_type_entry *entry = m_type_to_entry[type][player]; if (entry != nullptr) @@ -1897,7 +2055,7 @@ ioport_group ioport_manager::type_group(ioport_type type, int player) // given type/player //------------------------------------------------- -const input_seq &ioport_manager::type_seq(ioport_type type, int player, input_seq_type seqtype) +const input_seq &ioport_manager::type_seq(ioport_type type, int player, input_seq_type seqtype) const noexcept { assert(type >= 0 && type < IPT_COUNT); assert(player >= 0 && player < MAX_PLAYERS); @@ -1917,11 +2075,25 @@ const input_seq &ioport_manager::type_seq(ioport_type type, int player, input_se // the given type/player //------------------------------------------------- -void ioport_manager::set_type_seq(ioport_type type, int player, input_seq_type seqtype, const input_seq &newseq) +void ioport_manager::set_type_seq(ioport_type type, int player, input_seq_type seqtype, const input_seq &newseq) noexcept { - input_type_entry *entry = m_type_to_entry[type][player]; - if (entry != nullptr) - entry->m_seq[seqtype] = newseq; + input_type_entry *const entry = m_type_to_entry[type][player]; + if (entry) + { + if (newseq.is_default()) + { + entry->set_seq(seqtype, entry->defseq(seqtype)); + entry->set_cfg(seqtype, ""); + } + else + { + entry->set_seq(seqtype, newseq); + if (!newseq.length()) + entry->set_cfg(seqtype, "NONE"); + else + entry->set_cfg(seqtype, machine().input().seq_to_tokens(newseq)); + } + } } @@ -1941,10 +2113,10 @@ bool ioport_manager::type_pressed(ioport_type type, int player) // ioport_type_class exists in at least one port //------------------------------------------------- -bool ioport_manager::type_class_present(ioport_type_class inputclass) +bool ioport_manager::type_class_present(ioport_type_class inputclass) const noexcept { for (auto &port : m_portlist) - for (ioport_field &field : port.second->fields()) + for (ioport_field const &field : port.second->fields()) if (field.type_class() == inputclass) return true; return false; @@ -1956,11 +2128,11 @@ bool ioport_manager::type_class_present(ioport_type_class inputclass) // players //------------------------------------------------- -int ioport_manager::count_players() const +int ioport_manager::count_players() const noexcept { int max_player = 0; for (auto &port : m_portlist) - for (ioport_field &field : port.second->fields()) + for (ioport_field const &field : port.second->fields()) if (field.type_class() == INPUT_CLASS_CONTROLLER && max_player <= field.player() + 1) max_player = field.player() + 1; @@ -1969,30 +2141,6 @@ int ioport_manager::count_players() const //------------------------------------------------- -// crosshair_position - return the extracted -// crosshair values for the given player -//------------------------------------------------- - -bool ioport_manager::crosshair_position(int player, float &x, float &y) -{ - // read all the lightgun values - bool gotx = false, goty = false; - for (auto &port : m_portlist) - for (ioport_field &field : port.second->fields()) - if (field.player() == player && field.crosshair_axis() != CROSSHAIR_AXIS_NONE && field.enabled()) - { - field.crosshair_position(x, y, gotx, goty); - - // if we got both, stop - if (gotx && goty) - break; - } - - return (gotx && goty); -} - - -//------------------------------------------------- // frame_update - core logic for per-frame input // port updating //------------------------------------------------- @@ -2005,7 +2153,7 @@ digital_joystick &ioport_manager::digjoystick(int player, int number) return joystick; // create a new one - return m_joystick_list.append(*global_alloc(digital_joystick(player, number))); + return m_joystick_list.emplace_back(player, number); } @@ -2028,7 +2176,7 @@ void ioport_manager::frame_update_callback() void ioport_manager::frame_update() { -g_profiler.start(PROFILER_INPUT); + auto profile = g_profiler.start(PROFILER_INPUT); // record/playback information about the current frame attotime curtime = machine().time(); @@ -2065,8 +2213,6 @@ g_profiler.start(PROFILER_INPUT); if (dynfield.field().type() != IPT_OUTPUT) dynfield.write(newvalue); } - -g_profiler.stop(); } @@ -2092,8 +2238,19 @@ s32 ioport_manager::frame_interpolate(s32 oldval, s32 newval) // data from the XML nodes //------------------------------------------------- -void ioport_manager::load_config(config_type cfg_type, util::xml::data_node const *parentnode) +void ioport_manager::load_config(config_type cfg_type, config_level cfg_level, util::xml::data_node const *parentnode) { + // make sure device defaults get applied at some point + if ((cfg_type > config_type::CONTROLLER) && !m_applied_device_defaults) + { + apply_device_defaults(); + + // after applying controller config, push that to the backup, as it's what we'll diff against + for (input_type_entry &entry : m_typelist) + for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype) + entry.defseq(seqtype) = entry.seq(seqtype); + } + // in the completion phase, we finish the initialization with the final ports if (cfg_type == config_type::FINAL) { @@ -2102,32 +2259,39 @@ void ioport_manager::load_config(config_type cfg_type, util::xml::data_node cons } // early exit if no data to parse - if (parentnode == nullptr) + if (!parentnode) return; - // iterate over all the remap nodes for controller configs only - if (cfg_type == config_type::CONTROLLER) - load_remap_table(parentnode); - // load device map table for controller configs only if (cfg_type == config_type::CONTROLLER) { - std::unique_ptr<devicemap_table_type> devicemap_table = std::make_unique<devicemap_table_type>(); + // iterate over device remapping entries + input_manager::devicemap_table devicemap; for (util::xml::data_node const *mapdevice_node = parentnode->get_child("mapdevice"); mapdevice_node != nullptr; mapdevice_node = mapdevice_node->get_next_sibling("mapdevice")) { - const char *devicename = mapdevice_node->get_attribute_string("device", nullptr); - const char *controllername = mapdevice_node->get_attribute_string("controller", nullptr); - if (devicename != nullptr && controllername != nullptr) - { - devicemap_table->insert(std::make_pair(std::string(devicename), std::string(controllername))); - } + char const *const devicename = mapdevice_node->get_attribute_string("device", nullptr); + char const *const controllername = mapdevice_node->get_attribute_string("controller", nullptr); + if (devicename && controllername) + devicemap.emplace(devicename, controllername); } - // map device to controller if we have a device map - if (!devicemap_table->empty()) + // we can't rearrange controllers after applying device-supplied defaults + if (!m_applied_device_defaults) { - machine().input().map_device_to_controller(devicemap_table.get()); + // map device to controller if we have a device map + if (!devicemap.empty()) + machine().input().map_device_to_controller(devicemap); + + // add extra default assignments for input devices + apply_device_defaults(); + } + else if (!devicemap.empty()) + { + osd_printf_warning("Controller configuration: Only <mapdevice> elements from the first applicable <system> element are applied\n"); } + + // iterate over any input code remapping nodes + load_remap_table(*parentnode); } // iterate over all the port nodes @@ -2138,37 +2302,106 @@ void ioport_manager::load_config(config_type cfg_type, util::xml::data_node cons int type = token_to_input_type(portnode->get_attribute_string("type", ""), player); // initialize sequences to invalid defaults - input_seq newseq[SEQ_TYPE_TOTAL]; - for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype) - newseq[seqtype].set(INPUT_CODE_INVALID); + std::pair<input_seq, char const *> newseq[SEQ_TYPE_TOTAL]; + for (auto &seq : newseq) + { + seq.first.set(INPUT_CODE_INVALID); + seq.second = ""; + } // loop over new sequences for (util::xml::data_node const *seqnode = portnode->get_child("newseq"); seqnode; seqnode = seqnode->get_next_sibling("newseq")) { // with a valid type, parse out the new sequence input_seq_type seqtype = token_to_seq_type(seqnode->get_attribute_string("type", "")); - if (seqtype != -1 && seqnode->get_value() != nullptr) + if ((seqtype != -1) && seqnode->get_value()) { - if (strcmp(seqnode->get_value(), "NONE") == 0) - newseq[seqtype].set(); + if (!strcmp(seqnode->get_value(), "NONE")) + newseq[seqtype].first.reset(); else - machine().input().seq_from_tokens(newseq[seqtype], seqnode->get_value()); + machine().input().seq_from_tokens(newseq[seqtype].first, seqnode->get_value()); + if (config_type::CONTROLLER != cfg_type) + newseq[seqtype].second = seqnode->get_value(); } } - // if we're loading default ports, apply to the defaults - if (cfg_type != config_type::GAME) - load_default_config(portnode, type, player, newseq); + // load into the appropriate place for the config type/level + if (config_type::SYSTEM == cfg_type) + load_system_config(*portnode, type, player, newseq); + else if ((config_type::CONTROLLER == cfg_type) && (config_level::DEFAULT != cfg_level)) + load_controller_config(*portnode, type, player, newseq); else - load_game_config(portnode, type, player, newseq); + load_default_config(type, player, newseq); } - // after applying the controller config, push that back into the backup, since that is - // what we will diff against if (cfg_type == config_type::CONTROLLER) + { + // after applying the controller config, push that back into the backup, since that is what we will diff against for (input_type_entry &entry : m_typelist) for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype) entry.defseq(seqtype) = entry.seq(seqtype); + } + else if (cfg_type == config_type::SYSTEM) + { + // load keyboard enable/disable state + std::vector<bool> kbd_enable_set; + bool keyboard_enabled = false, missing_enabled = false; + natural_keyboard &natkbd = machine().natkeyboard(); + for (util::xml::data_node const *kbdnode = parentnode->get_child("keyboard"); kbdnode; kbdnode = kbdnode->get_next_sibling("keyboard")) + { + char const *const tag = kbdnode->get_attribute_string("tag", nullptr); + int const enabled = kbdnode->get_attribute_int("enabled", -1); + if (tag && (0 <= enabled)) + { + size_t i; + for (i = 0; natkbd.keyboard_count() > i; ++i) + { + if (!strcmp(natkbd.keyboard_device(i).tag(), tag)) + { + if (kbd_enable_set.empty()) + kbd_enable_set.resize(natkbd.keyboard_count(), false); + kbd_enable_set[i] = true; + if (enabled) + { + if (!natkbd.keyboard_is_keypad(i)) + keyboard_enabled = true; + natkbd.enable_keyboard(i); + } + else + { + natkbd.disable_keyboard(i); + } + break; + } + } + missing_enabled = missing_enabled || (enabled && (natkbd.keyboard_count() <= i)); + } + } + + // if keyboard enable configuration was loaded, patch it up for principle of least surprise + if (!kbd_enable_set.empty()) + { + for (size_t i = 0; natkbd.keyboard_count() > i; ++i) + { + if (!natkbd.keyboard_is_keypad(i)) + { + if (!keyboard_enabled && missing_enabled) + { + natkbd.enable_keyboard(i); + keyboard_enabled = true; + } + else if (!kbd_enable_set[i]) + { + if (keyboard_enabled) + natkbd.disable_keyboard(i); + else + natkbd.enable_keyboard(i); + keyboard_enabled = true; + } + } + } + } + } } @@ -2177,11 +2410,11 @@ void ioport_manager::load_config(config_type cfg_type, util::xml::data_node cons // global remapping table //------------------------------------------------- -void ioport_manager::load_remap_table(util::xml::data_node const *parentnode) +void ioport_manager::load_remap_table(util::xml::data_node const &parentnode) { // count items first so we can allocate int count = 0; - for (util::xml::data_node const *remapnode = parentnode->get_child("remap"); remapnode != nullptr; remapnode = remapnode->get_next_sibling("remap")) + for (util::xml::data_node const *remapnode = parentnode.get_child("remap"); remapnode != nullptr; remapnode = remapnode->get_next_sibling("remap")) count++; // if we have some, deal with them @@ -2193,7 +2426,7 @@ void ioport_manager::load_remap_table(util::xml::data_node const *parentnode) // build up the remap table count = 0; - for (util::xml::data_node const *remapnode = parentnode->get_child("remap"); remapnode != nullptr; remapnode = remapnode->get_next_sibling("remap")) + for (util::xml::data_node const *remapnode = parentnode.get_child("remap"); remapnode != nullptr; remapnode = remapnode->get_next_sibling("remap")) { input_code origcode = machine().input().code_from_token(remapnode->get_attribute_string("origcode", "")); input_code newcode = machine().input().code_from_token(remapnode->get_attribute_string("newcode", "")); @@ -2208,89 +2441,320 @@ void ioport_manager::load_remap_table(util::xml::data_node const *parentnode) // loop over the remapping table, then over default ports, replacing old with new for (int remapnum = 0; remapnum < count; remapnum++) for (input_type_entry &entry : m_typelist) - for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype) - entry.m_seq[seqtype].replace(oldtable[remapnum], newtable[remapnum]); + entry.replace_code(oldtable[remapnum], newtable[remapnum]); } } //------------------------------------------------- -// load_default_config - apply configuration -// data to the default mappings +// load_default_config - apply input settings +// to defaults for all systems //------------------------------------------------- -bool ioport_manager::load_default_config(util::xml::data_node const *portnode, int type, int player, const input_seq *newseq) +bool ioport_manager::load_default_config( + int type, + int player, + const std::pair<input_seq, char const *> (&newseq)[SEQ_TYPE_TOTAL]) { // find a matching port in the list for (input_type_entry &entry : m_typelist) + { if (entry.type() == type && entry.player() == player) { for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype) - if (newseq[seqtype][0] != INPUT_CODE_INVALID) - entry.m_seq[seqtype] = newseq[seqtype]; + { + if (input_seq_good(machine(), newseq[seqtype].first)) + entry.set_seq(seqtype, newseq[seqtype].first); + entry.set_cfg(seqtype, newseq[seqtype].second); + } return true; } - + } return false; } //------------------------------------------------- -// load_game_config - apply configuration -// data to the current set of input ports +// load_controller_config - apply controler +// profile settings to defaults //------------------------------------------------- -bool ioport_manager::load_game_config(util::xml::data_node const *portnode, int type, int player, const input_seq *newseq) +bool ioport_manager::load_controller_config( + util::xml::data_node const &portnode, + int type, + int player, + const std::pair<input_seq, char const *> (&newseq)[SEQ_TYPE_TOTAL]) { - // read the mask, index, and defvalue attributes - const char *tag = portnode->get_attribute_string("tag", nullptr); - ioport_value mask = portnode->get_attribute_int("mask", 0); - ioport_value defvalue = portnode->get_attribute_int("defvalue", 0); + // without a tag, apply to the defaults for all systems + char const *const tag = portnode.get_attribute_string("tag", nullptr); + if (!tag) + return load_default_config(type, player, newseq); + + // ensure the port actually exists + auto const port(m_portlist.find(tag)); + if (m_portlist.end() == port) + return false; + ioport_value const mask = portnode.get_attribute_int("mask", 0); + if (!mask) + return false; + + // find the matching field + ioport_value const defvalue = portnode.get_attribute_int("defvalue", 0); + bool matched = false; + for (ioport_field &field : port->second->fields()) + { + // find the matching mask and default value + if (field.type() == type && field.player() == player && + field.mask() == mask && (field.defvalue() & mask) == (defvalue & mask)) + { + // if a sequence was specified, override the developer-specified default for the field + for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype) + { + if (input_seq_good(machine(), newseq[seqtype].first)) + { + field.live().seq[seqtype] = newseq[seqtype].first; + field.set_defseq(seqtype, newseq[seqtype].first); + } + } + + // fetch configurable attributes + if (!field.live().analog) + { + // for non-analog fields + + // can't practically set value here + + // fetch yes/no for toggle setting + char const *const togstring = portnode.get_attribute_string("toggle", nullptr); + if (togstring && !strcmp(togstring, "yes")) + { + field.live().toggle = true; + field.m_flags |= ioport_field::FIELD_FLAG_TOGGLE; + } + else if (togstring && !strcmp(togstring, "no")) + { + field.live().toggle = false; + field.m_flags &= ~ioport_field::FIELD_FLAG_TOGGLE; + } + } + else + { + // for analog fields + +#if 0 // changing this stuff causes issues because of the way it's tied up with the analog_field object + // get base attributes + field.live().analog->m_delta = field.m_delta = portnode.get_attribute_int("keydelta", field.delta()); + field.live().analog->m_centerdelta = field.m_centerdelta = portnode.get_attribute_int("centerdelta", field.centerdelta()); + field.live().analog->m_sensitivity = field.m_sensitivity = portnode.get_attribute_int("sensitivity", field.sensitivity()); + + // fetch yes/no for reverse setting + char const *const revstring = portnode.get_attribute_string("reverse", nullptr); + if (revstring && !strcmp(revstring, "yes")) + { + field.live().analog->m_reverse = true; + field.m_flags |= ioport_field::ANALOG_FLAG_REVERSE; + } + else if (revstring && !strcmp(revstring, "no")) + { + field.live().analog->m_reverse = false; + field.m_flags &= ~ioport_field::ANALOG_FLAG_REVERSE; + } +#endif + } + + // only break out of the loop for unconditional inputs + if (field.condition().condition() == ioport_condition::ALWAYS) + return true; + else + matched = true; + } + } + + // return whether any field matched + return matched; +} - // find the port we want; if no tag, search them all - for (auto &port : m_portlist) - if (tag == nullptr || strcmp(port.second->tag(), tag) == 0) - for (ioport_field &field : port.second->fields()) - // find the matching mask and defvalue - if (field.type() == type && field.player() == player && - field.mask() == mask && (field.defvalue() & mask) == (defvalue & mask)) +//------------------------------------------------- +// load_system_config - apply saved input +// configuration for the current system +//------------------------------------------------- + +void ioport_manager::load_system_config( + util::xml::data_node const &portnode, + int type, + int player, + const std::pair<input_seq, char const *> (&newseq)[SEQ_TYPE_TOTAL]) +{ + // system-specific configuration should always apply by port/field + char const *const tag = portnode.get_attribute_string("tag", nullptr); + ioport_value const mask = portnode.get_attribute_int("mask", 0); + ioport_value const defvalue = portnode.get_attribute_int("defvalue", 0); + if (!tag || !mask) + return; + + // find the port we want + auto const port(m_portlist.find(tag)); + if (m_portlist.end() != port) + { + for (ioport_field &field : port->second->fields()) + { + // find the matching mask and default value + if (field.type() == type && field.player() == player && + field.mask() == mask && (field.defvalue() & mask) == (defvalue & mask)) + { + // if a sequence was specified, copy it in + for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype) { - // if a sequence was specified, copy it in - for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype) - if (newseq[seqtype][0] != INPUT_CODE_INVALID) - field.live().seq[seqtype] = newseq[seqtype]; + if (input_seq_good(machine(), newseq[seqtype].first)) + field.live().seq[seqtype] = newseq[seqtype].first; + field.live().cfg[seqtype] = newseq[seqtype].second; + } - // fetch configurable attributes + // fetch configurable attributes + if (!field.live().analog) + { // for non-analog fields - if (field.live().analog == nullptr) - { - // fetch the value - field.live().value = portnode->get_attribute_int("value", field.defvalue()); - // fetch yes/no for toggle setting - const char *togstring = portnode->get_attribute_string("toggle", nullptr); - if (togstring != nullptr) - field.live().toggle = (strcmp(togstring, "yes") == 0); - } + // fetch the value + field.live().value = portnode.get_attribute_int("value", field.defvalue()); + // fetch yes/no for toggle setting + char const *const togstring = portnode.get_attribute_string("toggle", nullptr); + if (togstring && !strcmp(togstring, "yes")) + field.live().toggle = true; + else if (togstring && !strcmp(togstring, "no")) + field.live().toggle = false; + } + else + { // for analog fields - else + + // get base attributes + field.live().analog->m_delta = portnode.get_attribute_int("keydelta", field.delta()); + field.live().analog->m_centerdelta = portnode.get_attribute_int("centerdelta", field.centerdelta()); + field.live().analog->m_sensitivity = portnode.get_attribute_int("sensitivity", field.sensitivity()); + + // fetch yes/no for reverse setting + char const *const revstring = portnode.get_attribute_string("reverse", nullptr); + if (revstring && !strcmp(revstring, "yes")) + field.live().analog->m_reverse = true; + else if (revstring && !strcmp(revstring, "no")) + field.live().analog->m_reverse = false; + } + + // only break out of the loop for unconditional inputs + if (field.condition().condition() == ioport_condition::ALWAYS) + break; + } + } + } + else + { + // see if this belongs to a slot card that isn't inserted + std::string_view parent_tag(tag); + auto pos(parent_tag.rfind(':')); + if (pos && (std::string_view::npos != pos)) + { + parent_tag = parent_tag.substr(0, pos); + if (!machine().root_device().subdevice(parent_tag)) + { + for (pos = parent_tag.rfind(':'); pos && (std::string_view::npos != pos); pos = parent_tag.rfind(':')) + { + std::string_view const child_tag(parent_tag.substr(pos + 1)); + parent_tag = parent_tag.substr(0, pos); + device_t const *const parent_device(machine().root_device().subdevice(parent_tag)); + if (parent_device) { - // get base attributes - field.live().analog->m_delta = portnode->get_attribute_int("keydelta", field.delta()); - field.live().analog->m_centerdelta = portnode->get_attribute_int("centerdelta", field.centerdelta()); - field.live().analog->m_sensitivity = portnode->get_attribute_int("sensitivity", field.sensitivity()); - - // fetch yes/no for reverse setting - const char *revstring = portnode->get_attribute_string("reverse", nullptr); - if (revstring != nullptr) - field.live().analog->m_reverse = (strcmp(revstring, "yes") == 0); + device_slot_interface const *slot; + if (parent_device->interface(slot) && (slot->option_list().find(std::string(child_tag)) != slot->option_list().end())) + { + if (!m_deselected_card_config) + m_deselected_card_config = util::xml::file::create(); + portnode.copy_into(*m_deselected_card_config); + } + break; } - return true; } + } + } + } +} - return false; + +//------------------------------------------------- +// apply_device_defaults - add default assignments +// supplied by input devices +//------------------------------------------------- + +void ioport_manager::apply_device_defaults() +{ + // make sure this only happens once + assert(!m_applied_device_defaults); + m_applied_device_defaults = true; + + // TODO: come up with a way to deal with non-multi device classes here? + for (input_device_class classno = DEVICE_CLASS_FIRST_VALID; DEVICE_CLASS_LAST_VALID >= classno; ++classno) + { + input_class &devclass = machine().input().device_class(classno); + for (int devnum = 0; devclass.maxindex() >= devnum; ++devnum) + { + // make sure device exists + input_device const *const device = devclass.device(devnum); + if (!device) + continue; + + // iterate over default assignments + for (auto [porttype, seqtype, seq] : device->default_assignments()) + { + assert(!seq.empty()); + assert(seq.is_valid()); + assert(!seq.is_default()); + + // only apply UI assignments for first device in a class + if ((IPT_UI_FIRST < porttype) && (IPT_UI_LAST > porttype) && (device->devindex() != 0)) + continue; + + // limit to maximum player count + if (device->devindex() >= MAX_PLAYERS) + continue; + + // find a matching port in the list + auto const found = std::find_if( + m_typelist.begin(), + m_typelist.end(), + [type = porttype, device] (input_type_entry const &entry) + { + return (entry.type() == type) && (entry.player() == device->devindex()); + }); + if (m_typelist.end() == found) + continue; + + // start with the current setting + input_seq remapped(found->seq(seqtype)); + if (!remapped.empty()) + remapped += input_seq::or_code; + + // append adjusting the device index + for (int i = 0, len = seq.length(); i < len; ++i) + { + input_code code = seq[i]; + if (!code.internal()) + { + assert(code.device_class() == classno); + assert(code.device_index() == 0); + assert(code.item_id() >= ITEM_ID_FIRST_VALID); + assert(code.item_id() <= ITEM_ID_ABSOLUTE_MAXIMUM); + code.set_device_index(device->devindex()); + } + remapped += code; + } + + // apply to the entry + found->set_seq(seqtype, remapped); + } + } + } } @@ -2307,39 +2771,18 @@ bool ioport_manager::load_game_config(util::xml::data_node const *portnode, int void ioport_manager::save_config(config_type cfg_type, util::xml::data_node *parentnode) { // if no parentnode, ignore - if (parentnode == nullptr) + if (!parentnode) return; // default ports save differently if (cfg_type == config_type::DEFAULT) save_default_inputs(*parentnode); - else + else if (cfg_type == config_type::SYSTEM) save_game_inputs(*parentnode); } //------------------------------------------------- -// save_sequence - add a node for an input -// sequence -//------------------------------------------------- - -void ioport_manager::save_sequence(util::xml::data_node &parentnode, input_seq_type type, ioport_type porttype, const input_seq &seq) -{ - // get the string for the sequence - std::string seqstring; - if (seq.length() == 0) - seqstring.assign("NONE"); - else - seqstring = machine().input().seq_to_tokens(seq); - - // add the new node - util::xml::data_node *const seqnode = parentnode.add_child("newseq", seqstring.c_str()); - if (seqnode != nullptr) - seqnode->set_attribute("type", seqtypestrings[type]); -} - - -//------------------------------------------------- // save_this_input_field_type - determine if the // given port type is worth saving //------------------------------------------------- @@ -2377,7 +2820,7 @@ void ioport_manager::save_default_inputs(util::xml::data_node &parentnode) // see if any of the sequences have changed input_seq_type seqtype; for (seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype) - if (entry.seq(seqtype) != entry.defseq(seqtype)) + if (!entry.cfg(seqtype).empty()) break; // if so, we need to add a node @@ -2385,15 +2828,21 @@ void ioport_manager::save_default_inputs(util::xml::data_node &parentnode) { // add a new port node util::xml::data_node *const portnode = parentnode.add_child("port", nullptr); - if (portnode != nullptr) + if (portnode) { // add the port information and attributes portnode->set_attribute("type", input_type_to_token(entry.type(), entry.player()).c_str()); // add only the sequences that have changed from the defaults for (input_seq_type type = SEQ_TYPE_STANDARD; type < SEQ_TYPE_TOTAL; ++type) - if (entry.seq(type) != entry.defseq(type)) - save_sequence(*portnode, type, entry.type(), entry.seq(type)); + { + if (!entry.cfg(type).empty()) + { + util::xml::data_node *const seqnode = portnode->add_child("newseq", entry.cfg(type).c_str()); + if (seqnode) + seqnode->set_attribute("type", seqtypestrings[type]); + } + } } } } @@ -2408,30 +2857,38 @@ void ioport_manager::save_default_inputs(util::xml::data_node &parentnode) void ioport_manager::save_game_inputs(util::xml::data_node &parentnode) { + // save keyboard enable/disable state + natural_keyboard &natkbd = machine().natkeyboard(); + for (size_t i = 0; natkbd.keyboard_count() > i; ++i) + { + util::xml::data_node *const kbdnode = parentnode.add_child("keyboard", nullptr); + kbdnode->set_attribute("tag", natkbd.keyboard_device(i).tag()); + kbdnode->set_attribute_int("enabled", natkbd.keyboard_enabled(i)); + } + // iterate over ports for (auto &port : m_portlist) - for (ioport_field &field : port.second->fields()) - if (save_this_input_field_type(field.type())) + for (ioport_field const &field : port.second->fields()) + if (save_this_input_field_type(field.type()) && field.enabled()) { // determine if we changed bool changed = false; - for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype) - changed |= (field.seq(seqtype) != field.defseq(seqtype)); + for (input_seq_type seqtype = SEQ_TYPE_STANDARD; (seqtype < SEQ_TYPE_TOTAL) && !changed; ++seqtype) + changed = !field.live().cfg[seqtype].empty(); - // non-analog changes if (!field.is_analog()) { - changed |= ((field.live().value & field.mask()) != (field.defvalue() & field.mask())); - changed |= (field.live().toggle != field.toggle()); + // non-analog changes + changed = changed || ((field.live().value & field.mask()) != (field.defvalue() & field.mask())); + changed = changed || (field.live().toggle != field.toggle()); } - - // analog changes else { - changed |= (field.live().analog->m_delta != field.delta()); - changed |= (field.live().analog->m_centerdelta != field.centerdelta()); - changed |= (field.live().analog->m_sensitivity != field.sensitivity()); - changed |= (field.live().analog->m_reverse != field.analog_reverse()); + // analog changes + changed = changed || (field.live().analog->m_delta != field.delta()); + changed = changed || (field.live().analog->m_centerdelta != field.centerdelta()); + changed = changed || (field.live().analog->m_sensitivity != field.sensitivity()); + changed = changed || (field.live().analog->m_reverse != field.analog_reverse()); } // if we did change, add a new node @@ -2439,7 +2896,7 @@ void ioport_manager::save_game_inputs(util::xml::data_node &parentnode) { // add a new port node util::xml::data_node *const portnode = parentnode.add_child("port", nullptr); - if (portnode != nullptr) + if (portnode) { // add the identifying information and attributes portnode->set_attribute("tag", port.second->tag()); @@ -2449,21 +2906,26 @@ void ioport_manager::save_game_inputs(util::xml::data_node &parentnode) // add sequences if changed for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype) - if (field.seq(seqtype) != field.defseq(seqtype)) - save_sequence(*portnode, seqtype, field.type(), field.seq(seqtype)); + { + if (!field.live().cfg[seqtype].empty()) + { + util::xml::data_node *const seqnode = portnode->add_child("newseq", field.live().cfg[seqtype].c_str()); + if (seqnode) + seqnode->set_attribute("type", seqtypestrings[seqtype]); + } + } - // write out non-analog changes if (!field.is_analog()) { + // write out non-analog changes if ((field.live().value & field.mask()) != (field.defvalue() & field.mask())) portnode->set_attribute_int("value", field.live().value & field.mask()); if (field.live().toggle != field.toggle()) portnode->set_attribute("toggle", field.live().toggle ? "yes" : "no"); } - - // write out analog changes else { + // write out analog changes if (field.live().analog->m_delta != field.delta()) portnode->set_attribute_int("keydelta", field.live().analog->m_delta); if (field.live().analog->m_centerdelta != field.centerdelta()) @@ -2476,6 +2938,13 @@ void ioport_manager::save_game_inputs(util::xml::data_node &parentnode) } } } + + // preserve configuration for deselected slot cards + if (m_deselected_card_config) + { + for (util::xml::data_node const *node = m_deselected_card_config->get_first_child(); node; node = node->get_next_sibling()) + node->copy_into(parentnode); + } } @@ -2489,27 +2958,34 @@ void ioport_manager::save_game_inputs(util::xml::data_node &parentnode) // file //------------------------------------------------- -template<typename _Type> -_Type ioport_manager::playback_read(_Type &result) +template<typename Type> +Type ioport_manager::playback_read(Type &result) { // protect against nullptr handles if previous reads fail - if (!m_playback_file.is_open()) - result = 0; + if (!m_playback_stream) + return result = Type(0); // read the value; if we fail, end playback - else if (m_playback_file.read(&result, sizeof(result)) != sizeof(result)) + auto const [err, actual] = read(*m_playback_stream, &result, sizeof(result)); + if (err) + { + playback_end("Read error"); + return result = Type(0); + } + else if (sizeof(result) != actual) { playback_end("End of file"); - result = 0; + return result = Type(0); } - // return the appropriate value - else if (sizeof(result) == 8) + // normalize byte order + if (sizeof(result) == 8) result = little_endianize_int64(result); else if (sizeof(result) == 4) result = little_endianize_int32(result); else if (sizeof(result) == 2) result = little_endianize_int16(result); + return result; } @@ -2534,19 +3010,20 @@ time_t ioport_manager::playback_init() return 0; // open the playback file - osd_file::error filerr = m_playback_file.open(filename); + m_playback_file = std::make_unique<emu_file>(machine().options().input_directory(), OPEN_FLAG_READ); + std::error_condition const filerr = m_playback_file->open(filename); // return an explicit error if file isn't found in given path - if(filerr == osd_file::error::NOT_FOUND) + if (filerr == std::errc::no_such_file_or_directory) fatalerror("Input file %s not found\n",filename); // TODO: bail out any other error laconically for now - if(filerr != osd_file::error::NONE) - fatalerror("Failed to open file %s for playback (code error=%d)\n",filename,int(filerr)); + if (filerr) + fatalerror("Failed to open file %s for playback (%s:%d %s)\n", filename, filerr.category().name(), filerr.value(), filerr.message()); // read the header and verify that it is a modern version; if not, print an error inp_header header; - if (!header.read(m_playback_file)) + if (!header.read(*m_playback_file)) fatalerror("Input file is corrupt or invalid (missing header)\n"); if (!header.check_magic()) fatalerror("Input file invalid or in an older, unsupported format\n"); @@ -2558,15 +3035,15 @@ time_t ioport_manager::playback_init() osd_printf_info("INP version %u.%u\n", header.get_majversion(), header.get_minversion()); time_t basetime = header.get_basetime(); osd_printf_info("Created %s\n", ctime(&basetime)); - osd_printf_info("Recorded using %s\n", header.get_appdesc().c_str()); + osd_printf_info("Recorded using %s\n", header.get_appdesc()); // verify the header against the current game std::string const sysname = header.get_sysname(); if (sysname != machine().system().name) - osd_printf_info("Input file is for machine '%s', not for current machine '%s'\n", sysname.c_str(), machine().system().name); + osd_printf_info("Input file is for machine '%s', not for current machine '%s'\n", sysname, machine().system().name); // enable compression - m_playback_file.compress(FCOMPRESS_MEDIUM); + m_playback_stream = util::zlib_read(*m_playback_file, 16386); return basetime; } @@ -2578,10 +3055,11 @@ time_t ioport_manager::playback_init() void ioport_manager::playback_end(const char *message) { // only applies if we have a live file - if (m_playback_file.is_open()) + if (m_playback_stream) { // close the file - m_playback_file.close(); + m_playback_stream.reset(); + m_playback_file.reset(); // pop a message if (message != nullptr) @@ -2594,7 +3072,8 @@ void ioport_manager::playback_end(const char *message) osd_printf_info("Average recorded speed: %d%%\n", u32((m_playback_accumulated_speed * 200 + 1) >> 21)); // close the program at the end of inp file playback - if (machine().options().exit_after_playback()) { + if (machine().options().exit_after_playback()) + { osd_printf_info("Exiting MAME now...\n"); machine().schedule_exit(); } @@ -2610,7 +3089,7 @@ void ioport_manager::playback_end(const char *message) void ioport_manager::playback_frame(const attotime &curtime) { // if playing back, fetch the information and verify - if (m_playback_file.is_open()) + if (m_playback_stream) { // first the absolute time seconds_t seconds_temp; @@ -2636,7 +3115,7 @@ void ioport_manager::playback_frame(const attotime &curtime) void ioport_manager::playback_port(ioport_port &port) { // if playing back, fetch information about this port - if (m_playback_file.is_open()) + if (m_playback_stream) { // read the default value and the digital state playback_read(port.live().defvalue); @@ -2661,16 +3140,24 @@ void ioport_manager::playback_port(ioport_port &port) // record_write - write a value to the record file //------------------------------------------------- -template<typename _Type> -void ioport_manager::record_write(_Type value) +template<typename Type> +void ioport_manager::record_write(Type value) { // protect against nullptr handles if previous reads fail - if (!m_record_file.is_open()) + if (!m_record_stream) return; - // read the value; if we fail, end playback - if (m_record_file.write(&value, sizeof(value)) != sizeof(value)) - record_end("Out of space"); + // normalize byte order + if (sizeof(value) == 8) + value = little_endianize_int64(value); + else if (sizeof(value) == 4) + value = little_endianize_int32(value); + else if (sizeof(value) == 2) + value = little_endianize_int16(value); + + // write the value; if we fail, end recording + if (write(*m_record_stream, &value, sizeof(value)).first) + record_end("Write error"); } template<> @@ -2680,29 +3167,6 @@ void ioport_manager::record_write<bool>(bool value) record_write(byte); } -template<typename _Type> -void ioport_manager::timecode_write(_Type value) -{ - // protect against nullptr handles if previous reads fail - if (!m_timecode_file.is_open()) - return; - - // read the value; if we fail, end playback - if (m_timecode_file.write(&value, sizeof(value)) != sizeof(value)) - timecode_end("Out of space"); -} - -/*template<> -void ioport_manager::timecode_write<bool>(bool value) -{ - u8 byte = u8(value); - timecode_write(byte); -}*/ -template<> -void ioport_manager::timecode_write<std::string>(std::string value) { - timecode_write(value.c_str()); -} - //------------------------------------------------- // record_init - initialize INP recording @@ -2716,9 +3180,10 @@ void ioport_manager::record_init() return; // open the record file - osd_file::error filerr = m_record_file.open(filename); - if (filerr != osd_file::error::NONE) - throw emu_fatalerror("ioport_manager::record_init: Failed to open file for recording"); + m_record_file = std::make_unique<emu_file>(machine().options().input_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); + std::error_condition const filerr = m_record_file->open(filename); + if (filerr) + throw emu_fatalerror("ioport_manager::record_init: Failed to open file for recording (%s:%d %s)", filerr.category().name(), filerr.value(), filerr.message()); // get the base time system_time systime; @@ -2733,55 +3198,13 @@ void ioport_manager::record_init() header.set_appdesc(util::string_format("%s %s", emulator_info::get_appname(), emulator_info::get_build_version())); // write it - header.write(m_record_file); + header.write(*m_record_file); // enable compression - m_record_file.compress(FCOMPRESS_MEDIUM); + m_record_stream = util::zlib_write(*m_record_file, 6, 16384); } -void ioport_manager::timecode_init() -{ - // check if option -record_timecode is enabled - if (!machine().options().record_timecode()) - { - machine().video().set_timecode_enabled(false); - return; - } - // if no file, nothing to do - const char *record_filename = machine().options().record(); - if (record_filename[0] == 0) - { - machine().video().set_timecode_enabled(false); - return; - } - - machine().video().set_timecode_enabled(true); - - // open the record file - std::string filename; - filename.append(record_filename).append(".timecode"); - osd_printf_info("Record input timecode file: %s\n", record_filename); - - osd_file::error filerr = m_timecode_file.open(filename.c_str()); - if (filerr != osd_file::error::NONE) - throw emu_fatalerror("ioport_manager::timecode_init: Failed to open file for input timecode recording"); - - m_timecode_file.puts(std::string("# ==========================================\n").c_str()); - m_timecode_file.puts(std::string("# TIMECODE FILE FOR VIDEO PREVIEW GENERATION\n").c_str()); - m_timecode_file.puts(std::string("# ==========================================\n").c_str()); - m_timecode_file.puts(std::string("#\n").c_str()); - m_timecode_file.puts(std::string("# VIDEO_PART: code of video timecode\n").c_str()); - m_timecode_file.puts(std::string("# START: start time (hh:mm:ss.mmm)\n").c_str()); - m_timecode_file.puts(std::string("# ELAPSED: elapsed time (hh:mm:ss.mmm)\n").c_str()); - m_timecode_file.puts(std::string("# MSEC_START: start time (milliseconds)\n").c_str()); - m_timecode_file.puts(std::string("# MSEC_ELAPSED: elapsed time (milliseconds)\n").c_str()); - m_timecode_file.puts(std::string("# FRAME_START: start time (frames)\n").c_str()); - m_timecode_file.puts(std::string("# FRAME_ELAPSED: elapsed time (frames)\n").c_str()); - m_timecode_file.puts(std::string("#\n").c_str()); - m_timecode_file.puts(std::string("# VIDEO_PART======= START======= ELAPSED===== MSEC_START===== MSEC_ELAPSED=== FRAME_START==== FRAME_ELAPSED==\n").c_str()); -} - //------------------------------------------------- // record_end - end INP recording //------------------------------------------------- @@ -2789,10 +3212,11 @@ void ioport_manager::timecode_init() void ioport_manager::record_end(const char *message) { // only applies if we have a live file - if (m_record_file.is_open()) + if (m_record_stream) { // close the file - m_record_file.close(); + m_record_stream.reset(); // TODO: check for errors flushing the last compressed block before doing this + m_record_file.reset(); // pop a message if (message != nullptr) @@ -2801,19 +3225,6 @@ void ioport_manager::record_end(const char *message) } -void ioport_manager::timecode_end(const char *message) -{ - // only applies if we have a live file - if (m_timecode_file.is_open()) { - // close the file - m_timecode_file.close(); - - // pop a message - if (message != nullptr) - machine().popmessage("Recording Timecode Ended\nReason: %s", message); - } -} - //------------------------------------------------- // record_frame - start of frame callback for // recording @@ -2822,7 +3233,7 @@ void ioport_manager::timecode_end(const char *message) void ioport_manager::record_frame(const attotime &curtime) { // if recording, record information about the current frame - if (m_record_file.is_open()) + if (m_record_stream) { // first the absolute time record_write(curtime.seconds()); @@ -2831,97 +3242,6 @@ void ioport_manager::record_frame(const attotime &curtime) // then the current speed record_write(u32(machine().video().speed_percent() * double(1 << 20))); } - - if (m_timecode_file.is_open() && machine().video().get_timecode_write()) - { - // Display the timecode - m_timecode_count++; - std::string const current_time_str = string_format("%02d:%02d:%02d.%03d", - (int)curtime.seconds() / (60 * 60), - (curtime.seconds() / 60) % 60, - curtime.seconds() % 60, - (int)(curtime.attoseconds()/ATTOSECONDS_PER_MILLISECOND)); - - // Elapsed from previous timecode - attotime const elapsed_time = curtime - m_timecode_last_time; - m_timecode_last_time = curtime; - std::string const elapsed_time_str = string_format("%02d:%02d:%02d.%03d", - elapsed_time.seconds() / (60 * 60), - (elapsed_time.seconds() / 60) % 60, - elapsed_time.seconds() % 60, - int(elapsed_time.attoseconds()/ATTOSECONDS_PER_MILLISECOND)); - - // Number of ms from beginning of playback - int const mseconds_start = curtime.seconds()*1000 + curtime.attoseconds()/ATTOSECONDS_PER_MILLISECOND; - std::string const mseconds_start_str = string_format("%015d", mseconds_start); - - // Number of ms from previous timecode - int mseconds_elapsed = elapsed_time.seconds()*1000 + elapsed_time.attoseconds()/ATTOSECONDS_PER_MILLISECOND; - std::string const mseconds_elapsed_str = string_format("%015d", mseconds_elapsed); - - // Number of frames from beginning of playback - int const frame_start = mseconds_start * 60 / 1000; - std::string const frame_start_str = string_format("%015d", frame_start); - - // Number of frames from previous timecode - int frame_elapsed = mseconds_elapsed * 60 / 1000; - std::string const frame_elapsed_str = string_format("%015d", frame_elapsed); - - std::string message; - std::string timecode_text; - std::string timecode_key; - bool show_timecode_counter = false; - if (m_timecode_count==1) { - message = string_format("TIMECODE: Intro started at %s", current_time_str); - timecode_key = "INTRO_START"; - timecode_text = "INTRO"; - show_timecode_counter = true; - } - else if (m_timecode_count==2) { - machine().video().add_to_total_time(elapsed_time); - message = string_format("TIMECODE: Intro duration %s", elapsed_time_str); - timecode_key = "INTRO_STOP"; - //timecode_text = "INTRO"; - } - else if (m_timecode_count==3) { - message = string_format("TIMECODE: Gameplay started at %s", current_time_str); - timecode_key = "GAMEPLAY_START"; - timecode_text = "GAMEPLAY"; - show_timecode_counter = true; - } - else if (m_timecode_count==4) { - machine().video().add_to_total_time(elapsed_time); - message = string_format("TIMECODE: Gameplay duration %s", elapsed_time_str); - timecode_key = "GAMEPLAY_STOP"; - //timecode_text = "GAMEPLAY"; - } - else if (m_timecode_count % 2 == 1) { - message = string_format("TIMECODE: Extra %d started at %s", (m_timecode_count-3)/2, current_time_str); - timecode_key = string_format("EXTRA_START_%03d", (m_timecode_count-3)/2); - timecode_text = string_format("EXTRA %d", (m_timecode_count-3)/2); - show_timecode_counter = true; - } - else { - machine().video().add_to_total_time(elapsed_time); - message = string_format("TIMECODE: Extra %d duration %s", (m_timecode_count-4)/2, elapsed_time_str); - timecode_key = string_format("EXTRA_STOP_%03d", (m_timecode_count-4)/2); - } - - osd_printf_info("%s \n", message.c_str()); - machine().popmessage("%s \n", message.c_str()); - - m_timecode_file.printf( - "%-19s %s %s %s %s %s %s\n", - timecode_key.c_str(), - current_time_str.c_str(), elapsed_time_str.c_str(), - mseconds_start_str.c_str(), mseconds_elapsed_str.c_str(), - frame_start_str.c_str(), frame_elapsed_str.c_str()); - - machine().video().set_timecode_write(false); - machine().video().set_timecode_text(timecode_text); - machine().video().set_timecode_start(m_timecode_last_time); - machine().ui().set_show_timecode_counter(show_timecode_counter); - } } @@ -2932,7 +3252,7 @@ void ioport_manager::record_frame(const attotime &curtime) void ioport_manager::record_port(ioport_port &port) { // if recording, store information about this port - if (m_record_file.is_open()) + if (m_record_stream) { // store the default value and digital state record_write(port.live().defvalue); @@ -2962,16 +3282,28 @@ void ioport_manager::record_port(ioport_port &port) // ioport_configurer - constructor //------------------------------------------------- -ioport_configurer::ioport_configurer(device_t &owner, ioport_list &portlist, std::string &errorbuf) - : m_owner(owner), - m_portlist(portlist), - m_errorbuf(errorbuf), - m_curport(nullptr), - m_curfield(nullptr), - m_cursetting(nullptr) +ioport_configurer::ioport_configurer(device_t &owner, ioport_list &portlist, std::ostream &errorbuf) : + m_owner(owner), + m_portlist(portlist), + m_errorbuf(errorbuf), + m_curport(nullptr), + m_curfield(nullptr), + m_cursetting(nullptr), + m_curshift(0) { } +//------------------------------------------------- +// field_set_gm_note - set a ioport as a general +// midi-encoded note number. Only sets the name +// for now +//------------------------------------------------- + +ioport_configurer& ioport_configurer::field_set_gm_note(u8 note) +{ + field_set_name(input_gm_notes_names[note]); + return *this; +} //------------------------------------------------- // string_from_token - convert an @@ -2991,12 +3323,9 @@ const char *ioport_configurer::string_from_token(const char *string) #if false // Set true, If you want to take care missing-token or wrong-sorting // otherwise, scan the list for a matching string and return it - { - int index; - for (index = 0; index < ARRAY_LENGTH(input_port_default_strings); index++) + for (int index = 0; index < std::size(input_port_default_strings); index++) if (input_port_default_strings[index].id == uintptr_t(string)) return input_port_default_strings[index].string; - } return "(Unknown Default)"; #else @@ -3037,9 +3366,9 @@ ioport_configurer& ioport_configurer::port_modify(const char *tag) std::string fulltag = m_owner.subtag(tag); // find the existing port - m_curport = m_portlist.find(fulltag.c_str())->second.get(); + m_curport = m_portlist.find(fulltag)->second.get(); if (m_curport == nullptr) - throw emu_fatalerror("Requested to modify nonexistent port '%s'", fulltag.c_str()); + throw emu_fatalerror("Requested to modify nonexistent port '%s'", fulltag); // bump the modification count, and reset current field/setting m_curport->m_modcount++; @@ -3061,10 +3390,11 @@ ioport_configurer& ioport_configurer::field_alloc(ioport_type type, ioport_value // append the field if (type != IPT_UNKNOWN && type != IPT_UNUSED) m_curport->m_active |= mask; - m_curfield = &m_curport->m_fieldlist.append(*global_alloc(ioport_field(*m_curport, type, defval, mask, string_from_token(name)))); + m_curfield = &m_curport->m_fieldlist.append(*new ioport_field(*m_curport, type, defval, mask, string_from_token(name))); // reset the current setting m_cursetting = nullptr; + m_curshift = 0; return *this; } @@ -3075,16 +3405,17 @@ ioport_configurer& ioport_configurer::field_alloc(ioport_type type, ioport_value ioport_configurer& ioport_configurer::field_add_char(std::initializer_list<char32_t> charlist) { - for (int index = 0; index < ARRAY_LENGTH(m_curfield->m_chars); index++) - if (m_curfield->m_chars[index][0] == 0) - { - const size_t char_count = ARRAY_LENGTH(m_curfield->m_chars[index]); - assert(charlist.size() > 0 && charlist.size() <= char_count); + if (m_curshift < std::size(m_curfield->m_chars)) + { + auto &chars = m_curfield->m_chars[m_curshift++]; + assert(chars[0] == UCHAR_INVALID); + assert(charlist.size() <= std::size(chars)); - for (size_t i = 0; i < char_count; i++) - m_curfield->m_chars[index][i] = i < charlist.size() ? *(charlist.begin() + i) : 0; - return *this; - } + std::copy(charlist.begin(), charlist.end(), std::begin(chars)); + std::fill(std::begin(chars) + charlist.size(), std::end(chars), UCHAR_INVALID); + + return *this; + } std::ostringstream s; bool is_first = true; @@ -3093,7 +3424,7 @@ ioport_configurer& ioport_configurer::field_add_char(std::initializer_list<char3 util::stream_format(s, "%s%d", is_first ? "" : ",", (int)ch); is_first = false; } - throw emu_fatalerror("PORT_CHAR(%s) could not be added - maximum amount exceeded\n", s.str().c_str()); + throw emu_fatalerror("PORT_CHAR(%s) could not be added - maximum amount exceeded\n", s.str()); } @@ -3115,12 +3446,11 @@ ioport_configurer& ioport_configurer::field_add_code(input_seq_type which, input ioport_configurer& ioport_configurer::setting_alloc(ioport_value value, const char *name) { // make sure we have a field - if (m_curfield == nullptr) - throw emu_fatalerror("alloc_setting called with no active field (value=%X name=%s)\n", value, name); + if (!m_curfield) + throw emu_fatalerror("setting_alloc called with no active field (value=%X name=%s)\n", value, name); - m_cursetting = global_alloc(ioport_setting(*m_curfield, value & m_curfield->mask(), string_from_token(name))); // append a new setting - m_curfield->m_settinglist.append(*m_cursetting); + m_cursetting = &m_curfield->m_settinglist.emplace_back(*m_curfield, value & m_curfield->mask(), string_from_token(name)); return *this; } @@ -3132,7 +3462,7 @@ ioport_configurer& ioport_configurer::setting_alloc(ioport_value value, const ch ioport_configurer& ioport_configurer::set_condition(ioport_condition::condition_t condition, const char *tag, ioport_value mask, ioport_value value) { - ioport_condition &target = (m_cursetting != nullptr) ? m_cursetting->condition() : m_curfield->condition(); + ioport_condition &target = m_cursetting ? m_cursetting->condition() : m_curfield->condition(); target.set(condition, tag, mask, value); return *this; } @@ -3169,11 +3499,10 @@ ioport_configurer& ioport_configurer::onoff_alloc(const char *name, ioport_value // dynamic_field - constructor //------------------------------------------------- -dynamic_field::dynamic_field(ioport_field &field) - : m_next(nullptr), - m_field(field), - m_shift(0), - m_oldval(field.defvalue()) +dynamic_field::dynamic_field(ioport_field &field) : + m_field(field), + m_shift(0), + m_oldval(field.defvalue()) { // fill in the data for (ioport_value mask = field.mask(); !(mask & 1); mask >>= 1) @@ -3190,15 +3519,15 @@ dynamic_field::dynamic_field(ioport_field &field) void dynamic_field::read(ioport_value &result) { // skip if not enabled - if (!m_field.enabled()) - return; - - // call the callback to read a new value - ioport_value newval = m_field.m_read(); - m_oldval = newval; + if (m_field.enabled()) + { + // call the callback to read a new value + ioport_value newval = m_field.m_read(); + m_oldval = newval; - // merge in the bits (don't invert yet, as all digitals are inverted together) - result = (result & ~m_field.mask()) | ((newval << m_shift) & m_field.mask()); + // merge in the bits (don't invert yet, as all digitals are inverted together) + result = (result & ~m_field.mask()) | ((newval << m_shift) & m_field.mask()); + } } @@ -3210,15 +3539,15 @@ void dynamic_field::read(ioport_value &result) void dynamic_field::write(ioport_value newval) { // skip if not enabled - if (!m_field.enabled()) - return; - - // if the bits have changed, call the handler - newval = (newval & m_field.mask()) >> m_shift; - if (m_oldval != newval) + if (m_field.enabled()) { - m_field.m_write(m_field, m_field.m_write_param, m_oldval, newval); - m_oldval = newval; + // if the bits have changed, call the handler + newval = (newval & m_field.mask()) >> m_shift; + if (m_oldval != newval) + { + m_field.m_write(m_field, m_field.m_write_param, m_oldval, newval); + m_oldval = newval; + } } } @@ -3227,45 +3556,37 @@ void dynamic_field::write(ioport_value newval) // analog_field - constructor //------------------------------------------------- -analog_field::analog_field(ioport_field &field) - : m_next(nullptr), - m_field(field), - m_shift(0), - m_adjdefvalue(field.defvalue() & field.mask()), - m_adjmin(field.minval() & field.mask()), - m_adjmax(field.maxval() & field.mask()), - m_sensitivity(field.sensitivity()), - m_reverse(field.analog_reverse()), - m_delta(field.delta()), - m_centerdelta(field.centerdelta()), - m_accum(0), - m_previous(0), - m_previousanalog(0), - m_minimum(INPUT_ABSOLUTE_MIN), - m_maximum(INPUT_ABSOLUTE_MAX), - m_center(0), - m_reverse_val(0), - m_scalepos(0), - m_scaleneg(0), - m_keyscalepos(0), - m_keyscaleneg(0), - m_positionalscale(0), - m_absolute(false), - m_wraps(false), - m_autocenter(false), - m_single_scale(false), - m_interpolate(false), - m_lastdigital(false) -{ - // compute the shift amount and number of bits - for (ioport_value mask = field.mask(); !(mask & 1); mask >>= 1) - m_shift++; - - // initialize core data - m_adjdefvalue >>= m_shift; - m_adjmin >>= m_shift; - m_adjmax >>= m_shift; - +analog_field::analog_field(ioport_field &field) : + m_field(field), + m_shift(compute_shift(field.mask())), + m_adjdefvalue((field.defvalue() & field.mask()) >> m_shift), + m_adjmin((field.minval() & field.mask()) >> m_shift), + m_adjmax((field.maxval() & field.mask()) >> m_shift), + m_adjoverride((field.defvalue() & field.mask()) >> m_shift), + m_sensitivity(field.sensitivity()), + m_reverse(field.analog_reverse()), + m_delta(field.delta()), + m_centerdelta(field.centerdelta()), + m_accum(0), + m_previous(0), + m_previousanalog(0), + m_minimum(osd::input_device::ABSOLUTE_MIN), + m_maximum(osd::input_device::ABSOLUTE_MAX), + m_center(0), + m_reverse_val(0), + m_scalepos(0), + m_scaleneg(0), + m_keyscalepos(0), + m_keyscaleneg(0), + m_positionalscale(0), + m_absolute(false), + m_wraps(false), + m_autocenter(false), + m_single_scale(false), + m_interpolate(false), + m_lastdigital(false), + m_use_adjoverride(false) +{ // set basic parameters based on the configured type switch (field.type()) { @@ -3284,7 +3605,7 @@ analog_field::analog_field(ioport_field &field) case IPT_PEDAL: case IPT_PEDAL2: case IPT_PEDAL3: - m_center = INPUT_ABSOLUTE_MIN; + m_center = osd::input_device::ABSOLUTE_MIN; m_accum = apply_inverse_sensitivity(m_center); m_absolute = true; m_autocenter = true; @@ -3303,7 +3624,7 @@ analog_field::analog_field(ioport_field &field) // set each position to be 512 units case IPT_POSITIONAL: case IPT_POSITIONAL_V: - m_positionalscale = compute_scale(field.maxval(), INPUT_ABSOLUTE_MAX - INPUT_ABSOLUTE_MIN); + m_positionalscale = compute_scale(field.maxval(), osd::input_device::ABSOLUTE_MAX - osd::input_device::ABSOLUTE_MIN); m_adjmin = 0; m_adjmax = field.maxval() - 1; m_wraps = field.analog_wraps(); @@ -3329,21 +3650,23 @@ analog_field::analog_field(ioport_field &field) fatalerror("Unknown analog port type -- don't know if it is absolute or not\n"); } - // further processing for absolute controls if (m_absolute) { + // further processing for absolute controls + // if the default value is pegged at the min or max, use a single scale value for the whole axis m_single_scale = (m_adjdefvalue == m_adjmin) || (m_adjdefvalue == m_adjmax); // if not "single scale", compute separate scales for each side of the default if (!m_single_scale) { - // unsigned - m_scalepos = compute_scale(m_adjmax - m_adjdefvalue, INPUT_ABSOLUTE_MAX - 0); - m_scaleneg = compute_scale(m_adjdefvalue - m_adjmin, 0 - INPUT_ABSOLUTE_MIN); - - if (m_adjmin > m_adjmax) - m_scaleneg = -m_scaleneg; + // unsigned, potentially passing through zero + m_scalepos = compute_scale( + (m_adjmax - m_adjdefvalue) & (field.mask() >> m_shift), + osd::input_device::ABSOLUTE_MAX); + m_scaleneg = compute_scale( + (m_adjdefvalue - m_adjmin) & (field.mask() >> m_shift), + -osd::input_device::ABSOLUTE_MIN); // reverse point is at center m_reverse_val = 0; @@ -3351,7 +3674,7 @@ analog_field::analog_field(ioport_field &field) else { // single axis that increases from default - m_scalepos = compute_scale(m_adjmax - m_adjmin, INPUT_ABSOLUTE_MAX - INPUT_ABSOLUTE_MIN); + m_scalepos = compute_scale(m_adjmax - m_adjmin, osd::input_device::ABSOLUTE_MAX - osd::input_device::ABSOLUTE_MIN); // make the scaling the same for easier coding when we need to scale m_scaleneg = m_scalepos; @@ -3360,11 +3683,11 @@ analog_field::analog_field(ioport_field &field) m_reverse_val = m_maximum; } } - - // relative and positional controls all map directly with a 512x scale factor else { - // The relative code is set up to allow specifing PORT_MINMAX and default values. + // relative and positional controls all map directly with a 512x scale factor + + // The relative code is set up to allow specifying PORT_MINMAX and default values. // The validity checks are purposely set up to not allow you to use anything other // a default of 0 and PORT_MINMAX(0,mask). This is in case the need arises to use // this feature in the future. Keeping the code in does not hurt anything. @@ -3375,15 +3698,17 @@ analog_field::analog_field(ioport_field &field) if (m_wraps) m_adjmax++; - m_minimum = (m_adjmin - m_adjdefvalue) * INPUT_RELATIVE_PER_PIXEL; - m_maximum = (m_adjmax - m_adjdefvalue) * INPUT_RELATIVE_PER_PIXEL; + m_minimum = (m_adjmin - m_adjdefvalue) * osd::input_device::RELATIVE_PER_PIXEL; + m_maximum = (m_adjmax - m_adjdefvalue) * osd::input_device::RELATIVE_PER_PIXEL; // make the scaling the same for easier coding when we need to scale - m_scaleneg = m_scalepos = compute_scale(1, INPUT_RELATIVE_PER_PIXEL); + m_scaleneg = m_scalepos = compute_scale(1, osd::input_device::RELATIVE_PER_PIXEL); if (m_field.analog_reset()) + { // delta values reverse from center m_reverse_val = 0; + } else { // positional controls reverse from their max range @@ -3392,11 +3717,11 @@ analog_field::analog_field(ioport_field &field) // relative controls reverse from 1 past their max range if (m_wraps) { - // FIXME: positional needs -1, using INPUT_RELATIVE_PER_PIXEL skips a position (and reads outside the table array) - if(field.type() == IPT_POSITIONAL || field.type() == IPT_POSITIONAL_V) - m_reverse_val --; + // FIXME: positional needs -1, using osd::input_device::RELATIVE_PER_PIXEL skips a position (and reads outside the table array) + if (field.type() == IPT_POSITIONAL || field.type() == IPT_POSITIONAL_V) + m_reverse_val--; else - m_reverse_val -= INPUT_RELATIVE_PER_PIXEL; + m_reverse_val -= osd::input_device::RELATIVE_PER_PIXEL; } } } @@ -3469,13 +3794,14 @@ s32 analog_field::apply_settings(s32 value) const else if (m_single_scale) // it's a pedal or the default value is equal to min/max // so we need to adjust the center to the minimum - value -= INPUT_ABSOLUTE_MIN; + value -= osd::input_device::ABSOLUTE_MIN; // map differently for positive and negative values + const s32 adjust = m_field.analog_reset() ? 0 : (1 << 23); if (value >= 0) - value = apply_scale(value, m_scalepos); + value = ((s64(value) * m_scalepos) + adjust) / (1 << 24); else - value = apply_scale(value, m_scaleneg); + value = ((s64(value) * m_scaleneg) - adjust) / (1 << 24); value += m_adjdefvalue; // for relative devices, wrap around when we go past the edge @@ -3495,6 +3821,29 @@ s32 analog_field::apply_settings(s32 value) const //------------------------------------------------- +// set_value - override the value that will be +// read from the field +//------------------------------------------------- + +void analog_field::set_value(s32 value) +{ + m_use_adjoverride = true; + m_adjoverride = std::clamp(value, m_adjmin, m_adjmax); +} + + +//------------------------------------------------- +// clear_value - clear programmatic override +//------------------------------------------------- + +void analog_field::clear_value() +{ + m_use_adjoverride = false; + m_adjoverride = m_adjdefvalue; +} + + +//------------------------------------------------- // frame_update - update the internals of a // single analog field periodically //------------------------------------------------- @@ -3515,7 +3864,23 @@ void analog_field::frame_update(running_machine &machine) // if we got an absolute input, it overrides everything else if (itemclass == ITEM_CLASS_ABSOLUTE) { - if (m_previousanalog != rawvalue) + if (!m_absolute && !m_positionalscale) + { + // if port is relative, we use the value to simulate the speed of relative movement + // sensitivity adjustment is allowed for this mode + if (rawvalue) + { + if (m_field.analog_reset()) + m_accum = rawvalue / 8; + else + m_accum += rawvalue / 8; + + // do not bother with other control types if the analog data is changing + m_lastdigital = false; + return; + } + } + else if (m_previousanalog != rawvalue) { // only update if analog value changed m_previousanalog = rawvalue; @@ -3528,38 +3893,30 @@ void analog_field::frame_update(running_machine &machine) // if port is absolute, then just return the absolute data supplied m_accum = apply_inverse_sensitivity(rawvalue); } - else if (m_positionalscale != 0) + else { + assert(m_positionalscale); // only way to get here due to previous if + // if port is positional, we will take the full analog control and divide it // into positions, that way as the control is moved full scale, // it moves through all the positions - rawvalue = apply_scale(rawvalue - INPUT_ABSOLUTE_MIN, m_positionalscale) * INPUT_RELATIVE_PER_PIXEL + m_minimum; + rawvalue = apply_scale(rawvalue - osd::input_device::ABSOLUTE_MIN, m_positionalscale) * osd::input_device::RELATIVE_PER_PIXEL + m_minimum; // clamp the high value so it does not roll over rawvalue = std::min(rawvalue, m_maximum); m_accum = apply_inverse_sensitivity(rawvalue); } - else - // if port is relative, we use the value to simulate the speed of relative movement - // sensitivity adjustment is allowed for this mode - m_accum += rawvalue; - m_lastdigital = false; // do not bother with other control types if the analog data is changing + m_lastdigital = false; return; } - else - { - // we still have to update fake relative from joystick control - if (!m_absolute && m_positionalscale == 0) - m_accum += rawvalue; - } } // if we got it from a relative device, use that as the starting delta // also note that the last input was not a digital one s32 delta = 0; - if (itemclass == ITEM_CLASS_RELATIVE && rawvalue != 0) + if (itemclass == ITEM_CLASS_RELATIVE && rawvalue) { delta = rawvalue; m_lastdigital = false; @@ -3611,9 +3968,9 @@ void analog_field::frame_update(running_machine &machine) s32 center = apply_inverse_sensitivity(m_center); if (m_lastdigital && !keypressed) { - // autocenter from positive values if (m_accum >= center) { + // autocenter from positive values m_accum -= apply_scale(m_centerdelta, m_keyscalepos); if (m_accum < center) { @@ -3621,10 +3978,9 @@ void analog_field::frame_update(running_machine &machine) m_lastdigital = false; } } - - // autocenter from negative values else { + // autocenter from negative values m_accum += apply_scale(m_centerdelta, m_keyscaleneg); if (m_accum > center) { @@ -3650,6 +4006,13 @@ void analog_field::read(ioport_value &result) if (!m_field.enabled()) return; + // if set programmatically, only use the override value + if (m_use_adjoverride) + { + result = m_adjoverride; + return; + } + // start with the raw value s32 value = m_accum; @@ -3703,7 +4066,7 @@ ioport_type ioport_manager::token_to_input_type(const char *string, int &player) return ioport_type(ipnum); // find the token in the list - for (input_type_entry &entry : m_typelist) + for (const input_type_entry &entry : m_typelist) if (entry.token() != nullptr && !strcmp(entry.token(), string)) { player = entry.player(); @@ -3741,7 +4104,7 @@ std::string ioport_manager::input_type_to_token(ioport_type type, int player) input_seq_type ioport_manager::token_to_seq_type(const char *string) { // look up the string in the table of possible sequence types and return the index - for (int seqindex = 0; seqindex < ARRAY_LENGTH(seqtypestrings); seqindex++) + for (int seqindex = 0; seqindex < std::size(seqtypestrings); seqindex++) if (!core_stricmp(string, seqtypestrings[seqindex])) return input_seq_type(seqindex); return SEQ_TYPE_INVALID; diff --git a/src/emu/ioport.h b/src/emu/ioport.h index df7d2ad026b..8e8009ffce6 100644 --- a/src/emu/ioport.h +++ b/src/emu/ioport.h @@ -17,44 +17,42 @@ #ifndef MAME_EMU_IOPORT_H #define MAME_EMU_IOPORT_H +#include "inpttype.h" + +#include "ioprocs.h" + +#include <array> #include <cstdint> #include <cstring> -#include <time.h> +#include <ctime> +#include <functional> +#include <iosfwd> +#include <initializer_list> +#include <list> +#include <memory> +#include <string_view> +#include <vector> //************************************************************************** // CONSTANTS //************************************************************************** -// input ports support up to 32 bits each -typedef u32 ioport_value; - // active high/low values for input ports -const ioport_value IP_ACTIVE_HIGH = 0x00000000; -const ioport_value IP_ACTIVE_LOW = 0xffffffff; +constexpr ioport_value IP_ACTIVE_HIGH = 0x00000000; +constexpr ioport_value IP_ACTIVE_LOW = 0xffffffff; // maximum number of players supported -const int MAX_PLAYERS = 10; +constexpr int MAX_PLAYERS = 10; // unicode constants -const char32_t UCHAR_PRIVATE = 0x100000; -const char32_t UCHAR_SHIFT_1 = UCHAR_PRIVATE + 0; -const char32_t UCHAR_SHIFT_2 = UCHAR_PRIVATE + 1; -const char32_t UCHAR_SHIFT_BEGIN = UCHAR_SHIFT_1; -const char32_t UCHAR_SHIFT_END = UCHAR_SHIFT_2; -const char32_t UCHAR_MAMEKEY_BEGIN = UCHAR_PRIVATE + 2; - - -// sequence types for input_port_seq() call -enum input_seq_type -{ - SEQ_TYPE_INVALID = -1, - SEQ_TYPE_STANDARD = 0, - SEQ_TYPE_INCREMENT, - SEQ_TYPE_DECREMENT, - SEQ_TYPE_TOTAL -}; -DECLARE_ENUM_INCDEC_OPERATORS(input_seq_type) +constexpr char32_t UCHAR_INVALID = 0xffff; +constexpr char32_t UCHAR_PRIVATE = 0x100000; +constexpr char32_t UCHAR_SHIFT_1 = UCHAR_PRIVATE + 0; +constexpr char32_t UCHAR_SHIFT_2 = UCHAR_PRIVATE + 1; +constexpr char32_t UCHAR_SHIFT_BEGIN = UCHAR_SHIFT_1; +constexpr char32_t UCHAR_SHIFT_END = UCHAR_SHIFT_2; +constexpr char32_t UCHAR_MAMEKEY_BEGIN = UCHAR_SHIFT_END + 1; // crosshair types @@ -86,326 +84,6 @@ enum ioport_group }; -// various input port types -enum ioport_type -{ - // pseudo-port types - IPT_INVALID = 0, - IPT_UNUSED, - IPT_END, - IPT_UNKNOWN, - IPT_PORT, - IPT_DIPSWITCH, - IPT_CONFIG, - - // start buttons - IPT_START1, - IPT_START2, - IPT_START3, - IPT_START4, - IPT_START5, - IPT_START6, - IPT_START7, - IPT_START8, - IPT_START9, - IPT_START10, - - // coin slots - IPT_COIN1, - IPT_COIN2, - IPT_COIN3, - IPT_COIN4, - IPT_COIN5, - IPT_COIN6, - IPT_COIN7, - IPT_COIN8, - IPT_COIN9, - IPT_COIN10, - IPT_COIN11, - IPT_COIN12, - IPT_BILL1, - - // service coin - IPT_SERVICE1, - IPT_SERVICE2, - IPT_SERVICE3, - IPT_SERVICE4, - - // tilt inputs - IPT_TILT1, - IPT_TILT2, - IPT_TILT3, - IPT_TILT4, - - // misc other digital inputs - IPT_POWER_ON, - IPT_POWER_OFF, - IPT_SERVICE, - IPT_TILT, - IPT_INTERLOCK, - IPT_MEMORY_RESET, - IPT_VOLUME_UP, - IPT_VOLUME_DOWN, - IPT_START, // use the numbered start button(s) for coin-ops - IPT_SELECT, - IPT_KEYPAD, - IPT_KEYBOARD, - - // digital joystick inputs - IPT_DIGITAL_JOYSTICK_FIRST, - - // use IPT_JOYSTICK for panels where the player has one single joystick - IPT_JOYSTICK_UP, - IPT_JOYSTICK_DOWN, - IPT_JOYSTICK_LEFT, - IPT_JOYSTICK_RIGHT, - - // use IPT_JOYSTICKLEFT and IPT_JOYSTICKRIGHT for dual joystick panels - IPT_JOYSTICKRIGHT_UP, - IPT_JOYSTICKRIGHT_DOWN, - IPT_JOYSTICKRIGHT_LEFT, - IPT_JOYSTICKRIGHT_RIGHT, - IPT_JOYSTICKLEFT_UP, - IPT_JOYSTICKLEFT_DOWN, - IPT_JOYSTICKLEFT_LEFT, - IPT_JOYSTICKLEFT_RIGHT, - - IPT_DIGITAL_JOYSTICK_LAST, - - // action buttons - IPT_BUTTON1, - IPT_BUTTON2, - IPT_BUTTON3, - IPT_BUTTON4, - IPT_BUTTON5, - IPT_BUTTON6, - IPT_BUTTON7, - IPT_BUTTON8, - IPT_BUTTON9, - IPT_BUTTON10, - IPT_BUTTON11, - IPT_BUTTON12, - IPT_BUTTON13, - IPT_BUTTON14, - IPT_BUTTON15, - IPT_BUTTON16, - - // mahjong inputs - IPT_MAHJONG_FIRST, - - IPT_MAHJONG_A, - IPT_MAHJONG_B, - IPT_MAHJONG_C, - IPT_MAHJONG_D, - IPT_MAHJONG_E, - IPT_MAHJONG_F, - IPT_MAHJONG_G, - IPT_MAHJONG_H, - IPT_MAHJONG_I, - IPT_MAHJONG_J, - IPT_MAHJONG_K, - IPT_MAHJONG_L, - IPT_MAHJONG_M, - IPT_MAHJONG_N, - IPT_MAHJONG_O, - IPT_MAHJONG_P, - IPT_MAHJONG_Q, - IPT_MAHJONG_KAN, - IPT_MAHJONG_PON, - IPT_MAHJONG_CHI, - IPT_MAHJONG_REACH, - IPT_MAHJONG_RON, - IPT_MAHJONG_BET, - IPT_MAHJONG_LAST_CHANCE, - IPT_MAHJONG_SCORE, - IPT_MAHJONG_DOUBLE_UP, - IPT_MAHJONG_FLIP_FLOP, - IPT_MAHJONG_BIG, - IPT_MAHJONG_SMALL, - - IPT_MAHJONG_LAST, - - // hanafuda inputs - IPT_HANAFUDA_FIRST, - - IPT_HANAFUDA_A, - IPT_HANAFUDA_B, - IPT_HANAFUDA_C, - IPT_HANAFUDA_D, - IPT_HANAFUDA_E, - IPT_HANAFUDA_F, - IPT_HANAFUDA_G, - IPT_HANAFUDA_H, - IPT_HANAFUDA_YES, - IPT_HANAFUDA_NO, - - IPT_HANAFUDA_LAST, - - // gambling inputs - IPT_GAMBLING_FIRST, - - IPT_GAMBLE_KEYIN, // attendant - IPT_GAMBLE_KEYOUT, // attendant - IPT_GAMBLE_SERVICE, // attendant - IPT_GAMBLE_BOOK, // attendant - IPT_GAMBLE_DOOR, // attendant - // IPT_GAMBLE_DOOR2, // many gambling games have several doors. - // IPT_GAMBLE_DOOR3, - // IPT_GAMBLE_DOOR4, - // IPT_GAMBLE_DOOR5, - - IPT_GAMBLE_HIGH, // player - IPT_GAMBLE_LOW, // player - IPT_GAMBLE_HALF, // player - IPT_GAMBLE_DEAL, // player - IPT_GAMBLE_D_UP, // player - IPT_GAMBLE_TAKE, // player - IPT_GAMBLE_STAND, // player - IPT_GAMBLE_BET, // player - IPT_GAMBLE_PAYOUT, // player - - // poker-specific inputs - IPT_POKER_HOLD1, - IPT_POKER_HOLD2, - IPT_POKER_HOLD3, - IPT_POKER_HOLD4, - IPT_POKER_HOLD5, - IPT_POKER_CANCEL, - IPT_POKER_BET, - - // slot-specific inputs - IPT_SLOT_STOP1, - IPT_SLOT_STOP2, - IPT_SLOT_STOP3, - IPT_SLOT_STOP4, - IPT_SLOT_STOP_ALL, - - IPT_GAMBLING_LAST, - - // analog inputs - IPT_ANALOG_FIRST, - - IPT_ANALOG_ABSOLUTE_FIRST, - - IPT_AD_STICK_X, // absolute // autocenter - IPT_AD_STICK_Y, // absolute // autocenter - IPT_AD_STICK_Z, // absolute // autocenter - IPT_PADDLE, // absolute // autocenter - IPT_PADDLE_V, // absolute // autocenter - IPT_PEDAL, // absolute // autocenter - IPT_PEDAL2, // absolute // autocenter - IPT_PEDAL3, // absolute // autocenter - IPT_LIGHTGUN_X, // absolute - IPT_LIGHTGUN_Y, // absolute - IPT_POSITIONAL, // absolute // autocenter if not wraps - IPT_POSITIONAL_V, // absolute // autocenter if not wraps - - IPT_ANALOG_ABSOLUTE_LAST, - - IPT_DIAL, // relative - IPT_DIAL_V, // relative - IPT_TRACKBALL_X, // relative - IPT_TRACKBALL_Y, // relative - IPT_MOUSE_X, // relative - IPT_MOUSE_Y, // relative - - IPT_ANALOG_LAST, - - // analog adjuster support - IPT_ADJUSTER, - - // the following are special codes for user interface handling - not to be used by drivers! - IPT_UI_FIRST, - - IPT_UI_CONFIGURE, - IPT_UI_ON_SCREEN_DISPLAY, - IPT_UI_DEBUG_BREAK, - IPT_UI_PAUSE, - IPT_UI_PAUSE_SINGLE, - IPT_UI_REWIND_SINGLE, - IPT_UI_RESET_MACHINE, - IPT_UI_SOFT_RESET, - IPT_UI_SHOW_GFX, - IPT_UI_FRAMESKIP_DEC, - IPT_UI_FRAMESKIP_INC, - IPT_UI_THROTTLE, - IPT_UI_FAST_FORWARD, - IPT_UI_SHOW_FPS, - IPT_UI_SNAPSHOT, - IPT_UI_TIMECODE, - IPT_UI_RECORD_MNG, - IPT_UI_RECORD_AVI, - IPT_UI_TOGGLE_CHEAT, - IPT_UI_UP, - IPT_UI_DOWN, - IPT_UI_LEFT, - IPT_UI_RIGHT, - IPT_UI_HOME, - IPT_UI_END, - IPT_UI_PAGE_UP, - IPT_UI_PAGE_DOWN, - IPT_UI_FOCUS_NEXT, - IPT_UI_FOCUS_PREV, - IPT_UI_SELECT, - IPT_UI_CANCEL, - IPT_UI_DISPLAY_COMMENT, - IPT_UI_CLEAR, - IPT_UI_ZOOM_IN, - IPT_UI_ZOOM_OUT, - IPT_UI_PREV_GROUP, - IPT_UI_NEXT_GROUP, - IPT_UI_ROTATE, - IPT_UI_SHOW_PROFILER, - IPT_UI_TOGGLE_UI, - IPT_UI_TOGGLE_DEBUG, - IPT_UI_PASTE, - IPT_UI_SAVE_STATE, - IPT_UI_LOAD_STATE, - IPT_UI_TAPE_START, - IPT_UI_TAPE_STOP, - IPT_UI_DATS, - IPT_UI_FAVORITES, - IPT_UI_EXPORT, - IPT_UI_AUDIT_FAST, - IPT_UI_AUDIT_ALL, - IPT_UI_TOGGLE_AUTOFIRE, - - // additional OSD-specified UI port types (up to 16) - IPT_OSD_1, - IPT_OSD_2, - IPT_OSD_3, - IPT_OSD_4, - IPT_OSD_5, - IPT_OSD_6, - IPT_OSD_7, - IPT_OSD_8, - IPT_OSD_9, - IPT_OSD_10, - IPT_OSD_11, - IPT_OSD_12, - IPT_OSD_13, - IPT_OSD_14, - IPT_OSD_15, - IPT_OSD_16, - - IPT_UI_LAST, - - IPT_OTHER, // not mapped to standard defaults - - IPT_SPECIAL, // uninterpreted characters - IPT_CUSTOM, // handled by custom code - IPT_OUTPUT, - - IPT_COUNT -}; -DECLARE_ENUM_INCDEC_OPERATORS(ioport_type) -// aliases for some types -#define IPT_PADDLE_H IPT_PADDLE -#define IPT_PEDAL1 IPT_PEDAL -#define IPT_POSITIONAL_H IPT_POSITIONAL -#define IPT_DIAL_H IPT_DIAL - - // input type classes enum ioport_type_class { @@ -452,13 +130,13 @@ enum INPUT_STRING_3C_1C, // 0.333333 INPUT_STRING_8C_3C, // 0.375000 // INPUT_STRING_10C_4C, // 0.400000 -// INPUT_STRING_5C_2C, // 0.400000 // INPUT_STRING_7C_3C, // 0.428571 // INPUT_STRING_9C_4C, // 0.444444 // INPUT_STRING_10C_5C, // 0.500000 // INPUT_STRING_8C_4C, // 0.500000 // INPUT_STRING_6C_3C, // 0.500000 INPUT_STRING_4C_2C, // 0.500000 + INPUT_STRING_5C_2C, // 0.500000 INPUT_STRING_2C_1C, // 0.500000 // INPUT_STRING_9C_5C, // 0.555556 // INPUT_STRING_7C_4C, // 0.571429 @@ -496,6 +174,7 @@ enum // INPUT_STRING_6C_7C, // 1.166667 // INPUT_STRING_5C_6C, // 1.200000 // INPUT_STRING_8C_10C, // 1.250000 + INPUT_STRING_3C_5C, // 1.250000 INPUT_STRING_4C_5C, // 1.250000 // INPUT_STRING_7C_9C, // 1.285714 // INPUT_STRING_6C_8C, // 1.333333 @@ -650,7 +329,7 @@ enum //************************************************************************** // constructor function pointer -typedef void(*ioport_constructor)(device_t &owner, ioport_list &portlist, std::string &errorbuf); +typedef void(*ioport_constructor)(device_t &owner, ioport_list &portlist, std::ostream &errorbuf); // I/O port callback function delegates typedef device_delegate<ioport_value ()> ioport_field_read_delegate; @@ -658,116 +337,6 @@ typedef device_delegate<void (ioport_field &, u32, ioport_value, ioport_value)> typedef device_delegate<float (float)> ioport_field_crossmap_delegate; -// ======================> inp_header - -// header at the front of INP files -class inp_header -{ -public: - // parameters - static constexpr unsigned MAJVERSION = 3; - static constexpr unsigned MINVERSION = 0; - - bool read(emu_file &f) - { - return f.read(m_data, sizeof(m_data)) == sizeof(m_data); - } - bool write(emu_file &f) const - { - return f.write(m_data, sizeof(m_data)) == sizeof(m_data); - } - - bool check_magic() const - { - return 0 == std::memcmp(MAGIC, m_data + OFFS_MAGIC, OFFS_BASETIME - OFFS_MAGIC); - } - u64 get_basetime() const - { - return - (u64(m_data[OFFS_BASETIME + 0]) << (0 * 8)) | - (u64(m_data[OFFS_BASETIME + 1]) << (1 * 8)) | - (u64(m_data[OFFS_BASETIME + 2]) << (2 * 8)) | - (u64(m_data[OFFS_BASETIME + 3]) << (3 * 8)) | - (u64(m_data[OFFS_BASETIME + 4]) << (4 * 8)) | - (u64(m_data[OFFS_BASETIME + 5]) << (5 * 8)) | - (u64(m_data[OFFS_BASETIME + 6]) << (6 * 8)) | - (u64(m_data[OFFS_BASETIME + 7]) << (7 * 8)); - } - unsigned get_majversion() const - { - return m_data[OFFS_MAJVERSION]; - } - unsigned get_minversion() const - { - return m_data[OFFS_MINVERSION]; - } - std::string get_sysname() const - { - return get_string<OFFS_SYSNAME, OFFS_APPDESC>(); - } - std::string get_appdesc() const - { - return get_string<OFFS_APPDESC, OFFS_END>(); - } - - void set_magic() - { - std::memcpy(m_data + OFFS_MAGIC, MAGIC, OFFS_BASETIME - OFFS_MAGIC); - } - void set_basetime(u64 time) - { - m_data[OFFS_BASETIME + 0] = u8((time >> (0 * 8)) & 0x00ff); - m_data[OFFS_BASETIME + 1] = u8((time >> (1 * 8)) & 0x00ff); - m_data[OFFS_BASETIME + 2] = u8((time >> (2 * 8)) & 0x00ff); - m_data[OFFS_BASETIME + 3] = u8((time >> (3 * 8)) & 0x00ff); - m_data[OFFS_BASETIME + 4] = u8((time >> (4 * 8)) & 0x00ff); - m_data[OFFS_BASETIME + 5] = u8((time >> (5 * 8)) & 0x00ff); - m_data[OFFS_BASETIME + 6] = u8((time >> (6 * 8)) & 0x00ff); - m_data[OFFS_BASETIME + 7] = u8((time >> (7 * 8)) & 0x00ff); - } - void set_version() - { - m_data[OFFS_MAJVERSION] = MAJVERSION; - m_data[OFFS_MINVERSION] = MINVERSION; - } - void set_sysname(std::string const &name) - { - set_string<OFFS_SYSNAME, OFFS_APPDESC>(name); - } - void set_appdesc(std::string const &desc) - { - set_string<OFFS_APPDESC, OFFS_END>(desc); - } - -private: - template <std::size_t BEGIN, std::size_t END> void set_string(std::string const &str) - { - std::size_t const used = (std::min<std::size_t>)(str.size() + 1, END - BEGIN); - std::memcpy(m_data + BEGIN, str.c_str(), used); - if ((END - BEGIN) > used) - std::memset(m_data + BEGIN + used, 0, (END - BEGIN) - used); - } - template <std::size_t BEGIN, std::size_t END> std::string get_string() const - { - char const *const begin = reinterpret_cast<char const *>(m_data + BEGIN); - return std::string(begin, std::find(begin, reinterpret_cast<char const *>(m_data + END), '\0')); - } - - static constexpr std::size_t OFFS_MAGIC = 0x00; // 0x08 bytes - static constexpr std::size_t OFFS_BASETIME = 0x08; // 0x08 bytes (little-endian binary integer) - static constexpr std::size_t OFFS_MAJVERSION = 0x10; // 0x01 bytes (binary integer) - static constexpr std::size_t OFFS_MINVERSION = 0x11; // 0x01 bytes (binary integer) - // 0x02 bytes reserved - static constexpr std::size_t OFFS_SYSNAME = 0x14; // 0x0c bytes (ASCII) - static constexpr std::size_t OFFS_APPDESC = 0x20; // 0x20 bytes (ASCII) - static constexpr std::size_t OFFS_END = 0x40; - - static u8 const MAGIC[OFFS_BASETIME - OFFS_MAGIC]; - - u8 m_data[OFFS_END]; -}; - - // ======================> input_device_default // device defined default input settings @@ -784,38 +353,39 @@ struct input_device_default // describes a fundamental input type, including default input sequences class input_type_entry { - friend class simple_list<input_type_entry>; - friend class ioport_manager; - public: // construction/destruction - input_type_entry(ioport_type type, ioport_group group, int player, const char *token, const char *name, input_seq standard); - input_type_entry(ioport_type type, ioport_group group, int player, const char *token, const char *name, input_seq standard, input_seq decrement, input_seq increment); + input_type_entry(ioport_type type, ioport_group group, int player, const char *token, const char *name, input_seq standard) noexcept; + input_type_entry(ioport_type type, ioport_group group, int player, const char *token, const char *name, input_seq standard, input_seq decrement, input_seq increment) noexcept; // getters - input_type_entry *next() const { return m_next; } - ioport_type type() const { return m_type; } - ioport_group group() const { return m_group; } - u8 player() const { return m_player; } - const char *token() const { return m_token; } - const char *name() const { return m_name; } - input_seq &defseq(input_seq_type seqtype = SEQ_TYPE_STANDARD) { return m_defseq[seqtype]; } - const input_seq &seq(input_seq_type seqtype = SEQ_TYPE_STANDARD) const { return m_seq[seqtype]; } - void restore_default_seq(); + ioport_type type() const noexcept { return m_type; } + ioport_group group() const noexcept { return m_group; } + u8 player() const noexcept { return m_player; } + const char *token() const noexcept { return m_token; } + std::string name() const; + input_seq &defseq(input_seq_type seqtype = SEQ_TYPE_STANDARD) noexcept { return m_defseq[seqtype]; } + const input_seq &defseq(input_seq_type seqtype = SEQ_TYPE_STANDARD) const noexcept { return m_defseq[seqtype]; } + const input_seq &seq(input_seq_type seqtype = SEQ_TYPE_STANDARD) const noexcept { return m_seq[seqtype]; } + const std::string &cfg(input_seq_type seqtype = SEQ_TYPE_STANDARD) const noexcept { return m_cfg[seqtype]; } // setters - void configure_osd(const char *token, const char *name); + void restore_default_seq() noexcept; + void set_seq(input_seq_type seqtype, const input_seq &seq) noexcept { m_seq[seqtype] = seq; } + template <typename... T> void set_cfg(input_seq_type seqtype, T &&... cfg) { m_cfg[seqtype].assign(std::forward<T>(cfg)...); } + void replace_code(input_code oldcode, input_code newcode) noexcept; + void configure_osd(const char *token, const char *name) noexcept; private: // internal state - input_type_entry * m_next; // next description in the list - ioport_type m_type; // IPT_* for this entry - ioport_group m_group; // which group the port belongs to - u8 m_player; // player number (0 is player 1) - const char * m_token; // token used to store settings - const char * m_name; // user-friendly name - input_seq m_defseq[SEQ_TYPE_TOTAL];// default input sequence - input_seq m_seq[SEQ_TYPE_TOTAL];// currently configured sequences + ioport_type m_type; // IPT_* for this entry + ioport_group m_group; // which group the port belongs to + u8 m_player; // player number (0 is player 1) + const char * m_token; // token used to store settings + const char * m_name; // user-friendly name + std::array<input_seq, SEQ_TYPE_TOTAL> m_defseq; // default input sequence + std::array<input_seq, SEQ_TYPE_TOTAL> m_seq; // currently configured sequences + std::array<std::string, SEQ_TYPE_TOTAL> m_cfg; // configuration strings }; @@ -825,7 +395,6 @@ private: class digital_joystick { DISABLE_COPYING(digital_joystick); - friend class simple_list<digital_joystick>; public: // directions @@ -848,11 +417,10 @@ public: digital_joystick(int player, int number); // getters - digital_joystick *next() const { return m_next; } - int player() const { return m_player; } - int number() const { return m_number; } - u8 current() const { return m_current; } - u8 current4way() const { return m_current4way; } + int player() const noexcept { return m_player; } + int number() const noexcept { return m_number; } + u8 current() const noexcept { return m_current; } + u8 current4way() const noexcept { return m_current4way; } // configuration direction_t add_axis(ioport_field &field); @@ -862,10 +430,9 @@ public: private: // internal state - digital_joystick * m_next; // next joystick in the list int m_player; // player number represented int m_number; // joystick number represented - simple_list<simple_list_wrapper<ioport_field> > m_field[JOYDIR_COUNT]; // potential input fields for each direction + std::forward_list<std::reference_wrapper<ioport_field> > m_field[JOYDIR_COUNT]; // potential input fields for each direction u8 m_current; // current value u8 m_current4way; // current 4-way value u8 m_previous; // previous value @@ -907,11 +474,12 @@ public: bool none() const { return (m_condition == ALWAYS); } // configuration - void reset() { set(ALWAYS, nullptr, 0, 0); } + void reset() { set(ALWAYS, "", 0, 0); } void set(condition_t condition, const char *tag, ioport_value mask, ioport_value value) { m_condition = condition; m_tag = tag; + m_port = nullptr; m_mask = mask; m_value = value; } @@ -921,7 +489,7 @@ public: private: // internal state condition_t m_condition; // condition to use - const char * m_tag; // tag of port whose condition is to be tested + const char * m_tag; // tag of port whose condition is to be tested (must never be nullptr) ioport_port * m_port; // reference to the port to be tested ioport_value m_mask; // mask to apply to the port ioport_value m_value; // value to compare against @@ -933,15 +501,11 @@ private: // a single setting for a configuration or DIP switch class ioport_setting { - DISABLE_COPYING(ioport_setting); - friend class simple_list<ioport_setting>; - public: // construction/destruction ioport_setting(ioport_field &field, ioport_value value, const char *name); // getters - ioport_setting *next() const { return m_next; } ioport_field &field() const { return m_field; } device_t &device() const; running_machine &machine() const; @@ -951,15 +515,14 @@ public: const char *name() const { return m_name; } // helpers - bool enabled() { return m_condition.eval(); } + bool enabled() const { return m_condition.eval(); } private: // internal state - ioport_setting * m_next; // pointer to next setting in sequence - ioport_field & m_field; // pointer back to the field that owns us - ioport_value m_value; // value of the bits in this setting - const char * m_name; // user-friendly name to display - ioport_condition m_condition; // condition under which this setting is valid + ioport_field & m_field; // pointer back to the field that owns us + ioport_value m_value; // value of the bits in this setting + const char * m_name; // user-friendly name to display + ioport_condition m_condition; // condition under which this setting is valid }; @@ -968,24 +531,19 @@ private: // a mapping from a bit to a physical DIP switch description class ioport_diplocation { - DISABLE_COPYING(ioport_diplocation); - friend class simple_list<ioport_diplocation>; - public: // construction/destruction - ioport_diplocation(const char *name, u8 swnum, bool invert); + ioport_diplocation(std::string_view name, u8 swnum, bool invert); // getters - ioport_diplocation *next() const { return m_next; } const char *name() const { return m_name.c_str(); } u8 number() const { return m_number; } bool inverted() const { return m_invert; } private: - ioport_diplocation * m_next; // pointer to the next bit - std::string m_name; // name of the physical DIP switch - u8 m_number; // physical switch number - bool m_invert; // is this an active-high DIP? + std::string m_name; // name of the physical DIP switch + u8 m_number; // physical switch number + bool m_invert; // is this an active-high DIP? }; @@ -996,18 +554,19 @@ class ioport_field { DISABLE_COPYING(ioport_field); friend class simple_list<ioport_field>; + friend class ioport_manager; friend class ioport_configurer; friend class dynamic_field; // flags for ioport_fields - static const int FIELD_FLAG_OPTIONAL = 0x0001; // set if this field is not required but recognized by hw - static const int FIELD_FLAG_COCKTAIL = 0x0002; // set if this field is relevant only for cocktail cabinets - static const int FIELD_FLAG_TOGGLE = 0x0004; // set if this field should behave as a toggle - static const int FIELD_FLAG_ROTATED = 0x0008; // set if this field represents a rotated control - static const int ANALOG_FLAG_REVERSE = 0x0010; // analog only: reverse the sense of the axis - static const int ANALOG_FLAG_RESET = 0x0020; // analog only: always preload in->default for relative axes, returning only deltas - static const int ANALOG_FLAG_WRAPS = 0x0040; // analog only: positional count wraps around - static const int ANALOG_FLAG_INVERT = 0x0080; // analog only: bitwise invert bits + static inline constexpr u32 FIELD_FLAG_OPTIONAL = 0x0001; // set if this field is not required but recognized by hw + static inline constexpr u32 FIELD_FLAG_COCKTAIL = 0x0002; // set if this field is relevant only for cocktail cabinets + static inline constexpr u32 FIELD_FLAG_TOGGLE = 0x0004; // set if this field should behave as a toggle + static inline constexpr u32 FIELD_FLAG_ROTATED = 0x0008; // set if this field represents a rotated control + static inline constexpr u32 ANALOG_FLAG_REVERSE = 0x0010; // analog only: reverse the sense of the axis + static inline constexpr u32 ANALOG_FLAG_RESET = 0x0020; // analog only: always preload in->default for relative axes, returning only deltas + static inline constexpr u32 ANALOG_FLAG_WRAPS = 0x0040; // analog only: positional count wraps around + static inline constexpr u32 ANALOG_FLAG_INVERT = 0x0080; // analog only: bitwise invert bits public: // construction/destruction @@ -1021,8 +580,8 @@ public: ioport_manager &manager() const; running_machine &machine() const; int modcount() const { return m_modcount; } - const simple_list<ioport_setting> &settings() const { return m_settinglist; } - const simple_list<ioport_diplocation> &diplocations() const { return m_diploclist; } + const std::vector<ioport_setting> &settings() const { return m_settinglist; } + const std::vector<ioport_diplocation> &diplocations() const { return m_diploclist; } ioport_value mask() const { return m_mask; } ioport_value defvalue() const { return m_defvalue; } @@ -1032,6 +591,7 @@ public: u8 player() const { return m_player; } bool digital_value() const { return m_digital_value; } void set_value(ioport_value value); + void clear_value(); bool optional() const { return ((m_flags & FIELD_FLAG_OPTIONAL) != 0); } bool cocktail() const { return ((m_flags & FIELD_FLAG_COCKTAIL) != 0); } @@ -1042,42 +602,43 @@ public: bool analog_wraps() const { return ((m_flags & ANALOG_FLAG_WRAPS) != 0); } bool analog_invert() const { return ((m_flags & ANALOG_FLAG_INVERT) != 0); } - u8 impulse() const { return m_impulse; } - const char *name() const; - const char *specific_name() const { return m_name; } - const input_seq &seq(input_seq_type seqtype = SEQ_TYPE_STANDARD) const; - const input_seq &defseq(input_seq_type seqtype = SEQ_TYPE_STANDARD) const; - const input_seq &defseq_unresolved(input_seq_type seqtype = SEQ_TYPE_STANDARD) const { return m_seq[seqtype]; } - void set_defseq(const input_seq &newseq) { set_defseq(SEQ_TYPE_STANDARD, newseq); } + u8 impulse() const noexcept { return m_impulse; } + std::string name() const; + const char *specific_name() const noexcept { return m_name; } + const input_seq &seq(input_seq_type seqtype = SEQ_TYPE_STANDARD) const noexcept; + const input_seq &defseq(input_seq_type seqtype = SEQ_TYPE_STANDARD) const noexcept; + const input_seq &defseq_unresolved(input_seq_type seqtype = SEQ_TYPE_STANDARD) const noexcept { return m_seq[seqtype]; } + void set_defseq(const input_seq &newseq) noexcept { set_defseq(SEQ_TYPE_STANDARD, newseq); } void set_defseq(input_seq_type seqtype, const input_seq &newseq); bool has_dynamic_read() const { return !m_read.isnull(); } bool has_dynamic_write() const { return !m_write.isnull(); } - ioport_value minval() const { return m_min; } - ioport_value maxval() const { return m_max; } - s32 sensitivity() const { return m_sensitivity; } - s32 delta() const { return m_delta; } - s32 centerdelta() const { return m_centerdelta; } - crosshair_axis_t crosshair_axis() const { return m_crosshair_axis; } - double crosshair_scale() const { return m_crosshair_scale; } - double crosshair_offset() const { return m_crosshair_offset; } - u16 full_turn_count() const { return m_full_turn_count; } - const ioport_value *remap_table() const { return m_remap_table; } - - u8 way() const { return m_way; } + ioport_value minval() const noexcept { return m_min; } + ioport_value maxval() const noexcept { return m_max; } + s32 sensitivity() const noexcept { return m_sensitivity; } + s32 delta() const noexcept { return m_delta; } + s32 centerdelta() const noexcept { return m_centerdelta; } + crosshair_axis_t crosshair_axis() const noexcept { return m_crosshair_axis; } + float crosshair_scale() const noexcept { return m_crosshair_scale; } + float crosshair_offset() const noexcept { return m_crosshair_offset; } + float crosshair_altaxis() const noexcept { return m_crosshair_altaxis; } + u16 full_turn_count() const noexcept { return m_full_turn_count; } + const ioport_value *remap_table() const noexcept { return m_remap_table; } + + u8 way() const noexcept { return m_way; } std::vector<char32_t> keyboard_codes(int which) const; std::string key_name(int which) const; ioport_field_live &live() const { assert(m_live != nullptr); return *m_live; } // setters - void set_crosshair_scale(double scale) { m_crosshair_scale = scale; } - void set_crosshair_offset(double offset) { m_crosshair_offset = offset; } + void set_crosshair_scale(float scale) { m_crosshair_scale = scale; } + void set_crosshair_offset(float offset) { m_crosshair_offset = offset; } void set_player(u8 player) { m_player = player; } // derived getters - ioport_type_class type_class() const; - bool is_analog() const { return (m_type > IPT_ANALOG_FIRST && m_type < IPT_ANALOG_LAST); } - bool is_digital_joystick() const { return (m_type > IPT_DIGITAL_JOYSTICK_FIRST && m_type < IPT_DIGITAL_JOYSTICK_LAST); } + ioport_type_class type_class() const noexcept; + bool is_analog() const noexcept { return (m_type > IPT_ANALOG_FIRST && m_type < IPT_ANALOG_LAST); } + bool is_digital_joystick() const noexcept { return (m_type > IPT_DIGITAL_JOYSTICK_FIRST && m_type < IPT_DIGITAL_JOYSTICK_LAST); } // additional operations bool enabled() const { return m_condition.eval(); } @@ -1086,7 +647,7 @@ public: void select_previous_setting(); bool has_next_setting() const; void select_next_setting(); - void crosshair_position(float &x, float &y, bool &gotx, bool &goty); + float crosshair_read() const; void init_live_state(analog_field *analog); void frame_update(ioport_value &result); void reduce_mask(ioport_value bits_to_remove) { m_mask &= ~bits_to_remove; } @@ -1094,28 +655,28 @@ public: // user-controllable settings for a field struct user_settings { - ioport_value value; // for DIP switches - bool autofire; // for autofire settings + ioport_value value = 0; // for DIP switches input_seq seq[SEQ_TYPE_TOTAL]; // sequences of all types - s32 sensitivity; // for analog controls - s32 delta; // for analog controls - s32 centerdelta; // for analog controls - bool reverse; // for analog controls - bool toggle; // for non-analog controls + std::string cfg[SEQ_TYPE_TOTAL]; // configuration strings of all types + s32 sensitivity = 0; // for analog controls + s32 delta = 0; // for analog controls + s32 centerdelta = 0; // for analog controls + bool reverse = false; // for analog controls + bool toggle = false; // for non-analog controls }; - void get_user_settings(user_settings &settings); + void get_user_settings(user_settings &settings) const; void set_user_settings(const user_settings &settings); private: - void expand_diplocation(const char *location, std::string &errorbuf); + void expand_diplocation(const char *location, std::ostream &errorbuf); // internal state ioport_field * m_next; // pointer to next field in sequence ioport_port & m_port; // reference to the port that owns us - std::unique_ptr<ioport_field_live> m_live; // live state of field (nullptr if not live) + std::unique_ptr<ioport_field_live> m_live; // live state of field (nullptr if not live) int m_modcount; // modification count - simple_list<ioport_setting> m_settinglist; // list of input_setting_configs - simple_list<ioport_diplocation> m_diploclist; // list of locations for various bits + std::vector<ioport_setting> m_settinglist; // list of input_setting_configs + std::vector<ioport_diplocation> m_diploclist; // list of locations for various bits // generally-applicable data ioport_value m_mask; // mask of bits belonging to the field @@ -1141,9 +702,9 @@ private: s32 m_delta; // delta to apply each frame a digital inc/dec key is pressed s32 m_centerdelta; // delta to apply each frame no digital inputs are pressed crosshair_axis_t m_crosshair_axis; // crosshair axis - double m_crosshair_scale; // crosshair scale - double m_crosshair_offset; // crosshair offset - double m_crosshair_altaxis;// crosshair alternate axis value + float m_crosshair_scale; // crosshair scale + float m_crosshair_offset; // crosshair offset + float m_crosshair_altaxis;// crosshair alternate axis value ioport_field_crossmap_delegate m_crosshair_mapper; // crosshair mapping function u16 m_full_turn_count; // number of optical counts for 1 full turn of the original control const ioport_value * m_remap_table; // pointer to an array that remaps the port value @@ -1171,10 +732,9 @@ struct ioport_field_live bool last; // were we pressed last time? bool toggle; // current toggle setting digital_joystick::direction_t joydir; // digital joystick direction index - bool autofire; // autofire - int autopressed; // autofire status bool lockout; // user lockout std::string name; // overridden name + std::string cfg[SEQ_TYPE_TOTAL];// configuration strings }; @@ -1188,7 +748,7 @@ class ioport_list : public std::map<std::string, std::unique_ptr<ioport_port>> public: ioport_list() { } - void append(device_t &device, std::string &errorbuf); + void append(device_t &device, std::ostream &errorbuf); }; @@ -1223,13 +783,13 @@ public: // other operations ioport_field *field(ioport_value mask) const; - void collapse_fields(std::string &errorbuf); + void collapse_fields(std::ostream &errorbuf); void frame_update(); void init_live_state(); void update_defvalue(bool flush_defaults); private: - void insert_field(ioport_field &newfield, ioport_value &disallowedbits, std::string &errorbuf); + void insert_field(ioport_field &newfield, ioport_value &disallowedbits, std::ostream &errorbuf); // internal state ioport_port * m_next; // pointer to next port @@ -1248,7 +808,6 @@ private: // live analog field information class analog_field { - friend class simple_list<analog_field>; friend class ioport_manager; friend void ioport_field::set_user_settings(const ioport_field::user_settings &settings); @@ -1257,19 +816,22 @@ public: analog_field(ioport_field &field); // getters - analog_field *next() const { return m_next; } - ioport_manager &manager() const { return m_field.manager(); } - ioport_field &field() const { return m_field; } - s32 sensitivity() const { return m_sensitivity; } - bool reverse() const { return m_reverse; } - s32 delta() const { return m_delta; } - s32 centerdelta() const { return m_centerdelta; } + ioport_manager &manager() const noexcept { return m_field.manager(); } + ioport_field &field() const noexcept { return m_field; } + s32 sensitivity() const noexcept { return m_sensitivity; } + bool reverse() const noexcept { return m_reverse; } + s32 delta() const noexcept { return m_delta; } + s32 centerdelta() const noexcept { return m_centerdelta; } // readers void read(ioport_value &value); float crosshair_read(); void frame_update(running_machine &machine); + // programmatic override (for script bindings) + void set_value(s32 value); + void clear_value(); + private: // helpers s32 apply_min_max(s32 value) const; @@ -1278,14 +840,14 @@ private: s32 apply_inverse_sensitivity(s32 value) const; // internal state - analog_field * m_next; // link to the next analog state for this port ioport_field & m_field; // pointer to the input field referenced // adjusted values (right-justified and tweaked) - u8 m_shift; // shift to align final value in the port - s32 m_adjdefvalue; // adjusted default value from the config + u8 const m_shift; // shift to align final value in the port + s32 const m_adjdefvalue; // adjusted default value from the config s32 m_adjmin; // adjusted minimum value from the config s32 m_adjmax; // adjusted maximum value from the config + s32 m_adjoverride; // programmatically set adjusted value // live values of configurable parameters s32 m_sensitivity; // current live sensitivity (100=normal) @@ -1318,6 +880,7 @@ private: bool m_single_scale; // scale joystick differently if default is between min/max bool m_interpolate; // should we do linear interpolation for mid-frame reads? bool m_lastdigital; // was the last modification caused by a digital form? + bool m_use_adjoverride; // override what will be read from the field }; @@ -1326,14 +889,11 @@ private: // live device field information class dynamic_field { - friend class simple_list<dynamic_field>; - public: // construction/destruction dynamic_field(ioport_field &field); // getters - dynamic_field *next() const { return m_next; } ioport_field &field() const { return m_field; } // read/write @@ -1342,7 +902,6 @@ public: private: // internal state - dynamic_field * m_next; // linked list of info for this port ioport_field & m_field; // reference to the input field u8 m_shift; // shift to apply to the final result ioport_value m_oldval; // last value @@ -1358,9 +917,9 @@ struct ioport_port_live ioport_port_live(ioport_port &port); // public state - simple_list<analog_field> analoglist; // list of analog port info - simple_list<dynamic_field> readlist; // list of dynamic read fields - simple_list<dynamic_field> writelist; // list of dynamic write fields + std::list<analog_field> analoglist; // list of analog port info + std::vector<dynamic_field> readlist; // list of dynamic read fields + std::vector<dynamic_field> writelist; // list of dynamic write fields ioport_value defvalue; // combined default value across the port ioport_value digital; // current value from all digital inputs ioport_value outputvalue; // current value for outputs @@ -1383,104 +942,91 @@ public: ~ioport_manager(); // getters - running_machine &machine() const { return m_machine; } - const ioport_list &ports() const { return m_portlist; } - bool safe_to_read() const { return m_safe_to_read; } - natural_keyboard &natkeyboard() { assert(m_natkeyboard != nullptr); return *m_natkeyboard; } + running_machine &machine() const noexcept { return m_machine; } + const ioport_list &ports() const noexcept { return m_portlist; } + bool safe_to_read() const noexcept { return m_safe_to_read; } // type helpers - const simple_list<input_type_entry> &types() const { return m_typelist; } + const std::vector<input_type_entry> &types() const noexcept { return m_typelist; } bool type_pressed(ioport_type type, int player = 0); - const char *type_name(ioport_type type, u8 player); - ioport_group type_group(ioport_type type, int player); - const input_seq &type_seq(ioport_type type, int player = 0, input_seq_type seqtype = SEQ_TYPE_STANDARD); - void set_type_seq(ioport_type type, int player, input_seq_type seqtype, const input_seq &newseq); - static bool type_is_analog(ioport_type type) { return (type > IPT_ANALOG_FIRST && type < IPT_ANALOG_LAST); } - bool type_class_present(ioport_type_class inputclass); + std::string type_name(ioport_type type, u8 player) const; + ioport_group type_group(ioport_type type, int player) const noexcept; + const input_seq &type_seq(ioport_type type, int player = 0, input_seq_type seqtype = SEQ_TYPE_STANDARD) const noexcept; + void set_type_seq(ioport_type type, int player, input_seq_type seqtype, const input_seq &newseq) noexcept; + static constexpr bool type_is_analog(ioport_type type) noexcept { return (type > IPT_ANALOG_FIRST && type < IPT_ANALOG_LAST); } + bool type_class_present(ioport_type_class inputclass) const noexcept; // other helpers digital_joystick &digjoystick(int player, int joysticknum); - int count_players() const; - bool crosshair_position(int player, float &x, float &y); + int count_players() const noexcept; s32 frame_interpolate(s32 oldval, s32 newval); ioport_type token_to_input_type(const char *string, int &player) const; std::string input_type_to_token(ioport_type type, int player); - // autofire - bool get_autofire_toggle() { return m_autofire_toggle; } - void set_autofire_toggle(bool toggle) { m_autofire_toggle = toggle; } - int get_autofire_delay() { return m_autofire_delay; } - void set_autofire_delay(int delay) { m_autofire_delay = delay; } - private: // internal helpers void init_port_types(); - void init_autoselect_devices(int type1, int type2, int type3, const char *option, const char *ananame); + void init_autoselect_devices(std::initializer_list<ioport_type> types, std::string_view option, std::string_view ananame); void frame_update_callback(); void frame_update(); - ioport_port *port(const char *tag) const { if (tag) { auto search = m_portlist.find(tag); if (search != m_portlist.end()) return search->second.get(); else return nullptr; } else return nullptr; } + ioport_port *port(const std::string &tag) const { auto search = m_portlist.find(tag); if (search != m_portlist.end()) return search->second.get(); else return nullptr; } void exit(); input_seq_type token_to_seq_type(const char *string); static const char *const seqtypestrings[]; - void load_config(config_type cfg_type, util::xml::data_node const *parentnode); - void load_remap_table(util::xml::data_node const *parentnode); - bool load_default_config(util::xml::data_node const *portnode, int type, int player, const input_seq *newseq); - bool load_game_config(util::xml::data_node const *portnode, int type, int player, const input_seq *newseq); + void load_config(config_type cfg_type, config_level cfg_level, util::xml::data_node const *parentnode); + void load_remap_table(util::xml::data_node const &parentnode); + bool load_default_config(int type, int player, const std::pair<input_seq, char const *> (&newseq)[SEQ_TYPE_TOTAL]); + bool load_controller_config(util::xml::data_node const &portnode, int type, int player, const std::pair<input_seq, char const *> (&newseq)[SEQ_TYPE_TOTAL]); + void load_system_config(util::xml::data_node const &portnode, int type, int player, const std::pair<input_seq, char const *> (&newseq)[SEQ_TYPE_TOTAL]); + void apply_device_defaults(); void save_config(config_type cfg_type, util::xml::data_node *parentnode); - void save_sequence(util::xml::data_node &parentnode, input_seq_type type, ioport_type porttype, const input_seq &seq); bool save_this_input_field_type(ioport_type type); void save_default_inputs(util::xml::data_node &parentnode); void save_game_inputs(util::xml::data_node &parentnode); - template<typename _Type> _Type playback_read(_Type &result); + template<typename Type> Type playback_read(Type &result); time_t playback_init(); void playback_end(const char *message = nullptr); void playback_frame(const attotime &curtime); void playback_port(ioport_port &port); - template<typename _Type> void record_write(_Type value); + template<typename Type> void record_write(Type value); void record_init(); void record_end(const char *message = nullptr); void record_frame(const attotime &curtime); void record_port(ioport_port &port); - template<typename _Type> void timecode_write(_Type value); - void timecode_init(); - void timecode_end(const char *message = nullptr); - // internal state running_machine & m_machine; // reference to owning machine bool m_safe_to_read; // clear at start; set after state is loaded ioport_list m_portlist; // list of input port configurations // types - simple_list<input_type_entry> m_typelist; // list of live type states + std::vector<input_type_entry> m_typelist; // list of live type states input_type_entry * m_type_to_entry[IPT_COUNT][MAX_PLAYERS]; // map from type/player to type state // specific special global input states - simple_list<digital_joystick> m_joystick_list; // list of digital joysticks - std::unique_ptr<natural_keyboard> m_natkeyboard; // natural keyboard support + std::list<digital_joystick> m_joystick_list; // list of digital joysticks // frame time tracking attotime m_last_frame_time; // time of the last frame callback attoseconds_t m_last_delta_nsec; // nanoseconds that passed since the previous callback // playback/record information - emu_file m_record_file; // recording file (nullptr if not recording) - emu_file m_playback_file; // playback file (nullptr if not recording) + std::unique_ptr<emu_file> m_record_file; // recording file (nullptr if not recording) + std::unique_ptr<emu_file> m_playback_file; // playback file (nullptr if not recording) + util::write_stream::ptr m_record_stream; // recording stream (nullptr if not recording) + util::read_stream::ptr m_playback_stream; // playback stream (nullptr if not recording) u64 m_playback_accumulated_speed; // accumulated speed during playback u32 m_playback_accumulated_frames; // accumulated frames during playback - emu_file m_timecode_file; // timecode/frames playback file (nullptr if not recording) - int m_timecode_count; - attotime m_timecode_last_time; - // autofire - bool m_autofire_toggle; // autofire toggle - int m_autofire_delay; // autofire delay + // storage for inactive configuration + std::unique_ptr<util::xml::file> m_deselected_card_config; + bool m_applied_device_defaults; }; @@ -1491,7 +1037,7 @@ class ioport_configurer { public: // construction/destruction - ioport_configurer(device_t &owner, ioport_list &portlist, std::string &errorbuf); + ioport_configurer(device_t &owner, ioport_list &portlist, std::ostream &errorbuf); // static helpers static const char *string_from_token(const char *string); @@ -1512,6 +1058,7 @@ public: ioport_configurer& field_set_toggle() { m_curfield->m_flags |= ioport_field::FIELD_FLAG_TOGGLE; return *this; } ioport_configurer& field_set_impulse(u8 impulse) { m_curfield->m_impulse = impulse; return *this; } ioport_configurer& field_set_analog_reverse() { m_curfield->m_flags |= ioport_field::ANALOG_FLAG_REVERSE; return *this; } + [[deprecated("PORT_RESET is deprecated; manage counter state explicitly")]] ioport_configurer& field_set_analog_reset() { m_curfield->m_flags |= ioport_field::ANALOG_FLAG_RESET; return *this; } ioport_configurer& field_set_optional() { m_curfield->m_flags |= ioport_field::FIELD_FLAG_OPTIONAL; return *this; } ioport_configurer& field_set_min_max(ioport_value minval, ioport_value maxval) { m_curfield->m_min = minval; m_curfield->m_max = maxval; return *this; } @@ -1527,6 +1074,7 @@ public: ioport_configurer& field_set_dynamic_read(ioport_field_read_delegate delegate) { m_curfield->m_read = delegate; return *this; } ioport_configurer& field_set_dynamic_write(ioport_field_write_delegate delegate, u32 param = 0) { m_curfield->m_write = delegate; m_curfield->m_write_param = param; return *this; } ioport_configurer& field_set_diplocation(const char *location) { m_curfield->expand_diplocation(location, m_errorbuf); return *this; } + ioport_configurer& field_set_gm_note(u8 note); // setting helpers ioport_configurer& setting_alloc(ioport_value value, const char *name); @@ -1539,11 +1087,12 @@ private: // internal state device_t & m_owner; ioport_list & m_portlist; - std::string & m_errorbuf; + std::ostream & m_errorbuf; ioport_port * m_curport; ioport_field * m_curfield; ioport_setting * m_cursetting; + int m_curshift; }; @@ -1554,10 +1103,6 @@ private: #define UCHAR_MAMEKEY(code) (UCHAR_MAMEKEY_BEGIN + ITEM_ID_##code) -// macro for a read callback function (PORT_CUSTOM) -#define CUSTOM_INPUT_MEMBER(name) ioport_value name() -#define DECLARE_CUSTOM_INPUT_MEMBER(name) ioport_value name() - // macro for port write callback functions (PORT_CHANGED) #define INPUT_CHANGED_MEMBER(name) void name(ioport_field &field, u32 param, ioport_value oldval, ioport_value newval) #define DECLARE_INPUT_CHANGED_MEMBER(name) void name(ioport_field &field, u32 param, ioport_value oldval, ioport_value newval) @@ -1583,7 +1128,7 @@ private: // start of table #define INPUT_PORTS_START(_name) \ -ATTR_COLD void INPUT_PORTS_NAME(_name)(device_t &owner, ioport_list &portlist, std::string &errorbuf) \ +ATTR_COLD void INPUT_PORTS_NAME(_name)(device_t &owner, ioport_list &portlist, std::ostream &errorbuf) \ { \ ioport_configurer configurer(owner, portlist, errorbuf); // end of table @@ -1592,7 +1137,7 @@ ATTR_COLD void INPUT_PORTS_NAME(_name)(device_t &owner, ioport_list &portlist, s // aliasing #define INPUT_PORTS_EXTERN(_name) \ - extern void INPUT_PORTS_NAME(_name)(device_t &owner, ioport_list &portlist, std::string &errorbuf) + extern void INPUT_PORTS_NAME(_name)(device_t &owner, ioport_list &portlist, std::ostream &errorbuf) // including #define PORT_INCLUDE(_name) \ @@ -1662,6 +1207,9 @@ ATTR_COLD void INPUT_PORTS_NAME(_name)(device_t &owner, ioport_list &portlist, s #define PORT_OPTIONAL \ configurer.field_set_optional(); +#define PORT_GM_NOTE(_id) \ + configurer.field_set_gm_note(_id); + // analog settings // if this macro is not used, the minimum defaults to 0 and maximum defaults to the mask value #define PORT_MINMAX(_min, _max) \ @@ -1679,11 +1227,10 @@ ATTR_COLD void INPUT_PORTS_NAME(_name)(device_t &owner, ioport_list &portlist, s #define PORT_CROSSHAIR(axis, scale, offset, altaxis) \ configurer.field_set_crosshair(CROSSHAIR_AXIS_##axis, altaxis, scale, offset); -#define PORT_CROSSHAIR_MAPPER(_callback) \ - configurer.field_set_crossmapper(ioport_field_crossmap_delegate(_callback, #_callback, DEVICE_SELF, (device_t *)nullptr)); - -#define PORT_CROSSHAIR_MAPPER_MEMBER(_device, _class, _member) \ - configurer.field_set_crossmapper(ioport_field_crossmap_delegate(&_class::_member, #_class "::" #_member, _device, (_class *)nullptr)); +#define PORT_CROSSHAIR_MAPPER_MEMBER_IMPL(_device, _funcptr, _name) \ + configurer.field_set_crossmapper(ioport_field_crossmap_delegate(owner, _device, _funcptr, _name)); +#define PORT_CROSSHAIR_MAPPER_DEVICE_MEMBER(...) PORT_CROSSHAIR_MAPPER_MEMBER_IMPL(__VA_ARGS__) +#define PORT_CROSSHAIR_MAPPER_MEMBER(...) PORT_CROSSHAIR_MAPPER_MEMBER_IMPL(DEVICE_SELF, __VA_ARGS__) // how many optical counts for 1 full turn of the control #define PORT_FULL_TURN_COUNT(_count) \ @@ -1709,28 +1256,41 @@ ATTR_COLD void INPUT_PORTS_NAME(_name)(device_t &owner, ioport_list &portlist, s configurer.field_set_analog_invert(); // read callbacks -#define PORT_CUSTOM_MEMBER(_class, _member) \ - configurer.field_set_dynamic_read(ioport_field_read_delegate(&_class::_member, #_class "::" #_member, DEVICE_SELF, (_class *)nullptr)); -#define PORT_CUSTOM_DEVICE_MEMBER(_device, _class, _member) \ - configurer.field_set_dynamic_read(ioport_field_read_delegate(&_class::_member, #_class "::" #_member, _device, (_class *)nullptr)); +#define PORT_CUSTOM_MEMBER_IMPL(_device, _funcptr, _name) \ + configurer.field_set_dynamic_read(ioport_field_read_delegate(owner, _device, _funcptr, _name)); +#define PORT_CUSTOM_DEVICE_MEMBER(...) PORT_CUSTOM_MEMBER_IMPL(__VA_ARGS__) +#define PORT_CUSTOM_MEMBER(...) PORT_CUSTOM_MEMBER_IMPL(DEVICE_SELF, __VA_ARGS__); // write callbacks -#define PORT_CHANGED_MEMBER(_device, _class, _member, _param) \ - configurer.field_set_dynamic_write(ioport_field_write_delegate(&_class::_member, #_class "::" #_member, _device, (_class *)nullptr), (_param)); +#define PORT_CHANGED_MEMBER_IMPL(_device, _funcptr, _name, _param) \ + configurer.field_set_dynamic_write(ioport_field_write_delegate(owner, _device, _funcptr, _name), (_param)); +#define PORT_CHANGED_MEMBER(...) PORT_CHANGED_MEMBER_IMPL(__VA_ARGS__) // input device handler -#define PORT_READ_LINE_MEMBER(_class, _member) \ - configurer.field_set_dynamic_read(ioport_field_read_delegate([](_class &device)->ioport_value { return (device._member() & 1) ? ~ioport_value(0) : 0; } , #_class "::" #_member, DEVICE_SELF, (_class *)nullptr)); -#define PORT_READ_LINE_DEVICE_MEMBER(_device, _class, _member) \ - configurer.field_set_dynamic_read(ioport_field_read_delegate([](_class &device)->ioport_value { return (device._member() & 1) ? ~ioport_value(0) : 0; } , #_class "::" #_member, _device, (_class *)nullptr)); +#define PORT_READ_LINE_MEMBER_IMPL(_device, _funcptr, _name) \ + configurer.field_set_dynamic_read( \ + ioport_field_read_delegate( \ + owner, \ + _device, \ + static_cast<ioport_value (*)(emu::detail::rw_delegate_device_class_t<decltype(_funcptr)> &)>( \ + [] (auto &device) -> ioport_value { return (std::invoke(_funcptr, device) & 1) ? ~ioport_value(0) : 0; }), \ + _name)); +#define PORT_READ_LINE_DEVICE_MEMBER(...) PORT_READ_LINE_MEMBER_IMPL(__VA_ARGS__) +#define PORT_READ_LINE_MEMBER(...) PORT_READ_LINE_MEMBER_IMPL(DEVICE_SELF, __VA_ARGS__) // output device handler -#define PORT_WRITE_LINE_MEMBER(_class, _member) \ - configurer.field_set_dynamic_write(ioport_field_write_delegate([](_class &device, ioport_field &field, u32 param, ioport_value oldval, ioport_value newval) { device._member(newval); }, #_class "::" #_member, DEVICE_SELF, (_class *)nullptr)); -#define PORT_WRITE_LINE_DEVICE_MEMBER(_device, _class, _member) \ - configurer.field_set_dynamic_write(ioport_field_write_delegate([](_class &device, ioport_field &field, u32 param, ioport_value oldval, ioport_value newval) { device._member(newval); }, #_class "::" #_member, _device, (_class *)nullptr)); - -// dip switch definition +#define PORT_WRITE_LINE_MEMBER_IMPL(_device, _funcptr, _name) \ + configurer.field_set_dynamic_write( \ + ioport_field_write_delegate( \ + owner, \ + _device, \ + static_cast<void (*)(emu::detail::rw_delegate_device_class_t<decltype(_funcptr)> &, ioport_field &, u32, ioport_value, ioport_value)>( \ + [] (auto &device, ioport_field &field, u32 param, ioport_value oldval, ioport_value newval) { std::invoke(_funcptr, device, newval); }), \ + _name)); +#define PORT_WRITE_LINE_DEVICE_MEMBER(...) PORT_WRITE_LINE_MEMBER_IMPL(__VA_ARGS__) +#define PORT_WRITE_LINE_MEMBER(...) PORT_WRITE_LINE_MEMBER_IMPL(DEVICE_SELF, __VA_ARGS__) + +// DIP switch definition #define PORT_DIPNAME(_mask, _default, _name) \ configurer.field_alloc(IPT_DIPSWITCH, (_default), (_mask), (_name)); #define PORT_DIPSETTING(_default, _name) \ @@ -1739,7 +1299,7 @@ ATTR_COLD void INPUT_PORTS_NAME(_name)(device_t &owner, ioport_list &portlist, s // note that these are specified LSB-first #define PORT_DIPLOCATION(_location) \ configurer.field_set_diplocation(_location); -// conditionals for dip switch settings +// conditionals for DIP switch settings #define PORT_CONDITION(_tag, _mask, _condition, _value) \ configurer.set_condition(ioport_condition::_condition, _tag, _mask, _value); // analog adjuster definition @@ -1756,6 +1316,103 @@ ATTR_COLD void INPUT_PORTS_NAME(_name)(device_t &owner, ioport_list &portlist, s #define PORT_CHAR(...) \ configurer.field_add_char({ __VA_ARGS__ }); +// General-midi derived piano notes +#define PORT_GM_A0 PORT_GM_NOTE( 21) // Start of 88-key keyboard +#define PORT_GM_AS0 PORT_GM_NOTE( 22) +#define PORT_GM_B0 PORT_GM_NOTE( 23) + +#define PORT_GM_C1 PORT_GM_NOTE( 24) +#define PORT_GM_CS1 PORT_GM_NOTE( 25) +#define PORT_GM_D1 PORT_GM_NOTE( 26) +#define PORT_GM_DS1 PORT_GM_NOTE( 27) +#define PORT_GM_E1 PORT_GM_NOTE( 28) // Start of 76-key keyboard +#define PORT_GM_F1 PORT_GM_NOTE( 29) +#define PORT_GM_FS1 PORT_GM_NOTE( 30) +#define PORT_GM_G1 PORT_GM_NOTE( 31) +#define PORT_GM_GS1 PORT_GM_NOTE( 32) +#define PORT_GM_A1 PORT_GM_NOTE( 33) +#define PORT_GM_AS1 PORT_GM_NOTE( 34) +#define PORT_GM_B1 PORT_GM_NOTE( 35) + +#define PORT_GM_C2 PORT_GM_NOTE( 36) // Start of 49 and 61-key keyboards +#define PORT_GM_CS2 PORT_GM_NOTE( 37) +#define PORT_GM_D2 PORT_GM_NOTE( 38) +#define PORT_GM_DS2 PORT_GM_NOTE( 39) +#define PORT_GM_E2 PORT_GM_NOTE( 40) +#define PORT_GM_F2 PORT_GM_NOTE( 41) +#define PORT_GM_FS2 PORT_GM_NOTE( 42) +#define PORT_GM_G2 PORT_GM_NOTE( 43) +#define PORT_GM_GS2 PORT_GM_NOTE( 44) +#define PORT_GM_A2 PORT_GM_NOTE( 45) +#define PORT_GM_AS2 PORT_GM_NOTE( 46) +#define PORT_GM_B2 PORT_GM_NOTE( 47) + +#define PORT_GM_C3 PORT_GM_NOTE( 48) +#define PORT_GM_CS3 PORT_GM_NOTE( 49) +#define PORT_GM_D3 PORT_GM_NOTE( 50) +#define PORT_GM_DS3 PORT_GM_NOTE( 51) +#define PORT_GM_E3 PORT_GM_NOTE( 52) +#define PORT_GM_F3 PORT_GM_NOTE( 53) +#define PORT_GM_FS3 PORT_GM_NOTE( 54) +#define PORT_GM_G3 PORT_GM_NOTE( 55) +#define PORT_GM_GS3 PORT_GM_NOTE( 56) +#define PORT_GM_A3 PORT_GM_NOTE( 57) +#define PORT_GM_AS3 PORT_GM_NOTE( 58) +#define PORT_GM_B3 PORT_GM_NOTE( 59) + +#define PORT_GM_C4 PORT_GM_NOTE( 60) // Middle C +#define PORT_GM_CS4 PORT_GM_NOTE( 61) +#define PORT_GM_D4 PORT_GM_NOTE( 62) +#define PORT_GM_DS4 PORT_GM_NOTE( 63) +#define PORT_GM_E4 PORT_GM_NOTE( 64) +#define PORT_GM_F4 PORT_GM_NOTE( 65) +#define PORT_GM_FS4 PORT_GM_NOTE( 66) +#define PORT_GM_G4 PORT_GM_NOTE( 67) +#define PORT_GM_GS4 PORT_GM_NOTE( 68) +#define PORT_GM_A4 PORT_GM_NOTE( 69) +#define PORT_GM_AS4 PORT_GM_NOTE( 70) +#define PORT_GM_B4 PORT_GM_NOTE( 71) + +#define PORT_GM_C5 PORT_GM_NOTE( 72) +#define PORT_GM_CS5 PORT_GM_NOTE( 73) +#define PORT_GM_D5 PORT_GM_NOTE( 74) +#define PORT_GM_DS5 PORT_GM_NOTE( 75) +#define PORT_GM_E5 PORT_GM_NOTE( 76) +#define PORT_GM_F5 PORT_GM_NOTE( 77) +#define PORT_GM_FS5 PORT_GM_NOTE( 78) +#define PORT_GM_G5 PORT_GM_NOTE( 79) +#define PORT_GM_GS5 PORT_GM_NOTE( 80) +#define PORT_GM_A5 PORT_GM_NOTE( 81) +#define PORT_GM_AS5 PORT_GM_NOTE( 82) +#define PORT_GM_B5 PORT_GM_NOTE( 83) + +#define PORT_GM_C6 PORT_GM_NOTE( 84) // End of 49-key keyboard +#define PORT_GM_CS6 PORT_GM_NOTE( 85) +#define PORT_GM_D6 PORT_GM_NOTE( 86) +#define PORT_GM_DS6 PORT_GM_NOTE( 87) +#define PORT_GM_E6 PORT_GM_NOTE( 88) +#define PORT_GM_F6 PORT_GM_NOTE( 89) +#define PORT_GM_FS6 PORT_GM_NOTE( 90) +#define PORT_GM_G6 PORT_GM_NOTE( 91) +#define PORT_GM_GS6 PORT_GM_NOTE( 92) +#define PORT_GM_A6 PORT_GM_NOTE( 93) +#define PORT_GM_AS6 PORT_GM_NOTE( 94) +#define PORT_GM_B6 PORT_GM_NOTE( 95) + +#define PORT_GM_C7 PORT_GM_NOTE( 96) // End of 61-key keyboard +#define PORT_GM_CS7 PORT_GM_NOTE( 97) +#define PORT_GM_D7 PORT_GM_NOTE( 98) +#define PORT_GM_DS7 PORT_GM_NOTE( 99) +#define PORT_GM_E7 PORT_GM_NOTE(100) +#define PORT_GM_F7 PORT_GM_NOTE(101) +#define PORT_GM_FS7 PORT_GM_NOTE(102) +#define PORT_GM_G7 PORT_GM_NOTE(103) // End of 76-key keyboard +#define PORT_GM_GS7 PORT_GM_NOTE(104) +#define PORT_GM_A7 PORT_GM_NOTE(105) +#define PORT_GM_AS7 PORT_GM_NOTE(106) +#define PORT_GM_B7 PORT_GM_NOTE(107) + +#define PORT_GM_C8 PORT_GM_NOTE(108) // End of 88-key keyboard // name of table #define DEVICE_INPUT_DEFAULTS_NAME(_name) device_iptdef_##_name @@ -1799,12 +1456,6 @@ ATTR_COLD void INPUT_PORTS_NAME(_name)(device_t &owner, ioport_list &portlist, s #define PORT_SERVICE_NO_TOGGLE(_mask, _default) \ PORT_BIT( _mask, _mask & _default, IPT_SERVICE ) PORT_NAME( DEF_STR( Service_Mode )) -#define PORT_VBLANK(_screen) \ - PORT_READ_LINE_DEVICE_MEMBER(_screen, screen_device, vblank) - -#define PORT_HBLANK(_screen) \ - PORT_READ_LINE_DEVICE_MEMBER(_screen, screen_device, hblank) - //************************************************************************** // INLINE FUNCTIONS //************************************************************************** diff --git a/src/emu/layout/LICENSE b/src/emu/layout/LICENSE deleted file mode 100644 index 670154e3538..00000000000 --- a/src/emu/layout/LICENSE +++ /dev/null @@ -1,116 +0,0 @@ -CC0 1.0 Universal - -Statement of Purpose - -The laws of most jurisdictions throughout the world automatically confer -exclusive Copyright and Related Rights (defined below) upon the creator and -subsequent owner(s) (each and all, an "owner") of an original work of -authorship and/or a database (each, a "Work"). - -Certain owners wish to permanently relinquish those rights to a Work for the -purpose of contributing to a commons of creative, cultural and scientific -works ("Commons") that the public can reliably and without fear of later -claims of infringement build upon, modify, incorporate in other works, reuse -and redistribute as freely as possible in any form whatsoever and for any -purposes, including without limitation commercial purposes. These owners may -contribute to the Commons to promote the ideal of a free culture and the -further production of creative, cultural and scientific works, or to gain -reputation or greater distribution for their Work in part through the use and -efforts of others. - -For these and/or other purposes and motivations, and without any expectation -of additional consideration or compensation, the person associating CC0 with a -Work (the "Affirmer"), to the extent that he or she is an owner of Copyright -and Related Rights in the Work, voluntarily elects to apply CC0 to the Work -and publicly distribute the Work under its terms, with knowledge of his or her -Copyright and Related Rights in the Work and the meaning and intended legal -effect of CC0 on those rights. - -1. Copyright and Related Rights. A Work made available under CC0 may be -protected by copyright and related or neighboring rights ("Copyright and -Related Rights"). Copyright and Related Rights include, but are not limited -to, the following: - - i. the right to reproduce, adapt, distribute, perform, display, communicate, - and translate a Work; - - ii. moral rights retained by the original author(s) and/or performer(s); - - iii. publicity and privacy rights pertaining to a person's image or likeness - depicted in a Work; - - iv. rights protecting against unfair competition in regards to a Work, - subject to the limitations in paragraph 4(a), below; - - v. rights protecting the extraction, dissemination, use and reuse of data in - a Work; - - vi. database rights (such as those arising under Directive 96/9/EC of the - European Parliament and of the Council of 11 March 1996 on the legal - protection of databases, and under any national implementation thereof, - including any amended or successor version of such directive); and - - vii. other similar, equivalent or corresponding rights throughout the world - based on applicable law or treaty, and any national implementations thereof. - -2. Waiver. To the greatest extent permitted by, but not in contravention of, -applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and -unconditionally waives, abandons, and surrenders all of Affirmer's Copyright -and Related Rights and associated claims and causes of action, whether now -known or unknown (including existing as well as future claims and causes of -action), in the Work (i) in all territories worldwide, (ii) for the maximum -duration provided by applicable law or treaty (including future time -extensions), (iii) in any current or future medium and for any number of -copies, and (iv) for any purpose whatsoever, including without limitation -commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes -the Waiver for the benefit of each member of the public at large and to the -detriment of Affirmer's heirs and successors, fully intending that such Waiver -shall not be subject to revocation, rescission, cancellation, termination, or -any other legal or equitable action to disrupt the quiet enjoyment of the Work -by the public as contemplated by Affirmer's express Statement of Purpose. - -3. Public License Fallback. Should any part of the Waiver for any reason be -judged legally invalid or ineffective under applicable law, then the Waiver -shall be preserved to the maximum extent permitted taking into account -Affirmer's express Statement of Purpose. In addition, to the extent the Waiver -is so judged Affirmer hereby grants to each affected person a royalty-free, -non transferable, non sublicensable, non exclusive, irrevocable and -unconditional license to exercise Affirmer's Copyright and Related Rights in -the Work (i) in all territories worldwide, (ii) for the maximum duration -provided by applicable law or treaty (including future time extensions), (iii) -in any current or future medium and for any number of copies, and (iv) for any -purpose whatsoever, including without limitation commercial, advertising or -promotional purposes (the "License"). The License shall be deemed effective as -of the date CC0 was applied by Affirmer to the Work. Should any part of the -License for any reason be judged legally invalid or ineffective under -applicable law, such partial invalidity or ineffectiveness shall not -invalidate the remainder of the License, and in such case Affirmer hereby -affirms that he or she will not (i) exercise any of his or her remaining -Copyright and Related Rights in the Work or (ii) assert any associated claims -and causes of action with respect to the Work, in either case contrary to -Affirmer's express Statement of Purpose. - -4. Limitations and Disclaimers. - - a. No trademark or patent rights held by Affirmer are waived, abandoned, - surrendered, licensed or otherwise affected by this document. - - b. Affirmer offers the Work as-is and makes no representations or warranties - of any kind concerning the Work, express, implied, statutory or otherwise, - including without limitation warranties of title, merchantability, fitness - for a particular purpose, non infringement, or the absence of latent or - other defects, accuracy, or the present or absence of errors, whether or not - discoverable, all to the greatest extent permissible under applicable law. - - c. Affirmer disclaims responsibility for clearing rights of other persons - that may apply to the Work or any use thereof, including without limitation - any person's Copyright and Related Rights in the Work. Further, Affirmer - disclaims responsibility for obtaining any necessary consents, permissions - or other rights required for any use of the Work. - - d. Affirmer understands and acknowledges that Creative Commons is not a - party to this document and has no duty or obligation with respect to this - CC0 or use of the Work. - -For more information, please see -<http://creativecommons.org/publicdomain/zero/1.0/> diff --git a/src/emu/layout/cyberlead.lay b/src/emu/layout/cyberlead.lay new file mode 100644 index 00000000000..9eb4e87e472 --- /dev/null +++ b/src/emu/layout/cyberlead.lay @@ -0,0 +1,14 @@ +<?xml version="1.0"?> +<!-- +license:CC0-1.0 +--> +<mamelayout version="2"> + <view name="Default Layout"> + <screen index="1"> + <bounds x="0" y="0" width="768" height="128" /> + </screen> + <screen index="0"> + <bounds x="0" y="128" width="768" height="576" /> + </screen> + </view> +</mamelayout> diff --git a/src/emu/layout/dualhovu.lay b/src/emu/layout/dualhovu.lay index a06941fe87a..0cd46b2d5c7 100644 --- a/src/emu/layout/dualhovu.lay +++ b/src/emu/layout/dualhovu.lay @@ -1,4 +1,7 @@ <?xml version="1.0"?> +<!-- +license:CC0-1.0 +--> <mamelayout version="2"> <view name="Dual Over-Under"> <screen index="0"> diff --git a/src/emu/layout/dualhsxs.lay b/src/emu/layout/dualhsxs.lay index ea7a506c370..f032d0fd48d 100644 --- a/src/emu/layout/dualhsxs.lay +++ b/src/emu/layout/dualhsxs.lay @@ -1,4 +1,7 @@ <?xml version="1.0"?> +<!-- +license:CC0-1.0 +--> <mamelayout version="2"> <view name="Dual Side-by-Side"> <screen index="0"> diff --git a/src/emu/layout/dualhuov.lay b/src/emu/layout/dualhuov.lay index d024d14cf90..8a94ca43dc7 100644 --- a/src/emu/layout/dualhuov.lay +++ b/src/emu/layout/dualhuov.lay @@ -1,4 +1,7 @@ <?xml version="1.0"?> +<!-- +license:CC0-1.0 +--> <mamelayout version="2"> <view name="Dual Under-Over"> <screen index="1"> diff --git a/src/emu/layout/ex800.lay b/src/emu/layout/ex800.lay index 90eaed17147..208fa7bc890 100644 --- a/src/emu/layout/ex800.lay +++ b/src/emu/layout/ex800.lay @@ -1,15 +1,10 @@ <?xml version="1.0"?> -<!-- Initial version. [Dirk Best] --> +<!-- +license:CC0-1.0 +copyright-holders:Dirk Best +--> <mamelayout version="2"> - <element name="background"> - <rect> - <bounds left="0" top="0" right="1" bottom="1" /> - <color red="0.0" green="0.0" blue="0.0" /> - </rect> - </element> <view name="Default Layout"> - <bezel element="background"> - <bounds left="0" top="0" right="1" bottom="1" /> - </bezel> + <bounds left="0" top="0" right="1" bottom="1" /> </view> </mamelayout> diff --git a/src/emu/layout/exorterm155.lay b/src/emu/layout/exorterm155.lay new file mode 100644 index 00000000000..28bb61c0d68 --- /dev/null +++ b/src/emu/layout/exorterm155.lay @@ -0,0 +1,77 @@ +<?xml version="1.0"?> +<!-- +license:CC0-1.0 + +LEDs for the Motorola EXORterm 155 +--> + +<mamelayout version="2"> + + <element name="red_led"> + <disk state="0"> + <color red="1.0" green="0.0" blue="0.0" /> + </disk> + </element> + + <element name="ON_LINE"> + <text string="ON LINE"> + <color red="1.0" green="1.0" blue="1.0" /> + </text> + </element> + <element name="AUTO_LF"> + <text string="AUTO LF"> + <color red="1.0" green="1.0" blue="1.0" /> + </text> + </element> + <element name="PAGE_MODE"> + <text string="PAGE MODE"> + <color red="1.0" green="1.0" blue="1.0" /> + </text> + </element> + <element name="INS_CHAR"> + <text string="INS CHAR"> + <color red="1.0" green="1.0" blue="1.0" /> + </text> + </element> + <element name="background"> + <rect> + <color red="0.0" green="0.0" blue="0.0" /> + </rect> + </element> + + <view name="Keyboard LEDs"> + <bounds left="0" top="0" right="720" bottom="544" /> + + <screen index="0"> + <bounds x="0" y="0" width="720" height="512" /> + </screen> + <element name="auto_lf_txt" ref="AUTO_LF"> + <bounds left="0" right="70" top="520" bottom="528" /> + </element> + <element name="auto_lf_led" ref="red_led"> + <bounds left="31" right="39" top="532" bottom="540" /> + </element> + + <element name="online_txt" ref="ON_LINE"> + <bounds left="70" right="140" top="520" bottom="528" /> + </element> + <element name="online_led" ref="red_led"> + <bounds left="101" right="109" top="532" bottom="540" /> + </element> + + <element name="page_mode_txt" ref="PAGE_MODE"> + <bounds left="140" right="210" top="520" bottom="528" /> + </element> + <element name="page_mode_led" ref="red_led"> + <bounds left="171" right="179" top="532" bottom="540" /> + </element> + + <element name="insert_char_txt" ref="INS_CHAR"> + <bounds left="281" right="351" top="520" bottom="528" /> + </element> + <element name="insert_char_led" ref="red_led"> + <bounds left="312" right="320" top="532" bottom="540" /> + </element> + </view> + +</mamelayout> diff --git a/src/emu/layout/generic.h b/src/emu/layout/generic.h new file mode 100644 index 00000000000..4249c3ef68b --- /dev/null +++ b/src/emu/layout/generic.h @@ -0,0 +1,38 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + generic.h + + Generic system layouts. + +***************************************************************************/ + +#ifndef MAME_EMU_LAYOUT_GENERIC_H +#define MAME_EMU_LAYOUT_GENERIC_H + +#pragma once + + +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** + +// no screens layouts +extern const internal_layout layout_noscreens; // for screenless systems + +// single screen layouts +extern const internal_layout layout_monitors; // common monitor aspect ratios + +// dual screen layouts +extern const internal_layout layout_dualhsxs; // dual 4:3 screens side-by-side +extern const internal_layout layout_dualhovu; // dual 4:3 screens above and below +extern const internal_layout layout_dualhuov; // dual 4:3 screens below and above + +// triple screen layouts +extern const internal_layout layout_triphsxs; // triple 4:3 screens side-by-side + +// quad screen layouts +extern const internal_layout layout_quadhsxs; // quad 4:3 screens side-by-side + +#endif // MAME_EMU_LAYOUT_GENERIC_H diff --git a/src/emu/layout/hp9122c.lay b/src/emu/layout/hp9122c.lay index 72ce2ef1b43..418d8964fbf 100644 --- a/src/emu/layout/hp9122c.lay +++ b/src/emu/layout/hp9122c.lay @@ -1,6 +1,6 @@ <?xml version="1.0"?> <!-- -license:CC0 +license:CC0-1.0 copyright-holders:Sven Schnelle Hewlett-Packard HP 9122C floppy drive Layout --> @@ -16,13 +16,13 @@ Hewlett-Packard HP 9122C floppy drive Layout <element name="led2_s"> <text string="E"> <color red="1.0" green="1.0" blue="1.0" /></text></element> <group name="panel"> - <bezel element="background"><bounds x="0" y="0" width="~scr0width~" height="64"/></bezel> + <element ref="background"><bounds x="0" y="0" width="~scr0width~" height="64"/></element> <repeat count="3"> <param name="left" start="20" increment="20" /> <param name="right" start="36" increment="20" /> <param name="num" start="0" increment="1" /> - <bezel name="led~num~_s" element="led~num~_s"><bounds left="~left~" right="~right~" top="8" bottom="24"/></bezel> - <bezel name="led~num~" element="ledr"><bounds left="~left~" right="~right~" top="32" bottom="48"/></bezel> + <element name="led~num~_s" ref="led~num~_s"><bounds left="~left~" right="~right~" top="8" bottom="24"/></element> + <element name="led~num~" ref="ledr"><bounds left="~left~" right="~right~" top="32" bottom="48"/></element> </repeat> </group> diff --git a/src/emu/layout/ie15.lay b/src/emu/layout/ie15.lay index 96cc316f22f..287fcbdf77a 100644 --- a/src/emu/layout/ie15.lay +++ b/src/emu/layout/ie15.lay @@ -1,8 +1,11 @@ <?xml version="1.0"?> +<!-- +license:CC0-1.0 +--> <mamelayout version="2"> <element name="e_led"> - <disk> + <disk state="0"> <color red="1.0" green="0.0" blue="0.0" /> </disk> </element> @@ -56,70 +59,71 @@ </element> <view name="Keyboard LEDs"> - <bezel name="lat_txt" element="e_LAT"> + <screen tag="screen"> + <bounds x="0" y="0" width="800" height="550" /> + </screen> + +<!-- + <element ref="e_background"> + <bounds left="0" right="800" top="550" bottom="600" /> + </element> +--> + + <element name="lat_txt" ref="e_LAT"> <bounds left="0" right="100" top="550" bottom="570" /> - </bezel> - <bezel name="lat_led" element="e_led"> + </element> + <element name="lat_led" ref="e_led"> <bounds left="40" right="60" top="580" bottom="600" /> - </bezel> + </element> - <bezel name="nr_txt" element="e_NR"> + <element name="nr_txt" ref="e_NR"> <bounds left="100" right="200" top="550" bottom="570" /> - </bezel> - <bezel name="nr_led" element="e_led"> + </element> + <element name="nr_led" ref="e_led"> <bounds left="140" right="160" top="580" bottom="600" /> - </bezel> + </element> - <bezel name="pch_txt" element="e_PCH"> + <element name="pch_txt" ref="e_PCH"> <bounds left="200" right="300" top="550" bottom="570" /> - </bezel> - <bezel name="pch_led" element="e_led"> + </element> + <element name="pch_led" ref="e_led"> <bounds left="240" right="260" top="580" bottom="600" /> - </bezel> + </element> - <bezel name="dup_txt" element="e_DUP"> + <element name="dup_txt" ref="e_DUP"> <bounds left="300" right="400" top="550" bottom="570" /> - </bezel> - <bezel name="dup_led" element="e_led"> + </element> + <element name="dup_led" ref="e_led"> <bounds left="340" right="360" top="580" bottom="600" /> - </bezel> + </element> - <bezel name="lin_txt" element="e_LIN"> + <element name="lin_txt" ref="e_LIN"> <bounds left="400" right="500" top="550" bottom="570" /> - </bezel> - <bezel name="lin_led" element="e_led"> + </element> + <element name="lin_led" ref="e_led"> <bounds left="440" right="460" top="580" bottom="600" /> - </bezel> + </element> - <bezel name="red_txt" element="e_RED"> + <element name="red_txt" ref="e_RED"> <bounds left="500" right="600" top="550" bottom="570" /> - </bezel> - <bezel name="red_led" element="e_led"> + </element> + <element name="red_led" ref="e_led"> <bounds left="540" right="560" top="580" bottom="600" /> - </bezel> + </element> - <bezel name="sdv_txt" element="e_SDV"> + <element name="sdv_txt" ref="e_SDV"> <bounds left="600" right="700" top="550" bottom="570" /> - </bezel> - <bezel name="sdv_led" element="e_led"> + </element> + <element name="sdv_led" ref="e_led"> <bounds left="640" right="660" top="580" bottom="600" /> - </bezel> + </element> - <bezel name="prd_txt" element="e_PRD"> + <element name="prd_txt" ref="e_PRD"> <bounds left="700" right="800" top="550" bottom="570" /> - </bezel> - <bezel name="prd_led" element="e_led"> + </element> + <element name="prd_led" ref="e_led"> <bounds left="740" right="760" top="580" bottom="600" /> - </bezel> - -<!-- - <bezel element="e_background"> - <bounds left="0" right="800" top="550" bottom="600" /> - </bezel> ---> - <screen tag="screen"> - <bounds x="0" y="0" width="800" height="550" /> - </screen> + </element> </view> </mamelayout> diff --git a/src/emu/layout/lx800.lay b/src/emu/layout/lx800.lay index 1ff533d1b6e..20303e50eab 100644 --- a/src/emu/layout/lx800.lay +++ b/src/emu/layout/lx800.lay @@ -1,5 +1,8 @@ <?xml version="1.0"?> -<!-- Initial version. [Dirk Best] --> +<!-- +license:CC0-1.0 +copyright-holders:Dirk Best +--> <mamelayout version="2"> <element name="background"> <rect> @@ -58,32 +61,32 @@ </element> <view name="Control panel"> - <bezel element="background"> + <element ref="background"> <bounds x="0" y="0" width="67" height="52" /> - </bezel> - <bezel name="power_led" element="power_led"> + </element> + <element name="power_led" ref="power_led"> <bounds x="8" y="9" width="8" height="4" /> - </bezel> - <bezel name="power_str" element="power_str"> + </element> + <element name="power_str" ref="power_str"> <bounds x="18" y="9" width="17" height="4" /> - </bezel> - <bezel name="ready_led" element="green_led"> + </element> + <element name="ready_led" ref="green_led"> <bounds x="8" y="20" width="8" height="4" /> - </bezel> - <bezel name="ready_str" element="ready_str"> + </element> + <element name="ready_str" ref="ready_str"> <bounds x="18" y="20" width="17" height="4" /> - </bezel> - <bezel name="paperout_led" element="red_led"> + </element> + <element name="paperout_led" ref="red_led"> <bounds x="8" y="31" width="8" height="4" /> - </bezel> - <bezel name="paperout_str" element="paperout_str"> + </element> + <element name="paperout_str" ref="paperout_str"> <bounds x="18" y="31" width="25" height="4" /> - </bezel> - <bezel name="online_led" element="green_led"> + </element> + <element name="online_led" ref="green_led"> <bounds x="8" y="42" width="8" height="4" /> - </bezel> - <bezel name="online_str" element="online_str"> + </element> + <element name="online_str" ref="online_str"> <bounds x="18" y="42" width="18" height="4" /> - </bezel> + </element> </view> </mamelayout> diff --git a/src/emu/layout/monitors.lay b/src/emu/layout/monitors.lay new file mode 100644 index 00000000000..b7a4f82f75a --- /dev/null +++ b/src/emu/layout/monitors.lay @@ -0,0 +1,41 @@ +<?xml version="1.0"?> +<!-- +license:CC0-1.0 +--> +<mamelayout version="2"> + <view name="4:3 Standard"> + <screen tag="screen"> + <bounds left="0" top="0" right="4" bottom="3" /> + </screen> + </view> + <view name="3:4 Portrait"> + <screen tag="screen"> + <bounds left="0" top="0" right="3" bottom="4" /> + </screen> + </view> + <view name="5:4 Two-Page"> + <screen tag="screen"> + <bounds left="0" top="0" right="5" bottom="4" /> + </screen> + </view> + <view name="16:9 Widescreen"> + <screen tag="screen"> + <bounds left="0" top="0" right="16" bottom="9" /> + </screen> + </view> + <view name="21:9 UltraWide"> + <screen tag="screen"> + <bounds left="0" top="0" right="21" bottom="9" /> + </screen> + </view> + <view name="32:9 Super UltraWide"> + <screen tag="screen"> + <bounds left="0" top="0" right="32" bottom="9" /> + </screen> + </view> + <view name="1:1 Air Traffic Control"> + <screen tag="screen"> + <bounds left="0" top="0" right="32" bottom="9" /> + </screen> + </view> +</mamelayout> diff --git a/src/emu/layout/nes_rob.lay b/src/emu/layout/nes_rob.lay new file mode 100644 index 00000000000..c5b10747151 --- /dev/null +++ b/src/emu/layout/nes_rob.lay @@ -0,0 +1,64 @@ +<?xml version="1.0"?> +<!-- +license:CC0-1.0 +authors:hap +--> +<mamelayout version="2"> + +<!-- define elements --> + + <element name="text_m1"><text align="1" string="motor 1:"><color red="0.8" green="0.8" blue="0.8" /></text></element> + <element name="text_m2"><text align="1" string="motor 2:"><color red="0.8" green="0.8" blue="0.8" /></text></element> + <element name="text_m3"><text align="1" string="motor 3:"><color red="0.8" green="0.8" blue="0.8" /></text></element> + + <element name="mstatus0"> + <text state="1" align="1" string="DOWN"><color red="1.0" green="1.0" blue="0.2" /></text> + <text state="0" align="1" string="DOWN"><color red="0.2" green="0.2" blue="0.2" /></text> + </element> + <element name="mstatus1"> + <text state="1" align="1" string="UP"><color red="1.0" green="1.0" blue="0.2" /></text> + <text state="0" align="1" string="UP"><color red="0.2" green="0.2" blue="0.2" /></text> + </element> + <element name="mstatus2"> + <text state="1" align="1" string="CLOSE"><color red="1.0" green="1.0" blue="0.2" /></text> + <text state="0" align="1" string="CLOSE"><color red="0.2" green="0.2" blue="0.2" /></text> + </element> + <element name="mstatus3"> + <text state="1" align="1" string="OPEN"><color red="1.0" green="1.0" blue="0.2" /></text> + <text state="0" align="1" string="OPEN"><color red="0.2" green="0.2" blue="0.2" /></text> + </element> + <element name="mstatus4"> + <text state="1" align="1" string="RIGHT"><color red="1.0" green="1.0" blue="0.2" /></text> + <text state="0" align="1" string="RIGHT"><color red="0.2" green="0.2" blue="0.2" /></text> + </element> + <element name="mstatus5"> + <text state="1" align="1" string="LEFT"><color red="1.0" green="1.0" blue="0.2" /></text> + <text state="0" align="1" string="LEFT"><color red="0.2" green="0.2" blue="0.2" /></text> + </element> + + <element name="led" defstate="0"> + <disk state="1"><color red="1.0" green="0.1" blue="0.15" /></disk> + <disk state="0"><color red="0.15" green="0.015" blue="0.017" /></disk> + </element> + + +<!-- build screen --> + + <view name="Internal Layout"> + <bounds left="4.5" right="17" top="7" bottom="14.25" /> + + <element ref="text_m1"><bounds x="5.5" y="10" width="5" height="1" /></element> + <element ref="text_m2"><bounds x="5.5" y="11.25" width="5" height="1" /></element> + <element ref="text_m3"><bounds x="5.5" y="12.5" width="5" height="1" /></element> + + <element name="rob_motor.1" ref="mstatus1"><bounds x="10" y="10" width="5" height="1" /></element> + <element name="rob_motor.0" ref="mstatus0"><bounds x="13.5" y="10" width="5" height="1" /></element> + <element name="rob_motor.5" ref="mstatus5"><bounds x="10" y="11.25" width="5" height="1" /></element> + <element name="rob_motor.4" ref="mstatus4"><bounds x="13.5" y="11.25" width="5" height="1" /></element> + <element name="rob_motor.2" ref="mstatus2"><bounds x="10" y="12.5" width="5" height="1" /></element> + <element name="rob_motor.3" ref="mstatus3"><bounds x="13.5" y="12.5" width="5" height="1" /></element> + + <element name="rob_led" ref="led"><bounds x="10" y="8" width="1" height="1" /></element> + + </view> +</mamelayout> diff --git a/src/emu/layout/noscreens.lay b/src/emu/layout/noscreens.lay index 8b9d4b10e26..e74d12e31a5 100644 --- a/src/emu/layout/noscreens.lay +++ b/src/emu/layout/noscreens.lay @@ -1,4 +1,7 @@ <?xml version="1.0"?> +<!-- +license:CC0-1.0 +--> <mamelayout version="2"> <element name="warning"> <text string="No screens attached to the system"> @@ -6,9 +9,9 @@ </text> </element> <view name="No screens attached"> - <bounds left="0" top="0" right="280" bottom="150" /> - <bezel name="warning_txt" element="warning"> - <bounds left="50" right="230" top="50" bottom="65" /> - </bezel> + <bounds left="0" top="0" right="400" bottom="300" /> + <element name="warning_txt" ref="warning"> + <bounds xc="200" yc="150" width="400" height="25" /> + </element> </view> </mamelayout> diff --git a/src/emu/layout/quadhsxs.lay b/src/emu/layout/quadhsxs.lay index a5a347299cb..d8f776cb238 100644 --- a/src/emu/layout/quadhsxs.lay +++ b/src/emu/layout/quadhsxs.lay @@ -1,4 +1,7 @@ <?xml version="1.0"?> +<!-- +license:CC0-1.0 +--> <mamelayout version="2"> <view name="Quad Side-by-Side"> <screen index="0"> diff --git a/src/emu/layout/qx10ascii.lay b/src/emu/layout/qx10ascii.lay new file mode 100644 index 00000000000..c00c7c88876 --- /dev/null +++ b/src/emu/layout/qx10ascii.lay @@ -0,0 +1,98 @@ +<?xml version="1.0"?> +<!-- +license:CC0-1.0 +--> +<mamelayout version="2"> + <element name="led" defstate="1"> + <disk state="0"> + <color red="0.75" green="0.0" blue="0.0" /> + </disk> + <disk state="1"> + <color red="0.20" green="0.0" blue="0.0" /> + </disk> + </element> + + <element name="capslock"> + <text string="CAPS"> + <color red="1.0" green="1.0" blue="1.0" /> + </text> + </element> + <element name="insert"> + <text string="INS"> + <color red="1.0" green="1.0" blue="1.0" /> + </text> + </element> + <element name="sf1"> + <text string="SF1"> + <color red="1.0" green="1.0" blue="1.0" /> + </text> + </element> + <element name="sf2"> + <text string="SF2"> + <color red="1.0" green="1.0" blue="1.0" /> + </text> + </element> + <element name="sf3"> + <text string="SF3"> + <color red="1.0" green="1.0" blue="1.0" /> + </text> + </element> + <element name="sf4"> + <text string="SF4"> + <color red="1.0" green="1.0" blue="1.0" /> + </text> + </element> + + <view name="Default Layout"> + <screen index="0"> + <bounds x="0" y="30" width="640" height="480" /> + </screen> + + <element name="led0" ref="led"> + <bounds x="33" y="17" width="10" height="10" /> + </element> + + <element name="led2" ref="led"> + <bounds x="53" y="17" width="10" height="10" /> + </element> + + <element name="led7" ref="led"> + <bounds x="83" y="17" width="10" height="10" /> + </element> + + <element name="led6" ref="led"> + <bounds x="103" y="17" width="10" height="10" /> + </element> + + <element name="led5" ref="led"> + <bounds x="123" y="17" width="10" height="10" /> + </element> + <element name="led4" ref="led"> + <bounds x="143" y="17" width="10" height="10" /> + </element> + + <element name="label1" ref="capslock"> + <bounds x="31" y="0" width="15" height="15" /> + </element> + + <element name="label2" ref="insert"> + <bounds x="51" y="0" width="15" height="15" /> + </element> + + <element name="label3" ref="sf1"> + <bounds x="81" y="0" width="15" height="15" /> + </element> + + <element name="label4" ref="sf2"> + <bounds x="101" y="0" width="15" height="15" /> + </element> + + <element name="label5" ref="sf3"> + <bounds x="121" y="0" width="15" height="15" /> + </element> + + <element name="label6" ref="sf4"> + <bounds x="141" y="0" width="15" height="15" /> + </element> + </view> +</mamelayout> diff --git a/src/emu/layout/qx10hasci.lay b/src/emu/layout/qx10hasci.lay new file mode 100644 index 00000000000..f0dadc6a0fa --- /dev/null +++ b/src/emu/layout/qx10hasci.lay @@ -0,0 +1,86 @@ +<?xml version="1.0"?> +<!-- +license:CC0-1.0 +--> +<mamelayout version="2"> + <element name="led" defstate="1"> + <disk state="0"> + <color red="0.75" green="0.0" blue="0.0" /> + </disk> + <disk state="1"> + <color red="0.20" green="0.0" blue="0.0" /> + </disk> + </element> + + <element name="capslock"> + <text string="CAPS"> + <color red="1.0" green="1.0" blue="1.0" /> + </text> + </element> + <element name="insert"> + <text string="INS"> + <color red="1.0" green="1.0" blue="1.0" /> + </text> + </element> + <element name="sf1"> + <text string="CALC"> + <color red="1.0" green="1.0" blue="1.0" /> + </text> + </element> + <element name="sf2"> + <text string="SCHED"> + <color red="1.0" green="1.0" blue="1.0" /> + </text> + </element> + <element name="sf3"> + <text string="DRAW"> + <color red="1.0" green="1.0" blue="1.0" /> + </text> + </element> + + <view name="Default Layout"> + <screen index="0"> + <bounds x="0" y="30" width="640" height="480" /> + </screen> + + <element name="led3" ref="led"> + <bounds x="33" y="17" width="10" height="10" /> + </element> + + <element name="led2" ref="led"> + <bounds x="53" y="17" width="10" height="10" /> + </element> + + <element name="led7" ref="led"> + <bounds x="83" y="17" width="10" height="10" /> + </element> + + <element name="led6" ref="led"> + <bounds x="103" y="17" width="10" height="10" /> + </element> + + <element name="led5" ref="led"> + <bounds x="123" y="17" width="10" height="10" /> + </element> + + <element name="label1" ref="capslock"> + <bounds x="31" y="0" width="15" height="15" /> + </element> + + <element name="label2" ref="insert"> + <bounds x="51" y="0" width="15" height="15" /> + </element> + + <element name="label3" ref="sf1"> + <bounds x="81" y="0" width="15" height="15" /> + </element> + + <element name="label4" ref="sf2"> + <bounds x="101" y="0" width="15" height="15" /> + </element> + + <element name="label5" ref="sf3"> + <bounds x="121" y="0" width="15" height="15" /> + </element> + </view> +</mamelayout> diff --git a/src/emu/layout/smartboard.lay b/src/emu/layout/smartboard.lay new file mode 100644 index 00000000000..1b028bc23fe --- /dev/null +++ b/src/emu/layout/smartboard.lay @@ -0,0 +1,434 @@ +<?xml version="1.0"?> +<!-- +license:CC0-1.0 +authors:Sandro Ronco, hap +--> +<mamelayout version="2"> + +<!-- define elements --> + + <element name="black2b"><rect><color red="0.12" green="0.11" blue="0.10" /></rect></element> + + <element name="ledo"> + <disk><color red="0.1" green="0.05" blue="0.07" /></disk> + </element> + <element name="ledr" defstate="0"> + <disk state="1"><color red="1" green="0" blue="0" /></disk> + </element> + + <element name="piece" defstate="0"> + <image file="chess/wn.svg" state="1"/> + <image file="chess/wn.svg" state="2"/> + <image file="chess/bk.svg" state="3"/> + <image file="chess/wk.svg" state="4"/> + <image file="chess/bq.svg" state="5"/> + <image file="chess/wq.svg" state="6"/> + <image file="chess/br.svg" state="7"/> + <image file="chess/br.svg" state="8"/> + <image file="chess/wr.svg" state="9"/> + <image file="chess/wr.svg" state="10"/> + <image file="chess/bb.svg" state="11"/> + <image file="chess/bb.svg" state="12"/> + <image file="chess/wb.svg" state="13"/> + <image file="chess/wb.svg" state="14"/> + <image file="chess/bn.svg" state="15"/> + <image file="chess/bn.svg" state="16"/> + <image file="chess/wp.svg" state="17"/> + <image file="chess/wp.svg" state="18"/> + <image file="chess/wp.svg" state="19"/> + <image file="chess/wp.svg" state="20"/> + <image file="chess/wp.svg" state="21"/> + <image file="chess/wp.svg" state="22"/> + <image file="chess/wp.svg" state="23"/> + <image file="chess/wp.svg" state="24"/> + <image file="chess/bp.svg" state="25"/> + <image file="chess/bp.svg" state="26"/> + <image file="chess/bp.svg" state="27"/> + <image file="chess/bp.svg" state="28"/> + <image file="chess/bp.svg" state="29"/> + <image file="chess/bp.svg" state="30"/> + <image file="chess/bp.svg" state="31"/> + <image file="chess/bp.svg" state="32"/> + + <!-- selected pieces --> + <image file="chess/wn.svg" state="33"><color alpha="0.5" /></image> + <image file="chess/wn.svg" state="34"><color alpha="0.5" /></image> + <image file="chess/bk.svg" state="35"><color alpha="0.5" /></image> + <image file="chess/wk.svg" state="36"><color alpha="0.5" /></image> + <image file="chess/bq.svg" state="37"><color alpha="0.5" /></image> + <image file="chess/wq.svg" state="38"><color alpha="0.5" /></image> + <image file="chess/br.svg" state="39"><color alpha="0.5" /></image> + <image file="chess/br.svg" state="40"><color alpha="0.5" /></image> + <image file="chess/wr.svg" state="41"><color alpha="0.5" /></image> + <image file="chess/wr.svg" state="42"><color alpha="0.5" /></image> + <image file="chess/bb.svg" state="43"><color alpha="0.5" /></image> + <image file="chess/bb.svg" state="44"><color alpha="0.5" /></image> + <image file="chess/wb.svg" state="45"><color alpha="0.5" /></image> + <image file="chess/wb.svg" state="46"><color alpha="0.5" /></image> + <image file="chess/bn.svg" state="47"><color alpha="0.5" /></image> + <image file="chess/bn.svg" state="48"><color alpha="0.5" /></image> + <image file="chess/wp.svg" state="49"><color alpha="0.5" /></image> + <image file="chess/wp.svg" state="50"><color alpha="0.5" /></image> + <image file="chess/wp.svg" state="51"><color alpha="0.5" /></image> + <image file="chess/wp.svg" state="52"><color alpha="0.5" /></image> + <image file="chess/wp.svg" state="53"><color alpha="0.5" /></image> + <image file="chess/wp.svg" state="54"><color alpha="0.5" /></image> + <image file="chess/wp.svg" state="55"><color alpha="0.5" /></image> + <image file="chess/wp.svg" state="56"><color alpha="0.5" /></image> + <image file="chess/bp.svg" state="57"><color alpha="0.5" /></image> + <image file="chess/bp.svg" state="58"><color alpha="0.5" /></image> + <image file="chess/bp.svg" state="59"><color alpha="0.5" /></image> + <image file="chess/bp.svg" state="60"><color alpha="0.5" /></image> + <image file="chess/bp.svg" state="61"><color alpha="0.5" /></image> + <image file="chess/bp.svg" state="62"><color alpha="0.5" /></image> + <image file="chess/bp.svg" state="63"><color alpha="0.5" /></image> + <image file="chess/bp.svg" state="64"><color alpha="0.5" /></image> + </element> + + <element name="text_1"> <text string="1"> <color red="1" green="1" blue="1" /></text> </element> + <element name="text_2"> <text string="2"> <color red="1" green="1" blue="1" /></text> </element> + <element name="text_3"> <text string="3"> <color red="1" green="1" blue="1" /></text> </element> + <element name="text_4"> <text string="4"> <color red="1" green="1" blue="1" /></text> </element> + <element name="text_5"> <text string="5"> <color red="1" green="1" blue="1" /></text> </element> + <element name="text_6"> <text string="6"> <color red="1" green="1" blue="1" /></text> </element> + <element name="text_7"> <text string="7"> <color red="1" green="1" blue="1" /></text> </element> + <element name="text_8"> <text string="8"> <color red="1" green="1" blue="1" /></text> </element> + <element name="text_a"> <text string="A"> <color red="1" green="1" blue="1" /></text> </element> + <element name="text_b"> <text string="B"> <color red="1" green="1" blue="1" /></text> </element> + <element name="text_c"> <text string="C"> <color red="1" green="1" blue="1" /></text> </element> + <element name="text_d"> <text string="D"> <color red="1" green="1" blue="1" /></text> </element> + <element name="text_e"> <text string="E"> <color red="1" green="1" blue="1" /></text> </element> + <element name="text_f"> <text string="F"> <color red="1" green="1" blue="1" /></text> </element> + <element name="text_g"> <text string="G"> <color red="1" green="1" blue="1" /></text> </element> + <element name="text_h"> <text string="H"> <color red="1" green="1" blue="1" /></text> </element> + + +<!-- sb board --> + + <element name="cblack"><rect><color red="0.56" green="0.33" blue="0.12" /></rect></element> + <element name="cwhite"><rect><color red="0.84" green="0.75" blue="0.50" /></rect></element> + + <element name="hlbb" defstate="0"> + <text string=" "><bounds x="0" y="0" width="1" height="1" /></text> + <disk state="1"> + <bounds x="0.12" y="0.12" width="0.76" height="0.76" /> + <color red="0" green="0" blue="0" /> + </disk> + </element> + + <group name="sb_board"> + <bounds x="-3.5" y="-3.5" width="89" height="89" /> + + <element ref="cblack"><bounds x="-3.5" y="-3.5" width="89" height="89" /></element> + <element ref="black2b"><bounds x="0.5" y="0.5" width="81" height="81" /></element> + + <!-- chessboard coords --> + <element ref="text_8"><bounds x="-2.3" y="5" width="2" height="2" /></element> + <element ref="text_7"><bounds x="-2.3" y="15" width="2" height="2" /></element> + <element ref="text_6"><bounds x="-2.3" y="25" width="2" height="2" /></element> + <element ref="text_5"><bounds x="-2.3" y="35" width="2" height="2" /></element> + <element ref="text_4"><bounds x="-2.3" y="45" width="2" height="2" /></element> + <element ref="text_3"><bounds x="-2.3" y="55" width="2" height="2" /></element> + <element ref="text_2"><bounds x="-2.3" y="65" width="2" height="2" /></element> + <element ref="text_1"><bounds x="-2.3" y="75" width="2" height="2" /></element> + <element ref="text_a"><bounds x="5" y="82.5" width="2" height="2" /></element> + <element ref="text_b"><bounds x="15" y="82.5" width="2" height="2" /></element> + <element ref="text_c"><bounds x="25" y="82.5" width="2" height="2" /></element> + <element ref="text_d"><bounds x="35" y="82.5" width="2" height="2" /></element> + <element ref="text_e"><bounds x="45" y="82.5" width="2" height="2" /></element> + <element ref="text_f"><bounds x="55" y="82.5" width="2" height="2" /></element> + <element ref="text_g"><bounds x="65" y="82.5" width="2" height="2" /></element> + <element ref="text_h"><bounds x="75" y="82.5" width="2" height="2" /></element> + + <!-- squares (avoid seams) --> + <element ref="cwhite"><bounds x="1" y="1" width="11" height="11" /></element> + <element ref="cblack"><bounds x="11" y="1" width="11" height="11" /></element> + <element ref="cwhite"><bounds x="21" y="1" width="11" height="11" /></element> + <element ref="cblack"><bounds x="31" y="1" width="11" height="11" /></element> + <element ref="cwhite"><bounds x="41" y="1" width="11" height="11" /></element> + <element ref="cblack"><bounds x="51" y="1" width="11" height="11" /></element> + <element ref="cwhite"><bounds x="61" y="1" width="11" height="11" /></element> + <element ref="cblack"><bounds x="71" y="1" width="10" height="11" /></element> + + <element ref="cblack"><bounds x="1" y="11" width="11" height="11" /></element> + <element ref="cwhite"><bounds x="11" y="11" width="11" height="11" /></element> + <element ref="cblack"><bounds x="21" y="11" width="11" height="11" /></element> + <element ref="cwhite"><bounds x="31" y="11" width="11" height="11" /></element> + <element ref="cblack"><bounds x="41" y="11" width="11" height="11" /></element> + <element ref="cwhite"><bounds x="51" y="11" width="11" height="11" /></element> + <element ref="cblack"><bounds x="61" y="11" width="11" height="11" /></element> + <element ref="cwhite"><bounds x="71" y="11" width="10" height="11" /></element> + + <element ref="cwhite"><bounds x="1" y="21" width="11" height="11" /></element> + <element ref="cblack"><bounds x="11" y="21" width="11" height="11" /></element> + <element ref="cwhite"><bounds x="21" y="21" width="11" height="11" /></element> + <element ref="cblack"><bounds x="31" y="21" width="11" height="11" /></element> + <element ref="cwhite"><bounds x="41" y="21" width="11" height="11" /></element> + <element ref="cblack"><bounds x="51" y="21" width="11" height="11" /></element> + <element ref="cwhite"><bounds x="61" y="21" width="11" height="11" /></element> + <element ref="cblack"><bounds x="71" y="21" width="10" height="11" /></element> + + <element ref="cblack"><bounds x="1" y="31" width="11" height="11" /></element> + <element ref="cwhite"><bounds x="11" y="31" width="11" height="11" /></element> + <element ref="cblack"><bounds x="21" y="31" width="11" height="11" /></element> + <element ref="cwhite"><bounds x="31" y="31" width="11" height="11" /></element> + <element ref="cblack"><bounds x="41" y="31" width="11" height="11" /></element> + <element ref="cwhite"><bounds x="51" y="31" width="11" height="11" /></element> + <element ref="cblack"><bounds x="61" y="31" width="11" height="11" /></element> + <element ref="cwhite"><bounds x="71" y="31" width="10" height="11" /></element> + + <element ref="cwhite"><bounds x="1" y="41" width="11" height="11" /></element> + <element ref="cblack"><bounds x="11" y="41" width="11" height="11" /></element> + <element ref="cwhite"><bounds x="21" y="41" width="11" height="11" /></element> + <element ref="cblack"><bounds x="31" y="41" width="11" height="11" /></element> + <element ref="cwhite"><bounds x="41" y="41" width="11" height="11" /></element> + <element ref="cblack"><bounds x="51" y="41" width="11" height="11" /></element> + <element ref="cwhite"><bounds x="61" y="41" width="11" height="11" /></element> + <element ref="cblack"><bounds x="71" y="41" width="10" height="11" /></element> + + <element ref="cblack"><bounds x="1" y="51" width="11" height="11" /></element> + <element ref="cwhite"><bounds x="11" y="51" width="11" height="11" /></element> + <element ref="cblack"><bounds x="21" y="51" width="11" height="11" /></element> + <element ref="cwhite"><bounds x="31" y="51" width="11" height="11" /></element> + <element ref="cblack"><bounds x="41" y="51" width="11" height="11" /></element> + <element ref="cwhite"><bounds x="51" y="51" width="11" height="11" /></element> + <element ref="cblack"><bounds x="61" y="51" width="11" height="11" /></element> + <element ref="cwhite"><bounds x="71" y="51" width="10" height="11" /></element> + + <element ref="cwhite"><bounds x="1" y="61" width="11" height="11" /></element> + <element ref="cblack"><bounds x="11" y="61" width="11" height="11" /></element> + <element ref="cwhite"><bounds x="21" y="61" width="11" height="11" /></element> + <element ref="cblack"><bounds x="31" y="61" width="11" height="11" /></element> + <element ref="cwhite"><bounds x="41" y="61" width="11" height="11" /></element> + <element ref="cblack"><bounds x="51" y="61" width="11" height="11" /></element> + <element ref="cwhite"><bounds x="61" y="61" width="11" height="11" /></element> + <element ref="cblack"><bounds x="71" y="61" width="10" height="11" /></element> + + <element ref="cblack"><bounds x="1" y="71" width="11" height="10" /></element> + <element ref="cwhite"><bounds x="11" y="71" width="11" height="10" /></element> + <element ref="cblack"><bounds x="21" y="71" width="11" height="10" /></element> + <element ref="cwhite"><bounds x="31" y="71" width="11" height="10" /></element> + <element ref="cblack"><bounds x="41" y="71" width="11" height="10" /></element> + <element ref="cwhite"><bounds x="51" y="71" width="11" height="10" /></element> + <element ref="cblack"><bounds x="61" y="71" width="11" height="10" /></element> + <element ref="cwhite"><bounds x="71" y="71" width="10" height="10" /></element> + + <!-- sensors, pieces --> + <repeat count="8"> + <param name="y" start="1" increment="10" /> + <param name="i" start="8" increment="-1" /> + + <element ref="hlbb" inputtag="smartboard:board:RANK.~i~" inputmask="0x01"><bounds x="1" y="~y~" width="10" height="10" /><color alpha="0.04" /></element> + <element ref="hlbb" inputtag="smartboard:board:RANK.~i~" inputmask="0x02"><bounds x="11" y="~y~" width="10" height="10" /><color alpha="0.04" /></element> + <element ref="hlbb" inputtag="smartboard:board:RANK.~i~" inputmask="0x04"><bounds x="21" y="~y~" width="10" height="10" /><color alpha="0.04" /></element> + <element ref="hlbb" inputtag="smartboard:board:RANK.~i~" inputmask="0x08"><bounds x="31" y="~y~" width="10" height="10" /><color alpha="0.04" /></element> + <element ref="hlbb" inputtag="smartboard:board:RANK.~i~" inputmask="0x10"><bounds x="41" y="~y~" width="10" height="10" /><color alpha="0.04" /></element> + <element ref="hlbb" inputtag="smartboard:board:RANK.~i~" inputmask="0x20"><bounds x="51" y="~y~" width="10" height="10" /><color alpha="0.04" /></element> + <element ref="hlbb" inputtag="smartboard:board:RANK.~i~" inputmask="0x40"><bounds x="61" y="~y~" width="10" height="10" /><color alpha="0.04" /></element> + <element ref="hlbb" inputtag="smartboard:board:RANK.~i~" inputmask="0x80"><bounds x="71" y="~y~" width="10" height="10" /><color alpha="0.04" /></element> + + <element name="piece_a~i~" ref="piece"><bounds x="1" y="~y~" width="10" height="10" /></element> + <element name="piece_b~i~" ref="piece"><bounds x="11" y="~y~" width="10" height="10" /></element> + <element name="piece_c~i~" ref="piece"><bounds x="21" y="~y~" width="10" height="10" /></element> + <element name="piece_d~i~" ref="piece"><bounds x="31" y="~y~" width="10" height="10" /></element> + <element name="piece_e~i~" ref="piece"><bounds x="41" y="~y~" width="10" height="10" /></element> + <element name="piece_f~i~" ref="piece"><bounds x="51" y="~y~" width="10" height="10" /></element> + <element name="piece_g~i~" ref="piece"><bounds x="61" y="~y~" width="10" height="10" /></element> + <element name="piece_h~i~" ref="piece"><bounds x="71" y="~y~" width="10" height="10" /></element> + </repeat> + + <!-- 81 LEDs from 64 outputs --> + <repeat count="9"> + <param name="x" start="0.5" increment="10" /> + <repeat count="9"> + <param name="y" start="0.5" increment="10" /> + <element ref="ledo"><bounds x="~x~" y="~y~" width="1" height="1" /></element> + </repeat> + </repeat> + + <repeat count="8"> + <param name="x" start="80.5" increment="-10" /> + <param name="i" start="0" increment="1" /> + <repeat count="8"> + <param name="y" start="0.5" increment="10" /> + <param name="j" start="0" increment="1" /> + <element name="sb30_led_~j~.~i~" ref="ledr" blend="add"><bounds x="~x~" y="~y~" width="1" height="1" /></element> + </repeat> + <repeat count="8"> + <param name="y" start="10.5" increment="10" /> + <param name="j" start="0" increment="1" /> + <element name="sb30_led_~j~.~i~" ref="ledr" blend="add"><bounds x="~x~" y="~y~" width="1" height="1" /></element> + </repeat> + </repeat> + + <repeat count="8"> + <param name="x" start="70.5" increment="-10" /> + <param name="i" start="0" increment="1" /> + <repeat count="8"> + <param name="y" start="0.5" increment="10" /> + <param name="j" start="0" increment="1" /> + <element name="sb30_led_~j~.~i~" ref="ledr" blend="add"><bounds x="~x~" y="~y~" width="1" height="1" /></element> + </repeat> + <repeat count="8"> + <param name="y" start="10.5" increment="10" /> + <param name="j" start="0" increment="1" /> + <element name="sb30_led_~j~.~i~" ref="ledr" blend="add"><bounds x="~x~" y="~y~" width="1" height="1" /></element> + </repeat> + </repeat> + </group> + + +<!-- sb ui --> + + <element name="piece_ui" defstate="0"> + <image file="chess/wp.svg" state="1"/> + <image file="chess/wn.svg" state="2"/> + <image file="chess/wb.svg" state="3"/> + <image file="chess/wr.svg" state="4"/> + <image file="chess/wq.svg" state="5"/> + <image file="chess/wk.svg" state="6"/> + + <image file="chess/bp.svg" state="7"/> + <image file="chess/bn.svg" state="8"/> + <image file="chess/bb.svg" state="9"/> + <image file="chess/br.svg" state="10"/> + <image file="chess/bq.svg" state="11"/> + <image file="chess/bk.svg" state="12"/> + + <!-- selected pieces --> + <image file="chess/wp.svg" state="13"><color alpha="0.5" /></image> + <image file="chess/wn.svg" state="14"><color alpha="0.5" /></image> + <image file="chess/wb.svg" state="15"><color alpha="0.5" /></image> + <image file="chess/wr.svg" state="16"><color alpha="0.5" /></image> + <image file="chess/wq.svg" state="17"><color alpha="0.5" /></image> + <image file="chess/wk.svg" state="18"><color alpha="0.5" /></image> + + <image file="chess/bp.svg" state="19"><color alpha="0.5" /></image> + <image file="chess/bn.svg" state="20"><color alpha="0.5" /></image> + <image file="chess/bb.svg" state="21"><color alpha="0.5" /></image> + <image file="chess/br.svg" state="22"><color alpha="0.5" /></image> + <image file="chess/bq.svg" state="23"><color alpha="0.5" /></image> + <image file="chess/bk.svg" state="24"><color alpha="0.5" /></image> + </element> + + <element name="hlub" defstate="0"> + <rect state="1"><color red="0" green="0" blue="0" /></rect> + </element> + + <element name="text_uit1"><text string="S.BOARD"><color red="0.81" green="0.8" blue="0.79" /></text></element> + <element name="text_uit2"><text string="INTERFACE"><color red="0.81" green="0.8" blue="0.79" /></text></element> + <element name="text_uib1"><text string="BOARD:"><color red="0.81" green="0.8" blue="0.79" /></text></element> + <element name="text_uib2"><text string="RESET"><color red="0.01" green="0.01" blue="0.01" /></text></element> + <element name="text_uib3"><text string="CLEAR"><color red="0.01" green="0.01" blue="0.01" /></text></element> + <element name="text_uis1"><text string="SPAWN:"><color red="0.81" green="0.8" blue="0.79" /></text></element> + <element name="text_uih1"><text string="HAND:"><color red="0.81" green="0.8" blue="0.79" /></text></element> + <element name="text_uih2"><text string="REMOVE"><color red="0.01" green="0.01" blue="0.01" /></text></element> + <element name="text_uiu1"><text string="UNDO:"><color red="0.81" green="0.8" blue="0.79" /></text></element> + <element name="text_uiu2a"><text string=" <<"><color red="0.01" green="0.01" blue="0.01" /></text></element> + <element name="text_uiu2b"><text string=" < "><color red="0.01" green="0.01" blue="0.01" /></text></element> + <element name="text_uiu2c"><text string=" >"><color red="0.01" green="0.01" blue="0.01" /></text></element> + <element name="text_uiu2d"><text string=" >>"><color red="0.01" green="0.01" blue="0.01" /></text></element> + <element name="text_uiu3b"><text string="/"><color red="0.81" green="0.8" blue="0.79" /></text></element> + + <element name="text_uiu3a" defstate="0"> + <simplecounter maxstate="999" digits="1" align="2"> + <color red="0.81" green="0.8" blue="0.79" /> + </simplecounter> + </element> + <element name="text_uiu3c" defstate="0"> + <simplecounter maxstate="999" digits="1" align="1"> + <color red="0.81" green="0.8" blue="0.79" /> + </simplecounter> + </element> + + <group name="sb_ui"> + <bounds x="0" y="0" width="10" height="80" /> + <element ref="cblack"><bounds x="0" y="0" width="10" height="1" /></element> + <element ref="cblack"><bounds x="0" y="7" width="10" height="1" /></element> + <element ref="cblack"><bounds x="0" y="79" width="10" height="1" /></element> + <element ref="text_uit1"><bounds x="0" y="2" width="10" height="2" /></element> + <element ref="text_uit2"><bounds x="0" y="4" width="10" height="2" /></element> + + <!-- board --> + <element ref="text_uib1"><bounds x="0" y="9" width="10" height="2" /></element> + <element ref="cwhite"><bounds x="1" y="11.5" width="8" height="2.5" /></element> + <element ref="cwhite"><bounds x="1" y="15" width="8" height="2.5" /></element> + + <element ref="text_uib2"><bounds x="1.5" y="11.75" width="7" height="2" /></element> + <element ref="text_uib3"><bounds x="1.5" y="15.25" width="7" height="2" /></element> + + <element ref="hlub" inputtag="smartboard:board:UI" inputmask="0x200"><bounds x="1" y="11.5" width="8" height="2.5" /><color alpha="0.25" /></element> + <element ref="hlub" inputtag="smartboard:board:UI" inputmask="0x100"><bounds x="1" y="15" width="8" height="2.5" /><color alpha="0.25" /></element> + + <!-- spawn --> + <element ref="text_uis1"><bounds x="0" y="20.5" width="10" height="2" /></element> + <element ref="cwhite"><bounds x="1" y="23" width="8" height="12" /></element> + <element ref="cwhite"><bounds x="1" y="36" width="8" height="12" /></element> + + <element name="piece_ui1" ref="piece_ui"><bounds x="1" y="23" width="4" height="4" /></element> + <element name="piece_ui2" ref="piece_ui"><bounds x="1" y="27" width="4" height="4" /></element> + <element name="piece_ui3" ref="piece_ui"><bounds x="1" y="31" width="4" height="4" /></element> + <element name="piece_ui4" ref="piece_ui"><bounds x="5" y="23" width="4" height="4" /></element> + <element name="piece_ui5" ref="piece_ui"><bounds x="5" y="27" width="4" height="4" /></element> + <element name="piece_ui6" ref="piece_ui"><bounds x="5" y="31" width="4" height="4" /></element> + <element name="piece_ui7" ref="piece_ui"><bounds x="1" y="36" width="4" height="4" /></element> + <element name="piece_ui8" ref="piece_ui"><bounds x="1" y="40" width="4" height="4" /></element> + <element name="piece_ui9" ref="piece_ui"><bounds x="1" y="44" width="4" height="4" /></element> + <element name="piece_ui10" ref="piece_ui"><bounds x="5" y="36" width="4" height="4" /></element> + <element name="piece_ui11" ref="piece_ui"><bounds x="5" y="40" width="4" height="4" /></element> + <element name="piece_ui12" ref="piece_ui"><bounds x="5" y="44" width="4" height="4" /></element> + + <element ref="hlub" inputtag="smartboard:board:SPAWN" inputmask="0x0001"><bounds x="1" y="23" width="4" height="4" /><color alpha="0.25" /></element> + <element ref="hlub" inputtag="smartboard:board:SPAWN" inputmask="0x0002"><bounds x="1" y="27" width="4" height="4" /><color alpha="0.25" /></element> + <element ref="hlub" inputtag="smartboard:board:SPAWN" inputmask="0x0004"><bounds x="1" y="31" width="4" height="4" /><color alpha="0.25" /></element> + <element ref="hlub" inputtag="smartboard:board:SPAWN" inputmask="0x0008"><bounds x="5" y="23" width="4" height="4" /><color alpha="0.25" /></element> + <element ref="hlub" inputtag="smartboard:board:SPAWN" inputmask="0x0010"><bounds x="5" y="27" width="4" height="4" /><color alpha="0.25" /></element> + <element ref="hlub" inputtag="smartboard:board:SPAWN" inputmask="0x0020"><bounds x="5" y="31" width="4" height="4" /><color alpha="0.25" /></element> + <element ref="hlub" inputtag="smartboard:board:SPAWN" inputmask="0x0040"><bounds x="1" y="36" width="4" height="4" /><color alpha="0.25" /></element> + <element ref="hlub" inputtag="smartboard:board:SPAWN" inputmask="0x0080"><bounds x="1" y="40" width="4" height="4" /><color alpha="0.25" /></element> + <element ref="hlub" inputtag="smartboard:board:SPAWN" inputmask="0x0100"><bounds x="1" y="44" width="4" height="4" /><color alpha="0.25" /></element> + <element ref="hlub" inputtag="smartboard:board:SPAWN" inputmask="0x0200"><bounds x="5" y="36" width="4" height="4" /><color alpha="0.25" /></element> + <element ref="hlub" inputtag="smartboard:board:SPAWN" inputmask="0x0400"><bounds x="5" y="40" width="4" height="4" /><color alpha="0.25" /></element> + <element ref="hlub" inputtag="smartboard:board:SPAWN" inputmask="0x0800"><bounds x="5" y="44" width="4" height="4" /><color alpha="0.25" /></element> + + <!-- hand --> + <element ref="text_uih1"><bounds x="0" y="51" width="10" height="2" /></element> + <element ref="cblack"><bounds x="1" y="53.5" width="8" height="6" /></element> + <element name="piece_ui0" ref="piece"><bounds x="2" y="53.5" width="6" height="6" /></element> + + <element ref="cwhite"><bounds x="1" y="60.5" width="8" height="2.5" /></element> + <element ref="text_uih2"><bounds x="1.5" y="60.75" width="7" height="2" /></element> + <element ref="hlub" inputtag="smartboard:board:UI" inputmask="0x08"><bounds x="1" y="60.5" width="8" height="2.5" /><color alpha="0.25" /></element> + + <!-- undo --> + <element ref="text_uiu1"><bounds x="0" y="66" width="10" height="2" /></element> + <element ref="cwhite"><bounds x="1" y="68.5" width="1.7" height="6" /></element> + <element ref="cwhite"><bounds x="3.1" y="68.5" width="1.7" height="6" /></element> + <element ref="cwhite"><bounds x="5.2" y="68.5" width="1.7" height="6" /></element> + <element ref="cwhite"><bounds x="7.3" y="68.5" width="1.7" height="6" /></element> + <element ref="text_uiu2a"><bounds x="1" y="69.5" width="1.7" height="4" /></element> + <element ref="text_uiu2b"><bounds x="3.1" y="69.5" width="1.7" height="4" /></element> + <element ref="text_uiu2c"><bounds x="5.2" y="69.5" width="1.7" height="4" /></element> + <element ref="text_uiu2d"><bounds x="7.3" y="69.5" width="1.7" height="4" /></element> + + <element ref="hlub" inputtag="smartboard:board:UI" inputmask="0x10"><bounds x="1" y="68.5" width="1.7" height="6" /><color alpha="0.25" /></element> + <element ref="hlub" inputtag="smartboard:board:UI" inputmask="0x20"><bounds x="3.1" y="68.5" width="1.7" height="6" /><color alpha="0.25" /></element> + <element ref="hlub" inputtag="smartboard:board:UI" inputmask="0x40"><bounds x="5.2" y="68.5" width="1.7" height="6" /><color alpha="0.25" /></element> + <element ref="hlub" inputtag="smartboard:board:UI" inputmask="0x80"><bounds x="7.3" y="68.5" width="1.7" height="6" /><color alpha="0.25" /></element> + + <element name="count_ui0" ref="text_uiu3a"><bounds x="0" y="75" width="4" height="2" /></element> + <element name="count_ui1" ref="text_uiu3c"><bounds x="6" y="75" width="4" height="2" /></element> + <element ref="text_uiu3b"><bounds x="4" y="75" width="2" height="2" /></element> + </group> + + +<!-- build screen --> + + <view name="Internal Layout"> + <bounds left="0" right="100.8" top="0" bottom="88.8" /> + + <group ref="sb_board"><bounds x="12" y="0" width="88.8" height="88.8" /></group> + <group ref="sb_ui"><bounds x="1" y="4.4" width="10" height="80" /></group> + </view> + +</mamelayout> diff --git a/src/emu/layout/teletex800.lay b/src/emu/layout/teletex800.lay new file mode 100644 index 00000000000..381c48ef34f --- /dev/null +++ b/src/emu/layout/teletex800.lay @@ -0,0 +1,167 @@ +<?xml version="1.0"?> +<!-- +license:CC0-1.0 +--> +<mamelayout version="2"> + <element name="panel"> + <!--<image file="teletex800.png" />--> + </element> + + <element name="button"> + <rect> + <color red="0.25" green="0.25" blue="0.25" /> + </rect> + </element> + + <element name="red_led" defstate="0"> + <disk state="0"> + <color red="0.20" green="0.0" blue="0.0" /> + </disk> + <disk state="1"> + <color red="0.75" green="0.0" blue="0.0" /> + </disk> + </element> + + <element name="green_led" defstate="0"> + <disk state="0"> + <color red="0.0" green="0.20" blue="0.0" /> + </disk> + <disk state="1"> + <color red="0.0" green="0.75" blue="0.0" /> + </disk> + </element> + + <element name="digit" defstate="0"> + <led7seg> + <color red="0.0" green="1.0" blue="0.0" /> + </led7seg> + </element> + + <element name="time_led" defstate="0"> + <text state="0" string="TID"> + <color red="0.0" green="0.20" blue="0.0" /> + </text> + <text state="1" string="TID"> + <color red="0.0" green="0.75" blue="0.0" /> + </text> + </element> + + <element name="date_led" defstate="0"> + <text state="0" string="DAT"> + <color red="0.0" green="0.20" blue="0.0" /> + </text> + <text state="1" string="DAT"> + <color red="0.0" green="0.75" blue="0.0" /> + </text> + </element> + + <element name="year_led" defstate="0"> + <text state="0" string="ÅR"> + <color red="0.0" green="0.20" blue="0.0" /> + </text> + <text state="1" string="ÅR"> + <color red="0.0" green="0.75" blue="0.0" /> + </text> + </element> + + <view name="Front panel"> + <bounds x="0" y="0" width="810" height="473" /> + + <element ref="panel"> + <bounds x="0" y="0" width="810" height="473" /> + </element> + + <!-- BATTERI DRIFT --> + <element name="bat_led" ref="red_led"> + <bounds x="114" y="118" width="16" height="16" /> + </element> + + <!-- SKRIVAR FEL --> + <element name="pr_led" ref="red_led"> + <bounds x="205" y="118" width="16" height="16" /> + </element> + + <!-- OKVITT TELEX --> + <element name="telex_led" ref="green_led"> + <bounds x="298" y="118" width="16" height="16" /> + </element> + + <!-- MOTTAGNA --> + <element name="rx_digit0" ref="digit"> + <bounds x="384" y="107" width="38" height="49" /> + </element> + + <element name="rx_digit1" ref="digit"> + <bounds x="440" y="107" width="38" height="49" /> + </element> + + <!-- SEND KÖ --> + <element name="tx_digit0" ref="digit"> + <bounds x="522" y="107" width="38" height="49" /> + </element> + + <element name="tx_digit1" ref="digit"> + <bounds x="578" y="107" width="38" height="49" /> + </element> + + <!-- TID --> + <element name="time_led" ref="time_led"> + <bounds x="658" y="99" width="29" height="14" /> + </element> + + <!-- DAT --> + <element name="date_led" ref="date_led"> + <bounds x="659" y="123" width="29" height="14" /> + </element> + + <!-- ÅR --> + <element name="year_led" ref="year_led"> + <bounds x="659" y="147" width="22" height="14" /> + </element> + + <!-- MINNES VARNING --> + <element name="mem_led" ref="red_led"> + <bounds x="114" y="204" width="16" height="16" /> + </element> + + <!-- OBS --> + <element name="obs_led" ref="red_led"> + <bounds x="205" y="204" width="16" height="16" /> + </element> + + <!-- SKRIV --> + <element name="write_led" ref="green_led"> + <bounds x="298" y="204" width="16" height="16" /> + </element> + + <!-- LOG --> + <element name="log_led" ref="green_led"> + <bounds x="388" y="204" width="16" height="16" /> + </element> + + <!-- KÖ --> + <element name="queue_led" ref="green_led"> + <bounds x="479" y="204" width="16" height="16" /> + </element> + + <!-- ALLA --> + <element name="all_led" ref="green_led"> + <bounds x="571" y="204" width="16" height="16" /> + </element> + + <!-- SKRIV --> + <element ref="button" inputtag="BTN" inputmask="0x01"> + <bounds x="275" y="262" width="54" height="54" /> + </element> + + <!-- ALLA --> + <element ref="button" inputtag="BTN" inputmask="0x02"> + <bounds x="556" y="261" width="54" height="54" /> + </element> + + <!-- KLOCK --> + <element ref="button" inputtag="BTN" inputmask="0x04"> + <bounds x="648" y="263" width="54" height="54" /> + </element> + </view> +</mamelayout> diff --git a/src/emu/layout/triphsxs.lay b/src/emu/layout/triphsxs.lay index 214b5ae87f0..4d7c0733956 100644 --- a/src/emu/layout/triphsxs.lay +++ b/src/emu/layout/triphsxs.lay @@ -1,4 +1,7 @@ <?xml version="1.0"?> +<!-- +license:CC0-1.0 +--> <mamelayout version="2"> <view name="Triple Side-by-Side"> <screen index="0"> diff --git a/src/emu/machine.cpp b/src/emu/machine.cpp index 68bf7dc74d9..a77140dba16 100644 --- a/src/emu/machine.cpp +++ b/src/emu/machine.cpp @@ -2,93 +2,44 @@ // copyright-holders:Aaron Giles /*************************************************************************** - machine.c + machine.cpp Controls execution of the core MAME system. -**************************************************************************** - - Since there has been confusion in the past over the order of - initialization and other such things, here it is, all spelled out - as of January, 2008: - - main() - - does platform-specific init - - calls mame_execute() [mame.c] - - mame_execute() [mame.c] - - calls mame_validitychecks() [validity.c] to perform validity checks on all compiled drivers - - begins resource tracking (level 1) - - calls create_machine [mame.c] to initialize the running_machine structure - - calls init_machine() [mame.c] - - init_machine() [mame.c] - - calls fileio_init() [fileio.c] to initialize file I/O info - - calls config_init() [config.c] to initialize configuration system - - calls input_init() [input.c] to initialize the input system - - calls output_init() [output.c] to initialize the output system - - calls state_init() [state.c] to initialize save state system - - calls state_save_allow_registration() [state.c] to allow registrations - - calls palette_init() [palette.c] to initialize palette system - - calls render_init() [render.c] to initialize the rendering system - - calls ui_init() [ui.c] to initialize the user interface - - calls generic_machine_init() [machine/generic.c] to initialize generic machine structures - - calls timer_init() [timer.c] to reset the timer system - - calls osd_init() [osdepend.h] to do platform-specific initialization - - calls input_port_init() [inptport.c] to set up the input ports - - calls rom_init() [romload.c] to load the game's ROMs - - calls memory_init() [memory.c] to process the game's memory maps - - calls the driver's DRIVER_INIT callback - - calls device_list_start() [devintrf.c] to start any devices - - calls video_init() [video.c] to start the video system - - calls tilemap_init() [tilemap.c] to start the tilemap system - - calls crosshair_init() [crsshair.c] to configure the crosshairs - - calls sound_init() [sound.c] to start the audio system - - calls debugger_init() [debugger.c] to set up the debugger - - calls the driver's MACHINE_START, SOUND_START, and VIDEO_START callbacks - - calls cheat_init() [cheat.c] to initialize the cheat system - - calls image_init() [image.c] to initialize the image system - - - calls config_load_settings() [config.c] to load the configuration file - - calls nvram_load [machine/generic.c] to load NVRAM - - calls ui_display_startup_screens() [ui.c] to display the startup screens - - begins resource tracking (level 2) - - calls soft_reset() [mame.c] to reset all systems - - -------------------( at this point, we're up and running )---------------------- - - - calls scheduler->timeslice() [schedule.c] over and over until we exit - - ends resource tracking (level 2), freeing all auto_mallocs and timers - - calls the nvram_save() [machine/generic.c] to save NVRAM - - calls config_save_settings() [config.c] to save the game's configuration - - calls all registered exit routines [mame.c] - - ends resource tracking (level 1), freeing all auto_mallocs and timers - - - exits the program - ***************************************************************************/ #include "emu.h" -#include "emuopts.h" -#include "osdepend.h" + #include "config.h" -#include "debugger.h" -#include "render.h" -#include "uiinput.h" #include "crsshair.h" -#include "unzip.h" -#include "debug/debugvw.h" #include "debug/debugcpu.h" +#include "debug/debugvw.h" +#include "debugger.h" #include "dirtc.h" +#include "emuopts.h" +#include "fileio.h" +#include "http.h" #include "image.h" +#include "main.h" +#include "natkeyboard.h" #include "network.h" +#include "render.h" #include "romload.h" #include "tilemap.h" +#include "uiinput.h" + #include "ui/uimain.h" -#include <time.h> + +#include "corestr.h" +#include "unzip.h" + +#include "osdepend.h" + #include <rapidjson/writer.h> #include <rapidjson/stringbuffer.h> +#include <ctime> + #if defined(__EMSCRIPTEN__) #include <emscripten.h> #endif @@ -109,38 +60,33 @@ osd_interface &running_machine::osd() const //------------------------------------------------- running_machine::running_machine(const machine_config &_config, machine_manager &manager) - : m_side_effects_disabled(0), - debug_flags(0), - m_config(_config), - m_system(_config.gamedrv()), - m_manager(manager), - m_current_phase(machine_phase::PREINIT), - m_paused(false), - m_hard_reset_pending(false), - m_exit_pending(false), - m_soft_reset_timer(nullptr), - m_rand_seed(0x9d14abd7), - m_ui_active(_config.options().ui_active()), - m_basename(_config.gamedrv().name), - m_sample_rate(_config.options().sample_rate()), - m_saveload_schedule(saveload_schedule::NONE), - m_saveload_schedule_time(attotime::zero), - m_saveload_searchpath(nullptr), - - m_save(*this), - m_memory(*this), - m_ioport(*this), - m_parameters(*this), - m_scheduler(*this), - m_dummy_space(_config, "dummy_space", &root_device(), 0) + : m_side_effects_disabled(0) + , debug_flags(0) + , m_config(_config) + , m_system(_config.gamedrv()) + , m_manager(manager) + , m_current_phase(machine_phase::PREINIT) + , m_paused(false) + , m_hard_reset_pending(false) + , m_exit_pending(false) + , m_soft_reset_timer(nullptr) + , m_rand_seed(0x9d14abd7) + , m_basename(_config.gamedrv().name) + , m_sample_rate(_config.options().sample_rate()) + , m_saveload_schedule(saveload_schedule::NONE) + , m_saveload_schedule_time(attotime::zero) + , m_saveload_searchpath(nullptr) + + , m_save(*this) + , m_memory(*this) + , m_ioport(*this) + , m_parameters(*this) + , m_scheduler(*this) { memset(&m_base_time, 0, sizeof(m_base_time)); - m_dummy_space.set_machine(*this); - m_dummy_space.config_complete(); - // set the machine on all devices - device_iterator iter(root_device()); + device_enumerator iter(root_device()); for (device_t &device : iter) device.set_machine(*this); @@ -178,7 +124,10 @@ std::string running_machine::describe_context() const } } - return std::string("(no context)"); + if (m_current_phase == machine_phase::RESET) + return std::string("(reset phase)"); + else + return std::string("(no context)"); } @@ -199,14 +148,15 @@ void running_machine::start() m_soft_reset_timer = m_scheduler.timer_alloc(timer_expired_delegate(FUNC(running_machine::soft_reset), this)); // initialize UI input - m_ui_input = make_unique_clear<ui_input_manager>(*this); + m_ui_input = std::make_unique<ui_input_manager>(*this); - // init the osd layer + // init the OSD layer m_manager.osd().init(*this); - // create the video manager + // create the video manager and UI manager m_video = std::make_unique<video_manager>(*this); m_ui = manager().create_ui(*this); + m_ui->set_startup_text("Initializing...", true); // initialize the base time (needed for doing record/playback) ::time(&m_base_time); @@ -218,11 +168,14 @@ void running_machine::start() if (newbase != 0) m_base_time = newbase; + // initialize natural keyboard support after ports have been initialized + m_natkeyboard = std::make_unique<natural_keyboard>(*this); + // initialize the streams engine before the sound devices start m_sound = std::make_unique<sound_manager>(*this); // resolve objects that can be used by memory maps - for (device_t &device : device_iterator(root_device())) + for (device_t &device : device_enumerator(root_device())) device.resolve_pre_map(); // configure the address spaces, load ROMs (which needs @@ -230,7 +183,7 @@ void running_machine::start() // needs rom bases), and finally initialize CPUs (which needs // complete address spaces). These operations must proceed in this // order - m_rom_load = make_unique_clear<rom_load_manager>(*this); + m_rom_load = std::make_unique<rom_load_manager>(*this); m_memory.initialize(); // save the random seed or save states might be broken in drivers that use the rand() method @@ -239,7 +192,7 @@ void running_machine::start() // initialize image devices m_image = std::make_unique<image_manager>(*this); m_tilemap = std::make_unique<tilemap_manager>(*this); - m_crosshair = make_unique_clear<crosshair_manager>(*this); + m_crosshair = std::make_unique<crosshair_manager>(*this); m_network = std::make_unique<network_manager>(*this); // initialize the debugger @@ -249,30 +202,60 @@ void running_machine::start() m_debugger = std::make_unique<debugger_manager>(*this); } - m_render->resolve_tags(); - manager().create_custom(*this); // resolve objects that are created by memory maps - for (device_t &device : device_iterator(root_device())) + for (device_t &device : device_enumerator(root_device())) device.resolve_post_map(); // register callbacks for the devices, then start them add_notifier(MACHINE_NOTIFY_RESET, machine_notify_delegate(&running_machine::reset_all_devices, this)); add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(&running_machine::stop_all_devices, this)); save().register_presave(save_prepost_delegate(FUNC(running_machine::presave_all_devices), this)); + m_sound->before_devices_init(); start_all_devices(); + m_sound->after_devices_init(); save().register_postload(save_prepost_delegate(FUNC(running_machine::postload_all_devices), this)); + + // save outputs created before start time + output().register_save(); + + m_render->resolve_tags(); + + // load cheat files manager().load_cheatfiles(*this); - // if we're coming in with a savegame request, process it now + // start recording movie if specified + const char *filename = options().mng_write(); + if (filename[0] != 0) + m_video->begin_recording(filename, movie_recording::format::MNG); + + filename = options().avi_write(); + if (filename[0] != 0 && !m_video->is_recording()) + m_video->begin_recording(filename, movie_recording::format::AVI); + const char *savegame = options().state(); if (savegame[0] != 0) + { + // if we're coming in with a savegame request, process it now schedule_load(savegame); - - // if we're in autosave mode, schedule a load - else if (options().autosave() && (m_system.flags & MACHINE_SUPPORTS_SAVE) != 0) - schedule_load("auto"); + } + else if (options().autosave()) + { + // if we're in autosave mode, schedule a load + // m_save.supported() won't be set until save state registrations are finalised + bool supported = true; + for (device_t &device : device_enumerator(root_device())) + { + if (device.type().emulation_flags() & device_t::flags::SAVE_UNSUPPORTED) + { + supported = false; + break; + } + } + if (supported) + schedule_load("auto"); + } manager().update_machine(); } @@ -298,26 +281,32 @@ int running_machine::run(bool quiet) if (options().log() && !quiet) { m_logfile = std::make_unique<emu_file>(OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); - osd_file::error filerr = m_logfile->open("error.log"); - if (filerr != osd_file::error::NONE) - throw emu_fatalerror("running_machine::run: unable to open log file"); + std::error_condition const filerr = m_logfile->open("error.log"); + if (filerr) + throw emu_fatalerror("running_machine::run: unable to open error.log file"); using namespace std::placeholders; add_logerror_callback(std::bind(&running_machine::logfile_callback, this, _1)); } + if (options().debug() && options().debuglog()) + { + m_debuglogfile = std::make_unique<emu_file>(OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); + std::error_condition const filerr = m_debuglogfile->open("debug.log"); + if (filerr) + throw emu_fatalerror("running_machine::run: unable to open debug.log file"); + } + // then finish setting up our local machine start(); + // disallow save state registrations starting here + m_save.allow_registration(false); + // load the configuration settings manager().before_load_settings(*this); m_configuration->load_settings(); - // disallow save state registrations starting here. - // Don't do it earlier, config load can create network - // devices with timers. - m_save.allow_registration(false); - // load the NVRAM nvram_load(); @@ -328,6 +317,8 @@ int running_machine::run(bool quiet) if (!quiet) sound().start_recording(); + m_hard_reset_pending = false; + // initialize ui lists // display the startup screens manager().ui_initialize(*this); @@ -341,8 +332,6 @@ int running_machine::run(bool quiet) export_http_api(); - m_hard_reset_pending = false; - #if defined(__EMSCRIPTEN__) // break out to our async javascript loop and halt emscripten_set_running_machine(this); @@ -351,20 +340,21 @@ int running_machine::run(bool quiet) // run the CPUs until a reset or exit while ((!m_hard_reset_pending && !m_exit_pending) || m_saveload_schedule != saveload_schedule::NONE) { - g_profiler.start(PROFILER_EXTRA); + auto profile = g_profiler.start(PROFILER_EXTRA); // execute CPUs if not paused if (!m_paused) m_scheduler.timeslice(); - // otherwise, just pump video updates through + // otherwise, just pump video updates and sound mapping updates through else + { m_video->frame_update(); + sound().mapping_update(); + } // handle save/load if (m_saveload_schedule != saveload_schedule::NONE) handle_saveload(); - - g_profiler.stop(); } m_manager.http()->clear(); @@ -377,29 +367,29 @@ int running_machine::run(bool quiet) nvram_save(); m_configuration->save_settings(); } - catch (emu_fatalerror &fatal) + catch (emu_fatalerror const &fatal) { - osd_printf_error("Fatal error: %s\n", fatal.string()); + osd_printf_error("Fatal error: %s\n", fatal.what()); error = EMU_ERR_FATALERROR; if (fatal.exitcode() != 0) error = fatal.exitcode(); } - catch (emu_exception &) + catch (emu_exception const &) { osd_printf_error("Caught unhandled emulator exception\n"); error = EMU_ERR_FATALERROR; } - catch (binding_type_exception &btex) + catch (binding_type_exception const &btex) { - osd_printf_error("Error performing a late bind of type %s to %s\n", btex.m_actual_type.name(), btex.m_target_type.name()); + osd_printf_error("Error performing a late bind of function expecting type %s to instance of type %s\n", btex.target_type().name(), btex.actual_type().name()); error = EMU_ERR_FATALERROR; } - catch (tag_add_exception &aex) + catch (tag_add_exception const &aex) { osd_printf_error("Tag '%s' already exists in tagged map\n", aex.tag()); error = EMU_ERR_FATALERROR; } - catch (std::exception &ex) + catch (std::exception const &ex) { osd_printf_error("Caught unhandled %s exception: %s\n", typeid(ex).name(), ex.what()); error = EMU_ERR_FATALERROR; @@ -435,7 +425,7 @@ void running_machine::schedule_exit() m_scheduler.eat_all_cycles(); // if we're autosaving on exit, schedule a save as well - if (options().autosave() && (m_system.flags & MACHINE_SUPPORTS_SAVE) && this->time() > attotime::zero) + if (options().autosave() && m_save.supported() && (this->time() > attotime::zero)) schedule_save("auto"); } @@ -474,7 +464,7 @@ void running_machine::schedule_soft_reset() //------------------------------------------------- // get_statename - allow to specify a subfolder of // the state directory for state loading/saving, -// very useful for MESS and consoles or computers +// very useful for consoles or computers // where you can have separate folders for diff // software //------------------------------------------------- @@ -499,7 +489,7 @@ std::string running_machine::get_statename(const char *option) const if (pos != -1) { // if more %d are found, revert to default and ignore them all - if (statename_str.find(statename_dev.c_str(), pos + 3) != -1) + if (statename_str.find(statename_dev, pos + 3) != -1) statename_str.assign("%g"); // else if there is a single %d, try to create the correct snapname else @@ -507,21 +497,10 @@ std::string running_machine::get_statename(const char *option) const int name_found = 0; // find length of the device name - int end1 = statename_str.find("/", pos + 3); - int end2 = statename_str.find("%", pos + 3); - int end; - - if ((end1 != -1) && (end2 != -1)) - end = std::min(end1, end2); - else if (end1 != -1) - end = end1; - else if (end2 != -1) - end = end2; - else + int end = statename_str.find_first_not_of("abcdefghijklmnopqrstuvwxyz1234567890", pos + 3); + if (end == -1) end = statename_str.length(); - if (end - pos < 3) - fatalerror("Something very wrong is going on!!!\n"); // copy the device name to an std::string std::string devname_str; @@ -529,7 +508,7 @@ std::string running_machine::get_statename(const char *option) const //printf("check template: %s\n", devname_str.c_str()); // verify that there is such a device for this system - for (device_image_interface &image : image_interface_iterator(root_device())) + for (device_image_interface &image : image_interface_enumerator(root_device())) { // get the device name std::string tempdevname(image.brief_instance_name()); @@ -543,7 +522,7 @@ std::string running_machine::get_statename(const char *option) const std::string filename(image.basename_noext()); // setup snapname and remove the %d_ - strreplace(statename_str, devname_str.c_str(), filename.c_str()); + strreplace(statename_str, devname_str, filename); statename_str.erase(pos, 3); //printf("check image: %s\n", filename.c_str()); @@ -633,10 +612,10 @@ void running_machine::schedule_save(std::string &&filename) // immediate_save - save state. //------------------------------------------------- -void running_machine::immediate_save(const char *filename) +void running_machine::immediate_save(std::string_view filename) { // specify the filename to save or load - set_saveload_filename(filename); + set_saveload_filename(std::string(filename)); // set up some parameters for handle_saveload() m_saveload_schedule = saveload_schedule::SAVE; @@ -670,10 +649,10 @@ void running_machine::schedule_load(std::string &&filename) // immediate_load - load state. //------------------------------------------------- -void running_machine::immediate_load(const char *filename) +void running_machine::immediate_load(std::string_view filename) { // specify the filename to save or load - set_saveload_filename(filename); + set_saveload_filename(std::string(filename)); // set up some parameters for handle_saveload() m_saveload_schedule = saveload_schedule::LOAD; @@ -856,7 +835,7 @@ void running_machine::current_datetime(system_time &systime) void running_machine::set_rtc_datetime(const system_time &systime) { - for (device_rtc_interface &rtc : rtc_interface_iterator(root_device())) + for (device_rtc_interface &rtc : rtc_interface_enumerator(root_device())) if (rtc.has_battery()) rtc.set_current_time(systime); } @@ -901,6 +880,7 @@ void running_machine::handle_saveload() if (!m_saveload_pending_file.empty()) { const char *const opname = (m_saveload_schedule == saveload_schedule::LOAD) ? "load" : "save"; + const char *const preposname = (m_saveload_schedule == saveload_schedule::LOAD) ? "from" : "to"; // if there are anonymous timers, we can't save just yet, and we can't load yet either // because the timers might overwrite data we have loaded @@ -908,7 +888,7 @@ void running_machine::handle_saveload() { // if more than a second has passed, we're probably screwed if ((this->time() - m_saveload_schedule_time) > attotime::from_seconds(1)) - popmessage("Unable to %s due to pending anonymous timers. See error.log for details.", opname); + popmessage("Error: Unable to %s state %s %s due to pending anonymous timers. See error.log for details.", opname, preposname, m_saveload_pending_file); else return; // return without cancelling the operation } @@ -919,41 +899,38 @@ void running_machine::handle_saveload() // open the file emu_file file(m_saveload_searchpath ? m_saveload_searchpath : "", openflags); auto const filerr = file.open(m_saveload_pending_file); - if (filerr == osd_file::error::NONE) + if (!filerr) { - const char *const opnamed = (m_saveload_schedule == saveload_schedule::LOAD) ? "loaded" : "saved"; - // read/write the save state save_error saverr = (m_saveload_schedule == saveload_schedule::LOAD) ? m_save.read_file(file) : m_save.write_file(file); // handle the result switch (saverr) { - case STATERR_ILLEGAL_REGISTRATIONS: - popmessage("Error: Unable to %s state due to illegal registrations. See error.log for details.", opname); - break; - case STATERR_INVALID_HEADER: - popmessage("Error: Unable to %s state due to an invalid header. Make sure the save state is correct for this machine.", opname); + popmessage("Error: Unable to %s state %s %s due to an invalid header. Make sure the save state is correct for this system.", opname, preposname, m_saveload_pending_file); break; case STATERR_READ_ERROR: - popmessage("Error: Unable to %s state due to a read error (file is likely corrupt).", opname); + popmessage("Error: Unable to %s state %s %s due to a read error (file is likely corrupt).", opname, preposname, m_saveload_pending_file); break; case STATERR_WRITE_ERROR: - popmessage("Error: Unable to %s state due to a write error. Verify there is enough disk space.", opname); + popmessage("Error: Unable to %s state %s %s due to a write error. Verify there is enough disk space.", opname, preposname, m_saveload_pending_file); break; case STATERR_NONE: - if (!(m_system.flags & MACHINE_SUPPORTS_SAVE)) - popmessage("State successfully %s.\nWarning: Save states are not officially supported for this machine.", opnamed); + { + const char *const opnamed = (m_saveload_schedule == saveload_schedule::LOAD) ? "Loaded" : "Saved"; + if (!m_save.supported()) + popmessage("%s state %s %s.\nWarning: Save states are not officially supported for this system.", opnamed, preposname, m_saveload_pending_file); else - popmessage("State successfully %s.", opnamed); + popmessage("%s state %s %s.", opnamed, preposname, m_saveload_pending_file); break; + } default: - popmessage("Error: Unknown error during state %s.", opnamed); + popmessage("Error: Unknown error during %s state %s %s.", opname, preposname, m_saveload_pending_file); break; } @@ -961,11 +938,15 @@ void running_machine::handle_saveload() if (saverr != STATERR_NONE && m_saveload_schedule == saveload_schedule::SAVE) file.remove_on_close(); } - else if (openflags == OPEN_FLAG_READ && filerr == osd_file::error::NOT_FOUND) + else if ((openflags == OPEN_FLAG_READ) && (std::errc::no_such_file_or_directory == filerr)) + { // attempt to load a non-existent savestate, report empty slot - popmessage("Error: No savestate file to load.", opname); + popmessage("Error: Load state file %s not found.", m_saveload_pending_file); + } else - popmessage("Error: Failed to open file for %s operation.", opname); + { + popmessage("Error: Failed to open %s for %s state operation.", m_saveload_pending_file, opname); + } } } @@ -981,7 +962,7 @@ void running_machine::handle_saveload() // of the system //------------------------------------------------- -void running_machine::soft_reset(void *ptr, s32 param) +void running_machine::soft_reset(s32 param) { logerror("Soft reset\n"); @@ -1012,49 +993,56 @@ void running_machine::logfile_callback(const char *buffer) //------------------------------------------------- +// steal_debuglogfile - relinquish ownership of +// the debug.log file +//------------------------------------------------- + +std::unique_ptr<emu_file> running_machine::steal_debuglogfile() +{ + return std::move(m_debuglogfile); +} + + +//------------------------------------------------- // start_all_devices - start any unstarted devices //------------------------------------------------- void running_machine::start_all_devices() { - m_dummy_space.start(); - // iterate through the devices int last_failed_starts = -1; - while (last_failed_starts != 0) + do { // iterate over all devices int failed_starts = 0; - for (device_t &device : device_iterator(root_device())) + for (device_t &device : device_enumerator(root_device())) if (!device.started()) { // attempt to start the device, catching any expected exceptions try { // if the device doesn't have a machine yet, set it first - if (device.m_machine == nullptr) + if (!device.m_machine) device.set_machine(*this); // now start the device osd_printf_verbose("Starting %s '%s'\n", device.name(), device.tag()); device.start(); } - - // handle missing dependencies by moving the device to the end - catch (device_missing_dependencies &) + catch (device_missing_dependencies const &) { - // if we're the end, fail + // handle missing dependencies by moving the device to the end osd_printf_verbose(" (missing dependencies; rescheduling)\n"); failed_starts++; } } - // each iteration should reduce the number of failed starts; error if - // this doesn't happen + // each iteration should reduce the number of failed starts; error if this doesn't happen if (failed_starts == last_failed_starts) throw emu_fatalerror("Circular dependency in device startup!"); last_failed_starts = failed_starts; } + while (last_failed_starts); } @@ -1082,7 +1070,7 @@ void running_machine::stop_all_devices() debugger().cpu().comment_save(); // iterate over devices and stop them - for (device_t &device : device_iterator(root_device())) + for (device_t &device : device_enumerator(root_device())) device.stop(); } @@ -1094,7 +1082,7 @@ void running_machine::stop_all_devices() void running_machine::presave_all_devices() { - for (device_t &device : device_iterator(root_device())) + for (device_t &device : device_enumerator(root_device())) device.pre_save(); } @@ -1106,7 +1094,7 @@ void running_machine::presave_all_devices() void running_machine::postload_all_devices() { - for (device_t &device : device_iterator(root_device())) + for (device_t &device : device_enumerator(root_device())) device.post_load(); } @@ -1159,12 +1147,13 @@ std::string running_machine::nvram_filename(device_t &device) const void running_machine::nvram_load() { - for (device_nvram_interface &nvram : nvram_interface_iterator(root_device())) + for (device_nvram_interface &nvram : nvram_interface_enumerator(root_device())) { emu_file file(options().nvram_directory(), OPEN_FLAG_READ); - if (file.open(nvram_filename(nvram.device())) == osd_file::error::NONE) + if (nvram.nvram_backup_enabled() && !file.open(nvram_filename(nvram.device()))) { - nvram.nvram_load(file); + if (!nvram.nvram_load(file)) + osd_printf_error("Error reading NVRAM file %s\n", file.filename()); file.close(); } else @@ -1179,13 +1168,26 @@ void running_machine::nvram_load() void running_machine::nvram_save() { - for (device_nvram_interface &nvram : nvram_interface_iterator(root_device())) + for (device_nvram_interface &nvram : nvram_interface_enumerator(root_device())) { - emu_file file(options().nvram_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); - if (file.open(nvram_filename(nvram.device())) == osd_file::error::NONE) + if (nvram.nvram_can_save()) { - nvram.nvram_save(file); - file.close(); + emu_file file(options().nvram_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); + if (!file.open(nvram_filename(nvram.device()))) + { + bool error = false; + + if (!nvram.nvram_save(file)) + { + error = true; + osd_printf_error("Error writing NVRAM file %s\n", file.filename()); + } + + // close and perhaps delete the file + if (error || file.size() == 0) + file.remove_on_close(); + file.close(); + } } } } @@ -1200,7 +1202,7 @@ void running_machine::popup_clear() const ui().popup_time(0, " "); } -void running_machine::popup_message(util::format_argument_pack<std::ostream> const &args) const +void running_machine::popup_message(util::format_argument_pack<char> const &args) const { std::string const temp(string_format(args)); ui().popup_time(temp.length() / 40 + 2, "%s", temp); @@ -1226,7 +1228,7 @@ running_machine::notifier_callback_item::notifier_callback_item(machine_notify_d //------------------------------------------------- running_machine::logerror_callback_item::logerror_callback_item(logerror_callback func) - : m_func(func) + : m_func(std::move(func)) { } @@ -1244,7 +1246,7 @@ void running_machine::export_http_api() writer.Key("devices"); writer.StartArray(); - device_iterator iter(this->root_device()); + device_enumerator iter(this->root_device()); for (device_t &device : iter) writer.String(device.tag()); @@ -1311,51 +1313,6 @@ void system_time::full_time::set(struct tm &t) //************************************************************************** -// DUMMY ADDRESS SPACE -//************************************************************************** - -READ8_MEMBER(dummy_space_device::read) -{ - throw emu_fatalerror("Attempted to read from generic address space (offs %X)\n", offset); -} - -WRITE8_MEMBER(dummy_space_device::write) -{ - throw emu_fatalerror("Attempted to write to generic address space (offs %X = %02X)\n", offset, data); -} - -void dummy_space_device::dummy(address_map &map) -{ - map(0x00000000, 0xffffffff).rw(FUNC(dummy_space_device::read), FUNC(dummy_space_device::write)); -} - -DEFINE_DEVICE_TYPE(DUMMY_SPACE, dummy_space_device, "dummy_space", "Dummy Space") - -dummy_space_device::dummy_space_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : - device_t(mconfig, DUMMY_SPACE, tag, owner, clock), - device_memory_interface(mconfig, *this), - m_space_config("dummy", ENDIANNESS_LITTLE, 8, 32, 0, address_map_constructor(FUNC(dummy_space_device::dummy), this)) -{ -} - -void dummy_space_device::device_start() -{ -} - -//------------------------------------------------- -// memory_space_config - return a description of -// any address spaces owned by this device -//------------------------------------------------- - -device_memory_interface::space_config_vector dummy_space_device::memory_space_config() const -{ - return space_config_vector { - std::make_pair(0, &m_space_config) - }; -} - - -//************************************************************************** // JAVASCRIPT PORT-SPECIFIC //************************************************************************** @@ -1367,7 +1324,7 @@ void running_machine::emscripten_main_loop() { running_machine *machine = emscripten_running_machine; - g_profiler.start(PROFILER_EXTRA); + auto profile = g_profiler.start(PROFILER_EXTRA); // execute CPUs if not paused if (!machine->m_paused) @@ -1400,8 +1357,6 @@ void running_machine::emscripten_main_loop() { emscripten_cancel_main_loop(); } - - g_profiler.stop(); } void running_machine::emscripten_set_running_machine(running_machine *machine) diff --git a/src/emu/machine.h b/src/emu/machine.h index 8d151bc0c92..7b4efec70da 100644 --- a/src/emu/machine.h +++ b/src/emu/machine.h @@ -19,7 +19,7 @@ #include <functional> -#include <time.h> +#include <ctime> //************************************************************************** // CONSTANTS @@ -51,29 +51,11 @@ enum machine_notification // debug flags constexpr int DEBUG_FLAG_ENABLED = 0x00000001; // debugging is enabled constexpr int DEBUG_FLAG_CALL_HOOK = 0x00000002; // CPU cores must call instruction hook -constexpr int DEBUG_FLAG_WPR_PROGRAM = 0x00000010; // watchpoints are enabled for PROGRAM memory reads -constexpr int DEBUG_FLAG_WPR_DATA = 0x00000020; // watchpoints are enabled for DATA memory reads -constexpr int DEBUG_FLAG_WPR_IO = 0x00000040; // watchpoints are enabled for IO memory reads -constexpr int DEBUG_FLAG_WPW_PROGRAM = 0x00000100; // watchpoints are enabled for PROGRAM memory writes -constexpr int DEBUG_FLAG_WPW_DATA = 0x00000200; // watchpoints are enabled for DATA memory writes -constexpr int DEBUG_FLAG_WPW_IO = 0x00000400; // watchpoints are enabled for IO memory writes constexpr int DEBUG_FLAG_OSD_ENABLED = 0x00001000; // The OSD debugger is enabled //************************************************************************** -// MACROS -//************************************************************************** - -// global allocation helpers -#define auto_alloc(m, t) pool_alloc(static_cast<running_machine &>(m).respool(), t) -#define auto_alloc_clear(m, t) pool_alloc_clear(static_cast<running_machine &>(m).respool(), t) -#define auto_alloc_array(m, t, c) pool_alloc_array(static_cast<running_machine &>(m).respool(), t, c) -#define auto_alloc_array_clear(m, t, c) pool_alloc_array_clear(static_cast<running_machine &>(m).respool(), t, c) -#define auto_free(m, v) pool_free(static_cast<running_machine &>(m).respool(), v) - - -//************************************************************************** // TYPE DEFINITIONS //************************************************************************** @@ -109,33 +91,6 @@ public: -// ======================> dummy_space_device - -// a dummy address space for passing to handlers outside of the memory system - -class dummy_space_device : public device_t, - public device_memory_interface -{ -public: - dummy_space_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); - - DECLARE_READ8_MEMBER(read); - DECLARE_WRITE8_MEMBER(write); - - void dummy(address_map &map); -protected: - // device-level overrides - virtual void device_start() override; - - // device_memory_interface overrides - virtual space_config_vector memory_space_config() const override; - -private: - const address_space_config m_space_config; -}; - - - // ======================> running_machine typedef delegate<void ()> machine_notify_delegate; @@ -152,9 +107,6 @@ class running_machine typedef std::function<void (const char*)> logerror_callback; - // must be at top of member variables - resource_pool m_respool; // pool of resources for this machine - public: // construction/destruction running_machine(const machine_config &config, machine_manager &manager); @@ -166,7 +118,6 @@ public: const game_driver &system() const { return m_system; } osd_interface &osd() const; machine_manager &manager() const { return m_manager; } - resource_pool &respool() { return m_respool; } device_scheduler &scheduler() { return m_scheduler; } save_manager &save() { return m_save; } memory_manager &memory() { return m_memory; } @@ -177,7 +128,7 @@ public: sound_manager &sound() const { assert(m_sound != nullptr); return *m_sound; } video_manager &video() const { assert(m_video != nullptr); return *m_video; } network_manager &network() const { assert(m_network != nullptr); return *m_network; } - bookkeeping_manager &bookkeeping() const { assert(m_network != nullptr); return *m_bookkeeping; } + bookkeeping_manager &bookkeeping() const { assert(m_bookkeeping != nullptr); return *m_bookkeeping; } configuration_manager &configuration() const { assert(m_configuration != nullptr); return *m_configuration; } output_manager &output() const { assert(m_output != nullptr); return *m_output; } ui_manager &ui() const { assert(m_ui != nullptr); return *m_ui; } @@ -188,14 +139,13 @@ public: tilemap_manager &tilemap() const { assert(m_tilemap != nullptr); return *m_tilemap; } debug_view_manager &debug_view() const { assert(m_debug_view != nullptr); return *m_debug_view; } debugger_manager &debugger() const { assert(m_debugger != nullptr); return *m_debugger; } - driver_device *driver_data() const { return &downcast<driver_device &>(root_device()); } + natural_keyboard &natkeyboard() noexcept { assert(m_natkeyboard != nullptr); return *m_natkeyboard; } template <class DriverClass> DriverClass *driver_data() const { return &downcast<DriverClass &>(root_device()); } machine_phase phase() const { return m_current_phase; } bool paused() const { return m_paused || (m_current_phase != machine_phase::RUNNING); } bool exit_pending() const { return m_exit_pending; } bool hard_reset_pending() const { return m_hard_reset_pending; } - bool ui_active() const { return m_ui_active; } - const char *basename() const { return m_basename.c_str(); } + const std::string &basename() const { return m_basename; } int sample_rate() const { return m_sample_rate; } bool save_or_load_pending() const { return !m_saveload_pending_file.empty(); } @@ -206,13 +156,12 @@ public: // additional helpers emu_options &options() const { return m_config.options(); } - attotime time() const { return m_scheduler.time(); } + attotime time() const noexcept { return m_scheduler.time(); } bool scheduled_event_pending() const { return m_exit_pending || m_hard_reset_pending; } bool allow_logging() const { return !m_logerror_list.empty(); } // fetch items by name - [[deprecated("absolute tag lookup; use subdevice or finder instead")]] inline device_t *device(const char *tag) const { return root_device().subdevice(tag); } - template <class DeviceClass> [[deprecated("absolute tag lookup; use subdevice or finder instead")]] inline DeviceClass *device(const char *tag) { return downcast<DeviceClass *>(device(tag)); } + template <class DeviceClass> [[deprecated("absolute tag lookup; use subdevice or finder instead")]] inline DeviceClass *device(const char *tag) { return downcast<DeviceClass *>(root_device().subdevice(tag)); } // immediate operations int run(bool quiet); @@ -222,13 +171,12 @@ public: void add_notifier(machine_notification event, machine_notify_delegate callback, bool first = false); void call_notifiers(machine_notification which); void add_logerror_callback(logerror_callback callback); - void set_ui_active(bool active) { m_ui_active = active; } void debug_break(); void export_http_api(); // TODO: Do saves and loads still require scheduling? - void immediate_save(const char *filename); - void immediate_load(const char *filename); + void immediate_save(std::string_view filename); + void immediate_load(std::string_view filename); // rewind operations bool rewind_capture(); @@ -248,7 +196,6 @@ public: void set_rtc_datetime(const system_time &systime); // misc - address_space &dummy_space() const { return m_dummy_space.space(AS_PROGRAM); } void popmessage() const { popmessage(static_cast<char const *>(nullptr)); } template <typename Format, typename... Params> void popmessage(Format &&fmt, Params &&... args) const; template <typename Format, typename... Params> void logerror(Format &&fmt, Params &&... args) const; @@ -265,6 +212,10 @@ private: public: // debugger-related information u32 debug_flags; // the current debug flags + bool debug_enabled() { return (debug_flags & DEBUG_FLAG_ENABLED) != 0; } + + // used by debug_console to take ownership of the debug.log file + std::unique_ptr<emu_file> steal_debuglogfile(); private: class side_effects_disabler { @@ -295,12 +246,12 @@ private: void start(); void set_saveload_filename(std::string &&filename); void handle_saveload(); - void soft_reset(void *ptr = nullptr, s32 param = 0); + void soft_reset(s32 param = 0); std::string nvram_filename(device_t &device) const; void nvram_load(); void nvram_save(); void popup_clear() const; - void popup_message(util::format_argument_pack<std::ostream> const &args) const; + void popup_message(util::format_argument_pack<char> const &args) const; // internal callbacks void logfile_callback(const char *buffer); @@ -333,6 +284,7 @@ private: std::unique_ptr<image_manager> m_image; // internal data from image.cpp std::unique_ptr<rom_load_manager> m_rom_load; // internal data from romload.cpp std::unique_ptr<debugger_manager> m_debugger; // internal data from debugger.cpp + std::unique_ptr<natural_keyboard> m_natkeyboard; // internal data from natkeyboard.cpp // system state machine_phase m_current_phase; // current execution phase @@ -343,11 +295,11 @@ private: // misc state u32 m_rand_seed; // current random number seed - bool m_ui_active; // ui active or not (useful for games / systems with keyboard inputs) time_t m_base_time; // real time at initial emulation time std::string m_basename; // basename used for game-related paths int m_sample_rate; // the digital audio sample rate - std::unique_ptr<emu_file> m_logfile; // pointer to the active log file + std::unique_ptr<emu_file> m_logfile; // pointer to the active error.log file + std::unique_ptr<emu_file> m_debuglogfile; // pointer to the active debug.log file // load/save management enum class saveload_schedule @@ -394,9 +346,6 @@ private: // string formatting buffer mutable util::ovectorstream m_string_buffer; - // configuration state - dummy_space_device m_dummy_space; - #if defined(__EMSCRIPTEN__) private: static running_machine *emscripten_running_machine; @@ -448,7 +397,7 @@ inline void running_machine::logerror(Format &&fmt, Params &&... args) const // process only if there is a target if (allow_logging()) { - g_profiler.start(PROFILER_LOGERROR); + auto profile = g_profiler.start(PROFILER_LOGERROR); // dump to the buffer m_string_buffer.clear(); @@ -457,9 +406,7 @@ inline void running_machine::logerror(Format &&fmt, Params &&... args) const m_string_buffer.put('\0'); strlog(&m_string_buffer.vec()[0]); - - g_profiler.stop(); } } -#endif /* MAME_EMU_MACHINE_H */ +#endif // MAME_EMU_MACHINE_H diff --git a/src/emu/main.cpp b/src/emu/main.cpp index 9255d9f7c67..12cde5cf299 100644 --- a/src/emu/main.cpp +++ b/src/emu/main.cpp @@ -2,24 +2,39 @@ // copyright-holders:Nicola Salmoria, Aaron Giles /*************************************************************************** -main.cpp + main.cpp -Controls execution of the core MAME system. + Controls execution of the core MAME system. ***************************************************************************/ #include "emu.h" +#include "main.h" + #include "emuopts.h" +#include "http.h" + -machine_manager::machine_manager(emu_options& options, osd_interface& osd) - : m_osd(osd), +machine_manager::machine_manager(emu_options& options, osd_interface& osd) : + m_osd(osd), m_options(options), m_machine(nullptr) { } + +machine_manager::~machine_manager() +{ +} + + void machine_manager::start_http_server() { m_http = std::make_unique<http_manager>(options().http(), options().http_port(), options().http_root()); } + +http_manager *machine_manager::http() +{ + return m_http.get(); +} diff --git a/src/emu/main.h b/src/emu/main.h index a62457a1c75..a62ccb46211 100644 --- a/src/emu/main.h +++ b/src/emu/main.h @@ -6,36 +6,34 @@ Controls execution of the core emulator system. ***************************************************************************/ +#ifndef MAME_EMU_MAIN_H +#define MAME_EMU_MAIN_H #pragma once -#ifndef __EMU_H__ -#error Dont include this file directly; include emu.h instead. -#endif +#include "emufwd.h" -#ifndef MAME_EMU_MAIN_H -#define MAME_EMU_MAIN_H +#include <map> +#include <memory> +#include <string> +#include <vector> -#include <thread> -#include <time.h> //************************************************************************** // CONSTANTS //************************************************************************** -enum -{ - EMU_ERR_NONE = 0, /* no error */ - EMU_ERR_FAILED_VALIDITY = 1, /* failed validity checks */ - EMU_ERR_MISSING_FILES = 2, /* missing files */ - EMU_ERR_FATALERROR = 3, /* some other fatal error */ - EMU_ERR_DEVICE = 4, /* device initialization error (MESS-specific) */ - EMU_ERR_NO_SUCH_GAME = 5, /* game was specified but doesn't exist */ - EMU_ERR_INVALID_CONFIG = 6, /* some sort of error in configuration */ - EMU_ERR_IDENT_NONROMS = 7, /* identified all non-ROM files */ - EMU_ERR_IDENT_PARTIAL = 8, /* identified some files but not all */ - EMU_ERR_IDENT_NONE = 9 /* identified no files */ -}; +constexpr int EMU_ERR_NONE = 0; // no error +constexpr int EMU_ERR_FAILED_VALIDITY = 1; // failed validity checks +constexpr int EMU_ERR_MISSING_FILES = 2; // missing files +constexpr int EMU_ERR_FATALERROR = 3; // some other fatal error +constexpr int EMU_ERR_DEVICE = 4; // device initialization error +constexpr int EMU_ERR_NO_SUCH_SYSTEM = 5; // system was specified but doesn't exist +constexpr int EMU_ERR_INVALID_CONFIG = 6; // some sort of error in configuration +constexpr int EMU_ERR_IDENT_NONROMS = 7; // identified all non-ROM files +constexpr int EMU_ERR_IDENT_PARTIAL = 8; // identified some files but not all +constexpr int EMU_ERR_IDENT_NONE = 9; // identified no files + //************************************************************************** // TYPE DEFINITIONS @@ -45,35 +43,36 @@ class emulator_info { public: // construction/destruction - emulator_info() {}; - - static const char * get_appname(); - static const char * get_appname_lower(); - static const char * get_configname(); - static const char * get_copyright(); - static const char * get_copyright_info(); - static const char * get_bare_build_version(); - static const char * get_build_version(); - static void display_ui_chooser(running_machine& machine); + emulator_info() = default; + + static const char *get_appname(); + static const char *get_appname_lower(); + static const char *get_configname(); + static const char *get_copyright(); + static const char *get_copyright_info(); + static const char *get_bare_build_version(); + static const char *get_build_version(); + static void display_ui_chooser(running_machine &machine); static int start_frontend(emu_options &options, osd_interface &osd, std::vector<std::string> &args); static int start_frontend(emu_options &options, osd_interface &osd, int argc, char *argv[]); - static void draw_user_interface(running_machine& machine); + static bool draw_user_interface(running_machine& machine); static void periodic_check(); static bool frame_hook(); - static void sound_hook(); - static void layout_file_cb(util::xml::data_node const &layout); + static void sound_hook(const std::map<std::string, std::vector<std::pair<const float *, int>>> &sound); // Can't use sound_stream::sample_t sadly + static void layout_script_cb(layout_file &file, const char *script); static bool standalone(); }; class machine_manager { - DISABLE_COPYING(machine_manager); protected: // construction/destruction - machine_manager(emu_options& options, osd_interface& osd); + machine_manager(emu_options &options, osd_interface &osd); + machine_manager(machine_manager const &) = delete; + public: - virtual ~machine_manager() { } + virtual ~machine_manager(); osd_interface &osd() const { return m_osd; } emu_options &options() const { return m_options; } @@ -90,7 +89,7 @@ public: virtual void update_machine() { } - http_manager *http() { return m_http.get(); } + http_manager *http(); void start_http_server(); protected: diff --git a/src/emu/mconfig.cpp b/src/emu/mconfig.cpp index e9533aff7d8..275a4af46ae 100644 --- a/src/emu/mconfig.cpp +++ b/src/emu/mconfig.cpp @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles /*************************************************************************** - mconfig.c + mconfig.cpp Machine configuration macros and functions. @@ -12,8 +12,10 @@ #include "emuopts.h" #include "screen.h" -#include <ctype.h> +#include <cctype> + #include <cstring> +#include <numeric> //************************************************************************** @@ -37,18 +39,19 @@ private: //------------------------------------------------- machine_config::machine_config(const game_driver &gamedrv, emu_options &options) - : m_minimum_quantum(attotime::zero) - , m_gamedrv(gamedrv) + : m_gamedrv(gamedrv) , m_options(options) , m_root_device() , m_default_layouts([] (char const *a, char const *b) { return 0 > std::strcmp(a, b); }) , m_current_device(nullptr) + , m_maximum_quantums([] (char const *a, char const *b) { return 0 > std::strcmp(a, b); }) + , m_perfect_quantum_device(nullptr, "") { // add the root device device_add("root", gamedrv.type, 0); // intialize slot devices - make sure that any required devices have been allocated - for (device_slot_interface &slot : slot_interface_iterator(root_device())) + for (device_slot_interface &slot : slot_interface_enumerator(root_device())) { device_t &owner = slot.device(); const char *slot_option_name = owner.tag() + 1; @@ -101,7 +104,7 @@ machine_config::machine_config(const game_driver &gamedrv, emu_options &options) } // then notify all devices that their configuration is complete - for (device_t &device : device_iterator(root_device())) + for (device_t &device : device_enumerator(root_device())) if (!device.configured()) device.config_complete(); } @@ -117,6 +120,51 @@ machine_config::~machine_config() //------------------------------------------------- +// maximum_quantum - get smallest configured +// maximum quantum +//------------------------------------------------- + +attotime machine_config::maximum_quantum(attotime const &default_quantum) const +{ + return std::accumulate( + m_maximum_quantums.begin(), + m_maximum_quantums.end(), + default_quantum, + [] (attotime const &lhs, maximum_quantum_map::value_type const &rhs) { return (std::min)(lhs, rhs.second); }); +} + +//------------------------------------------------- +// perfect_quantum_device - get device configured +// for perfect quantum if any +//------------------------------------------------- + +device_execute_interface *machine_config::perfect_quantum_device() const +{ + if (!m_perfect_quantum_device.first) + return nullptr; + + device_t *const found(m_perfect_quantum_device.first->subdevice(m_perfect_quantum_device.second)); + if (!found) + { + throw emu_fatalerror( + "Device %s relative to %s specified for perfect interleave is not present!\n", + m_perfect_quantum_device.second, + m_perfect_quantum_device.first->tag()); + } + + device_execute_interface *result; + if (!found->interface(result)) + { + throw emu_fatalerror("Device %s (%s) specified for perfect interleave does not implement device_execute_interface!\n", + found->tag(), + found->shortname()); + } + + return result; +} + + +//------------------------------------------------- // set_default_layout - set layout for current // device //------------------------------------------------- @@ -130,6 +178,19 @@ void machine_config::set_default_layout(internal_layout const &layout) //------------------------------------------------- +// set_maximum_quantum - set maximum scheduling +// quantum for current device device +//------------------------------------------------- + +void machine_config::set_maximum_quantum(attotime const &quantum) +{ + std::pair<maximum_quantum_map::iterator, bool> const ins(m_maximum_quantums.emplace(current_device().tag(), quantum)); + if (!ins.second) + ins.first->second = quantum; +} + + +//------------------------------------------------- // device_add - configuration helper to add a // new device //------------------------------------------------- @@ -178,7 +239,7 @@ device_t *machine_config::device_remove(const char *tag) remove_references(*device); // let the device's owner do the work - owner->subdevices().m_list.remove(*device); + owner->subdevices().remove(*device); } return nullptr; } @@ -196,25 +257,23 @@ std::pair<const char *, device_t *> machine_config::resolve_owner(const char *ta device_t *owner(m_current_device); // if the device path is absolute, start from the root - if (tag[0] == ':') - { - tag++; - owner = m_root_device.get(); - } + if (!*tag || (':' == *tag) || ('^' == *tag)) + throw emu_fatalerror("Attempting to add device with tag containing parent references '%s'\n", orig_tag); // go down the path until we're done with it - std::string part; - while (strchr(tag, ':')) + char const *next; + while ((next = strchr(tag, ':')) != nullptr) { - const char *next = strchr(tag, ':'); assert(next != tag); - part.assign(tag, next - tag); + std::string_view part(tag, next - tag); owner = owner->subdevices().find(part); if (!owner) - throw emu_fatalerror("Could not find %s when looking up path for device %s\n", part.c_str(), orig_tag); - tag = next+1; + throw emu_fatalerror("Could not find '%s' when looking up path for device '%s'\n", part, orig_tag); + tag = next + 1; + if ('^' == *tag) + throw emu_fatalerror("Attempting to add device with tag containing parent references '%s'\n", orig_tag); } - assert(tag[0] != '\0'); + assert(*tag != '\0'); return std::make_pair(tag, owner); } @@ -236,7 +295,7 @@ std::tuple<const char *, device_t *, device_t *> machine_config::prepare_replace if (old_device) remove_references(*old_device); else - osd_printf_warning("Warning: attempting to replace non-existent device '%s'\n", tag); + throw emu_fatalerror("Attempting to replace non-existent device '%s'\n", tag); return std::make_tuple(owner.first, owner.second, old_device); } @@ -253,7 +312,7 @@ device_t &machine_config::add_device(std::unique_ptr<device_t> &&device, device_ if (owner) { // allocate the new device and append it to the owner's list - device_t &result(owner->subdevices().m_list.append(*device.release())); + device_t &result(owner->subdevices().append(std::move(device))); result.add_machine_configuration(*this); return result; } @@ -262,9 +321,6 @@ device_t &machine_config::add_device(std::unique_ptr<device_t> &&device, device_ // allocate the root device directly assert(!m_root_device); m_root_device = std::move(device); - driver_device *driver = dynamic_cast<driver_device *>(m_root_device.get()); - if (driver) - driver->set_game_driver(m_gamedrv); m_root_device->add_machine_configuration(*this); return *m_root_device; } @@ -280,51 +336,74 @@ device_t &machine_config::replace_device(std::unique_ptr<device_t> &&device, dev { current_device_stack const context(*this); device_t &result(existing - ? owner.subdevices().m_list.replace_and_remove(*device.release(), *existing) - : owner.subdevices().m_list.append(*device.release())); + ? owner.subdevices().replace_and_remove(std::move(device), *existing) + : owner.subdevices().append(std::move(device))); result.add_machine_configuration(*this); return result; } //------------------------------------------------- -// device_find - configuration helper to -// locate a device -//------------------------------------------------- - -device_t *machine_config::device_find(device_t *owner, const char *tag) -{ - // find the original device by relative tag (must exist) - assert(owner != nullptr); - device_t *device = owner->subdevice(tag); - if (device == nullptr) - throw emu_fatalerror("Unable to find device '%s'\n", tag); - - // return the device - return device; -} - - -//------------------------------------------------- // remove_references - globally remove references // to a device about to be removed from the tree //------------------------------------------------- void machine_config::remove_references(device_t &device) { - // remove default layouts for subdevices + // sanity check + if (m_perfect_quantum_device.first == &device) + { + throw emu_fatalerror( + "Removing %s device %s would make the perfect quantum device target invalid\n", + device.shortname(), + device.tag()); + } + + // remove default layouts and maximum quantum settings for subdevices char const *const tag(device.tag()); std::size_t const taglen(std::strlen(tag)); - default_layout_map::iterator it(m_default_layouts.lower_bound(tag)); - while ((m_default_layouts.end() != it) && !std::strncmp(tag, it->first, taglen)) + for (auto it = m_default_layouts.lower_bound(tag); (m_default_layouts.end() != it) && !std::strncmp(tag, it->first, taglen); ) { if (!it->first[taglen] || (':' == it->first[taglen])) it = m_default_layouts.erase(it); else ++it; } + for (auto it = m_maximum_quantums.lower_bound(tag); (m_maximum_quantums.end() != it) && !std::strncmp(tag, it->first, taglen); ) + { + if (!it->first[taglen] || (':' == it->first[taglen])) + it = m_maximum_quantums.erase(it); + else + ++it; + } +} + + +//------------------------------------------------- +// set_perfect_quantum - set device to base +// scheduling interval on +//------------------------------------------------- + +void machine_config::set_perfect_quantum(device_t &device, std::string tag) +{ + if (!m_current_device) + { + throw emu_fatalerror( + "Perfect quantum device can only be set during configuration (set to %s relative to %s)\n", + tag, + device.tag()); + } + + if (m_current_device != m_root_device.get()) + { + throw emu_fatalerror( + "Perfect quantum device can only be set by the root device (set to %s relative to %s while configuring %s device %s)\n", + tag, + device.tag(), + m_current_device->shortname(), + m_current_device->tag()); + } - // iterate over all devices and remove any references - for (device_t &scan : device_iterator(root_device())) - scan.subdevices().m_tagmap.clear(); + m_perfect_quantum_device.first = tag.empty() ? nullptr : &device; + m_perfect_quantum_device.second = std::move(tag); } diff --git a/src/emu/mconfig.h b/src/emu/mconfig.h index 84d42d94996..c6cacde581f 100644 --- a/src/emu/mconfig.h +++ b/src/emu/mconfig.h @@ -31,19 +31,34 @@ // TYPE DEFINITIONS //************************************************************************** -namespace emu { namespace detail { +namespace emu::detail { -struct machine_config_replace { machine_config &config; }; +class machine_config_replace +{ +public: + machine_config_replace(machine_config_replace const &) = default; + machine_config &config; +private: + machine_config_replace(machine_config &c) : config(c) { } + friend class ::machine_config; +}; -} } // namesapce emu::detail +} // namespace emu::detail +/// \brief Internal layout description +/// +/// Holds the compressed and decompressed data size, compression method, +/// and a reference to the compressed layout data. Note that copying +/// the structure will not copy the referenced data. struct internal_layout { + enum class compression { NONE, ZLIB }; + size_t decompressed_size; size_t compressed_size; - u8 compression_type; - const u8* data; + compression compression_type; + u8 const *data; }; @@ -96,19 +111,68 @@ public: emu_options &options() const { return m_options; } device_t *device(const char *tag) const { return root_device().subdevice(tag); } template <class DeviceClass> DeviceClass *device(const char *tag) const { return downcast<DeviceClass *>(device(tag)); } + attotime maximum_quantum(attotime const &default_quantum) const; + device_execute_interface *perfect_quantum_device() const; + + /// \brief Apply visitor to internal layouts + /// + /// Calls the supplied visitor for each device with an internal + /// layout. The order of devices is implementation-dependent. + /// \param [in] op The visitor. It must provide a function call + // operator that can be invoked with two arguments: a reference + // to a #device_t and a const reference to an #internal_layout. template <typename T> void apply_default_layouts(T &&op) const { - for (std::pair<char const *, internal_layout const *> const &lay : m_default_layouts) + for (std::pair<char const *const, internal_layout const *> const &lay : m_default_layouts) op(*device(lay.first), *lay.second); } - // public state - attotime m_minimum_quantum; // minimum scheduling quantum - std::string m_perfect_cpu_quantum; // tag of CPU to use for "perfect" scheduling - - // configuration methods + /// \brief Get a device replacement helper + /// + /// Pass the result in place of the machine configuration itself to + /// replace an existing device. + /// \return A device replacement helper to pass to a device type + /// when replacing an existing device. + emu::detail::machine_config_replace replace() { return emu::detail::machine_config_replace(*this); } + + /// \brief Set internal layout for current device + /// + /// Set internal layout for current device. Each device in the + /// system can have its own internal layout. Tags in the layout + /// will be resolved relative to the device. Replaces previously + /// set layout if any. + /// \param [in] layout Reference to the internal layout description + /// structure. Neither the description structure nor the + /// compressed data is copied. It is the caller's responsibility + /// to ensure both remain valid until layouts and views are + /// instantiated. void set_default_layout(internal_layout const &layout); + /// \brief Set maximum scheduling quantum + /// + /// Set the maximum scheduling quantum required for the current + /// device. The smallest maximum quantum requested by a device in + /// the system will be used. + /// \param [in] quantum Maximum scheduling quantum in attoseconds. + void set_maximum_quantum(attotime const &quantum); + + template <typename T> + void set_perfect_quantum(T &&tag) + { + set_perfect_quantum(current_device(), std::forward<T>(tag)); + } + template <class DeviceClass, bool Required> + void set_perfect_quantum(device_finder<DeviceClass, Required> const &finder) + { + std::pair<device_t &, char const *> const target(finder.finder_target()); + set_perfect_quantum(target.first, target.second); + } + template <class DeviceClass, bool Required> + void set_perfect_quantum(device_finder<DeviceClass, Required> &finder) + { + set_perfect_quantum(const_cast<device_finder<DeviceClass, Required> const &>(finder)); + } + // helpers during configuration; not for general use token begin_configuration(device_t &device) { @@ -116,7 +180,6 @@ public: m_current_device = &device; return token(*this, device); } - emu::detail::machine_config_replace replace() { return emu::detail::machine_config_replace{ *this }; }; device_t *device_add(const char *tag, device_type type, u32 clock); template <typename Creator> device_t *device_add(const char *tag, Creator &&type, u32 clock) @@ -162,11 +225,11 @@ public: return device_replace(tag, std::forward<Creator>(type), clock.value(), std::forward<Params>(args)...); } device_t *device_remove(const char *tag); - device_t *device_find(device_t *owner, const char *tag); private: class current_device_stack; typedef std::map<char const *, internal_layout const *, bool (*)(char const *, char const *)> default_layout_map; + typedef std::map<char const *, attotime, bool (*)(char const *, char const *)> maximum_quantum_map; // internal helpers std::pair<const char *, device_t *> resolve_owner(const char *tag) const; @@ -174,13 +237,16 @@ private: device_t &add_device(std::unique_ptr<device_t> &&device, device_t *owner); device_t &replace_device(std::unique_ptr<device_t> &&device, device_t &owner, device_t *existing); void remove_references(device_t &device); + void set_perfect_quantum(device_t &device, std::string tag); // internal state - game_driver const & m_gamedrv; - emu_options & m_options; - std::unique_ptr<device_t> m_root_device; - default_layout_map m_default_layouts; - device_t * m_current_device; + game_driver const & m_gamedrv; + emu_options & m_options; + std::unique_ptr<device_t> m_root_device; + default_layout_map m_default_layouts; + device_t * m_current_device; + maximum_quantum_map m_maximum_quantums; + std::pair<device_t *, std::string> m_perfect_quantum_device; }; #endif // MAME_EMU_MCONFIG_H diff --git a/src/emu/memarray.cpp b/src/emu/memarray.cpp index 50bd270fd4f..eb03974ff1c 100644 --- a/src/emu/memarray.cpp +++ b/src/emu/memarray.cpp @@ -2,13 +2,14 @@ // copyright-holders:Aaron Giles /*************************************************************************** - memarray.c + memarray.cpp Generic memory array accessor helper. ***************************************************************************/ #include "emu.h" +#include "memarray.h" //************************************************************************** @@ -51,34 +52,34 @@ void memory_array::set(void *base, u32 bytes, int membits, endianness_t endianne m_bytes_per_entry = bpe; // derive data - switch (bpe*1000 + membits*10 + endianness) + switch (bpe*1000 + membits*10 + (endianness == ENDIANNESS_LITTLE ? 0 : 1)) { - case 1*1000 + 8*10 + ENDIANNESS_LITTLE: m_read_entry = &memory_array::read8_from_8; m_write_entry = &memory_array::write8_to_8; break; - case 1*1000 + 8*10 + ENDIANNESS_BIG: m_read_entry = &memory_array::read8_from_8; m_write_entry = &memory_array::write8_to_8; break; - case 1*1000 + 16*10 + ENDIANNESS_LITTLE: m_read_entry = &memory_array::read8_from_16le; m_write_entry = &memory_array::write8_to_16le; break; - case 1*1000 + 16*10 + ENDIANNESS_BIG: m_read_entry = &memory_array::read8_from_16be; m_write_entry = &memory_array::write8_to_16be; break; - case 1*1000 + 32*10 + ENDIANNESS_LITTLE: m_read_entry = &memory_array::read8_from_32le; m_write_entry = &memory_array::write8_to_32le; break; - case 1*1000 + 32*10 + ENDIANNESS_BIG: m_read_entry = &memory_array::read8_from_32be; m_write_entry = &memory_array::write8_to_32be; break; - case 1*1000 + 64*10 + ENDIANNESS_LITTLE: m_read_entry = &memory_array::read8_from_64le; m_write_entry = &memory_array::write8_to_64le; break; - case 1*1000 + 64*10 + ENDIANNESS_BIG: m_read_entry = &memory_array::read8_from_64be; m_write_entry = &memory_array::write8_to_64be; break; - - case 2*1000 + 8*10 + ENDIANNESS_LITTLE: m_read_entry = &memory_array::read16_from_8le; m_write_entry = &memory_array::write16_to_8le; break; - case 2*1000 + 8*10 + ENDIANNESS_BIG: m_read_entry = &memory_array::read16_from_8be; m_write_entry = &memory_array::write16_to_8be; break; - case 2*1000 + 16*10 + ENDIANNESS_LITTLE: m_read_entry = &memory_array::read16_from_16; m_write_entry = &memory_array::write16_to_16; break; - case 2*1000 + 16*10 + ENDIANNESS_BIG: m_read_entry = &memory_array::read16_from_16; m_write_entry = &memory_array::write16_to_16; break; - case 2*1000 + 32*10 + ENDIANNESS_LITTLE: m_read_entry = &memory_array::read16_from_32le; m_write_entry = &memory_array::write16_to_32le; break; - case 2*1000 + 32*10 + ENDIANNESS_BIG: m_read_entry = &memory_array::read16_from_32be; m_write_entry = &memory_array::write16_to_32be; break; - case 2*1000 + 64*10 + ENDIANNESS_LITTLE: m_read_entry = &memory_array::read16_from_64le; m_write_entry = &memory_array::write16_to_64le; break; - case 2*1000 + 64*10 + ENDIANNESS_BIG: m_read_entry = &memory_array::read16_from_64be; m_write_entry = &memory_array::write16_to_64be; break; - - case 4*1000 + 8*10 + ENDIANNESS_LITTLE: m_read_entry = &memory_array::read32_from_8le; m_write_entry = &memory_array::write32_to_8le; break; - case 4*1000 + 8*10 + ENDIANNESS_BIG: m_read_entry = &memory_array::read32_from_8be; m_write_entry = &memory_array::write32_to_8be; break; - case 4*1000 + 16*10 + ENDIANNESS_LITTLE: m_read_entry = &memory_array::read32_from_16le; m_write_entry = &memory_array::write32_to_16le; break; - case 4*1000 + 16*10 + ENDIANNESS_BIG: m_read_entry = &memory_array::read32_from_16be; m_write_entry = &memory_array::write32_to_16be; break; - case 4*1000 + 32*10 + ENDIANNESS_LITTLE: m_read_entry = &memory_array::read32_from_32; m_write_entry = &memory_array::write32_to_32; break; - case 4*1000 + 32*10 + ENDIANNESS_BIG: m_read_entry = &memory_array::read32_from_32; m_write_entry = &memory_array::write32_to_32; break; - case 4*1000 + 64*10 + ENDIANNESS_LITTLE: m_read_entry = &memory_array::read32_from_64le; m_write_entry = &memory_array::write32_to_64le; break; - case 4*1000 + 64*10 + ENDIANNESS_BIG: m_read_entry = &memory_array::read32_from_64be; m_write_entry = &memory_array::write32_to_64be; break; + case 1*1000 + 8*10 + 0: m_read_entry = &memory_array::read8_from_8; m_write_entry = &memory_array::write8_to_8; break; + case 1*1000 + 8*10 + 1: m_read_entry = &memory_array::read8_from_8; m_write_entry = &memory_array::write8_to_8; break; + case 1*1000 + 16*10 + 0: m_read_entry = &memory_array::read8_from_16le; m_write_entry = &memory_array::write8_to_16le; break; + case 1*1000 + 16*10 + 1: m_read_entry = &memory_array::read8_from_16be; m_write_entry = &memory_array::write8_to_16be; break; + case 1*1000 + 32*10 + 0: m_read_entry = &memory_array::read8_from_32le; m_write_entry = &memory_array::write8_to_32le; break; + case 1*1000 + 32*10 + 1: m_read_entry = &memory_array::read8_from_32be; m_write_entry = &memory_array::write8_to_32be; break; + case 1*1000 + 64*10 + 0: m_read_entry = &memory_array::read8_from_64le; m_write_entry = &memory_array::write8_to_64le; break; + case 1*1000 + 64*10 + 1: m_read_entry = &memory_array::read8_from_64be; m_write_entry = &memory_array::write8_to_64be; break; + + case 2*1000 + 8*10 + 0: m_read_entry = &memory_array::read16_from_8le; m_write_entry = &memory_array::write16_to_8le; break; + case 2*1000 + 8*10 + 1: m_read_entry = &memory_array::read16_from_8be; m_write_entry = &memory_array::write16_to_8be; break; + case 2*1000 + 16*10 + 0: m_read_entry = &memory_array::read16_from_16; m_write_entry = &memory_array::write16_to_16; break; + case 2*1000 + 16*10 + 1: m_read_entry = &memory_array::read16_from_16; m_write_entry = &memory_array::write16_to_16; break; + case 2*1000 + 32*10 + 0: m_read_entry = &memory_array::read16_from_32le; m_write_entry = &memory_array::write16_to_32le; break; + case 2*1000 + 32*10 + 1: m_read_entry = &memory_array::read16_from_32be; m_write_entry = &memory_array::write16_to_32be; break; + case 2*1000 + 64*10 + 0: m_read_entry = &memory_array::read16_from_64le; m_write_entry = &memory_array::write16_to_64le; break; + case 2*1000 + 64*10 + 1: m_read_entry = &memory_array::read16_from_64be; m_write_entry = &memory_array::write16_to_64be; break; + + case 4*1000 + 8*10 + 0: m_read_entry = &memory_array::read32_from_8le; m_write_entry = &memory_array::write32_to_8le; break; + case 4*1000 + 8*10 + 1: m_read_entry = &memory_array::read32_from_8be; m_write_entry = &memory_array::write32_to_8be; break; + case 4*1000 + 16*10 + 0: m_read_entry = &memory_array::read32_from_16le; m_write_entry = &memory_array::write32_to_16le; break; + case 4*1000 + 16*10 + 1: m_read_entry = &memory_array::read32_from_16be; m_write_entry = &memory_array::write32_to_16be; break; + case 4*1000 + 32*10 + 0: m_read_entry = &memory_array::read32_from_32; m_write_entry = &memory_array::write32_to_32; break; + case 4*1000 + 32*10 + 1: m_read_entry = &memory_array::read32_from_32; m_write_entry = &memory_array::write32_to_32; break; + case 4*1000 + 64*10 + 0: m_read_entry = &memory_array::read32_from_64le; m_write_entry = &memory_array::write32_to_64le; break; + case 4*1000 + 64*10 + 1: m_read_entry = &memory_array::read32_from_64be; m_write_entry = &memory_array::write32_to_64be; break; default: throw emu_fatalerror("Illegal memory bits/bus width combo in memory_array"); } @@ -89,11 +90,6 @@ void memory_array::set(void *base, u32 bytes, int membits, endianness_t endianne // set - additional setter variants //------------------------------------------------- -void memory_array::set(const address_space &space, void *base, u32 bytes, int bpe) -{ - set(base, bytes, space.data_width(), space.endianness(), bpe); -} - void memory_array::set(const memory_share &share, int bpe) { set(share.ptr(), share.bytes(), share.bitwidth(), share.endianness(), bpe); @@ -122,7 +118,7 @@ void memory_array::set_endianness(endianness_t endianness) //------------------------------------------------- // read8_from_*/write8_to_* - entry read/write -// heleprs for 1 byte-per-entry +// helpers for 1 byte-per-entry //------------------------------------------------- u32 memory_array::read8_from_8(int index) const { return reinterpret_cast<u8 *>(m_base)[index]; } @@ -138,15 +134,15 @@ void memory_array::write8_to_32le(int index, u32 data) { reinterpret_cast<u8 *>( u32 memory_array::read8_from_32be(int index) const { return reinterpret_cast<u8 *>(m_base)[BYTE4_XOR_BE(index)]; } void memory_array::write8_to_32be(int index, u32 data) { reinterpret_cast<u8 *>(m_base)[BYTE4_XOR_BE(index)] = data; } -u32 memory_array::read8_from_64le(int index) const { return reinterpret_cast<u8 *>(m_base)[BYTE8_XOR_BE(index)]; } -void memory_array::write8_to_64le(int index, u32 data) { reinterpret_cast<u8 *>(m_base)[BYTE8_XOR_BE(index)] = data; } +u32 memory_array::read8_from_64le(int index) const { return reinterpret_cast<u8 *>(m_base)[BYTE8_XOR_LE(index)]; } +void memory_array::write8_to_64le(int index, u32 data) { reinterpret_cast<u8 *>(m_base)[BYTE8_XOR_LE(index)] = data; } u32 memory_array::read8_from_64be(int index) const { return reinterpret_cast<u8 *>(m_base)[BYTE8_XOR_BE(index)]; } void memory_array::write8_to_64be(int index, u32 data) { reinterpret_cast<u8 *>(m_base)[BYTE8_XOR_BE(index)] = data; } //------------------------------------------------- // read16_from_*/write16_to_* - entry read/write -// heleprs for 2 bytes-per-entry +// helpers for 2 bytes-per-entry //------------------------------------------------- u32 memory_array::read16_from_8le(int index) const { return read8_from_8(index*2) | (read8_from_8(index*2+1) << 8); } @@ -170,7 +166,7 @@ void memory_array::write16_to_64be(int index, u32 data) { reinterpret_cast<u16 * //------------------------------------------------- // read32_from_*/write32_to_* - entry read/write -// heleprs for 4 bytes-per-entry +// helpers for 4 bytes-per-entry //------------------------------------------------- u32 memory_array::read32_from_8le(int index) const { return read16_from_8le(index*2) | (read16_from_8le(index*2+1) << 16); } diff --git a/src/emu/memarray.h b/src/emu/memarray.h index d7bbd51df1c..fc7e47c4b3c 100644 --- a/src/emu/memarray.h +++ b/src/emu/memarray.h @@ -12,7 +12,7 @@ arranged logically. A memory array is stored in "natural" order, i.e., read/writes to it - are done via AM_RAM, or standard COMBINE_DATA, even if the width of + are done via ram(), or standard COMBINE_DATA, even if the width of the CPU is different from the array width. The read_entry/write_entry functions serve to read/write entries of @@ -23,10 +23,6 @@ #pragma once -#ifndef __EMU_H__ -#error Dont include this file directly; include emu.h instead. -#endif - #ifndef MAME_EMU_MEMARRAY_H #define MAME_EMU_MEMARRAY_H @@ -46,14 +42,12 @@ public: memory_array(); memory_array(void *base, u32 bytes, int membits, endianness_t endianness, int bpe) { set(base, bytes, membits, endianness, bpe); } template <typename _Type> memory_array(std::vector<_Type> &array, endianness_t endianness, int bpe) { set(array, endianness, bpe); } - memory_array(const address_space &space, void *base, u32 bytes, int bpe) { set(space, base, bytes, bpe); } memory_array(const memory_share &share, int bpe) { set(share, bpe); } memory_array(const memory_array &array) { set(array); } // configuration void set(void *base, u32 bytes, int membits, endianness_t endianness, int bpe); template <typename _Type> void set(std::vector<_Type> &array, endianness_t endianness, int bpe) { set(&array[0], array.size(), 8*sizeof(_Type), endianness, bpe); } - void set(const address_space &space, void *base, u32 bytes, int bpe); void set(const memory_share &share, int bpe); void set(const memory_array &array); diff --git a/src/emu/natkeyboard.cpp b/src/emu/natkeyboard.cpp index 7d154787c0c..1abe9a140ff 100644 --- a/src/emu/natkeyboard.cpp +++ b/src/emu/natkeyboard.cpp @@ -1,5 +1,5 @@ // license:BSD-3-Clause -// copyright-holders:Aaron Giles +// copyright-holders:Aaron Giles,Vas Crabb /*************************************************************************** natkeyboard.cpp @@ -10,7 +10,16 @@ #include "emu.h" #include "natkeyboard.h" + +#include "corestr.h" #include "emuopts.h" +#include "unicode.h" + +// FIXME: allow OSD module headers to be included in a less ugly way +#include "../osd/modules/lib/osdlib.h" + +#include <algorithm> +#include <cstring> //************************************************************************** @@ -25,7 +34,8 @@ // CONSTANTS //************************************************************************** -const int KEY_BUFFER_SIZE = 4096; +const u32 KEY_BUFFER_CHUNK_SIZE = 0x1000; +const u32 KEY_BUFFER_MAX_SIZE = 0x800000; const char32_t INVALID_CHAR = '?'; @@ -38,7 +48,7 @@ const char32_t INVALID_CHAR = '?'; struct char_info { char32_t ch; - const char *alternate; // alternative string, in UTF-8 + const char *alternate; // alternative string, in UTF-8 static const char_info *find(char32_t target); }; @@ -317,9 +327,11 @@ const char_info charinfo[] = natural_keyboard::natural_keyboard(running_machine &machine) : m_machine(machine) + , m_have_charkeys(false) , m_in_use(false) , m_bufbegin(0) , m_bufend(0) + , m_current_code(nullptr) , m_fieldnum(0) , m_status_keydown(false) , m_last_cr(false) @@ -330,10 +342,10 @@ natural_keyboard::natural_keyboard(running_machine &machine) , m_charqueue_empty() { // try building a list of keycodes; if none are available, don't bother - build_codes(machine.ioport()); - if (!m_keycode_map.empty()) + build_codes(); + if (!m_keyboards.empty()) { - m_buffer.resize(KEY_BUFFER_SIZE); + m_buffer.resize(KEY_BUFFER_CHUNK_SIZE); m_timer = machine.scheduler().timer_alloc(timer_expired_delegate(FUNC(natural_keyboard::timer), this)); } @@ -350,9 +362,10 @@ natural_keyboard::natural_keyboard(running_machine &machine) void natural_keyboard::configure(ioport_queue_chars_delegate queue_chars, ioport_accept_char_delegate accept_char, ioport_charqueue_empty_delegate charqueue_empty) { // set the callbacks - m_queue_chars = queue_chars; - m_accept_char = accept_char; - m_charqueue_empty = charqueue_empty; + assert(m_timer != nullptr); + m_queue_chars = std::move(queue_chars); + m_accept_char = std::move(accept_char); + m_charqueue_empty = std::move(charqueue_empty); } @@ -370,59 +383,69 @@ void natural_keyboard::set_in_use(bool usage) machine().options().set_value(OPTION_NATURAL_KEYBOARD, usage, OPTION_PRIORITY_CMDLINE); // lock out (or unlock) all keyboard inputs - for (auto &port : machine().ioport().ports()) - for (ioport_field &field : port.second->fields()) - if (field.type() == IPT_KEYBOARD) - { - field.live().lockout = usage; + for (kbd_dev_info &devinfo : m_keyboards) + { + for (ioport_field &field : devinfo.keyfields) + { + bool const is_keyboard(field.type() == IPT_KEYBOARD); + field.live().lockout = !devinfo.enabled || (is_keyboard && usage); - // clear pressed status when going out of use - if (!usage) - field.set_value(0); - } + // clear pressed status when going out of use + if (is_keyboard && !usage) + field.set_value(0); + } + } } } //------------------------------------------------- -// post - post a single character +// post_char - post a single character //------------------------------------------------- -void natural_keyboard::post(char32_t ch) +void natural_keyboard::post_char(char32_t ch, bool normalize_crlf) { - // ignore any \n that are preceded by \r - if (m_last_cr && ch == '\n') + if (normalize_crlf) { - m_last_cr = false; - return; - } + // ignore any \n that are preceded by \r + if (m_last_cr && ch == '\n') + { + m_last_cr = false; + return; + } - // change all eolns to '\r' - if (ch == '\n') - ch = '\r'; - else - m_last_cr = (ch == '\r'); + // change all eolns to '\r' + if (ch == '\n') + ch = '\r'; + else + m_last_cr = (ch == '\r'); + } // logging if (LOG_NATURAL_KEYBOARD) { const keycode_map_entry *code = find_code(ch); - machine().logerror("natural_keyboard::post(): code=%i (%s) field.name='%s'\n", int(ch), unicode_to_string(ch).c_str(), (code != nullptr && code->field[0] != nullptr) ? code->field[0]->name() : "<null>"); + machine().logerror("natural_keyboard::post_char(): code=%i (%s) field.name='%s'\n", int(ch), unicode_to_string(ch).c_str(), (code != nullptr && code->field[0] != nullptr) ? code->field[0]->name() : "<null>"); } - // can we post this key in the queue directly? if (can_post_directly(ch)) + { + // can post this key in the queue directly internal_post(ch); - - // can we post this key with an alternate representation? + } else if (can_post_alternate(ch)) { - const char_info *info = char_info::find(ch); + // can post this key with an alternate representation + char_info const *const info = char_info::find(ch); assert(info != nullptr && info->alternate != nullptr); - const char *altstring = info->alternate; - while (*altstring != 0) + char const *altstring = info->alternate; + unsigned remain(std::strlen(altstring)); + while (remain) { - altstring += uchar_from_utf8(&ch, altstring, strlen(altstring)); + int const used(uchar_from_utf8(&ch, altstring, remain)); + assert(0 < used); + altstring += used; + remain -= used; internal_post(ch); } } @@ -433,23 +456,14 @@ void natural_keyboard::post(char32_t ch) // post - post a unicode encoded string //------------------------------------------------- -void natural_keyboard::post(const char32_t *text, size_t length, const attotime &rate) +void natural_keyboard::post(std::u32string_view text, const attotime &rate) { // set the fixed rate m_current_rate = rate; - // 0 length means strlen - if (length == 0) - for (const char32_t *scan = text; *scan != 0; scan++) - length++; - - // iterate over characters or until the buffer is full up - while (length > 0 && !full()) - { - // fetch next character - post(*text++); - length--; - } + // iterate over characters + for (char32_t ch : text) + post_char(ch, true); } @@ -457,21 +471,17 @@ void natural_keyboard::post(const char32_t *text, size_t length, const attotime // post_utf8 - post a UTF-8 encoded string //------------------------------------------------- -void natural_keyboard::post_utf8(const char *text, size_t length, const attotime &rate) +void natural_keyboard::post_utf8(std::string_view text, const attotime &rate) { // set the fixed rate m_current_rate = rate; - // 0-length means strlen - if (length == 0) - length = strlen(text); - // iterate until out of characters - while (length > 0) + while (!text.empty()) { // decode the next character char32_t uc; - int count = uchar_from_utf8(&uc, text, length); + int count = uchar_from_utf8(&uc, text); if (count < 0) { count = 1; @@ -479,84 +489,75 @@ void natural_keyboard::post_utf8(const char *text, size_t length, const attotime } // append to the buffer - post(uc); - text += count; - length -= count; + post_char(uc, true); + text.remove_prefix(count); } } -void natural_keyboard::post_utf8(const std::string &text, const attotime &rate) -{ - if (!text.empty()) - post_utf8(text.c_str(), text.size(), rate); -} - - //------------------------------------------------- // post_coded - post a coded string //------------------------------------------------- -void natural_keyboard::post_coded(const char *text, size_t length, const attotime &rate) +void natural_keyboard::post_coded(std::string_view text, const attotime &rate) { + using namespace std::literals; static const struct { - const char *key; + std::string_view key; char32_t code; } codes[] = { - { "BACKSPACE", 8 }, - { "BS", 8 }, - { "BKSP", 8 }, - { "DEL", UCHAR_MAMEKEY(DEL) }, - { "DELETE", UCHAR_MAMEKEY(DEL) }, - { "END", UCHAR_MAMEKEY(END) }, - { "ENTER", 13 }, - { "ESC", '\033' }, - { "HOME", UCHAR_MAMEKEY(HOME) }, - { "INS", UCHAR_MAMEKEY(INSERT) }, - { "INSERT", UCHAR_MAMEKEY(INSERT) }, - { "PGDN", UCHAR_MAMEKEY(PGDN) }, - { "PGUP", UCHAR_MAMEKEY(PGUP) }, - { "SPACE", 32 }, - { "TAB", 9 }, - { "F1", UCHAR_MAMEKEY(F1) }, - { "F2", UCHAR_MAMEKEY(F2) }, - { "F3", UCHAR_MAMEKEY(F3) }, - { "F4", UCHAR_MAMEKEY(F4) }, - { "F5", UCHAR_MAMEKEY(F5) }, - { "F6", UCHAR_MAMEKEY(F6) }, - { "F7", UCHAR_MAMEKEY(F7) }, - { "F8", UCHAR_MAMEKEY(F8) }, - { "F9", UCHAR_MAMEKEY(F9) }, - { "F10", UCHAR_MAMEKEY(F10) }, - { "F11", UCHAR_MAMEKEY(F11) }, - { "F12", UCHAR_MAMEKEY(F12) }, - { "QUOTE", '\"' } + { "BACKSPACE"sv, 8 }, + { "BS"sv, 8 }, + { "BKSP"sv, 8 }, + { "CAPSLOCK"sv, UCHAR_MAMEKEY(CAPSLOCK) }, + { "CR"sv, 13 }, + { "DEL"sv, UCHAR_MAMEKEY(DEL) }, + { "DELETE"sv, UCHAR_MAMEKEY(DEL) }, + { "END"sv, UCHAR_MAMEKEY(END) }, + { "ENTER"sv, 13 }, + { "ESC"sv, '\033' }, + { "HOME"sv, UCHAR_MAMEKEY(HOME) }, + { "INS"sv, UCHAR_MAMEKEY(INSERT) }, + { "INSERT"sv, UCHAR_MAMEKEY(INSERT) }, + { "LF"sv, 10 }, + { "PGDN"sv, UCHAR_MAMEKEY(PGDN) }, + { "PGUP"sv, UCHAR_MAMEKEY(PGUP) }, + { "SPACE"sv, 32 }, + { "TAB"sv, 9 }, + { "F1"sv, UCHAR_MAMEKEY(F1) }, + { "F2"sv, UCHAR_MAMEKEY(F2) }, + { "F3"sv, UCHAR_MAMEKEY(F3) }, + { "F4"sv, UCHAR_MAMEKEY(F4) }, + { "F5"sv, UCHAR_MAMEKEY(F5) }, + { "F6"sv, UCHAR_MAMEKEY(F6) }, + { "F7"sv, UCHAR_MAMEKEY(F7) }, + { "F8"sv, UCHAR_MAMEKEY(F8) }, + { "F9"sv, UCHAR_MAMEKEY(F9) }, + { "F10"sv, UCHAR_MAMEKEY(F10) }, + { "F11"sv, UCHAR_MAMEKEY(F11) }, + { "F12"sv, UCHAR_MAMEKEY(F12) }, + { "QUOTE"sv, '\"' } }; // set the fixed rate m_current_rate = rate; - // 0-length means strlen - if (length == 0) - length = strlen(text); - // iterate through the source string - size_t curpos = 0; - while (curpos < length) + while (!text.empty()) { // extract next character - char32_t ch = text[curpos]; - size_t increment = 1; + char32_t ch = text.front(); + std::string_view::size_type increment = 1; // look for escape characters if (ch == '{') for (auto & code : codes) { - size_t keylen = strlen(code.key); - if (curpos + keylen + 2 <= length) - if (core_strnicmp(code.key, &text[curpos + 1], keylen) == 0 && text[curpos + keylen + 1] == '}') + std::string_view::size_type keylen = code.key.length(); + if (keylen + 2 <= text.length()) + if (util::strequpper(text.substr(1, keylen), code.key) && text[keylen + 1] == '}') { ch = code.code; increment = keylen + 2; @@ -565,19 +566,12 @@ void natural_keyboard::post_coded(const char *text, size_t length, const attotim // if we got a code, post it if (ch != 0) - post(ch); - curpos += increment; + post_char(ch); + text.remove_prefix(increment); } } -void natural_keyboard::post_coded(const std::string &text, const attotime &rate) -{ - if (!text.empty()) - post_coded(text.c_str(), text.size(), rate); -} - - //------------------------------------------------- // paste - does a paste from the keyboard //------------------------------------------------- @@ -598,16 +592,63 @@ void natural_keyboard::paste() // chars //------------------------------------------------- -void natural_keyboard::build_codes(ioport_manager &manager) +void natural_keyboard::build_codes() { - // find all shift keys - unsigned mask = 0; - std::array<ioport_field *, SHIFT_COUNT> shift; - std::fill(std::begin(shift), std::end(shift), nullptr); + ioport_manager &manager(machine().ioport()); + + // find all the devices with keyboard or keypad inputs for (auto const &port : manager.ports()) { + auto devinfo( + std::find_if( + m_keyboards.begin(), + m_keyboards.end(), + [&port] (kbd_dev_info const &info) + { + return &port.second->device() == &info.device.get(); + })); for (ioport_field &field : port.second->fields()) { + bool const is_keyboard(field.type() == IPT_KEYBOARD); + if (is_keyboard || (field.type() == IPT_KEYPAD)) + { + if (m_keyboards.end() == devinfo) + devinfo = m_keyboards.emplace(devinfo, port.second->device()); + devinfo->keyfields.emplace_back(field); + if (is_keyboard) + devinfo->keyboard = true; + else + devinfo->keypad = true; + } + } + } + std::sort( + std::begin(m_keyboards), + std::end(m_keyboards), + [] (kbd_dev_info const &l, kbd_dev_info const &r) + { + return 0 > std::strcmp(l.device.get().tag(), r.device.get().tag()); + }); + + // set up key mappings for each keyboard + std::array<ioport_field *, SHIFT_COUNT> shift; + unsigned mask; + bool have_keyboard(false); + for (kbd_dev_info &devinfo : m_keyboards) + { + if (LOG_NATURAL_KEYBOARD) + machine().logerror("natural_keyboard: building codes for %s... (%u fields)\n", devinfo.device.get().tag(), devinfo.keyfields.size()); + + // enable all pure keypads and the first keyboard + if (!devinfo.keyboard || !have_keyboard) + devinfo.enabled = true; + have_keyboard = have_keyboard || devinfo.keyboard; + + // find all shift keys + std::fill(std::begin(shift), std::end(shift), nullptr); + mask = 0; + for (ioport_field &field : devinfo.keyfields) + { if (field.type() == IPT_KEYBOARD) { std::vector<char32_t> const codes = field.keyboard_codes(0); @@ -617,17 +658,17 @@ void natural_keyboard::build_codes(ioport_manager &manager) { mask |= 1U << (code - UCHAR_SHIFT_BEGIN); shift[code - UCHAR_SHIFT_BEGIN] = &field; + if (LOG_NATURAL_KEYBOARD) + machine().logerror("natural_keyboard: UCHAR_SHIFT_%d found\n", code - UCHAR_SHIFT_BEGIN + 1); } } } } - } - // iterate over ports and fields - for (auto const &port : manager.ports()) - { - for (ioport_field &field : port.second->fields()) + // iterate over keyboard/keypad fields + for (ioport_field &field : devinfo.keyfields) { + field.live().lockout = !devinfo.enabled; if (field.type() == IPT_KEYBOARD) { // iterate over all shift states @@ -635,38 +676,40 @@ void natural_keyboard::build_codes(ioport_manager &manager) { if (!(curshift & ~mask)) { - // fetch the code, ignoring 0 and shiters + // fetch the code, ignoring shifters std::vector<char32_t> const codes = field.keyboard_codes(curshift); for (char32_t code : codes) { - if (((code < UCHAR_SHIFT_BEGIN) || (code > UCHAR_SHIFT_END)) && (code != 0)) + if ((code < UCHAR_SHIFT_BEGIN) || (code > UCHAR_SHIFT_END)) { - // prefer lowest shift state - keycode_map::iterator const found(m_keycode_map.find(code)); - if ((m_keycode_map.end() == found) || (found->second.shift > curshift)) + m_have_charkeys = true; + keycode_map::iterator const found(devinfo.codemap.find(code)); + keycode_map_entry newcode; + std::fill(std::begin(newcode.field), std::end(newcode.field), nullptr); + newcode.shift = curshift; + newcode.condition = field.condition(); + + unsigned fieldnum(0); + for (unsigned i = 0, bits = curshift; (i < SHIFT_COUNT) && bits; ++i, bits >>= 1) { - keycode_map_entry newcode; - std::fill(std::begin(newcode.field), std::end(newcode.field), nullptr); - newcode.shift = curshift; - - unsigned fieldnum = 0; - for (unsigned i = 0, bits = curshift; (i < SHIFT_COUNT) && bits; ++i, bits >>= 1) - { - if (BIT(bits, 0)) - newcode.field[fieldnum++] = shift[i]; - } - - newcode.field[fieldnum] = &field; - if (m_keycode_map.end() == found) - m_keycode_map.emplace(code, newcode); - else - found->second = newcode; - - if (LOG_NATURAL_KEYBOARD) - { - machine().logerror("natural_keyboard: code=%u (%s) port=%p field.name='%s'\n", - code, unicode_to_string(code), (void *)&port, field.name()); - } + if (BIT(bits, 0)) + newcode.field[fieldnum++] = shift[i]; + } + + newcode.field[fieldnum] = &field; + if (devinfo.codemap.end() == found) + { + keycode_map_entries entries; + entries.emplace_back(newcode); + devinfo.codemap.emplace(code, std::move(entries)); + } + else + found->second.emplace_back(newcode); + + if (LOG_NATURAL_KEYBOARD) + { + machine().logerror("natural_keyboard: code=%u (%s) port=%p field.name='%s'\n", + code, unicode_to_string(code), (void *)&field.port(), field.name()); } } } @@ -674,6 +717,31 @@ void natural_keyboard::build_codes(ioport_manager &manager) } } } + + // sort mapping entries by shift state + for (auto &mapping : devinfo.codemap) + { + std::sort( + mapping.second.begin(), + mapping.second.end(), + [] (keycode_map_entry const &x, keycode_map_entry const &y) { return x.shift < y.shift; }); + } + } +} + + +//------------------------------------------------- +// set_keyboard_enabled - enable or disable a +// device with key inputs +//------------------------------------------------- + +void natural_keyboard::set_keyboard_enabled(size_t n, bool enable) +{ + if (enable != m_keyboards[n].enabled) + { + m_keyboards[n].enabled = enable; + for (ioport_field &field : m_keyboards[n].keyfields) + field.live().lockout = !enable || ((field.type() == IPT_KEYBOARD) && in_use()); } } @@ -759,10 +827,19 @@ void natural_keyboard::internal_post(char32_t ch) } // add to the buffer, resizing if necessary - m_buffer[m_bufend++] = ch; - if ((m_bufend + 1) % m_buffer.size() == m_bufbegin) - m_buffer.resize(m_buffer.size() + KEY_BUFFER_SIZE); - m_bufend %= m_buffer.size(); + m_buffer[m_bufend] = ch; + size_t size = m_buffer.size(); + + if ((m_bufend + 1) % size == m_bufbegin) + { + if (size >= KEY_BUFFER_MAX_SIZE) + return; + + m_buffer.insert(m_buffer.begin() + m_bufbegin, KEY_BUFFER_CHUNK_SIZE, INVALID_CHAR); + m_bufbegin += KEY_BUFFER_CHUNK_SIZE; + } + + m_bufend = (m_bufend + 1) % size; } @@ -771,7 +848,7 @@ void natural_keyboard::internal_post(char32_t ch) // when posting a string of characters //------------------------------------------------- -void natural_keyboard::timer(void *ptr, int param) +void natural_keyboard::timer(s32 param) { if (!m_queue_chars.isnull()) { @@ -788,13 +865,15 @@ void natural_keyboard::timer(void *ptr, int param) // the driver does not have a queue_chars handler // loop through this character's component codes - const keycode_map_entry *const code = find_code(m_buffer[m_bufbegin]); + if (!m_fieldnum) + m_current_code = find_code(m_buffer[m_bufbegin]); bool advance; - if (code) + if (m_current_code) { + keycode_map_entry const code(*m_current_code); do { - ioport_field *const field = code->field[m_fieldnum]; + ioport_field *const field = code.field[m_fieldnum]; if (field) { // special handling for toggle fields @@ -804,8 +883,8 @@ void natural_keyboard::timer(void *ptr, int param) field->set_value(!field->digital_value()); } } - while (code->field[m_fieldnum] && (++m_fieldnum < code->field.size()) && m_status_keydown); - advance = (m_fieldnum >= code->field.size()) || !code->field[m_fieldnum]; + while (code.field[m_fieldnum] && (++m_fieldnum < code.field.size()) && m_status_keydown); + advance = (m_fieldnum >= code.field.size()) || !code.field[m_fieldnum]; } else { @@ -875,8 +954,22 @@ std::string natural_keyboard::unicode_to_string(char32_t ch) const const natural_keyboard::keycode_map_entry *natural_keyboard::find_code(char32_t ch) const { - keycode_map::const_iterator const found(m_keycode_map.find(ch)); - return (m_keycode_map.end() != found) ? &found->second : nullptr; + for (kbd_dev_info const &devinfo : m_keyboards) + { + if (devinfo.enabled) + { + keycode_map::const_iterator const found(devinfo.codemap.find(ch)); + if (devinfo.codemap.end() != found) + { + for (keycode_map_entry const &entry : found->second) + { + if (entry.condition.eval()) + return &entry; + } + } + } + } + return nullptr; } @@ -888,23 +981,46 @@ void natural_keyboard::dump(std::ostream &str) const { constexpr size_t left_column_width = 24; - // loop through all codes - bool first(true); - for (auto &code : m_keycode_map) + // loop through all devices + bool firstdev(true); + for (kbd_dev_info const &devinfo : m_keyboards) { - // describe the character code - std::string const description(string_format("%08X (%s) ", code.first, unicode_to_string(code.first))); + if (!firstdev) + str << '\n'; + util::stream_format( + str, + "%s(%s) - %s%s%s %sabled\n", + devinfo.device.get().type().shortname(), + devinfo.device.get().tag(), + devinfo.keyboard ? "keyboard" : "", + (devinfo.keyboard && devinfo.keypad) ? "/" : "", + devinfo.keypad ? "keypad" : "", + devinfo.enabled ? "en" : "dis"); + firstdev = false; + + // loop through all codes + for (auto &code : devinfo.codemap) + { + // describe the character code + std::string const description(string_format("%08X (%s) ", code.first, unicode_to_string(code.first))); - // pad with spaces - util::stream_format(str, "%-*s", left_column_width, description); + // pad with spaces + util::stream_format(str, "%-*s", left_column_width, description); - // identify the keys used - for (std::size_t field = 0; (code.second.field.size() > field) && code.second.field[field]; ++field) - util::stream_format(str, "%s'%s'", first ? "" : ", ", code.second.field[field]->name()); + for (auto &entry : code.second) + { + // identify the keys used + bool firstkey(true); + for (std::size_t field = 0; (entry.field.size() > field) && entry.field[field]; ++field) + { + util::stream_format(str, "%s'%s'", firstkey ? "" : ", ", entry.field[field]->name()); + firstkey = false; + } - // carriage return - str << '\n'; - first = false; + // carriage return + str << '\n'; + } + } } } @@ -934,7 +1050,7 @@ const char_info *char_info::find(char32_t target) { // perform a simple binary search to find the proper alternate int low = 0; - int high = ARRAY_LENGTH(charinfo); + int high = std::size(charinfo); while (high > low) { int middle = (high + low) / 2; @@ -964,7 +1080,7 @@ bool validate_natural_keyboard_statics(void) const char_info *ci; // check to make sure that charinfo is in order - for (i = 0; i < ARRAY_LENGTH(charinfo); i++) + for (i = 0; i < std::size(charinfo); i++) { if (last_char >= charinfo[i].ch) { @@ -975,7 +1091,7 @@ bool validate_natural_keyboard_statics(void) } // check to make sure that I can look up everything on alternate_charmap - for (i = 0; i < ARRAY_LENGTH(charinfo); i++) + for (i = 0; i < std::size(charinfo); i++) { ci = char_info::find(charinfo[i].ch); if (ci != &charinfo[i]) diff --git a/src/emu/natkeyboard.h b/src/emu/natkeyboard.h index 41b33ace105..57b58ffd173 100644 --- a/src/emu/natkeyboard.h +++ b/src/emu/natkeyboard.h @@ -1,5 +1,5 @@ // license:BSD-3-Clause -// copyright-holders:Aaron Giles +// copyright-holders:Aaron Giles,Vas Crabb /*************************************************************************** natkeyboard.h @@ -12,8 +12,13 @@ #pragma once +#include <array> +#include <functional> #include <iosfwd> +#include <string> +#include <string_view> #include <unordered_map> +#include <vector> //************************************************************************** @@ -21,9 +26,9 @@ //************************************************************************** // keyboard helper function delegates -typedef delegate<int (const char32_t *, size_t)> ioport_queue_chars_delegate; -typedef delegate<bool (char32_t)> ioport_accept_char_delegate; -typedef delegate<bool ()> ioport_charqueue_empty_delegate; +using ioport_queue_chars_delegate = delegate<int (const char32_t *, size_t)>; +using ioport_accept_char_delegate = delegate<bool (char32_t)>; +using ioport_charqueue_empty_delegate = delegate<bool ()>; // ======================> natural_keyboard @@ -40,22 +45,25 @@ public: // getters and queries running_machine &machine() const { return m_machine; } bool empty() const { return (m_bufbegin == m_bufend); } - bool full() const { return ((m_bufend + 1) % m_buffer.size()) == m_bufbegin; } - bool can_post() const { return (!m_queue_chars.isnull() || !m_keycode_map.empty()); } + bool can_post() const { return m_have_charkeys || !m_queue_chars.isnull(); } bool is_posting() const { return (!empty() || (!m_charqueue_empty.isnull() && !m_charqueue_empty())); } bool in_use() const { return m_in_use; } // configuration void configure(ioport_queue_chars_delegate queue_chars, ioport_accept_char_delegate accept_char, ioport_charqueue_empty_delegate charqueue_empty); void set_in_use(bool usage); + size_t keyboard_count() const { return m_keyboards.size(); } + device_t &keyboard_device(size_t n) const { return m_keyboards[n].device; } + bool keyboard_is_keypad(size_t n) const { return !m_keyboards[n].keyboard; } + bool keyboard_enabled(size_t n) const { return m_keyboards[n].enabled; } + void enable_keyboard(size_t n) { set_keyboard_enabled(n, true); } + void disable_keyboard(size_t n) { set_keyboard_enabled(n, false); } // posting - void post(char32_t ch); - void post(const char32_t *text, size_t length = 0, const attotime &rate = attotime::zero); - void post_utf8(const char *text, size_t length = 0, const attotime &rate = attotime::zero); - void post_utf8(const std::string &text, const attotime &rate = attotime::zero); - void post_coded(const char *text, size_t length = 0, const attotime &rate = attotime::zero); - void post_coded(const std::string &text, const attotime &rate = attotime::zero); + void post_char(char32_t ch, bool normalize_crlf = false); + void post(std::u32string_view text, const attotime &rate = attotime::zero); + void post_utf8(std::string_view text, const attotime &rate = attotime::zero); + void post_coded(std::string_view text, const attotime &rate = attotime::zero); void paste(); // debugging @@ -74,25 +82,44 @@ private: { std::array<ioport_field *, SHIFT_COUNT + 1> field; unsigned shift; + ioport_condition condition; + }; + typedef std::vector<keycode_map_entry> keycode_map_entries; + typedef std::unordered_map<char32_t, keycode_map_entries> keycode_map; + + // per-device character-to-key mapping + struct kbd_dev_info + { + kbd_dev_info(device_t &dev) : device(dev) { } + + std::reference_wrapper<device_t> device; + std::vector<std::reference_wrapper<ioport_field> > keyfields; + keycode_map codemap; + bool keyboard = false; + bool keypad = false; + bool enabled = false; }; - typedef std::unordered_map<char32_t, keycode_map_entry> keycode_map; // internal helpers - void build_codes(ioport_manager &manager); + void build_codes(); + void set_keyboard_enabled(size_t n, bool enable); bool can_post_directly(char32_t ch); bool can_post_alternate(char32_t ch); attotime choose_delay(char32_t ch); void internal_post(char32_t ch); - void timer(void *ptr, int param); + void timer(s32 param); std::string unicode_to_string(char32_t ch) const; const keycode_map_entry *find_code(char32_t ch) const; // internal state + std::vector<kbd_dev_info> m_keyboards; // info on keyboard devices in system running_machine & m_machine; // reference to our machine + bool m_have_charkeys; // are there keys with character? bool m_in_use; // is natural keyboard in use? u32 m_bufbegin; // index of starting character u32 m_bufend; // index of ending character std::vector<char32_t> m_buffer; // actual buffer + keycode_map_entry const * m_current_code; // current code being typed unsigned m_fieldnum; // current step in multi-key sequence bool m_status_keydown; // current keydown status bool m_last_cr; // was the last char a CR? @@ -101,7 +128,6 @@ private: ioport_queue_chars_delegate m_queue_chars; // queue characters callback ioport_accept_char_delegate m_accept_char; // accept character callback ioport_charqueue_empty_delegate m_charqueue_empty; // character queue empty callback - keycode_map m_keycode_map; // keycode map }; diff --git a/src/emu/network.cpp b/src/emu/network.cpp index 259cca30efb..5953031133e 100644 --- a/src/emu/network.cpp +++ b/src/emu/network.cpp @@ -2,17 +2,22 @@ // copyright-holders:Carl /*************************************************************************** - network.c + network.cpp Core network functions and definitions. + ***************************************************************************/ -#include <ctype.h> #include "emu.h" #include "network.h" + #include "config.h" +#include "dinetwork.h" + #include "xmlfile.h" +#include <cctype> + //************************************************************************** // NETWORK MANAGER //************************************************************************** @@ -24,7 +29,10 @@ network_manager::network_manager(running_machine &machine) : m_machine(machine) { - machine.configuration().config_register("network", config_load_delegate(&network_manager::config_load, this), config_save_delegate(&network_manager::config_save, this)); + machine.configuration().config_register( + "network", + configuration_manager::load_delegate(&network_manager::config_load, this), + configuration_manager::save_delegate(&network_manager::config_save, this)); } //------------------------------------------------- @@ -32,9 +40,9 @@ network_manager::network_manager(running_machine &machine) // configuration file //------------------------------------------------- -void network_manager::config_load(config_type cfg_type, util::xml::data_node const *parentnode) +void network_manager::config_load(config_type cfg_type, config_level cfg_level, util::xml::data_node const *parentnode) { - if ((cfg_type == config_type::GAME) && (parentnode != nullptr)) + if ((cfg_type == config_type::SYSTEM) && parentnode) { for (util::xml::data_node const *node = parentnode->get_child("device"); node; node = node->get_next_sibling("device")) { @@ -42,14 +50,14 @@ void network_manager::config_load(config_type cfg_type, util::xml::data_node con if ((tag != nullptr) && (tag[0] != '\0')) { - for (device_network_interface &network : network_interface_iterator(machine().root_device())) + for (device_network_interface &network : network_interface_enumerator(machine().root_device())) { if (!strcmp(tag, network.device().tag())) { int interface = node->get_attribute_int("interface", 0); network.set_interface(interface); const char *mac_addr = node->get_attribute_string("mac", nullptr); if (mac_addr != nullptr && strlen(mac_addr) == 17) { - char mac[7]; + uint8_t mac[6]; unsigned int mac_num[6]; sscanf(mac_addr, "%02x:%02x:%02x:%02x:%02x:%02x", &mac_num[0], &mac_num[1], &mac_num[2], &mac_num[3], &mac_num[4], &mac_num[5]); for (int i = 0; i<6; i++) mac[i] = mac_num[i]; @@ -69,20 +77,21 @@ void network_manager::config_load(config_type cfg_type, util::xml::data_node con void network_manager::config_save(config_type cfg_type, util::xml::data_node *parentnode) { - /* only care about game-specific data */ - if (cfg_type == config_type::GAME) + // only save about system-specific data + if (cfg_type == config_type::SYSTEM) { - for (device_network_interface &network : network_interface_iterator(machine().root_device())) + for (device_network_interface &network : network_interface_enumerator(machine().root_device())) { util::xml::data_node *const node = parentnode->add_child("device", nullptr); - if (node != nullptr) + if (node) { node->set_attribute("tag", network.device().tag()); node->set_attribute_int("interface", network.get_interface()); - const char *mac = network.get_mac(); - char mac_addr[6 * 3]; - sprintf(mac_addr, "%02x:%02x:%02x:%02x:%02x:%02x", u8(mac[0]), u8(mac[1]), u8(mac[2]), u8(mac[3]), u8(mac[4]), u8(mac[5])); - node->set_attribute("mac", mac_addr); + const std::array<u8, 6> &mac = network.get_mac(); + const std::string mac_addr = util::string_format( + "%02x:%02x:%02x:%02x:%02x:%02x", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + node->set_attribute("mac", mac_addr.c_str()); } } } diff --git a/src/emu/network.h b/src/emu/network.h index 5ac8e4f2d37..fff7d68a120 100644 --- a/src/emu/network.h +++ b/src/emu/network.h @@ -23,7 +23,7 @@ public: // getters running_machine &machine() const { return m_machine; } private: - void config_load(config_type cfg_type, util::xml::data_node const *parentnode); + void config_load(config_type cfg_type, config_level cfg_lvl, util::xml::data_node const *parentnode); void config_save(config_type cfg_type, util::xml::data_node *parentnode); // internal state diff --git a/src/emu/output.cpp b/src/emu/output.cpp index 5969d1dbfc5..95b92cfc5d0 100644 --- a/src/emu/output.cpp +++ b/src/emu/output.cpp @@ -2,17 +2,19 @@ // copyright-holders:Nicola Salmoria, Aaron Giles, Vas Crabb /*************************************************************************** - output.c + output.cpp General purpose output routines. + ***************************************************************************/ #include "emu.h" -#include "coreutil.h" -#include "modules/output/output_module.h" +#include "output.h" + +#include <algorithm> -#define OUTPUT_VERBOSE 0 +#define OUTPUT_VERBOSE 0 @@ -55,10 +57,10 @@ void output_manager::output_item::notify(s32 value) // OUTPUT ITEM PROXY //************************************************************************** -void output_manager::item_proxy::resolve(device_t &device, std::string const &name) +void output_manager::item_proxy::resolve(device_t &device, std::string_view name) { assert(!m_item); - m_item = &device.machine().output().find_or_create_item(name.c_str(), 0); + m_item = &device.machine().output().find_or_create_item(name, 0); } @@ -67,24 +69,54 @@ void output_manager::item_proxy::resolve(device_t &device, std::string const &na // OUTPUT MANAGER //************************************************************************** -//------------------------------------------------- -// output_manager - constructor -//------------------------------------------------- +/*------------------------------------------------- + output_manager - constructor +-------------------------------------------------*/ output_manager::output_manager(running_machine &machine) - : m_machine(machine), - m_uniqueid(12345) + : m_machine(machine) + , m_uniqueid(12345) { - /* add pause callback */ + // add callbacks machine.add_notifier(MACHINE_NOTIFY_PAUSE, machine_notify_delegate(&output_manager::pause, this)); machine.add_notifier(MACHINE_NOTIFY_RESUME, machine_notify_delegate(&output_manager::resume, this)); + machine.save().register_presave(save_prepost_delegate(FUNC(output_manager::presave), this)); + machine.save().register_postload(save_prepost_delegate(FUNC(output_manager::postload), this)); } + +/*------------------------------------------------- + register_save - register for save states +-------------------------------------------------*/ + +void output_manager::register_save() +{ + assert(m_save_order.empty()); + assert(!m_save_data); + + // make space for the data + m_save_order.clear(); + m_save_order.reserve(m_itemtable.size()); + m_save_data = std::make_unique<s32 []>(m_itemtable.size()); + + // sort existing outputs by name and register for save + for (auto &item : m_itemtable) + m_save_order.emplace_back(item.second); + std::sort(m_save_order.begin(), m_save_order.end(), [] (auto const &l, auto const &r) { return l.get().name() < r.get().name(); }); + + // register the reserved space for saving + machine().save().save_pointer(nullptr, "output", nullptr, 0, NAME(m_save_data), m_itemtable.size()); + if (OUTPUT_VERBOSE) + osd_printf_verbose("Registered %u outputs for save states\n", m_itemtable.size()); + +} + + /*------------------------------------------------- find_item - find an item based on a string -------------------------------------------------*/ -output_manager::output_item* output_manager::find_item(const char *string) +output_manager::output_item *output_manager::find_item(std::string_view string) { auto item = m_itemtable.find(std::string(string)); if (item != m_itemtable.end()) @@ -98,22 +130,26 @@ output_manager::output_item* output_manager::find_item(const char *string) create_new_item - create a new item -------------------------------------------------*/ -output_manager::output_item &output_manager::create_new_item(const char *outname, s32 value) +output_manager::output_item &output_manager::create_new_item(std::string_view outname, s32 value) { + if (OUTPUT_VERBOSE) + osd_printf_verbose("Creating output %s = %d%s\n", outname, value, m_save_data ? " (will not be saved)" : ""); + auto const ins(m_itemtable.emplace( std::piecewise_construct, std::forward_as_tuple(outname), - std::forward_as_tuple(*this, outname, m_uniqueid++, value))); + std::forward_as_tuple(*this, std::string(outname), m_uniqueid++, value))); assert(ins.second); return ins.first->second; } -output_manager::output_item &output_manager::find_or_create_item(const char *outname, s32 value) +output_manager::output_item &output_manager::find_or_create_item(std::string_view outname, s32 value) { output_item *const item = find_item(outname); return item ? *item : create_new_item(outname, value); } + /*------------------------------------------------- output_pause - send pause message -------------------------------------------------*/ @@ -130,44 +166,40 @@ void output_manager::resume() /*------------------------------------------------- - output_set_value - set the value of an output + presave - prepare data for save state -------------------------------------------------*/ -void output_manager::set_value(const char *outname, s32 value) +void output_manager::presave() { - output_item *const item = find_item(outname); - - // if no item of that name, create a new one and force notification - if (!item) - create_new_item(outname, value).notify(value); - else - item->set(value); // set the new value (notifies on change) + for (size_t i = 0; m_save_order.size() > i; ++i) + m_save_data[i] = m_save_order[i].get().get(); } /*------------------------------------------------- - output_set_indexed_value - set the value of an - indexed output + postload - restore loaded data -------------------------------------------------*/ -void output_manager::set_indexed_value(const char *basename, int index, int value) +void output_manager::postload() { - char buffer[100]; - char *dest = buffer; + for (size_t i = 0; m_save_order.size() > i; ++i) + m_save_order[i].get().set(m_save_data[i]); +} + - /* copy the string */ - while (*basename != 0) - *dest++ = *basename++; +/*------------------------------------------------- + output_set_value - set the value of an output +-------------------------------------------------*/ - /* append the index */ - if (index >= 1000) *dest++ = '0' + ((index / 1000) % 10); - if (index >= 100) *dest++ = '0' + ((index / 100) % 10); - if (index >= 10) *dest++ = '0' + ((index / 10) % 10); - *dest++ = '0' + (index % 10); - *dest++ = 0; +void output_manager::set_value(std::string_view outname, s32 value) +{ + output_item *const item = find_item(outname); - /* set the value */ - set_value(buffer, value); + // if no item of that name, create a new one and force notification + if (!item) + create_new_item(outname, value).notify(value); + else + item->set(value); // set the new value (notifies on change) } @@ -176,7 +208,7 @@ void output_manager::set_indexed_value(const char *basename, int index, int valu output -------------------------------------------------*/ -s32 output_manager::get_value(const char *outname) +s32 output_manager::get_value(std::string_view outname) { output_item const *const item = find_item(outname); @@ -185,36 +217,27 @@ s32 output_manager::get_value(const char *outname) } -/*------------------------------------------------- - output_set_notifier - sets a notifier callback - for a particular output, or for all outputs - if nullptr is specified --------------------------------------------------*/ +//------------------------------------------------- +// set_notifier - sets a notifier callback for a +// particular output +//------------------------------------------------- -void output_manager::set_notifier(const char *outname, output_notifier_func callback, void *param) +void output_manager::set_notifier(std::string_view outname, notifier_func callback, void *param) { // if an item is specified, find/create it - if (outname) - { - output_item *const item = find_item(outname); - (item ? *item : create_new_item(outname, 0)).set_notifier(callback, param); - } - else - { - m_global_notifylist.emplace_back(callback, param); - } + output_item *const item = find_item(outname); + (item ? *item : create_new_item(outname, 0)).set_notifier(callback, param); } -/*------------------------------------------------- - output_notify_all - immediately call the given - notifier for all outputs --------------------------------------------------*/ +//------------------------------------------------- +// set_global_notifier - sets a notifier callback +// for all outputs +//------------------------------------------------- -void output_manager::notify_all(output_module *module) +void output_manager::set_global_notifier(notifier_func callback, void *param) { - for (auto &item : m_itemtable) - module->notify(item.second.name().c_str(), item.second.get()); + m_global_notifylist.emplace_back(callback, param); } @@ -223,7 +246,7 @@ void output_manager::notify_all(output_module *module) a given name -------------------------------------------------*/ -u32 output_manager::name_to_id(const char *outname) +u32 output_manager::name_to_id(std::string_view outname) { // if no item, ID is 0 output_item const *const item = find_item(outname); @@ -236,12 +259,12 @@ u32 output_manager::name_to_id(const char *outname) to a given unique ID -------------------------------------------------*/ -const char *output_manager::id_to_name(u32 id) +char const *output_manager::id_to_name(u32 id) { for (auto &item : m_itemtable) if (item.second.id() == id) return item.second.name().c_str(); - /* nothing found, return nullptr */ + // nothing found, return nullptr return nullptr; } diff --git a/src/emu/output.h b/src/emu/output.h index 79cf1c0af8b..79c0c61eb16 100644 --- a/src/emu/output.h +++ b/src/emu/output.h @@ -6,23 +6,25 @@ General purpose output routines. ***************************************************************************/ +#ifndef MAME_EMU_OUTPUT_H +#define MAME_EMU_OUTPUT_H #pragma once -#ifndef __EMU_H__ -#error Dont include this file directly; include emu.h instead. -#endif - -#ifndef MAME_EMU_OUTPUT_H -#define MAME_EMU_OUTPUT_H +#include <functional> +#include <iterator> +#include <memory> +#include <string> +#include <type_traits> +#include <unordered_map> +#include <utility> +#include <vector> /*************************************************************************** TYPE DEFINITIONS ***************************************************************************/ -typedef void (*output_notifier_func)(const char *outname, s32 value, void *param); - // ======================> output_manager class output_manager @@ -30,10 +32,12 @@ class output_manager private: template <typename Input, std::make_unsigned_t<Input> DefaultMask> friend class devcb_write; + typedef void (*notifier_func)(const char *outname, s32 value, void *param); + class output_notify { public: - output_notify(output_notifier_func callback, void *param) + output_notify(notifier_func callback, void *param) : m_notifier(callback) , m_param(param) { @@ -42,8 +46,8 @@ private: void operator()(char const *outname, s32 value) const { m_notifier(outname, value, m_param); } private: - output_notifier_func m_notifier; // callback to call - void * m_param; // parameter to pass the callback + notifier_func m_notifier; // callback to call + void * m_param; // parameter to pass the callback }; using notify_vector = std::vector<output_notify>; @@ -67,7 +71,7 @@ private: void set(s32 value) { if (m_value != value) { notify(value); } } void notify(s32 value); - void set_notifier(output_notifier_func callback, void *param) { m_notifylist.emplace_back(callback, param); } + void set_notifier(notifier_func callback, void *param) { m_notifylist.emplace_back(callback, param); } private: output_manager &m_manager; // parent output manager @@ -81,7 +85,7 @@ private: { public: item_proxy() = default; - void resolve(device_t &device, std::string const &name); + void resolve(device_t &device, std::string_view name); operator s32() const { return m_item->get(); } s32 operator=(s32 value) { m_item->set(value); return m_item->get(); } private: @@ -105,6 +109,7 @@ public: auto &operator[](unsigned n) { return m_proxies[n]; } auto &operator[](unsigned n) const { return m_proxies[n]; } + auto size() const { return std::size(m_proxies); } auto begin() { return std::begin(m_proxies); } auto end() { return std::end(m_proxies); } auto begin() const { return std::begin(m_proxies); } @@ -158,45 +163,57 @@ public: // construction/destruction output_manager(running_machine &machine); + // register for save states + void register_save() ATTR_COLD; + // getters running_machine &machine() const { return m_machine; } // set the value for a given output - void set_value(const char *outname, s32 value); + void set_value(std::string_view outname, s32 value); // return the current value for a given output - s32 get_value(const char *outname); + s32 get_value(std::string_view outname); - // set a notifier on a particular output, or globally if nullptr - void set_notifier(const char *outname, output_notifier_func callback, void *param); + // set a notifier on a particular output + void set_notifier(std::string_view outname, notifier_func callback, void *param); - // set a notifier on a particular output, or globally if nullptr - void notify_all(output_module *module); + // set a notifier globally + void set_global_notifier(notifier_func callback, void *param); + + // immdediately call a notifier for all outputs + template <typename T> void notify_all(T &¬ifier) const + { + for (auto const &item : m_itemtable) + notifier(item.second.name().c_str(), item.second.get()); + } // map a name to a unique ID - u32 name_to_id(const char *outname); + u32 name_to_id(std::string_view outname); // map a unique ID back to a name const char *id_to_name(u32 id); - void pause(); - void resume(); - private: - // set an indexed value for an output (concatenates basename + index) - void set_indexed_value(const char *basename, int index, int value); + output_item *find_item(std::string_view string); + output_item &create_new_item(std::string_view outname, s32 value); + output_item &find_or_create_item(std::string_view outname, s32 value); - output_item *find_item(const char *string); - output_item &create_new_item(const char *outname, s32 value); - output_item &find_or_create_item(const char *outname, s32 value); + // event handlers + void pause(); + void resume(); + void presave() ATTR_COLD; + void postload() ATTR_COLD; // internal state running_machine &m_machine; // reference to our machine std::unordered_map<std::string, output_item> m_itemtable; notify_vector m_global_notifylist; + std::vector<std::reference_wrapper<output_item> > m_save_order; + std::unique_ptr<s32 []> m_save_data; u32 m_uniqueid; }; template <unsigned... N> using output_finder = output_manager::output_finder<void, N...>; -#endif // MAME_EMU_OUTPUT_H +#endif // MAME_EMU_OUTPUT_H diff --git a/src/emu/parameters.cpp b/src/emu/parameters.cpp index 9a0a38a74ce..8b02cd47ebf 100644 --- a/src/emu/parameters.cpp +++ b/src/emu/parameters.cpp @@ -2,7 +2,7 @@ // copyright-holders:Olivier Galibert, Aaron Giles /*************************************************************************** - parameters.c + parameters.cpp Per-game parameters handling. diff --git a/src/emu/profiler.cpp b/src/emu/profiler.cpp index c3c0ea05608..19f3908f097 100644 --- a/src/emu/profiler.cpp +++ b/src/emu/profiler.cpp @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles /*************************************************************************** - profiler.c + profiler.cpp Functions to manage profiling of MAME execution. @@ -89,8 +89,11 @@ real_profiler_state::real_profiler_state() // reset - initializes state //------------------------------------------------- -void real_profiler_state::reset(bool enabled) +void real_profiler_state::reset(bool enabled) noexcept { + // disabling the profiler from the UI happens while PROFILER_EXTRA is active + //assert(!m_filoptr || (m_filoptr == m_filo)); + m_text_time = attotime::never; if (enabled) @@ -117,10 +120,10 @@ void real_profiler_state::reset(bool enabled) const char *real_profiler_state::text(running_machine &machine) { - start(PROFILER_PROFILER); + auto profile = start(PROFILER_PROFILER); // get the current time - attotime current_time = machine.scheduler().time(); + attotime const current_time = machine.scheduler().time(); // we only want to update the text periodically if ((m_text_time == attotime::never) || ((current_time - m_text_time).as_double() >= TEXT_UPDATE_TIME)) @@ -129,7 +132,6 @@ const char *real_profiler_state::text(running_machine &machine) m_text_time = current_time; } - stop(); return m_text.c_str(); } @@ -193,7 +195,7 @@ void real_profiler_state::update_text(running_machine &machine) } // loop over all types and generate the string - device_iterator iter(machine.root_device()); + device_enumerator iter(machine.root_device()); std::ostringstream stream; for (curtype = PROFILER_DEVICE_FIRST; curtype < PROFILER_TOTAL; ++curtype) { diff --git a/src/emu/profiler.h b/src/emu/profiler.h index cff0801f65c..8ea65ac7f07 100644 --- a/src/emu/profiler.h +++ b/src/emu/profiler.h @@ -8,11 +8,11 @@ **************************************************************************** - Profiling is scope-based. To start profiling, put a profiler_scope + Profiling is scope-based. To start profiling, put a profiler scope object on the stack. To end profiling, just end the scope: { - profiler_scope scope(PROFILER_VIDEO); + auto scope = g_profiler.start(PROFILER_VIDEO); your_work_here(); } @@ -26,6 +26,17 @@ #pragma once +#include "emufwd.h" + +#include "attotime.h" + +#include "eminline.h" // for get_profile_ticks() +#include "osdcore.h" + +#include <cstdio> +#include <cstdlib> +#include <string> + //************************************************************************** // CONSTANTS @@ -83,18 +94,57 @@ DECLARE_ENUM_INCDEC_OPERATORS(profile_type) class real_profiler_state { public: + class scope + { + private: + real_profiler_state &m_host; + bool m_active; + + public: + scope(scope const &) = delete; + scope &operator=(scope const &) = delete; + + scope(scope &&that) noexcept : m_host(that.m_host), m_active(that.m_active) + { + that.m_active = false; + } + + scope(real_profiler_state &host, profile_type type) noexcept : m_host(host), m_active(false) + { + if (m_host.enabled()) + { + m_host.real_start(type); + m_active = true; + } + } + + ~scope() + { + stop(); + } + + void stop() noexcept + { + if (m_active) + { + m_host.real_stop(); + m_active = false; + } + } + }; + // construction/destruction real_profiler_state(); // getters - bool enabled() const + bool enabled() const noexcept { return m_filoptr != nullptr; } const char *text(running_machine &machine); // enable/disable - void enable(bool state = true) + void enable(bool state = true) noexcept { if (state != enabled()) { @@ -103,22 +153,31 @@ public: } // start/stop - void start(profile_type type) { if (enabled()) real_start(type); } - void stop() { if (enabled()) real_stop(); } + [[nodiscard]] auto start(profile_type type) noexcept { return scope(*this, type); } private: - void reset(bool enabled); + // an entry in the FILO + struct filo_entry + { + int type; // type of entry + osd_ticks_t start; // start time + }; + + void reset(bool enabled) noexcept; void update_text(running_machine &machine); //------------------------------------------------- // real_start - mark the beginning of a // profiler entry //------------------------------------------------- - ATTR_FORCE_INLINE void real_start(profile_type type) + ATTR_FORCE_INLINE void real_start(profile_type type) noexcept { // fail if we overflow - if (m_filoptr >= &m_filo[ARRAY_LENGTH(m_filo) - 1]) - throw emu_fatalerror("Profiler FILO overflow (type = %d)\n", type); + if (UNEXPECTED(m_filoptr >= &m_filo[std::size(m_filo) - 1])) + { + std::fprintf(stderr, "Profiler FILO overflow (type = %d)\n", int(type)); + std::abort(); + } // get current tick count osd_ticks_t curticks = get_profile_ticks(); @@ -137,7 +196,7 @@ private: //------------------------------------------------- // real_stop - mark the end of a profiler entry //------------------------------------------------- - ATTR_FORCE_INLINE void real_stop() + ATTR_FORCE_INLINE void real_stop() noexcept { // degenerate scenario if (UNEXPECTED(m_filoptr <= m_filo)) @@ -156,13 +215,6 @@ private: m_filoptr->start = curticks; } - // an entry in the FILO - struct filo_entry - { - int type; // type of entry - osd_ticks_t start; // start time - }; - // internal state filo_entry * m_filoptr; // current FILO index std::string m_text; // profiler text @@ -177,19 +229,35 @@ private: class dummy_profiler_state { public: + class scope + { + private: + dummy_profiler_state &m_host; + + public: + scope(scope const &) = delete; + scope &operator=(scope const &) = delete; + scope(scope &&that) noexcept = default; + scope(dummy_profiler_state &host, profile_type type) noexcept : m_host(host) { } + ~scope() { m_host.real_stop(); } + void stop() noexcept { } + }; + // construction/destruction dummy_profiler_state(); // getters - bool enabled() const { return false; } + bool enabled() const noexcept { return false; } const char *text(running_machine &machine) { return ""; } // enable/disable - void enable(bool state = true) { } + void enable(bool state = true) noexcept { } // start/stop - void start(profile_type type) { } - void stop() { } + [[nodiscard]] auto start(profile_type type) noexcept { return scope(*this, type); } + +private: + void real_stop() noexcept { } }; @@ -210,4 +278,4 @@ typedef dummy_profiler_state profiler_state; extern profiler_state g_profiler; -#endif /* MAME_EMU_PROFILER_H */ +#endif // MAME_EMU_PROFILER_H diff --git a/src/emu/recording.cpp b/src/emu/recording.cpp new file mode 100644 index 00000000000..91bc0e58bf7 --- /dev/null +++ b/src/emu/recording.cpp @@ -0,0 +1,309 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + recording.cpp + + MAME AVI/MNG video recording routines. + +***************************************************************************/ + +#include "emu.h" + +#include "fileio.h" +#include "main.h" +#include "screen.h" + +#include "aviio.h" +#include "png.h" + + +namespace +{ + class avi_movie_recording : public movie_recording + { + public: + avi_movie_recording(screen_device *screen) + : movie_recording(screen) + { + } + + bool initialize(running_machine &machine, std::unique_ptr<emu_file> &&file, int32_t width, int32_t height); + virtual bool add_sound_to_recording(const s16 *sound, int numsamples) override; + + protected: + virtual bool append_single_video_frame(bitmap_rgb32 &bitmap, const rgb_t *palette, int palette_entries) override; + + private: + avi_file::ptr m_avi_file; // handle to the open movie file + }; + + + class mng_movie_recording : public movie_recording + { + public: + mng_movie_recording(screen_device *screen, std::map<std::string, std::string> &&info_fields); + ~mng_movie_recording(); + + bool initialize(std::unique_ptr<emu_file> &&file, bitmap_t &snap_bitmap); + virtual bool add_sound_to_recording(const s16 *sound, int numsamples) override; + + protected: + virtual bool append_single_video_frame(bitmap_rgb32 &bitmap, const rgb_t *palette, int palette_entries) override; + + private: + std::unique_ptr<emu_file> m_mng_file; // handle to the open movie file + std::map<std::string, std::string> m_info_fields; + }; +}; + + +//************************************************************************** +// MOVIE RECORDING +//************************************************************************** + +//------------------------------------------------- +// movie_recording - constructor +//------------------------------------------------- + +movie_recording::movie_recording(screen_device *screen) + : m_screen(screen) + , m_frame_period(attotime::zero) + , m_next_frame_time(attotime::zero) + , m_frame(0) +{ +} + + +//------------------------------------------------- +// movie_recording - destructor +//------------------------------------------------- + +movie_recording::~movie_recording() +{ +} + + +//------------------------------------------------- +// movie_recording::append_video_frame +//------------------------------------------------- + +bool movie_recording::append_video_frame(bitmap_rgb32 &bitmap, attotime curtime) +{ + // identify the palette + bool has_palette = screen() && screen()->has_palette(); + const rgb_t *palette = has_palette ? screen()->palette().palette()->entry_list_adjusted() : nullptr; + int palette_entries = has_palette ? screen()->palette().entries() : 0; + + // keep appending frames until we're at curtime + while (next_frame_time() <= curtime) + { + // append this bitmap as a single frame + if (!append_single_video_frame(bitmap, palette, palette_entries)) + return false; + m_frame++; + + // advance time + set_next_frame_time(next_frame_time() + frame_period()); + } + return true; +} + + +//------------------------------------------------- +// movie_recording::create - creates a new recording +// for the specified format +//------------------------------------------------- + +movie_recording::ptr movie_recording::create(running_machine &machine, screen_device *screen, format fmt, std::unique_ptr<emu_file> &&file, bitmap_rgb32 &snap_bitmap) +{ + movie_recording::ptr result; + switch (fmt) + { + case movie_recording::format::AVI: + { + auto avi_recording = std::make_unique<avi_movie_recording>(screen); + if (avi_recording->initialize(machine, std::move(file), snap_bitmap.width(), snap_bitmap.height())) + result = std::move(avi_recording); + } + break; + + case movie_recording::format::MNG: + { + std::map<std::string, std::string> info_fields; + info_fields["Software"] = std::string(emulator_info::get_appname()).append(" ").append(emulator_info::get_build_version()); + info_fields["System"] = std::string(machine.system().manufacturer).append(" ").append(machine.system().type.fullname()); + + auto mng_recording = std::make_unique<mng_movie_recording>(screen, std::move(info_fields)); + if (mng_recording->initialize(std::move(file), snap_bitmap)) + result = std::move(mng_recording); + } + break; + + default: + throw false; + } + + // if we successfully create a recording, set the current time and return it + if (result) + { + result->set_next_frame_time(machine.time()); + result->set_channel_count(machine.sound().outputs_count()); + } + return result; +} + + +//------------------------------------------------- +// movie_recording::format_file_extension +//------------------------------------------------- + +const char *movie_recording::format_file_extension(movie_recording::format fmt) +{ + switch (fmt) + { + case format::AVI: return "avi"; + case format::MNG: return "mng"; + default: throw false; + } +} + + +//------------------------------------------------- +// avi_movie_recording::initialize +//------------------------------------------------- + +bool avi_movie_recording::initialize(running_machine &machine, std::unique_ptr<emu_file> &&file, int32_t width, int32_t height) +{ + // we only use the file we're passed to get the full path + std::string fullpath = file->fullpath(); + file.reset(); + + // build up information about this new movie + avi_file::movie_info info; + info.video_format = 0; + info.video_timescale = 0x3fff'fffc; // multiple of 60, for screenless machines + info.video_sampletime = screen() ? screen()->frame_period().as_ticks(info.video_timescale) : 0x0111'1111; + info.video_numsamples = 0; + info.video_width = width; + info.video_height = height; + info.video_depth = 24; + + info.audio_format = 0; + info.audio_timescale = machine.sample_rate(); + info.audio_sampletime = 1; + info.audio_numsamples = 0; + info.audio_channels = machine.sound().outputs_count(); + info.audio_samplebits = 16; + info.audio_samplerate = machine.sample_rate(); + + m_channels = info.audio_channels; + + // compute the frame time + set_frame_period(attotime::from_ticks(info.video_sampletime, info.video_timescale)); + + // create the file + avi_file::error avierr = avi_file::create(fullpath, info, m_avi_file); + if (avierr != avi_file::error::NONE) + osd_printf_error("Error creating AVI: %s\n", avi_file::error_string(avierr)); + return avierr == avi_file::error::NONE; +} + + +//------------------------------------------------- +// avi_movie_recording::append_single_video_frame +//------------------------------------------------- + +bool avi_movie_recording::append_single_video_frame(bitmap_rgb32 &bitmap, const rgb_t *palette, int palette_entries) +{ + avi_file::error avierr = m_avi_file->append_video_frame(bitmap); + return avierr == avi_file::error::NONE; +} + + +//------------------------------------------------- +// avi_movie_recording::append_video_frame +//------------------------------------------------- + +bool avi_movie_recording::add_sound_to_recording(const s16 *sound, int numsamples) +{ + auto profile = g_profiler.start(PROFILER_MOVIE_REC); + + // write the next frame + avi_file::error avierr = avi_file::error::NONE; + for (int channel = 0; channel != m_channels && avierr == avi_file::error::NONE; channel ++) + avierr = m_avi_file->append_sound_samples(channel, sound + channel, numsamples, m_channels-1); + + return avierr == avi_file::error::NONE; +} + + +//------------------------------------------------- +// mng_movie_recording - constructor +//------------------------------------------------- + +mng_movie_recording::mng_movie_recording(screen_device *screen, std::map<std::string, std::string> &&info_fields) + : movie_recording(screen) + , m_info_fields(std::move(info_fields)) +{ +} + + +//------------------------------------------------- +// mng_movie_recording - destructor +//------------------------------------------------- + +mng_movie_recording::~mng_movie_recording() +{ + if (m_mng_file) + util::mng_capture_stop(*m_mng_file); +} + + +//------------------------------------------------- +// mng_movie_recording::initialize +//------------------------------------------------- + +bool mng_movie_recording::initialize(std::unique_ptr<emu_file> &&file, bitmap_t &snap_bitmap) +{ + // compute the frame time (MNG rate is an unsigned integer) + attotime period = screen() ? screen()->frame_period() : attotime::from_hz(screen_device::DEFAULT_FRAME_RATE); + u32 rate = u32(period.as_hz()); + set_frame_period(attotime::from_hz(rate)); + + m_mng_file = std::move(file); + std::error_condition const pngerr = util::mng_capture_start(*m_mng_file, snap_bitmap, rate); + if (pngerr) + osd_printf_error("Error capturing MNG (%s:%d %s)\n", pngerr.category().name(), pngerr.value(), pngerr.message()); + return !pngerr; +} + + +//------------------------------------------------- +// mng_movie_recording::append_single_video_frame +//------------------------------------------------- + +bool mng_movie_recording::append_single_video_frame(bitmap_rgb32 &bitmap, const rgb_t *palette, int palette_entries) +{ + // set up the text fields in the movie info + util::png_info pnginfo; + if (current_frame() == 0) + { + for (auto &ent : m_info_fields) + pnginfo.add_text(ent.first, ent.second); + } + + std::error_condition const error = util::mng_capture_frame(*m_mng_file, pnginfo, bitmap, palette_entries, palette); + return !error; +} + + +//------------------------------------------------- +// mng_movie_recording::add_sound_to_recording +//------------------------------------------------- + +bool mng_movie_recording::add_sound_to_recording(const s16 *sound, int numsamples) +{ + // not supported; do nothing + return true; +} diff --git a/src/emu/recording.h b/src/emu/recording.h new file mode 100644 index 00000000000..98798063a73 --- /dev/null +++ b/src/emu/recording.h @@ -0,0 +1,89 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + recording.h + + MAME AVI/MNG video recording routines. + +***************************************************************************/ + +#pragma once + +#ifndef __EMU_H__ +#error Dont include this file directly; include emu.h instead. +#endif + +#ifndef MAME_EMU_RECORDING_H +#define MAME_EMU_RECORDING_H + +#include <memory> + +#include "attotime.h" +#include "palette.h" + +class screen_device; + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> movie_recording + +class movie_recording +{ +public: + // movie format options + enum class format + { + MNG, + AVI + }; + + typedef std::unique_ptr<movie_recording> ptr; + + // dtor + virtual ~movie_recording(); + + // accessors + screen_device *screen() { return m_screen; } + attotime frame_period() { return m_frame_period; } + void set_next_frame_time(attotime time) { m_next_frame_time = time; } + void set_channel_count(int channels) { m_channels = channels; } + attotime next_frame_time() const { return m_next_frame_time; } + + // methods + bool append_video_frame(bitmap_rgb32 &bitmap, attotime curtime); + + // virtuals + virtual bool add_sound_to_recording(const s16 *sound, int numsamples) = 0; + + // statics + static movie_recording::ptr create(running_machine &machine, screen_device *screen, format fmt, std::unique_ptr<emu_file> &&file, bitmap_rgb32 &snap_bitmap); + static const char *format_file_extension(format fmt); + +protected: + int m_channels; // count of audio channels + + // ctor + movie_recording(screen_device *screen); + movie_recording(const movie_recording &) = delete; + movie_recording(movie_recording &&) = delete; + + // virtuals + virtual bool append_single_video_frame(bitmap_rgb32 &bitmap, const rgb_t *palette, int palette_entries) = 0; + + // accessors + int current_frame() const { return m_frame; } + void set_frame_period(attotime time) { m_frame_period = time; } + +private: + screen_device * m_screen; // screen associated with this movie (can be nullptr) + attotime m_frame_period; // duration of movie frame + attotime m_next_frame_time; // time of next frame + int m_frame; // current movie frame number +}; + + +#endif // MAME_EMU_RECORDING_H diff --git a/src/emu/render.cpp b/src/emu/render.cpp index 58d2c2961c9..545827262bb 100644 --- a/src/emu/render.cpp +++ b/src/emu/render.cpp @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles /*************************************************************************** - render.c + render.cpp Core rendering system. @@ -37,20 +37,27 @@ ***************************************************************************/ #include "emu.h" -#include "emuopts.h" #include "render.h" + +#include "corestr.h" +#include "emuopts.h" +#include "fileio.h" #include "rendfont.h" #include "rendlay.h" #include "rendutil.h" #include "config.h" #include "drivenum.h" -#include "xmlfile.h" +#include "layout/generic.h" + #include "ui/uimain.h" -#include <zlib.h> +#include "util/ioprocsfilter.h" +#include "util/language.h" +#include "util/path.h" +#include "util/xmlfile.h" #include <algorithm> -#include <functional> +#include <limits> @@ -93,6 +100,40 @@ struct render_target::object_transform }; +struct render_target::pointer_info +{ + pointer_info() noexcept + : type(osd::ui_event_handler::pointer::UNKNOWN) + , oldpos(std::numeric_limits<float>::min(), std::numeric_limits<float>::min()) + , newpos(std::numeric_limits<float>::min(), std::numeric_limits<float>::min()) + , oldbuttons(0U) + , newbuttons(0U) + , edges(0U, 0U) + { + } + + osd::ui_event_handler::pointer type; + std::pair<float, float> oldpos; + std::pair<float, float> newpos; + u32 oldbuttons, newbuttons; + std::pair<size_t, size_t> edges; +}; + + +struct render_target::hit_test +{ + hit_test() noexcept + : inbounds(0U, 0U) + , hit(0U) + { + } + + std::pair<u64, u64> inbounds; + u64 hit; +}; + + + //************************************************************************** // GLOBAL VARIABLES @@ -295,7 +336,8 @@ render_texture::render_texture() m_curseq(0) { m_sbounds.set(0, -1, 0, -1); - memset(m_scaled, 0, sizeof(m_scaled)); + for (auto &elem : m_scaled) + elem.seqid = 0; } @@ -335,11 +377,10 @@ void render_texture::reset(render_manager &manager, texture_scaler_func scaler, void render_texture::release() { // free all scaled versions - for (auto & elem : m_scaled) + for (auto &elem : m_scaled) { - m_manager->invalidate_all(elem.bitmap); - global_free(elem.bitmap); - elem.bitmap = nullptr; + m_manager->invalidate_all(elem.bitmap.get()); + elem.bitmap.reset(); elem.seqid = 0; } @@ -348,6 +389,7 @@ void render_texture::release() m_bitmap = nullptr; m_sbounds.set(0, -1, 0, -1); m_format = TEXFORMAT_ARGB32; + m_scaler = nullptr; m_curseq = 0; } @@ -376,12 +418,9 @@ void render_texture::set_bitmap(bitmap_t &bitmap, const rectangle &sbounds, text // invalidate all scaled versions for (auto & elem : m_scaled) { - if (elem.bitmap != nullptr) - { - m_manager->invalidate_all(elem.bitmap); - global_free(elem.bitmap); - } - elem.bitmap = nullptr; + if (elem.bitmap) + m_manager->invalidate_all(elem.bitmap.get()); + elem.bitmap.reset(); elem.seqid = 0; } } @@ -429,6 +468,7 @@ void render_texture::get_scaled(u32 dwidth, u32 dheight, render_texinfo &texinfo texinfo.base = m_bitmap->raw_pixptr(m_sbounds.top(), m_sbounds.left()); texinfo.rowpixels = m_bitmap->rowpixels(); texinfo.width = swidth; + texinfo.width_margin = m_sbounds.left(); texinfo.height = sheight; // palette will be set later texinfo.seqid = ++m_curseq; @@ -442,7 +482,7 @@ void render_texture::get_scaled(u32 dwidth, u32 dheight, render_texinfo &texinfo // is it a size we already have? scaled_texture *scaled = nullptr; int scalenum; - for (scalenum = 0; scalenum < ARRAY_LENGTH(m_scaled); scalenum++) + for (scalenum = 0; scalenum < std::size(m_scaled); scalenum++) { scaled = &m_scaled[scalenum]; @@ -452,27 +492,27 @@ void render_texture::get_scaled(u32 dwidth, u32 dheight, render_texinfo &texinfo } // did we get one? - if (scalenum == ARRAY_LENGTH(m_scaled)) + if (scalenum == std::size(m_scaled)) { int lowest = -1; // didn't find one -- take the entry with the lowest seqnum - for (scalenum = 0; scalenum < ARRAY_LENGTH(m_scaled); scalenum++) - if ((lowest == -1 || m_scaled[scalenum].seqid < m_scaled[lowest].seqid) && !primlist.has_reference(m_scaled[scalenum].bitmap)) + for (scalenum = 0; scalenum < std::size(m_scaled); scalenum++) + if ((lowest == -1 || m_scaled[scalenum].seqid < m_scaled[lowest].seqid) && !primlist.has_reference(m_scaled[scalenum].bitmap.get())) lowest = scalenum; if (-1 == lowest) throw emu_fatalerror("render_texture::get_scaled: Too many live texture instances!"); // throw out any existing entries scaled = &m_scaled[lowest]; - if (scaled->bitmap != nullptr) + if (scaled->bitmap) { - m_manager->invalidate_all(scaled->bitmap); - global_free(scaled->bitmap); + m_manager->invalidate_all(scaled->bitmap.get()); + scaled->bitmap.reset(); } // allocate a new bitmap - scaled->bitmap = global_alloc(bitmap_argb32(dwidth, dheight)); + scaled->bitmap = std::make_unique<bitmap_argb32>(dwidth, dheight); scaled->seqid = ++m_curseq; // let the scaler do the work @@ -480,8 +520,8 @@ void render_texture::get_scaled(u32 dwidth, u32 dheight, render_texinfo &texinfo } // finally fill out the new info - primlist.add_reference(scaled->bitmap); - texinfo.base = &scaled->bitmap->pix32(0); + primlist.add_reference(scaled->bitmap.get()); + texinfo.base = &scaled->bitmap->pix(0); texinfo.rowpixels = scaled->bitmap->rowpixels(); texinfo.width = dwidth; texinfo.height = dheight; @@ -496,7 +536,7 @@ void render_texture::get_scaled(u32 dwidth, u32 dheight, render_texinfo &texinfo // palette for a texture //------------------------------------------------- -const rgb_t *render_texture::get_adjusted_palette(render_container &container) +const rgb_t *render_texture::get_adjusted_palette(render_container &container, u32 &out_length) { // override the palette with our adjusted palette switch (m_format) @@ -506,7 +546,7 @@ const rgb_t *render_texture::get_adjusted_palette(render_container &container) assert(m_bitmap->palette() != nullptr); // return our adjusted palette - return container.bcg_lookup_table(m_format, m_bitmap->palette()); + return container.bcg_lookup_table(m_format, out_length, m_bitmap->palette()); case TEXFORMAT_RGB32: case TEXFORMAT_ARGB32: @@ -515,7 +555,7 @@ const rgb_t *render_texture::get_adjusted_palette(render_container &container) // if no adjustment necessary, return nullptr if (!container.has_brightness_contrast_gamma_changes()) return nullptr; - return container.bcg_lookup_table(m_format); + return container.bcg_lookup_table(m_format, out_length); default: assert(false); @@ -535,8 +575,7 @@ const rgb_t *render_texture::get_adjusted_palette(render_container &container) //------------------------------------------------- render_container::render_container(render_manager &manager, screen_device *screen) - : m_next(nullptr) - , m_manager(manager) + : m_manager(manager) , m_screen(screen) , m_overlaybitmap(nullptr) , m_overlaytexture(nullptr) @@ -679,7 +718,7 @@ float render_container::apply_brightness_contrast_gamma_fp(float value) // given texture mode //------------------------------------------------- -const rgb_t *render_container::bcg_lookup_table(int texformat, palette_t *palette) +const rgb_t *render_container::bcg_lookup_table(int texformat, u32 &out_length, palette_t *palette) { switch (texformat) { @@ -690,15 +729,18 @@ const rgb_t *render_container::bcg_lookup_table(int texformat, palette_t *palett m_bcglookup.resize(palette->max_index()); recompute_lookups(); } - assert (palette == &m_palclient->palette()); + assert(palette == &m_palclient->palette()); + out_length = palette->max_index(); return &m_bcglookup[0]; case TEXFORMAT_RGB32: case TEXFORMAT_ARGB32: case TEXFORMAT_YUY16: + out_length = std::size(m_bcglookup256); return m_bcglookup256; default: + out_length = 0; return nullptr; } } @@ -713,8 +755,8 @@ void render_container::overlay_scale(bitmap_argb32 &dest, bitmap_argb32 &source, // simply replicate the source bitmap over the target for (int y = 0; y < dest.height(); y++) { - u32 *src = &source.pix32(y % source.height()); - u32 *dst = &dest.pix32(y); + u32 const *const src = &source.pix(y % source.height()); + u32 *dst = &dest.pix(y); int sx = 0; // loop over columns @@ -877,25 +919,31 @@ render_container::user_settings::user_settings() // render_target - constructor //------------------------------------------------- -render_target::render_target(render_manager &manager, const internal_layout *layoutfile, u32 flags) - : render_target(manager, layoutfile, flags, CONSTRUCTOR_IMPL) +render_target::render_target(render_manager &manager, render_container *ui, const internal_layout *layoutfile, u32 flags) + : render_target(manager, ui, layoutfile, flags, CONSTRUCTOR_IMPL) { } -render_target::render_target(render_manager &manager, util::xml::data_node const &layout, u32 flags) - : render_target(manager, layout, flags, CONSTRUCTOR_IMPL) +render_target::render_target(render_manager &manager, render_container *ui, util::xml::data_node const &layout, u32 flags) + : render_target(manager, ui, layout, flags, CONSTRUCTOR_IMPL) { } -template <typename T> render_target::render_target(render_manager &manager, T &&layout, u32 flags, constructor_impl_t) +template <typename T> +render_target::render_target(render_manager &manager, render_container *ui, T &&layout, u32 flags, constructor_impl_t) : m_next(nullptr) , m_manager(manager) - , m_curview(nullptr) + , m_ui_container(ui) + , m_curview(0U) , m_flags(flags) , m_listindex(0) , m_width(640) , m_height(480) + , m_keepaspect(false) + , m_int_overscan(false) , m_pixel_aspect(0.0f) + , m_int_scale_x(0) + , m_int_scale_y(0) , m_max_refresh(0) , m_orientation(0) , m_base_view(nullptr) @@ -903,25 +951,31 @@ template <typename T> render_target::render_target(render_manager &manager, T && , m_maxtexwidth(65536) , m_maxtexheight(65536) , m_transform_container(true) + , m_external_artwork(false) { // determine the base layer configuration based on options m_base_layerconfig.set_zoom_to_screen(manager.machine().options().artwork_crop()); // aspect and scale options - m_keepaspect = (manager.machine().options().keep_aspect() && !(flags & RENDER_CREATE_HIDDEN)); - m_int_overscan = manager.machine().options().int_overscan(); - m_int_scale_x = manager.machine().options().int_scale_x(); - m_int_scale_y = manager.machine().options().int_scale_y(); - if (m_manager.machine().options().auto_stretch_xy()) - m_scale_mode = SCALE_FRACTIONAL_AUTO; - else if (manager.machine().options().uneven_stretch_x()) - m_scale_mode = SCALE_FRACTIONAL_X; - else if (manager.machine().options().uneven_stretch_y()) - m_scale_mode = SCALE_FRACTIONAL_Y; - else if (manager.machine().options().uneven_stretch()) - m_scale_mode = SCALE_FRACTIONAL; + if (!(flags & RENDER_CREATE_HIDDEN)) + { + m_keepaspect = manager.machine().options().keep_aspect(); + m_int_overscan = manager.machine().options().int_overscan(); + m_int_scale_x = manager.machine().options().int_scale_x(); + m_int_scale_y = manager.machine().options().int_scale_y(); + if (m_manager.machine().options().auto_stretch_xy()) + m_scale_mode = SCALE_FRACTIONAL_AUTO; + else if (manager.machine().options().uneven_stretch_x()) + m_scale_mode = SCALE_FRACTIONAL_X; + else if (manager.machine().options().uneven_stretch_y()) + m_scale_mode = SCALE_FRACTIONAL_Y; + else if (manager.machine().options().uneven_stretch()) + m_scale_mode = SCALE_FRACTIONAL; + else + m_scale_mode = SCALE_INTEGER; + } else - m_scale_mode = SCALE_INTEGER; + m_scale_mode = SCALE_FRACTIONAL; // determine the base orientation based on options if (!manager.machine().options().rotate()) @@ -945,6 +999,10 @@ template <typename T> render_target::render_target(render_manager &manager, T && // load the layout files load_layout_files(std::forward<T>(layout), flags & RENDER_CREATE_SINGLE_FILE); + for (layout_file &file : m_filelist) + for (layout_view &view : file.views()) + if (!(m_flags & RENDER_CREATE_NO_ART) || !view.has_art()) + m_views.emplace_back(view, view.default_visibility_mask()); // set the current view to the first one set_view(0); @@ -995,9 +1053,9 @@ void render_target::set_bounds(s32 width, s32 height, float pixel_aspect) m_width = width; m_height = height; m_bounds.x0 = m_bounds.y0 = 0; - m_bounds.x1 = (float)width; - m_bounds.y1 = (float)height; - m_pixel_aspect = pixel_aspect != 0.0? pixel_aspect : 1.0; + m_bounds.x1 = float(width); + m_bounds.y1 = float(height); + m_pixel_aspect = pixel_aspect != 0.0F ? pixel_aspect : 1.0F; } @@ -1006,13 +1064,16 @@ void render_target::set_bounds(s32 width, s32 height, float pixel_aspect) // a target //------------------------------------------------- -void render_target::set_view(int viewindex) +void render_target::set_view(unsigned viewindex) { - layout_view *view = view_by_index(viewindex); - if (view) + if (m_views.size() > viewindex) { - m_curview = view; - view->recompute(m_layerconfig); + forget_pointers(); + m_curview = viewindex; + current_view().recompute(visibility_mask(), m_layerconfig.zoom_to_screen()); + current_view().preload(); + m_clickable_items.clear(); + m_clickable_items.resize(current_view().interactive_items().size()); } } @@ -1030,100 +1091,317 @@ void render_target::set_max_texture_size(int maxwidth, int maxheight) //------------------------------------------------- -// configured_view - select a view for this -// target based on the configuration parameters +// pointer_updated - pointer activity within +// target +//------------------------------------------------- + +void render_target::pointer_updated( + osd::ui_event_handler::pointer type, + u16 ptrid, + u16 device, + s32 x, + s32 y, + u32 buttons, + u32 pressed, + u32 released, + s16 clicks) +{ + auto const target_f(map_point_layout(x, y)); + current_view().pointer_updated(type, ptrid, device, target_f.first, target_f.second, buttons, pressed, released, clicks); + + // 64 pointers ought to be enough for anyone + if (64 <= ptrid) + return; + + // just store the updated pointer state + if (m_pointers.size() <= ptrid) + m_pointers.resize(ptrid + 1); + m_pointers[ptrid].type = type; + m_pointers[ptrid].newpos = target_f; + m_pointers[ptrid].newbuttons = buttons; +} + + +//------------------------------------------------- +// pointer_left - pointer left target normally //------------------------------------------------- -int render_target::configured_view(const char *viewname, int targetindex, int numtargets) +void render_target::pointer_left( + osd::ui_event_handler::pointer type, + u16 ptrid, + u16 device, + s32 x, + s32 y, + u32 released, + s16 clicks) { - layout_view *view = nullptr; - int viewindex; + auto const target_f(map_point_layout(x, y)); + current_view().pointer_left(type, ptrid, device, target_f.first, target_f.second, released, clicks); - // auto view just selects the nth view - if (strcmp(viewname, "auto") != 0) + // store the updated state if relevant + if (m_pointers.size() > ptrid) { - // scan for a matching view name - size_t viewlen = strlen(viewname); - for (view = view_by_index(viewindex = 0); view != nullptr; view = view_by_index(++viewindex)) - if (core_strnicmp(view->name().c_str(), viewname, viewlen) == 0) - break; + m_pointers[ptrid].newpos = std::make_pair(std::numeric_limits<float>::min(), std::numeric_limits<float>::min()); + m_pointers[ptrid].newbuttons = 0; } +} - // if we don't have a match, default to the nth view - screen_device_iterator iter(m_manager.machine().root_device()); - int scrcount = iter.count(); - if (view == nullptr && scrcount > 0) + +//------------------------------------------------- +// pointer_aborted - pointer left target +// abnormally +//------------------------------------------------- + +void render_target::pointer_aborted( + osd::ui_event_handler::pointer type, + u16 ptrid, + u16 device, + s32 x, + s32 y, + u32 released, + s16 clicks) +{ + // let layout scripts handle pointer input + auto const target_f(map_point_layout(x, y)); + current_view().pointer_aborted(type, ptrid, device, target_f.first, target_f.second, released, clicks); + + // store the updated state if relevant + if (m_pointers.size() > ptrid) { - // if we have enough targets to be one per screen, assign in order - if (numtargets >= scrcount) + m_pointers[ptrid].newpos = std::make_pair(std::numeric_limits<float>::min(), std::numeric_limits<float>::min()); + m_pointers[ptrid].newbuttons = 0; + } +} + + +//------------------------------------------------- +// forget_pointers - stop processing pointer +// input +//------------------------------------------------- + +void render_target::forget_pointers() +{ + current_view().forget_pointers(); + m_pointers.clear(); + for (size_t i = 0; m_clickable_items.size() > i; ++i) + { + if (m_clickable_items[i].hit) { - int ourindex = index() % scrcount; - screen_device *screen = iter.byindex(ourindex); + layout_view_item const &item(current_view().interactive_items()[i]); + auto const [port, mask] = item.input_tag_and_mask(); + ioport_field *const field(port ? port->field(mask) : nullptr); + if (field) + field->set_value(0); + } + m_clickable_items[i] = hit_test(); + } +} - // find the first view with this screen and this screen only - for (view = view_by_index(viewindex = 0); view != nullptr; view = view_by_index(++viewindex)) + +//------------------------------------------------- +// update_pointer_fields - update inputs for new +// pointer state +//------------------------------------------------- + +void render_target::update_pointer_fields() +{ + auto const &x_edges(current_view().interactive_edges_x()); + auto const &y_edges(current_view().interactive_edges_y()); + + // update items bounds intersection checks for pointers + for (size_t ptr = 0; m_pointers.size() > ptr; ++ptr) + { + auto const [x, y] = m_pointers[ptr].newpos; + auto edges = m_pointers[ptr].edges; + + // check for moving across horizontal edges + if (x < m_pointers[ptr].oldpos.first) + { + while (edges.first && (x < x_edges[edges.first - 1].position())) { - const render_screen_list &viewscreens = view->screens(); - if (viewscreens.count() == 0) - { - view = nullptr; - break; - } - else if (viewscreens.count() == viewscreens.contains(*screen)) - break; + --edges.first; + auto const &edge(x_edges[edges.first]); + if (edge.trailing()) + m_clickable_items[edge.index()].inbounds.first |= u64(1) << ptr; + else + m_clickable_items[edge.index()].inbounds.first &= ~(u64(1) << ptr); + } + } + else if (x > m_pointers[ptr].oldpos.first) + { + while ((x_edges.size() > edges.first) && (x >= x_edges[edges.first].position())) + { + auto const &edge(x_edges[edges.first]); + if (edge.trailing()) + m_clickable_items[edge.index()].inbounds.first &= ~(u64(1) << ptr); + else + m_clickable_items[edge.index()].inbounds.first |= u64(1) << ptr; + ++edges.first; } } - // otherwise, find the first view that has all the screens - if (view == nullptr) + // check for moving across vertical edges + if (y < m_pointers[ptr].oldpos.second) { - for (view = view_by_index(viewindex = 0); view != nullptr; view = view_by_index(++viewindex)) + while (edges.second && (y < y_edges[edges.second - 1].position())) { - render_screen_list const &viewscreens(view->screens()); - if (viewscreens.count() >= scrcount) - { - bool screen_missing(false); - for (screen_device &screen : iter) - { - if (!viewscreens.contains(screen)) - { - screen_missing = true; - break; - } - } - if (!screen_missing) - break; - } + --edges.second; + auto const &edge(y_edges[edges.second]); + if (edge.trailing()) + m_clickable_items[edge.index()].inbounds.second |= u64(1) << ptr; + else + m_clickable_items[edge.index()].inbounds.second &= ~(u64(1) << ptr); } } + else if (y > m_pointers[ptr].oldpos.second) + { + while ((y_edges.size() > edges.second) && (y >= y_edges[edges.second].position())) + { + auto const &edge(y_edges[edges.second]); + if (edge.trailing()) + m_clickable_items[edge.index()].inbounds.second &= ~(u64(1) << ptr); + else + m_clickable_items[edge.index()].inbounds.second |= u64(1) << ptr; + ++edges.second; + } + } + + // update the pointer's state + m_pointers[ptr].oldpos = m_pointers[ptr].newpos; + m_pointers[ptr].oldbuttons = m_pointers[ptr].newbuttons; + m_pointers[ptr].edges = edges; } - // make sure it's a valid view - return (view != nullptr) ? view_index(*view) : 0; + // update item hit states + u64 obscured(0U); + for (size_t i = 0; m_clickable_items.size() > i; ++i) + { + layout_view_item const &item(current_view().interactive_items()[i]); + u64 const inbounds(m_clickable_items[i].inbounds.first & m_clickable_items[i].inbounds.second); + u64 hit(m_clickable_items[i].hit); + for (unsigned ptr = 0; m_pointers.size() > ptr; ++ptr) + { + pointer_info const &pointer(m_pointers[ptr]); + bool const prefilter(BIT(~obscured & inbounds, ptr)); + if (!prefilter || !BIT(pointer.newbuttons, 0) || !item.bounds().includes(pointer.newpos.first, pointer.newpos.second)) + { + hit &= ~(u64(1) << ptr); + } + else + { + hit |= u64(1) << ptr; + if (!item.clickthrough()) + obscured |= u64(1) << ptr; + } + } + + // update field state + if (bool(hit) != bool(m_clickable_items[i].hit)) + { + auto const [port, mask] = item.input_tag_and_mask(); + ioport_field *const field(port ? port->field(mask) : nullptr); + if (field) + { + if (hit) + field->set_value(1); + else + field->clear_value(); + } + } + m_clickable_items[i].hit = hit; + } } //------------------------------------------------- -// view_name - return the name of the given view +// set_visibility_toggle - show or hide selected +// parts of a view //------------------------------------------------- -const char *render_target::view_name(int viewindex) +void render_target::set_visibility_toggle(unsigned index, bool enable) { - layout_view const *const view = view_by_index(viewindex); - return view ? view->name().c_str() : nullptr; + assert(current_view().visibility_toggles().size() > index); + if (enable) + m_views[m_curview].second |= u32(1) << index; + else + m_views[m_curview].second &= ~(u32(1) << index); + update_layer_config(); + current_view().preload(); } //------------------------------------------------- -// render_target_get_view_screens - return a -// bitmask of which screens are visible on a -// given view +// configured_view - select a view for this +// target based on the configuration parameters //------------------------------------------------- -const render_screen_list &render_target::view_screens(int viewindex) +unsigned render_target::configured_view(const char *viewname, int targetindex, int numtargets) { - layout_view *view = view_by_index(viewindex); - return (view != nullptr) ? view->screens() : s_empty_screen_list; + // if it isn't "auto" or an empty string, try to match it as a view name prefix + if (viewname && *viewname && strcmp(viewname, "auto")) + { + // scan for a matching view name + size_t const viewlen = strlen(viewname); + for (unsigned i = 0; m_views.size() > i; ++i) + { + if (!core_strnicmp(m_views[i].first.name().c_str(), viewname, viewlen)) + return i; + } + } + + // if we don't have a match, default to the nth view + std::vector<std::reference_wrapper<screen_device> > screens; + for (screen_device &screen : screen_device_enumerator(m_manager.machine().root_device())) + screens.push_back(screen); + if (!screens.empty()) + { + // if we have enough targets to be one per screen, assign in order + if (numtargets >= screens.size()) + { + // find the first view with this screen and this screen only + layout_view *view = nullptr; + screen_device const &screen = screens[index() % screens.size()]; + for (unsigned i = 0; !view && (m_views.size() > i); ++i) + { + for (layout_view_item &viewitem : m_views[i].first.items()) + { + screen_device const *const viewscreen(viewitem.screen()); + if (viewscreen == &screen) + { + view = &m_views[i].first; + } + else if (viewscreen) + { + view = nullptr; + break; + } + } + } + if (view) + return view_index(*view); + } + + // otherwise, find the first view that has all the screens + for (unsigned i = 0; m_views.size() > i; ++i) + { + layout_view &curview = m_views[i].first; + if (std::find_if(screens.begin(), screens.end(), [&curview] (screen_device &screen) { return !curview.has_screen(screen); }) == screens.end()) + return i; + } + } + + // default to the first view + return 0; +} + + +//------------------------------------------------- +// view_name - return the name of the given view +//------------------------------------------------- + +const char *render_target::view_name(unsigned viewindex) +{ + return (m_views.size() > viewindex) ? m_views[viewindex].first.name().c_str() : nullptr; } @@ -1146,7 +1424,7 @@ void render_target::compute_visible_area(s32 target_width, s32 target_height, fl if (m_keepaspect) { // start with the aspect ratio of the square pixel layout - width = m_curview->effective_aspect(m_layerconfig); + width = current_view().effective_aspect(); height = 1.0f; // first apply target orientation @@ -1182,49 +1460,107 @@ void render_target::compute_visible_area(s32 target_width, s32 target_height, fl // get source size and aspect s32 src_width, src_height; compute_minimum_size(src_width, src_height); - float src_aspect = m_curview->effective_aspect(m_layerconfig); + float src_aspect = current_view().effective_aspect(); // apply orientation if required if (target_orientation & ORIENTATION_SWAP_XY) - src_aspect = 1.0 / src_aspect; + src_aspect = 1.0f / src_aspect; - // get target aspect - float target_aspect = (float)target_width / (float)target_height * target_pixel_aspect; - bool target_is_portrait = (target_aspect < 1.0f); + // we need the ratio of target to source aspect + float aspect_ratio = m_keepaspect ? (float)target_width / (float)target_height * target_pixel_aspect / src_aspect : 1.0f; + + // first compute (a, b) scale factors to fit the screen + float a = (float)target_width / src_width; + float b = (float)target_height / src_height; // apply automatic axial stretching if required int scale_mode = m_scale_mode; - if (m_scale_mode == SCALE_FRACTIONAL_AUTO) + if (scale_mode == SCALE_FRACTIONAL_AUTO) + scale_mode = (m_manager.machine().system().flags & ORIENTATION_SWAP_XY) ^ (target_orientation & ORIENTATION_SWAP_XY) ? + SCALE_FRACTIONAL_Y : SCALE_FRACTIONAL_X; + + // determine the scaling method for each axis + bool a_is_fract = (scale_mode == SCALE_FRACTIONAL_X || scale_mode == SCALE_FRACTIONAL); + bool b_is_fract = (scale_mode == SCALE_FRACTIONAL_Y || scale_mode == SCALE_FRACTIONAL); + + // check if we have user defined scale factors, if so use them instead, but only on integer axes + int a_user = a_is_fract ? 0 : m_int_scale_x; + int b_user = b_is_fract ? 0 : m_int_scale_y; + + // we allow overscan either explicitely or if integer scale factors are forced by user + bool int_overscan = m_int_overscan || (m_keepaspect && (a_user != 0 || b_user != 0)); + float a_max = std::max(a, (float)a_user); + float b_max = std::max(b, (float)b_user); + + + // get the usable bounding box considering the type of scaling for each axis + float usable_aspect = (a_is_fract ? a : std::max(1.0f, floorf(a))) * src_width / + ((b_is_fract ? b : std::max(1.0f, floorf(b))) * src_height) * target_pixel_aspect; + + // depending on the relative shape between target and source, let's define 'a' and 'b' so that: + // * a is the leader axis (first to hit a boundary) + // * b is the follower axis + if (usable_aspect > src_aspect) { - bool is_rotated = (m_manager.machine().system().flags & ORIENTATION_SWAP_XY) ^ (target_orientation & ORIENTATION_SWAP_XY); - scale_mode = is_rotated ^ target_is_portrait ? SCALE_FRACTIONAL_Y : SCALE_FRACTIONAL_X; + std::swap(a, b); + std::swap(a_user, b_user); + std::swap(a_is_fract, b_is_fract); + std::swap(a_max, b_max); + aspect_ratio = 1.0f / aspect_ratio; } - // determine the scale mode for each axis - bool x_is_integer = !((!target_is_portrait && scale_mode == SCALE_FRACTIONAL_X) || (target_is_portrait && scale_mode == SCALE_FRACTIONAL_Y)); - bool y_is_integer = !((target_is_portrait && scale_mode == SCALE_FRACTIONAL_X) || (!target_is_portrait && scale_mode == SCALE_FRACTIONAL_Y)); + // now find an (a, b) pair that best fits our boundaries and scale options + float a_best = 1.0f, b_best = 1.0f; + float diff = 1000; + + // fill (a0, a1) range + float u = a_user == 0 ? a : (float)a_user; + float a_range[] = {a_is_fract ? u : std::max(1.0f, floorf(u)), a_is_fract ? u : std::max(1.0f, roundf(u))}; + + for (float aa : a_range) + { + // apply aspect correction to 'b' axis if needed, considering resulting 'a' borders + float ba = b * (m_keepaspect ? aspect_ratio * (aa / a) : 1.0f); + + // fill (b0, b1) range + float v = b_user == 0 ? ba : (float)b_user; + float b_range[] = {b_is_fract ? v : std::max(1.0f, floorf(v)), b_is_fract ? v : std::max(1.0f, roundf(v))}; - // first compute scale factors to fit the screen - float xscale = (float)target_width / src_width; - float yscale = (float)target_height / src_height; - float maxxscale = std::max(1.0f, float(m_int_overscan ? render_round_nearest(xscale) : floor(xscale))); - float maxyscale = std::max(1.0f, float(m_int_overscan ? render_round_nearest(yscale) : floor(yscale))); + for (float bb : b_range) + { + // we may need to propagate proportions back to 'a' axis + float ab = aa; + if (m_keepaspect && a_user == 0) + { + if (a_is_fract) ab *= (bb / ba); + else if (b_user != 0) ab = std::max(1.0f, roundf(ab * (bb / ba))); + } - // now apply desired scale mode and aspect correction - if (m_keepaspect && target_aspect > src_aspect) xscale *= src_aspect / target_aspect * (maxyscale / yscale); - if (m_keepaspect && target_aspect < src_aspect) yscale *= target_aspect / src_aspect * (maxxscale / xscale); - if (x_is_integer) xscale = std::min(maxxscale, std::max(1.0f, render_round_nearest(xscale))); - if (y_is_integer) yscale = std::min(maxyscale, std::max(1.0f, render_round_nearest(yscale))); + // if overscan isn't allowed, discard values that exceed the usable bounding box, except a minimum of 1.0f + if (!int_overscan && ((ab > a_max && bb > 1.0f) || (bb > b_max && ab > 1.0f))) + continue; + + // score the result + float new_diff = fabsf(aspect_ratio * (a / b) - (ab / bb)); + + if (new_diff <= diff) + { + diff = new_diff; + a_best = ab; + b_best = bb; + } + } + } + a = a_best; + b = b_best; - // check if we have user defined scale factors, if so use them instead - int user_scale_x = target_is_portrait? m_int_scale_y : m_int_scale_x; - int user_scale_y = target_is_portrait? m_int_scale_x : m_int_scale_y; - xscale = user_scale_x > 0 ? user_scale_x : xscale; - yscale = user_scale_y > 0 ? user_scale_y : yscale; + // restore orientation + if (usable_aspect > src_aspect) + std::swap(a, b); // set the final width/height - visible_width = render_round_nearest(src_width * xscale); - visible_height = render_round_nearest(src_height * yscale); + visible_width = render_round_nearest(src_width * a); + visible_height = render_round_nearest(src_height * b); break; } } @@ -1251,16 +1587,16 @@ void render_target::compute_minimum_size(s32 &minwidth, s32 &minheight) return; } - if (!m_curview) + if (m_views.empty()) throw emu_fatalerror("Mandatory artwork is missing"); // scan the current view for all screens - for (layout_view::item &curitem : m_curview->items()) + for (layout_view_item &curitem : current_view().items()) { - if (curitem.screen()) + screen_device const *const screen = curitem.screen(); + if (screen) { // use a hard-coded default visible area for vector screens - screen_device *const screen = curitem.screen(); const rectangle vectorvis(0, 639, 0, 479); const rectangle &visarea = (screen->screen_type() == SCREEN_TYPE_VECTOR) ? vectorvis : screen->visible_area(); @@ -1309,13 +1645,9 @@ void render_target::compute_minimum_size(s32 &minwidth, s32 &minheight) render_primitive_list &render_target::get_primitives() { - // remember the base values if this is the first frame - if (m_base_view == nullptr) - m_base_view = m_curview; - // switch to the next primitive list render_primitive_list &list = m_primlist[m_listindex]; - m_listindex = (m_listindex + 1) % ARRAY_LENGTH(m_primlist); + m_listindex = (m_listindex + 1) % std::size(m_primlist); list.acquire_lock(); // free any previous primitives @@ -1335,9 +1667,11 @@ render_primitive_list &render_target::get_primitives() root_xform.orientation = m_orientation; root_xform.no_center = false; - // iterate over items in the view, but only if we're running if (m_manager.machine().phase() >= machine_phase::RESET) - for (layout_view::item &curitem : m_curview->items()) + { + // we're running - iterate over items in the view + current_view().prepare_items(); + for (layout_view_item &curitem : current_view().visible_items()) { // first apply orientation to the bounds render_bounds bounds = curitem.bounds(); @@ -1350,27 +1684,24 @@ render_primitive_list &render_target::get_primitives() item_xform.yoffs = root_xform.yoffs + bounds.y0 * root_xform.yscale; item_xform.xscale = (bounds.x1 - bounds.x0) * root_xform.xscale; item_xform.yscale = (bounds.y1 - bounds.y0) * root_xform.yscale; - item_xform.color.r = curitem.color().r * root_xform.color.r; - item_xform.color.g = curitem.color().g * root_xform.color.g; - item_xform.color.b = curitem.color().b * root_xform.color.b; - item_xform.color.a = curitem.color().a * root_xform.color.a; + item_xform.color = curitem.color() * root_xform.color; item_xform.orientation = orientation_add(curitem.orientation(), root_xform.orientation); item_xform.no_center = false; // if there is no associated element, it must be a screen element - if (curitem.screen() != nullptr) + if (curitem.screen()) add_container_primitives(list, root_xform, item_xform, curitem.screen()->container(), curitem.blend_mode()); else - add_element_primitives(list, item_xform, *curitem.element(), curitem.state(), curitem.blend_mode()); + add_element_primitives(list, item_xform, curitem); } - - // if we are not in the running stage, draw an outer box + } else { + // if we are not in the running stage, draw an outer box render_primitive *prim = list.alloc(render_primitive::QUAD); - set_render_bounds_xy(prim->bounds, 0.0f, 0.0f, (float)m_width, (float)m_height); + prim->bounds.set_xy(0.0f, 0.0f, (float)m_width, (float)m_height); prim->full_bounds = prim->bounds; - set_render_color(&prim->color, 1.0f, 1.0f, 1.0f, 1.0f); + prim->color.set(1.0f, 0.1f, 0.1f, 0.1f); prim->texture.base = nullptr; prim->flags = PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA); list.append(*prim); @@ -1378,34 +1709,17 @@ render_primitive_list &render_target::get_primitives() if (m_width > 1 && m_height > 1) { prim = list.alloc(render_primitive::QUAD); - set_render_bounds_xy(prim->bounds, 1.0f, 1.0f, (float)(m_width - 1), (float)(m_height - 1)); + prim->bounds.set_xy(1.0f, 1.0f, float(m_width - 1), float(m_height - 1)); prim->full_bounds = prim->bounds; - set_render_color(&prim->color, 1.0f, 0.0f, 0.0f, 0.0f); + prim->color.set(1.0f, 0.0f, 0.0f, 0.0f); prim->texture.base = nullptr; prim->flags = PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA); list.append(*prim); } } - // process the debug containers - for (render_container &debug : m_debug_containers) - { - object_transform ui_xform; - ui_xform.xoffs = 0; - ui_xform.yoffs = 0; - ui_xform.xscale = (float)m_width; - ui_xform.yscale = (float)m_height; - ui_xform.color.r = ui_xform.color.g = ui_xform.color.b = 1.0f; - ui_xform.color.a = 0.9f; - ui_xform.orientation = m_orientation; - ui_xform.no_center = true; - - // add UI elements - add_container_primitives(list, root_xform, ui_xform, debug, BLENDMODE_ALPHA); - } - - // process the UI if we are the UI target - if (is_ui_target()) + // process UI elements if applicable + if (m_ui_container) { // compute the transform for the UI object_transform ui_xform; @@ -1418,7 +1732,7 @@ render_primitive_list &render_target::get_primitives() ui_xform.no_center = false; // add UI elements - add_container_primitives(list, root_xform, ui_xform, m_manager.ui_container(), BLENDMODE_ALPHA); + add_container_primitives(list, root_xform, ui_xform, *m_ui_container, BLENDMODE_ALPHA); } // optimize the list before handing it off @@ -1436,21 +1750,45 @@ render_primitive_list &render_target::get_primitives() bool render_target::map_point_container(s32 target_x, s32 target_y, render_container &container, float &container_x, float &container_y) { - ioport_port *input_port; - ioport_value input_mask; - return map_point_internal(target_x, target_y, &container, container_x, container_y, input_port, input_mask); -} + std::pair<float, float> target_f(map_point_internal(target_x, target_y)); + // explicitly check for the UI container + if (&container == m_ui_container) + { + // this hit test went against the UI container + container_x = float(target_x) / m_width; + container_y = float(target_y) / m_height; + return (target_f.first >= 0.0f) && (target_f.first < 1.0f) && (target_f.second >= 0.0f) && (target_f.second < 1.0f); + } + else + { + if (m_orientation & ORIENTATION_FLIP_X) + target_f.first = 1.0f - target_f.first; + if (m_orientation & ORIENTATION_FLIP_Y) + target_f.second = 1.0f - target_f.second; + if (m_orientation & ORIENTATION_SWAP_XY) + std::swap(target_f.first, target_f.second); + + // try to find the right container + auto const &items(current_view().visible_screen_items()); + auto const found(std::find_if( + items.begin(), + items.end(), + [&container] (layout_view_item &item) { return &item.screen()->container() == &container; })); + if (items.end() != found) + { + // point successfully mapped + layout_view_item &item(*found); + render_bounds const bounds(item.bounds()); + container_x = (target_f.first - bounds.x0) / bounds.width(); + container_y = (target_f.second - bounds.y0) / bounds.height(); + return bounds.includes(target_f.first, target_f.second); + } + } -//------------------------------------------------- -// map_point_input - attempts to map a point on -// the specified render_target to the specified -// container, if possible -//------------------------------------------------- - -bool render_target::map_point_input(s32 target_x, s32 target_y, ioport_port *&input_port, ioport_value &input_mask, float &input_x, float &input_y) -{ - return map_point_internal(target_x, target_y, nullptr, input_x, input_y, input_port, input_mask);; + // default to point not mapped + container_x = container_y = -1.0f; + return false; } @@ -1475,50 +1813,16 @@ void render_target::invalidate_all(void *refptr) //------------------------------------------------- -// debug_alloc - allocate a container for a debug -// view -//------------------------------------------------- - -render_container *render_target::debug_alloc() -{ - return &m_debug_containers.append(*m_manager.container_alloc()); -} - - -//------------------------------------------------- -// debug_free - free a container for a debug view -//------------------------------------------------- - -void render_target::debug_free(render_container &container) -{ - m_debug_containers.remove(container); -} - - -//------------------------------------------------- -// debug_append - move a debug view container to -// the end of the list -//------------------------------------------------- - -void render_target::debug_append(render_container &container) -{ - m_debug_containers.append(m_debug_containers.detach(container)); -} - - -//------------------------------------------------- // resolve_tags - resolve tag lookups //------------------------------------------------- void render_target::resolve_tags() { for (layout_file &file : m_filelist) - { - for (layout_view &view : file.views()) - { - view.resolve_tags(); - } - } + file.resolve_tags(); + + update_layer_config(); + current_view().preload(); } @@ -1529,7 +1833,10 @@ void render_target::resolve_tags() void render_target::update_layer_config() { - m_curview->recompute(m_layerconfig); + forget_pointers(); + current_view().recompute(visibility_mask(), m_layerconfig.zoom_to_screen()); + m_clickable_items.clear(); + m_clickable_items.resize(current_view().interactive_items().size()); } @@ -1540,96 +1847,97 @@ void render_target::update_layer_config() void render_target::load_layout_files(const internal_layout *layoutfile, bool singlefile) { - bool have_artwork = false; + bool have_artwork = false; // if there's an explicit file, load that first - const char *basename = m_manager.machine().basename(); + const std::string &basename = m_manager.machine().basename(); if (layoutfile) - have_artwork |= load_layout_file(basename, *layoutfile); + have_artwork |= load_layout_file(basename.c_str(), *layoutfile); // if we're only loading this file, we know our final result if (!singlefile) - load_additional_layout_files(basename, have_artwork); + load_additional_layout_files(basename.c_str(), have_artwork); } void render_target::load_layout_files(util::xml::data_node const &rootnode, bool singlefile) { - bool have_artwork = false; + bool have_artwork = false; // if there's an explicit file, load that first - const char *basename = m_manager.machine().basename(); - have_artwork |= load_layout_file(m_manager.machine().root_device(), basename, rootnode); + const std::string &basename = m_manager.machine().basename(); + have_artwork |= load_layout_file(m_manager.machine().root_device(), rootnode, m_manager.machine().options().art_path(), basename.c_str()); // if we're only loading this file, we know our final result if (!singlefile) - load_additional_layout_files(basename, have_artwork); + load_additional_layout_files(basename.c_str(), have_artwork); } void render_target::load_additional_layout_files(const char *basename, bool have_artwork) { - bool have_default = false; - bool have_override = false; + using util::lang_translate; + + m_external_artwork = false; // if override_artwork defined, load that and skip artwork other than default - if (m_manager.machine().options().override_artwork()) + const char *const override_art = m_manager.machine().options().override_artwork(); + if (override_art && *override_art) { - if (load_layout_file(m_manager.machine().options().override_artwork(), m_manager.machine().options().override_artwork())) - have_override = true; - else if (load_layout_file(m_manager.machine().options().override_artwork(), "default")) - have_override = true; + if (load_layout_file(override_art, override_art)) + m_external_artwork = true; + else if (load_layout_file(override_art, "default")) + m_external_artwork = true; } const game_driver &system = m_manager.machine().system(); // Skip if override_artwork has found artwork - if (!have_override) + if (!m_external_artwork) { - // try to load a file based on the driver name if (!load_layout_file(basename, system.name)) - have_artwork |= load_layout_file(basename, "default"); + m_external_artwork |= load_layout_file(basename, "default"); else - have_artwork = true; + m_external_artwork = true; + + // try to load another file based on the parent driver name + int cloneof = driver_list::clone(system); + while (0 <= cloneof) + { + if (!m_external_artwork || driver_list::driver(cloneof).flags & MACHINE_IS_BIOS_ROOT) + { + if (!load_layout_file(driver_list::driver(cloneof).name, driver_list::driver(cloneof).name)) + m_external_artwork |= load_layout_file(driver_list::driver(cloneof).name, "default"); + else + m_external_artwork = true; + } + + // Check the parent of the parent to cover bios based artwork + const game_driver &parent(driver_list::driver(cloneof)); + cloneof = driver_list::clone(parent); + } // if a default view has been specified, use that as a fallback - if (system.default_layout != nullptr) + bool have_default = false; + if (system.default_layout) have_default |= load_layout_file(nullptr, *system.default_layout); m_manager.machine().config().apply_default_layouts( [this, &have_default] (device_t &dev, internal_layout const &layout) { have_default |= load_layout_file(nullptr, layout, &dev); }); - // try to load another file based on the parent driver name - int cloneof = driver_list::clone(system); - if (cloneof != -1) - { - if (!load_layout_file(driver_list::driver(cloneof).name, driver_list::driver(cloneof).name)) - have_artwork |= load_layout_file(driver_list::driver(cloneof).name, "default"); - else - have_artwork = true; - } + have_artwork |= m_external_artwork; - // Check the parent of the parent to cover bios based artwork - if (cloneof != -1) { - const game_driver &clone(driver_list::driver(cloneof)); - int cloneofclone = driver_list::clone(clone); - if (cloneofclone != -1 && cloneofclone != cloneof) + // Use fallback artwork if defined and no artwork has been found yet + if (!have_artwork) + { + const char *const fallback_art = m_manager.machine().options().fallback_artwork(); + if (fallback_art && *fallback_art) { - if (!load_layout_file(driver_list::driver(cloneofclone).name, driver_list::driver(cloneofclone).name)) - have_artwork |= load_layout_file(driver_list::driver(cloneofclone).name, "default"); + if (!load_layout_file(fallback_art, fallback_art)) + have_artwork |= load_layout_file(fallback_art, "default"); else have_artwork = true; } } - - // Use fallback artwork if defined and no artwork has been found yet - if (!have_artwork && m_manager.machine().options().fallback_artwork()) - { - if (!load_layout_file(m_manager.machine().options().fallback_artwork(), m_manager.machine().options().fallback_artwork())) - have_artwork |= load_layout_file(m_manager.machine().options().fallback_artwork(), "default"); - else - have_artwork = true; - } - } // local screen info to avoid repeated code @@ -1673,13 +1981,24 @@ void render_target::load_additional_layout_files(const char *basename, bool have bool m_rotated; std::pair<unsigned, unsigned> m_physical, m_native; }; - screen_device_iterator iter(m_manager.machine().root_device()); + screen_device_enumerator iter(m_manager.machine().root_device()); std::vector<screen_info> const screens(std::begin(iter), std::end(iter)); + // need this because views aren't fully set up yet + auto const nth_view = + [this] (unsigned n) -> layout_view * + { + for (layout_file &file : m_filelist) + for (layout_view &view : file.views()) + if (!(m_flags & RENDER_CREATE_NO_ART) || !view.has_art()) + if (n-- == 0) + return &view; + return nullptr; + }; if (screens.empty()) // ensure the fallback view for systems with no screens is loaded if necessary { - if (!view_by_index(0)) + if (!nth_view(0)) { load_layout_file(nullptr, layout_noscreens); if (m_filelist.empty()) @@ -1704,10 +2023,9 @@ void render_target::load_additional_layout_files(const char *basename, bool have throw emu_fatalerror("Couldn't create XML node??"); viewnode->set_attribute( "name", - util::xml::normalize_string( - util::string_format( - "Screen %1$u Standard (%2$u:%3$u)", - i, screens[i].physical_x(), screens[i].physical_y()).c_str())); + util::string_format( + _("view-name", "Screen %1$u Standard (%2$u:%3$u)"), + i, screens[i].physical_x(), screens[i].physical_y()).c_str()); util::xml::data_node *const screennode(viewnode->add_child("screen", nullptr)); if (!screennode) throw emu_fatalerror("Couldn't create XML node??"); @@ -1731,10 +2049,9 @@ void render_target::load_additional_layout_files(const char *basename, bool have throw emu_fatalerror("Couldn't create XML node??"); viewnode->set_attribute( "name", - util::xml::normalize_string( - util::string_format( - "Screen %1$u Pixel Aspect (%2$u:%3$u)", - i, screens[i].native_x(), screens[i].native_y()).c_str())); + util::string_format( + _("view-name", "Screen %1$u Pixel Aspect (%2$u:%3$u)"), + i, screens[i].native_x(), screens[i].native_y()).c_str()); util::xml::data_node *const screennode(viewnode->add_child("screen", nullptr)); if (!screennode) throw emu_fatalerror("Couldn't create XML node??"); @@ -1755,7 +2072,7 @@ void render_target::load_additional_layout_files(const char *basename, bool have util::xml::data_node *const viewnode(layoutnode->add_child("view", nullptr)); if (!viewnode) throw emu_fatalerror("Couldn't create XML node??"); - viewnode->set_attribute("name", "Cocktail"); + viewnode->set_attribute("name", _("view-name", "Cocktail")); util::xml::data_node *const mirrornode(viewnode->add_child("screen", nullptr)); if (!mirrornode) @@ -1792,23 +2109,19 @@ void render_target::load_additional_layout_files(const char *basename, bool have { need_tiles = true; int viewindex(0); - for (layout_view *view = view_by_index(viewindex); need_tiles && view; view = view_by_index(++viewindex)) + for (layout_view *view = nth_view(viewindex); need_tiles && view; view = nth_view(++viewindex)) { - render_screen_list const &viewscreens(view->screens()); - if (viewscreens.count() >= screens.size()) + bool screen_missing(false); + for (screen_device &screen : iter) { - bool screen_missing(false); - for (screen_device &screen : iter) + if (!view->has_screen(screen)) { - if (!viewscreens.contains(screen)) - { - screen_missing = true; - break; - } + screen_missing = true; + break; } - if (!screen_missing) - need_tiles = false; } + if (!screen_missing) + need_tiles = false; } } if (need_tiles) @@ -1852,7 +2165,7 @@ void render_target::load_additional_layout_files(const char *basename, bool have util::xml::data_node *viewnode = layoutnode->add_child("view", nullptr); if (!viewnode) throw emu_fatalerror("Couldn't create XML node??"); - viewnode->set_attribute("name", util::xml::normalize_string(title)); + viewnode->set_attribute("name", title); float ypos(0.0F); for (unsigned y = 0U; rows > y; ypos += heights[y] + spacing, ++y) { @@ -1879,10 +2192,10 @@ void render_target::load_additional_layout_files(const char *basename, bool have }; // generate linear views - generate_view("Left-to-Right", screens.size(), false, [] (unsigned x, unsigned y) { return x; }); - generate_view("Left-to-Right (Gapless)", screens.size(), true, [] (unsigned x, unsigned y) { return x; }); - generate_view("Top-to-Bottom", 1U, false, [] (unsigned x, unsigned y) { return y; }); - generate_view("Top-to-Bottom (Gapless)", 1U, true, [] (unsigned x, unsigned y) { return y; }); + generate_view(_("view-name", "Left-to-Right"), screens.size(), false, [] (unsigned x, unsigned y) { return x; }); + generate_view(_("view-name", "Left-to-Right (Gapless)"), screens.size(), true, [] (unsigned x, unsigned y) { return x; }); + generate_view(_("view-name", "Top-to-Bottom"), 1U, false, [] (unsigned x, unsigned y) { return y; }); + generate_view(_("view-name", "Top-to-Bottom (Gapless)"), 1U, true, [] (unsigned x, unsigned y) { return y; }); // generate fake cocktail view for systems with two screens if (screens.size() == 2U) @@ -1894,7 +2207,7 @@ void render_target::load_additional_layout_files(const char *basename, bool have util::xml::data_node *const viewnode(layoutnode->add_child("view", nullptr)); if (!viewnode) throw emu_fatalerror("Couldn't create XML node??"); - viewnode->set_attribute("name", "Cocktail"); + viewnode->set_attribute("name", _("view-name", "Cocktail")); util::xml::data_node *const mirrornode(viewnode->add_child("screen", nullptr)); if (!mirrornode) @@ -1933,7 +2246,7 @@ void render_target::load_additional_layout_files(const char *basename, bool have if (!remainder || (((majdim + 1) / 2) <= remainder)) { generate_view( - util::string_format("%1$u\xC3\x97%2$u Left-to-Right, Top-to-Bottom", majdim, mindim).c_str(), + util::string_format(_("view-name", u8"%1$u×%2$u Left-to-Right, Top-to-Bottom"), majdim, mindim).c_str(), majdim, false, [&screens, majdim] (unsigned x, unsigned y) @@ -1942,7 +2255,7 @@ void render_target::load_additional_layout_files(const char *basename, bool have return (screens.size() > i) ? int(i) : -1; }); generate_view( - util::string_format("%1$u\xC3\x97%2$u Left-to-Right, Top-to-Bottom (Gapless)", majdim, mindim).c_str(), + util::string_format(_("view-name", u8"%1$u×%2$u Left-to-Right, Top-to-Bottom (Gapless)"), majdim, mindim).c_str(), majdim, true, [&screens, majdim] (unsigned x, unsigned y) @@ -1951,7 +2264,7 @@ void render_target::load_additional_layout_files(const char *basename, bool have return (screens.size() > i) ? int(i) : -1; }); generate_view( - util::string_format("%1$u\xC3\x97%2$u Top-to-Bottom, Left-to-Right", mindim, majdim).c_str(), + util::string_format(_("view-name", u8"%1$u×%2$u Top-to-Bottom, Left-to-Right"), mindim, majdim).c_str(), mindim, false, [&screens, majdim] (unsigned x, unsigned y) @@ -1960,7 +2273,7 @@ void render_target::load_additional_layout_files(const char *basename, bool have return (screens.size() > i) ? int(i) : -1; }); generate_view( - util::string_format("%1$u\xC3\x97%2$u Top-to-Bottom, Left-to-Right (Gapless)", mindim, majdim).c_str(), + util::string_format(_("view-name", u8"%1$u×%2$u Top-to-Bottom, Left-to-Right (Gapless)"), mindim, majdim).c_str(), mindim, true, [&screens, majdim] (unsigned x, unsigned y) @@ -1973,7 +2286,7 @@ void render_target::load_additional_layout_files(const char *basename, bool have } // try to parse it - if (!load_layout_file(m_manager.machine().root_device(), nullptr, *root)) + if (!load_layout_file(m_manager.machine().root_device(), *root, m_manager.machine().options().art_path(), nullptr)) throw emu_fatalerror("Couldn't parse generated layout??"); } } @@ -1987,87 +2300,93 @@ void render_target::load_additional_layout_files(const char *basename, bool have bool render_target::load_layout_file(const char *dirname, const internal_layout &layout_data, device_t *device) { // +1 to ensure data is terminated for XML parser - auto tempout = make_unique_clear<u8 []>(layout_data.decompressed_size + 1); - - z_stream stream; - int zerr; - - /* initialize the stream */ - memset(&stream, 0, sizeof(stream)); - stream.next_out = tempout.get(); - stream.avail_out = layout_data.decompressed_size; - - - zerr = inflateInit(&stream); - if (zerr != Z_OK) - { - fatalerror("could not inflateInit"); - return false; - } - - /* decompress this chunk */ - stream.next_in = (unsigned char *)layout_data.data; - stream.avail_in = layout_data.compressed_size; - zerr = inflate(&stream, Z_NO_FLUSH); - - /* stop at the end of the stream */ - if (zerr == Z_STREAM_END) - { - // OK - } - else if (zerr != Z_OK) + std::unique_ptr<u8 []> tempout(new (std::nothrow) u8 [layout_data.decompressed_size + 1]); + auto inflater(util::zlib_read(util::ram_read(layout_data.data, layout_data.compressed_size), 8192)); + if (!tempout || !inflater) { - fatalerror("decompression error\n"); + osd_printf_error("render_target::load_layout_file: not enough memory to decompress layout\n"); return false; } - /* clean up */ - zerr = inflateEnd(&stream); - if (zerr != Z_OK) + size_t decompressed = 0; + do { - fatalerror("inflateEnd error\n"); - return false; + auto const [err, actual] = read( + *inflater, + &tempout[decompressed], + layout_data.decompressed_size - decompressed); + decompressed += actual; + if (err) + { + osd_printf_error( + "render_target::load_layout_file: error decompressing layout (%s:%d %s)\n", + err.category().name(), + err.value(), + err.message()); + return false; + } + if (!actual && (layout_data.decompressed_size < decompressed)) + { + osd_printf_warning( + "render_target::load_layout_file: expected %u bytes of decompressed data but only got %u\n", + layout_data.decompressed_size, + decompressed); + break; + } } + while (layout_data.decompressed_size > decompressed); + inflater.reset(); + tempout[decompressed] = 0U; util::xml::file::ptr rootnode(util::xml::file::string_read(reinterpret_cast<char const *>(tempout.get()), nullptr)); tempout.reset(); // if we didn't get a properly-formatted XML file, record a warning and exit - if (!load_layout_file(device ? *device : m_manager.machine().root_device(), dirname, *rootnode)) + if (!load_layout_file(device ? *device : m_manager.machine().root_device(), *rootnode, m_manager.machine().options().art_path(), dirname)) { - osd_printf_warning("Improperly formatted XML string, ignoring\n"); + osd_printf_warning("render_target::load_layout_file: Improperly formatted XML string, ignoring\n"); return false; } - else - { - return true; - } + + return true; } bool render_target::load_layout_file(const char *dirname, const char *filename) { // build the path and optionally prepend the directory - std::string fname = std::string(filename).append(".lay"); + std::string fname; if (dirname) - fname.insert(0, PATH_SEPARATOR).insert(0, dirname); - - // attempt to open the file; bail if we can't - emu_file layoutfile(m_manager.machine().options().art_path(), OPEN_FLAG_READ); - osd_file::error const filerr(layoutfile.open(fname.c_str())); - if (filerr != osd_file::error::NONE) - return false; + fname.append(dirname).append(PATH_SEPARATOR); + fname.append(filename).append(".lay"); - // read the file + // attempt to open matching files util::xml::parse_options parseopt; util::xml::parse_error parseerr; parseopt.error = &parseerr; - util::xml::file::ptr rootnode(util::xml::file::read(layoutfile, &parseopt)); - if (!rootnode) + emu_file layoutfile(m_manager.machine().options().art_path(), OPEN_FLAG_READ); + layoutfile.set_restrict_to_mediapath(1); + bool result(false); + for (std::error_condition filerr = layoutfile.open(fname); !filerr; filerr = layoutfile.open_next()) { - if (parseerr.error_message) + // read the file and parse as XML + util::xml::file::ptr const rootnode(util::xml::file::read(layoutfile, &parseopt)); + if (rootnode) + { + // extract directory name from location of layout file + std::string artdir(layoutfile.fullpath()); + auto const dirsep(std::find_if(artdir.rbegin(), artdir.rend(), &util::is_directory_separator)); + artdir.erase(dirsep.base(), artdir.end()); + + // record a warning if we didn't get a properly-formatted XML file + if (!load_layout_file(m_manager.machine().root_device(), *rootnode, nullptr, artdir.c_str())) + osd_printf_warning("Improperly formatted XML layout file '%s', ignoring\n", filename); + else + result = true; + } + else if (parseerr.error_message) { osd_printf_warning( - "Error parsing XML file '%s' at line %d column %d: %s, ignoring\n", + "Error parsing XML layout file '%s' at line %d column %d: %s, ignoring\n", filename, parseerr.error_line, parseerr.error_column, @@ -2075,37 +2394,25 @@ bool render_target::load_layout_file(const char *dirname, const char *filename) } else { - osd_printf_warning("Error parsing XML file '%s', ignorning\n", filename); + osd_printf_warning("Error parsing XML layout file '%s', ignorning\n", filename); } - return false; - } - - // if we didn't get a properly-formatted XML file, record a warning and exit - if (!load_layout_file(m_manager.machine().root_device(), dirname, *rootnode)) - { - osd_printf_warning("Improperly formatted XML file '%s', ignoring\n", filename); - return false; - } - else - { - return true; } + return result; } -bool render_target::load_layout_file(device_t &device, const char *dirname, util::xml::data_node const &rootnode) +bool render_target::load_layout_file(device_t &device, util::xml::data_node const &rootnode, const char *searchpath, const char *dirname) { // parse and catch any errors try { - m_filelist.emplace_back(device, rootnode, dirname); + m_filelist.emplace_back(device, rootnode, searchpath, dirname); } - catch (emu_fatalerror &) + catch (emu_fatalerror const &err) { + osd_printf_warning("%s\n", err.what()); return false; } - emulator_info::layout_file_cb(rootnode); - return true; } @@ -2126,7 +2433,7 @@ void render_target::add_container_primitives(render_primitive_list &list, const cliprect.y0 = xform.yoffs; cliprect.x1 = xform.xoffs + xform.xscale; cliprect.y1 = xform.yoffs + xform.yscale; - sect_render_bounds(cliprect, m_bounds); + cliprect &= m_bounds; float root_xoffs = root_xform.xoffs + fabsf(root_xform.xscale - xform.xscale) * 0.5f; float root_yoffs = root_xform.yoffs + fabsf(root_xform.yscale - xform.yscale) * 0.5f; @@ -2136,7 +2443,7 @@ void render_target::add_container_primitives(render_primitive_list &list, const root_cliprect.y0 = root_yoffs; root_cliprect.x1 = root_xoffs + root_xform.xscale; root_cliprect.y1 = root_yoffs + root_xform.yscale; - sect_render_bounds(root_cliprect, m_bounds); + root_cliprect &= m_bounds; // compute the container transform object_transform container_xform; @@ -2236,11 +2543,11 @@ void render_target::add_container_primitives(render_primitive_list &list, const // clip the primitive if (!m_transform_container && PRIMFLAG_GET_VECTOR(curitem.flags())) { - clipped = render_clip_line(&prim->bounds, &root_cliprect); + clipped = render_clip_line(prim->bounds, root_cliprect); } else { - clipped = render_clip_line(&prim->bounds, &cliprect); + clipped = render_clip_line(prim->bounds, cliprect); } break; @@ -2267,13 +2574,13 @@ void render_target::add_container_primitives(render_primitive_list &list, const curitem.texture()->get_scaled(width, height, prim->texture, list, curitem.flags()); // set the palette - prim->texture.palette = curitem.texture()->get_adjusted_palette(container); + prim->texture.palette = curitem.texture()->get_adjusted_palette(container, prim->texture.palette_length); // determine UV coordinates prim->texcoords = oriented_texcoords[finalorient]; // apply clipping - clipped = render_clip_quad(&prim->bounds, &cliprect, &prim->texcoords); + clipped = render_clip_quad(prim->bounds, cliprect, &prim->texcoords); // apply the final orientation from the quad flags and then build up the final flags prim->flags |= (curitem.flags() & ~(PRIMFLAG_TEXORIENT_MASK | PRIMFLAG_BLENDMODE_MASK | PRIMFLAG_TEXFORMAT_MASK)) @@ -2333,7 +2640,7 @@ void render_target::add_container_primitives(render_primitive_list &list, const prim->texcoords = oriented_texcoords[finalorient]; // apply clipping - clipped = render_clip_quad(&prim->bounds, &cliprect, &prim->texcoords); + clipped = render_clip_quad(prim->bounds, cliprect, &prim->texcoords); // apply the final orientation from the quad flags and then build up the final flags prim->flags |= (curitem.flags() & ~(PRIMFLAG_TEXORIENT_MASK | PRIMFLAG_BLENDMODE_MASK | PRIMFLAG_TEXFORMAT_MASK)) @@ -2349,7 +2656,7 @@ void render_target::add_container_primitives(render_primitive_list &list, const | PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA); // apply clipping - clipped = render_clip_quad(&prim->bounds, &cliprect, nullptr); + clipped = render_clip_quad(prim->bounds, cliprect, nullptr); } } break; @@ -2366,7 +2673,7 @@ void render_target::add_container_primitives(render_primitive_list &list, const // allocate a primitive render_primitive *prim = list.alloc(render_primitive::QUAD); - set_render_bounds_wh(prim->bounds, xform.xoffs, xform.yoffs, xform.xscale, xform.yscale); + prim->bounds.set_wh(xform.xoffs, xform.yoffs, xform.xscale, xform.yscale); prim->full_bounds = prim->bounds; prim->color = container_xform.color; width = render_round_nearest(prim->bounds.x1) - render_round_nearest(prim->bounds.x0); @@ -2395,51 +2702,86 @@ void render_target::add_container_primitives(render_primitive_list &list, const // for an element in the current state //------------------------------------------------- -void render_target::add_element_primitives(render_primitive_list &list, const object_transform &xform, layout_element &element, int state, int blendmode) +void render_target::add_element_primitives(render_primitive_list &list, const object_transform &xform, layout_view_item &item) { - // if we're out of range, bail - if (state > element.maxstate()) - return; - if (state < 0) - state = 0; + layout_element &element(*item.element()); + int const blendmode(item.blend_mode()); + + // limit state range to non-negative values + int const state((std::max)(item.element_state(), 0)); // get a pointer to the relevant texture render_texture *texture = element.state_texture(state); - if (texture != nullptr) + if (texture) { render_primitive *prim = list.alloc(render_primitive::QUAD); // configure the basics prim->color = xform.color; - prim->flags = PRIMFLAG_TEXORIENT(xform.orientation) | PRIMFLAG_BLENDMODE(blendmode) | PRIMFLAG_TEXFORMAT(texture->format()); + prim->flags = + PRIMFLAG_TEXORIENT(xform.orientation) | + PRIMFLAG_TEXFORMAT(texture->format()) | + PRIMFLAG_BLENDMODE(blendmode) | + PRIMFLAG_TEXWRAP((item.scroll_wrap_x() || item.scroll_wrap_y()) ? 1 : 0); // compute the bounds - s32 width = render_round_nearest(xform.xscale); - s32 height = render_round_nearest(xform.yscale); - set_render_bounds_wh(prim->bounds, render_round_nearest(xform.xoffs), render_round_nearest(xform.yoffs), (float) width, (float) height); + float const primwidth(render_round_nearest(xform.xscale)); + float const primheight(render_round_nearest(xform.yscale)); + prim->bounds.set_wh(render_round_nearest(xform.xoffs), render_round_nearest(xform.yoffs), primwidth, primheight); prim->full_bounds = prim->bounds; - if (xform.orientation & ORIENTATION_SWAP_XY) - std::swap(width, height); - width = std::min(width, m_maxtexwidth); - height = std::min(height, m_maxtexheight); // get the scaled texture and append it - - texture->get_scaled(width, height, prim->texture, list, prim->flags); + float const xsize(item.scroll_size_x()); + float const ysize(item.scroll_size_y()); + s32 texwidth = render_round_nearest(((xform.orientation & ORIENTATION_SWAP_XY) ? primheight : primwidth) / xsize); + s32 texheight = render_round_nearest(((xform.orientation & ORIENTATION_SWAP_XY) ? primwidth : primheight) / ysize); + texwidth = (std::min)(texwidth, m_maxtexwidth); + texheight = (std::min)(texheight, m_maxtexheight); + texture->get_scaled(texwidth, texheight, prim->texture, list, prim->flags); // compute the clip rect - render_bounds cliprect; - cliprect.x0 = render_round_nearest(xform.xoffs); - cliprect.y0 = render_round_nearest(xform.yoffs); - cliprect.x1 = render_round_nearest(xform.xoffs + xform.xscale); - cliprect.y1 = render_round_nearest(xform.yoffs + xform.yscale); - sect_render_bounds(cliprect, m_bounds); + render_bounds cliprect = prim->bounds & m_bounds; // determine UV coordinates and apply clipping - prim->texcoords = oriented_texcoords[xform.orientation]; - bool clipped = render_clip_quad(&prim->bounds, &cliprect, &prim->texcoords); + float const xwindow((xform.orientation & ORIENTATION_SWAP_XY) ? primheight : primwidth); + float const ywindow((xform.orientation & ORIENTATION_SWAP_XY) ? primwidth : primheight); + float const xrange(float(texwidth) - (item.scroll_wrap_x() ? 0.0f : xwindow)); + float const yrange(float(texheight) - (item.scroll_wrap_y() ? 0.0f : ywindow)); + float const xoffset(render_round_nearest(item.scroll_pos_x() * xrange) / float(texwidth)); + float const yoffset(render_round_nearest(item.scroll_pos_y() * yrange) / float(texheight)); + float const xend(xoffset + (xwindow / float(texwidth))); + float const yend(yoffset + (ywindow / float(texheight))); + switch (xform.orientation) + { + default: + case 0: + prim->texcoords = render_quad_texuv{ { xoffset, yoffset }, { xend, yoffset }, { xoffset, yend }, { xend, yend } }; + break; + case ORIENTATION_FLIP_X: + prim->texcoords = render_quad_texuv{ { xend, yoffset }, { xoffset, yoffset }, { xend, yend }, { xoffset, yend } }; + break; + case ORIENTATION_FLIP_Y: + prim->texcoords = render_quad_texuv{ { xoffset, yend }, { xend, yend }, { xoffset, yoffset }, { xend, yoffset } }; + break; + case ORIENTATION_FLIP_X | ORIENTATION_FLIP_Y: + prim->texcoords = render_quad_texuv{ { xend, yend }, { xoffset, yend }, { xend, yoffset }, { xoffset, yoffset } }; + break; + case ORIENTATION_SWAP_XY: + prim->texcoords = render_quad_texuv{ { xoffset, yoffset }, { xoffset, yend }, { xend, yoffset }, { xend, yend } }; + break; + case ORIENTATION_SWAP_XY | ORIENTATION_FLIP_X: + prim->texcoords = render_quad_texuv{ { xoffset, yend }, { xoffset, yoffset }, { xend, yend }, { xend, yoffset } }; + break; + case ORIENTATION_SWAP_XY | ORIENTATION_FLIP_Y: + prim->texcoords = render_quad_texuv{ { xend, yoffset }, { xend, yend }, { xoffset, yoffset }, { xoffset, yend } }; + break; + case ORIENTATION_SWAP_XY | ORIENTATION_FLIP_X | ORIENTATION_FLIP_Y: + prim->texcoords = render_quad_texuv{ { xend, yend }, { xend, yoffset }, { xoffset, yend }, { xoffset, yoffset } }; + break; + } // add to the list or free if we're clipped out + bool const clipped = render_clip_quad(prim->bounds, cliprect, &prim->texcoords); list.append_or_return(*prim, clipped); } } @@ -2450,7 +2792,7 @@ void render_target::add_element_primitives(render_primitive_list &list, const ob // mapping points //------------------------------------------------- -bool render_target::map_point_internal(s32 target_x, s32 target_y, render_container *container, float &mapped_x, float &mapped_y, ioport_port *&mapped_input_port, ioport_value &mapped_input_mask) +std::pair<float, float> render_target::map_point_internal(s32 target_x, s32 target_y) { // compute the visible width/height s32 viswidth, visheight; @@ -2458,62 +2800,33 @@ bool render_target::map_point_internal(s32 target_x, s32 target_y, render_contai // create a root transform for the target object_transform root_xform; - root_xform.xoffs = (float)(m_width - viswidth) / 2; - root_xform.yoffs = (float)(m_height - visheight) / 2; - - // default to point not mapped - mapped_x = -1.0; - mapped_y = -1.0; - mapped_input_port = nullptr; - mapped_input_mask = 0; + root_xform.xoffs = float(m_width - viswidth) / 2; + root_xform.yoffs = float(m_height - visheight) / 2; // convert target coordinates to float - float target_fx = (float)(target_x - root_xform.xoffs) / viswidth; - float target_fy = (float)(target_y - root_xform.yoffs) / visheight; - if (m_manager.machine().ui().is_menu_active()) - { - target_fx = (float)target_x / m_width; - target_fy = (float)target_y / m_height; - } - // explicitly check for the UI container - if (container != nullptr && container == &m_manager.ui_container()) - { - // this hit test went against the UI container - if (target_fx >= 0.0f && target_fx < 1.0f && target_fy >= 0.0f && target_fy < 1.0f) - { - // this point was successfully mapped - mapped_x = (float)target_x / m_width; - mapped_y = (float)target_y / m_height; - return true; - } - return false; - } - - // iterate over items in the view - for (layout_view::item &item : m_curview->items()) - { - bool checkit; + if (!m_manager.machine().ui().is_menu_active()) + return std::make_pair(float(target_x - root_xform.xoffs) / viswidth, float(target_y - root_xform.yoffs) / visheight); + else + return std::make_pair(float(target_x) / m_width, float(target_y) / m_height); +} - // if we're looking for a particular container, verify that we have the right one - if (container != nullptr) - checkit = (item.screen() != nullptr && &item.screen()->container() == container); - // otherwise, assume we're looking for an input - else - checkit = item.has_input(); - - // this target is worth looking at; now check the point - if (checkit && target_fx >= item.bounds().x0 && target_fx < item.bounds().x1 && target_fy >= item.bounds().y0 && target_fy < item.bounds().y1) - { - // point successfully mapped - mapped_x = (target_fx - item.bounds().x0) / (item.bounds().x1 - item.bounds().x0); - mapped_y = (target_fy - item.bounds().y0) / (item.bounds().y1 - item.bounds().y0); - mapped_input_port = item.input_tag_and_mask(mapped_input_mask); - return true; - } - } +//------------------------------------------------- +// map_point_layout - map point from screen +// coordinates to layout coordinates +//------------------------------------------------- - return false; +std::pair<float, float> render_target::map_point_layout(s32 target_x, s32 target_y) +{ + using std::swap; + std::pair<float, float> result(map_point_internal(target_x, target_y)); + if (m_orientation & ORIENTATION_FLIP_X) + result.first = 1.0f - result.first; + if (m_orientation & ORIENTATION_FLIP_Y) + result.second = 1.0f - result.second; + if (m_orientation & ORIENTATION_SWAP_XY) + swap(result.first, result.second); + return result; } @@ -2522,15 +2835,9 @@ bool render_target::map_point_internal(s32 target_x, s32 target_y, render_contai // view, or nullptr if it doesn't exist //------------------------------------------------- -layout_view *render_target::view_by_index(int index) +layout_view *render_target::view_by_index(unsigned index) { - // scan the list of views within each layout, skipping those that don't apply - for (layout_file &file : m_filelist) - for (layout_view &view : file.views()) - if (!(m_flags & RENDER_CREATE_NO_ART) || !view.has_art()) - if (index-- == 0) - return &view; - return nullptr; + return (m_views.size() > index) ? &m_views[index].first : nullptr; } @@ -2541,18 +2848,12 @@ layout_view *render_target::view_by_index(int index) int render_target::view_index(layout_view &targetview) const { - // find the first named match - int index = 0; - - // scan the list of views within each layout, skipping those that don't apply - for (layout_file const &file : m_filelist) - for (layout_view const &view : file.views()) - if (!(m_flags & RENDER_CREATE_NO_ART) || !view.has_art()) - { - if (&targetview == &view) - return index; - index++; - } + // return index of view, or zero if not found + for (int index = 0; m_views.size() > index; ++index) + { + if (&m_views[index].first == &targetview) + return index; + } return 0; } @@ -2561,53 +2862,99 @@ int render_target::view_index(layout_view &targetview) const // config_load - process config information //------------------------------------------------- -void render_target::config_load(util::xml::data_node const &targetnode) +void render_target::config_load(util::xml::data_node const *targetnode) { - int tmpint; + // remember the view selected via command line and INI options + if (!m_base_view) + m_base_view = ¤t_view(); + + // bail if no configuration + if (!targetnode) + return; + + // TODO: consider option priority - command line should take precedence over CFG + // not practical at the moment because view selection options are in the OSD layer // find the view - const char *viewname = targetnode.get_attribute_string("view", nullptr); - if (viewname != nullptr) - for (int viewnum = 0; viewnum < 1000; viewnum++) + const char *viewname = targetnode->get_attribute_string("view", nullptr); + if (viewname) + { + for (unsigned viewnum = 0; m_views.size() > viewnum; viewnum++) { - const char *testname = view_name(viewnum); - if (testname == nullptr) - break; - if (!strcmp(viewname, testname)) + if (!strcmp(viewname, view_name(viewnum))) { set_view(viewnum); break; } } + } // modify the artwork config - tmpint = targetnode.get_attribute_int("zoom", -1); - if (tmpint == 0 || tmpint == 1) - set_zoom_to_screen(tmpint); + int const zoom = targetnode->get_attribute_int("zoom", -1); + if (zoom == 0 || zoom == 1) + set_zoom_to_screen(zoom); // apply orientation - tmpint = targetnode.get_attribute_int("rotate", -1); - if (tmpint != -1) - { - if (tmpint == 90) - tmpint = ROT90; - else if (tmpint == 180) - tmpint = ROT180; - else if (tmpint == 270) - tmpint = ROT270; + int rotate = targetnode->get_attribute_int("rotate", -1); + if (rotate != -1) + { + if (rotate == 90) + rotate = ROT90; + else if (rotate == 180) + rotate = ROT180; + else if (rotate == 270) + rotate = ROT270; else - tmpint = ROT0; - set_orientation(orientation_add(tmpint, orientation())); + rotate = ROT0; + set_orientation(orientation_add(rotate, orientation())); // apply the opposite orientation to the UI - if (is_ui_target()) + if (m_ui_container) { - render_container::user_settings settings; - render_container &ui_container = m_manager.ui_container(); + render_container::user_settings settings = m_ui_container->get_user_settings(); + settings.m_orientation = orientation_add(orientation_reverse(rotate), settings.m_orientation); + m_ui_container->set_user_settings(settings); + } + } + + // apply per-view settings + for (util::xml::data_node const *viewnode = targetnode->get_child("view"); viewnode; viewnode = viewnode->get_next_sibling("view")) + { + char const *const viewname = viewnode->get_attribute_string("name", nullptr); + if (!viewname) + continue; + + auto const view = std::find_if(m_views.begin(), m_views.end(), [viewname] (auto const &x) { return x.first.name() == viewname; }); + if (m_views.end() == view) + continue; - ui_container.get_user_settings(settings); - settings.m_orientation = orientation_add(orientation_reverse(tmpint), settings.m_orientation); - ui_container.set_user_settings(settings); + for (util::xml::data_node const *vistogglenode = viewnode->get_child("collection"); vistogglenode; vistogglenode = vistogglenode->get_next_sibling("collection")) + { + char const *const vistogglename = vistogglenode->get_attribute_string("name", nullptr); + if (!vistogglename) + continue; + + auto const &vistoggles = view->first.visibility_toggles(); + auto const vistoggle = std::find_if(vistoggles.begin(), vistoggles.end(), [vistogglename] (auto const &x) { return x.name() == vistogglename; }); + if (vistoggles.end() == vistoggle) + continue; + + int const enable = vistogglenode->get_attribute_int("visible", -1); + if (0 <= enable) + { + if (enable) + view->second |= u32(1) << std::distance(vistoggles.begin(), vistoggle); + else + view->second &= ~(u32(1) << std::distance(vistoggles.begin(), vistoggle)); + } + } + + if (¤t_view() == &view->first) + { + current_view().recompute(visibility_mask(), m_layerconfig.zoom_to_screen()); + current_view().preload(); + m_clickable_items.clear(); + m_clickable_items.resize(current_view().interactive_items().size()); } } } @@ -2626,9 +2973,9 @@ bool render_target::config_save(util::xml::data_node &targetnode) targetnode.set_attribute_int("index", index()); // output the view - if (m_curview != m_base_view) + if (¤t_view() != m_base_view) { - targetnode.set_attribute("view", m_curview->name().c_str()); + targetnode.set_attribute("view", current_view().name().c_str()); changed = true; } @@ -2654,6 +3001,33 @@ bool render_target::config_save(util::xml::data_node &targetnode) changed = true; } + // output layer configuration + for (auto const &view : m_views) + { + u32 const defvismask = view.first.default_visibility_mask(); + if (defvismask != view.second) + { + util::xml::data_node *viewnode = nullptr; + unsigned i = 0; + for (layout_view::visibility_toggle const &toggle : view.first.visibility_toggles()) + { + if (BIT(defvismask, i) != BIT(view.second, i)) + { + if (!viewnode) + { + viewnode = targetnode.add_child("view", nullptr); + viewnode->set_attribute("name", view.first.name().c_str()); + } + util::xml::data_node *const vistogglenode = viewnode->add_child("collection", nullptr); + vistogglenode->set_attribute("name", toggle.name().c_str()); + vistogglenode->set_attribute_int("visible", BIT(view.second, i)); + changed = true; + } + ++i; + } + } + } + return changed; } @@ -2843,9 +3217,9 @@ void render_target::add_clear_extents(render_primitive_list &list) if (x1 - x0 > 0) { render_primitive *prim = list.alloc(render_primitive::QUAD); - set_render_bounds_xy(prim->bounds, (float)x0, (float)y0, (float)x1, (float)y1); + prim->bounds.set_xy(float(x0), float(y0), float(x1), float(y1)); prim->full_bounds = prim->bounds; - set_render_color(&prim->color, 1.0f, 0.0f, 0.0f, 0.0f); + prim->color.set(1.0f, 0.0f, 0.0f, 0.0f); prim->texture.base = nullptr; prim->flags = PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA); clearlist.append(*prim); @@ -2899,7 +3273,7 @@ void render_target::add_clear_and_optimize_primitive_list(render_primitive_list if (PRIMFLAG_GET_BLENDMODE(prim.flags) == BLENDMODE_RGB_MULTIPLY) { // RGB multiply will multiply against 0, leaving nothing - set_render_color(&prim.color, 1.0f, 0.0f, 0.0f, 0.0f); + prim.color.set(1.0f, 0.0f, 0.0f, 0.0f); prim.texture.base = nullptr; prim.flags = (prim.flags & ~PRIMFLAG_BLENDMODE_MASK) | PRIMFLAG_BLENDMODE(BLENDMODE_NONE); } @@ -2938,18 +3312,20 @@ done: //------------------------------------------------- render_manager::render_manager(running_machine &machine) - : m_machine(machine), - m_ui_target(nullptr), - m_live_textures(0), - m_texture_id(0), - m_ui_container(global_alloc(render_container(*this))) + : m_machine(machine) + , m_ui_target(nullptr) + , m_live_textures(0) + , m_texture_id(0) { // register callbacks - machine.configuration().config_register("video", config_load_delegate(&render_manager::config_load, this), config_save_delegate(&render_manager::config_save, this)); + machine.configuration().config_register( + "video", + configuration_manager::load_delegate(&render_manager::config_load, this), + configuration_manager::save_delegate(&render_manager::config_save, this)); // create one container per screen - for (screen_device &screen : screen_device_iterator(machine.root_device())) - screen.set_container(*container_alloc(&screen)); + for (screen_device &screen : screen_device_enumerator(machine.root_device())) + screen.set_container(m_screen_container_list.emplace_back(*this, &screen)); } @@ -2960,8 +3336,8 @@ render_manager::render_manager(running_machine &machine) render_manager::~render_manager() { // free all the containers since they may own textures - container_free(m_ui_container); - m_screen_container_list.reset(); + m_ui_containers.clear(); + m_screen_container_list.clear(); // better not be any outstanding textures when we die assert(m_live_textures == 0); @@ -2975,9 +3351,15 @@ render_manager::~render_manager() bool render_manager::is_live(screen_device &screen) const { // iterate over all live targets and or together their screen masks - for (render_target &target : m_targetlist) - if (!target.hidden() && target.view_screens(target.view()).contains(screen)) - return true; + for (render_target const &target : m_targetlist) + { + if (!target.hidden()) + { + layout_view const *view = &target.current_view(); + if (view->has_visible_screen(screen)) + return true; + } + } return false; } @@ -3010,12 +3392,14 @@ float render_manager::max_update_rate() const render_target *render_manager::target_alloc(const internal_layout *layoutfile, u32 flags) { - return &m_targetlist.append(*global_alloc(render_target(*this, layoutfile, flags))); + render_container *const ui = (flags & RENDER_CREATE_HIDDEN) ? nullptr : &m_ui_containers.emplace_back(*this); + return &m_targetlist.append(*new render_target(*this, ui, layoutfile, flags)); } render_target *render_manager::target_alloc(util::xml::data_node const &layout, u32 flags) { - return &m_targetlist.append(*global_alloc(render_target(*this, layout, flags))); + render_container *const ui = (flags & RENDER_CREATE_HIDDEN) ? nullptr : &m_ui_containers.emplace_back(*this); + return &m_targetlist.append(*new render_target(*this, ui, layout, flags)); } @@ -3052,35 +3436,56 @@ render_target *render_manager::target_by_index(int index) const float render_manager::ui_aspect(render_container *rc) { - int orient; + // work out if this is a UI container + render_target *target = nullptr; + if (!rc) + { + target = &ui_target(); + rc = target->ui_container(); + assert(rc); + } + else + { + for (render_target &t : m_targetlist) + { + if (t.ui_container() == rc) + { + target = &t; + break; + } + } + } + float aspect; - if (rc == m_ui_container || rc == nullptr) { - // ui container, aggregated multi-screen target + if (target) + { + // UI container, aggregated multi-screen target - orient = orientation_add(m_ui_target->orientation(), m_ui_container->orientation()); // based on the orientation of the target, compute height/width or width/height + int const orient = orientation_add(target->orientation(), rc->orientation()); if (!(orient & ORIENTATION_SWAP_XY)) - aspect = (float)m_ui_target->height() / (float)m_ui_target->width(); + aspect = float(target->height()) / float(target->width()); else - aspect = (float)m_ui_target->width() / (float)m_ui_target->height(); + aspect = float(target->width()) / float(target->height()); // if we have a valid pixel aspect, apply that and return - if (m_ui_target->pixel_aspect() != 0.0f) + if (target->pixel_aspect() != 0.0f) { - float pixel_aspect = m_ui_target->pixel_aspect(); + float pixel_aspect = target->pixel_aspect(); if (orient & ORIENTATION_SWAP_XY) pixel_aspect = 1.0f / pixel_aspect; return aspect /= pixel_aspect; } - - } else { + } + else + { // single screen container - orient = rc->orientation(); // based on the orientation of the target, compute height/width or width/height + int const orient = rc->orientation(); if (!(orient & ORIENTATION_SWAP_XY)) aspect = (float)rc->screen()->visible_area().height() / (float)rc->screen()->visible_area().width(); else @@ -3088,12 +3493,7 @@ float render_manager::ui_aspect(render_container *rc) } // clamp for extreme proportions - if (aspect < 0.66f) - aspect = 0.66f; - if (aspect > 1.5f) - aspect = 1.5f; - - return aspect; + return std::clamp(aspect, 0.66f, 1.5f); } @@ -3132,19 +3532,9 @@ void render_manager::texture_free(render_texture *texture) // font_alloc - allocate a new font instance //------------------------------------------------- -render_font *render_manager::font_alloc(const char *filename) -{ - return global_alloc(render_font(*this, filename)); -} - - -//------------------------------------------------- -// font_free - release a font instance -//------------------------------------------------- - -void render_manager::font_free(render_font *font) +std::unique_ptr<render_font> render_manager::font_alloc(const char *filename) { - global_free(font); + return std::unique_ptr<render_font>(new render_font(*this, filename)); } @@ -3177,83 +3567,72 @@ void render_manager::resolve_tags() //------------------------------------------------- -// container_alloc - allocate a new container -//------------------------------------------------- - -render_container *render_manager::container_alloc(screen_device *screen) -{ - auto container = global_alloc(render_container(*this, screen)); - if (screen != nullptr) - m_screen_container_list.append(*container); - return container; -} - - -//------------------------------------------------- -// container_free - release a container -//------------------------------------------------- - -void render_manager::container_free(render_container *container) -{ - m_screen_container_list.remove(*container); -} - - -//------------------------------------------------- // config_load - read and apply data from the // configuration file //------------------------------------------------- -void render_manager::config_load(config_type cfg_type, util::xml::data_node const *parentnode) +void render_manager::config_load(config_type cfg_type, config_level cfg_level, util::xml::data_node const *parentnode) { - // we only care about game files - if (cfg_type != config_type::GAME) + // we only care about system-specific configuration with matching nodes + if (cfg_type == config_type::DEFAULT) + { + // let the targets stabilise themselves + for (render_target &target : m_targetlist) + { + if (!target.hidden()) + target.config_load(nullptr); + } return; - - // might not have any data - if (parentnode == nullptr) + } + else if ((cfg_type != config_type::SYSTEM) || !parentnode) + { return; + } // check the UI target util::xml::data_node const *const uinode = parentnode->get_child("interface"); - if (uinode != nullptr) + if (uinode) { - render_target *target = target_by_index(uinode->get_attribute_int("target", 0)); - if (target != nullptr) + render_target *const target = target_by_index(uinode->get_attribute_int("target", 0)); + if (target) set_ui_target(*target); } // iterate over target nodes for (util::xml::data_node const *targetnode = parentnode->get_child("target"); targetnode; targetnode = targetnode->get_next_sibling("target")) { - render_target *target = target_by_index(targetnode->get_attribute_int("index", -1)); - if (target != nullptr) - target->config_load(*targetnode); + render_target *const target = target_by_index(targetnode->get_attribute_int("index", -1)); + if (target && !target->hidden()) + target->config_load(targetnode); } // iterate over screen nodes for (util::xml::data_node const *screennode = parentnode->get_child("screen"); screennode; screennode = screennode->get_next_sibling("screen")) { - int index = screennode->get_attribute_int("index", -1); - render_container *container = m_screen_container_list.find(index); - render_container::user_settings settings; - - // fetch current settings - container->get_user_settings(settings); - - // fetch color controls - settings.m_brightness = screennode->get_attribute_float("brightness", settings.m_brightness); - settings.m_contrast = screennode->get_attribute_float("contrast", settings.m_contrast); - settings.m_gamma = screennode->get_attribute_float("gamma", settings.m_gamma); - - // fetch positioning controls - settings.m_xoffset = screennode->get_attribute_float("hoffset", settings.m_xoffset); - settings.m_xscale = screennode->get_attribute_float("hstretch", settings.m_xscale); - settings.m_yoffset = screennode->get_attribute_float("voffset", settings.m_yoffset); - settings.m_yscale = screennode->get_attribute_float("vstretch", settings.m_yscale); + int const index = screennode->get_attribute_int("index", -1); + render_container *container = nullptr; + if (index >= 0 && index < m_screen_container_list.size()) + container = &*std::next(m_screen_container_list.begin(), index); - // set the new values - container->set_user_settings(settings); + if (container != nullptr) + { + // fetch current settings + render_container::user_settings settings = container->get_user_settings(); + + // fetch color controls + settings.m_brightness = screennode->get_attribute_float("brightness", settings.m_brightness); + settings.m_contrast = screennode->get_attribute_float("contrast", settings.m_contrast); + settings.m_gamma = screennode->get_attribute_float("gamma", settings.m_gamma); + + // fetch positioning controls + settings.m_xoffset = screennode->get_attribute_float("hoffset", settings.m_xoffset); + settings.m_xscale = screennode->get_attribute_float("hstretch", settings.m_xscale); + settings.m_yoffset = screennode->get_attribute_float("voffset", settings.m_yoffset); + settings.m_yscale = screennode->get_attribute_float("vstretch", settings.m_yscale); + + // set the new values + container->set_user_settings(settings); + } } } @@ -3265,8 +3644,8 @@ void render_manager::config_load(config_type cfg_type, util::xml::data_node cons void render_manager::config_save(config_type cfg_type, util::xml::data_node *parentnode) { - // we only care about game files - if (cfg_type != config_type::GAME) + // we only save system-specific configuration + if (cfg_type != config_type::SYSTEM) return; // write out the interface target @@ -3279,22 +3658,26 @@ void render_manager::config_save(config_type cfg_type, util::xml::data_node *par } // iterate over targets - for (int targetnum = 0; targetnum < 1000; targetnum++) + for (int targetnum = 0; ; ++targetnum) { // get this target and break when we fail render_target *target = target_by_index(targetnum); - if (target == nullptr) + if (!target) + { break; - - // create a node - util::xml::data_node *const targetnode = parentnode->add_child("target", nullptr); - if (targetnode != nullptr && !target->config_save(*targetnode)) - targetnode->delete_node(); + } + else if (!target->hidden()) + { + // create a node + util::xml::data_node *const targetnode = parentnode->add_child("target", nullptr); + if (targetnode && !target->config_save(*targetnode)) + targetnode->delete_node(); + } } // iterate over screen containers int scrnum = 0; - for (render_container *container = m_screen_container_list.first(); container != nullptr; container = container->next(), scrnum++) + for (render_container &container : m_screen_container_list) { // create a node util::xml::data_node *const screennode = parentnode->add_child("screen", nullptr); @@ -3305,8 +3688,7 @@ void render_manager::config_save(config_type cfg_type, util::xml::data_node *par // output the basics screennode->set_attribute_int("index", scrnum); - render_container::user_settings settings; - container->get_user_settings(settings); + render_container::user_settings const settings = container.get_user_settings(); // output the color controls if (settings.m_brightness != machine().options().brightness()) @@ -3356,5 +3738,7 @@ void render_manager::config_save(config_type cfg_type, util::xml::data_node *par if (!changed) screennode->delete_node(); } + + scrnum++; } } diff --git a/src/emu/render.h b/src/emu/render.h index 0d70dc7684b..c4c0f504edd 100644 --- a/src/emu/render.h +++ b/src/emu/render.h @@ -46,36 +46,22 @@ #ifndef MAME_EMU_RENDER_H #define MAME_EMU_RENDER_H -#include "screen.h" +#include "rendertypes.h" -#include <math.h> -#include <array> -#include <map> +#include "interface/uievents.h" + +#include <cmath> +#include <list> #include <memory> #include <mutex> -#include <string> -#include <unordered_map> +#include <utility> #include <vector> -namespace emu { namespace render { namespace detail { class layout_environment; } } } - - //************************************************************************** // CONSTANTS //************************************************************************** -// blending modes -enum -{ - BLENDMODE_NONE = 0, // no blending - BLENDMODE_ALPHA, // standard alpha blend - BLENDMODE_RGB_MULTIPLY, // apply source alpha to source pix, then multiply RGB values - BLENDMODE_ADD, // apply source alpha to source pix, then add to destination - - BLENDMODE_COUNT -}; - // render creation flags constexpr u8 RENDER_CREATE_NO_ART = 0x01; // ignore any views that have art in them @@ -166,46 +152,6 @@ constexpr u32 PRIMFLAG_GET_VECTORBUF(u32 x) { return (x & PRIMFLAG_VECTORBUF_MAS // texture scaling callback typedef void (*texture_scaler_func)(bitmap_argb32 &dest, bitmap_argb32 &source, const rectangle &sbounds, void *param); -// render_bounds - floating point bounding rectangle -struct render_bounds -{ - float x0; // leftmost X coordinate - float y0; // topmost Y coordinate - float x1; // rightmost X coordinate - float y1; // bottommost Y coordinate - - constexpr float width() const { return x1 - x0; } - constexpr float height() const { return y1 - y0; } -}; - - -// render_color - floating point set of ARGB values -struct render_color -{ - float a; // alpha component (0.0 = transparent, 1.0 = opaque) - float r; // red component (0.0 = none, 1.0 = max) - float g; // green component (0.0 = none, 1.0 = max) - float b; // blue component (0.0 = none, 1.0 = max) -}; - - -// render_texuv - floating point set of UV texture coordinates -struct render_texuv -{ - float u; // U coordinate (0.0-1.0) - float v; // V coordinate (0.0-1.0) -}; - - -// render_quad_texuv - floating point set of UV texture coordinates -struct render_quad_texuv -{ - render_texuv tl; // top-left UV coordinate - render_texuv tr; // top-right UV coordinate - render_texuv bl; // bottom-left UV coordinate - render_texuv br; // bottom-right UV coordinate -}; - // render_texinfo - texture information struct render_texinfo @@ -213,54 +159,13 @@ struct render_texinfo void * base; // base of the data u32 rowpixels; // pixels per row u32 width; // width of the image + u32 width_margin; // left margin of the scaled bounds, if applicable u32 height; // height of the image u32 seqid; // sequence ID u64 unique_id; // unique identifier to pass to osd u64 old_id; // previously allocated id, if applicable const rgb_t * palette; // palette for PALETTE16 textures, bcg lookup table for RGB32/YUY16 -}; - - -// ======================> render_screen_list - -// a render_screen_list is a list of screen_devices -class render_screen_list -{ - // screen list item - class item - { - friend class simple_list<item>; - friend class render_screen_list; - - public: - // construction/destruction - item(screen_device &screen) : m_screen(screen) { } - - // state - item * m_next = nullptr; // next screen in list - screen_device & m_screen; // reference to screen device - }; - -public: - // getters - int count() const { return m_list.count(); } - - // operations - void add(screen_device &screen) { m_list.append(*global_alloc(item(screen))); } - void reset() { m_list.reset(); } - - // query - int contains(screen_device &screen) const - { - int count = 0; - for (item *curitem = m_list.first(); curitem; curitem = curitem->m_next) - if (&curitem->m_screen == &screen) count++; - return count; - } - -private: - // internal state - simple_list<item> m_list; + u32 palette_length; }; @@ -403,7 +308,6 @@ private: // a render_texture is used to track transformations when building an object list class render_texture { - friend resource_pool_object<render_texture>::~resource_pool_object(); friend class simple_list<render_texture>; friend class fixed_allocator<render_texture>; friend class render_manager; @@ -436,15 +340,15 @@ public: private: // internal helpers void get_scaled(u32 dwidth, u32 dheight, render_texinfo &texinfo, render_primitive_list &primlist, u32 flags = 0); - const rgb_t *get_adjusted_palette(render_container &container); + const rgb_t *get_adjusted_palette(render_container &container, u32 &out_length); - static const int MAX_TEXTURE_SCALES = 16; + static constexpr int MAX_TEXTURE_SCALES = 100; // a scaled_texture contains a single scaled entry for a texture struct scaled_texture { - bitmap_argb32 * bitmap; // final bitmap - u32 seqid; // sequence number + std::unique_ptr<bitmap_argb32> bitmap; // final bitmap + u32 seqid; // sequence number }; // internal state @@ -469,16 +373,14 @@ private: // a render_container holds a list of items and an orientation for the entire collection class render_container { - friend resource_pool_object<render_container>::~resource_pool_object(); - friend class simple_list<render_container>; friend class render_manager; friend class render_target; +public: // construction/destruction render_container(render_manager &manager, screen_device *screen = nullptr); ~render_container(); -public: // user settings describes the collected user-controllable settings struct user_settings { @@ -497,7 +399,6 @@ public: }; // getters - render_container *next() const { return m_next; } screen_device *screen() const { return m_screen; } render_manager &manager() const { return m_manager; } render_texture *overlay() const { return m_overlaytexture; } @@ -506,8 +407,8 @@ public: float yscale() const { return m_user.m_yscale; } float xoffset() const { return m_user.m_xoffset; } float yoffset() const { return m_user.m_yoffset; } - bool is_empty() const { return (m_itemlist.count() == 0); } - void get_user_settings(user_settings &settings) const { settings = m_user; } + bool is_empty() const { return m_itemlist.empty(); } + const user_settings &get_user_settings() const { return m_user; } // setters void set_overlay(bitmap_argb32 *bitmap); @@ -527,7 +428,7 @@ public: bool has_brightness_contrast_gamma_changes() const { return (m_user.m_brightness != 1.0f || m_user.m_contrast != 1.0f || m_user.m_gamma != 1.0f); } u8 apply_brightness_contrast_gamma(u8 value); float apply_brightness_contrast_gamma_fp(float value); - const rgb_t *bcg_lookup_table(int texformat, palette_t *palette = nullptr); + const rgb_t *bcg_lookup_table(int texformat, u32 &out_length, palette_t *palette = nullptr); private: // an item describes a high level primitive that is added to a container @@ -571,7 +472,6 @@ private: void update_palette(); // internal state - render_container * m_next; // the next container in the list render_manager & m_manager; // reference back to the owning manager simple_list<item> m_itemlist; // head of the item list fixed_allocator<item> m_item_allocator; // free container items @@ -585,382 +485,35 @@ private: }; - -//************************************************************************** -// TYPE DEFINITIONS -//************************************************************************** - - -/// \brief A description of a piece of visible artwork -/// -/// Most view_items (except for those in the screen layer) have exactly -/// one layout_element which describes the contents of the item. -/// Elements are separate from items because they can be re-used -/// multiple times within a layout. Even though an element can contain -/// a number of components, they are treated as if they were a single -/// bitmap. -class layout_element -{ -public: - using environment = emu::render::detail::layout_environment; - - // construction/destruction - layout_element(environment &env, util::xml::data_node const &elemnode, const char *dirname); - virtual ~layout_element(); - - // getters - running_machine &machine() const { return m_machine; } - int default_state() const { return m_defstate; } - int maxstate() const { return m_maxstate; } - render_texture *state_texture(int state); - -private: - /// \brief An image, rectangle, or disk in an element - /// - /// Each layout_element contains one or more components. Each - /// component can describe either an image or a rectangle/disk - /// primitive. Each component also has a "state" associated with it, - /// which controls whether or not the component is visible (if the - /// owning item has the same state, it is visible). - class component - { - public: - typedef std::unique_ptr<component> ptr; - - // construction/destruction - component(environment &env, util::xml::data_node const &compnode, const char *dirname); - virtual ~component() = default; - - // setup - void normalize_bounds(float xoffs, float yoffs, float xscale, float yscale); - - // getters - int state() const { return m_state; } - virtual int maxstate() const { return m_state; } - const render_bounds &bounds() const { return m_bounds; } - const render_color &color() const { return m_color; } - - // operations - virtual void draw(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) = 0; - - protected: - // helpers - void draw_text(render_font &font, bitmap_argb32 &dest, const rectangle &bounds, const char *str, int align); - void draw_segment_horizontal_caps(bitmap_argb32 &dest, int minx, int maxx, int midy, int width, int caps, rgb_t color); - void draw_segment_horizontal(bitmap_argb32 &dest, int minx, int maxx, int midy, int width, rgb_t color); - void draw_segment_vertical_caps(bitmap_argb32 &dest, int miny, int maxy, int midx, int width, int caps, rgb_t color); - void draw_segment_vertical(bitmap_argb32 &dest, int miny, int maxy, int midx, int width, rgb_t color); - void draw_segment_diagonal_1(bitmap_argb32 &dest, int minx, int maxx, int miny, int maxy, int width, rgb_t color); - void draw_segment_diagonal_2(bitmap_argb32 &dest, int minx, int maxx, int miny, int maxy, int width, rgb_t color); - void draw_segment_decimal(bitmap_argb32 &dest, int midx, int midy, int width, rgb_t color); - void draw_segment_comma(bitmap_argb32 &dest, int minx, int maxx, int miny, int maxy, int width, rgb_t color); - void apply_skew(bitmap_argb32 &dest, int skewwidth); - - private: - // internal state - int m_state; // state where this component is visible (-1 means all states) - render_bounds m_bounds; // bounds of the element - render_color m_color; // color of the element - }; - - // component implementations - class image_component; - class rect_component; - class disk_component; - class text_component; - class led7seg_component; - class led8seg_gts1_component; - class led14seg_component; - class led16seg_component; - class led14segsc_component; - class led16segsc_component; - class dotmatrix_component; - class simplecounter_component; - class reel_component; - - // a texture encapsulates a texture for a given element in a given state - class texture - { - public: - texture(); - texture(texture const &that) = delete; - texture(texture &&that); - - ~texture(); - - texture &operator=(texture const &that) = delete; - texture &operator=(texture &&that); - - layout_element * m_element; // pointer back to the element - render_texture * m_texture; // texture for this state - int m_state; // associated state number - }; - - typedef component::ptr (*make_component_func)(environment &env, util::xml::data_node const &compnode, const char *dirname); - typedef std::map<std::string, make_component_func> make_component_map; - - // internal helpers - static void element_scale(bitmap_argb32 &dest, bitmap_argb32 &source, const rectangle &sbounds, void *param); - template <typename T> static component::ptr make_component(environment &env, util::xml::data_node const &compnode, const char *dirname); - template <int D> static component::ptr make_dotmatrix_component(environment &env, util::xml::data_node const &compnode, const char *dirname); - - static make_component_map const s_make_component; // maps component XML names to creator functions - - // internal state - running_machine & m_machine; // reference to the owning machine - std::vector<component::ptr> m_complist; // list of components - int m_defstate; // default state of this element - int m_maxstate; // maximum state value for all components - std::vector<texture> m_elemtex; // array of element textures used for managing the scaled bitmaps -}; - - -/// \brief A reusable group of elements -/// -/// Views expand/flatten groups into their component elements applying -/// an optional coordinate transform. This is mainly useful duplicating -/// the same sublayout in multiple views. It would be more useful -/// within a view if it could be parameterised. Groups only exist while -/// parsing a layout file - no information about element grouping is -/// preserved. -class layout_group -{ -public: - using environment = emu::render::detail::layout_environment; - using group_map = std::unordered_map<std::string, layout_group>; - using transform = std::array<std::array<float, 3>, 3>; - - layout_group(util::xml::data_node const &groupnode); - ~layout_group(); - - util::xml::data_node const &get_groupnode() const { return m_groupnode; } - - transform make_transform(int orientation, render_bounds const &dest) const; - transform make_transform(int orientation, transform const &trans) const; - transform make_transform(int orientation, render_bounds const &dest, transform const &trans) const; - - void set_bounds_unresolved(); - void resolve_bounds(environment &env, group_map &groupmap); - -private: - void resolve_bounds(environment &env, group_map &groupmap, std::vector<layout_group const *> &seen); - void resolve_bounds( - environment &env, - util::xml::data_node const &parentnode, - group_map &groupmap, - std::vector<layout_group const *> &seen, - bool empty, - bool repeat, - bool init); - - util::xml::data_node const & m_groupnode; - render_bounds m_bounds; - bool m_bounds_resolved; -}; - - -/// \brief A single view within a layout_file -/// -/// The view is described using arbitrary coordinates that are scaled to -/// fit within the render target. Pixels within a view are assumed to -/// be square. -class layout_view -{ -public: - using environment = emu::render::detail::layout_environment; - using element_map = std::unordered_map<std::string, layout_element>; - using group_map = std::unordered_map<std::string, layout_group>; - - /// \brief A single backdrop/screen/overlay/bezel/cpanel/marquee item - /// - /// Each view has four lists of view_items, one for each "layer." - /// Each view item is specified using floating point coordinates in - /// arbitrary units, and is assumed to have square pixels. Each - /// view item can control its orientation independently. Each item - /// can also have an optional name, and can be set at runtime into - /// different "states", which control how the embedded elements are - /// displayed. - class item - { - friend class layout_view; - - public: - // construction/destruction - item( - environment &env, - util::xml::data_node const &itemnode, - element_map &elemmap, - int orientation, - layout_group::transform const &trans, - render_color const &color); - ~item(); - - // getters - layout_element *element() const { return m_element; } - screen_device *screen() { return m_screen; } - const render_bounds &bounds() const { return m_bounds; } - const render_color &color() const { return m_color; } - int blend_mode() const { return m_blend_mode; } - int orientation() const { return m_orientation; } - render_container *screen_container(running_machine &machine) const; - bool has_input() const { return bool(m_input_port); } - ioport_port *input_tag_and_mask(ioport_value &mask) const { mask = m_input_mask; return m_input_port; }; - - // fetch state based on configured source - int state() const; - - // resolve tags, if any - void resolve_tags(); - - // setters - void set_blend_mode(int mode) { m_blend_mode = mode; } - - private: - static layout_element *find_element(environment &env, util::xml::data_node const &itemnode, element_map &elemmap); - static render_bounds make_bounds(environment &env, util::xml::data_node const &itemnode, layout_group::transform const &trans); - static std::string make_input_tag(environment &env, util::xml::data_node const &itemnode); - static int get_blend_mode(environment &env, util::xml::data_node const &itemnode); - - // internal state - layout_element *const m_element; // pointer to the associated element (non-screens only) - output_finder<> m_output; // associated output - bool const m_have_output; // whether we actually have an output - std::string const m_input_tag; // input tag of this item - ioport_port * m_input_port; // input port of this item - ioport_field const * m_input_field; // input port field of this item - ioport_value const m_input_mask; // input mask of this item - u8 m_input_shift; // input mask rightshift for raw (trailing 0s) - bool const m_input_raw; // get raw data from input port - screen_device * m_screen; // pointer to screen - int m_orientation; // orientation of this item - render_bounds m_bounds; // bounds of the item - render_bounds const m_rawbounds; // raw (original) bounds of the item - render_color m_color; // color of the item - int m_blend_mode; // blending mode to use when drawing - }; - using item_list = std::list<item>; - - // construction/destruction - layout_view( - environment &env, - util::xml::data_node const &viewnode, - element_map &elemmap, - group_map &groupmap); - ~layout_view(); - - // getters - item_list &items() { return m_items; } - const std::string &name() const { return m_name; } - const render_bounds &bounds() const { return m_bounds; } - const render_bounds &screen_bounds() const { return m_scrbounds; } - const render_screen_list &screens() const { return m_screens; } - - // - bool has_art() const { return m_has_art; } - float effective_aspect(render_layer_config config) const { return (config.zoom_to_screen() && m_screens.count() != 0) ? m_scraspect : m_aspect; } - - // operations - void recompute(render_layer_config layerconfig); - - // resolve tags, if any - void resolve_tags(); - -private: - struct layer_lists; - - // add items, recursing for groups - void add_items( - layer_lists &layers, - environment &env, - util::xml::data_node const &parentnode, - element_map &elemmap, - group_map &groupmap, - int orientation, - layout_group::transform const &trans, - render_color const &color, - bool root, - bool repeat, - bool init); - - static std::string make_name(environment &env, util::xml::data_node const &viewnode); - - // internal state - std::string m_name; // name of the layout - float m_aspect; // X/Y of the layout - float m_scraspect; // X/Y of the screen areas - render_screen_list m_screens; // list of active screens - render_bounds m_bounds; // computed bounds of the view - render_bounds m_scrbounds; // computed bounds of the screens within the view - render_bounds m_expbounds; // explicit bounds of the view - item_list m_items; // list of layout items - bool m_has_art; // true if the layout contains non-screen elements -}; - - -/// \brief Layout description file -/// -/// Comprises a list of elements and a list of views. The elements are -/// reusable items that the views reference. -class layout_file -{ -public: - using element_map = std::unordered_map<std::string, layout_element>; - using group_map = std::unordered_map<std::string, layout_group>; - using view_list = std::list<layout_view>; - - // construction/destruction - layout_file(device_t &device, util::xml::data_node const &rootnode, char const *dirname); - ~layout_file(); - - // getters - element_map const &elements() const { return m_elemmap; } - view_list &views() { return m_viewlist; } - view_list const &views() const { return m_viewlist; } - -private: - using environment = emu::render::detail::layout_environment; - - // add elements and parameters - void add_elements( - char const *dirname, - environment &env, - util::xml::data_node const &parentnode, - group_map &groupmap, - bool repeat, - bool init); - - // internal state - element_map m_elemmap; // list of shared layout elements - view_list m_viewlist; // list of views -}; - // ======================> render_target // a render_target describes a surface that is being rendered to class render_target { - friend resource_pool_object<render_target>::~resource_pool_object(); friend class simple_list<render_target>; friend class render_manager; // construction/destruction - render_target(render_manager &manager, const internal_layout *layoutfile = nullptr, u32 flags = 0); - render_target(render_manager &manager, util::xml::data_node const &layout, u32 flags = 0); + render_target(render_manager &manager, render_container *ui, const internal_layout *layoutfile, u32 flags); + render_target(render_manager &manager, render_container *ui, util::xml::data_node const &layout, u32 flags); ~render_target(); public: // getters render_target *next() const { return m_next; } render_manager &manager() const { return m_manager; } + render_container *ui_container() const { return m_ui_container; } u32 width() const { return m_width; } u32 height() const { return m_height; } float pixel_aspect() const { return m_pixel_aspect; } + bool keepaspect() const { return m_keepaspect; } int scale_mode() const { return m_scale_mode; } float max_update_rate() const { return m_max_refresh; } int orientation() const { return m_orientation; } render_layer_config layer_config() const { return m_layerconfig; } - layout_view *current_view() const { return m_curview; } - int view() const { return view_index(*m_curview); } + layout_view ¤t_view() const { return m_views[m_curview].first; } + unsigned view() const { return m_curview; } + bool external_artwork() const { return m_external_artwork; } bool hidden() const { return ((m_flags & RENDER_CREATE_HIDDEN) != 0); } bool is_ui_target() const; int index() const; @@ -969,26 +522,34 @@ public: void set_bounds(s32 width, s32 height, float pixel_aspect = 0); void set_max_update_rate(float updates_per_second) { m_max_refresh = updates_per_second; } void set_orientation(int orientation) { m_orientation = orientation; } - void set_view(int viewindex); + void set_view(unsigned viewindex); void set_max_texture_size(int maxwidth, int maxheight); void set_transform_container(bool transform_container) { m_transform_container = transform_container; } void set_keepaspect(bool keepaspect) { m_keepaspect = keepaspect; } void set_scale_mode(int scale_mode) { m_scale_mode = scale_mode; } + // pointer input handling + void pointer_updated(osd::ui_event_handler::pointer type, u16 ptrid, u16 device, s32 x, s32 y, u32 buttons, u32 pressed, u32 released, s16 clicks); + void pointer_left(osd::ui_event_handler::pointer type, u16 ptrid, u16 device, s32 x, s32 y, u32 released, s16 clicks); + void pointer_aborted(osd::ui_event_handler::pointer type, u16 ptrid, u16 device, s32 x, s32 y, u32 released, s16 clicks); + void forget_pointers(); + void update_pointer_fields(); + // layer config getters bool screen_overlay_enabled() const { return m_layerconfig.screen_overlay_enabled(); } bool zoom_to_screen() const { return m_layerconfig.zoom_to_screen(); } + u32 visibility_mask() const { return m_views[m_curview].second; } // layer config setters + void set_visibility_toggle(unsigned index, bool enable); void set_screen_overlay_enabled(bool enable) { m_layerconfig.set_screen_overlay_enabled(enable); update_layer_config(); } void set_zoom_to_screen(bool zoom) { m_layerconfig.set_zoom_to_screen(zoom); update_layer_config(); } // view configuration helper - int configured_view(const char *viewname, int targetindex, int numtargets); + unsigned configured_view(const char *viewname, int targetindex, int numtargets); // view information - const char *view_name(int viewindex); - const render_screen_list &view_screens(int viewindex); + char const *view_name(unsigned index); // bounds computations void compute_visible_area(s32 target_width, s32 target_height, float target_pixel_aspect, int target_orientation, s32 &visible_width, s32 &visible_height); @@ -999,43 +560,50 @@ public: // hit testing bool map_point_container(s32 target_x, s32 target_y, render_container &container, float &container_x, float &container_y); - bool map_point_input(s32 target_x, s32 target_y, ioport_port *&input_port, ioport_value &input_mask, float &input_x, float &input_y); // reference tracking void invalidate_all(void *refptr); - // debug containers - render_container *debug_alloc(); - void debug_free(render_container &container); - void debug_append(render_container &container); - // resolve tag lookups void resolve_tags(); private: + // constants + static inline constexpr int NUM_PRIMLISTS = 3; + static inline constexpr int MAX_CLEAR_EXTENTS = 1000; + + using view_mask_pair = std::pair<layout_view &, u32>; + using view_mask_vector = std::vector<view_mask_pair>; + // private classes declared in render.cpp struct object_transform; + struct pointer_info; + struct hit_test; + + using pointer_info_vector = std::vector<pointer_info>; + using hit_test_vector = std::vector<hit_test>; // internal helpers enum constructor_impl_t { CONSTRUCTOR_IMPL }; - template <typename T> render_target(render_manager &manager, T&& layout, u32 flags, constructor_impl_t); + template <typename T> render_target(render_manager &manager, render_container *ui, T&& layout, u32 flags, constructor_impl_t); void update_layer_config(); void load_layout_files(const internal_layout *layoutfile, bool singlefile); void load_layout_files(util::xml::data_node const &rootnode, bool singlefile); void load_additional_layout_files(const char *basename, bool have_artwork); bool load_layout_file(const char *dirname, const char *filename); bool load_layout_file(const char *dirname, const internal_layout &layout_data, device_t *device = nullptr); - bool load_layout_file(device_t &device, const char *dirname, util::xml::data_node const &rootnode); + bool load_layout_file(device_t &device, util::xml::data_node const &rootnode, const char *searchpath, const char *dirname); void add_container_primitives(render_primitive_list &list, const object_transform &root_xform, const object_transform &xform, render_container &container, int blendmode); - void add_element_primitives(render_primitive_list &list, const object_transform &xform, layout_element &element, int state, int blendmode); - bool map_point_internal(s32 target_x, s32 target_y, render_container *container, float &mapped_x, float &mapped_y, ioport_port *&mapped_input_port, ioport_value &mapped_input_mask); + void add_element_primitives(render_primitive_list &list, const object_transform &xform, layout_view_item &item); + std::pair<float, float> map_point_internal(s32 target_x, s32 target_y); + std::pair<float, float> map_point_layout(s32 target_x, s32 target_y); // config callbacks - void config_load(util::xml::data_node const &targetnode); + void config_load(util::xml::data_node const *targetnode); bool config_save(util::xml::data_node &targetnode); // view lookups - layout_view *view_by_index(int index); + layout_view *view_by_index(unsigned index); int view_index(layout_view &view) const; // optimized clearing @@ -1044,15 +612,13 @@ private: void add_clear_extents(render_primitive_list &list); void add_clear_and_optimize_primitive_list(render_primitive_list &list); - // constants - static constexpr int NUM_PRIMLISTS = 3; - static constexpr int MAX_CLEAR_EXTENTS = 1000; - // internal state render_target * m_next; // link to next target render_manager & m_manager; // reference to our owning manager - layout_view * m_curview; // current view + render_container *const m_ui_container; // container for drawing UI elements std::list<layout_file> m_filelist; // list of layout files + view_mask_vector m_views; // views we consider + unsigned m_curview; // current view index u32 m_flags; // creation flags render_primitive_list m_primlist[NUM_PRIMLISTS]; // list of primitives int m_listindex; // index of next primlist to use @@ -1068,18 +634,18 @@ private: float m_max_refresh; // maximum refresh rate, 0 or if none int m_orientation; // orientation render_layer_config m_layerconfig; // layer configuration + pointer_info_vector m_pointers; // state of pointers over this target + hit_test_vector m_clickable_items; // for tracking clicked elements layout_view * m_base_view; // the view at the time of first frame int m_base_orientation; // the orientation at the time of first frame render_layer_config m_base_layerconfig; // the layer configuration at the time of first frame int m_maxtexwidth; // maximum width of a texture int m_maxtexheight; // maximum height of a texture - simple_list<render_container> m_debug_containers; // list of debug containers s32 m_clear_extent_count; // number of clear extents s32 m_clear_extents[MAX_CLEAR_EXTENTS]; // array of clear extents bool m_transform_container; // determines whether the screen container is transformed by the core renderer, // otherwise the respective render API will handle the transformation (scale, offset) - - static render_screen_list s_empty_screen_list; + bool m_external_artwork; // external artwork was loaded (driver file or override) }; @@ -1107,7 +673,7 @@ public: render_target *target_alloc(util::xml::data_node const &layout, u32 flags = 0); void target_free(render_target *target); const simple_list<render_target> &targets() const { return m_targetlist; } - render_target *first_target() const { return m_targetlist.first(); } + render_target *first_target() { return m_targetlist.first(); } render_target *target_by_index(int index) const; // UI targets @@ -1116,15 +682,14 @@ public: float ui_aspect(render_container *rc = nullptr); // UI containers - render_container &ui_container() const { assert(m_ui_container != nullptr); return *m_ui_container; } + render_container &ui_container() const { assert(ui_target().ui_container()); return *ui_target().ui_container(); } // textures render_texture *texture_alloc(texture_scaler_func scaler = nullptr, void *param = nullptr); void texture_free(render_texture *texture); // fonts - render_font *font_alloc(const char *filename = nullptr); - void font_free(render_font *font); + std::unique_ptr<render_font> font_alloc(const char *filename = nullptr); // reference tracking void invalidate_all(void *refptr); @@ -1133,29 +698,25 @@ public: void resolve_tags(); private: - // containers - render_container *container_alloc(screen_device *screen = nullptr); - void container_free(render_container *container); - // config callbacks - void config_load(config_type cfg_type, util::xml::data_node const *parentnode); + void config_load(config_type cfg_type, config_level cfg_lvl, util::xml::data_node const *parentnode); void config_save(config_type cfg_type, util::xml::data_node *parentnode); // internal state - running_machine & m_machine; // reference back to the machine + running_machine & m_machine; // reference back to the machine // array of live targets - simple_list<render_target> m_targetlist; // list of targets - render_target * m_ui_target; // current UI target + simple_list<render_target> m_targetlist; // list of targets + render_target * m_ui_target; // current UI target // texture lists - u32 m_live_textures; // number of live textures - u64 m_texture_id; // rolling texture ID counter - fixed_allocator<render_texture> m_texture_allocator;// texture allocator + u32 m_live_textures; // number of live textures + u64 m_texture_id; // rolling texture ID counter + fixed_allocator<render_texture> m_texture_allocator; // texture allocator - // containers for the UI and for screens - render_container * m_ui_container; // UI container - simple_list<render_container> m_screen_container_list; // list of containers for the screen + // containers for UI elements and for screens + std::list<render_container> m_ui_containers; // containers for drawing UI elements + std::list<render_container> m_screen_container_list; // list of containers for the screen }; #endif // MAME_EMU_RENDER_H diff --git a/src/emu/rendersw.hxx b/src/emu/rendersw.hxx index b1a032258d1..d4fde37566e 100644 --- a/src/emu/rendersw.hxx +++ b/src/emu/rendersw.hxx @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles /*************************************************************************** - rendersw.inc + rendersw.hxx Software-only rasterization system. @@ -14,8 +14,9 @@ #include "video/rgbutil.h" #include "render.h" +#include <array> -template<typename _PixelType, int _SrcShiftR, int _SrcShiftG, int _SrcShiftB, int _DstShiftR, int _DstShiftG, int _DstShiftB, bool _NoDestRead = false, bool _BilinearFilter = false> +template <typename PixelType, int SrcShiftR, int SrcShiftG, int SrcShiftB, int DstShiftR, int DstShiftG, int DstShiftB, bool NoDestRead = false, bool BilinearFilter = false> class software_renderer { private: @@ -29,29 +30,34 @@ private: }; // internal helpers - static inline bool is_opaque(float alpha) { return (alpha >= (_NoDestRead ? 0.5f : 1.0f)); } - static inline bool is_transparent(float alpha) { return (alpha < (_NoDestRead ? 0.5f : 0.0001f)); } - static inline rgb_t apply_intensity(int intensity, rgb_t color) { return color.scale8(intensity); } - static inline float round_nearest(float f) { return floor(f + 0.5f); } + template <int... Values> + static auto make_cosine_table(std::integer_sequence<int, Values...>) + { + return std::array<u32, sizeof...(Values)>{ u32((1.0 / cos(atan(double(Values) / double(sizeof...(Values) - 1)))) * 0x10000000 + 0.5)... }; + } + static constexpr bool is_opaque(float alpha) { return (alpha >= (NoDestRead ? 0.5f : 1.0f)); } + static constexpr bool is_transparent(float alpha) { return (alpha < (NoDestRead ? 0.5f : 0.0001f)); } + static rgb_t apply_intensity(int intensity, rgb_t color) { return color.scale8(intensity); } + static float round_nearest(float f) { return floor(f + 0.5f); } // destination pixels are written based on the values of the template parameters - static inline _PixelType dest_assemble_rgb(u32 r, u32 g, u32 b) { return (r << _DstShiftR) | (g << _DstShiftG) | (b << _DstShiftB); } - static inline _PixelType dest_rgb_to_pixel(u32 r, u32 g, u32 b) { return dest_assemble_rgb(r >> _SrcShiftR, g >> _SrcShiftG, b >> _SrcShiftB); } + static constexpr PixelType dest_assemble_rgb(u32 r, u32 g, u32 b) { return (r << DstShiftR) | (g << DstShiftG) | (b << DstShiftB); } + static constexpr PixelType dest_rgb_to_pixel(u32 r, u32 g, u32 b) { return dest_assemble_rgb(r >> SrcShiftR, g >> SrcShiftG, b >> SrcShiftB); } // source 32-bit pixels are in MAME standardized format - static inline u32 source32_r(u32 pixel) { return (pixel >> (16 + _SrcShiftR)) & (0xff >> _SrcShiftR); } - static inline u32 source32_g(u32 pixel) { return (pixel >> ( 8 + _SrcShiftG)) & (0xff >> _SrcShiftG); } - static inline u32 source32_b(u32 pixel) { return (pixel >> ( 0 + _SrcShiftB)) & (0xff >> _SrcShiftB); } + static constexpr u32 source32_r(u32 pixel) { return (pixel >> (16 + SrcShiftR)) & (0xff >> SrcShiftR); } + static constexpr u32 source32_g(u32 pixel) { return (pixel >> ( 8 + SrcShiftG)) & (0xff >> SrcShiftG); } + static constexpr u32 source32_b(u32 pixel) { return (pixel >> ( 0 + SrcShiftB)) & (0xff >> SrcShiftB); } // destination pixel masks are based on the template parameters as well - static inline u32 dest_r(_PixelType pixel) { return (pixel >> _DstShiftR) & (0xff >> _SrcShiftR); } - static inline u32 dest_g(_PixelType pixel) { return (pixel >> _DstShiftG) & (0xff >> _SrcShiftG); } - static inline u32 dest_b(_PixelType pixel) { return (pixel >> _DstShiftB) & (0xff >> _SrcShiftB); } + static constexpr u32 dest_r(PixelType pixel) { return (pixel >> DstShiftR) & (0xff >> SrcShiftR); } + static constexpr u32 dest_g(PixelType pixel) { return (pixel >> DstShiftG) & (0xff >> SrcShiftG); } + static constexpr u32 dest_b(PixelType pixel) { return (pixel >> DstShiftB) & (0xff >> SrcShiftB); } // generic conversion with special optimization for destinations in the standard format - static inline _PixelType source32_to_dest(u32 pixel) + static constexpr PixelType source32_to_dest(u32 pixel) { - if (_SrcShiftR == 0 && _SrcShiftG == 0 && _SrcShiftB == 0 && _DstShiftR == 16 && _DstShiftG == 8 && _DstShiftB == 0) + if (SrcShiftR == 0 && SrcShiftG == 0 && SrcShiftB == 0 && DstShiftR == 16 && DstShiftG == 8 && DstShiftB == 0) return pixel; else return dest_assemble_rgb(source32_r(pixel), source32_g(pixel), source32_b(pixel)); @@ -67,12 +73,12 @@ private: // The document also contains the constants below as floats. //------------------------------------------------- - static inline u32 clamp16_shift8(u32 x) + static constexpr u32 clamp16_shift8(u32 x) { - return ((s32(x) < 0) ? 0 : (x > 65535 ? 255: x >> 8)); + return (s32(x) < 0) ? 0 : (x > 65535) ? 255 : (x >> 8); } - static inline u32 ycc_to_rgb(u32 ycc) + static constexpr u32 ycc_to_rgb(u32 ycc) { // original equations: // @@ -130,8 +136,8 @@ private: static inline u32 get_texel_palette16(const render_texinfo &texture, s32 curu, s32 curv) { - const rgb_t *palbase = texture.palette; - if (_BilinearFilter) + rgb_t const *const palbase = texture.palette; + if constexpr (BilinearFilter) { s32 u0 = curu >> 16; s32 u1 = 1; @@ -142,7 +148,7 @@ private: if (v0 < 0) v0 = v1 = 0; else if (v0 + 1 >= texture.height) v0 = texture.height - 1, v1 = 0; - const u16 *texbase = reinterpret_cast<const u16 *>(texture.base); + u16 const *texbase = reinterpret_cast<u16 const *>(texture.base); texbase += v0 * texture.rowpixels + u0; u32 pix00 = palbase[texbase[0]]; @@ -153,7 +159,10 @@ private: } else { - const u16 *texbase = reinterpret_cast<const u16 *>(texture.base) + (curv >> 16) * texture.rowpixels + (curu >> 16); + s32 u = std::clamp<s32>(curu >> 16, 0, texture.width - 1); + s32 v = std::clamp<s32>(curv >> 16, 0, texture.height - 1); + + u16 const *const texbase = reinterpret_cast<u16 const *>(texture.base) + v * texture.rowpixels + u; return palbase[texbase[0]]; } } @@ -166,8 +175,8 @@ private: static inline u32 get_texel_palette16a(const render_texinfo &texture, s32 curu, s32 curv) { - const rgb_t *palbase = texture.palette; - if (_BilinearFilter) + rgb_t const *const palbase = texture.palette; + if constexpr (BilinearFilter) { s32 u0 = curu >> 16; s32 u1 = 1; @@ -178,14 +187,17 @@ private: if (v0 < 0) v0 = v1 = 0; else if (v0 + 1 >= texture.height) v0 = texture.height - 1, v1 = 0; - const u16 *texbase = reinterpret_cast<const u16 *>(texture.base); + u16 const *texbase = reinterpret_cast<u16 const *>(texture.base); texbase += v0 * texture.rowpixels + u0; return rgbaint_t::bilinear_filter(palbase[texbase[0]], palbase[texbase[u1]], palbase[texbase[v1]], palbase[texbase[u1 + v1]], curu >> 8, curv >> 8); } else { - const u16 *texbase = reinterpret_cast<const u16 *>(texture.base) + (curv >> 16) * texture.rowpixels + (curu >> 16); + s32 u = std::clamp<s32>(curu >> 16, 0, texture.width - 1); + s32 v = std::clamp<s32>(curv >> 16, 0, texture.height - 1); + + u16 const *const texbase = reinterpret_cast<u16 const *>(texture.base) + v * texture.rowpixels + u; return palbase[texbase[0]]; } } @@ -198,7 +210,7 @@ private: static inline u32 get_texel_yuy16(const render_texinfo &texture, s32 curu, s32 curv) { - if (_BilinearFilter) + if constexpr (BilinearFilter) { s32 u0 = curu >> 16; s32 u1 = 1; @@ -247,8 +259,11 @@ private: } else { - const u16 *texbase = reinterpret_cast<const u16 *>(texture.base) + (curv >> 16) * texture.rowpixels + (curu >> 17) * 2; - return (texbase[(curu >> 16) & 1] >> 8) | ((texbase[0] & 0xff) << 8) | ((texbase[1] & 0xff) << 16); + s32 u = std::clamp<s32>(curu >> 16, 0, texture.width - 1); + s32 v = std::clamp<s32>(curv >> 16, 0, texture.height - 1); + + const u16 *texbase = reinterpret_cast<const u16 *>(texture.base) + v * texture.rowpixels + (u >> 1) * 2; + return (texbase[u & 1] >> 8) | ((texbase[0] & 0xff) << 8) | ((texbase[1] & 0xff) << 16); } } @@ -258,28 +273,67 @@ private: // RGB source //------------------------------------------------- + template <bool Wrap> static inline u32 get_texel_rgb32(const render_texinfo &texture, s32 curu, s32 curv) { - if (_BilinearFilter) + if constexpr (BilinearFilter) { - s32 u0 = curu >> 16; - s32 u1 = 1; - if (u0 < 0) u0 = u1 = 0; - else if (u0 + 1 >= texture.width) u0 = texture.width - 1, u1 = 0; - s32 v0 = curv >> 16; - s32 v1 = texture.rowpixels; - if (v0 < 0) v0 = v1 = 0; - else if (v0 + 1 >= texture.height) v0 = texture.height - 1, v1 = 0; - - const u32 *texbase = reinterpret_cast<const u32 *>(texture.base); - texbase += v0 * texture.rowpixels + u0; + s32 u0, u1, v0, v1; + if constexpr (Wrap) + { + u0 = (curu >> 16) % texture.width; + if (0 > u0) + u0 += texture.width; + u1 = (u0 + 1) % texture.width; + + v0 = (curv >> 16) % texture.height; + if (0 > v0) + v0 += texture.height; + v1 = (v0 + 1) % texture.height; + } + else + { + u0 = curu >> 16; + if (u0 < 0) + u0 = u1 = 0; + else if (texture.width <= (u0 + 1)) + u0 = u1 = texture.width - 1; + else + u1 = u0 + 1; - return rgbaint_t::bilinear_filter(texbase[0], texbase[u1], texbase[v1], texbase[u1 + v1], curu >> 8, curv >> 8); + v0 = curv >> 16; + if (v0 < 0) + v0 = v1 = 0; + else if (texture.height <= (v0 + 1)) + v0 = v1 = texture.height - 1; + else + v1 = v0 + 1; + } + u32 const *const texbase = reinterpret_cast<u32 const *>(texture.base); + u32 const *const row0base = texbase + (v0 * texture.rowpixels); + u32 const *const row1base = texbase + (v1 * texture.rowpixels); + return rgbaint_t::bilinear_filter(row0base[u0], row0base[u1], row1base[u0], row1base[u1], curu >> 8, curv >> 8); } else { - const u32 *texbase = reinterpret_cast<const u32 *>(texture.base) + (curv >> 16) * texture.rowpixels + (curu >> 16); - return texbase[0]; + s32 u, v; + if constexpr (Wrap) + { + u = (curu >> 16) % texture.width; + if (0 > u) + u += texture.width; + + v = (curv >> 16) % texture.height; + if (0 > v) + v += texture.height; + } + else + { + u = std::clamp<s32>(curu >> 16, 0, texture.width - 1); + v = std::clamp<s32>(curv >> 16, 0, texture.height - 1); + } + u32 const *const rowbase = reinterpret_cast<u32 const *>(texture.base) + (v * texture.rowpixels); + return rowbase[u]; } } @@ -289,28 +343,67 @@ private: // ARGB source //------------------------------------------------- - static inline u32 get_texel_argb32(const render_texinfo &texture, s32 curu, s32 curv) + template <bool Wrap> + static inline u32 get_texel_argb32(render_texinfo const &texture, s32 curu, s32 curv) { - if (_BilinearFilter) + if constexpr (BilinearFilter) { - s32 u0 = curu >> 16; - s32 u1 = 1; - if (u0 < 0) u0 = u1 = 0; - else if (u0 + 1 >= texture.width) u0 = texture.width - 1, u1 = 0; - s32 v0 = curv >> 16; - s32 v1 = texture.rowpixels; - if (v0 < 0) v0 = v1 = 0; - else if (v0 + 1 >= texture.height) v0 = texture.height - 1, v1 = 0; - - const u32 *texbase = reinterpret_cast<const u32 *>(texture.base); - texbase += v0 * texture.rowpixels + u0; + s32 u0, u1, v0, v1; + if constexpr (Wrap) + { + u0 = (curu >> 16) % texture.width; + if (0 > u0) + u0 += texture.width; + u1 = (u0 + 1) % texture.width; + + v0 = (curv >> 16) % texture.height; + if (0 > v0) + v0 += texture.height; + v1 = (v0 + 1) % texture.height; + } + else + { + u0 = curu >> 16; + if (u0 < 0) + u0 = u1 = 0; + else if (texture.width <= (u0 + 1)) + u0 = u1 = texture.width - 1; + else + u1 = u0 + 1; - return rgbaint_t::bilinear_filter(texbase[0], texbase[u1], texbase[v1], texbase[u1 + v1], curu >> 8, curv >> 8); + v0 = curv >> 16; + if (v0 < 0) + v0 = v1 = 0; + else if (texture.height <= (v0 + 1)) + v0 = v1 = texture.height - 1; + else + v1 = v0 + 1; + } + u32 const *const texbase = reinterpret_cast<u32 const *>(texture.base); + u32 const *const row0base = texbase + (v0 * texture.rowpixels); + u32 const *const row1base = texbase + (v1 * texture.rowpixels); + return rgbaint_t::bilinear_filter(row0base[u0], row0base[u1], row1base[u0], row1base[u1], curu >> 8, curv >> 8); } else { - const u32 *texbase = reinterpret_cast<const u32 *>(texture.base) + (curv >> 16) * texture.rowpixels + (curu >> 16); - return texbase[0]; + s32 u, v; + if constexpr (Wrap) + { + u = (curu >> 16) % texture.width; + if (0 > u) + u += texture.width; + + v = (curv >> 16) % texture.height; + if (0 > v) + v += texture.height; + } + else + { + u = std::clamp<s32>(curu >> 16, 0, texture.width - 1); + v = std::clamp<s32>(curv >> 16, 0, texture.height - 1); + } + u32 const *const rowbase = reinterpret_cast<u32 const *>(texture.base) + (v * texture.rowpixels); + return rowbase[u]; } } @@ -319,16 +412,16 @@ private: // draw_aa_pixel - draw an antialiased pixel //------------------------------------------------- - static inline void draw_aa_pixel(_PixelType *dstdata, u32 pitch, int x, int y, u32 col) + static inline void draw_aa_pixel(PixelType *dstdata, u32 pitch, int x, int y, u32 col) { - _PixelType *dest = dstdata + y * pitch + x; - u32 dpix = _NoDestRead ? 0 : *dest; + PixelType *dest = dstdata + y * pitch + x; + u32 dpix = NoDestRead ? 0 : *dest; u32 dr = source32_r(col) + dest_r(dpix); u32 dg = source32_g(col) + dest_g(dpix); u32 db = source32_b(col) + dest_b(dpix); - dr = (dr | -(dr >> (8 - _SrcShiftR))) & (0xff >> _SrcShiftR); - dg = (dg | -(dg >> (8 - _SrcShiftG))) & (0xff >> _SrcShiftG); - db = (db | -(db >> (8 - _SrcShiftB))) & (0xff >> _SrcShiftB); + dr = (dr | -(dr >> (8 - SrcShiftR))) & (0xff >> SrcShiftR); + dg = (dg | -(dg >> (8 - SrcShiftG))) & (0xff >> SrcShiftG); + db = (db | -(db >> (8 - SrcShiftB))) & (0xff >> SrcShiftB); *dest = dest_assemble_rgb(dr, dg, db); } @@ -337,10 +430,10 @@ private: // draw_line - draw a line or point //------------------------------------------------- - static void draw_line(const render_primitive &prim, _PixelType *dstdata, s32 width, s32 height, u32 pitch) + static void draw_line(render_primitive const &prim, PixelType *dstdata, s32 width, s32 height, u32 pitch) { - // internal tables - static u32 s_cosine_table[2049]; + // internal cosine table generated at compile time + static auto const s_cosine_table = make_cosine_table(std::make_integer_sequence<int, 2049>()); // compute the start/end coordinates int x1 = int(prim.bounds.x0 * 65536.0f); @@ -353,11 +446,6 @@ private: if (PRIMFLAG_GET_ANTIALIAS(prim.flags)) { - // build up the cosine table if we haven't yet - if (s_cosine_table[0] == 0) - for (int entry = 0; entry <= 2048; entry++) - s_cosine_table[entry] = int(double(1.0 / cos(atan(double(entry) / 2048.0))) * 0x10000000 + 0.5); - int beam = prim.width * 65536.0f; if (beam < 0x00010000) beam = 0x00010000; @@ -386,9 +474,9 @@ private: draw_aa_pixel(dstdata, pitch, x1, dy, apply_intensity(0xff & (~y1 >> 8), col)); dy++; dx -= 0x10000 - (0xffff & y1); // take off amount plotted - u8 a1 = (dx >> 8) & 0xff; // calc remainder pixel - dx >>= 16; // adjust to pixel (solid) count - while (dx--) // plot rest of pixels + u8 a1 = (dx >> 8) & 0xff; // calc remainder pixel + dx >>= 16; // adjust to pixel (solid) count + while (dx--) // plot rest of pixels { if (dy >= 0 && dy < height) draw_aa_pixel(dstdata, pitch, x1, dy, col); @@ -422,9 +510,9 @@ private: draw_aa_pixel(dstdata, pitch, dx, y1, apply_intensity(0xff & (~x1 >> 8), col)); dx++; dy -= 0x10000 - (0xffff & x1); // take off amount plotted - u8 a1 = (dy >> 8) & 0xff; // remainder pixel - dy >>= 16; // adjust to pixel (solid) count - while (dy--) // plot rest of pixels + u8 a1 = (dy >> 8) & 0xff; // remainder pixel + dy >>= 16; // adjust to pixel (solid) count + while (dy--) // plot rest of pixels { if (dx >= 0 && dx < width) draw_aa_pixel(dstdata, pitch, dx, y1, col); @@ -489,7 +577,6 @@ private: } - //************************************************************************** // RECT RASTERIZERS //************************************************************************** @@ -498,77 +585,58 @@ private: // draw_rect - draw a solid rectangle //------------------------------------------------- - static void draw_rect(const render_primitive &prim, _PixelType *dstdata, s32 width, s32 height, u32 pitch) + static void draw_rect(render_primitive const &prim, PixelType *dstdata, s32 width, s32 height, u32 pitch) { - render_bounds fpos = prim.bounds; + render_bounds const fpos = prim.bounds; assert(fpos.x0 <= fpos.x1); assert(fpos.y0 <= fpos.y1); - // clamp to integers - s32 startx = round_nearest(fpos.x0); - s32 starty = round_nearest(fpos.y0); - s32 endx = round_nearest(fpos.x1); - s32 endy = round_nearest(fpos.y1); - - // ensure we fit - if (startx < 0) startx = 0; - if (startx >= width) startx = width; - if (endx < 0) endx = 0; - if (endx >= width) endx = width; - if (starty < 0) starty = 0; - if (starty >= height) starty = height; - if (endy < 0) endy = 0; - if (endy >= height) endy = height; + // clamp to integers and ensure we fit + s32 const startx = std::clamp<s32>(round_nearest(fpos.x0), 0, width); + s32 const starty = std::clamp<s32>(round_nearest(fpos.y0), 0, height); + s32 const endx = std::clamp<s32>(round_nearest(fpos.x1), 0, width); + s32 const endy = std::clamp<s32>(round_nearest(fpos.y1), 0, height); // bail if nothing left - if (fpos.x0 > fpos.x1 || fpos.y0 > fpos.y1) + if ((startx > endx) || (starty > endy)) return; // only support alpha and "none" blendmodes assert(PRIMFLAG_GET_BLENDMODE(prim.flags) == BLENDMODE_NONE || PRIMFLAG_GET_BLENDMODE(prim.flags) == BLENDMODE_ALPHA); - // fast case: no alpha - if (PRIMFLAG_GET_BLENDMODE(prim.flags) == BLENDMODE_NONE || is_opaque(prim.color.a)) + if ((PRIMFLAG_GET_BLENDMODE(prim.flags) == BLENDMODE_NONE) || is_opaque(prim.color.a)) { - u32 r = u32(256.0f * prim.color.r); - u32 g = u32(256.0f * prim.color.g); - u32 b = u32(256.0f * prim.color.b); - u32 pix; + // fast case: no alpha // clamp R,G,B to 0-256 range - if (r > 0xff) { if (s32(r) < 0) r = 0; else r = 0xff; } - if (g > 0xff) { if (s32(g) < 0) g = 0; else g = 0xff; } - if (b > 0xff) { if (s32(b) < 0) b = 0; else b = 0xff; } - pix = dest_rgb_to_pixel(r, g, b); + u32 const r = u32(std::clamp(256.0f * prim.color.r, 0.0f, 255.0f)); + u32 const g = u32(std::clamp(256.0f * prim.color.g, 0.0f, 255.0f)); + u32 const b = u32(std::clamp(256.0f * prim.color.b, 0.0f, 255.0f)); + u32 const pix = dest_rgb_to_pixel(r, g, b); // loop over rows for (s32 y = starty; y < endy; y++) { - _PixelType *dest = dstdata + y * pitch + startx; + PixelType *dest = dstdata + y * pitch + startx; // loop over cols for (s32 x = startx; x < endx; x++) *dest++ = pix; } } - - // alpha and/or coloring case else if (!is_transparent(prim.color.a)) { - u32 rmask = dest_rgb_to_pixel(0xff,0x00,0x00); - u32 gmask = dest_rgb_to_pixel(0x00,0xff,0x00); - u32 bmask = dest_rgb_to_pixel(0x00,0x00,0xff); - u32 r = u32(256.0f * prim.color.r * prim.color.a); - u32 g = u32(256.0f * prim.color.g * prim.color.a); - u32 b = u32(256.0f * prim.color.b * prim.color.a); - u32 inva = u32(256.0f * (1.0f - prim.color.a)); + // alpha and/or coloring case + u32 const rmask = dest_rgb_to_pixel(0xff,0x00,0x00); + u32 const gmask = dest_rgb_to_pixel(0x00,0xff,0x00); + u32 const bmask = dest_rgb_to_pixel(0x00,0x00,0xff); // clamp R,G,B and inverse A to 0-256 range - if (r > 0xff) { if (s32(r) < 0) r = 0; else r = 0xff; } - if (g > 0xff) { if (s32(g) < 0) g = 0; else g = 0xff; } - if (b > 0xff) { if (s32(b) < 0) b = 0; else b = 0xff; } - if (inva > 0x100) { if (s32(inva) < 0) inva = 0; else inva = 0x100; } + u32 r = u32(std::clamp(256.0f * prim.color.r * prim.color.a, 0.0f, 255.0f)); + u32 g = u32(std::clamp(256.0f * prim.color.g * prim.color.a, 0.0f, 255.0f)); + u32 b = u32(std::clamp(256.0f * prim.color.b * prim.color.a, 0.0f, 255.0f)); + u32 const inva = u32(std::clamp(256.0f * (1.0f - prim.color.a), 0.0f, 256.0f)); // pre-shift the RGBA pieces r = dest_rgb_to_pixel(r, 0, 0) << 8; @@ -578,12 +646,12 @@ private: // loop over rows for (s32 y = starty; y < endy; y++) { - _PixelType *dest = dstdata + y * pitch + startx; + PixelType *dest = dstdata + y * pitch + startx; // loop over cols for (s32 x = startx; x < endx; x++) { - u32 dpix = _NoDestRead ? 0 : *dest; + u32 dpix = NoDestRead ? 0 : *dest; u32 dr = (r + ((dpix & rmask) * inva)) & (rmask << 8); u32 dg = (g + ((dpix & gmask) * inva)) & (gmask << 8); u32 db = (b + ((dpix & bmask) * inva)) & (bmask << 8); @@ -595,111 +663,97 @@ private: //************************************************************************** - // 16-BIT PALETTE RASTERIZERS + // 16-BIT RASTERIZERS //************************************************************************** //------------------------------------------------- - // draw_quad_palette16_none - perform - // rasterization of a 16bpp palettized texture + // draw_quad_convert_none - perform + // rasterization of a texture after conversion //------------------------------------------------- - static void draw_quad_palette16_none(const render_primitive &prim, _PixelType *dstdata, u32 pitch, quad_setup_data &setup) + template <typename T> + static void draw_quad_convert_none(render_primitive const &prim, PixelType *dstdata, u32 pitch, quad_setup_data const &setup, T &&gettexel) { - s32 dudx = setup.dudx; - s32 dvdx = setup.dvdx; - s32 endx = setup.endx; - - // ensure all parameters are valid - assert(prim.texture.palette != nullptr); - - // fast case: no coloring, no alpha if (prim.color.r >= 1.0f && prim.color.g >= 1.0f && prim.color.b >= 1.0f && is_opaque(prim.color.a)) { + // fast case: no coloring, no alpha + // loop over rows for (s32 y = setup.starty; y < setup.endy; y++) { - _PixelType *dest = dstdata + y * pitch + setup.startx; + PixelType *dest = dstdata + y * pitch + setup.startx; s32 curu = setup.startu + (y - setup.starty) * setup.dudy; s32 curv = setup.startv + (y - setup.starty) * setup.dvdy; // loop over cols - for (s32 x = setup.startx; x < endx; x++) + for (s32 x = setup.startx; x < setup.endx; x++) { - u32 pix = get_texel_palette16(prim.texture, curu, curv); + u32 const pix = gettexel(prim, curu, curv); *dest++ = source32_to_dest(pix); - curu += dudx; - curv += dvdx; + curu += setup.dudx; + curv += setup.dvdx; } } } - - // coloring-only case else if (is_opaque(prim.color.a)) { - u32 sr = u32(256.0f * prim.color.r); - u32 sg = u32(256.0f * prim.color.g); - u32 sb = u32(256.0f * prim.color.b); + // coloring-only case // clamp R,G,B to 0-256 range - if (sr > 0x100) { if (s32(sr) < 0) sr = 0; else sr = 0x100; } - if (sg > 0x100) { if (s32(sg) < 0) sg = 0; else sg = 0x100; } - if (sb > 0x100) { if (s32(sb) < 0) sb = 0; else sb = 0x100; } + u32 const sr = u32(std::clamp(256.0f * prim.color.r, 0.0f, 256.0f)); + u32 const sg = u32(std::clamp(256.0f * prim.color.g, 0.0f, 256.0f)); + u32 const sb = u32(std::clamp(256.0f * prim.color.b, 0.0f, 256.0f)); // loop over rows for (s32 y = setup.starty; y < setup.endy; y++) { - _PixelType *dest = dstdata + y * pitch + setup.startx; + PixelType *dest = dstdata + y * pitch + setup.startx; s32 curu = setup.startu + (y - setup.starty) * setup.dudy; s32 curv = setup.startv + (y - setup.starty) * setup.dvdy; // loop over cols - for (s32 x = setup.startx; x < endx; x++) + for (s32 x = setup.startx; x < setup.endx; x++) { - u32 pix = get_texel_palette16(prim.texture, curu, curv); - u32 r = (source32_r(pix) * sr) >> 8; - u32 g = (source32_g(pix) * sg) >> 8; - u32 b = (source32_b(pix) * sb) >> 8; + u32 const pix = gettexel(prim, curu, curv); + u32 const r = (source32_r(pix) * sr) >> 8; + u32 const g = (source32_g(pix) * sg) >> 8; + u32 const b = (source32_b(pix) * sb) >> 8; *dest++ = dest_assemble_rgb(r, g, b); - curu += dudx; - curv += dvdx; + curu += setup.dudx; + curv += setup.dvdx; } } } - - // alpha and/or coloring case else if (!is_transparent(prim.color.a)) { - u32 sr = u32(256.0f * prim.color.r * prim.color.a); - u32 sg = u32(256.0f * prim.color.g * prim.color.a); - u32 sb = u32(256.0f * prim.color.b * prim.color.a); - u32 invsa = u32(256.0f * (1.0f - prim.color.a)); + // alpha and/or coloring case // clamp R,G,B and inverse A to 0-256 range - if (sr > 0x100) { if (s32(sr) < 0) sr = 0; else sr = 0x100; } - if (sg > 0x100) { if (s32(sg) < 0) sg = 0; else sg = 0x100; } - if (sb > 0x100) { if (s32(sb) < 0) sb = 0; else sb = 0x100; } - if (invsa > 0x100) { if (s32(invsa) < 0) invsa = 0; else invsa = 0x100; } + u32 const sr = u32(std::clamp(256.0f * prim.color.r * prim.color.a, 0.0f, 256.0f)); + u32 const sg = u32(std::clamp(256.0f * prim.color.g * prim.color.a, 0.0f, 256.0f)); + u32 const sb = u32(std::clamp(256.0f * prim.color.b * prim.color.a, 0.0f, 256.0f)); + u32 const invsa = u32(std::clamp(256.0f * (1.0f - prim.color.a), 0.0f, 256.0f)); // loop over rows for (s32 y = setup.starty; y < setup.endy; y++) { - _PixelType *dest = dstdata + y * pitch + setup.startx; + PixelType *dest = dstdata + y * pitch + setup.startx; s32 curu = setup.startu + (y - setup.starty) * setup.dudy; s32 curv = setup.startv + (y - setup.starty) * setup.dvdy; // loop over cols - for (s32 x = setup.startx; x < endx; x++) + for (s32 x = setup.startx; x < setup.endx; x++) { - u32 pix = get_texel_palette16(prim.texture, curu, curv); - u32 dpix = _NoDestRead ? 0 : *dest; - u32 r = (source32_r(pix) * sr + dest_r(dpix) * invsa) >> 8; - u32 g = (source32_g(pix) * sg + dest_g(dpix) * invsa) >> 8; - u32 b = (source32_b(pix) * sb + dest_b(dpix) * invsa) >> 8; + u32 const pix = gettexel(prim, curu, curv); + u32 const dpix = NoDestRead ? 0 : *dest; + u32 const r = (source32_r(pix) * sr + dest_r(dpix) * invsa) >> 8; + u32 const g = (source32_g(pix) * sg + dest_g(dpix) * invsa) >> 8; + u32 const b = (source32_b(pix) * sb + dest_b(dpix) * invsa) >> 8; *dest++ = dest_assemble_rgb(r, g, b); - curu += dudx; - curv += dvdx; + curu += setup.dudx; + curv += setup.dvdx; } } } @@ -707,282 +761,62 @@ private: //------------------------------------------------- - // draw_quad_palette16_add - perform - // rasterization of a 16bpp palettized texture + // draw_quad_convert_rop - perform rasterization + // by using RGB operation after conversion //------------------------------------------------- - static void draw_quad_palette16_add(const render_primitive &prim, _PixelType *dstdata, u32 pitch, quad_setup_data &setup) + template <typename T, typename U, typename V> + static void draw_quad_convert_rop(render_primitive const &prim, PixelType *dstdata, u32 pitch, quad_setup_data const &setup, T &&gettexel, U &¬int, V &&tinted) { - s32 dudx = setup.dudx; - s32 dvdx = setup.dvdx; - s32 endx = setup.endx; - // ensure all parameters are valid assert(prim.texture.palette != nullptr); - // fast case: no coloring, no alpha - if (prim.color.r >= 1.0f && prim.color.g >= 1.0f && prim.color.b >= 1.0f && is_opaque(prim.color.a)) - { - // loop over rows - for (s32 y = setup.starty; y < setup.endy; y++) - { - _PixelType *dest = dstdata + y * pitch + setup.startx; - s32 curu = setup.startu + (y - setup.starty) * setup.dudy; - s32 curv = setup.startv + (y - setup.starty) * setup.dvdy; - - // loop over cols - for (s32 x = setup.startx; x < endx; x++) - { - u32 pix = get_texel_palette16(prim.texture, curu, curv); - if ((pix & 0xffffff) != 0) - { - u32 dpix = _NoDestRead ? 0 : *dest; - u32 r = source32_r(pix) + dest_r(dpix); - u32 g = source32_g(pix) + dest_g(dpix); - u32 b = source32_b(pix) + dest_b(dpix); - r = (r | -(r >> (8 - _SrcShiftR))) & (0xff >> _SrcShiftR); - g = (g | -(g >> (8 - _SrcShiftG))) & (0xff >> _SrcShiftG); - b = (b | -(b >> (8 - _SrcShiftB))) & (0xff >> _SrcShiftB); - *dest = dest_assemble_rgb(r, g, b); - } - dest++; - curu += dudx; - curv += dvdx; - } - } - } - - // alpha and/or coloring case - else - { - u32 sr = u32(256.0f * prim.color.r * prim.color.a); - u32 sg = u32(256.0f * prim.color.g * prim.color.a); - u32 sb = u32(256.0f * prim.color.b * prim.color.a); - - // clamp R,G,B and inverse A to 0-256 range - if (sr > 0x100) { if (s32(sr) < 0) sr = 0; else sr = 0x100; } - if (sg > 0x100) { if (s32(sg) < 0) sg = 0; else sg = 0x100; } - if (sb > 0x100) { if (s32(sb) < 0) sb = 0; else sb = 0x100; } - - // loop over rows - for (s32 y = setup.starty; y < setup.endy; y++) - { - _PixelType *dest = dstdata + y * pitch + setup.startx; - s32 curu = setup.startu + (y - setup.starty) * setup.dudy; - s32 curv = setup.startv + (y - setup.starty) * setup.dvdy; - - // loop over cols - for (s32 x = setup.startx; x < endx; x++) - { - u32 pix = get_texel_palette16(prim.texture, curu, curv); - if ((pix & 0xffffff) != 0) - { - u32 dpix = _NoDestRead ? 0 : *dest; - u32 r = ((source32_r(pix) * sr) >> 8) + dest_r(dpix); - u32 g = ((source32_g(pix) * sg) >> 8) + dest_g(dpix); - u32 b = ((source32_b(pix) * sb) >> 8) + dest_b(dpix); - r = (r | -(r >> (8 - _SrcShiftR))) & (0xff >> _SrcShiftR); - g = (g | -(g >> (8 - _SrcShiftG))) & (0xff >> _SrcShiftG); - b = (b | -(b >> (8 - _SrcShiftB))) & (0xff >> _SrcShiftB); - *dest++ = dest_assemble_rgb(r, g, b); - curu += dudx; - curv += dvdx; - } - } - } - } - } - - - - //************************************************************************** - // 16-BIT YUY RASTERIZERS - //************************************************************************** - - //------------------------------------------------- - // draw_quad_yuy16_none - perform - // rasterization of a 16bpp YUY image - //------------------------------------------------- - - static void draw_quad_yuy16_none(const render_primitive &prim, _PixelType *dstdata, u32 pitch, quad_setup_data &setup) - { - s32 dudx = setup.dudx; - s32 dvdx = setup.dvdx; - s32 endx = setup.endx; - - // fast case: no coloring, no alpha if (prim.color.r >= 1.0f && prim.color.g >= 1.0f && prim.color.b >= 1.0f && is_opaque(prim.color.a)) { - // loop over rows - for (s32 y = setup.starty; y < setup.endy; y++) - { - _PixelType *dest = dstdata + y * pitch + setup.startx; - s32 curu = setup.startu + (y - setup.starty) * setup.dudy; - s32 curv = setup.startv + (y - setup.starty) * setup.dvdy; - - // loop over cols - for (s32 x = setup.startx; x < endx; x++) - { - u32 pix = ycc_to_rgb(get_texel_yuy16(prim.texture, curu, curv)); - *dest++ = source32_to_dest(pix); - curu += dudx; - curv += dvdx; - } - } - } - - // coloring-only case - else if (is_opaque(prim.color.a)) - { - u32 sr = u32(256.0f * prim.color.r); - u32 sg = u32(256.0f * prim.color.g); - u32 sb = u32(256.0f * prim.color.b); - - // clamp R,G,B to 0-256 range - if (sr > 0x100) { if (s32(sr) < 0) sr = 0; else sr = 0x100; } - if (sg > 0x100) { if (s32(sg) < 0) sg = 0; else sg = 0x100; } - if (sb > 0x100) { if (s32(sb) < 0) sb = 0; else sb = 0x100; } + // fast case: no coloring, no alpha // loop over rows for (s32 y = setup.starty; y < setup.endy; y++) { - _PixelType *dest = dstdata + y * pitch + setup.startx; + PixelType *dest = dstdata + y * pitch + setup.startx; s32 curu = setup.startu + (y - setup.starty) * setup.dudy; s32 curv = setup.startv + (y - setup.starty) * setup.dvdy; // loop over cols - for (s32 x = setup.startx; x < endx; x++) + for (s32 x = setup.startx; x < setup.endx; x++) { - u32 pix = ycc_to_rgb(get_texel_yuy16(prim.texture, curu, curv)); - u32 r = (source32_r(pix) * sr) >> 8; - u32 g = (source32_g(pix) * sg) >> 8; - u32 b = (source32_b(pix) * sb) >> 8; - - *dest++ = dest_assemble_rgb(r, g, b); - curu += dudx; - curv += dvdx; - } - } - } - - // alpha and/or coloring case - else if (!is_transparent(prim.color.a)) - { - u32 sr = u32(256.0f * prim.color.r * prim.color.a); - u32 sg = u32(256.0f * prim.color.g * prim.color.a); - u32 sb = u32(256.0f * prim.color.b * prim.color.a); - u32 invsa = u32(256.0f * (1.0f - prim.color.a)); - - // clamp R,G,B and inverse A to 0-256 range - if (sr > 0x100) { if (s32(sr) < 0) sr = 0; else sr = 0x100; } - if (sg > 0x100) { if (s32(sg) < 0) sg = 0; else sg = 0x100; } - if (sb > 0x100) { if (s32(sb) < 0) sb = 0; else sb = 0x100; } - if (invsa > 0x100) { if (s32(invsa) < 0) invsa = 0; else invsa = 0x100; } - - // loop over rows - for (s32 y = setup.starty; y < setup.endy; y++) - { - _PixelType *dest = dstdata + y * pitch + setup.startx; - s32 curu = setup.startu + (y - setup.starty) * setup.dudy; - s32 curv = setup.startv + (y - setup.starty) * setup.dvdy; - - // loop over cols - for (s32 x = setup.startx; x < endx; x++) - { - u32 pix = ycc_to_rgb(get_texel_yuy16(prim.texture, curu, curv)); - u32 dpix = _NoDestRead ? 0 : *dest; - u32 r = (source32_r(pix) * sr + dest_r(dpix) * invsa) >> 8; - u32 g = (source32_g(pix) * sg + dest_g(dpix) * invsa) >> 8; - u32 b = (source32_b(pix) * sb + dest_b(dpix) * invsa) >> 8; - - *dest++ = dest_assemble_rgb(r, g, b); - curu += dudx; - curv += dvdx; - } - } - } - } - - - //------------------------------------------------- - // draw_quad_yuy16_add - perform - // rasterization by using RGB add after YUY - // conversion - //------------------------------------------------- - - static void draw_quad_yuy16_add(const render_primitive &prim, _PixelType *dstdata, u32 pitch, quad_setup_data &setup) - { - s32 dudx = setup.dudx; - s32 dvdx = setup.dvdx; - s32 endx = setup.endx; - - // simply can't do this without reading from the dest - if (_NoDestRead) - return; - - // fast case: no coloring, no alpha - if (prim.color.r >= 1.0f && prim.color.g >= 1.0f && prim.color.b >= 1.0f && is_opaque(prim.color.a)) - { - // loop over rows - for (s32 y = setup.starty; y < setup.endy; y++) - { - _PixelType *dest = dstdata + y * pitch + setup.startx; - s32 curu = setup.startu + (y - setup.starty) * setup.dudy; - s32 curv = setup.startv + (y - setup.starty) * setup.dvdy; - - // loop over cols - for (s32 x = setup.startx; x < endx; x++) - { - u32 pix = ycc_to_rgb(get_texel_yuy16(prim.texture, curu, curv)); - u32 dpix = _NoDestRead ? 0 : *dest; - u32 r = source32_r(pix) + dest_r(dpix); - u32 g = source32_g(pix) + dest_g(dpix); - u32 b = source32_b(pix) + dest_b(dpix); - r = (r | -(r >> (8 - _SrcShiftR))) & (0xff >> _SrcShiftR); - g = (g | -(g >> (8 - _SrcShiftG))) & (0xff >> _SrcShiftG); - b = (b | -(b >> (8 - _SrcShiftB))) & (0xff >> _SrcShiftB); - *dest++ = dest_assemble_rgb(r, g, b); - curu += dudx; - curv += dvdx; + u32 const pix = gettexel(prim, curu, curv); + notint(*dest, pix); + dest++; + curu += setup.dudx; + curv += setup.dvdx; } } } - - // alpha and/or coloring case else { - u32 sr = u32(256.0f * prim.color.r); - u32 sg = u32(256.0f * prim.color.g); - u32 sb = u32(256.0f * prim.color.b); - u32 sa = u32(256.0f * prim.color.a); + // alpha and/or coloring case // clamp R,G,B and inverse A to 0-256 range - if (sr > 0x100) { if (s32(sr) < 0) sr = 0; else sr = 0x100; } - if (sg > 0x100) { if (s32(sg) < 0) sg = 0; else sg = 0x100; } - if (sb > 0x100) { if (s32(sb) < 0) sb = 0; else sb = 0x100; } - if (sa > 0x100) { if (s32(sa) < 0) sa = 0; else sa = 0x100; } + u32 const sr = u32(std::clamp(256.0f * prim.color.r * prim.color.a, 0.0f, 256.0f)); + u32 const sg = u32(std::clamp(256.0f * prim.color.g * prim.color.a, 0.0f, 256.0f)); + u32 const sb = u32(std::clamp(256.0f * prim.color.b * prim.color.a, 0.0f, 256.0f)); // loop over rows for (s32 y = setup.starty; y < setup.endy; y++) { - _PixelType *dest = dstdata + y * pitch + setup.startx; + PixelType *dest = dstdata + y * pitch + setup.startx; s32 curu = setup.startu + (y - setup.starty) * setup.dudy; s32 curv = setup.startv + (y - setup.starty) * setup.dvdy; // loop over cols - for (s32 x = setup.startx; x < endx; x++) + for (s32 x = setup.startx; x < setup.endx; x++) { - u32 pix = ycc_to_rgb(get_texel_yuy16(prim.texture, curu, curv)); - u32 dpix = _NoDestRead ? 0 : *dest; - u32 r = ((source32_r(pix) * sr * sa) >> 16) + dest_r(dpix); - u32 g = ((source32_g(pix) * sg * sa) >> 16) + dest_g(dpix); - u32 b = ((source32_b(pix) * sb * sa) >> 16) + dest_b(dpix); - r = (r | -(r >> (8 - _SrcShiftR))) & (0xff >> _SrcShiftR); - g = (g | -(g >> (8 - _SrcShiftG))) & (0xff >> _SrcShiftG); - b = (b | -(b >> (8 - _SrcShiftB))) & (0xff >> _SrcShiftB); - *dest++ = dest_assemble_rgb(r, g, b); - curu += dudx; - curv += dvdx; + u32 const pix = gettexel(prim, curu, curv); + tinted(*dest, pix, sr, sg, sb); + dest++; + curu += setup.dudx; + curv += setup.dvdx; } } } @@ -998,12 +832,10 @@ private: // a 32bpp RGB texture //------------------------------------------------- - static void draw_quad_rgb32(const render_primitive &prim, _PixelType *dstdata, u32 pitch, quad_setup_data &setup) + template <bool Wrap> + static void draw_quad_rgb32(render_primitive const &prim, PixelType *dstdata, u32 pitch, quad_setup_data const &setup) { - const rgb_t *palbase = prim.texture.palette; - s32 dudx = setup.dudx; - s32 dvdx = setup.dvdx; - s32 endx = setup.endx; + rgb_t const *const palbase = prim.texture.palette; // fast case: no coloring, no alpha if (prim.color.r >= 1.0f && prim.color.g >= 1.0f && prim.color.b >= 1.0f && is_opaque(prim.color.a)) @@ -1011,151 +843,145 @@ private: // loop over rows for (s32 y = setup.starty; y < setup.endy; y++) { - _PixelType *dest = dstdata + y * pitch + setup.startx; + PixelType *dest = dstdata + y * pitch + setup.startx; s32 curu = setup.startu + (y - setup.starty) * setup.dudy; s32 curv = setup.startv + (y - setup.starty) * setup.dvdy; - // no lookup case - if (palbase == nullptr) + if (!palbase) { + // no lookup case + // loop over cols - for (s32 x = setup.startx; x < endx; x++) + for (s32 x = setup.startx; x < setup.endx; x++) { - u32 pix = get_texel_rgb32(prim.texture, curu, curv); + u32 const pix = get_texel_rgb32<Wrap>(prim.texture, curu, curv); *dest++ = source32_to_dest(pix); - curu += dudx; - curv += dvdx; + curu += setup.dudx; + curv += setup.dvdx; } } - - // lookup case else { + // lookup case + // loop over cols - for (s32 x = setup.startx; x < endx; x++) + for (s32 x = setup.startx; x < setup.endx; x++) { - u32 pix = get_texel_rgb32(prim.texture, curu, curv); - u32 r = palbase[(pix >> 16) & 0xff] >> _SrcShiftR; - u32 g = palbase[(pix >> 8) & 0xff] >> _SrcShiftG; - u32 b = palbase[(pix >> 0) & 0xff] >> _SrcShiftB; + u32 const pix = get_texel_rgb32<Wrap>(prim.texture, curu, curv); + u32 const r = palbase[(pix >> 16) & 0xff] >> SrcShiftR; + u32 const g = palbase[(pix >> 8) & 0xff] >> SrcShiftG; + u32 const b = palbase[(pix >> 0) & 0xff] >> SrcShiftB; *dest++ = dest_assemble_rgb(r, g, b); - curu += dudx; - curv += dvdx; + curu += setup.dudx; + curv += setup.dvdx; } } } } - - // coloring-only case else if (is_opaque(prim.color.a)) { - u32 sr = u32(256.0f * prim.color.r); - u32 sg = u32(256.0f * prim.color.g); - u32 sb = u32(256.0f * prim.color.b); + // coloring-only case // clamp R,G,B to 0-256 range - if (sr > 0x100) { if (s32(sr) < 0) sr = 0; else sr = 0x100; } - if (sg > 0x100) { if (s32(sg) < 0) sg = 0; else sg = 0x100; } - if (sb > 0x100) { if (s32(sb) < 0) sb = 0; else sb = 0x100; } + u32 const sr = u32(std::clamp(256.0f * prim.color.r, 0.0f, 256.0f)); + u32 const sg = u32(std::clamp(256.0f * prim.color.g, 0.0f, 256.0f)); + u32 const sb = u32(std::clamp(256.0f * prim.color.b, 0.0f, 256.0f)); // loop over rows for (s32 y = setup.starty; y < setup.endy; y++) { - _PixelType *dest = dstdata + y * pitch + setup.startx; + PixelType *dest = dstdata + y * pitch + setup.startx; s32 curu = setup.startu + (y - setup.starty) * setup.dudy; s32 curv = setup.startv + (y - setup.starty) * setup.dvdy; - // no lookup case - if (palbase == nullptr) + if (!palbase) { + // no lookup case + // loop over cols - for (s32 x = setup.startx; x < endx; x++) + for (s32 x = setup.startx; x < setup.endx; x++) { - u32 pix = get_texel_rgb32(prim.texture, curu, curv); - u32 r = (source32_r(pix) * sr) >> 8; - u32 g = (source32_g(pix) * sg) >> 8; - u32 b = (source32_b(pix) * sb) >> 8; + u32 const pix = get_texel_rgb32<Wrap>(prim.texture, curu, curv); + u32 const r = (source32_r(pix) * sr) >> 8; + u32 const g = (source32_g(pix) * sg) >> 8; + u32 const b = (source32_b(pix) * sb) >> 8; *dest++ = dest_assemble_rgb(r, g, b); - curu += dudx; - curv += dvdx; + curu += setup.dudx; + curv += setup.dvdx; } } - - // lookup case else { + // lookup case + // loop over cols - for (s32 x = setup.startx; x < endx; x++) + for (s32 x = setup.startx; x < setup.endx; x++) { - u32 pix = get_texel_rgb32(prim.texture, curu, curv); - u32 r = (palbase[(pix >> 16) & 0xff] * sr) >> (8 + _SrcShiftR); - u32 g = (palbase[(pix >> 8) & 0xff] * sg) >> (8 + _SrcShiftG); - u32 b = (palbase[(pix >> 0) & 0xff] * sb) >> (8 + _SrcShiftB); + u32 const pix = get_texel_rgb32<Wrap>(prim.texture, curu, curv); + u32 const r = (palbase[(pix >> 16) & 0xff] * sr) >> (8 + SrcShiftR); + u32 const g = (palbase[(pix >> 8) & 0xff] * sg) >> (8 + SrcShiftG); + u32 const b = (palbase[(pix >> 0) & 0xff] * sb) >> (8 + SrcShiftB); *dest++ = dest_assemble_rgb(r, g, b); - curu += dudx; - curv += dvdx; + curu += setup.dudx; + curv += setup.dvdx; } } } } - - // alpha and/or coloring case else if (!is_transparent(prim.color.a)) { - u32 sr = u32(256.0f * prim.color.r * prim.color.a); - u32 sg = u32(256.0f * prim.color.g * prim.color.a); - u32 sb = u32(256.0f * prim.color.b * prim.color.a); - u32 invsa = u32(256.0f * (1.0f - prim.color.a)); + // alpha and/or coloring case // clamp R,G,B and inverse A to 0-256 range - if (sr > 0x100) { if (s32(sr) < 0) sr = 0; else sr = 0x100; } - if (sg > 0x100) { if (s32(sg) < 0) sg = 0; else sg = 0x100; } - if (sb > 0x100) { if (s32(sb) < 0) sb = 0; else sb = 0x100; } - if (invsa > 0x100) { if (s32(invsa) < 0) invsa = 0; else invsa = 0x100; } + u32 const sr = u32(std::clamp(256.0f * prim.color.r * prim.color.a, 0.0f, 256.0f)); + u32 const sg = u32(std::clamp(256.0f * prim.color.g * prim.color.a, 0.0f, 256.0f)); + u32 const sb = u32(std::clamp(256.0f * prim.color.b * prim.color.a, 0.0f, 256.0f)); + u32 const invsa = u32(std::clamp(256.0f * (1.0f - prim.color.a), 0.0f, 256.0f)); // loop over rows for (s32 y = setup.starty; y < setup.endy; y++) { - _PixelType *dest = dstdata + y * pitch + setup.startx; + PixelType *dest = dstdata + y * pitch + setup.startx; s32 curu = setup.startu + (y - setup.starty) * setup.dudy; s32 curv = setup.startv + (y - setup.starty) * setup.dvdy; - // no lookup case - if (palbase == nullptr) + if (!palbase) { + // no lookup case + // loop over cols - for (s32 x = setup.startx; x < endx; x++) + for (s32 x = setup.startx; x < setup.endx; x++) { - u32 pix = get_texel_rgb32(prim.texture, curu, curv); - u32 dpix = _NoDestRead ? 0 : *dest; - u32 r = (source32_r(pix) * sr + dest_r(dpix) * invsa) >> 8; - u32 g = (source32_g(pix) * sg + dest_g(dpix) * invsa) >> 8; - u32 b = (source32_b(pix) * sb + dest_b(dpix) * invsa) >> 8; + u32 const pix = get_texel_rgb32<Wrap>(prim.texture, curu, curv); + u32 const dpix = NoDestRead ? 0 : *dest; + u32 const r = (source32_r(pix) * sr + dest_r(dpix) * invsa) >> 8; + u32 const g = (source32_g(pix) * sg + dest_g(dpix) * invsa) >> 8; + u32 const b = (source32_b(pix) * sb + dest_b(dpix) * invsa) >> 8; *dest++ = dest_assemble_rgb(r, g, b); - curu += dudx; - curv += dvdx; + curu += setup.dudx; + curv += setup.dvdx; } } - - // lookup case else { + // lookup case + // loop over cols - for (s32 x = setup.startx; x < endx; x++) + for (s32 x = setup.startx; x < setup.endx; x++) { - u32 pix = get_texel_rgb32(prim.texture, curu, curv); - u32 dpix = _NoDestRead ? 0 : *dest; - u32 r = ((palbase[(pix >> 16) & 0xff] >> _SrcShiftR) * sr + dest_r(dpix) * invsa) >> 8; - u32 g = ((palbase[(pix >> 8) & 0xff] >> _SrcShiftG) * sg + dest_g(dpix) * invsa) >> 8; - u32 b = ((palbase[(pix >> 0) & 0xff] >> _SrcShiftB) * sb + dest_b(dpix) * invsa) >> 8; + u32 const pix = get_texel_rgb32<Wrap>(prim.texture, curu, curv); + u32 const dpix = NoDestRead ? 0 : *dest; + u32 const r = ((palbase[(pix >> 16) & 0xff] >> SrcShiftR) * sr + dest_r(dpix) * invsa) >> 8; + u32 const g = ((palbase[(pix >> 8) & 0xff] >> SrcShiftG) * sg + dest_g(dpix) * invsa) >> 8; + u32 const b = ((palbase[(pix >> 0) & 0xff] >> SrcShiftB) * sb + dest_b(dpix) * invsa) >> 8; *dest++ = dest_assemble_rgb(r, g, b); - curu += dudx; - curv += dvdx; + curu += setup.dudx; + curv += setup.dvdx; } } } @@ -1168,127 +994,127 @@ private: // rasterization by using RGB add //------------------------------------------------- - static void draw_quad_rgb32_add(const render_primitive &prim, _PixelType *dstdata, u32 pitch, quad_setup_data &setup) + template <bool Wrap> + static void draw_quad_rgb32_add(render_primitive const &prim, PixelType *dstdata, u32 pitch, quad_setup_data const &setup) { - const rgb_t *palbase = prim.texture.palette; - s32 dudx = setup.dudx; - s32 dvdx = setup.dvdx; - s32 endx = setup.endx; - // simply can't do this without reading from the dest - if (_NoDestRead) + if (NoDestRead) return; - // fast case: no coloring, no alpha + rgb_t const *const palbase = prim.texture.palette; + if (prim.color.r >= 1.0f && prim.color.g >= 1.0f && prim.color.b >= 1.0f && is_opaque(prim.color.a)) { + // fast case: no coloring, no alpha + // loop over rows for (s32 y = setup.starty; y < setup.endy; y++) { - _PixelType *dest = dstdata + y * pitch + setup.startx; + PixelType *dest = dstdata + y * pitch + setup.startx; s32 curu = setup.startu + (y - setup.starty) * setup.dudy; s32 curv = setup.startv + (y - setup.starty) * setup.dvdy; - // no lookup case - if (palbase == nullptr) + if (!palbase) { + // no lookup case + // loop over cols - for (s32 x = setup.startx; x < endx; x++) + for (s32 x = setup.startx; x < setup.endx; x++) { - u32 pix = get_texel_argb32(prim.texture, curu, curv); - u32 dpix = _NoDestRead ? 0 : *dest; + u32 const pix = get_texel_argb32<Wrap>(prim.texture, curu, curv); + u32 const dpix = NoDestRead ? 0 : *dest; u32 r = source32_r(pix) + dest_r(dpix); u32 g = source32_g(pix) + dest_g(dpix); u32 b = source32_b(pix) + dest_b(dpix); - r = (r | -(r >> (8 - _SrcShiftR))) & (0xff >> _SrcShiftR); - g = (g | -(g >> (8 - _SrcShiftG))) & (0xff >> _SrcShiftG); - b = (b | -(b >> (8 - _SrcShiftB))) & (0xff >> _SrcShiftB); + r = (r | -(r >> (8 - SrcShiftR))) & (0xff >> SrcShiftR); + g = (g | -(g >> (8 - SrcShiftG))) & (0xff >> SrcShiftG); + b = (b | -(b >> (8 - SrcShiftB))) & (0xff >> SrcShiftB); + *dest++ = dest_assemble_rgb(r, g, b); - curu += dudx; - curv += dvdx; + curu += setup.dudx; + curv += setup.dvdx; } } - - // lookup case else { + // lookup case + // loop over cols - for (s32 x = setup.startx; x < endx; x++) + for (s32 x = setup.startx; x < setup.endx; x++) { - u32 pix = get_texel_argb32(prim.texture, curu, curv); - u32 dpix = _NoDestRead ? 0 : *dest; - u32 r = (palbase[(pix >> 16) & 0xff] >> _SrcShiftR) + dest_r(dpix); - u32 g = (palbase[(pix >> 8) & 0xff] >> _SrcShiftG) + dest_g(dpix); - u32 b = (palbase[(pix >> 0) & 0xff] >> _SrcShiftB) + dest_b(dpix); - r = (r | -(r >> (8 - _SrcShiftR))) & (0xff >> _SrcShiftR); - g = (g | -(g >> (8 - _SrcShiftG))) & (0xff >> _SrcShiftG); - b = (b | -(b >> (8 - _SrcShiftB))) & (0xff >> _SrcShiftB); + u32 const pix = get_texel_argb32<Wrap>(prim.texture, curu, curv); + u32 const dpix = NoDestRead ? 0 : *dest; + u32 r = (palbase[(pix >> 16) & 0xff] >> SrcShiftR) + dest_r(dpix); + u32 g = (palbase[(pix >> 8) & 0xff] >> SrcShiftG) + dest_g(dpix); + u32 b = (palbase[(pix >> 0) & 0xff] >> SrcShiftB) + dest_b(dpix); + r = (r | -(r >> (8 - SrcShiftR))) & (0xff >> SrcShiftR); + g = (g | -(g >> (8 - SrcShiftG))) & (0xff >> SrcShiftG); + b = (b | -(b >> (8 - SrcShiftB))) & (0xff >> SrcShiftB); + *dest++ = dest_assemble_rgb(r, g, b); - curu += dudx; - curv += dvdx; + curu += setup.dudx; + curv += setup.dvdx; } } } } - - // alpha and/or coloring case else { - u32 sr = u32(256.0f * prim.color.r); - u32 sg = u32(256.0f * prim.color.g); - u32 sb = u32(256.0f * prim.color.b); - u32 sa = u32(256.0f * prim.color.a); + // alpha and/or coloring case // clamp R,G,B and inverse A to 0-256 range - if (sr > 0x100) { if (s32(sr) < 0) sr = 0; else sr = 0x100; } - if (sg > 0x100) { if (s32(sg) < 0) sg = 0; else sg = 0x100; } - if (sb > 0x100) { if (s32(sb) < 0) sb = 0; else sb = 0x100; } - if (sa > 0x100) { if (s32(sa) < 0) sa = 0; else sa = 0x100; } + u32 const sr = u32(std::clamp(256.0f * prim.color.r, 0.0f, 256.0f)); + u32 const sg = u32(std::clamp(256.0f * prim.color.g, 0.0f, 256.0f)); + u32 const sb = u32(std::clamp(256.0f * prim.color.b, 0.0f, 256.0f)); + u32 const sa = u32(std::clamp(256.0f * prim.color.a, 0.0f, 256.0f)); // loop over rows for (s32 y = setup.starty; y < setup.endy; y++) { - _PixelType *dest = dstdata + y * pitch + setup.startx; + PixelType *dest = dstdata + y * pitch + setup.startx; s32 curu = setup.startu + (y - setup.starty) * setup.dudy; s32 curv = setup.startv + (y - setup.starty) * setup.dvdy; - // no lookup case - if (palbase == nullptr) + if (!palbase) { + // no lookup case + // loop over cols - for (s32 x = setup.startx; x < endx; x++) + for (s32 x = setup.startx; x < setup.endx; x++) { - u32 pix = get_texel_argb32(prim.texture, curu, curv); - u32 dpix = _NoDestRead ? 0 : *dest; + u32 const pix = get_texel_argb32<Wrap>(prim.texture, curu, curv); + u32 const dpix = NoDestRead ? 0 : *dest; u32 r = ((source32_r(pix) * sr * sa) >> 16) + dest_r(dpix); u32 g = ((source32_g(pix) * sg * sa) >> 16) + dest_g(dpix); u32 b = ((source32_b(pix) * sb * sa) >> 16) + dest_b(dpix); - r = (r | -(r >> (8 - _SrcShiftR))) & (0xff >> _SrcShiftR); - g = (g | -(g >> (8 - _SrcShiftG))) & (0xff >> _SrcShiftG); - b = (b | -(b >> (8 - _SrcShiftB))) & (0xff >> _SrcShiftB); + r = (r | -(r >> (8 - SrcShiftR))) & (0xff >> SrcShiftR); + g = (g | -(g >> (8 - SrcShiftG))) & (0xff >> SrcShiftG); + b = (b | -(b >> (8 - SrcShiftB))) & (0xff >> SrcShiftB); + *dest++ = dest_assemble_rgb(r, g, b); - curu += dudx; - curv += dvdx; + curu += setup.dudx; + curv += setup.dvdx; } } - - // lookup case else { + // lookup case + // loop over cols - for (s32 x = setup.startx; x < endx; x++) + for (s32 x = setup.startx; x < setup.endx; x++) { - u32 pix = get_texel_argb32(prim.texture, curu, curv); - u32 dpix = _NoDestRead ? 0 : *dest; - u32 r = ((palbase[(pix >> 16) & 0xff] * sr * sa) >> (16 + _SrcShiftR)) + dest_r(dpix); - u32 g = ((palbase[(pix >> 8) & 0xff] * sr * sa) >> (16 + _SrcShiftR)) + dest_g(dpix); - u32 b = ((palbase[(pix >> 0) & 0xff] * sr * sa) >> (16 + _SrcShiftR)) + dest_b(dpix); - r = (r | -(r >> (8 - _SrcShiftR))) & (0xff >> _SrcShiftR); - g = (g | -(g >> (8 - _SrcShiftG))) & (0xff >> _SrcShiftG); - b = (b | -(b >> (8 - _SrcShiftB))) & (0xff >> _SrcShiftB); + u32 const pix = get_texel_argb32<Wrap>(prim.texture, curu, curv); + u32 const dpix = NoDestRead ? 0 : *dest; + u32 r = ((palbase[(pix >> 16) & 0xff] * sr * sa) >> (16 + SrcShiftR)) + dest_r(dpix); + u32 g = ((palbase[(pix >> 8) & 0xff] * sr * sa) >> (16 + SrcShiftR)) + dest_g(dpix); + u32 b = ((palbase[(pix >> 0) & 0xff] * sr * sa) >> (16 + SrcShiftR)) + dest_b(dpix); + r = (r | -(r >> (8 - SrcShiftR))) & (0xff >> SrcShiftR); + g = (g | -(g >> (8 - SrcShiftG))) & (0xff >> SrcShiftG); + b = (b | -(b >> (8 - SrcShiftB))) & (0xff >> SrcShiftB); + *dest++ = dest_assemble_rgb(r, g, b); - curu += dudx; - curv += dvdx; + curu += setup.dudx; + curv += setup.dvdx; } } } @@ -1301,113 +1127,114 @@ private: // rasterization using RGB multiply //------------------------------------------------- - static void draw_quad_rgb32_multiply(const render_primitive &prim, _PixelType *dstdata, u32 pitch, quad_setup_data &setup) + template <bool Wrap> + static void draw_quad_rgb32_multiply(render_primitive const &prim, PixelType *dstdata, u32 pitch, quad_setup_data const &setup) { - const rgb_t *palbase = prim.texture.palette; - s32 dudx = setup.dudx; - s32 dvdx = setup.dvdx; - s32 endx = setup.endx; - // simply can't do this without reading from the dest - if (_NoDestRead) + if (NoDestRead) return; - // fast case: no coloring, no alpha + rgb_t const *const palbase = prim.texture.palette; + if (prim.color.r >= 1.0f && prim.color.g >= 1.0f && prim.color.b >= 1.0f && is_opaque(prim.color.a)) { + // fast case: no coloring, no alpha + // loop over rows for (s32 y = setup.starty; y < setup.endy; y++) { - _PixelType *dest = dstdata + y * pitch + setup.startx; + PixelType *dest = dstdata + y * pitch + setup.startx; s32 curu = setup.startu + (y - setup.starty) * setup.dudy; s32 curv = setup.startv + (y - setup.starty) * setup.dvdy; - // no lookup case - if (palbase == nullptr) + if (!palbase) { + // no lookup case + // loop over cols - for (s32 x = setup.startx; x < endx; x++) + for (s32 x = setup.startx; x < setup.endx; x++) { - u32 pix = get_texel_argb32(prim.texture, curu, curv); - u32 dpix = _NoDestRead ? 0 : *dest; - u32 r = (source32_r(pix) * dest_r(dpix)) >> (8 - _SrcShiftR); - u32 g = (source32_g(pix) * dest_g(dpix)) >> (8 - _SrcShiftG); - u32 b = (source32_b(pix) * dest_b(dpix)) >> (8 - _SrcShiftB); + u32 const pix = get_texel_argb32<Wrap>(prim.texture, curu, curv); + u32 const dpix = NoDestRead ? 0 : *dest; + u32 const r = (source32_r(pix) * dest_r(dpix)) >> (8 - SrcShiftR); + u32 const g = (source32_g(pix) * dest_g(dpix)) >> (8 - SrcShiftG); + u32 const b = (source32_b(pix) * dest_b(dpix)) >> (8 - SrcShiftB); *dest++ = dest_assemble_rgb(r, g, b); - curu += dudx; - curv += dvdx; + curu += setup.dudx; + curv += setup.dvdx; } } else { + // lookup case + // loop over cols - for (s32 x = setup.startx; x < endx; x++) + for (s32 x = setup.startx; x < setup.endx; x++) { - u32 pix = get_texel_argb32(prim.texture, curu, curv); - u32 dpix = _NoDestRead ? 0 : *dest; - u32 r = (palbase[(pix >> 16) & 0xff] * dest_r(dpix)) >> 8; - u32 g = (palbase[(pix >> 8) & 0xff] * dest_g(dpix)) >> 8; - u32 b = (palbase[(pix >> 0) & 0xff] * dest_b(dpix)) >> 8; + u32 const pix = get_texel_argb32<Wrap>(prim.texture, curu, curv); + u32 const dpix = NoDestRead ? 0 : *dest; + u32 const r = (palbase[(pix >> 16) & 0xff] * dest_r(dpix)) >> 8; + u32 const g = (palbase[(pix >> 8) & 0xff] * dest_g(dpix)) >> 8; + u32 const b = (palbase[(pix >> 0) & 0xff] * dest_b(dpix)) >> 8; *dest++ = dest_assemble_rgb(r, g, b); - curu += dudx; - curv += dvdx; + curu += setup.dudx; + curv += setup.dvdx; } } } } - - // alpha and/or coloring case else { - u32 sr = u32(256.0f * prim.color.r * prim.color.a); - u32 sg = u32(256.0f * prim.color.g * prim.color.a); - u32 sb = u32(256.0f * prim.color.b * prim.color.a); + // alpha and/or coloring case // clamp R,G,B to 0-256 range - if (sr > 0x100) { if (s32(sr) < 0) sr = 0; else sr = 0x100; } - if (sg > 0x100) { if (s32(sg) < 0) sg = 0; else sg = 0x100; } - if (sb > 0x100) { if (s32(sb) < 0) sb = 0; else sb = 0x100; } + u32 const sr = u32(std::clamp(256.0f * prim.color.r * prim.color.a, 0.0f, 256.0f)); + u32 const sg = u32(std::clamp(256.0f * prim.color.g * prim.color.a, 0.0f, 256.0f)); + u32 const sb = u32(std::clamp(256.0f * prim.color.b * prim.color.a, 0.0f, 256.0f)); // loop over rows for (s32 y = setup.starty; y < setup.endy; y++) { - _PixelType *dest = dstdata + y * pitch + setup.startx; + PixelType *dest = dstdata + y * pitch + setup.startx; s32 curu = setup.startu + (y - setup.starty) * setup.dudy; s32 curv = setup.startv + (y - setup.starty) * setup.dvdy; - // no lookup case - if (palbase == nullptr) + if (!palbase) { + // no lookup case + // loop over cols - for (s32 x = setup.startx; x < endx; x++) + for (s32 x = setup.startx; x < setup.endx; x++) { - u32 pix = get_texel_argb32(prim.texture, curu, curv); - u32 dpix = _NoDestRead ? 0 : *dest; - u32 r = (source32_r(pix) * sr * dest_r(dpix)) >> (16 - _SrcShiftR); - u32 g = (source32_g(pix) * sg * dest_g(dpix)) >> (16 - _SrcShiftG); - u32 b = (source32_b(pix) * sb * dest_b(dpix)) >> (16 - _SrcShiftB); + u32 const pix = get_texel_argb32<Wrap>(prim.texture, curu, curv); + u32 const dpix = NoDestRead ? 0 : *dest; + u32 const r = (source32_r(pix) * sr * dest_r(dpix)) >> (16 - SrcShiftR); + u32 const g = (source32_g(pix) * sg * dest_g(dpix)) >> (16 - SrcShiftG); + u32 const b = (source32_b(pix) * sb * dest_b(dpix)) >> (16 - SrcShiftB); *dest++ = dest_assemble_rgb(r, g, b); - curu += dudx; - curv += dvdx; + curu += setup.dudx; + curv += setup.dvdx; } } else { + // lookup case + // loop over cols - for (s32 x = setup.startx; x < endx; x++) + for (s32 x = setup.startx; x < setup.endx; x++) { - u32 pix = get_texel_argb32(prim.texture, curu, curv); - u32 dpix = _NoDestRead ? 0 : *dest; - u32 r = (palbase[(pix >> 16) & 0xff] * sr * dest_r(dpix)) >> 16; - u32 g = (palbase[(pix >> 8) & 0xff] * sg * dest_g(dpix)) >> 16; - u32 b = (palbase[(pix >> 0) & 0xff] * sb * dest_b(dpix)) >> 16; + u32 const pix = get_texel_argb32<Wrap>(prim.texture, curu, curv); + u32 const dpix = NoDestRead ? 0 : *dest; + u32 const r = (palbase[(pix >> 16) & 0xff] * sr * dest_r(dpix)) >> 16; + u32 const g = (palbase[(pix >> 8) & 0xff] * sg * dest_g(dpix)) >> 16; + u32 const b = (palbase[(pix >> 0) & 0xff] * sb * dest_b(dpix)) >> 16; *dest++ = dest_assemble_rgb(r, g, b); - curu += dudx; - curv += dvdx; + curu += setup.dudx; + curv += setup.dvdx; } } } @@ -1424,140 +1251,135 @@ private: // rasterization using standard alpha blending //------------------------------------------------- - static void draw_quad_argb32_alpha(const render_primitive &prim, _PixelType *dstdata, u32 pitch, quad_setup_data &setup) + template <bool Wrap> + static void draw_quad_argb32_alpha(render_primitive const &prim, PixelType *dstdata, u32 pitch, quad_setup_data const &setup) { - const rgb_t *palbase = prim.texture.palette; - s32 dudx = setup.dudx; - s32 dvdx = setup.dvdx; - s32 endx = setup.endx; + rgb_t const *const palbase = prim.texture.palette; - // fast case: no coloring, no alpha if (prim.color.r >= 1.0f && prim.color.g >= 1.0f && prim.color.b >= 1.0f && is_opaque(prim.color.a)) { + // fast case: no coloring, no alpha + // loop over rows for (s32 y = setup.starty; y < setup.endy; y++) { - _PixelType *dest = dstdata + y * pitch + setup.startx; + PixelType *dest = dstdata + y * pitch + setup.startx; s32 curu = setup.startu + (y - setup.starty) * setup.dudy; s32 curv = setup.startv + (y - setup.starty) * setup.dvdy; - // no lookup case - if (palbase == nullptr) + if (!palbase) { + // no lookup case + // loop over cols - for (s32 x = setup.startx; x < endx; x++) + for (s32 x = setup.startx; x < setup.endx; x++) { - u32 pix = get_texel_argb32(prim.texture, curu, curv); - u32 ta = pix >> 24; + u32 const pix = get_texel_argb32<Wrap>(prim.texture, curu, curv); + u32 const ta = pix >> 24; if (ta != 0) { - u32 dpix = _NoDestRead ? 0 : *dest; - u32 invta = 0x100 - ta; - u32 r = (source32_r(pix) * ta + dest_r(dpix) * invta) >> 8; - u32 g = (source32_g(pix) * ta + dest_g(dpix) * invta) >> 8; - u32 b = (source32_b(pix) * ta + dest_b(dpix) * invta) >> 8; + u32 const dpix = NoDestRead ? 0 : *dest; + u32 const invta = 0x100 - ta; + u32 const r = (source32_r(pix) * ta + dest_r(dpix) * invta) >> 8; + u32 const g = (source32_g(pix) * ta + dest_g(dpix) * invta) >> 8; + u32 const b = (source32_b(pix) * ta + dest_b(dpix) * invta) >> 8; *dest = dest_assemble_rgb(r, g, b); } dest++; - curu += dudx; - curv += dvdx; + curu += setup.dudx; + curv += setup.dvdx; } } - - // lookup case else { + // lookup case + // loop over cols - for (s32 x = setup.startx; x < endx; x++) + for (s32 x = setup.startx; x < setup.endx; x++) { - u32 pix = get_texel_argb32(prim.texture, curu, curv); - u32 ta = pix >> 24; + u32 const pix = get_texel_argb32<Wrap>(prim.texture, curu, curv); + u32 const ta = pix >> 24; if (ta != 0) { - u32 dpix = _NoDestRead ? 0 : *dest; - u32 invta = 0x100 - ta; - u32 r = ((palbase[(pix >> 16) & 0xff] >> _SrcShiftR) * ta + dest_r(dpix) * invta) >> 8; - u32 g = ((palbase[(pix >> 8) & 0xff] >> _SrcShiftG) * ta + dest_g(dpix) * invta) >> 8; - u32 b = ((palbase[(pix >> 0) & 0xff] >> _SrcShiftB) * ta + dest_b(dpix) * invta) >> 8; + u32 const dpix = NoDestRead ? 0 : *dest; + u32 const invta = 0x100 - ta; + u32 const r = ((palbase[(pix >> 16) & 0xff] >> SrcShiftR) * ta + dest_r(dpix) * invta) >> 8; + u32 const g = ((palbase[(pix >> 8) & 0xff] >> SrcShiftG) * ta + dest_g(dpix) * invta) >> 8; + u32 const b = ((palbase[(pix >> 0) & 0xff] >> SrcShiftB) * ta + dest_b(dpix) * invta) >> 8; *dest = dest_assemble_rgb(r, g, b); } dest++; - curu += dudx; - curv += dvdx; + curu += setup.dudx; + curv += setup.dvdx; } } } } - - // alpha and/or coloring case else { - u32 sr = u32(256.0f * prim.color.r); - u32 sg = u32(256.0f * prim.color.g); - u32 sb = u32(256.0f * prim.color.b); - u32 sa = u32(256.0f * prim.color.a); + // alpha and/or coloring case // clamp R,G,B and inverse A to 0-256 range - if (sr > 0x100) { if (s32(sr) < 0) sr = 0; else sr = 0x100; } - if (sg > 0x100) { if (s32(sg) < 0) sg = 0; else sg = 0x100; } - if (sb > 0x100) { if (s32(sb) < 0) sb = 0; else sb = 0x100; } - if (sa > 0x100) { if (s32(sa) < 0) sa = 0; else sa = 0x100; } + u32 const sr = u32(std::clamp(256.0f * prim.color.r, 0.0f, 256.0f)); + u32 const sg = u32(std::clamp(256.0f * prim.color.g, 0.0f, 256.0f)); + u32 const sb = u32(std::clamp(256.0f * prim.color.b, 0.0f, 256.0f)); + u32 const sa = u32(std::clamp(256.0f * prim.color.a, 0.0f, 256.0f)); // loop over rows for (s32 y = setup.starty; y < setup.endy; y++) { - _PixelType *dest = dstdata + y * pitch + setup.startx; + PixelType *dest = dstdata + y * pitch + setup.startx; s32 curu = setup.startu + (y - setup.starty) * setup.dudy; s32 curv = setup.startv + (y - setup.starty) * setup.dvdy; - // no lookup case - if (palbase == nullptr) + if (!palbase) { + // no lookup case + // loop over cols - for (s32 x = setup.startx; x < endx; x++) + for (s32 x = setup.startx; x < setup.endx; x++) { - u32 pix = get_texel_argb32(prim.texture, curu, curv); - u32 ta = (pix >> 24) * sa; + u32 const pix = get_texel_argb32<Wrap>(prim.texture, curu, curv); + u32 const ta = (pix >> 24) * sa; if (ta != 0) { - u32 dpix = _NoDestRead ? 0 : *dest; - u32 invsta = (0x10000 - ta) << 8; - u32 r = (source32_r(pix) * sr * ta + dest_r(dpix) * invsta) >> 24; - u32 g = (source32_g(pix) * sg * ta + dest_g(dpix) * invsta) >> 24; - u32 b = (source32_b(pix) * sb * ta + dest_b(dpix) * invsta) >> 24; + u32 const dpix = NoDestRead ? 0 : *dest; + u32 const invsta = (0x10000 - ta) << 8; + u32 const r = (source32_r(pix) * sr * ta + dest_r(dpix) * invsta) >> 24; + u32 const g = (source32_g(pix) * sg * ta + dest_g(dpix) * invsta) >> 24; + u32 const b = (source32_b(pix) * sb * ta + dest_b(dpix) * invsta) >> 24; *dest = dest_assemble_rgb(r, g, b); } dest++; - curu += dudx; - curv += dvdx; + curu += setup.dudx; + curv += setup.dvdx; } } - - - // lookup case else { + // lookup case + // loop over cols - for (s32 x = setup.startx; x < endx; x++) + for (s32 x = setup.startx; x < setup.endx; x++) { - u32 pix = get_texel_argb32(prim.texture, curu, curv); - u32 ta = (pix >> 24) * sa; + u32 const pix = get_texel_argb32<Wrap>(prim.texture, curu, curv); + u32 const ta = (pix >> 24) * sa; if (ta != 0) { - u32 dpix = _NoDestRead ? 0 : *dest; - u32 invsta = (0x10000 - ta) << 8; - u32 r = ((palbase[(pix >> 16) & 0xff] >> _SrcShiftR) * sr * ta + dest_r(dpix) * invsta) >> 24; - u32 g = ((palbase[(pix >> 8) & 0xff] >> _SrcShiftG) * sg * ta + dest_g(dpix) * invsta) >> 24; - u32 b = ((palbase[(pix >> 0) & 0xff] >> _SrcShiftB) * sb * ta + dest_b(dpix) * invsta) >> 24; + u32 const dpix = NoDestRead ? 0 : *dest; + u32 const invsta = (0x10000 - ta) << 8; + u32 const r = ((palbase[(pix >> 16) & 0xff] >> SrcShiftR) * sr * ta + dest_r(dpix) * invsta) >> 24; + u32 const g = ((palbase[(pix >> 8) & 0xff] >> SrcShiftG) * sg * ta + dest_g(dpix) * invsta) >> 24; + u32 const b = ((palbase[(pix >> 0) & 0xff] >> SrcShiftB) * sb * ta + dest_b(dpix) * invsta) >> 24; *dest = dest_assemble_rgb(r, g, b); } dest++; - curu += dudx; - curv += dvdx; + curu += setup.dudx; + curv += setup.dvdx; } } } @@ -1570,147 +1392,142 @@ private: // rasterization by using RGB add //------------------------------------------------- - static void draw_quad_argb32_add(const render_primitive &prim, _PixelType *dstdata, u32 pitch, quad_setup_data &setup) + template <bool Wrap> + static void draw_quad_argb32_add(render_primitive const &prim, PixelType *dstdata, u32 pitch, quad_setup_data const &setup) { - const rgb_t *palbase = prim.texture.palette; - s32 dudx = setup.dudx; - s32 dvdx = setup.dvdx; - s32 endx = setup.endx; - // simply can't do this without reading from the dest - if (_NoDestRead) + if (NoDestRead) return; + rgb_t const *const palbase = prim.texture.palette; + // fast case: no coloring, no alpha if (prim.color.r >= 1.0f && prim.color.g >= 1.0f && prim.color.b >= 1.0f && is_opaque(prim.color.a)) { // loop over rows for (s32 y = setup.starty; y < setup.endy; y++) { - _PixelType *dest = dstdata + y * pitch + setup.startx; + PixelType *dest = dstdata + y * pitch + setup.startx; s32 curu = setup.startu + (y - setup.starty) * setup.dudy; s32 curv = setup.startv + (y - setup.starty) * setup.dvdy; - // no lookup case - if (palbase == nullptr) + if (!palbase) { + // no lookup case + // loop over cols - for (s32 x = setup.startx; x < endx; x++) + for (s32 x = setup.startx; x < setup.endx; x++) { - u32 pix = get_texel_argb32(prim.texture, curu, curv); - u32 ta = pix >> 24; + u32 const pix = get_texel_argb32<Wrap>(prim.texture, curu, curv); + u32 const ta = pix >> 24; if (ta != 0) { - u32 dpix = _NoDestRead ? 0 : *dest; + u32 const dpix = NoDestRead ? 0 : *dest; u32 r = ((source32_r(pix) * ta) >> 8) + dest_r(dpix); u32 g = ((source32_g(pix) * ta) >> 8) + dest_g(dpix); u32 b = ((source32_b(pix) * ta) >> 8) + dest_b(dpix); - r = (r | -(r >> (8 - _SrcShiftR))) & (0xff >> _SrcShiftR); - g = (g | -(g >> (8 - _SrcShiftG))) & (0xff >> _SrcShiftG); - b = (b | -(b >> (8 - _SrcShiftB))) & (0xff >> _SrcShiftB); + r = (r | -(r >> (8 - SrcShiftR))) & (0xff >> SrcShiftR); + g = (g | -(g >> (8 - SrcShiftG))) & (0xff >> SrcShiftG); + b = (b | -(b >> (8 - SrcShiftB))) & (0xff >> SrcShiftB); *dest = dest_assemble_rgb(r, g, b); } dest++; - curu += dudx; - curv += dvdx; + curu += setup.dudx; + curv += setup.dvdx; } } - - // lookup case else { + // lookup case + // loop over cols - for (s32 x = setup.startx; x < endx; x++) + for (s32 x = setup.startx; x < setup.endx; x++) { - u32 pix = get_texel_argb32(prim.texture, curu, curv); - u32 ta = pix >> 24; + u32 const pix = get_texel_argb32<Wrap>(prim.texture, curu, curv); + u32 const ta = pix >> 24; if (ta != 0) { - u32 dpix = _NoDestRead ? 0 : *dest; - u32 r = ((palbase[(pix >> 16) & 0xff] * ta) >> (8 + _SrcShiftR)) + dest_r(dpix); - u32 g = ((palbase[(pix >> 8) & 0xff] * ta) >> (8 + _SrcShiftG)) + dest_g(dpix); - u32 b = ((palbase[(pix >> 0) & 0xff] * ta) >> (8 + _SrcShiftB)) + dest_b(dpix); - r = (r | -(r >> (8 - _SrcShiftR))) & (0xff >> _SrcShiftR); - g = (g | -(g >> (8 - _SrcShiftG))) & (0xff >> _SrcShiftG); - b = (b | -(b >> (8 - _SrcShiftB))) & (0xff >> _SrcShiftB); + u32 const dpix = NoDestRead ? 0 : *dest; + u32 r = ((palbase[(pix >> 16) & 0xff] * ta) >> (8 + SrcShiftR)) + dest_r(dpix); + u32 g = ((palbase[(pix >> 8) & 0xff] * ta) >> (8 + SrcShiftG)) + dest_g(dpix); + u32 b = ((palbase[(pix >> 0) & 0xff] * ta) >> (8 + SrcShiftB)) + dest_b(dpix); + r = (r | -(r >> (8 - SrcShiftR))) & (0xff >> SrcShiftR); + g = (g | -(g >> (8 - SrcShiftG))) & (0xff >> SrcShiftG); + b = (b | -(b >> (8 - SrcShiftB))) & (0xff >> SrcShiftB); *dest = dest_assemble_rgb(r, g, b); } dest++; - curu += dudx; - curv += dvdx; + curu += setup.dudx; + curv += setup.dvdx; } } } } - - // alpha and/or coloring case else { - u32 sr = u32(256.0f * prim.color.r); - u32 sg = u32(256.0f * prim.color.g); - u32 sb = u32(256.0f * prim.color.b); - u32 sa = u32(256.0f * prim.color.a); + // alpha and/or coloring case // clamp R,G,B and inverse A to 0-256 range - if (sr > 0x100) { if (s32(sr) < 0) sr = 0; else sr = 0x100; } - if (sg > 0x100) { if (s32(sg) < 0) sg = 0; else sg = 0x100; } - if (sb > 0x100) { if (s32(sb) < 0) sb = 0; else sb = 0x100; } - if (sa > 0x100) { if (s32(sa) < 0) sa = 0; else sa = 0x100; } + u32 const sr = u32(std::clamp(256.0f * prim.color.r, 0.0f, 256.0f)); + u32 const sg = u32(std::clamp(256.0f * prim.color.g, 0.0f, 256.0f)); + u32 const sb = u32(std::clamp(256.0f * prim.color.b, 0.0f, 256.0f)); + u32 const sa = u32(std::clamp(256.0f * prim.color.a, 0.0f, 256.0f)); // loop over rows for (s32 y = setup.starty; y < setup.endy; y++) { - _PixelType *dest = dstdata + y * pitch + setup.startx; + PixelType *dest = dstdata + y * pitch + setup.startx; s32 curu = setup.startu + (y - setup.starty) * setup.dudy; s32 curv = setup.startv + (y - setup.starty) * setup.dvdy; - // no lookup case - if (palbase == nullptr) + if (!palbase) { + // no lookup case + // loop over cols - for (s32 x = setup.startx; x < endx; x++) + for (s32 x = setup.startx; x < setup.endx; x++) { - u32 pix = get_texel_argb32(prim.texture, curu, curv); - u32 ta = (pix >> 24) * sa; + u32 const pix = get_texel_argb32<Wrap>(prim.texture, curu, curv); + u32 const ta = (pix >> 24) * sa; if (ta != 0) { - u32 dpix = _NoDestRead ? 0 : *dest; + u32 const dpix = NoDestRead ? 0 : *dest; u32 r = ((source32_r(pix) * sr * ta) >> 24) + dest_r(dpix); u32 g = ((source32_g(pix) * sg * ta) >> 24) + dest_g(dpix); u32 b = ((source32_b(pix) * sb * ta) >> 24) + dest_b(dpix); - r = (r | -(r >> (8 - _SrcShiftR))) & (0xff >> _SrcShiftR); - g = (g | -(g >> (8 - _SrcShiftG))) & (0xff >> _SrcShiftG); - b = (b | -(b >> (8 - _SrcShiftB))) & (0xff >> _SrcShiftB); + r = (r | -(r >> (8 - SrcShiftR))) & (0xff >> SrcShiftR); + g = (g | -(g >> (8 - SrcShiftG))) & (0xff >> SrcShiftG); + b = (b | -(b >> (8 - SrcShiftB))) & (0xff >> SrcShiftB); *dest = dest_assemble_rgb(r, g, b); } dest++; - curu += dudx; - curv += dvdx; + curu += setup.dudx; + curv += setup.dvdx; } } - - // lookup case else { + // lookup case + // loop over cols - for (s32 x = setup.startx; x < endx; x++) + for (s32 x = setup.startx; x < setup.endx; x++) { - u32 pix = get_texel_argb32(prim.texture, curu, curv); - u32 ta = (pix >> 24) * sa; + u32 const pix = get_texel_argb32<Wrap>(prim.texture, curu, curv); + u32 const ta = (pix >> 24) * sa; if (ta != 0) { - u32 dpix = _NoDestRead ? 0 : *dest; - u32 r = ((palbase[(pix >> 16) & 0xff] * sr * ta) >> (24 + _SrcShiftR)) + dest_r(dpix); - u32 g = ((palbase[(pix >> 8) & 0xff] * sr * ta) >> (24 + _SrcShiftR)) + dest_g(dpix); - u32 b = ((palbase[(pix >> 0) & 0xff] * sr * ta) >> (24 + _SrcShiftR)) + dest_b(dpix); - r = (r | -(r >> (8 - _SrcShiftR))) & (0xff >> _SrcShiftR); - g = (g | -(g >> (8 - _SrcShiftG))) & (0xff >> _SrcShiftG); - b = (b | -(b >> (8 - _SrcShiftB))) & (0xff >> _SrcShiftB); + u32 const dpix = NoDestRead ? 0 : *dest; + u32 r = ((palbase[(pix >> 16) & 0xff] * sr * ta) >> (24 + SrcShiftR)) + dest_r(dpix); + u32 g = ((palbase[(pix >> 8) & 0xff] * sr * ta) >> (24 + SrcShiftR)) + dest_g(dpix); + u32 b = ((palbase[(pix >> 0) & 0xff] * sr * ta) >> (24 + SrcShiftR)) + dest_b(dpix); + r = (r | -(r >> (8 - SrcShiftR))) & (0xff >> SrcShiftR); + g = (g | -(g >> (8 - SrcShiftG))) & (0xff >> SrcShiftG); + b = (b | -(b >> (8 - SrcShiftB))) & (0xff >> SrcShiftB); *dest = dest_assemble_rgb(r, g, b); } dest++; - curu += dudx; - curv += dvdx; + curu += setup.dudx; + curv += setup.dvdx; } } } @@ -1728,16 +1545,16 @@ private: // drawing routine //------------------------------------------------- - static void setup_and_draw_textured_quad(const render_primitive &prim, _PixelType *dstdata, s32 width, s32 height, u32 pitch) + static void setup_and_draw_textured_quad(render_primitive const &prim, PixelType *dstdata, s32 width, s32 height, u32 pitch) { assert(prim.bounds.x0 <= prim.bounds.x1); assert(prim.bounds.y0 <= prim.bounds.y1); // determine U/V deltas - float fdudx = (prim.texcoords.tr.u - prim.texcoords.tl.u) / (prim.bounds.x1 - prim.bounds.x0); - float fdvdx = (prim.texcoords.tr.v - prim.texcoords.tl.v) / (prim.bounds.x1 - prim.bounds.x0); - float fdudy = (prim.texcoords.bl.u - prim.texcoords.tl.u) / (prim.bounds.y1 - prim.bounds.y0); - float fdvdy = (prim.texcoords.bl.v - prim.texcoords.tl.v) / (prim.bounds.y1 - prim.bounds.y0); + float const fdudx = (prim.texcoords.tr.u - prim.texcoords.tl.u) / (prim.bounds.x1 - prim.bounds.x0); + float const fdvdx = (prim.texcoords.tr.v - prim.texcoords.tl.v) / (prim.bounds.x1 - prim.bounds.x0); + float const fdudy = (prim.texcoords.bl.u - prim.texcoords.tl.u) / (prim.bounds.y1 - prim.bounds.y0); + float const fdvdy = (prim.texcoords.bl.v - prim.texcoords.tl.v) / (prim.bounds.y1 - prim.bounds.y0); // clamp to integers quad_setup_data setup; @@ -1769,54 +1586,145 @@ private: setup.startv += (setup.dvdx + setup.dvdy) / 2; // if we're bilinear filtering, we need to offset u/v by half a texel - if (_BilinearFilter) + if constexpr (BilinearFilter) { setup.startu -= 0x8000; setup.startv -= 0x8000; } + auto const gettexel_palette16 = + [] (render_primitive const &prim, s32 u, s32 v) -> u32 + { + return get_texel_palette16(prim.texture, u, v); + }; + auto const gettexel_yuy16 = + [] (render_primitive const &prim, s32 u, s32 v) -> u32 + { + return ycc_to_rgb(get_texel_yuy16(prim.texture, u, v)); + }; + auto const rop_mul_notint = + [] (PixelType &dest, u32 pix) + { + if ((pix & 0x00ff'ffff) != 0x00ff'ffff) + { + u32 const dpix = NoDestRead ? 0x00ff'ffff : dest; + u32 const r = (source32_r(pix) * dest_r(dpix)) >> (8 - SrcShiftR); + u32 const g = (source32_g(pix) * dest_g(dpix)) >> (8 - SrcShiftG); + u32 const b = (source32_b(pix) * dest_b(dpix)) >> (8 - SrcShiftB); + dest = dest_assemble_rgb(r, g, b); + } + }; + auto const rop_mul_tinted = + [] (PixelType &dest, u32 pix, u32 sr, u32 sg, u32 sb) + { + if ((pix & 0x00ff'ffff) != 0x00ff'ffff) + { + u32 const dpix = NoDestRead ? 0x00ff'ffff : dest; + u32 const r = (source32_r(pix) * sr * dest_r(dpix)) >> (16 - SrcShiftR); + u32 const g = (source32_g(pix) * sg * dest_g(dpix)) >> (16 - SrcShiftG); + u32 const b = (source32_b(pix) * sb * dest_b(dpix)) >> (16 - SrcShiftB); + dest = dest_assemble_rgb(r, g, b); + } + }; + auto const rop_add_notint = + [] (PixelType &dest, u32 pix) + { + if ((pix & 0x00ff'ffff) != 0) + { + u32 const dpix = NoDestRead ? 0 : dest; + u32 r = source32_r(pix) + dest_r(dpix); + u32 g = source32_g(pix) + dest_g(dpix); + u32 b = source32_b(pix) + dest_b(dpix); + r = (r | -(r >> (8 - SrcShiftR))) & (0xff >> SrcShiftR); + g = (g | -(g >> (8 - SrcShiftG))) & (0xff >> SrcShiftG); + b = (b | -(b >> (8 - SrcShiftB))) & (0xff >> SrcShiftB); + dest = dest_assemble_rgb(r, g, b); + } + }; + auto const rop_add_tinted = + [] (PixelType &dest, u32 pix, u32 sr, u32 sg, u32 sb) + { + if ((pix & 0x00ff'ffff) != 0) + { + u32 const dpix = NoDestRead ? 0 : dest; + u32 r = ((source32_r(pix) * sr) >> 8) + dest_r(dpix); + u32 g = ((source32_g(pix) * sg) >> 8) + dest_g(dpix); + u32 b = ((source32_b(pix) * sb) >> 8) + dest_b(dpix); + r = (r | -(r >> (8 - SrcShiftR))) & (0xff >> SrcShiftR); + g = (g | -(g >> (8 - SrcShiftG))) & (0xff >> SrcShiftG); + b = (b | -(b >> (8 - SrcShiftB))) & (0xff >> SrcShiftB); + dest = dest_assemble_rgb(r, g, b); + } + }; + // render based on the texture coordinates switch (prim.flags & (PRIMFLAG_TEXFORMAT_MASK | PRIMFLAG_BLENDMODE_MASK)) { case PRIMFLAG_TEXFORMAT(TEXFORMAT_PALETTE16) | PRIMFLAG_BLENDMODE(BLENDMODE_NONE): case PRIMFLAG_TEXFORMAT(TEXFORMAT_PALETTE16) | PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA): - draw_quad_palette16_none(prim, dstdata, pitch, setup); + assert(prim.texture.palette); + draw_quad_convert_none(prim, dstdata, pitch, setup, gettexel_palette16); + break; + + case PRIMFLAG_TEXFORMAT(TEXFORMAT_PALETTE16) | PRIMFLAG_BLENDMODE(BLENDMODE_RGB_MULTIPLY): + assert(prim.texture.palette); + draw_quad_convert_rop(prim, dstdata, pitch, setup, gettexel_palette16, rop_mul_notint, rop_mul_tinted); break; case PRIMFLAG_TEXFORMAT(TEXFORMAT_PALETTE16) | PRIMFLAG_BLENDMODE(BLENDMODE_ADD): - draw_quad_palette16_add(prim, dstdata, pitch, setup); + assert(prim.texture.palette); + draw_quad_convert_rop(prim, dstdata, pitch, setup, gettexel_palette16, rop_add_notint, rop_add_tinted); break; case PRIMFLAG_TEXFORMAT(TEXFORMAT_YUY16) | PRIMFLAG_BLENDMODE(BLENDMODE_NONE): case PRIMFLAG_TEXFORMAT(TEXFORMAT_YUY16) | PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA): - draw_quad_yuy16_none(prim, dstdata, pitch, setup); + draw_quad_convert_none(prim, dstdata, pitch, setup, gettexel_yuy16); + break; + + case PRIMFLAG_TEXFORMAT(TEXFORMAT_YUY16) | PRIMFLAG_BLENDMODE(BLENDMODE_RGB_MULTIPLY): + draw_quad_convert_rop(prim, dstdata, pitch, setup, gettexel_yuy16, rop_mul_notint, rop_mul_tinted); break; case PRIMFLAG_TEXFORMAT(TEXFORMAT_YUY16) | PRIMFLAG_BLENDMODE(BLENDMODE_ADD): - draw_quad_yuy16_add(prim, dstdata, pitch, setup); + draw_quad_convert_rop(prim, dstdata, pitch, setup, gettexel_yuy16, rop_add_notint, rop_add_tinted); break; case PRIMFLAG_TEXFORMAT(TEXFORMAT_RGB32) | PRIMFLAG_BLENDMODE(BLENDMODE_NONE): case PRIMFLAG_TEXFORMAT(TEXFORMAT_RGB32) | PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA): case PRIMFLAG_TEXFORMAT(TEXFORMAT_ARGB32) | PRIMFLAG_BLENDMODE(BLENDMODE_NONE): - draw_quad_rgb32(prim, dstdata, pitch, setup); + if (PRIMFLAG_GET_TEXWRAP(prim.flags)) + draw_quad_rgb32<true>(prim, dstdata, pitch, setup); + else + draw_quad_rgb32<false>(prim, dstdata, pitch, setup); break; case PRIMFLAG_TEXFORMAT(TEXFORMAT_RGB32) | PRIMFLAG_BLENDMODE(BLENDMODE_RGB_MULTIPLY): case PRIMFLAG_TEXFORMAT(TEXFORMAT_ARGB32) | PRIMFLAG_BLENDMODE(BLENDMODE_RGB_MULTIPLY): - draw_quad_rgb32_multiply(prim, dstdata, pitch, setup); + if (PRIMFLAG_GET_TEXWRAP(prim.flags)) + draw_quad_rgb32_multiply<true>(prim, dstdata, pitch, setup); + else + draw_quad_rgb32_multiply<false>(prim, dstdata, pitch, setup); break; case PRIMFLAG_TEXFORMAT(TEXFORMAT_RGB32) | PRIMFLAG_BLENDMODE(BLENDMODE_ADD): - draw_quad_rgb32_add(prim, dstdata, pitch, setup); + if (PRIMFLAG_GET_TEXWRAP(prim.flags)) + draw_quad_rgb32_add<true>(prim, dstdata, pitch, setup); + else + draw_quad_rgb32_add<false>(prim, dstdata, pitch, setup); break; case PRIMFLAG_TEXFORMAT(TEXFORMAT_ARGB32) | PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA): - draw_quad_argb32_alpha(prim, dstdata, pitch, setup); + if (PRIMFLAG_GET_TEXWRAP(prim.flags)) + draw_quad_argb32_alpha<true>(prim, dstdata, pitch, setup); + else + draw_quad_argb32_alpha<false>(prim, dstdata, pitch, setup); break; case PRIMFLAG_TEXFORMAT(TEXFORMAT_ARGB32) | PRIMFLAG_BLENDMODE(BLENDMODE_ADD): - draw_quad_argb32_add(prim, dstdata, pitch, setup); + if (PRIMFLAG_GET_TEXWRAP(prim.flags)) + draw_quad_argb32_add<true>(prim, dstdata, pitch, setup); + else + draw_quad_argb32_add<false>(prim, dstdata, pitch, setup); break; default: @@ -1836,21 +1744,21 @@ private: //------------------------------------------------- public: - static void draw_primitives(const render_primitive_list &primlist, void *dstdata, u32 width, u32 height, u32 pitch) + static void draw_primitives(render_primitive_list const &primlist, void *dstdata, u32 width, u32 height, u32 pitch) { // loop over the list and render each element - for (const render_primitive *prim = primlist.first(); prim != nullptr; prim = prim->next()) + for (render_primitive const *prim = primlist.first(); prim != nullptr; prim = prim->next()) switch (prim->type) { case render_primitive::LINE: - draw_line(*prim, reinterpret_cast<_PixelType *>(dstdata), width, height, pitch); + draw_line(*prim, reinterpret_cast<PixelType *>(dstdata), width, height, pitch); break; case render_primitive::QUAD: if (!prim->texture.base) - draw_rect(*prim, reinterpret_cast<_PixelType *>(dstdata), width, height, pitch); + draw_rect(*prim, reinterpret_cast<PixelType *>(dstdata), width, height, pitch); else - setup_and_draw_textured_quad(*prim, reinterpret_cast<_PixelType *>(dstdata), width, height, pitch); + setup_and_draw_textured_quad(*prim, reinterpret_cast<PixelType *>(dstdata), width, height, pitch); break; default: diff --git a/src/emu/rendertypes.h b/src/emu/rendertypes.h new file mode 100644 index 00000000000..b3eed6ad9bd --- /dev/null +++ b/src/emu/rendertypes.h @@ -0,0 +1,163 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles, Vas Crabb +/*************************************************************************** + + rendertypes.h + + Core renderer constants and structures for MAME. + +***************************************************************************/ +#ifndef MAME_EMU_RENDERTYPES_H +#define MAME_EMU_RENDERTYPES_H + +#pragma once + +#include <algorithm> + + +//************************************************************************** +// CONSTANTS +//************************************************************************** + + +// texture formats +enum texture_format +{ + TEXFORMAT_UNDEFINED = 0, // require a format to be specified + TEXFORMAT_PALETTE16, // 16bpp palettized, no alpha + TEXFORMAT_RGB32, // 32bpp 8-8-8 RGB + TEXFORMAT_ARGB32, // 32bpp 8-8-8-8 ARGB + TEXFORMAT_YUY16 // 16bpp 8-8 Y/Cb, Y/Cr in sequence +}; + +// blending modes +enum +{ + BLENDMODE_NONE = 0, // no blending + BLENDMODE_ALPHA, // standard alpha blend + BLENDMODE_RGB_MULTIPLY, // apply source alpha to source pix, then multiply RGB values + BLENDMODE_ADD, // apply source alpha to source pix, then add to destination + + BLENDMODE_COUNT +}; + + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// render_bounds - floating point bounding rectangle +struct render_bounds +{ + float x0; // leftmost X coordinate + float y0; // topmost Y coordinate + float x1; // rightmost X coordinate + float y1; // bottommost Y coordinate + + constexpr float width() const { return x1 - x0; } + constexpr float height() const { return y1 - y0; } + constexpr float aspect() const { return width() / height(); } + constexpr bool includes(float x, float y) const { return (x >= x0) && (x <= x1) && (y >= y0) && (y <= y1); } + + // intersection + constexpr render_bounds operator&(render_bounds const &b) const + { + return render_bounds{ (std::max)(x0, b.x0), (std::max)(y0, b.y0), (std::min)(x1, b.x1), (std::min)(y1, b.y1) }; + } + + render_bounds &operator&=(render_bounds const &b) + { + x0 = (std::max)(x0, b.x0); + y0 = (std::max)(y0, b.y0); + x1 = (std::min)(x1, b.x1); + y1 = (std::min)(y1, b.y1); + return *this; + } + + // union + constexpr render_bounds operator|(render_bounds const &b) const + { + return render_bounds{ (std::min)(x0, b.x0), (std::min)(y0, b.y0), (std::max)(x1, b.x1), (std::max)(y1, b.y1) }; + } + + render_bounds &operator|=(render_bounds const &b) + { + x0 = (std::min)(x0, b.x0); + y0 = (std::min)(y0, b.y0); + x1 = (std::max)(x1, b.x1); + y1 = (std::max)(y1, b.y1); + return *this; + } + + render_bounds &set_xy(float left, float top, float right, float bottom) + { + x0 = left; + y0 = top; + x1 = right; + y1 = bottom; + return *this; + } + + render_bounds &set_wh(float left, float top, float width, float height) + { + x0 = left; + y0 = top; + x1 = left + width; + y1 = top + height; + return *this; + } +}; + + +// render_color - floating point set of ARGB values +struct render_color +{ + float a; // alpha component (0.0 = transparent, 1.0 = opaque) + float r; // red component (0.0 = none, 1.0 = max) + float g; // green component (0.0 = none, 1.0 = max) + float b; // blue component (0.0 = none, 1.0 = max) + + constexpr render_color operator*(render_color const &c) const + { + return render_color{ a * c.a, r * c.r, g * c.g, b * c.b }; + } + + render_color &operator*=(render_color const &c) + { + a *= c.a; + r *= c.r; + g *= c.g; + b *= c.b; + return *this; + } + + render_color &set(float alpha, float red, float green, float blue) + { + a = alpha; + r = red; + g = green; + b = blue; + return *this; + } +}; + + +// render_texuv - floating point set of UV texture coordinates +struct render_texuv +{ + float u; // U coordinate (0.0-1.0) + float v; // V coordinate (0.0-1.0) +}; + + +// render_quad_texuv - floating point set of UV texture coordinates +struct render_quad_texuv +{ + render_texuv tl; // top-left UV coordinate + render_texuv tr; // top-right UV coordinate + render_texuv bl; // bottom-left UV coordinate + render_texuv br; // bottom-right UV coordinate +}; + +#endif // MAME_EMU_RENDERTYPES_H diff --git a/src/emu/rendfont.cpp b/src/emu/rendfont.cpp index c5debb5f055..259d7731fe5 100644 --- a/src/emu/rendfont.cpp +++ b/src/emu/rendfont.cpp @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles, Vas Crabb /*************************************************************************** - rendfont.c + rendfont.cpp Rendering system font management. @@ -10,11 +10,18 @@ #include "emu.h" #include "rendfont.h" + #include "emuopts.h" -#include "coreutil.h" +#include "fileio.h" +#include "render.h" + +#include "corestr.h" +#include "multibyte.h" +#include "path.h" #include "osdepend.h" #include "uismall.fh" +#include "unicode.h" #include "ui/uicmd14.fh" #include "ui/cmddata.h" @@ -119,13 +126,14 @@ public: static constexpr unsigned MAJVERSION = 1; static constexpr unsigned MINVERSION = 0; - bool read(emu_file &f) + bool read(util::read_stream &f) { - return f.read(m_data, sizeof(m_data)) == sizeof(m_data); + auto const [err, actual] = util::read(f, m_data, sizeof(m_data)); + return !err && (actual == sizeof(m_data)); } - bool write(emu_file &f) + bool write(util::write_stream &f) { - return f.write(m_data, sizeof(m_data)) == sizeof(m_data); + return !util::write(f, m_data, sizeof(m_data)).first; } bool check_magic() const @@ -142,51 +150,27 @@ public: } u64 get_original_length() const { - return - (u64(m_data[OFFS_ORIGLENGTH + 0]) << (7 * 8)) | - (u64(m_data[OFFS_ORIGLENGTH + 1]) << (6 * 8)) | - (u64(m_data[OFFS_ORIGLENGTH + 2]) << (5 * 8)) | - (u64(m_data[OFFS_ORIGLENGTH + 3]) << (4 * 8)) | - (u64(m_data[OFFS_ORIGLENGTH + 4]) << (3 * 8)) | - (u64(m_data[OFFS_ORIGLENGTH + 5]) << (2 * 8)) | - (u64(m_data[OFFS_ORIGLENGTH + 6]) << (1 * 8)) | - (u64(m_data[OFFS_ORIGLENGTH + 7]) << (0 * 8)); + return get_u64be(m_data + OFFS_ORIGLENGTH); } u32 get_original_hash() const { - return - (u32(m_data[OFFS_ORIGHASH + 0]) << (3 * 8)) | - (u32(m_data[OFFS_ORIGHASH + 1]) << (2 * 8)) | - (u32(m_data[OFFS_ORIGHASH + 2]) << (1 * 8)) | - (u32(m_data[OFFS_ORIGHASH + 3]) << (0 * 8)); + return get_u32be(m_data + OFFS_ORIGHASH); } u32 get_glyph_count() const { - return - (u32(m_data[OFFS_GLYPHCOUNT + 0]) << (3 * 8)) | - (u32(m_data[OFFS_GLYPHCOUNT + 1]) << (2 * 8)) | - (u32(m_data[OFFS_GLYPHCOUNT + 2]) << (1 * 8)) | - (u32(m_data[OFFS_GLYPHCOUNT + 3]) << (0 * 8)); + return get_u32be(m_data + OFFS_GLYPHCOUNT); } u16 get_height() const { - return - (u16(m_data[OFFS_HEIGHT + 0]) << (1 * 8)) | - (u16(m_data[OFFS_HEIGHT + 1]) << (0 * 8)); + return get_u16be(m_data + OFFS_HEIGHT); } s16 get_y_offset() const { - return - (u16(m_data[OFFS_YOFFSET + 0]) << (1 * 8)) | - (u16(m_data[OFFS_YOFFSET + 1]) << (0 * 8)); + return get_u16be(m_data + OFFS_YOFFSET); } s32 get_default_character() const { - return - (u32(m_data[OFFS_DEFCHAR + 0]) << (3 * 8)) | - (u32(m_data[OFFS_DEFCHAR + 1]) << (2 * 8)) | - (u32(m_data[OFFS_DEFCHAR + 2]) << (1 * 8)) | - (u32(m_data[OFFS_DEFCHAR + 3]) << (0 * 8)); + return get_u32be(m_data + OFFS_DEFCHAR); } void set_magic() @@ -200,45 +184,27 @@ public: } void set_original_length(u64 value) { - m_data[OFFS_ORIGLENGTH + 0] = u8((value >> (7 * 8)) & 0x00ff); - m_data[OFFS_ORIGLENGTH + 1] = u8((value >> (6 * 8)) & 0x00ff); - m_data[OFFS_ORIGLENGTH + 2] = u8((value >> (5 * 8)) & 0x00ff); - m_data[OFFS_ORIGLENGTH + 3] = u8((value >> (4 * 8)) & 0x00ff); - m_data[OFFS_ORIGLENGTH + 4] = u8((value >> (3 * 8)) & 0x00ff); - m_data[OFFS_ORIGLENGTH + 5] = u8((value >> (2 * 8)) & 0x00ff); - m_data[OFFS_ORIGLENGTH + 6] = u8((value >> (1 * 8)) & 0x00ff); - m_data[OFFS_ORIGLENGTH + 7] = u8((value >> (0 * 8)) & 0x00ff); + put_u64be(m_data + OFFS_ORIGLENGTH, value); } void set_original_hash(u32 value) { - m_data[OFFS_ORIGHASH + 0] = u8((value >> (3 * 8)) & 0x00ff); - m_data[OFFS_ORIGHASH + 1] = u8((value >> (2 * 8)) & 0x00ff); - m_data[OFFS_ORIGHASH + 2] = u8((value >> (1 * 8)) & 0x00ff); - m_data[OFFS_ORIGHASH + 3] = u8((value >> (0 * 8)) & 0x00ff); + put_u32be(m_data + OFFS_ORIGHASH, value); } void set_glyph_count(u32 value) { - m_data[OFFS_GLYPHCOUNT + 0] = u8((value >> (3 * 8)) & 0x00ff); - m_data[OFFS_GLYPHCOUNT + 1] = u8((value >> (2 * 8)) & 0x00ff); - m_data[OFFS_GLYPHCOUNT + 2] = u8((value >> (1 * 8)) & 0x00ff); - m_data[OFFS_GLYPHCOUNT + 3] = u8((value >> (0 * 8)) & 0x00ff); + put_u32be(m_data + OFFS_GLYPHCOUNT, value); } void set_height(u16 value) { - m_data[OFFS_HEIGHT + 0] = u8((value >> (1 * 8)) & 0x00ff); - m_data[OFFS_HEIGHT + 1] = u8((value >> (0 * 8)) & 0x00ff); + put_u16be(m_data + OFFS_HEIGHT, value); } void set_y_offset(s16 value) { - m_data[OFFS_YOFFSET + 0] = u8((value >> (1 * 8)) & 0x00ff); - m_data[OFFS_YOFFSET + 1] = u8((value >> (0 * 8)) & 0x00ff); + put_u16be(m_data + OFFS_YOFFSET, value); } void set_default_character(s32 value) { - m_data[OFFS_DEFCHAR + 0] = u8((value >> (3 * 8)) & 0x00ff); - m_data[OFFS_DEFCHAR + 1] = u8((value >> (2 * 8)) & 0x00ff); - m_data[OFFS_DEFCHAR + 2] = u8((value >> (1 * 8)) & 0x00ff); - m_data[OFFS_DEFCHAR + 3] = u8((value >> (0 * 8)) & 0x00ff); + put_u32be(m_data + OFFS_DEFCHAR, value); } private: @@ -278,74 +244,52 @@ public: u32 get_encoding() const { - return - (u32(m_ptr[OFFS_ENCODING + 0]) << (3 * 8)) | - (u32(m_ptr[OFFS_ENCODING + 1]) << (2 * 8)) | - (u32(m_ptr[OFFS_ENCODING + 2]) << (1 * 8)) | - (u32(m_ptr[OFFS_ENCODING + 3]) << (0 * 8)); + return get_u32be(m_ptr + OFFS_ENCODING); } u16 get_x_advance() const { - return - (u16(m_ptr[OFFS_XADVANCE + 0]) << (1 * 8)) | - (u16(m_ptr[OFFS_XADVANCE + 1]) << (0 * 8)); + return get_u16be(m_ptr + OFFS_XADVANCE); } s16 get_bb_x_offset() const { - return - (u16(m_ptr[OFFS_BBXOFFSET + 0]) << (1 * 8)) | - (u16(m_ptr[OFFS_BBXOFFSET + 1]) << (0 * 8)); + return get_u16be(m_ptr + OFFS_BBXOFFSET); } s16 get_bb_y_offset() const { - return - (u16(m_ptr[OFFS_BBYOFFSET + 0]) << (1 * 8)) | - (u16(m_ptr[OFFS_BBYOFFSET + 1]) << (0 * 8)); + return get_u16be(m_ptr + OFFS_BBYOFFSET); } u16 get_bb_width() const { - return - (u16(m_ptr[OFFS_BBWIDTH + 0]) << (1 * 8)) | - (u16(m_ptr[OFFS_BBWIDTH + 1]) << (0 * 8)); + return get_u16be(m_ptr + OFFS_BBWIDTH); } u16 get_bb_height() const { - return - (u16(m_ptr[OFFS_BBHEIGHT + 0]) << (1 * 8)) | - (u16(m_ptr[OFFS_BBHEIGHT + 1]) << (0 * 8)); + return get_u16be(m_ptr + OFFS_BBHEIGHT); } void set_encoding(u32 value) { - m_ptr[OFFS_ENCODING + 0] = u8((value >> (3 * 8)) & 0x00ff); - m_ptr[OFFS_ENCODING + 1] = u8((value >> (2 * 8)) & 0x00ff); - m_ptr[OFFS_ENCODING + 2] = u8((value >> (1 * 8)) & 0x00ff); - m_ptr[OFFS_ENCODING + 3] = u8((value >> (0 * 8)) & 0x00ff); + put_u32be(m_ptr + OFFS_ENCODING, value); } void set_x_advance(u16 value) { - m_ptr[OFFS_XADVANCE + 0] = u8((value >> (1 * 8)) & 0x00ff); - m_ptr[OFFS_XADVANCE + 1] = u8((value >> (0 * 8)) & 0x00ff); + put_u16be(m_ptr + OFFS_XADVANCE, value); } void set_bb_x_offset(s16 value) { - m_ptr[OFFS_BBXOFFSET + 0] = u8((value >> (1 * 8)) & 0x00ff); - m_ptr[OFFS_BBXOFFSET + 1] = u8((value >> (0 * 8)) & 0x00ff); + put_u16be(m_ptr + OFFS_BBXOFFSET, value); } void set_bb_y_offset(s16 value) { - m_ptr[OFFS_BBYOFFSET + 0] = u8((value >> (1 * 8)) & 0x00ff); - m_ptr[OFFS_BBYOFFSET + 1] = u8((value >> (0 * 8)) & 0x00ff); + put_u16be(m_ptr + OFFS_BBYOFFSET, value); } void set_bb_width(u16 value) { - m_ptr[OFFS_BBWIDTH + 0] = u8((value >> (1 * 8)) & 0x00ff); - m_ptr[OFFS_BBWIDTH + 1] = u8((value >> (0 * 8)) & 0x00ff); + put_u16be(m_ptr + OFFS_BBWIDTH, value); } void set_bb_height(u16 value) { - m_ptr[OFFS_BBHEIGHT + 0] = u8((value >> (1 * 8)) & 0x00ff); - m_ptr[OFFS_BBHEIGHT + 1] = u8((value >> (0 * 8)) & 0x00ff); + put_u16be(m_ptr + OFFS_BBHEIGHT, value); } bdc_table_entry &operator=(bdc_table_entry const &that) = default; @@ -372,35 +316,30 @@ private: } // anonymous namespace -void convert_command_glyph(std::string &str) +std::string convert_command_glyph(std::string_view str) { - (void)str.c_str(); // force NUL-termination - we depend on it later - std::size_t const len(str.length()); - std::vector<char> buf(2 * (len + 1)); + std::vector<char> buf(2 * (str.length() + 1)); std::size_t j(0); - for (std::size_t i = 0; len > i; ) + while (!str.empty()) { // decode UTF-8 char32_t uchar; - int const codelen(uchar_from_utf8(&uchar, &str[i], len - i)); + int const codelen(uchar_from_utf8(&uchar, str)); if (0 >= codelen) break; - i += codelen; + str.remove_prefix(codelen); // check for three metacharacters fix_command_t const *fixcmd(nullptr); switch (uchar) { case COMMAND_CONVERT_TEXT: - for (fix_strings_t *fixtext = convert_text; fixtext->glyph_code; ++fixtext) + for (fix_strings_t const *fixtext = convert_text; fixtext->glyph_code; ++fixtext) { - if (!fixtext->glyph_str_len) - fixtext->glyph_str_len = std::strlen(fixtext->glyph_str); - - if (!std::strncmp(fixtext->glyph_str, &str[i], fixtext->glyph_str_len)) + if (str.substr(0, fixtext->glyph_str.length()) == fixtext->glyph_str) { uchar = fixtext->glyph_code + COMMAND_UNICODE; - i += strlen(fixtext->glyph_str); + str.remove_prefix(fixtext->glyph_str.length()); break; } } @@ -416,20 +355,20 @@ void convert_command_glyph(std::string &str) } // this substitutes a single character - if (fixcmd) + if (fixcmd && !str.empty()) { - if (str[i] == uchar) + if (str[0] == uchar) { - ++i; + str.remove_prefix(1); } else { - while (fixcmd->glyph_code && (fixcmd->glyph_char != str[i])) + while (fixcmd->glyph_code && !str.empty() && fixcmd->glyph_char != str[0]) ++fixcmd; - if (fixcmd->glyph_code) + if (fixcmd->glyph_code && !str.empty()) { uchar = COMMAND_UNICODE + fixcmd->glyph_code; - ++i; + str.remove_prefix(1); } } } @@ -440,7 +379,7 @@ void convert_command_glyph(std::string &str) break; j += outlen; } - str.assign(&buf[0], j); + return std::string(&buf[0], j); } @@ -481,7 +420,7 @@ inline render_font::glyph &render_font::get_char(char32_t chnum) static glyph dummy_glyph; unsigned const page(chnum / 256); - if (page >= ARRAY_LENGTH(m_glyphs)) + if (page >= std::size(m_glyphs)) { if ((0 <= m_defchar) && (chnum != m_defchar)) return get_char(m_defchar); @@ -596,10 +535,9 @@ render_font::render_font(render_manager &manager, const char *filename) } // load the compiled in data instead - emu_file ramfile(OPEN_FLAG_READ); - osd_file::error const filerr(ramfile.open_ram(font_uismall, sizeof(font_uismall))); - if (osd_file::error::NONE == filerr) - load_cached(ramfile, 0, 0); + util::random_read::ptr ramfile = util::ram_read(font_uismall, sizeof(font_uismall)); + if (ramfile) + load_cached(*ramfile, 0, 0); render_font_command_glyph(); } @@ -664,14 +602,14 @@ void render_font::char_expand(char32_t chnum, glyph &gl) for (int y = 0; y < gl.bmheight; y++) { int desty = y + m_height_cmd + m_yoffs_cmd - gl.yoffs - gl.bmheight; - u32 *dest = (desty >= 0 && desty < m_height_cmd) ? &gl.bitmap.pix32(desty, 0) : nullptr; + u32 *dest = (desty >= 0 && desty < m_height_cmd) ? &gl.bitmap.pix(desty, 0) : nullptr; { for (int x = 0; x < gl.bmwidth; x++) { if (accumbit == 7) accum = *ptr++; if (dest != nullptr) - *dest++ = (accum & (1 << accumbit)) ? fgcol : bgcol; + *dest++ = BIT(accum, accumbit) ? fgcol : bgcol; accumbit = (accumbit - 1) & 7; } } @@ -723,7 +661,7 @@ void render_font::char_expand(char32_t chnum, glyph &gl) for (int y = 0; y < gl.bmheight; ++y) { int const desty(y + m_height + m_yoffs - gl.yoffs - gl.bmheight); - u32 *dest(((0 <= desty) && (m_height > desty)) ? &gl.bitmap.pix32(desty) : nullptr); + u32 *dest(((0 <= desty) && (m_height > desty)) ? &gl.bitmap.pix(desty) : nullptr); if (m_format == format::TEXT) { @@ -767,7 +705,7 @@ void render_font::char_expand(char32_t chnum, glyph &gl) if (accumbit == 7) accum = *ptr++; if (dest != nullptr) - *dest++ = (accum & (1 << accumbit)) ? fgcol : bgcol; + *dest++ = BIT(accum, accumbit) ? fgcol : bgcol; accumbit = (accumbit - 1) & 7; } } @@ -820,7 +758,8 @@ void render_font::get_scaled_bitmap_and_bounds(bitmap_argb32 &dest, float height bounds.min_y = 0; // compute x1,y1 from there based on the bitmap size - bounds.set_width(float(gl.bmwidth) * scale * aspect); + float width = float(gl.bmwidth) * scale * aspect; + bounds.set_width(width < 0.5f ? 0 : std::max(int(width), 1)); bounds.set_height(float(m_height) * scale); // if the bitmap isn't big enough, bail @@ -856,24 +795,23 @@ float render_font::char_width(float height, float aspect, char32_t ch) // at the given height //------------------------------------------------- -float render_font::string_width(float height, float aspect, const char *string) +float render_font::string_width(float height, float aspect, std::string_view string) { // loop over the string and accumulate widths int totwidth = 0; - const char *ends = string + strlen(string); - const char *s = string; char32_t schar; // loop over characters - while (*s != 0) + int scharcount; + while ((scharcount = uchar_from_utf8(&schar, string)) != 0) { - int scharcount = uchar_from_utf8(&schar, s, ends - s); + if (0 > scharcount) + schar = 0xfffd; + string.remove_prefix((0 > scharcount) ? 1 : scharcount); totwidth += get_char(schar).width; - s += scharcount; } - // scale the final result based on height return float(totwidth) * m_scale * height * aspect; } @@ -884,21 +822,19 @@ float render_font::string_width(float height, float aspect, const char *string) // UTF8-encoded string at the given height //------------------------------------------------- -float render_font::utf8string_width(float height, float aspect, const char *utf8string) +float render_font::utf8string_width(float height, float aspect, std::string_view utf8string) { - std::size_t const length = std::strlen(utf8string); - // loop over the string and accumulate widths - int count; s32 totwidth = 0; - for (std::size_t offset = 0U; offset < length; offset += unsigned(count)) + while (!utf8string.empty()) { char32_t uchar; - count = uchar_from_utf8(&uchar, utf8string + offset, length - offset); + int count = uchar_from_utf8(&uchar, utf8string); if (count < 0) break; totwidth += get_char(uchar).width; + utf8string.remove_prefix(count); } // scale the final result based on height @@ -913,16 +849,16 @@ float render_font::utf8string_width(float height, float aspect, const char *utf8 // and create a new cached version //------------------------------------------------- -bool render_font::load_cached_bdf(const char *filename) +bool render_font::load_cached_bdf(std::string_view filename) { - osd_file::error filerr; + std::error_condition filerr; u32 chunk; - u64 bytes; + std::size_t bytes; // first try to open the BDF itself emu_file file(m_manager.machine().options().font_path(), OPEN_FLAG_READ); filerr = file.open(filename); - if (filerr != osd_file::error::NONE) + if (filerr) return false; // determine the file size and allocate memory @@ -947,19 +883,17 @@ bool render_font::load_cached_bdf(const char *filename) m_rawdata.clear(); return false; } - u32 const hash(core_crc32(0, reinterpret_cast<u8 const *>(&m_rawdata[0]), bytes)); + u32 const hash(util::crc32_creator::simple(&m_rawdata[0], bytes)); // create the cached filename, changing the 'F' to a 'C' on the extension - std::string cachedname(filename); - if ((4U < cachedname.length()) && !core_stricmp(&cachedname[cachedname.length() - 4], ".bdf")) - cachedname.erase(cachedname.length() - 4); + std::string cachedname(filename, 0, filename.length() - ((4U < filename.length()) && core_filename_ends_with(filename, ".bdf") ? 4 : 0)); cachedname.append(".bdc"); // attempt to open the cached version of the font { emu_file cachefile(m_manager.machine().options().font_path(), OPEN_FLAG_READ); - filerr = cachefile.open(cachedname.c_str()); - if (filerr == osd_file::error::NONE) + filerr = cachefile.open(cachedname); + if (!filerr) { // if we have a cached version, load it bool const result = load_cached(cachefile, m_rawsize, hash); @@ -985,11 +919,25 @@ bool render_font::load_cached_bdf(const char *filename) m_rawdata[m_rawsize] = '\0'; // load the BDF - bool const result = load_bdf(); + bool result = load_bdf(); // if we loaded okay, create a cached one if (result) - save_cached(cachedname.c_str(), m_rawsize, hash); + { + osd_printf_info("Generating cached BDF font...\n"); + + // attempt to open the file + emu_file cachefile(m_manager.machine().options().font_path(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE); + filerr = cachefile.open(cachedname); + if (filerr) + result = false; + else + { + result = save_cached(cachefile, m_rawsize, hash); + if (!result) + cachefile.remove_on_close(); + } + } else m_rawdata.clear(); @@ -1299,7 +1247,7 @@ bool render_font::load_bdf() { LOG("render_font::load_bdf: ignoring character with negative x advance\n"); } - else if ((256 * ARRAY_LENGTH(m_glyphs)) <= encoding) + else if ((256 * std::size(m_glyphs)) <= encoding) { LOG("render_font::load_bdf: ignoring character with encoding outside range\n"); } @@ -1331,7 +1279,7 @@ bool render_font::load_bdf() // some progress for big fonts if (0 == (++charcount % 256)) - osd_printf_warning("Loading BDF font... (%d characters loaded)\n", charcount); + osd_printf_info("Loading BDF font... (%d characters loaded)\n", charcount); } } @@ -1351,12 +1299,17 @@ bool render_font::load_bdf() // load_cached - load a font in cached format //------------------------------------------------- -bool render_font::load_cached(emu_file &file, u64 length, u32 hash) +bool render_font::load_cached(util::random_read &file, u64 length, u32 hash) { // get the file size, read the header, and check that it looks good - u64 const filesize(file.size()); + u64 filesize; bdc_header header; - if (!header.read(file)) + if (file.length(filesize)) + { + LOG("render_font::load_cached: error determining size of BDC file\n"); + return false; + } + else if (!header.read(file)) { osd_printf_warning("render_font::load_cached: error reading BDC header\n"); return false; @@ -1378,14 +1331,25 @@ bool render_font::load_cached(emu_file &file, u64 length, u32 hash) m_yoffs = header.get_y_offset(); m_defchar = header.get_default_character(); u32 const numchars(header.get_glyph_count()); - if ((file.tell() + (u64(numchars) * bdc_table_entry::size())) > filesize) + u64 filepos; + if (file.tell(filepos)) + { + LOG("render_font::load_cached: failed to determine position in BDC file\n"); + return false; + } + else if ((filepos + (u64(numchars) * bdc_table_entry::size())) > filesize) { LOG("render_font::load_cached: BDC file is too small to hold glyph table\n"); return false; } // now read the rest of the data - u64 const remaining(filesize - file.tell()); + u64 const remaining(filesize - filepos); + if (remaining > std::numeric_limits<std::size_t>::max()) + { + osd_printf_error("render_font::load_cached: BDC file is too large to read into memory\n"); + return false; + } try { m_rawdata.resize(std::size_t(remaining)); @@ -1393,17 +1357,14 @@ bool render_font::load_cached(emu_file &file, u64 length, u32 hash) catch (...) { osd_printf_error("render_font::load_cached: allocation error\n"); + return false; } - for (u64 bytes_read = 0; remaining > bytes_read; ) + auto const [err, bytes] = read(file, &m_rawdata[0], remaining); + if (err || (bytes != remaining)) { - u32 const chunk((std::min)(u64(std::numeric_limits<u32>::max()), remaining)); - if (file.read(&m_rawdata[bytes_read], chunk) != chunk) - { - osd_printf_error("render_font::load_cached: error reading BDC data\n"); - m_rawdata.clear(); - return false; - } - bytes_read += chunk; + osd_printf_error("render_font::load_cached: error reading BDC data\n"); + m_rawdata.clear(); + return false; } // extract the data from the data @@ -1457,16 +1418,8 @@ bool render_font::load_cached(emu_file &file, u64 length, u32 hash) // save_cached - save a font in cached format //------------------------------------------------- -bool render_font::save_cached(const char *filename, u64 length, u32 hash) +bool render_font::save_cached(util::random_write &file, u64 length, u32 hash) { - osd_printf_warning("Generating cached BDF font...\n"); - - // attempt to open the file - emu_file file(m_manager.machine().options().font_path(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE); - osd_file::error const filerr = file.open(filename); - if (osd_file::error::NONE != filerr) - return false; - // count glyphs unsigned numchars = 0; for (glyph const *const page : m_glyphs) @@ -1481,8 +1434,6 @@ bool render_font::save_cached(const char *filename, u64 length, u32 hash) try { - u32 bytes_written; - { LOG("render_font::save_cached: writing header\n"); bdc_header hdr; @@ -1495,9 +1446,11 @@ bool render_font::save_cached(const char *filename, u64 length, u32 hash) hdr.set_y_offset(m_yoffs); hdr.set_default_character(m_defchar); if (!hdr.write(file)) - throw emu_fatalerror("Error writing cached file"); + throw emu_fatalerror("Error writing cached font file"); } - u64 const table_offs(file.tell()); + u64 table_offs; + if (file.tell(table_offs)) + throw emu_fatalerror("Error writing cached font file"); // allocate an array to hold the character data std::vector<u8> chartable(std::size_t(numchars) * bdc_table_entry::size(), 0); @@ -1506,13 +1459,12 @@ bool render_font::save_cached(const char *filename, u64 length, u32 hash) std::vector<u8> tempbuffer(65536); // write the empty table to the beginning of the file - bytes_written = file.write(&chartable[0], chartable.size()); - if (bytes_written != chartable.size()) - throw emu_fatalerror("Error writing cached file"); + if (write(file, &chartable[0], chartable.size()).first) + throw emu_fatalerror("Error writing cached font file"); // loop over all characters bdc_table_entry table_entry(chartable.empty() ? nullptr : &chartable[0]); - for (unsigned chnum = 0; chnum < (256 * ARRAY_LENGTH(m_glyphs)); chnum++) + for (unsigned chnum = 0; chnum < (256 * std::size(m_glyphs)); chnum++) { if (m_glyphs[chnum / 256] && (0 < m_glyphs[chnum / 256][chnum % 256].width)) { @@ -1531,7 +1483,7 @@ bool render_font::save_cached(const char *filename, u64 length, u32 hash) for (int y = 0; y < gl.bmheight; y++) { int desty = y + m_height + m_yoffs - gl.yoffs - gl.bmheight; - const u32 *src = (desty >= 0 && desty < m_height) ? &gl.bitmap.pix32(desty) : nullptr; + u32 const *const src = (desty >= 0 && desty < m_height) ? &gl.bitmap.pix(desty) : nullptr; for (int x = 0; x < gl.bmwidth; x++) { if (src != nullptr && rgb_t(src[x]).a() != 0) @@ -1550,9 +1502,8 @@ bool render_font::save_cached(const char *filename, u64 length, u32 hash) *dest++ = accum; // write the data - bytes_written = file.write(&tempbuffer[0], dest - &tempbuffer[0]); - if (bytes_written != dest - &tempbuffer[0]) - throw emu_fatalerror("Error writing cached file"); + if (write(file, &tempbuffer[0], dest - &tempbuffer[0]).first) + throw emu_fatalerror("Error writing cached font file"); // free the bitmap and texture m_manager.texture_free(gl.texture); @@ -1575,17 +1526,10 @@ bool render_font::save_cached(const char *filename, u64 length, u32 hash) if (!chartable.empty()) { LOG("render_font::save_cached: writing character table\n"); - file.seek(table_offs, SEEK_SET); - u8 const *bytes(&chartable[0]); - for (u64 remaining = chartable.size(); remaining; ) - { - u32 const chunk((std::min<u64>)(std::numeric_limits<u32>::max(), remaining)); - bytes_written = file.write(bytes, chunk); - if (chunk != bytes_written) - throw emu_fatalerror("Error writing cached file"); - bytes += chunk; - remaining -= chunk; - } + if (file.seek(table_offs, SEEK_SET)) + return false; + if (write(file, &chartable[0], chartable.size()).first) + throw emu_fatalerror("Error writing cached font file"); } // no trouble? @@ -1593,7 +1537,6 @@ bool render_font::save_cached(const char *filename, u64 length, u32 hash) } catch (...) { - file.remove_on_close(); return false; } } @@ -1602,13 +1545,13 @@ bool render_font::save_cached(const char *filename, u64 length, u32 hash) void render_font::render_font_command_glyph() { // FIXME: this is copy/pasta from the BDC loading, and it shouldn't be injected into every font - emu_file file(OPEN_FLAG_READ); - if (file.open_ram(font_uicmd14, sizeof(font_uicmd14)) == osd_file::error::NONE) + util::random_read::ptr file = util::ram_read(font_uicmd14, sizeof(font_uicmd14)); + if (file) { // get the file size, read the header, and check that it looks good - u64 const filesize(file.size()); + u64 const filesize = sizeof(font_uicmd14); bdc_header header; - if (!header.read(file)) + if (!header.read(*file)) { osd_printf_warning("render_font::render_font_command_glyph: error reading BDC header\n"); return; @@ -1623,14 +1566,25 @@ void render_font::render_font_command_glyph() m_height_cmd = header.get_height(); m_yoffs_cmd = header.get_y_offset(); u32 const numchars(header.get_glyph_count()); - if ((file.tell() + (u64(numchars) * bdc_table_entry::size())) > filesize) + u64 filepos; + if (file->tell(filepos)) + { + LOG("render_font::render_font_command_glyph: failed to determine position in BDC file\n"); + return; + } + else if ((filepos + (u64(numchars) * bdc_table_entry::size())) > filesize) { LOG("render_font::render_font_command_glyph: BDC file is too small to hold glyph table\n"); return; } // now read the rest of the data - u64 const remaining(filesize - file.tell()); + u64 const remaining(filesize - filepos); + if (remaining > std::numeric_limits<std::size_t>::max()) + { + osd_printf_error("render_font::render_font_command_glyph: BDC file is too large to read into memory\n"); + return; + } try { m_rawdata_cmd.resize(std::size_t(remaining)); @@ -1638,17 +1592,14 @@ void render_font::render_font_command_glyph() catch (...) { osd_printf_error("render_font::render_font_command_glyph: allocation error\n"); + return; } - for (u64 bytes_read = 0; remaining > bytes_read; ) + auto const [err, bytes] = read(*file, &m_rawdata_cmd[0], remaining); + if (err || (bytes != remaining)) { - u32 const chunk((std::min)(u64(std::numeric_limits<u32>::max()), remaining)); - if (file.read(&m_rawdata_cmd[bytes_read], chunk) != chunk) - { - osd_printf_error("render_font::render_font_command_glyph: error reading BDC data\n"); - m_rawdata_cmd.clear(); - return; - } - bytes_read += chunk; + osd_printf_error("render_font::render_font_command_glyph: error reading BDC data\n"); + m_rawdata_cmd.clear(); + return; } // extract the data from the data diff --git a/src/emu/rendfont.h b/src/emu/rendfont.h index aa04a094d75..9ada358ed5d 100644 --- a/src/emu/rendfont.h +++ b/src/emu/rendfont.h @@ -11,8 +11,6 @@ #ifndef MAME_EMU_RENDFONT_H #define MAME_EMU_RENDFONT_H -#include "render.h" - //************************************************************************** // TYPE DEFINITIONS //************************************************************************** @@ -27,17 +25,18 @@ class render_font // construction/destruction render_font(render_manager &manager, const char *filename); - virtual ~render_font(); public: + virtual ~render_font(); + // getters render_manager &manager() const { return m_manager; } // size queries s32 pixel_height() const { return m_height; } float char_width(float height, float aspect, char32_t ch); - float string_width(float height, float aspect, const char *string); - float utf8string_width(float height, float aspect, const char *utf8string); + float string_width(float height, float aspect, std::string_view string); + float utf8string_width(float height, float aspect, std::string_view utf8string); // texture/bitmap queries render_texture *get_char_texture_and_bounds(float height, float aspect, char32_t ch, render_bounds &bounds); @@ -81,10 +80,10 @@ private: // helpers glyph &get_char(char32_t chnum); void char_expand(char32_t chnum, glyph &ch); - bool load_cached_bdf(const char *filename); + bool load_cached_bdf(std::string_view filename); bool load_bdf(); - bool load_cached(emu_file &file, u64 length, u32 hash); - bool save_cached(const char *filename, u64 length, u32 hash); + bool load_cached(util::random_read &file, u64 length, u32 hash); + bool save_cached(util::random_write &file, u64 length, u32 hash); void render_font_command_glyph(); @@ -109,6 +108,6 @@ private: static const u64 CACHED_BDF_HASH_SIZE = 1024; }; -void convert_command_glyph(std::string &s); +std::string convert_command_glyph(std::string_view str); #endif /* MAME_EMU_RENDFONT_H */ diff --git a/src/emu/rendlay.cpp b/src/emu/rendlay.cpp index 23a60c57e13..489f232a1ad 100644 --- a/src/emu/rendlay.cpp +++ b/src/emu/rendlay.cpp @@ -2,29 +2,37 @@ // copyright-holders:Aaron Giles, Vas Crabb /*************************************************************************** - rendlay.c + rendlay.cpp Core rendering layout parser and manager. ***************************************************************************/ #include "emu.h" +#include "render.h" +#include "rendlay.h" #include "emuopts.h" -#include "render.h" +#include "fileio.h" +#include "main.h" #include "rendfont.h" -#include "rendlay.h" #include "rendutil.h" -#include "vecstream.h" -#include "xmlfile.h" +#include "video/rgbutil.h" + +#include "util/nanosvg.h" +#include "util/path.h" +#include "util/unicode.h" +#include "util/vecstream.h" +#include "util/xmlfile.h" -#include <ctype.h> +#include <cctype> #include <algorithm> #include <cmath> #include <cstddef> #include <cstdio> #include <cstring> #include <iomanip> +#include <limits> #include <locale> #include <sstream> #include <stdexcept> @@ -32,15 +40,29 @@ #include <type_traits> #include <utility> +#define LOG_GROUP_BOUNDS_RESOLUTION (1U << 1) +#define LOG_INTERACTIVE_ITEMS (1U << 2) +#define LOG_DISK_DRAW (1U << 3) +#define LOG_IMAGE_LOAD (1U << 4) + +//#define VERBOSE (LOG_GROUP_BOUNDS_RESOLUTION | LOG_INTERACTIVE_ITEMS | LOG_DISK_DRAW | LOG_IMAGE_LOAD) +#define LOG_OUTPUT_FUNC osd_printf_verbose +#include "logmacro.h" + /*************************************************************************** STANDARD LAYOUTS ***************************************************************************/ +#include "layout/generic.h" + // screenless layouts #include "noscreens.lh" +// single screen layouts +#include "monitors.lh" + // dual screen layouts #include "dualhsxs.lh" #include "dualhovu.lh" @@ -68,14 +90,12 @@ enum LINE_CAP_END = 2 }; -std::locale const f_portable_locale("C"); - constexpr layout_group::transform identity_transform{{ {{ 1.0F, 0.0F, 0.0F }}, {{ 0.0F, 1.0F, 0.0F }}, {{ 0.0F, 0.0F, 1.0F }} }}; //************************************************************************** -// INLINE HELPERS +// HELPERS //************************************************************************** inline void render_bounds_transform(render_bounds &bounds, layout_group::transform const &trans) @@ -87,9 +107,27 @@ inline void render_bounds_transform(render_bounds &bounds, layout_group::transfo (bounds.x1 * trans[1][0]) + (bounds.y1 * trans[1][1]) + trans[1][2] }; } -constexpr render_color render_color_multiply(render_color const &x, render_color const &y) +inline void alpha_blend(u32 &dest, u32 a, u32 r, u32 g, u32 b, u32 inva) { - return render_color{ x.a * y.a, x.r * y.r, x.g * y.g, x.b * y.b }; + rgb_t const dpix(dest); + u32 const da(dpix.a()); + u32 const finala((a * 255) + (da * inva)); + u32 const finalr(r + (u32(dpix.r()) * da * inva)); + u32 const finalg(g + (u32(dpix.g()) * da * inva)); + u32 const finalb(b + (u32(dpix.b()) * da * inva)); + dest = rgb_t(finala / 255, finalr / finala, finalg / finala, finalb / finala); +} + +inline void alpha_blend(u32 &dest, render_color const &c, float fill) +{ + u32 const a(c.a * fill * 255.0F); + if (a) + { + u32 const r(u32(c.r * (255.0F * 255.0F)) * a); + u32 const g(u32(c.g * (255.0F * 255.0F)) * a); + u32 const b(u32(c.b * (255.0F * 255.0F)) * a); + alpha_blend(dest, a, r, g, b, 255 - a); + } } @@ -104,7 +142,7 @@ class layout_reference_error : public std::out_of_range { using std::out_of_rang } // anonymous namespace -namespace emu { namespace render { namespace detail { +namespace emu::render::detail { class layout_environment { @@ -117,6 +155,16 @@ private: , m_text(std::move(t)) , m_text_valid(true) { } + entry(std::string &&name, std::string_view t) + : m_name(std::move(name)) + , m_text(t) + , m_text_valid(true) + { } + entry(std::string &&name, const char *t) + : m_name(std::move(name)) + , m_text(t) + , m_text_valid(true) + { } entry(std::string &&name, s64 i) : m_name(std::move(name)) , m_int(i) @@ -177,12 +225,18 @@ private: { if (m_float_valid) { - m_text = std::to_string(m_float); + std::ostringstream stream; + stream.imbue(std::locale::classic()); + stream << m_float; + m_text = std::move(stream).str(); m_text_valid = true; } else if (m_int_valid) { - m_text = std::to_string(m_int); + std::ostringstream stream; + stream.imbue(std::locale::classic()); + stream << m_int; + m_text = std::move(stream).str(); m_text_valid = true; } } @@ -204,7 +258,7 @@ private: if (m_text_valid && !m_float_valid) { std::istringstream stream(m_text); - stream.imbue(f_portable_locale); + stream.imbue(std::locale::classic()); if (m_text[0] == '$') { stream.get(); @@ -240,7 +294,7 @@ private: if (m_text_valid && !m_int_valid && !m_float_valid) { std::istringstream stream(m_text); - stream.imbue(f_portable_locale); + stream.imbue(std::locale::classic()); if (m_text[0] == '$') { stream.get(); @@ -299,7 +353,7 @@ private: if (m_text_valid && !m_int_valid) { std::istringstream stream(m_text); - stream.imbue(f_portable_locale); + stream.imbue(std::locale::classic()); if (m_text[0] == '$') { stream.get(); @@ -348,7 +402,20 @@ private: using entry_vector = std::vector<entry>; - template <typename T, typename U> + template <typename T> + void try_insert(std::string &&name, T &&value) + { + entry_vector::iterator const pos( + std::lower_bound( + m_entries.begin(), + m_entries.end(), + name, + [] (entry const &lhs, auto const &rhs) { return lhs.name() < rhs; })); + if ((m_entries.end() == pos) || (pos->name() != name)) + m_entries.emplace(pos, std::move(name), std::forward<T>(value)); + } + + template <typename T, typename U, typename = std::enable_if_t<std::is_constructible_v<std::string, T>>> void try_insert(T &&name, U &&value) { entry_vector::iterator const pos( @@ -358,7 +425,7 @@ private: name, [] (entry const &lhs, auto const &rhs) { return lhs.name() < rhs; })); if ((m_entries.end() == pos) || (pos->name() != name)) - m_entries.emplace(pos, std::forward<T>(name), std::forward<U>(value)); + m_entries.emplace(pos, std::string(name), std::forward<U>(value)); } template <typename T, typename U> @@ -386,7 +453,7 @@ private: try_insert("deviceshortname", device().shortname()); util::ovectorstream tmp; unsigned i(0U); - for (screen_device const &screen : screen_device_iterator(machine().root_device())) + for (screen_device const &screen : screen_device_enumerator(machine().root_device())) { std::pair<u64, u64> const physaspect(screen.physical_aspect()); s64 const w(screen.visible_area().width()), h(screen.visible_area().height()); @@ -395,33 +462,27 @@ private: tmp.seekp(0); util::stream_format(tmp, "scr%uphysicalxaspect", i); - tmp.put('\0'); - try_insert(&tmp.vec()[0], s64(physaspect.first)); + try_insert(util::buf_to_string_view(tmp), s64(physaspect.first)); tmp.seekp(0); util::stream_format(tmp, "scr%uphysicalyaspect", i); - tmp.put('\0'); - try_insert(&tmp.vec()[0], s64(physaspect.second)); + try_insert(util::buf_to_string_view(tmp), s64(physaspect.second)); tmp.seekp(0); util::stream_format(tmp, "scr%unativexaspect", i); - tmp.put('\0'); - try_insert(&tmp.vec()[0], xaspect); + try_insert(util::buf_to_string_view(tmp), xaspect); tmp.seekp(0); util::stream_format(tmp, "scr%unativeyaspect", i); - tmp.put('\0'); - try_insert(&tmp.vec()[0], yaspect); + try_insert(util::buf_to_string_view(tmp), yaspect); tmp.seekp(0); util::stream_format(tmp, "scr%uwidth", i); - tmp.put('\0'); - try_insert(&tmp.vec()[0], w); + try_insert(util::buf_to_string_view(tmp), w); tmp.seekp(0); util::stream_format(tmp, "scr%uheight", i); - tmp.put('\0'); - try_insert(&tmp.vec()[0], h); + try_insert(util::buf_to_string_view(tmp), h); ++i; } @@ -429,112 +490,124 @@ private: } } - entry *find_entry(char const *begin, char const *end) + entry *find_entry(std::string_view str) { cache_device_entries(); entry_vector::iterator const pos( std::lower_bound( m_entries.begin(), m_entries.end(), - std::make_pair(begin, end - begin), - [] (entry const &lhs, std::pair<char const *, std::ptrdiff_t> const &rhs) - { return 0 > std::strncmp(lhs.name().c_str(), rhs.first, rhs.second); })); - if ((m_entries.end() != pos) && (pos->name().length() == (end - begin)) && !std::strncmp(pos->name().c_str(), begin, end - begin)) + str, + [] (entry const &lhs, std::string_view const &rhs) { return lhs.name() < rhs; })); + if ((m_entries.end() != pos) && pos->name() == str) return &*pos; else - return m_next ? m_next->find_entry(begin, end) : nullptr; + return m_next ? m_next->find_entry(str) : nullptr; } - template <typename... T> - std::tuple<char const *, char const *, bool> get_variable_text(T &&... args) + std::pair<std::string_view, bool> get_variable_text(std::string_view str) { - entry *const found(find_entry(std::forward<T>(args)...)); + entry *const found(find_entry(str)); if (found) { - std::string const &text(found->get_text()); - char const *const begin(text.c_str()); - return std::make_tuple(begin, begin + text.length(), true); + return std::make_pair(std::string_view(found->get_text()), true); } else { - return std::make_tuple(nullptr, nullptr, false); + return std::make_pair(std::string_view(), false); } } - std::pair<char const *, char const *> expand(char const *begin, char const *end) + std::string_view expand(std::string_view str) { + constexpr char variable_start_char = '~'; + constexpr char variable_end_char = '~'; + // search for candidate variable references - char const *start(begin); - char const *pos(std::find_if(start, end, is_variable_start)); - while (pos != end) + std::string_view::size_type start(0); + for (std::string_view::size_type pos = str.find_first_of(variable_start_char); pos != std::string_view::npos; ) { - char const *const term(std::find_if(pos + 1, end, [] (char ch) { return !is_variable_char(ch); })); - if ((term == end) || !is_variable_end(*term)) + auto term = std::find_if_not(str.begin() + pos + 1, str.end(), is_variable_char); + if ((term == str.end()) || (*term != variable_end_char)) { // not a valid variable name - keep searching - pos = std::find_if(term, end, is_variable_start); + pos = str.find_first_of(variable_start_char, term - str.begin()); } else { // looks like a variable reference - try to look it up - std::tuple<char const *, char const *, bool> const text(get_variable_text(pos + 1, term)); - if (std::get<2>(text)) + std::pair<std::string_view, bool> const text(get_variable_text(str.substr(pos + 1, term - (str.begin() + pos + 1)))); + if (text.second) { // variable found - if (begin == start) + if (start == 0) m_buffer.seekp(0); - m_buffer.write(start, pos - start); - m_buffer.write(std::get<0>(text), std::get<1>(text) - std::get<0>(text)); - start = term + 1; - pos = std::find_if(start, end, is_variable_start); + assert(start < str.length()); + m_buffer.write(&str[start], pos - start); + m_buffer.write(text.first.data(), text.first.length()); + start = term - str.begin() + 1; + pos = str.find_first_of(variable_start_char, start); } else { // variable not found - move on - pos = std::find_if(pos + 1, end, is_variable_start); + pos = str.find_first_of(variable_start_char, pos + 1); } } } // short-circuit the case where no substitutions were made - if (start == begin) + if (start == 0) { - return std::make_pair(begin, end); + return str; } else { - m_buffer.write(start, pos - start); - m_buffer.put('\0'); - std::vector<char> const &vec(m_buffer.vec()); - if (vec.empty()) - return std::make_pair(nullptr, nullptr); - else - return std::make_pair(&vec[0], &vec[0] + vec.size() - 1); + if (start < str.length()) + m_buffer.write(&str[start], str.length() - start); + return util::buf_to_string_view(m_buffer); } } - std::pair<char const *, char const *> expand(char const *str) + static constexpr unsigned hex_prefix(std::string_view s) { - return expand(str, str + strlen(str)); + return ((0 != s.length()) && (s[0] == '$')) ? 1U : ((2 <= s.length()) && (s[0] == '0') && ((s[1] == 'x') || (s[1] == 'X'))) ? 2U : 0U; } - - std::string parameter_name(util::xml::data_node const &node) + static constexpr unsigned dec_prefix(std::string_view s) { - char const *const attrib(node.get_attribute_string("name", nullptr)); - if (!attrib) - throw layout_syntax_error("parameter lacks name attribute"); - std::pair<char const *, char const *> const expanded(expand(attrib)); - return std::string(expanded.first, expanded.second); + return ((0 != s.length()) && (s[0] == '#')) ? 1U : 0U; } - static constexpr bool is_variable_start(char ch) + int parse_int(std::string_view s, int defvalue) { - return '~' == ch; + std::istringstream stream; + stream.imbue(std::locale::classic()); + int result; + unsigned const hexprefix = hex_prefix(s); + if (hexprefix) + { + stream.str(std::string(s.substr(hexprefix))); + unsigned uvalue; + stream >> std::hex >> uvalue; + result = int(uvalue); + } + else + { + stream.str(std::string(s.substr(dec_prefix(s)))); + stream >> result; + } + + return stream ? result : defvalue; } - static constexpr bool is_variable_end(char ch) + + std::string parameter_name(util::xml::data_node const &node) { - return '~' == ch; + std::string const *const attrib(node.get_attribute_string_ptr("name")); + if (!attrib) + throw layout_syntax_error("parameter lacks name attribute"); + return std::string(expand(*attrib)); } + static constexpr bool is_variable_char(char ch) { return (('0' <= ch) && ('9' >= ch)) || (('A' <= ch) && ('Z' >= ch)) || (('a' <= ch) && ('z' >= ch)) || ('_' == ch); @@ -542,18 +615,37 @@ private: entry_vector m_entries; util::ovectorstream m_buffer; + std::shared_ptr<NSVGrasterizer> const m_svg_rasterizer; device_t &m_device; + char const *const m_search_path; + char const *const m_directory_name; layout_environment *const m_next = nullptr; bool m_cached = false; public: - explicit layout_environment(device_t &device) : m_device(device) { } - explicit layout_environment(layout_environment &next) : m_device(next.m_device), m_next(&next) { } + layout_environment(device_t &device, char const *searchpath, char const *dirname) + : m_svg_rasterizer(nsvgCreateRasterizer(), util::nsvg_deleter()) + , m_device(device) + , m_search_path(searchpath) + , m_directory_name(dirname) + { + } + explicit layout_environment(layout_environment &next) + : m_svg_rasterizer(next.m_svg_rasterizer) + , m_device(next.m_device) + , m_search_path(next.m_search_path) + , m_directory_name(next.m_directory_name) + , m_next(&next) + { + } layout_environment(layout_environment const &) = delete; - device_t &device() { return m_device; } - running_machine &machine() { return device().machine(); } - bool is_root_device() { return &device() == &machine().root_device(); } + device_t &device() const { return m_device; } + running_machine &machine() const { return device().machine(); } + bool is_root_device() const { return &device() == &machine().root_device(); } + char const *search_path() const { return m_search_path; } + char const *directory_name() const { return m_directory_name; } + std::shared_ptr<NSVGrasterizer> const &svg_rasterizer() const { return m_svg_rasterizer; } void set_parameter(std::string &&name, std::string &&value) { @@ -576,20 +668,19 @@ public: std::string name(parameter_name(node)); if (node.has_attribute("start") || node.has_attribute("increment") || node.has_attribute("lshift") || node.has_attribute("rshift")) throw layout_syntax_error("start/increment/lshift/rshift attributes are only allowed for repeat parameters"); - char const *const value(node.get_attribute_string("value", nullptr)); + std::string const *const value(node.get_attribute_string_ptr("value")); if (!value) throw layout_syntax_error("parameter lacks value attribute"); // expand value and stash - std::pair<char const *, char const *> const expanded(expand(value)); - set(std::move(name), std::string(expanded.first, expanded.second)); + set(std::move(name), std::string(expand(*value))); } void set_repeat_parameter(util::xml::data_node const &node, bool init) { // two types are allowed here - static value, and start/increment/lshift/rshift std::string name(parameter_name(node)); - char const *const start(node.get_attribute_string("start", nullptr)); + std::string const *const start(node.get_attribute_string_ptr("start")); if (start) { // simple validity checks @@ -603,15 +694,15 @@ public: // increment is more complex - it may be an integer or a floating-point number s64 intincrement(0); double floatincrement(0); - char const *const increment(node.get_attribute_string("increment", nullptr)); + std::string const *const increment(node.get_attribute_string_ptr("increment")); if (increment) { - std::pair<char const *, char const *> const expanded(expand(increment)); - unsigned const hexprefix((expanded.first[0] == '$') ? 1U : ((expanded.first[0] == '0') && ((expanded.first[1] == 'x') || (expanded.first[1] == 'X'))) ? 2U : 0U); - unsigned const decprefix((expanded.first[0] == '#') ? 1U : 0U); - bool const floatchars(std::find_if(expanded.first, expanded.second, [] (char ch) { return ('.' == ch) || ('e' == ch) || ('E' == ch); }) != expanded.second); - std::istringstream stream(std::string(expanded.first + hexprefix + decprefix, expanded.second)); - stream.imbue(f_portable_locale); + std::string_view const expanded(expand(*increment)); + unsigned const hexprefix(hex_prefix(expanded)); + unsigned const decprefix(dec_prefix(expanded)); + bool const floatchars(expanded.find_first_of(".eE") != std::string_view::npos); + std::istringstream stream(std::string(expanded.substr(hexprefix + decprefix))); + stream.imbue(std::locale::classic()); if (!hexprefix && !decprefix && floatchars) { stream >> floatincrement; @@ -644,11 +735,10 @@ public: if ((m_entries.end() != pos) && (pos->name() == name)) throw layout_syntax_error("generator parameters must be defined exactly once per scope"); - std::pair<char const *, char const *> const expanded(expand(start)); if (floatincrement) - m_entries.emplace(pos, std::move(name), std::string(expanded.first, expanded.second), floatincrement, lshift - rshift); + m_entries.emplace(pos, std::move(name), std::string(expand(*start)), floatincrement, lshift - rshift); else - m_entries.emplace(pos, std::move(name), std::string(expanded.first, expanded.second), intincrement, lshift - rshift); + m_entries.emplace(pos, std::move(name), std::string(expand(*start)), intincrement, lshift - rshift); } } else if (node.has_attribute("increment") || node.has_attribute("lshift") || node.has_attribute("rshift")) @@ -657,10 +747,9 @@ public: } else { - char const *const value(node.get_attribute_string("value", nullptr)); + std::string const *const value(node.get_attribute_string_ptr("value")); if (!value) throw layout_syntax_error("parameter lacks value attribute"); - std::pair<char const *, char const *> const expanded(expand(value)); entry_vector::iterator const pos( std::lower_bound( m_entries.begin(), @@ -668,11 +757,11 @@ public: name, [] (entry const &lhs, auto const &rhs) { return lhs.name() < rhs; })); if ((m_entries.end() == pos) || (pos->name() != name)) - m_entries.emplace(pos, std::move(name), std::string(expanded.first, expanded.second)); + m_entries.emplace(pos, std::move(name), std::string(expand(*value))); else if (pos->is_generator()) throw layout_syntax_error("generator parameters must be defined exactly once per scope"); else - pos->set(std::string(expanded.first, expanded.second)); + pos->set(std::string(expand(*value))); } } @@ -692,95 +781,98 @@ public: m_entries.end()); } - char const *get_attribute_string(util::xml::data_node const &node, char const *name, char const *defvalue) + std::string_view get_attribute_string(util::xml::data_node const &node, char const *name, std::string_view defvalue = std::string_view()) { - char const *const attrib(node.get_attribute_string(name, nullptr)); - return attrib ? expand(attrib).first : defvalue; + std::string const *const attrib(node.get_attribute_string_ptr(name)); + return attrib ? expand(*attrib) : defvalue; + } + + std::string get_attribute_subtag(util::xml::data_node const &node, char const *name) + { + std::string const *const attrib(node.get_attribute_string_ptr(name)); + return attrib ? device().subtag(expand(*attrib)) : std::string(); } int get_attribute_int(util::xml::data_node const &node, const char *name, int defvalue) { - char const *const attrib(node.get_attribute_string(name, nullptr)); + std::string const *const attrib(node.get_attribute_string_ptr(name)); if (!attrib) return defvalue; // similar to what XML nodes do - std::pair<char const *, char const *> const expanded(expand(attrib)); - std::istringstream stream; - stream.imbue(f_portable_locale); - int result; - if (expanded.first[0] == '$') - { - stream.str(std::string(expanded.first + 1, expanded.second)); - unsigned uvalue; - stream >> std::hex >> uvalue; - result = int(uvalue); - } - else if ((expanded.first[0] == '0') && ((expanded.first[1] == 'x') || (expanded.first[1] == 'X'))) - { - stream.str(std::string(expanded.first + 2, expanded.second)); - unsigned uvalue; - stream >> std::hex >> uvalue; - result = int(uvalue); - } - else if (expanded.first[0] == '#') - { - stream.str(std::string(expanded.first + 1, expanded.second)); - stream >> result; - } - else - { - stream.str(std::string(expanded.first, expanded.second)); - stream >> result; - } - - return stream ? result : defvalue; + return parse_int(expand(*attrib), defvalue); } float get_attribute_float(util::xml::data_node const &node, char const *name, float defvalue) { - char const *const attrib(node.get_attribute_string(name, nullptr)); + std::string const *const attrib(node.get_attribute_string_ptr(name)); if (!attrib) return defvalue; // similar to what XML nodes do - std::pair<char const *, char const *> const expanded(expand(attrib)); - std::istringstream stream(std::string(expanded.first, expanded.second)); - stream.imbue(f_portable_locale); + std::istringstream stream(std::string(expand(*attrib))); + stream.imbue(std::locale::classic()); float result; return (stream >> result) ? result : defvalue; } + bool get_attribute_bool(util::xml::data_node const &node, char const *name, bool defvalue) + { + std::string const *const attrib(node.get_attribute_string_ptr(name)); + if (!attrib) + return defvalue; + + // first try yes/no strings + std::string_view const expanded(expand(*attrib)); + if ("yes" == expanded || "true" == expanded) + return true; + if ("no" == expanded || "false" == expanded) + return false; + + // fall back to integer parsing + return parse_int(expanded, defvalue ? 1 : 0) != 0; + } + void parse_bounds(util::xml::data_node const *node, render_bounds &result) { - // default to unit rectangle if (!node) { + // default to unit rectangle result.x0 = result.y0 = 0.0F; result.x1 = result.y1 = 1.0F; } else { - // parse attributes + // horizontal position/size if (node->has_attribute("left")) { - // left/right/top/bottom format result.x0 = get_attribute_float(*node, "left", 0.0F); result.x1 = get_attribute_float(*node, "right", 1.0F); - result.y0 = get_attribute_float(*node, "top", 0.0F); - result.y1 = get_attribute_float(*node, "bottom", 1.0F); } - else if (node->has_attribute("x")) + else + { + float const width = get_attribute_float(*node, "width", 1.0F); + if (node->has_attribute("xc")) + result.x0 = get_attribute_float(*node, "xc", 0.0F) - (width / 2.0F); + else + result.x0 = get_attribute_float(*node, "x", 0.0F); + result.x1 = result.x0 + width; + } + + // vertical position/size + if (node->has_attribute("top")) { - // x/y/width/height format - result.x0 = get_attribute_float(*node, "x", 0.0F); - result.x1 = result.x0 + get_attribute_float(*node, "width", 1.0F); - result.y0 = get_attribute_float(*node, "y", 0.0F); - result.y1 = result.y0 + get_attribute_float(*node, "height", 1.0F); + result.y0 = get_attribute_float(*node, "top", 0.0F); + result.y1 = get_attribute_float(*node, "bottom", 1.0F); } else { - throw layout_syntax_error("bounds element requires either left or x attribute"); + float const height = get_attribute_float(*node, "height", 1.0F); + if (node->has_attribute("yc")) + result.y0 = get_attribute_float(*node, "yc", 0.0F) - (height / 2.0F); + else + result.y0 = get_attribute_float(*node, "y", 0.0F); + result.y1 = result.y0 + height; } // check for errors @@ -826,25 +918,335 @@ public: case 270: result = ROT270; break; default: throw layout_syntax_error(util::string_format("invalid rotate attribute %d", rotate)); } - if (!std::strcmp("yes", get_attribute_string(*node, "swapxy", "no"))) + if (get_attribute_bool(*node, "swapxy", false)) result ^= ORIENTATION_SWAP_XY; - if (!std::strcmp("yes", get_attribute_string(*node, "flipx", "no"))) + if (get_attribute_bool(*node, "flipx", false)) result ^= ORIENTATION_FLIP_X; - if (!std::strcmp("yes", get_attribute_string(*node, "flipy", "no"))) + if (get_attribute_bool(*node, "flipy", false)) result ^= ORIENTATION_FLIP_Y; return result; } }; -} } } // namespace emu::render::detail + +class view_environment : public layout_environment +{ +private: + view_environment *const m_next_view = nullptr; + char const *const m_name; + u32 const m_visibility_mask = 0U; + unsigned m_next_visibility_bit = 0U; + +public: + view_environment(layout_environment &next, char const *name) + : layout_environment(next) + , m_name(name) + { + } + view_environment(view_environment &next, bool visibility) + : layout_environment(next) + , m_next_view(&next) + , m_name(next.m_name) + , m_visibility_mask(next.m_visibility_mask | (u32(visibility ? 1 : 0) << next.m_next_visibility_bit)) + , m_next_visibility_bit(next.m_next_visibility_bit + (visibility ? 1 : 0)) + { + if (32U < m_next_visibility_bit) + throw layout_syntax_error(util::string_format("view '%s' contains too many visibility toggles", m_name)); + } + ~view_environment() + { + if (m_next_view) + m_next_view->m_next_visibility_bit = m_next_visibility_bit; + } + + u32 visibility_mask() const { return m_visibility_mask; } +}; + +} // namespace emu::render::detail +namespace { -//************************************************************************** -// GLOBAL VARIABLES -//************************************************************************** +bool add_bounds_step(emu::render::detail::layout_environment &env, emu::render::detail::bounds_vector &steps, util::xml::data_node const &node) +{ + int const state(env.get_attribute_int(node, "state", 0)); + auto const pos( + std::lower_bound( + steps.begin(), + steps.end(), + state, + [] (emu::render::detail::bounds_step const &lhs, int rhs) { return lhs.state < rhs; })); + if ((steps.end() != pos) && (state == pos->state)) + return false; + + auto &ins(*steps.emplace(pos, emu::render::detail::bounds_step{ state, { 0.0F, 0.0F, 0.0F, 0.0F }, { 0.0F, 0.0F, 0.0F, 0.0F } })); + env.parse_bounds(&node, ins.bounds); + return true; +} + +void set_bounds_deltas(emu::render::detail::bounds_vector &steps) +{ + if (steps.empty()) + { + steps.emplace_back(emu::render::detail::bounds_step{ 0, { 0.0F, 0.0F, 1.0F, 1.0F }, { 0.0F, 0.0F, 0.0F, 0.0F } }); + } + else + { + auto i(steps.begin()); + auto j(i); + while (steps.end() != ++j) + { + assert(j->state > i->state); + + i->delta.x0 = (j->bounds.x0 - i->bounds.x0) / (j->state - i->state); + i->delta.x1 = (j->bounds.x1 - i->bounds.x1) / (j->state - i->state); + i->delta.y0 = (j->bounds.y0 - i->bounds.y0) / (j->state - i->state); + i->delta.y1 = (j->bounds.y1 - i->bounds.y1) / (j->state - i->state); + + i = j; + } + } +} + +void normalize_bounds(emu::render::detail::bounds_vector &steps, float x0, float y0, float xoffs, float yoffs, float xscale, float yscale) +{ + auto i(steps.begin()); + i->bounds.x0 = x0 + (i->bounds.x0 - xoffs) * xscale; + i->bounds.x1 = x0 + (i->bounds.x1 - xoffs) * xscale; + i->bounds.y0 = y0 + (i->bounds.y0 - yoffs) * yscale; + i->bounds.y1 = y0 + (i->bounds.y1 - yoffs) * yscale; + + auto j(i); + while (steps.end() != ++j) + { + j->bounds.x0 = x0 + (j->bounds.x0 - xoffs) * xscale; + j->bounds.x1 = x0 + (j->bounds.x1 - xoffs) * xscale; + j->bounds.y0 = y0 + (j->bounds.y0 - yoffs) * yscale; + j->bounds.y1 = y0 + (j->bounds.y1 - yoffs) * yscale; + + i->delta.x0 = (j->bounds.x0 - i->bounds.x0) / (j->state - i->state); + i->delta.x1 = (j->bounds.x1 - i->bounds.x1) / (j->state - i->state); + i->delta.y0 = (j->bounds.y0 - i->bounds.y0) / (j->state - i->state); + i->delta.y1 = (j->bounds.y1 - i->bounds.y1) / (j->state - i->state); + + i = j; + } +} + +render_bounds accumulate_bounds(emu::render::detail::bounds_vector const &steps) +{ + auto i(steps.begin()); + render_bounds result(i->bounds); + while (steps.end() != ++i) + result |= i->bounds; + return result; +} + +inline render_bounds interpolate_bounds(emu::render::detail::bounds_vector const &steps, int state) +{ + auto pos( + std::lower_bound( + steps.begin(), + steps.end(), + state, + [] (emu::render::detail::bounds_step const &lhs, int rhs) { return lhs.state < rhs; })); + if (steps.begin() == pos) + { + return pos->bounds; + } + else + { + --pos; + render_bounds result(pos->bounds); + result.x0 += pos->delta.x0 * (state - pos->state); + result.x1 += pos->delta.x1 * (state - pos->state); + result.y0 += pos->delta.y0 * (state - pos->state); + result.y1 += pos->delta.y1 * (state - pos->state); + return result; + } +} + + +bool add_color_step(emu::render::detail::layout_environment &env, emu::render::detail::color_vector &steps, util::xml::data_node const &node) +{ + int const state(env.get_attribute_int(node, "state", 0)); + auto const pos( + std::lower_bound( + steps.begin(), + steps.end(), + state, + [] (emu::render::detail::color_step const &lhs, int rhs) { return lhs.state < rhs; })); + if ((steps.end() != pos) && (state == pos->state)) + return false; + + steps.emplace(pos, emu::render::detail::color_step{ state, env.parse_color(&node), { 0.0F, 0.0F, 0.0F, 0.0F } }); + return true; +} + +void set_color_deltas(emu::render::detail::color_vector &steps) +{ + if (steps.empty()) + { + steps.emplace_back(emu::render::detail::color_step{ 0, { 1.0F, 1.0F, 1.0F, 1.0F }, { 0.0F, 0.0F, 0.0F, 0.0F } }); + } + else + { + auto i(steps.begin()); + auto j(i); + while (steps.end() != ++j) + { + assert(j->state > i->state); + + i->delta.a = (j->color.a - i->color.a) / (j->state - i->state); + i->delta.r = (j->color.r - i->color.r) / (j->state - i->state); + i->delta.g = (j->color.g - i->color.g) / (j->state - i->state); + i->delta.b = (j->color.b - i->color.b) / (j->state - i->state); + + i = j; + } + } +} + +inline render_color interpolate_color(emu::render::detail::color_vector const &steps, int state) +{ + auto pos( + std::lower_bound( + steps.begin(), + steps.end(), + state, + [] (emu::render::detail::color_step const &lhs, int rhs) { return lhs.state < rhs; })); + if (steps.begin() == pos) + { + return pos->color; + } + else + { + --pos; + render_color result(pos->color); + result.a += pos->delta.a * (state - pos->state); + result.r += pos->delta.r * (state - pos->state); + result.g += pos->delta.g * (state - pos->state); + result.b += pos->delta.b * (state - pos->state); + return result; + } +} + + +unsigned get_state_shift(ioport_value mask) +{ + // get shift to right-align LSB + unsigned result(0U); + while (mask && !BIT(mask, 0)) + { + ++result; + mask >>= 1; + } + return result; +} + +std::string make_child_output_tag( + emu::render::detail::view_environment &env, + util::xml::data_node const &itemnode, + char const *child) +{ + util::xml::data_node const *const childnode(itemnode.get_child(child)); + if (childnode) + return std::string(env.get_attribute_string(*childnode, "name")); + else + return std::string(); +} + +std::string make_child_input_tag( + emu::render::detail::view_environment &env, + util::xml::data_node const &itemnode, + char const *child) +{ + util::xml::data_node const *const childnode(itemnode.get_child(child)); + return childnode ? env.get_attribute_subtag(*childnode, "inputtag") : std::string(); +} + +ioport_value make_child_mask( + emu::render::detail::view_environment &env, + util::xml::data_node const &itemnode, + char const *child) +{ + util::xml::data_node const *const childnode(itemnode.get_child(child)); + return childnode ? env.get_attribute_int(*childnode, "mask", ~ioport_value(0)) : ~ioport_value(0); +} + +bool make_child_wrap( + emu::render::detail::view_environment &env, + util::xml::data_node const &itemnode, + char const *child) +{ + util::xml::data_node const *const childnode(itemnode.get_child(child)); + return childnode ? env.get_attribute_bool(*childnode, "wrap", false) : false; +} + +float make_child_size( + emu::render::detail::view_environment &env, + util::xml::data_node const &itemnode, + char const *child) +{ + util::xml::data_node const *const childnode(itemnode.get_child(child)); + return std::clamp(childnode ? env.get_attribute_float(*childnode, "size", 1.0f) : 1.0f, 0.01f, 1.0f); +} + +ioport_value make_child_min( + emu::render::detail::view_environment &env, + util::xml::data_node const &itemnode, + char const *child) +{ + util::xml::data_node const *const childnode(itemnode.get_child(child)); + return childnode ? env.get_attribute_int(*childnode, "min", ioport_value(0)) : ioport_value(0); +} + +ioport_value make_child_max( + emu::render::detail::view_environment &env, + util::xml::data_node const &itemnode, + char const *child, + ioport_value mask) +{ + util::xml::data_node const *const childnode(itemnode.get_child(child)); + ioport_value const dflt(mask >> get_state_shift(mask)); + return childnode ? env.get_attribute_int(*childnode, "max", dflt) : dflt; +} + +std::string make_input_tag( + emu::render::detail::view_environment &env, + util::xml::data_node const &itemnode) +{ + return env.get_attribute_subtag(itemnode, "inputtag"); +} + +int get_blend_mode(emu::render::detail::view_environment &env, util::xml::data_node const &itemnode) +{ + // see if there's a blend mode attribute + std::string const *const mode(itemnode.get_attribute_string_ptr("blend")); + if (mode) + { + if (*mode == "none") + return BLENDMODE_NONE; + else if (*mode == "alpha") + return BLENDMODE_ALPHA; + else if (*mode == "multiply") + return BLENDMODE_RGB_MULTIPLY; + else if (*mode == "add") + return BLENDMODE_ADD; + else + throw layout_syntax_error(util::string_format("unknown blend mode %s", *mode)); + } + + // fall back to implicit blend mode based on element type + if (!strcmp(itemnode.get_name(), "screen")) + return -1; // magic number recognised by render.cpp to allow per-element blend mode + else if (!strcmp(itemnode.get_name(), "overlay")) + return BLENDMODE_RGB_MULTIPLY; + else + return BLENDMODE_ALPHA; +} -render_screen_list render_target::s_empty_screen_list; +} // anonymous namespace @@ -855,13 +1257,9 @@ render_screen_list render_target::s_empty_screen_list; layout_element::make_component_map const layout_element::s_make_component{ { "image", &make_component<image_component> }, { "text", &make_component<text_component> }, - { "dotmatrix", &make_dotmatrix_component<8> }, - { "dotmatrix5dot", &make_dotmatrix_component<5> }, - { "dotmatrixdot", &make_dotmatrix_component<1> }, { "simplecounter", &make_component<simplecounter_component> }, { "reel", &make_component<reel_component> }, { "led7seg", &make_component<led7seg_component> }, - { "led8seg_gts1", &make_component<led8seg_gts1_component> }, { "led14seg", &make_component<led14seg_component> }, { "led14segsc", &make_component<led14segsc_component> }, { "led16seg", &make_component<led16seg_component> }, @@ -874,14 +1272,13 @@ layout_element::make_component_map const layout_element::s_make_component{ // layout_element - constructor //------------------------------------------------- -layout_element::layout_element(environment &env, util::xml::data_node const &elemnode, const char *dirname) +layout_element::layout_element(environment &env, util::xml::data_node const &elemnode) : m_machine(env.machine()) - , m_defstate(0) - , m_maxstate(0) + , m_defstate(env.get_attribute_int(elemnode, "defstate", -1)) + , m_statemask(0) + , m_foldhigh(false) + , m_invalidated(false) { - // get the default state - m_defstate = env.get_attribute_int(elemnode, "defstate", -1); - // parse components in order bool first = true; render_bounds bounds = { 0.0, 0.0, 0.0, 0.0 }; @@ -892,17 +1289,19 @@ layout_element::layout_element(environment &env, util::xml::data_node const &ele throw layout_syntax_error(util::string_format("unknown element component %s", compnode->get_name())); // insert the new component into the list - component const &newcomp(**m_complist.emplace(m_complist.end(), make_func->second(env, *compnode, dirname))); + component const &newcomp(*m_complist.emplace_back(make_func->second(env, *compnode))); // accumulate bounds if (first) - bounds = newcomp.bounds(); + bounds = newcomp.overall_bounds(); else - union_render_bounds(bounds, newcomp.bounds()); + bounds |= newcomp.overall_bounds(); first = false; // determine the maximum state - m_maxstate = std::max(m_maxstate, newcomp.maxstate()); + std::pair<int, bool> const wrap(newcomp.statewrap()); + m_statemask |= wrap.first; + m_foldhigh = m_foldhigh || wrap.second; } if (!m_complist.empty()) @@ -910,8 +1309,8 @@ layout_element::layout_element(environment &env, util::xml::data_node const &ele // determine the scale/offset for normalization float xoffs = bounds.x0; float yoffs = bounds.y0; - float xscale = 1.0f / (bounds.x1 - bounds.x0); - float yscale = 1.0f / (bounds.y1 - bounds.y0); + float xscale = 1.0F / (bounds.x1 - bounds.x0); + float yscale = 1.0F / (bounds.y1 - bounds.y0); // normalize all the component bounds for (component::ptr const &curcomp : m_complist) @@ -919,7 +1318,7 @@ layout_element::layout_element(environment &env, util::xml::data_node const &ele } // allocate an array of element textures for the states - m_elemtex.resize(m_maxstate + 1); + m_elemtex.resize((m_statemask + 1) << (m_foldhigh ? 1 : 0)); } @@ -943,7 +1342,7 @@ layout_element::~layout_element() layout_group::layout_group(util::xml::data_node const &groupnode) : m_groupnode(groupnode) - , m_bounds{ 0.0f, 0.0f, 0.0f, 0.0f } + , m_bounds{ 0.0F, 0.0F, 0.0F, 0.0F } , m_bounds_resolved(false) { } @@ -1051,17 +1450,18 @@ void layout_group::resolve_bounds(environment &env, group_map &groupmap, std::ve // a wild loop appears! std::ostringstream path; for (layout_group const *const group : seen) - path << ' ' << group->m_groupnode.get_attribute_string("name", nullptr); - path << ' ' << m_groupnode.get_attribute_string("name", nullptr); + path << ' ' << group->m_groupnode.get_attribute_string("name", ""); + path << ' ' << m_groupnode.get_attribute_string("name", ""); throw layout_syntax_error(util::string_format("recursively nested groups %s", path.str())); } seen.push_back(this); if (!m_bounds_resolved) { - set_render_bounds_xy(m_bounds, 0.0F, 0.0F, 1.0F, 1.0F); + m_bounds.set_xy(0.0F, 0.0F, 1.0F, 1.0F); environment local(env); - resolve_bounds(local, m_groupnode, groupmap, seen, true, false, true); + bool empty(true); + resolve_bounds(local, m_groupnode, groupmap, seen, empty, false, false, true); } seen.pop_back(); } @@ -1071,10 +1471,13 @@ void layout_group::resolve_bounds( util::xml::data_node const &parentnode, group_map &groupmap, std::vector<layout_group const *> &seen, - bool empty, + bool &empty, + bool vistoggle, bool repeat, bool init) { + LOGMASKED(LOG_GROUP_BOUNDS_RESOLUTION, "Group '%s' resolve bounds empty=%s vistoggle=%s repeat=%s init=%s\n", + parentnode.get_attribute_string("name", ""), empty, vistoggle, repeat, init); bool envaltered(false); bool unresolved(true); for (util::xml::data_node const *itemnode = parentnode.get_first_child(); !m_bounds_resolved && itemnode; itemnode = itemnode->get_next_sibling()) @@ -1090,6 +1493,7 @@ void layout_group::resolve_bounds( envaltered = true; if (!unresolved) { + LOGMASKED(LOG_GROUP_BOUNDS_RESOLUTION, "Environment altered%s, unresolving groups\n", envaltered ? " again" : ""); unresolved = true; for (group_map::value_type &group : groupmap) group.second.set_bounds_unresolved(); @@ -1108,12 +1512,26 @@ void layout_group::resolve_bounds( !strcmp(itemnode->get_name(), "marquee")) { render_bounds itembounds; - env.parse_bounds(itemnode->get_child("bounds"), itembounds); + util::xml::data_node const *boundsnode = itemnode->get_child("bounds"); + env.parse_bounds(boundsnode, itembounds); + while (boundsnode) + { + boundsnode = boundsnode->get_next_sibling("bounds"); + if (boundsnode) + { + render_bounds b; + env.parse_bounds(boundsnode, b); + itembounds |= b; + } + } if (empty) m_bounds = itembounds; else - union_render_bounds(m_bounds, itembounds); + m_bounds |= itembounds; empty = false; + LOGMASKED(LOG_GROUP_BOUNDS_RESOLUTION, "Accumulate item bounds (%s %s %s %s) -> (%s %s %s %s)\n", + itembounds.x0, itembounds.y0, itembounds.x1, itembounds.y1, + m_bounds.x0, m_bounds.y0, m_bounds.x1, m_bounds.y1); } else if (!strcmp(itemnode->get_name(), "group")) { @@ -1125,14 +1543,18 @@ void layout_group::resolve_bounds( if (empty) m_bounds = itembounds; else - union_render_bounds(m_bounds, itembounds); + m_bounds |= itembounds; empty = false; + LOGMASKED(LOG_GROUP_BOUNDS_RESOLUTION, "Accumulate group '%s' reference explicit bounds (%s %s %s %s) -> (%s %s %s %s)\n", + itemnode->get_attribute_string("ref", ""), + itembounds.x0, itembounds.y0, itembounds.x1, itembounds.y1, + m_bounds.x0, m_bounds.y0, m_bounds.x1, m_bounds.y1); } else { - char const *ref(env.get_attribute_string(*itemnode, "ref", nullptr)); - if (!ref) - throw layout_syntax_error("nested group must have ref attribute"); + std::string const ref(env.get_attribute_string(*itemnode, "ref")); + if (ref.empty()) + throw layout_syntax_error("nested group must have non-empty ref attribute"); group_map::iterator const found(groupmap.find(ref)); if (groupmap.end() == found) @@ -1149,8 +1571,13 @@ void layout_group::resolve_bounds( if (empty) m_bounds = itembounds; else - union_render_bounds(m_bounds, itembounds); + m_bounds |= itembounds; empty = false; + unresolved = false; + LOGMASKED(LOG_GROUP_BOUNDS_RESOLUTION, "Accumulate group '%s' reference computed bounds (%s %s %s %s) -> (%s %s %s %s)\n", + itemnode->get_attribute_string("ref", ""), + itembounds.x0, itembounds.y0, itembounds.x1, itembounds.y1, + m_bounds.x0, m_bounds.y0, m_bounds.x1, m_bounds.y1); } } else if (!strcmp(itemnode->get_name(), "repeat")) @@ -1161,10 +1588,17 @@ void layout_group::resolve_bounds( environment local(env); for (int i = 0; !m_bounds_resolved && (count > i); ++i) { - resolve_bounds(local, *itemnode, groupmap, seen, empty, true, !i); + resolve_bounds(local, *itemnode, groupmap, seen, empty, false, true, !i); local.increment_parameters(); } } + else if (!strcmp(itemnode->get_name(), "collection")) + { + if (!itemnode->has_attribute("name")) + throw layout_syntax_error("collection must have name attribute"); + environment local(env); + resolve_bounds(local, *itemnode, groupmap, seen, empty, true, false, true); + } else { throw layout_syntax_error(util::string_format("unknown group element %s", itemnode->get_name())); @@ -1173,14 +1607,19 @@ void layout_group::resolve_bounds( if (envaltered && !unresolved) { + LOGMASKED(LOG_GROUP_BOUNDS_RESOLUTION, "Environment was altered, marking groups unresolved\n"); bool const resolved(m_bounds_resolved); for (group_map::value_type &group : groupmap) group.second.set_bounds_unresolved(); m_bounds_resolved = resolved; } - if (!repeat) + if (!vistoggle && !repeat) + { + LOGMASKED(LOG_GROUP_BOUNDS_RESOLUTION, "Marking group '%s' bounds resolved\n", + parentnode.get_attribute_string("name", "")); m_bounds_resolved = true; + } } @@ -1193,8 +1632,12 @@ void layout_group::resolve_bounds( render_texture *layout_element::state_texture(int state) { - assert(state <= m_maxstate); - if (m_elemtex[state].m_texture == nullptr) + if (m_foldhigh && (state & ~m_statemask)) + state = (state & m_statemask) | (((m_statemask << 1) | 1) & ~m_statemask); + else + state &= m_statemask; + assert(m_elemtex.size() > state); + if (!m_elemtex[state].m_texture) { m_elemtex[state].m_element = this; m_elemtex[state].m_state = state; @@ -1205,6 +1648,48 @@ render_texture *layout_element::state_texture(int state) //------------------------------------------------- +// set_draw_callback - set handler called after +// drawing components +//------------------------------------------------- + +void layout_element::set_draw_callback(draw_delegate &&handler) +{ + m_draw = std::move(handler); +} + + +//------------------------------------------------- +// preload - perform expensive loading upfront +// for all components +//------------------------------------------------- + +void layout_element::preload() +{ + for (component::ptr const &curcomp : m_complist) + curcomp->preload(machine()); +} + + +//------------------------------------------------- +// prepare - perform additional tasks before +// drawing a frame +//------------------------------------------------- + +void layout_element::prepare() +{ + if (m_invalidated) + { + m_invalidated = false; + for (texture &tex : m_elemtex) + { + machine().render().texture_free(tex.m_texture); + tex.m_texture = nullptr; + } + } +} + + +//------------------------------------------------- // element_scale - scale an element by rendering // all the components at the appropriate // resolution @@ -1212,23 +1697,18 @@ render_texture *layout_element::state_texture(int state) void layout_element::element_scale(bitmap_argb32 &dest, bitmap_argb32 &source, const rectangle &sbounds, void *param) { - texture *elemtex = (texture *)param; + texture const &elemtex(*reinterpret_cast<texture const *>(param)); - // iterate over components that are part of the current state - for (auto &curcomp : elemtex->m_element->m_complist) - if (curcomp->state() == -1 || curcomp->state() == elemtex->m_state) - { - // get the local scaled bounds - rectangle bounds( - render_round_nearest(curcomp->bounds().x0 * dest.width()), - render_round_nearest(curcomp->bounds().x1 * dest.width()), - render_round_nearest(curcomp->bounds().y0 * dest.height()), - render_round_nearest(curcomp->bounds().y1 * dest.height())); - bounds &= dest.cliprect(); + // draw components that are visible in the current state + for (auto const &curcomp : elemtex.m_element->m_complist) + { + if ((elemtex.m_state & curcomp->statemask()) == curcomp->stateval()) + curcomp->draw(elemtex.m_element->machine(), dest, elemtex.m_state); + } - // based on the component type, add to the texture - curcomp->draw(elemtex->m_element->machine(), dest, bounds, elemtex->m_state); - } + // if there's a callback for additional drawing, invoke it + if (!elemtex.m_element->m_draw.isnull()) + elemtex.m_element->m_draw(elemtex.m_state, dest); } @@ -1237,80 +1717,410 @@ class layout_element::image_component : public component { public: // construction/destruction - image_component(environment &env, util::xml::data_node const &compnode, const char *dirname) - : component(env, compnode, dirname) - , m_hasalpha(false) + image_component(environment &env, util::xml::data_node const &compnode) + : component(env, compnode) + , m_rasterizer(env.svg_rasterizer()) + , m_searchpath(env.search_path() ? env.search_path() : "") + , m_dirname(env.directory_name() ? env.directory_name() : "") + , m_imagefile(env.get_attribute_string(compnode, "file")) + , m_alphafile(env.get_attribute_string(compnode, "alphafile")) + , m_data(get_data(compnode)) { - if (dirname != nullptr) - m_dirname = dirname; - m_imagefile = env.get_attribute_string(compnode, "file", ""); - m_alphafile = env.get_attribute_string(compnode, "alphafile", ""); - m_file = std::make_unique<emu_file>(env.machine().options().art_path(), OPEN_FLAG_READ); } -protected: // overrides - virtual void draw(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override + virtual void preload(running_machine &machine) override + { + if (!m_bitmap.valid() && !m_svg) + load_image(machine); + } + +protected: + virtual void draw_aligned(running_machine &machine, bitmap_argb32 &dest, rectangle const &bounds, int state) override { - if (!m_bitmap.valid()) - load_bitmap(); + if (!m_bitmap.valid() && !m_svg) + load_image(machine); - bitmap_argb32 destsub(dest, bounds); - render_resample_argb_bitmap_hq(destsub, m_bitmap, color()); + if (m_bitmap.valid()) + draw_bitmap(dest, bounds, state); + else if (m_svg) + draw_svg(dest, bounds, state); } private: // internal helpers - void load_bitmap() + void draw_bitmap(bitmap_argb32 &dest, rectangle const &bounds, int state) { - assert(m_file != nullptr); + render_color const c(color(state)); + if (m_hasalpha || (1.0F > c.a)) + { + bitmap_argb32 tempbitmap(dest.width(), dest.height()); + render_resample_argb_bitmap_hq(tempbitmap, m_bitmap, c); + alpha_blend(tempbitmap, dest, bounds); + } + else + { + bitmap_argb32 destsub(dest, bounds); + render_resample_argb_bitmap_hq(destsub, m_bitmap, c); + } + } - ru_imgformat const format = render_detect_image(*m_file, m_dirname.c_str(), m_imagefile.c_str()); - switch (format) + void draw_svg(bitmap_argb32 &dest, rectangle const &bounds, int state) + { + // rasterise into a temporary bitmap + float const xscale(bounds.width() / m_svg->width); + float const yscale(bounds.height() / m_svg->height); + float const drawscale((std::max)(xscale, yscale)); + bitmap_argb32 tempbitmap(int(m_svg->width * drawscale), int(m_svg->height * drawscale)); + nsvgRasterize( + m_rasterizer.get(), + m_svg.get(), + 0, 0, drawscale, + reinterpret_cast<unsigned char *>(&tempbitmap.pix(0)), + tempbitmap.width(), tempbitmap.height(), + tempbitmap.rowbytes()); + + // correct colour format and multiply by state colour + bool havealpha(false); + render_color const c(color(state)); + for (s32 y = 0; tempbitmap.height() > y; ++y) { - case RENDUTIL_IMGFORMAT_ERROR: - break; + u32 *dst(&tempbitmap.pix(y)); + for (s32 x = 0; tempbitmap.width() > x; ++x, ++dst) + { + u8 const *const src(reinterpret_cast<u8 const *>(dst)); + rgb_t const d( + u8((float(src[3]) * c.a) + 0.5F), + u8((float(src[0]) * c.r) + 0.5F), + u8((float(src[1]) * c.g) + 0.5F), + u8((float(src[2]) * c.b) + 0.5F)); + *dst = d; + havealpha = havealpha || (d.a() < 255U); + } + } - case RENDUTIL_IMGFORMAT_PNG: - // load the basic bitmap - m_hasalpha = render_load_png(m_bitmap, *m_file, m_dirname.c_str(), m_imagefile.c_str()); - break; + // find most efficient way to insert it in the target bitmap + if (!havealpha) + { + if ((tempbitmap.width() == bounds.width()) && (tempbitmap.height() == bounds.height())) + { + for (s32 y = 0; tempbitmap.height() > y; ++y) + std::copy_n(&tempbitmap.pix(y), bounds.width(), &dest.pix(y + bounds.top(), bounds.left())); + } + else + { + bitmap_argb32 destsub(dest, bounds); + render_resample_argb_bitmap_hq(destsub, tempbitmap, render_color{ 1.0F, 1.0F, 1.0F, 1.0F }); + } + } + else if ((tempbitmap.width() == bounds.width()) && (tempbitmap.height() == bounds.height())) + { + alpha_blend(tempbitmap, dest, bounds); + } + else + { + bitmap_argb32 scaled(bounds.width(), bounds.height()); + render_resample_argb_bitmap_hq(scaled, tempbitmap, render_color{ 1.0F, 1.0F, 1.0F, 1.0F }); + tempbitmap.reset(); + alpha_blend(scaled, dest, bounds); + } + } - default: - // try JPG - render_load_jpeg(m_bitmap, *m_file, m_dirname.c_str(), m_imagefile.c_str()); - break; + void alpha_blend(bitmap_argb32 const &srcbitmap, bitmap_argb32 &dstbitmap, rectangle const &bounds) + { + for (s32 y0 = 0, y1 = bounds.top(); bounds.bottom() >= y1; ++y0, ++y1) + { + u32 const *src(&srcbitmap.pix(y0, 0)); + u32 *dst(&dstbitmap.pix(y1, bounds.left())); + for (s32 x1 = bounds.left(); bounds.right() >= x1; ++x1, ++src, ++dst) + { + rgb_t const a(*src); + u32 const aa(a.a()); + if (255 == aa) + { + *dst = *src; + } + else if (aa) + { + rgb_t const b(*dst); + u32 const ba(b.a()); + if (ba) + { + u32 const ca((aa * 255) + (ba * (255 - aa))); + *dst = rgb_t( + u8(ca / 255), + u8(((a.r() * aa * 255) + (b.r() * ba * (255 - aa))) / ca), + u8(((a.g() * aa * 255) + (b.g() * ba * (255 - aa))) / ca), + u8(((a.b() * aa * 255) + (b.b() * ba * (255 - aa))) / ca)); + } + else + { + *dst = *src; + } + } + } + } + } + + void load_image(running_machine &machine) + { + // if we have a filename, go with that + emu_file file(m_searchpath.empty() ? m_dirname : m_searchpath, OPEN_FLAG_READ); + if (!m_imagefile.empty()) + { + std::string filename; + if (!m_searchpath.empty()) + filename = m_dirname; + util::path_append(filename, m_imagefile); + LOGMASKED(LOG_IMAGE_LOAD, "Image component attempt to load image file '%s'\n", filename); + std::error_condition const imgerr = file.open(filename); + if (!imgerr) + { + if (!load_bitmap(file)) + { + LOGMASKED(LOG_IMAGE_LOAD, "Image component will attempt to parse file as SVG\n"); + load_svg(file); + } + file.close(); + } + else + { + LOGMASKED(LOG_IMAGE_LOAD, "Image component unable to open image file '%s' (%s:%d %s)\n", + filename, imgerr.category().name(), imgerr.value(), imgerr.message()); + } + } + else if (!m_data.empty()) + { + load_image_data(); } // load the alpha bitmap if specified - if (m_bitmap.valid() && !m_alphafile.empty()) - render_load_png(m_bitmap, *m_file, m_dirname.c_str(), m_alphafile.c_str(), true); + if (!m_alphafile.empty()) + { + if (m_bitmap.valid()) + { + std::string filename; + if (!m_searchpath.empty()) + filename = m_dirname; + util::path_append(filename, m_alphafile); + LOGMASKED(LOG_IMAGE_LOAD, "Image component attempt to load alpha channel from file '%s'\n", filename); + std::error_condition const alferr = file.open(filename); + if (!alferr) + { + // TODO: no way to detect corner case where we had alpha from the image but the alpha PNG makes it entirely opaque + if (render_load_png(m_bitmap, file, true)) + m_hasalpha = true; + file.close(); + } + else + { + LOGMASKED(LOG_IMAGE_LOAD, "Image component unable to open alpha channel file '%s' (%s:%d %s)\n", + filename, alferr.category().name(), alferr.value(), alferr.message()); + } + } + else if (m_svg) + { + osd_printf_warning("Component alpha channel file '%s' ignored for SVG image '%s'\n", m_alphafile, m_imagefile); + } + } - // if we can't load the bitmap, allocate a dummy one and report an error - if (!m_bitmap.valid()) + // if we can't load an image, allocate a dummy one and report an error + if (!m_bitmap.valid() && !m_svg) { // draw some stripes in the bitmap m_bitmap.allocate(100, 100); m_bitmap.fill(0); for (int step = 0; step < 100; step += 25) for (int line = 0; line < 100; line++) - m_bitmap.pix32((step + line) % 100, line % 100) = rgb_t(0xff,0xff,0xff,0xff); + m_bitmap.pix((step + line) % 100, line % 100) = rgb_t(0xff,0xff,0xff,0xff); // log an error if (m_alphafile.empty()) - osd_printf_warning("Unable to load component bitmap '%s'\n", m_imagefile.c_str()); + osd_printf_warning("Unable to load component image '%s'\n", m_imagefile); else - osd_printf_warning("Unable to load component bitmap '%s'/'%s'\n", m_imagefile.c_str(), m_alphafile.c_str()); + osd_printf_warning("Unable to load component image '%s'/'%s'\n", m_imagefile, m_alphafile); + } + + // clear out this stuff in case it's large + if (!m_svg) + m_rasterizer.reset(); + m_searchpath.clear(); + m_dirname.clear(); + m_imagefile.clear(); + m_alphafile.clear(); + m_data.clear(); + } + + void load_image_data() + { + // in-place Base64 decode + static constexpr char base64chars[] = + "\t\n\v\f\r +/0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz"; + static constexpr char base64tail[] = + "\t\n\v\f\r ="; + std::string::size_type const tail(m_data.find_first_not_of(base64chars)); + std::string::size_type const end(m_data.find_first_not_of(base64tail, tail)); + if (std::string::npos == end) + { + LOGMASKED(LOG_IMAGE_LOAD, "Image component decoding Base64 image data\n"); + char *dst(&m_data[0]); + unsigned trailing(0U); + for (std::string::size_type i = 0U; (m_data.size() > i) && ('=' != m_data[i]); ++i) + { + u8 sym; + if (('A' <= m_data[i]) && ('Z' >= m_data[i])) + sym = m_data[i] - 'A'; + else if (('a' <= m_data[i]) && ('z' >= m_data[i])) + sym = m_data[i] - 'a' + 26; + else if (('0' <= m_data[i]) && ('9' >= m_data[i])) + sym = m_data[i] - '0' + 52; + else if ('+' == m_data[i]) + sym = 62; + else if ('/' == m_data[i]) + sym = 63; + else + continue; + if (trailing) + *dst |= (sym << 2) >> trailing; + else + *dst = sym << 2; + if (trailing >= 2U) + ++dst; + trailing = (trailing + 6U) & 7U; + if (trailing) + *dst = sym << (8U - trailing); + } + m_data.resize(dst - &m_data[0]); + } + + // make a file wrapper for the data and see if it looks like a bitmap + util::core_file::ptr file; + std::error_condition const filerr(util::core_file::open_ram(m_data.c_str(), m_data.size(), OPEN_FLAG_READ, file)); + bool const bitmapdata(!filerr && file && load_bitmap(*file)); + file.reset(); + + // if it didn't look like a bitmap, see if it looks like it might be XML and hence SVG + if (!bitmapdata) + { + bool const utf16be((0xfe == u8(m_data[0])) && (0xff == u8(m_data[1]))); + bool const utf16le((0xff == u8(m_data[0])) && (0xfe == u8(m_data[1]))); + bool const utf8((0xef == u8(m_data[0])) && (0xbb == u8(m_data[1])) && (0xbf == u8(m_data[2]))); + std::string::size_type const found(m_data.find_first_not_of("\t\n\v\f\r ")); + bool const xmltag((std::string::npos != found) && ('<' == m_data[found])); + if (utf16be || utf16le || utf8 || xmltag) + { + LOGMASKED(LOG_IMAGE_LOAD, "Image component will attempt to parse data as SVG\n"); + parse_svg(&m_data[0]); + } + } + } + + bool load_bitmap(util::random_read &file) + { + ru_imgformat const format = render_detect_image(file); + switch (format) + { + case RENDUTIL_IMGFORMAT_ERROR: + LOGMASKED(LOG_IMAGE_LOAD, "Image component error detecting image file format\n"); + return false; + + case RENDUTIL_IMGFORMAT_PNG: + LOGMASKED(LOG_IMAGE_LOAD, "Image component detected PNG file format\n"); + m_hasalpha = render_load_png(m_bitmap, file); + return true; + + case RENDUTIL_IMGFORMAT_JPEG: + LOGMASKED(LOG_IMAGE_LOAD, "Image component detected JPEG file format\n"); + render_load_jpeg(m_bitmap, file); + return true; + + case RENDUTIL_IMGFORMAT_MSDIB: + LOGMASKED(LOG_IMAGE_LOAD, "Image component detected Microsoft DIB file format\n"); + render_load_msdib(m_bitmap, file); + return true; + + default: + LOGMASKED(LOG_IMAGE_LOAD, "Image component failed to detect bitmap file format\n"); + return false; + } + } + + void load_svg(util::random_read &file) + { + std::error_condition filerr; + u64 len; + filerr = file.length(len); + if (filerr) + { + osd_printf_warning("Error getting length of component image '%s'\n", m_imagefile); + return; + } + if ((std::numeric_limits<size_t>::max() - 1) < len) + { + osd_printf_warning("Component image '%s' is too large to read into memory\n", m_imagefile); + return; + } + std::unique_ptr<char []> svgbuf(new (std::nothrow) char [size_t(len) + 1]); + if (!svgbuf) + { + osd_printf_warning("Error allocating memory to read component image '%s'\n", m_imagefile); + return; + } + svgbuf[len] = '\0'; + size_t actual; + std::tie(filerr, actual) = read(file, svgbuf.get(), len); + if (filerr || (actual < len)) + { + osd_printf_warning("Error reading component image '%s'\n", m_imagefile); + return; + } + parse_svg(svgbuf.get()); + } + + void parse_svg(char *svgdata) + { + if (!m_rasterizer) + { + osd_printf_warning("No SVG rasteriser available, won't attempt to parse component image '%s' as SVG\n", m_imagefile); + return; + } + m_svg.reset(nsvgParse(svgdata, "px", 72)); + if (!m_svg) + { + osd_printf_warning("Failed to parse component image '%s' as SVG\n", m_imagefile); + return; + } + if ((0.0F >= m_svg->width) || (0.0F >= m_svg->height)) + { + osd_printf_warning("Parsing component image '%s' as SVG produced empty image\n", m_imagefile); + m_svg.reset(); + return; } } + static std::string get_data(util::xml::data_node const &compnode) + { + util::xml::data_node const *datanode(compnode.get_child("data")); + if (datanode && datanode->get_value()) + return datanode->get_value(); + else + return ""; + } + // internal state - bitmap_argb32 m_bitmap; // source bitmap for images - std::string m_dirname; // directory name of image file (for lazy loading) - std::unique_ptr<emu_file> m_file; // file object for reading image/alpha files - std::string m_imagefile; // name of the image file (for lazy loading) - std::string m_alphafile; // name of the alpha file (for lazy loading) - bool m_hasalpha; // is there any alpha component present? + util::nsvg_image_ptr m_svg; // parsed SVG image + std::shared_ptr<NSVGrasterizer> m_rasterizer; // SVG rasteriser + bitmap_argb32 m_bitmap; // source bitmap for images + bool m_hasalpha = false; // is there any alpha component present? + + // cold state + std::string m_searchpath; // asset search path (for lazy loading) + std::string m_dirname; // directory name of image file (for lazy loading) + std::string m_imagefile; // name of the image file (for lazy loading) + std::string m_alphafile; // name of the alpha file (for lazy loading) + std::string m_data; // embedded image data }; @@ -1319,41 +2129,42 @@ class layout_element::rect_component : public component { public: // construction/destruction - rect_component(environment &env, util::xml::data_node const &compnode, const char *dirname) - : component(env, compnode, dirname) + rect_component(environment &env, util::xml::data_node const &compnode) + : component(env, compnode) { } protected: // overrides - virtual void draw(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override + virtual void draw_aligned(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override { - // compute premultiplied colors - u32 const r = color().r * color().a * 255.0f; - u32 const g = color().g * color().a * 255.0f; - u32 const b = color().b * color().a * 255.0f; - u32 const inva = (1.0f - color().a) * 255.0f; - - // iterate over X and Y - for (u32 y = bounds.top(); y <= bounds.bottom(); y++) + render_color const c(color(state)); + if (1.0f <= c.a) + { + // optimise opaque pixels + u32 const f(rgb_t(u8(c.r * 255), u8(c.g * 255), u8(c.b * 255))); + s32 const width(bounds.width()); + for (u32 y = bounds.top(); y <= bounds.bottom(); ++y) + std::fill_n(&dest.pix(y, bounds.left()), width, f); + } + else { - for (u32 x = bounds.left(); x <= bounds.right(); x++) + // compute premultiplied color + u32 const a(c.a * 255.0F); + u32 const r(u32(c.r * (255.0F * 255.0F)) * a); + u32 const g(u32(c.g * (255.0F * 255.0F)) * a); + u32 const b(u32(c.b * (255.0F * 255.0F)) * a); + u32 const inva(255 - a); + + if (!a) + return; + + // we're translucent, add in the destination pixel contribution + for (u32 y = bounds.top(); y <= bounds.bottom(); ++y) { - u32 finalr = r; - u32 finalg = g; - u32 finalb = b; - - // if we're translucent, add in the destination pixel contribution - if (inva > 0) - { - rgb_t dpix = dest.pix32(y, x); - finalr += (dpix.r() * inva) >> 8; - finalg += (dpix.g() * inva) >> 8; - finalb += (dpix.b() * inva) >> 8; - } - - // store the target pixel, dividing the RGBA values by the overall scale factor - dest.pix32(y, x) = rgb_t(finalr, finalg, finalb); + u32 *dst(&dest.pix(y, bounds.left())); + for (u32 x = bounds.left(); x <= bounds.right(); ++x, ++dst) + alpha_blend(*dst, a, r, g, b, inva); } } } @@ -1365,59 +2176,293 @@ class layout_element::disk_component : public component { public: // construction/destruction - disk_component(environment &env, util::xml::data_node const &compnode, const char *dirname) - : component(env, compnode, dirname) + disk_component(environment &env, util::xml::data_node const &compnode) + : component(env, compnode) { } -protected: // overrides - virtual void draw(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override + virtual void draw(running_machine &machine, bitmap_argb32 &dest, int state) override { - // compute premultiplied colors - u32 const r = color().r * color().a * 255.0f; - u32 const g = color().g * color().a * 255.0f; - u32 const b = color().b * color().a * 255.0f; - u32 const inva = (1.0f - color().a) * 255.0f; - - // find the center - float const xcenter = float(bounds.xcenter()); - float const ycenter = float(bounds.ycenter()); - float const xradius = float(bounds.width()) * 0.5f; - float const yradius = float(bounds.height()) * 0.5f; - float const ooyradius2 = 1.0f / (yradius * yradius); + // compute premultiplied color + render_color const c(color(state)); + u32 const f(rgb_t(u8(c.r * 255), u8(c.g * 255), u8(c.b * 255))); + u32 const a(c.a * 255.0F); + u32 const r(c.r * (255.0F * 255.0F) * a); + u32 const g(c.g * (255.0F * 255.0F) * a); + u32 const b(c.b * (255.0F * 255.0F) * a); + u32 const inva(255 - a); + + if (!a) + return; - // iterate over y - for (u32 y = bounds.top(); y <= bounds.bottom(); y++) + // calculate the position and size + render_bounds const curbounds = bounds(state); + double const xcenter = (curbounds.x0 + curbounds.x1) * double(dest.width()) * 0.5; + double const ycenter = (curbounds.y0 + curbounds.y1) * double(dest.height()) * 0.5; + double const xradius = curbounds.width() * double(dest.width()) * 0.5; + double const yradius = curbounds.height() * double(dest.height()) * 0.5; + s32 const miny = s32(curbounds.y0 * double(dest.height())); + s32 const maxy = s32(std::ceil(curbounds.y1 * double(dest.height()))) - 1; + LOGMASKED(LOG_DISK_DRAW, "Draw disk: bounds (%s %s %s %s); (((x - %s) ** 2) / (%s ** 2) + ((y - %s) ** 2) / (%s ** 2)) = 1; rows [%s %s]\n", + curbounds.x0, curbounds.y0, curbounds.x1, curbounds.y1, xcenter, xradius, ycenter, yradius, miny, maxy); + + if (miny == maxy) + { + // fits in a single row of pixels - integrate entire area of ellipse + double const scale = xradius * yradius * 0.5; + s32 const minx = s32(curbounds.x0 * double(dest.width())); + s32 const maxx = s32(std::ceil(curbounds.x1 * double(dest.width()))) - 1; + double x1 = (double(minx) - xcenter) / xradius; + u32 *dst = &dest.pix(miny, minx); + for (s32 x = minx; maxx >= x; ++x, ++dst) + { + double const x0 = x1; + x1 = (double(x + 1) - xcenter) / xradius; + double const val = integral((std::max)(x0, -1.0), (std::min)(x1, 1.0)) * scale; + alpha_blend(*dst, c, val); + } + } + else { - float ycoord = ycenter - ((float)y + 0.5f); - float xval = xradius * sqrtf(1.0f - (ycoord * ycoord) * ooyradius2); + double const scale = xradius * yradius * 0.25; + double const ooyradius2 = 1.0 / (yradius * yradius); + auto const draw_edge_row = + [&dest, &c, &curbounds, xcenter, xradius, scale, ooyradius2] (s32 row, double ycoord, bool cross_axis) + { + double const xval = xradius * std::sqrt((std::max)(1.0 - (ycoord * ycoord) * ooyradius2, 0.0)); + double const l = xcenter - xval; + double const r = xcenter + xval; + if (!cross_axis) + { + s32 minx = s32(l); + s32 maxx = s32(std::ceil(r)) - 1; + double x1 = double(minx) - xcenter; + u32 *dst = &dest.pix(row, minx); + for (s32 x = minx; maxx >= x; ++x, ++dst) + { + double const x0 = x1; + x1 = double(x + 1) - xcenter; + double val = integral((std::max)(x0, -xval) / xradius, (std::min)(x1, xval) / xradius) * scale; + val -= ((std::min)(double(x + 1), r) - (std::max)(double(x), l)) * ycoord; + alpha_blend(*dst, c, val); + } + } + else + { + s32 const minx = s32(curbounds.x0 * double(dest.width())); + s32 const maxx = s32(std::ceil(curbounds.x1 * double(dest.width()))) - 1; + double x1 = (double(minx) - xcenter) / xradius; + u32 *dst = &dest.pix(row, minx); + for (s32 x = minx; maxx >= x; ++x, ++dst) + { + double const x0 = x1; + x1 = (double(x + 1) - xcenter) / xradius; + double val = integral((std::max)(x0, -1.0), (std::min)(x1, 1.0)); + if (double(x + 1) <= l) + val += integral((std::max)(x0, -1.0), x1); + else if (double(x) <= l) + val += integral((std::max)(x0, -1.0), -xval / xradius); + if (double(x) >= r) + val += integral(x0, (std::min)(x1, 1.0)); + else if (double(x + 1) >= r) + val += integral(xval / xradius, (std::min)(x1, 1.0)); + val *= scale; + val -= (std::max)(((std::min)(double(x + 1), r) - (std::max)(double(x), l)), 0.0) * ycoord; + alpha_blend(*dst, c, val); + } + } + }; + + // draw the top row - in a thin ellipse it may extend below the axis + draw_edge_row(miny, ycenter - double(miny + 1), double(miny + 1) > ycenter); + + // draw rows above the axis + s32 y = miny + 1; + double ycoord1 = ycenter - double(y); + double xval1 = std::sqrt((std::max)(1.0 - (ycoord1 * ycoord1) * ooyradius2, 0.0)); + double l1 = xcenter - (xval1 * xradius); + double r1 = xcenter + (xval1 * xradius); + for ( ; (maxy > y) && (double(y + 1) <= ycenter); ++y) + { + double const xval0 = xval1; + double const l0 = l1; + double const r0 = r1; + ycoord1 = ycenter - double(y + 1); + xval1 = std::sqrt((std::max)(1.0 - (ycoord1 * ycoord1) * ooyradius2, 0.0)); + l1 = xcenter - (xval1 * xradius); + r1 = xcenter + (xval1 * xradius); + s32 const minx = s32(l1); + s32 const maxx = s32(std::ceil(r1)) - 1; + s32 const minfill = s32(std::ceil(l0)); + s32 const maxfill = s32(r0) - 1; + u32 *dst = &dest.pix(y, minx); + for (s32 x = minx; maxx >= x; ++x, ++dst) + { + if ((x >= minfill) && (x <= maxfill)) + { + if (255 <= a) + dst = std::fill_n(dst, maxfill - x + 1, f); + else + while (x++ <= maxfill) + alpha_blend(*dst++, a, r, g, b, inva); - // compute left/right coordinates - s32 left = s32(xcenter - xval + 0.5f); - s32 right = s32(xcenter + xval + 0.5f); + --dst; + x = maxfill; + } + else + { + double val = 0.0; + + // integrate where perimeter passes through pixel cell + if (double(x + 1) <= l0) // perimeter intercepts right edge of pixel cell (left side) + val += integral((std::max)((double(x) - xcenter) / xradius, -xval1), (double(x + 1) - xcenter) / xradius); + else if (double(x) <= l0) // perimeter intercepts top edge of pixel cell (left side) + val += integral((std::max)((double(x) - xcenter) / xradius, -xval1), -xval0); + else if (double(x) >= r0) // perimeter intercepts left edge of pixel cell (right side) + val += integral((double(x) - xcenter) / xradius, (std::min)((double(x + 1) - xcenter) / xradius, xval1)); + else if (double(x + 1) >= r0) // perimeter intercepts top edge of pixel cell (right side) + val += integral(xval0, (std::min)((double(x + 1) - xcenter) / xradius, xval1)); + val *= scale; + + // subtract area between vertical centre and bottom of pixel cell + if (double(x) <= l0) + val -= ((std::min)(double(x + 1), l0) - (std::max)(double(x), l1)) * ycoord1; + else if (double(x + 1) >= r0) + val -= ((std::min)(double(x + 1), r1) - (std::max)(double(x), r0)) * ycoord1; + + // add in the fully covered part of the pixel + val += (std::max)((std::min)(double(x + 1), r0) - (std::max)(double(x), l0), 0.0); + + alpha_blend(*dst, c, (std::min)(val, 1.0)); + } + } + } - // draw this scanline - for (u32 x = left; x < right; x++) + // row spanning the axis + if ((maxy > y) && (double(y) < ycenter)) { - u32 finalr = r; - u32 finalg = g; - u32 finalb = b; - - // if we're translucent, add in the destination pixel contribution - if (inva > 0) + double const xval0 = xval1; + double const l0 = l1; + double const r0 = r1; + ycoord1 = double(y + 1) - ycenter; + xval1 = std::sqrt((std::max)(1.0 - (ycoord1 * ycoord1) * ooyradius2, 0.0)); + l1 = xcenter - (xval1 * xradius); + r1 = xcenter + (xval1 * xradius); + s32 const minx = s32(curbounds.x0 * double(dest.width())); + s32 const maxx = s32(std::ceil(curbounds.x1 * double(dest.width()))) - 1; + u32 *dst = &dest.pix(y, minx); + for (s32 x = minx; maxx >= x; ++x, ++dst) { - rgb_t dpix = dest.pix32(y, x); - finalr += (dpix.r() * inva) >> 8; - finalg += (dpix.g() * inva) >> 8; - finalb += (dpix.b() * inva) >> 8; + if ((double(x) >= (std::max)(l0, l1)) && (double(x + 1) <= (std::min)(r0, r1))) + { + if (255 <= a) + *dst = f; + else + alpha_blend(*dst, a, r, g, b, inva); + } + else + { + double val = 0.0; + if (double(x + 1) <= l0) + val += integral((xcenter - double(x + 1)) / xradius, (std::min)((xcenter - double(x)) / xradius, 1.0)); + else if (double(x) <= l0) + val += integral(xval0, (std::min)((xcenter - double(x)) / xradius, 1.0)); + else if (double(x) >= r0) + val += integral((double(x) - xcenter) / xradius, (std::min)((double(x + 1) - xcenter) / xradius, 1.0)); + else if (double(x + 1) >= r0) + val += integral(xval0, (std::min)((double(x + 1) - xcenter) / xradius, 1.0)); + if (double(x + 1) <= l1) + val += integral((xcenter - double(x + 1)) / xradius, (std::min)((xcenter - double(x)) / xradius, 1.0)); + else if (double(x) <= l1) + val += integral(xval1, (std::min)((xcenter - double(x)) / xradius, 1.0)); + else if (double(x) >= r1) + val += integral((double(x) - xcenter) / xradius, (std::min)((double(x + 1) - xcenter) / xradius, 1.0)); + else if (double(x + 1) >= r1) + val += integral(xval1, (std::min)((double(x + 1) - xcenter) / xradius, 1.0)); + val *= scale; + val += (std::max)(((std::min)(double(x + 1), r0) - (std::max)(double(x), l0)), 0.0) * (ycenter - double(y)); + val += (std::max)(((std::min)(double(x + 1), r1) - (std::max)(double(x), l1)), 0.0) * (double(y + 1) - ycenter); + alpha_blend(*dst, c, val); + } } + ++y; + } + + // draw rows below the axis + for ( ; maxy > y; ++y) + { + double const ycoord0 = ycoord1; + double const xval0 = xval1; + double const l0 = l1; + double const r0 = r1; + ycoord1 = double(y + 1) - ycenter; + xval1 = std::sqrt((std::max)(1.0 - (ycoord1 * ycoord1) * ooyradius2, 0.0)); + l1 = xcenter - (xval1 * xradius); + r1 = xcenter + (xval1 * xradius); + s32 const minx = s32(l0); + s32 const maxx = s32(std::ceil(r0)) - 1; + s32 const minfill = s32(std::ceil(l1)); + s32 const maxfill = s32(r1) - 1; + u32 *dst = &dest.pix(y, minx); + for (s32 x = minx; maxx >= x; ++x, ++dst) + { + if ((x >= minfill) && (x <= maxfill)) + { + if (255 <= a) + dst = std::fill_n(dst, maxfill - x + 1, f); + else + while (x++ <= maxfill) + alpha_blend(*dst++, a, r, g, b, inva); - // store the target pixel, dividing the RGBA values by the overall scale factor - dest.pix32(y, x) = rgb_t(finalr, finalg, finalb); + --dst; + x = maxfill; + } + else + { + double val = 0.0; + + // integrate where perimeter passes through pixel cell + if (double(x + 1) <= l1) // perimeter intercepts right edge of pixel cell (left side) + val += integral((std::max)((double(x) - xcenter) / xradius, -xval0), (double(x + 1) - xcenter) / xradius); + else if (double(x) <= l1) // perimeter intercepts bottom edge of pixel cell (left side) + val += integral((std::max)((double(x) - xcenter) / xradius, -xval0), -xval1); + else if (double(x) >= r1) // perimeter intercepts left edge of pixel cell (right side) + val += integral((double(x) - xcenter) / xradius, (std::min)((double(x + 1) - xcenter) / xradius, xval0)); + else if (double(x + 1) >= r1) // perimeter intercepts bottom edge of pixel cell (right side) + val += integral(xval1, (std::min)((double(x + 1) - xcenter) / xradius, xval0)); + val *= scale; + + // subtract area between vertical centre and top of pixel cell + if (double(x) <= l1) + val -= ((std::min)(double(x + 1), l1) - (std::max)(double(x), l0)) * ycoord0; + else if (double(x + 1) >= r1) + val -= ((std::min)(double(x + 1), r0) - (std::max)(double(x), r1)) * ycoord0; + + // add in the fully covered part of the pixel + val += (std::max)((std::min)(double(x + 1), r1) - (std::max)(double(x), l1), 0.0); + + alpha_blend(*dst, c, (std::min)(val, 1.0)); + } + } } + + // last row is an inversion of the first + draw_edge_row(maxy, double(maxy) - ycenter, double(maxy) < ycenter); } } + +private: + static double integral(double x0, double x1) + { + return integral(x1) - integral(x0); + } + + static double integral(double x) + { + double const u(2.0 * std::asin(x)); + return u + std::sin(u); + }; }; @@ -1426,20 +2471,19 @@ class layout_element::text_component : public component { public: // construction/destruction - text_component(environment &env, util::xml::data_node const &compnode, const char *dirname) - : component(env, compnode, dirname) + text_component(environment &env, util::xml::data_node const &compnode) + : component(env, compnode) { - m_string = env.get_attribute_string(compnode, "string", ""); + m_string = env.get_attribute_string(compnode, "string"); m_textalign = env.get_attribute_int(compnode, "align", 0); } protected: // overrides - virtual void draw(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override + virtual void draw_aligned(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override { - render_font *font = machine.render().font_alloc("default"); - draw_text(*font, dest, bounds, m_string.c_str(), m_textalign); - machine.render().font_free(font); + auto font = machine.render().font_alloc("default"); + draw_text(*font, dest, bounds, m_string, m_textalign, color(state)); } private: @@ -1454,25 +2498,26 @@ class layout_element::led7seg_component : public component { public: // construction/destruction - led7seg_component(environment &env, util::xml::data_node const &compnode, const char *dirname) - : component(env, compnode, dirname) + led7seg_component(environment &env, util::xml::data_node const &compnode) + : component(env, compnode) { + m_invert = env.get_attribute_int(compnode, "invert", 0); } protected: // overrides virtual int maxstate() const override { return 255; } - virtual void draw(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override + virtual void draw_aligned(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override { - const rgb_t onpen = rgb_t(0xff,0xff,0xff,0xff); - const rgb_t offpen = rgb_t(0x20,0xff,0xff,0xff); + rgb_t const onpen = rgb_t(m_invert ? 0x20 : 0xff, 0xff, 0xff, 0xff); + rgb_t const offpen = rgb_t(m_invert ? 0xff : 0x20, 0xff, 0xff, 0xff); // sizes for computation - int bmwidth = 250; - int bmheight = 400; - int segwidth = 40; - int skewwidth = 40; + int const bmwidth = 250; + int const bmheight = 400; + int const segwidth = 40; + int const skewwidth = 40; // allocate a temporary bitmap for drawing bitmap_argb32 tempbitmap(bmwidth + skewwidth, bmheight); @@ -1506,76 +2551,10 @@ protected: draw_segment_decimal(tempbitmap, bmwidth + segwidth/2, bmheight - segwidth/2, segwidth, BIT(state, 7) ? onpen : offpen); // resample to the target size - render_resample_argb_bitmap_hq(dest, tempbitmap, color()); - } -}; - - -// 8-segment fluorescent (Gottlieb System 1) -class layout_element::led8seg_gts1_component : public component -{ -public: - // construction/destruction - led8seg_gts1_component(environment &env, util::xml::data_node const &compnode, const char *dirname) - : component(env, compnode, dirname) - { - } - -protected: - // overrides - virtual int maxstate() const override { return 255; } - - virtual void draw(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override - { - const rgb_t onpen = rgb_t(0xff,0xff,0xff,0xff); - const rgb_t offpen = rgb_t(0x20,0xff,0xff,0xff); - const rgb_t backpen = rgb_t(0x00,0x00,0x00,0x00); - - // sizes for computation - int bmwidth = 250; - int bmheight = 400; - int segwidth = 40; - int skewwidth = 40; - - // allocate a temporary bitmap for drawing - bitmap_argb32 tempbitmap(bmwidth + skewwidth, bmheight); - tempbitmap.fill(backpen); - - // top bar - draw_segment_horizontal(tempbitmap, 0 + 2*segwidth/3, bmwidth - 2*segwidth/3, 0 + segwidth/2, segwidth, (state & (1 << 0)) ? onpen : offpen); - - // top-right bar - draw_segment_vertical(tempbitmap, 0 + 2*segwidth/3, bmheight/2 - segwidth/3, bmwidth - segwidth/2, segwidth, (state & (1 << 1)) ? onpen : offpen); - - // bottom-right bar - draw_segment_vertical(tempbitmap, bmheight/2 + segwidth/3, bmheight - 2*segwidth/3, bmwidth - segwidth/2, segwidth, (state & (1 << 2)) ? onpen : offpen); - - // bottom bar - draw_segment_horizontal(tempbitmap, 0 + 2*segwidth/3, bmwidth - 2*segwidth/3, bmheight - segwidth/2, segwidth, (state & (1 << 3)) ? onpen : offpen); - - // bottom-left bar - draw_segment_vertical(tempbitmap, bmheight/2 + segwidth/3, bmheight - 2*segwidth/3, 0 + segwidth/2, segwidth, (state & (1 << 4)) ? onpen : offpen); - - // top-left bar - draw_segment_vertical(tempbitmap, 0 + 2*segwidth/3, bmheight/2 - segwidth/3, 0 + segwidth/2, segwidth, (state & (1 << 5)) ? onpen : offpen); - - // horizontal bars - draw_segment_horizontal(tempbitmap, 0 + 2*segwidth/3, 2*bmwidth/3 - 2*segwidth/3, bmheight/2, segwidth, (state & (1 << 6)) ? onpen : offpen); - draw_segment_horizontal(tempbitmap, 0 + 2*segwidth/3 + bmwidth/2, bmwidth - 2*segwidth/3, bmheight/2, segwidth, (state & (1 << 6)) ? onpen : offpen); - - // vertical bars - draw_segment_vertical(tempbitmap, 0 + segwidth/3 - 8, bmheight/2 - segwidth/3 + 2, 2*bmwidth/3 - segwidth/2 - 4, segwidth + 8, backpen); - draw_segment_vertical(tempbitmap, 0 + segwidth/3, bmheight/2 - segwidth/3, 2*bmwidth/3 - segwidth/2 - 4, segwidth, (state & (1 << 7)) ? onpen : offpen); - - draw_segment_vertical(tempbitmap, bmheight/2 + segwidth/3 - 2, bmheight - segwidth/3 + 8, 2*bmwidth/3 - segwidth/2 - 4, segwidth + 8, backpen); - draw_segment_vertical(tempbitmap, bmheight/2 + segwidth/3, bmheight - segwidth/3, 2*bmwidth/3 - segwidth/2 - 4, segwidth, (state & (1 << 7)) ? onpen : offpen); - - // apply skew - apply_skew(tempbitmap, 40); - - // resample to the target size - render_resample_argb_bitmap_hq(dest, tempbitmap, color()); + render_resample_argb_bitmap_hq(dest, tempbitmap, color(state)); } +private: + int m_invert = 0; }; @@ -1584,8 +2563,8 @@ class layout_element::led14seg_component : public component { public: // construction/destruction - led14seg_component(environment &env, util::xml::data_node const &compnode, const char *dirname) - : component(env, compnode, dirname) + led14seg_component(environment &env, util::xml::data_node const &compnode) + : component(env, compnode) { } @@ -1593,16 +2572,16 @@ protected: // overrides virtual int maxstate() const override { return 16383; } - virtual void draw(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override + virtual void draw_aligned(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override { - const rgb_t onpen = rgb_t(0xff, 0xff, 0xff, 0xff); - const rgb_t offpen = rgb_t(0x20, 0xff, 0xff, 0xff); + rgb_t const onpen = rgb_t(0xff, 0xff, 0xff, 0xff); + rgb_t const offpen = rgb_t(0x20, 0xff, 0xff, 0xff); // sizes for computation - int bmwidth = 250; - int bmheight = 400; - int segwidth = 40; - int skewwidth = 40; + int const bmwidth = 250; + int const bmheight = 400; + int const segwidth = 40; + int const skewwidth = 40; // allocate a temporary bitmap for drawing bitmap_argb32 tempbitmap(bmwidth + skewwidth, bmheight); @@ -1610,83 +2589,83 @@ protected: // top bar draw_segment_horizontal(tempbitmap, - 0 + 2*segwidth/3, bmwidth - 2*segwidth/3, 0 + segwidth/2, - segwidth, (state & (1 << 0)) ? onpen : offpen); + 0 + 2*segwidth/3, bmwidth - 2*segwidth/3, 0 + segwidth/2, + segwidth, (state & (1 << 0)) ? onpen : offpen); // right-top bar draw_segment_vertical(tempbitmap, - 0 + 2*segwidth/3, bmheight/2 - segwidth/3, bmwidth - segwidth/2, - segwidth, (state & (1 << 1)) ? onpen : offpen); + 0 + 2*segwidth/3, bmheight/2 - segwidth/3, bmwidth - segwidth/2, + segwidth, (state & (1 << 1)) ? onpen : offpen); // right-bottom bar draw_segment_vertical(tempbitmap, - bmheight/2 + segwidth/3, bmheight - 2*segwidth/3, bmwidth - segwidth/2, - segwidth, (state & (1 << 2)) ? onpen : offpen); + bmheight/2 + segwidth/3, bmheight - 2*segwidth/3, bmwidth - segwidth/2, + segwidth, (state & (1 << 2)) ? onpen : offpen); // bottom bar draw_segment_horizontal(tempbitmap, - 0 + 2*segwidth/3, bmwidth - 2*segwidth/3, bmheight - segwidth/2, - segwidth, (state & (1 << 3)) ? onpen : offpen); + 0 + 2*segwidth/3, bmwidth - 2*segwidth/3, bmheight - segwidth/2, + segwidth, (state & (1 << 3)) ? onpen : offpen); // left-bottom bar draw_segment_vertical(tempbitmap, - bmheight/2 + segwidth/3, bmheight - 2*segwidth/3, 0 + segwidth/2, - segwidth, (state & (1 << 4)) ? onpen : offpen); + bmheight/2 + segwidth/3, bmheight - 2*segwidth/3, 0 + segwidth/2, + segwidth, (state & (1 << 4)) ? onpen : offpen); // left-top bar draw_segment_vertical(tempbitmap, - 0 + 2*segwidth/3, bmheight/2 - segwidth/3, 0 + segwidth/2, - segwidth, (state & (1 << 5)) ? onpen : offpen); + 0 + 2*segwidth/3, bmheight/2 - segwidth/3, 0 + segwidth/2, + segwidth, (state & (1 << 5)) ? onpen : offpen); // horizontal-middle-left bar draw_segment_horizontal_caps(tempbitmap, - 0 + 2*segwidth/3, bmwidth/2 - segwidth/10, bmheight/2, - segwidth, LINE_CAP_START, (state & (1 << 6)) ? onpen : offpen); + 0 + 2*segwidth/3, bmwidth/2 - segwidth/10, bmheight/2, + segwidth, LINE_CAP_START, (state & (1 << 6)) ? onpen : offpen); // horizontal-middle-right bar draw_segment_horizontal_caps(tempbitmap, - 0 + bmwidth/2 + segwidth/10, bmwidth - 2*segwidth/3, bmheight/2, - segwidth, LINE_CAP_END, (state & (1 << 7)) ? onpen : offpen); + 0 + bmwidth/2 + segwidth/10, bmwidth - 2*segwidth/3, bmheight/2, + segwidth, LINE_CAP_END, (state & (1 << 7)) ? onpen : offpen); // vertical-middle-top bar draw_segment_vertical_caps(tempbitmap, - 0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3, bmwidth/2, - segwidth, LINE_CAP_NONE, (state & (1 << 8)) ? onpen : offpen); + 0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3, bmwidth/2, + segwidth, LINE_CAP_NONE, (state & (1 << 8)) ? onpen : offpen); // vertical-middle-bottom bar draw_segment_vertical_caps(tempbitmap, - bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3, bmwidth/2, - segwidth, LINE_CAP_NONE, (state & (1 << 9)) ? onpen : offpen); + bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3, bmwidth/2, + segwidth, LINE_CAP_NONE, (state & (1 << 9)) ? onpen : offpen); // diagonal-left-bottom bar draw_segment_diagonal_1(tempbitmap, - 0 + segwidth + segwidth/5, bmwidth/2 - segwidth/2 - segwidth/5, - bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3, - segwidth, (state & (1 << 10)) ? onpen : offpen); + 0 + segwidth + segwidth/5, bmwidth/2 - segwidth/2 - segwidth/5, + bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3, + segwidth, (state & (1 << 10)) ? onpen : offpen); // diagonal-left-top bar draw_segment_diagonal_2(tempbitmap, - 0 + segwidth + segwidth/5, bmwidth/2 - segwidth/2 - segwidth/5, - 0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3, - segwidth, (state & (1 << 11)) ? onpen : offpen); + 0 + segwidth + segwidth/5, bmwidth/2 - segwidth/2 - segwidth/5, + 0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3, + segwidth, (state & (1 << 11)) ? onpen : offpen); // diagonal-right-top bar draw_segment_diagonal_1(tempbitmap, - bmwidth/2 + segwidth/2 + segwidth/5, bmwidth - segwidth - segwidth/5, - 0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3, - segwidth, (state & (1 << 12)) ? onpen : offpen); + bmwidth/2 + segwidth/2 + segwidth/5, bmwidth - segwidth - segwidth/5, + 0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3, + segwidth, (state & (1 << 12)) ? onpen : offpen); // diagonal-right-bottom bar draw_segment_diagonal_2(tempbitmap, - bmwidth/2 + segwidth/2 + segwidth/5, bmwidth - segwidth - segwidth/5, - bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3, - segwidth, (state & (1 << 13)) ? onpen : offpen); + bmwidth/2 + segwidth/2 + segwidth/5, bmwidth - segwidth - segwidth/5, + bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3, + segwidth, (state & (1 << 13)) ? onpen : offpen); // apply skew apply_skew(tempbitmap, 40); // resample to the target size - render_resample_argb_bitmap_hq(dest, tempbitmap, color()); + render_resample_argb_bitmap_hq(dest, tempbitmap, color(state)); } }; @@ -1696,8 +2675,8 @@ class layout_element::led16seg_component : public component { public: // construction/destruction - led16seg_component(environment &env, util::xml::data_node const &compnode, const char *dirname) - : component(env, compnode, dirname) + led16seg_component(environment &env, util::xml::data_node const &compnode) + : component(env, compnode) { } @@ -1705,7 +2684,7 @@ protected: // overrides virtual int maxstate() const override { return 65535; } - virtual void draw(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override + virtual void draw_aligned(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override { const rgb_t onpen = rgb_t(0xff, 0xff, 0xff, 0xff); const rgb_t offpen = rgb_t(0x20, 0xff, 0xff, 0xff); @@ -1808,7 +2787,7 @@ protected: apply_skew(tempbitmap, 40); // resample to the target size - render_resample_argb_bitmap_hq(dest, tempbitmap, color()); + render_resample_argb_bitmap_hq(dest, tempbitmap, color(state)); } }; @@ -1818,8 +2797,8 @@ class layout_element::led14segsc_component : public component { public: // construction/destruction - led14segsc_component(environment &env, util::xml::data_node const &compnode, const char *dirname) - : component(env, compnode, dirname) + led14segsc_component(environment &env, util::xml::data_node const &compnode) + : component(env, compnode) { } @@ -1827,16 +2806,16 @@ protected: // overrides virtual int maxstate() const override { return 65535; } - virtual void draw(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override + virtual void draw_aligned(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override { - const rgb_t onpen = rgb_t(0xff, 0xff, 0xff, 0xff); - const rgb_t offpen = rgb_t(0x20, 0xff, 0xff, 0xff); + rgb_t const onpen = rgb_t(0xff, 0xff, 0xff, 0xff); + rgb_t const offpen = rgb_t(0x20, 0xff, 0xff, 0xff); // sizes for computation - int bmwidth = 250; - int bmheight = 400; - int segwidth = 40; - int skewwidth = 40; + int const bmwidth = 250; + int const bmheight = 400; + int const segwidth = 40; + int const skewwidth = 40; // allocate a temporary bitmap for drawing, adding some extra space for the tail bitmap_argb32 tempbitmap(bmwidth + skewwidth, bmheight + segwidth); @@ -1844,92 +2823,94 @@ protected: // top bar draw_segment_horizontal(tempbitmap, - 0 + 2*segwidth/3, bmwidth - 2*segwidth/3, 0 + segwidth/2, - segwidth, (state & (1 << 0)) ? onpen : offpen); + 0 + 2*segwidth/3, bmwidth - 2*segwidth/3, 0 + segwidth/2, + segwidth, (state & (1 << 0)) ? onpen : offpen); // right-top bar draw_segment_vertical(tempbitmap, - 0 + 2*segwidth/3, bmheight/2 - segwidth/3, bmwidth - segwidth/2, - segwidth, (state & (1 << 1)) ? onpen : offpen); + 0 + 2*segwidth/3, bmheight/2 - segwidth/3, bmwidth - segwidth/2, + segwidth, (state & (1 << 1)) ? onpen : offpen); // right-bottom bar draw_segment_vertical(tempbitmap, - bmheight/2 + segwidth/3, bmheight - 2*segwidth/3, bmwidth - segwidth/2, - segwidth, (state & (1 << 2)) ? onpen : offpen); + bmheight/2 + segwidth/3, bmheight - 2*segwidth/3, bmwidth - segwidth/2, + segwidth, (state & (1 << 2)) ? onpen : offpen); // bottom bar draw_segment_horizontal(tempbitmap, - 0 + 2*segwidth/3, bmwidth - 2*segwidth/3, bmheight - segwidth/2, - segwidth, (state & (1 << 3)) ? onpen : offpen); + 0 + 2*segwidth/3, bmwidth - 2*segwidth/3, bmheight - segwidth/2, + segwidth, (state & (1 << 3)) ? onpen : offpen); // left-bottom bar draw_segment_vertical(tempbitmap, - bmheight/2 + segwidth/3, bmheight - 2*segwidth/3, 0 + segwidth/2, - segwidth, (state & (1 << 4)) ? onpen : offpen); + bmheight/2 + segwidth/3, bmheight - 2*segwidth/3, 0 + segwidth/2, + segwidth, (state & (1 << 4)) ? onpen : offpen); // left-top bar draw_segment_vertical(tempbitmap, - 0 + 2*segwidth/3, bmheight/2 - segwidth/3, 0 + segwidth/2, - segwidth, (state & (1 << 5)) ? onpen : offpen); + 0 + 2*segwidth/3, bmheight/2 - segwidth/3, 0 + segwidth/2, + segwidth, (state & (1 << 5)) ? onpen : offpen); // horizontal-middle-left bar draw_segment_horizontal_caps(tempbitmap, - 0 + 2*segwidth/3, bmwidth/2 - segwidth/10, bmheight/2, - segwidth, LINE_CAP_START, (state & (1 << 6)) ? onpen : offpen); + 0 + 2*segwidth/3, bmwidth/2 - segwidth/10, bmheight/2, + segwidth, LINE_CAP_START, (state & (1 << 6)) ? onpen : offpen); // horizontal-middle-right bar draw_segment_horizontal_caps(tempbitmap, - 0 + bmwidth/2 + segwidth/10, bmwidth - 2*segwidth/3, bmheight/2, - segwidth, LINE_CAP_END, (state & (1 << 7)) ? onpen : offpen); + 0 + bmwidth/2 + segwidth/10, bmwidth - 2*segwidth/3, bmheight/2, + segwidth, LINE_CAP_END, (state & (1 << 7)) ? onpen : offpen); // vertical-middle-top bar draw_segment_vertical_caps(tempbitmap, - 0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3, bmwidth/2, - segwidth, LINE_CAP_NONE, (state & (1 << 8)) ? onpen : offpen); + 0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3, bmwidth/2, + segwidth, LINE_CAP_NONE, (state & (1 << 8)) ? onpen : offpen); // vertical-middle-bottom bar draw_segment_vertical_caps(tempbitmap, - bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3, bmwidth/2, - segwidth, LINE_CAP_NONE, (state & (1 << 9)) ? onpen : offpen); + bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3, bmwidth/2, + segwidth, LINE_CAP_NONE, (state & (1 << 9)) ? onpen : offpen); // diagonal-left-bottom bar draw_segment_diagonal_1(tempbitmap, - 0 + segwidth + segwidth/5, bmwidth/2 - segwidth/2 - segwidth/5, - bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3, - segwidth, (state & (1 << 10)) ? onpen : offpen); + 0 + segwidth + segwidth/5, bmwidth/2 - segwidth/2 - segwidth/5, + bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3, + segwidth, (state & (1 << 10)) ? onpen : offpen); // diagonal-left-top bar draw_segment_diagonal_2(tempbitmap, - 0 + segwidth + segwidth/5, bmwidth/2 - segwidth/2 - segwidth/5, - 0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3, - segwidth, (state & (1 << 11)) ? onpen : offpen); + 0 + segwidth + segwidth/5, bmwidth/2 - segwidth/2 - segwidth/5, + 0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3, + segwidth, (state & (1 << 11)) ? onpen : offpen); // diagonal-right-top bar draw_segment_diagonal_1(tempbitmap, - bmwidth/2 + segwidth/2 + segwidth/5, bmwidth - segwidth - segwidth/5, - 0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3, - segwidth, (state & (1 << 12)) ? onpen : offpen); + bmwidth/2 + segwidth/2 + segwidth/5, bmwidth - segwidth - segwidth/5, + 0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3, + segwidth, (state & (1 << 12)) ? onpen : offpen); // diagonal-right-bottom bar draw_segment_diagonal_2(tempbitmap, - bmwidth/2 + segwidth/2 + segwidth/5, bmwidth - segwidth - segwidth/5, - bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3, - segwidth, (state & (1 << 13)) ? onpen : offpen); + bmwidth/2 + segwidth/2 + segwidth/5, bmwidth - segwidth - segwidth/5, + bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3, + segwidth, (state & (1 << 13)) ? onpen : offpen); // apply skew apply_skew(tempbitmap, 40); // comma tail draw_segment_diagonal_1(tempbitmap, - bmwidth - (segwidth/2), bmwidth + segwidth, - bmheight - (segwidth), bmheight + segwidth*1.5, - segwidth/2, (state & (1 << 15)) ? onpen : offpen); + bmwidth - (segwidth/2), bmwidth + segwidth, + bmheight - (segwidth), bmheight + segwidth*1.5, + segwidth/2, (state & (1 << 15)) ? onpen : offpen); // decimal point - draw_segment_decimal(tempbitmap, bmwidth + segwidth/2, bmheight - segwidth/2, segwidth, (state & (1 << 14)) ? onpen : offpen); + draw_segment_decimal(tempbitmap, + bmwidth + segwidth/2, bmheight - segwidth/2, + segwidth, (state & (1 << 14)) ? onpen : offpen); // resample to the target size - render_resample_argb_bitmap_hq(dest, tempbitmap, color()); + render_resample_argb_bitmap_hq(dest, tempbitmap, color(state)); } }; @@ -1939,8 +2920,8 @@ class layout_element::led16segsc_component : public component { public: // construction/destruction - led16segsc_component(environment &env, util::xml::data_node const &compnode, const char *dirname) - : component(env, compnode, dirname) + led16segsc_component(environment &env, util::xml::data_node const &compnode) + : component(env, compnode) { } @@ -1948,16 +2929,16 @@ protected: // overrides virtual int maxstate() const override { return 262143; } - virtual void draw(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override + virtual void draw_aligned(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override { - const rgb_t onpen = rgb_t(0xff, 0xff, 0xff, 0xff); - const rgb_t offpen = rgb_t(0x20, 0xff, 0xff, 0xff); + rgb_t const onpen = rgb_t(0xff, 0xff, 0xff, 0xff); + rgb_t const offpen = rgb_t(0x20, 0xff, 0xff, 0xff); // sizes for computation - int bmwidth = 250; - int bmheight = 400; - int segwidth = 40; - int skewwidth = 40; + int const bmwidth = 250; + int const bmheight = 400; + int const segwidth = 40; + int const skewwidth = 40; // allocate a temporary bitmap for drawing bitmap_argb32 tempbitmap(bmwidth + skewwidth, bmheight + segwidth); @@ -1965,144 +2946,104 @@ protected: // top-left bar draw_segment_horizontal_caps(tempbitmap, - 0 + 2*segwidth/3, bmwidth/2 - segwidth/10, 0 + segwidth/2, - segwidth, LINE_CAP_START, (state & (1 << 0)) ? onpen : offpen); + 0 + 2*segwidth/3, bmwidth/2 - segwidth/10, 0 + segwidth/2, + segwidth, LINE_CAP_START, (state & (1 << 0)) ? onpen : offpen); // top-right bar draw_segment_horizontal_caps(tempbitmap, - 0 + bmwidth/2 + segwidth/10, bmwidth - 2*segwidth/3, 0 + segwidth/2, - segwidth, LINE_CAP_END, (state & (1 << 1)) ? onpen : offpen); + 0 + bmwidth/2 + segwidth/10, bmwidth - 2*segwidth/3, 0 + segwidth/2, + segwidth, LINE_CAP_END, (state & (1 << 1)) ? onpen : offpen); // right-top bar draw_segment_vertical(tempbitmap, - 0 + 2*segwidth/3, bmheight/2 - segwidth/3, bmwidth - segwidth/2, - segwidth, (state & (1 << 2)) ? onpen : offpen); + 0 + 2*segwidth/3, bmheight/2 - segwidth/3, bmwidth - segwidth/2, + segwidth, (state & (1 << 2)) ? onpen : offpen); // right-bottom bar draw_segment_vertical(tempbitmap, - bmheight/2 + segwidth/3, bmheight - 2*segwidth/3, bmwidth - segwidth/2, - segwidth, (state & (1 << 3)) ? onpen : offpen); + bmheight/2 + segwidth/3, bmheight - 2*segwidth/3, bmwidth - segwidth/2, + segwidth, (state & (1 << 3)) ? onpen : offpen); // bottom-right bar draw_segment_horizontal_caps(tempbitmap, - 0 + bmwidth/2 + segwidth/10, bmwidth - 2*segwidth/3, bmheight - segwidth/2, - segwidth, LINE_CAP_END, (state & (1 << 4)) ? onpen : offpen); + 0 + bmwidth/2 + segwidth/10, bmwidth - 2*segwidth/3, bmheight - segwidth/2, + segwidth, LINE_CAP_END, (state & (1 << 4)) ? onpen : offpen); // bottom-left bar draw_segment_horizontal_caps(tempbitmap, - 0 + 2*segwidth/3, bmwidth/2 - segwidth/10, bmheight - segwidth/2, - segwidth, LINE_CAP_START, (state & (1 << 5)) ? onpen : offpen); + 0 + 2*segwidth/3, bmwidth/2 - segwidth/10, bmheight - segwidth/2, + segwidth, LINE_CAP_START, (state & (1 << 5)) ? onpen : offpen); // left-bottom bar draw_segment_vertical(tempbitmap, - bmheight/2 + segwidth/3, bmheight - 2*segwidth/3, 0 + segwidth/2, - segwidth, (state & (1 << 6)) ? onpen : offpen); + bmheight/2 + segwidth/3, bmheight - 2*segwidth/3, 0 + segwidth/2, + segwidth, (state & (1 << 6)) ? onpen : offpen); // left-top bar draw_segment_vertical(tempbitmap, - 0 + 2*segwidth/3, bmheight/2 - segwidth/3, 0 + segwidth/2, - segwidth, (state & (1 << 7)) ? onpen : offpen); + 0 + 2*segwidth/3, bmheight/2 - segwidth/3, 0 + segwidth/2, + segwidth, (state & (1 << 7)) ? onpen : offpen); // horizontal-middle-left bar draw_segment_horizontal_caps(tempbitmap, - 0 + 2*segwidth/3, bmwidth/2 - segwidth/10, bmheight/2, - segwidth, LINE_CAP_START, (state & (1 << 8)) ? onpen : offpen); + 0 + 2*segwidth/3, bmwidth/2 - segwidth/10, bmheight/2, + segwidth, LINE_CAP_START, (state & (1 << 8)) ? onpen : offpen); // horizontal-middle-right bar draw_segment_horizontal_caps(tempbitmap, - 0 + bmwidth/2 + segwidth/10, bmwidth - 2*segwidth/3, bmheight/2, - segwidth, LINE_CAP_END, (state & (1 << 9)) ? onpen : offpen); + 0 + bmwidth/2 + segwidth/10, bmwidth - 2*segwidth/3, bmheight/2, + segwidth, LINE_CAP_END, (state & (1 << 9)) ? onpen : offpen); // vertical-middle-top bar draw_segment_vertical_caps(tempbitmap, - 0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3, bmwidth/2, - segwidth, LINE_CAP_NONE, (state & (1 << 10)) ? onpen : offpen); + 0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3, bmwidth/2, + segwidth, LINE_CAP_NONE, (state & (1 << 10)) ? onpen : offpen); // vertical-middle-bottom bar draw_segment_vertical_caps(tempbitmap, - bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3, bmwidth/2, - segwidth, LINE_CAP_NONE, (state & (1 << 11)) ? onpen : offpen); + bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3, bmwidth/2, + segwidth, LINE_CAP_NONE, (state & (1 << 11)) ? onpen : offpen); // diagonal-left-bottom bar draw_segment_diagonal_1(tempbitmap, - 0 + segwidth + segwidth/5, bmwidth/2 - segwidth/2 - segwidth/5, - bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3, - segwidth, (state & (1 << 12)) ? onpen : offpen); + 0 + segwidth + segwidth/5, bmwidth/2 - segwidth/2 - segwidth/5, + bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3, + segwidth, (state & (1 << 12)) ? onpen : offpen); // diagonal-left-top bar draw_segment_diagonal_2(tempbitmap, - 0 + segwidth + segwidth/5, bmwidth/2 - segwidth/2 - segwidth/5, - 0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3, - segwidth, (state & (1 << 13)) ? onpen : offpen); + 0 + segwidth + segwidth/5, bmwidth/2 - segwidth/2 - segwidth/5, + 0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3, + segwidth, (state & (1 << 13)) ? onpen : offpen); // diagonal-right-top bar draw_segment_diagonal_1(tempbitmap, - bmwidth/2 + segwidth/2 + segwidth/5, bmwidth - segwidth - segwidth/5, - 0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3, - segwidth, (state & (1 << 14)) ? onpen : offpen); + bmwidth/2 + segwidth/2 + segwidth/5, bmwidth - segwidth - segwidth/5, + 0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3, + segwidth, (state & (1 << 14)) ? onpen : offpen); // diagonal-right-bottom bar draw_segment_diagonal_2(tempbitmap, - bmwidth/2 + segwidth/2 + segwidth/5, bmwidth - segwidth - segwidth/5, - bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3, - segwidth, (state & (1 << 15)) ? onpen : offpen); - - // comma tail - draw_segment_diagonal_1(tempbitmap, - bmwidth - (segwidth/2), bmwidth + segwidth, - bmheight - (segwidth), bmheight + segwidth*1.5, - segwidth/2, (state & (1 << 17)) ? onpen : offpen); - - // decimal point (draw last for priority) - draw_segment_decimal(tempbitmap, bmwidth + segwidth/2, bmheight - segwidth/2, segwidth, (state & (1 << 16)) ? onpen : offpen); + bmwidth/2 + segwidth/2 + segwidth/5, bmwidth - segwidth - segwidth/5, + bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3, + segwidth, (state & (1 << 15)) ? onpen : offpen); // apply skew apply_skew(tempbitmap, 40); - // resample to the target size - render_resample_argb_bitmap_hq(dest, tempbitmap, color()); - } -}; - - -// row of dots for a dotmatrix -class layout_element::dotmatrix_component : public component -{ -public: - // construction/destruction - dotmatrix_component(int dots, environment &env, util::xml::data_node const &compnode, const char *dirname) - : component(env, compnode, dirname) - , m_dots(dots) - { - } - -protected: - // overrides - virtual int maxstate() const override { return (1 << m_dots) - 1; } - - virtual void draw(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override - { - const rgb_t onpen = rgb_t(0xff, 0xff, 0xff, 0xff); - const rgb_t offpen = rgb_t(0xff, 0x20, 0x20, 0x20); - - // sizes for computation - int bmheight = 300; - int dotwidth = 250; - - // allocate a temporary bitmap for drawing - bitmap_argb32 tempbitmap(dotwidth*m_dots, bmheight); - tempbitmap.fill(rgb_t(0xff, 0x00, 0x00, 0x00)); + // comma tail + draw_segment_diagonal_1(tempbitmap, + bmwidth - (segwidth/2), bmwidth + segwidth, bmheight - (segwidth), bmheight + segwidth*1.5, + segwidth/2, (state & (1 << 17)) ? onpen : offpen); - for (int i = 0; i < m_dots; i++) - draw_segment_decimal(tempbitmap, ((dotwidth / 2) + (i * dotwidth)), bmheight / 2, dotwidth, BIT(state, i) ? onpen : offpen); + // decimal point (draw last for priority) + draw_segment_decimal(tempbitmap, + bmwidth + segwidth/2, bmheight - segwidth/2, + segwidth, (state & (1 << 16)) ? onpen : offpen); // resample to the target size - render_resample_argb_bitmap_hq(dest, tempbitmap, color()); + render_resample_argb_bitmap_hq(dest, tempbitmap, color(state)); } - -private: - // internal state - int m_dots; }; @@ -2111,8 +3052,8 @@ class layout_element::simplecounter_component : public component { public: // construction/destruction - simplecounter_component(environment &env, util::xml::data_node const &compnode, const char *dirname) - : component(env, compnode, dirname) + simplecounter_component(environment &env, util::xml::data_node const &compnode) + : component(env, compnode) , m_digits(env.get_attribute_int(compnode, "digits", 2)) , m_textalign(env.get_attribute_int(compnode, "align", 0)) , m_maxstate(env.get_attribute_int(compnode, "maxstate", 999)) @@ -2123,12 +3064,10 @@ protected: // overrides virtual int maxstate() const override { return m_maxstate; } - virtual void draw(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override + virtual void draw_aligned(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override { - render_font *font = machine.render().font_alloc("default"); - std::string temp = string_format("%0*d", m_digits, state); - draw_text(*font, dest, bounds, temp.c_str(), m_textalign); - machine.render().font_free(font); + auto font = machine.render().font_alloc("default"); + draw_text(*font, dest, bounds, string_format("%0*d", m_digits, state), m_textalign, color(state)); } private: @@ -2146,49 +3085,32 @@ class layout_element::reel_component : public component public: // construction/destruction - reel_component(environment &env, util::xml::data_node const &compnode, const char *dirname) - : component(env, compnode, dirname) + reel_component(environment &env, util::xml::data_node const &compnode) + : component(env, compnode) + , m_searchpath(env.search_path() ? env.search_path() : "") + , m_dirname(env.directory_name() ? env.directory_name() : "") { - for (auto & elem : m_hasalpha) - elem = false; + osd_printf_warning("Warning: layout file contains deprecated reel component\n"); - std::string symbollist = env.get_attribute_string(compnode, "symbollist", "0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15"); + std::string_view symbollist = env.get_attribute_string(compnode, "symbollist", "0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15"); // split out position names from string and figure out our number of symbols - int location; m_numstops = 0; - location=symbollist.find(","); - while (location!=-1) + for (std::string_view::size_type location = symbollist.find(','); std::string_view::npos != location; location = symbollist.find(',')) { - m_stopnames[m_numstops] = symbollist; - m_stopnames[m_numstops] = m_stopnames[m_numstops].substr(0, location); - symbollist = symbollist.substr(location+1, symbollist.length()-(location-1)); + m_stopnames[m_numstops] = symbollist.substr(0, location); + symbollist.remove_prefix(location + 1); m_numstops++; - location=symbollist.find(","); } m_stopnames[m_numstops++] = symbollist; - // careful, dirname is nullptr if we're coming from internal layout, and our string assignment doesn't like that - if (dirname != nullptr) - m_dirname = dirname; - - for (int i=0;i<m_numstops;i++) + for (int i = 0; i < m_numstops; i++) { - location=m_stopnames[i].find(":"); - if (location!=-1) + std::string::size_type const location = m_stopnames[i].find(':'); + if (location != std::string::npos) { - m_imagefile[i] = m_stopnames[i]; - m_stopnames[i] = m_stopnames[i].substr(0, location); - m_imagefile[i] = m_imagefile[i].substr(location+1, m_imagefile[i].length()-(location-1)); - - //m_alphafile[i] = - m_file[i] = std::make_unique<emu_file>(env.machine().options().art_path(), OPEN_FLAG_READ); - } - else - { - //m_imagefile[i] = 0; - //m_alphafile[i] = 0; - m_file[i].reset(); + m_imagefile[i] = m_stopnames[i].substr(location + 1); + m_stopnames[i].erase(location); } } @@ -2198,10 +3120,21 @@ public: m_beltreel = env.get_attribute_int(compnode, "beltreel", 0); } -protected: // overrides + virtual void preload(running_machine &machine) override + { + for (int i = 0; i < m_numstops; i++) + { + if (!m_imagefile[i].empty() && !m_bitmap[i].valid()) + load_reel_bitmap(i); + } + + } + +protected: virtual int maxstate() const override { return 65535; } - virtual void draw(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override + + virtual void draw_aligned(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override { if (m_beltreel) { @@ -2215,22 +3148,19 @@ protected: // shift the reels a bit based on this param, allows fine tuning int use_state = (state + m_stateoffset) % max_state_used; - // compute premultiplied colors - u32 r = color().r * 255.0f; - u32 g = color().g * 255.0f; - u32 b = color().b * 255.0f; - u32 a = color().a * 255.0f; - - // get the width of the string - render_font *font = machine.render().font_alloc("default"); - float aspect = 1.0f; - s32 width; + // compute premultiplied color + render_color const c(color(state)); + u32 const r = c.r * 255.0f; + u32 const g = c.g * 255.0f; + u32 const b = c.b * 255.0f; + u32 const a = c.a * 255.0f; int curry = 0; int num_shown = m_numsymbolsvisible; int ourheight = bounds.height(); + auto font = machine.render().font_alloc("default"); for (int fruit = 0;fruit<m_numstops;fruit++) { int basey; @@ -2252,50 +3182,34 @@ protected: int endpos = basey+ourheight/num_shown; - // only render the symbol / text if it's atually in view because the code is SLOW + // only render the symbol / text if it's actually in view because the code is SLOW if ((endpos >= bounds.top()) && (basey <= bounds.bottom())) { - while (1) - { - width = font->string_width(ourheight / num_shown, aspect, m_stopnames[fruit].c_str()); - if (width < bounds.width()) - break; - aspect *= 0.9f; - } - - s32 curx; - curx = bounds.left() + (bounds.width() - width) / 2; - - if (m_file[fruit]) - if (!m_bitmap[fruit].valid()) - load_reel_bitmap(fruit); + if (!m_imagefile[fruit].empty() && !m_bitmap[fruit].valid()) + load_reel_bitmap(fruit); - if (m_file[fruit]) // render gfx + if (m_bitmap[fruit].valid()) // render gfx { bitmap_argb32 tempbitmap2(dest.width(), ourheight/num_shown); + render_resample_argb_bitmap_hq(tempbitmap2, m_bitmap[fruit], c); - if (m_bitmap[fruit].valid()) + for (int y = 0; y < ourheight/num_shown; y++) { - render_resample_argb_bitmap_hq(tempbitmap2, m_bitmap[fruit], color()); + int effy = basey + y; - for (int y = 0; y < ourheight/num_shown; y++) + if (effy >= bounds.top() && effy <= bounds.bottom()) { - int effy = basey + y; - - if (effy >= bounds.top() && effy <= bounds.bottom()) + u32 const *const src = &tempbitmap2.pix(y); + u32 *const d = &dest.pix(effy); + for (int x = 0; x < dest.width(); x++) { - u32 *src = &tempbitmap2.pix32(y); - u32 *d = &dest.pix32(effy); - for (int x = 0; x < dest.width(); x++) + int effx = x; + if (effx >= bounds.left() && effx <= bounds.right()) { - int effx = x; - if (effx >= bounds.left() && effx <= bounds.right()) + u32 spix = rgb_t(src[x]).a(); + if (spix != 0) { - u32 spix = rgb_t(src[x]).a(); - if (spix != 0) - { - d[effx] = src[x]; - } + d[effx] = src[x]; } } } @@ -2307,15 +3221,26 @@ protected: // allocate a temporary bitmap bitmap_argb32 tempbitmap(dest.width(), dest.height()); - const char *origs = m_stopnames[fruit].c_str(); - const char *ends = origs + strlen(origs); - const char *s = origs; - char32_t schar; + // get the width of the string + float aspect = 1.0f; + s32 width; + + while (1) + { + width = font->string_width(ourheight / num_shown, aspect, m_stopnames[fruit]); + if (width < bounds.width()) + break; + aspect *= 0.95f; + } + + float curx = bounds.left() + (bounds.width() - width) / 2.0f; // loop over characters - while (*s != 0) + std::string_view s = m_stopnames[fruit]; + while (!s.empty()) { - int scharcount = uchar_from_utf8(&schar, s, ends - s); + char32_t schar; + int scharcount = uchar_from_utf8(&schar, s); if (scharcount == -1) break; @@ -2331,11 +3256,11 @@ protected: if (effy >= bounds.top() && effy <= bounds.bottom()) { - u32 *src = &tempbitmap.pix32(y); - u32 *d = &dest.pix32(effy); + u32 const *const src = &tempbitmap.pix(y); + u32 *const d = &dest.pix(effy); for (int x = 0; x < chbounds.width(); x++) { - int effx = curx + x + chbounds.left(); + int effx = int(curx) + x + chbounds.left(); if (effx >= bounds.left() && effx <= bounds.right()) { u32 spix = rgb_t(src[x]).a(); @@ -2355,7 +3280,7 @@ protected: // advance in the X direction curx += font->char_width(ourheight/num_shown, aspect, schar); - s += scharcount; + s.remove_prefix(scharcount); } } } @@ -2364,7 +3289,6 @@ protected: } // free the temporary bitmap and font - machine.render().font_free(font); } private: @@ -2376,21 +3300,19 @@ private: // shift the reels a bit based on this param, allows fine tuning int use_state = (state + m_stateoffset) % max_state_used; - // compute premultiplied colors - u32 r = color().r * 255.0f; - u32 g = color().g * 255.0f; - u32 b = color().b * 255.0f; - u32 a = color().a * 255.0f; + // compute premultiplied color + render_color const c(color(state)); + u32 const r = c.r * 255.0f; + u32 const g = c.g * 255.0f; + u32 const b = c.b * 255.0f; + u32 const a = c.a * 255.0f; - // get the width of the string - render_font *font = machine.render().font_alloc("default"); - float aspect = 1.0f; - s32 width; int currx = 0; int num_shown = m_numsymbolsvisible; int ourwidth = bounds.width(); + auto font = machine.render().font_alloc("default"); for (int fruit = 0;fruit<m_numstops;fruit++) { int basex; @@ -2411,71 +3333,65 @@ private: int endpos = basex+(ourwidth/num_shown); - // only render the symbol / text if it's atually in view because the code is SLOW + // only render the symbol / text if it's actually in view because the code is SLOW if ((endpos >= bounds.left()) && (basex <= bounds.right())) { - while (1) - { - width = font->string_width(dest.height(), aspect, m_stopnames[fruit].c_str()); - if (width < bounds.width()) - break; - aspect *= 0.9f; - } - - s32 curx; - curx = bounds.left(); - - if (m_file[fruit]) - if (!m_bitmap[fruit].valid()) - load_reel_bitmap(fruit); + if (!m_imagefile[fruit].empty() && !m_bitmap[fruit].valid()) + load_reel_bitmap(fruit); - if (m_file[fruit]) // render gfx + if (m_bitmap[fruit].valid()) // render gfx { bitmap_argb32 tempbitmap2(ourwidth/num_shown, dest.height()); + render_resample_argb_bitmap_hq(tempbitmap2, m_bitmap[fruit], c); - if (m_bitmap[fruit].valid()) + for (int y = 0; y < dest.height(); y++) { - render_resample_argb_bitmap_hq(tempbitmap2, m_bitmap[fruit], color()); + int effy = y; - for (int y = 0; y < dest.height(); y++) + if (effy >= bounds.top() && effy <= bounds.bottom()) { - int effy = y; - - if (effy >= bounds.top() && effy <= bounds.bottom()) + u32 const *const src = &tempbitmap2.pix(y); + u32 *const d = &dest.pix(effy); + for (int x = 0; x < ourwidth/num_shown; x++) { - u32 *src = &tempbitmap2.pix32(y); - u32 *d = &dest.pix32(effy); - for (int x = 0; x < ourwidth/num_shown; x++) + int effx = basex + x; + if (effx >= bounds.left() && effx <= bounds.right()) { - int effx = basex + x; - if (effx >= bounds.left() && effx <= bounds.right()) + u32 spix = rgb_t(src[x]).a(); + if (spix != 0) { - u32 spix = rgb_t(src[x]).a(); - if (spix != 0) - { - d[effx] = src[x]; - } + d[effx] = src[x]; } } } - } + } } else // render text (fallback) { + // get the width of the string + float aspect = 1.0f; + s32 width; + while (1) + { + width = font->string_width(dest.height(), aspect, m_stopnames[fruit]); + if (width < bounds.width()) + break; + aspect *= 0.95f; + } + + float curx = bounds.left(); + // allocate a temporary bitmap bitmap_argb32 tempbitmap(dest.width(), dest.height()); - const char *origs = m_stopnames[fruit].c_str(); - const char *ends = origs + strlen(origs); - const char *s = origs; - char32_t schar; - // loop over characters - while (*s != 0) + std::string_view s = m_stopnames[fruit]; + while (!s.empty()) { - int scharcount = uchar_from_utf8(&schar, s, ends - s); + char32_t schar; + int scharcount = uchar_from_utf8(&schar, s); if (scharcount == -1) break; @@ -2491,11 +3407,11 @@ private: if (effy >= bounds.top() && effy <= bounds.bottom()) { - u32 *src = &tempbitmap.pix32(y); - u32 *d = &dest.pix32(effy); + u32 const *const src = &tempbitmap.pix(y); + u32 *const d = &dest.pix(effy); for (int x = 0; x < chbounds.width(); x++) { - int effx = basex + curx + x; + int effx = basex + int(curx) + x; if (effx >= bounds.left() && effx <= bounds.right()) { u32 spix = rgb_t(src[x]).a(); @@ -2515,7 +3431,7 @@ private: // advance in the X direction curx += font->char_width(dest.height(), aspect, schar); - s += scharcount; + s.remove_prefix(scharcount); } } } @@ -2524,35 +3440,31 @@ private: } // free the temporary bitmap and font - machine.render().font_free(font); } void load_reel_bitmap(int number) { - // load the basic bitmap - assert(m_file != nullptr); - /*m_hasalpha[number] = */ render_load_png(m_bitmap[number], *m_file[number], m_dirname.c_str(), m_imagefile[number].c_str()); + emu_file file(m_searchpath.empty() ? m_dirname : m_searchpath, OPEN_FLAG_READ); + std::string filename; + if (!m_searchpath.empty()) + filename = m_dirname; + util::path_append(filename, m_imagefile[number]); - // load the alpha bitmap if specified - //if (m_bitmap[number].valid() && m_alphafile[number]) - // render_load_png(m_bitmap[number], *m_file[number], m_dirname, m_alphafile[number], true); + // load the basic bitmap + if (!file.open(filename)) + render_load_png(m_bitmap[number], file); // if we can't load the bitmap just use text rendering if (!m_bitmap[number].valid()) - { - // fallback to text rendering - m_file[number].reset(); - } + m_imagefile[number].clear(); } // internal state bitmap_argb32 m_bitmap[MAX_BITMAPS]; // source bitmap for images + std::string m_searchpath; // asset search path (for lazy loading) std::string m_dirname; // directory name of image file (for lazy loading) - std::unique_ptr<emu_file> m_file[MAX_BITMAPS]; // file object for reading image/alpha files std::string m_imagefile[MAX_BITMAPS]; // name of the image file (for lazy loading) - std::string m_alphafile[MAX_BITMAPS]; // name of the alpha file (for lazy loading) - bool m_hasalpha[MAX_BITMAPS]; // is there any alpha component present? // basically made up of multiple text strings / gfx int m_numstops; @@ -2569,21 +3481,9 @@ private: //------------------------------------------------- template <typename T> -layout_element::component::ptr layout_element::make_component(environment &env, util::xml::data_node const &compnode, const char *dirname) -{ - return std::make_unique<T>(env, compnode, dirname); -} - - -//------------------------------------------------- -// make_component - create dotmatrix component -// with given vertical resolution -//------------------------------------------------- - -template <int D> -layout_element::component::ptr layout_element::make_dotmatrix_component(environment &env, util::xml::data_node const &compnode, const char *dirname) +layout_element::component::ptr layout_element::make_component(environment &env, util::xml::data_node const &compnode) { - return std::make_unique<dotmatrix_component>(D, env, compnode, dirname); + return std::make_unique<T>(env, compnode); } @@ -2616,7 +3516,7 @@ layout_element::texture::texture(texture &&that) : texture() layout_element::texture::~texture() { - if (m_element != nullptr) + if (m_element) m_element->machine().render().texture_free(m_texture); } @@ -2644,11 +3544,35 @@ layout_element::texture &layout_element::texture::operator=(texture &&that) // component - constructor //------------------------------------------------- -layout_element::component::component(environment &env, util::xml::data_node const &compnode, const char *dirname) - : m_state(env.get_attribute_int(compnode, "state", -1)) - , m_color(env.parse_color(compnode.get_child("color"))) +layout_element::component::component(environment &env, util::xml::data_node const &compnode) + : m_statemask(env.get_attribute_int(compnode, "statemask", env.get_attribute_string(compnode, "state").empty() ? 0 : ~0)) + , m_stateval(env.get_attribute_int(compnode, "state", m_statemask) & m_statemask) { - env.parse_bounds(compnode.get_child("bounds"), m_bounds); + for (util::xml::data_node const *child = compnode.get_first_child(); child; child = child->get_next_sibling()) + { + if (!strcmp(child->get_name(), "bounds")) + { + if (!add_bounds_step(env, m_bounds, *child)) + { + throw layout_syntax_error( + util::string_format( + "%s component has duplicate bounds for state", + compnode.get_name())); + } + } + else if (!strcmp(child->get_name(), "color")) + { + if (!add_color_step(env, m_color, *child)) + { + throw layout_syntax_error( + util::string_format( + "%s component has duplicate color for state", + compnode.get_name())); + } + } + } + set_bounds_deltas(m_bounds); + set_color_deltas(m_color); } @@ -2658,25 +3582,142 @@ layout_element::component::component(environment &env, util::xml::data_node cons void layout_element::component::normalize_bounds(float xoffs, float yoffs, float xscale, float yscale) { - m_bounds.x0 = (m_bounds.x0 - xoffs) * xscale; - m_bounds.x1 = (m_bounds.x1 - xoffs) * xscale; - m_bounds.y0 = (m_bounds.y0 - yoffs) * yscale; - m_bounds.y1 = (m_bounds.y1 - yoffs) * yscale; + ::normalize_bounds(m_bounds, 0.0F, 0.0F, xoffs, yoffs, xscale, yscale); } //------------------------------------------------- -// draw_text - draw text in the specified color +// statewrap - get state wraparound requirements +//------------------------------------------------- + +std::pair<int, bool> layout_element::component::statewrap() const +{ + int result(0); + bool fold; + auto const adjustmask = + [&result, &fold] (int val, int mask) + { + assert(!(val & ~mask)); + auto const splatright = + [] (int x) + { + for (unsigned shift = 1; (sizeof(x) * 4) >= shift; shift <<= 1) + x |= (x >> shift); + return x; + }; + int const unfolded(splatright(mask)); + int const folded(splatright(~mask | splatright(val))); + if (unsigned(folded) < unsigned(unfolded)) + { + result |= folded; + fold = true; + } + else + { + result |= unfolded; + } + }; + adjustmask(stateval(), statemask()); + int max(maxstate()); + if (m_bounds.size() > 1U) + max = (std::max)(max, m_bounds.back().state); + if (m_color.size() > 1U) + max = (std::max)(max, m_color.back().state); + if (0 <= max) + adjustmask(max, ~0); + return std::make_pair(result, fold); +} + + +//------------------------------------------------- +// overall_bounds - maximum bounds for all states +//------------------------------------------------- + +render_bounds layout_element::component::overall_bounds() const +{ + return accumulate_bounds(m_bounds); +} + + +//------------------------------------------------- +// bounds - bounds for a given state +//------------------------------------------------- + +render_bounds layout_element::component::bounds(int state) const +{ + return interpolate_bounds(m_bounds, state); +} + + +//------------------------------------------------- +// color - color for a given state +//------------------------------------------------- + +render_color layout_element::component::color(int state) const +{ + return interpolate_color(m_color, state); +} + + +//------------------------------------------------- +// preload - perform expensive operations upfront +//------------------------------------------------- + +void layout_element::component::preload(running_machine &machine) +{ +} + + +//------------------------------------------------- +// draw - draw element to texture for a given +// state +//------------------------------------------------- + +void layout_element::component::draw(running_machine &machine, bitmap_argb32 &dest, int state) +{ + // get the local scaled bounds + render_bounds const curbounds(bounds(state)); + rectangle pixelbounds( + s32(curbounds.x0 * float(dest.width()) + 0.5F), + s32(floorf(curbounds.x1 * float(dest.width()) - 0.5F)), + s32(curbounds.y0 * float(dest.height()) + 0.5F), + s32(floorf(curbounds.y1 * float(dest.height()) - 0.5F))); + + // based on the component type, add to the texture + if (!pixelbounds.empty()) + draw_aligned(machine, dest, pixelbounds, state); +} + + +void layout_element::component::draw_aligned(running_machine &machine, bitmap_argb32 &dest, rectangle const &bounds, int state) +{ + // derived classes must override one form or other + throw false; +} + + +//------------------------------------------------- +// maxstate - maximum state drawn differently //------------------------------------------------- -void layout_element::component::draw_text(render_font &font, bitmap_argb32 &dest, const rectangle &bounds, const char *str, int align) +int layout_element::component::maxstate() const { - // compute premultiplied colors - u32 r = color().r * 255.0f; - u32 g = color().g * 255.0f; - u32 b = color().b * 255.0f; - u32 a = color().a * 255.0f; + return -1; +} + + +//------------------------------------------------- +// draw_text - draw text in the specified color +//------------------------------------------------- +void layout_element::component::draw_text( + render_font &font, + bitmap_argb32 &dest, + const rectangle &bounds, + std::string_view str, + int align, + const render_color &color) +{ // get the width of the string float aspect = 1.0f; s32 width; @@ -2686,11 +3727,11 @@ void layout_element::component::draw_text(render_font &font, bitmap_argb32 &dest width = font.string_width(bounds.height(), aspect, str); if (width < bounds.width()) break; - aspect *= 0.9f; + aspect *= 0.95f; } // get alignment - s32 curx; + float curx; switch (align) { // left @@ -2705,7 +3746,7 @@ void layout_element::component::draw_text(render_font &font, bitmap_argb32 &dest // default to center default: - curx = bounds.left() + (bounds.width() - width) / 2; + curx = bounds.left() + (bounds.width() - width) / 2.0f; break; } @@ -2713,15 +3754,10 @@ void layout_element::component::draw_text(render_font &font, bitmap_argb32 &dest bitmap_argb32 tempbitmap(dest.width(), dest.height()); // loop over characters - const char *origs = str; - const char *ends = origs + strlen(origs); - const char *s = origs; - char32_t schar; - - // loop over characters - while (*s != 0) + while (!str.empty()) { - int scharcount = uchar_from_utf8(&schar, s, ends - s); + char32_t schar; + int scharcount = uchar_from_utf8(&schar, str); if (scharcount == -1) break; @@ -2736,22 +3772,17 @@ void layout_element::component::draw_text(render_font &font, bitmap_argb32 &dest int effy = bounds.top() + y; if (effy >= bounds.top() && effy <= bounds.bottom()) { - u32 *src = &tempbitmap.pix32(y); - u32 *d = &dest.pix32(effy); + u32 const *const src = &tempbitmap.pix(y); + u32 *const d = &dest.pix(effy); for (int x = 0; x < chbounds.width(); x++) { - int effx = curx + x + chbounds.left(); + int effx = int(curx) + x + chbounds.left(); if (effx >= bounds.left() && effx <= bounds.right()) { u32 spix = rgb_t(src[x]).a(); if (spix != 0) { - rgb_t dpix = d[effx]; - u32 ta = (a * (spix + 1)) >> 8; - u32 tr = (r * ta + dpix.r() * (0x100 - ta)) >> 8; - u32 tg = (g * ta + dpix.g() * (0x100 - ta)) >> 8; - u32 tb = (b * ta + dpix.b() * (0x100 - ta)) >> 8; - d[effx] = rgb_t(tr, tg, tb); + alpha_blend(d[effx], color, spix / 255.0); } } } @@ -2760,7 +3791,7 @@ void layout_element::component::draw_text(render_font &font, bitmap_argb32 &dest // advance in the X direction curx += font.char_width(bounds.height(), aspect, schar); - s += scharcount; + str.remove_prefix(scharcount); } } @@ -2776,8 +3807,8 @@ void layout_element::component::draw_segment_horizontal_caps(bitmap_argb32 &dest // loop over the width of the segment for (int y = 0; y < width / 2; y++) { - u32 *d0 = &dest.pix32(midy - y); - u32 *d1 = &dest.pix32(midy + y); + u32 *const d0 = &dest.pix(midy - y); + u32 *const d1 = &dest.pix(midy + y); int ty = (y < width / 8) ? width / 8 : y; // loop over the length of the segment @@ -2809,8 +3840,8 @@ void layout_element::component::draw_segment_vertical_caps(bitmap_argb32 &dest, // loop over the width of the segment for (int x = 0; x < width / 2; x++) { - u32 *d0 = &dest.pix32(0, midx - x); - u32 *d1 = &dest.pix32(0, midx + x); + u32 *const d0 = &dest.pix(0, midx - x); + u32 *const d1 = &dest.pix(0, midx + x); int tx = (x < width / 8) ? width / 8 : x; // loop over the length of the segment @@ -2846,7 +3877,7 @@ void layout_element::component::draw_segment_diagonal_1(bitmap_argb32 &dest, int for (int x = minx; x < maxx; x++) if (x >= 0 && x < dest.width()) { - u32 *d = &dest.pix32(0, x); + u32 *const d = &dest.pix(0, x); int step = (x - minx) * ratio; for (int y = maxy - width - step; y < maxy - step; y++) @@ -2871,7 +3902,7 @@ void layout_element::component::draw_segment_diagonal_2(bitmap_argb32 &dest, int for (int x = minx; x < maxx; x++) if (x >= 0 && x < dest.width()) { - u32 *d = &dest.pix32(0, x); + u32 *const d = &dest.pix(0, x); int step = (x - minx) * ratio; for (int y = miny + step; y < miny + step + width; y++) @@ -2894,8 +3925,8 @@ void layout_element::component::draw_segment_decimal(bitmap_argb32 &dest, int mi // iterate over y for (u32 y = 0; y <= width; y++) { - u32 *d0 = &dest.pix32(midy - y); - u32 *d1 = &dest.pix32(midy + y); + u32 *const d0 = &dest.pix(midy - y); + u32 *const d1 = &dest.pix(midy + y); float xval = width * sqrt(1.0f - (float)(y * y) * ooradius2); s32 left, right; @@ -2923,7 +3954,7 @@ void layout_element::component::draw_segment_comma(bitmap_argb32 &dest, int minx // draw line for (int x = minx; x < maxx; x++) { - u32 *d = &dest.pix32(0, x); + u32 *const d = &dest.pix(0, x); int step = (x - minx) * ratio; for (int y = maxy; y < maxy - width - step; y--) @@ -2940,7 +3971,7 @@ void layout_element::component::apply_skew(bitmap_argb32 &dest, int skewwidth) { for (int y = 0; y < dest.height(); y++) { - u32 *destrow = &dest.pix32(y); + u32 *const destrow = &dest.pix(y); int offs = skewwidth * (dest.height() - y) / dest.height(); for (int x = dest.width() - skewwidth - 1; x >= 0; x--) destrow[x + offs] = destrow[x]; @@ -2963,29 +3994,87 @@ struct layout_view::layer_lists { item_list backdrops, screens, overlays, bezels //------------------------------------------------- layout_view::layout_view( - environment &env, + layout_environment &env, util::xml::data_node const &viewnode, element_map &elemmap, group_map &groupmap) - : m_name(make_name(env, viewnode)) - , m_aspect(1.0f) - , m_scraspect(1.0f) - , m_items() + : m_effaspect(1.0f) + , m_name(make_name(env, viewnode)) + , m_unqualified_name(env.get_attribute_string(viewnode, "name")) + , m_elemmap(elemmap) + , m_defvismask(0U) , m_has_art(false) + , m_show_ptr(false) + , m_ptr_time_out(true) // FIXME: add attribute for this + , m_exp_show_ptr(-1) { + // check for explicit pointer display setting + if (viewnode.get_attribute_string_ptr("showpointers")) + { + m_show_ptr = env.get_attribute_bool(viewnode, "showpointers", false); + m_exp_show_ptr = m_show_ptr ? 1 : 0; + } + // parse the layout m_expbounds.x0 = m_expbounds.y0 = m_expbounds.x1 = m_expbounds.y1 = 0; - environment local(env); + view_environment local(env, m_name.c_str()); layer_lists layers; local.set_parameter("viewname", std::string(m_name)); add_items(layers, local, viewnode, elemmap, groupmap, ROT0, identity_transform, render_color{ 1.0F, 1.0F, 1.0F, 1.0F }, true, false, true); + // can't support legacy layers and modern visibility toggles at the same time + if (!m_vistoggles.empty() && (!layers.backdrops.empty() || !layers.overlays.empty() || !layers.bezels.empty() || !layers.cpanels.empty() || !layers.marquees.empty())) + throw layout_syntax_error("view contains visibility toggles as well as legacy backdrop, overlay, bezel, cpanel and/or marquee elements"); + + // create visibility toggles for legacy layers + u32 mask(1U); + if (!layers.backdrops.empty()) + { + m_vistoggles.emplace_back("Backdrops", mask); + for (item &backdrop : layers.backdrops) + backdrop.m_visibility_mask = mask; + m_defvismask |= mask; + mask <<= 1; + } + if (!layers.overlays.empty()) + { + m_vistoggles.emplace_back("Overlays", mask); + for (item &overlay : layers.overlays) + overlay.m_visibility_mask = mask; + m_defvismask |= mask; + mask <<= 1; + } + if (!layers.bezels.empty()) + { + m_vistoggles.emplace_back("Bezels", mask); + for (item &bezel : layers.bezels) + bezel.m_visibility_mask = mask; + m_defvismask |= mask; + mask <<= 1; + } + if (!layers.cpanels.empty()) + { + m_vistoggles.emplace_back("Control Panels", mask); + for (item &cpanel : layers.cpanels) + cpanel.m_visibility_mask = mask; + m_defvismask |= mask; + mask <<= 1; + } + if (!layers.marquees.empty()) + { + m_vistoggles.emplace_back("Backdrops", mask); + for (item &marquee : layers.marquees) + marquee.m_visibility_mask = mask; + m_defvismask |= mask; + mask <<= 1; + } + // deal with legacy element groupings if (!layers.overlays.empty() || (layers.backdrops.size() <= 1)) { // screens (-1) + overlays (RGB multiply) + backdrop (add) + bezels (alpha) + cpanels (alpha) + marquees (alpha) for (item &backdrop : layers.backdrops) - backdrop.set_blend_mode(BLENDMODE_ADD); + backdrop.m_blend_mode = BLENDMODE_ADD; m_items.splice(m_items.end(), layers.screens); m_items.splice(m_items.end(), layers.overlays); m_items.splice(m_items.end(), layers.backdrops); @@ -3000,7 +4089,7 @@ layout_view::layout_view( for (item &screen : layers.screens) { if (screen.blend_mode() == -1) - screen.set_blend_mode(BLENDMODE_ADD); + screen.m_blend_mode = BLENDMODE_ADD; } m_items.splice(m_items.end(), layers.backdrops); m_items.splice(m_items.end(), layers.screens); @@ -3009,8 +4098,18 @@ layout_view::layout_view( m_items.splice(m_items.end(), layers.marquees); } + // index items with keys supplied + for (item &curitem : m_items) + { + if (!curitem.id().empty()) + { + if (!m_items_by_id.emplace(curitem.id(), curitem).second) + throw layout_syntax_error("view contains item with duplicate id attribute"); + } + } + // calculate metrics - recompute(render_layer_config()); + recompute(default_visibility_mask(), false); for (group_map::value_type &group : groupmap) group.second.set_bounds_unresolved(); } @@ -3026,86 +4125,299 @@ layout_view::~layout_view() //------------------------------------------------- +// get_item - get item by ID +//------------------------------------------------- + +layout_view_item *layout_view::get_item(std::string const &id) +{ + auto const found(m_items_by_id.find(id)); + return (m_items_by_id.end() != found) ? &found->second : nullptr; +} + + +//------------------------------------------------- +// has_screen - return true if this view contains +// the specified screen +//------------------------------------------------- + +bool layout_view::has_screen(screen_device const &screen) const +{ + return std::find_if(m_items.begin(), m_items.end(), [&screen] (auto &itm) { return itm.screen() == &screen; }) != m_items.end(); +} + + +//------------------------------------------------- +// has_visible_screen - return true if this view +// has the given screen visble +//------------------------------------------------- + +bool layout_view::has_visible_screen(screen_device const &screen) const +{ + return std::find_if(m_screens.begin(), m_screens.end(), [&screen] (auto const &scr) { return &scr.get() == &screen; }) != m_screens.end(); +} + + +//------------------------------------------------- +// prepare_items - perform additional tasks +// before rendering a frame +//------------------------------------------------- + +void layout_view::prepare_items() +{ + if (!m_prepare_items.isnull()) + m_prepare_items(); + + for (auto &[name, element] : m_elemmap) + element.prepare(); +} + + +//------------------------------------------------- // recompute - recompute the bounds and aspect // ratio of a view and all of its contained items //------------------------------------------------- -void layout_view::recompute(render_layer_config layerconfig) +void layout_view::recompute(u32 visibility_mask, bool zoom_to_screen) { - // reset the bounds - m_bounds.x0 = m_bounds.y0 = m_bounds.x1 = m_bounds.y1 = 0.0f; - m_scrbounds.x0 = m_scrbounds.y0 = m_scrbounds.x1 = m_scrbounds.y1 = 0.0f; - m_screens.reset(); - - // loop over all layers + // reset the bounds and collected active items + render_bounds scrbounds{ 0.0f, 0.0f, 0.0f, 0.0f }; + m_bounds = scrbounds; + m_visible_items.clear(); + m_screen_items.clear(); + m_interactive_items.clear(); + m_interactive_edges_x.clear(); + m_interactive_edges_y.clear(); + m_screens.clear(); + + // loop over items and filter by visibility mask bool first = true; bool scrfirst = true; + bool haveinput = false; for (item &curitem : m_items) { - // accumulate bounds - if (first) - m_bounds = curitem.m_rawbounds; - else - union_render_bounds(m_bounds, curitem.m_rawbounds); - first = false; - - // accumulate screen bounds - if (curitem.m_screen) + if ((visibility_mask & curitem.visibility_mask()) == curitem.visibility_mask()) { - if (scrfirst) - m_scrbounds = curitem.m_rawbounds; + render_bounds const rawbounds = accumulate_bounds(curitem.m_rawbounds); + + // accumulate bounds + m_visible_items.emplace_back(curitem); + if (first) + m_bounds = rawbounds; else - union_render_bounds(m_scrbounds, curitem.m_rawbounds); - scrfirst = false; + m_bounds |= rawbounds; + first = false; + + // accumulate visible screens and their bounds bounds + if (curitem.screen()) + { + if (scrfirst) + scrbounds = rawbounds; + else + scrbounds |= rawbounds; + scrfirst = false; + + // accumulate active screens + m_screen_items.emplace_back(curitem); + m_screens.emplace_back(*curitem.screen()); + } - // accumulate the screens in use while we're scanning - m_screens.add(*curitem.m_screen); + // accumulate interactive elements + if (!curitem.clickthrough() || curitem.has_input()) + m_interactive_items.emplace_back(curitem); + if (curitem.has_input()) + haveinput = true; } } + // if show pointers isn't explicitly, update it based on visible items + if (0 > m_exp_show_ptr) + m_show_ptr = haveinput; + // if we have an explicit bounds, override it if (m_expbounds.x1 > m_expbounds.x0) m_bounds = m_expbounds; - // if we're handling things normally, the target bounds are (0,0)-(1,1) render_bounds target_bounds; - if (!layerconfig.zoom_to_screen() || m_screens.count() == 0) + if (!zoom_to_screen || scrfirst) { - // compute the aspect ratio of the view - m_aspect = (m_bounds.x1 - m_bounds.x0) / (m_bounds.y1 - m_bounds.y0); - + // if we're handling things normally, the target bounds are (0,0)-(1,1) + m_effaspect = ((m_bounds.x1 > m_bounds.x0) && (m_bounds.y1 > m_bounds.y0)) ? m_bounds.aspect() : 1.0f; target_bounds.x0 = target_bounds.y0 = 0.0f; target_bounds.x1 = target_bounds.y1 = 1.0f; } - - // if we're cropping, we want the screen area to fill (0,0)-(1,1) else { - // compute the aspect ratio of the screen - m_scraspect = (m_scrbounds.x1 - m_scrbounds.x0) / (m_scrbounds.y1 - m_scrbounds.y0); - - float targwidth = (m_bounds.x1 - m_bounds.x0) / (m_scrbounds.x1 - m_scrbounds.x0); - float targheight = (m_bounds.y1 - m_bounds.y0) / (m_scrbounds.y1 - m_scrbounds.y0); - target_bounds.x0 = (m_bounds.x0 - m_scrbounds.x0) / (m_bounds.x1 - m_bounds.x0) * targwidth; - target_bounds.y0 = (m_bounds.y0 - m_scrbounds.y0) / (m_bounds.y1 - m_bounds.y0) * targheight; - target_bounds.x1 = target_bounds.x0 + targwidth; - target_bounds.y1 = target_bounds.y0 + targheight; + // if we're cropping, we want the screen area to fill (0,0)-(1,1) + m_effaspect = ((scrbounds.x1 > scrbounds.x0) && (scrbounds.y1 > scrbounds.y0)) ? scrbounds.aspect() : 1.0f; + target_bounds.x0 = (m_bounds.x0 - scrbounds.x0) / scrbounds.width(); + target_bounds.y0 = (m_bounds.y0 - scrbounds.y0) / scrbounds.height(); + target_bounds.x1 = target_bounds.x0 + (m_bounds.width() / scrbounds.width()); + target_bounds.y1 = target_bounds.y0 + (m_bounds.height() / scrbounds.height()); } // determine the scale/offset for normalization - float xoffs = m_bounds.x0; - float yoffs = m_bounds.y0; - float xscale = (target_bounds.x1 - target_bounds.x0) / (m_bounds.x1 - m_bounds.x0); - float yscale = (target_bounds.y1 - target_bounds.y0) / (m_bounds.y1 - m_bounds.y0); + float const xoffs = m_bounds.x0; + float const yoffs = m_bounds.y0; + float const xscale = target_bounds.width() / m_bounds.width(); + float const yscale = target_bounds.height() / m_bounds.height(); // normalize all the item bounds for (item &curitem : items()) { - curitem.m_bounds.x0 = target_bounds.x0 + (curitem.m_rawbounds.x0 - xoffs) * xscale; - curitem.m_bounds.x1 = target_bounds.x0 + (curitem.m_rawbounds.x1 - xoffs) * xscale; - curitem.m_bounds.y0 = target_bounds.y0 + (curitem.m_rawbounds.y0 - yoffs) * yscale; - curitem.m_bounds.y1 = target_bounds.y0 + (curitem.m_rawbounds.y1 - yoffs) * yscale; + assert(curitem.m_rawbounds.size() == curitem.m_bounds.size()); + std::copy(curitem.m_rawbounds.begin(), curitem.m_rawbounds.end(), curitem.m_bounds.begin()); + normalize_bounds(curitem.m_bounds, target_bounds.x0, target_bounds.y0, xoffs, yoffs, xscale, yscale); + } + + // sort edges of interactive items + LOGMASKED(LOG_INTERACTIVE_ITEMS, "Recalculated view '%s' with %u interactive items\n", + name(), m_interactive_items.size()); + //std::reverse(m_interactive_items.begin(), m_interactive_items.end()); TODO: flip hit test order to match visual order + m_interactive_edges_x.reserve(m_interactive_items.size() * 2); + m_interactive_edges_y.reserve(m_interactive_items.size() * 2); + for (unsigned i = 0; m_interactive_items.size() > i; ++i) + { + item &curitem(m_interactive_items[i]); + render_bounds const curbounds(accumulate_bounds(curitem.m_bounds)); + LOGMASKED(LOG_INTERACTIVE_ITEMS, "%u: (%s %s %s %s) hasinput=%s clickthrough=%s\n", + i, curbounds.x0, curbounds.y0, curbounds.x1, curbounds.y1, curitem.has_input(), curitem.clickthrough()); + m_interactive_edges_x.emplace_back(i, curbounds.x0, false); + m_interactive_edges_x.emplace_back(i, curbounds.x1, true); + m_interactive_edges_y.emplace_back(i, curbounds.y0, false); + m_interactive_edges_y.emplace_back(i, curbounds.y1, true); + } + std::sort(m_interactive_edges_x.begin(), m_interactive_edges_x.end()); + std::sort(m_interactive_edges_y.begin(), m_interactive_edges_y.end()); + + if (VERBOSE & LOG_INTERACTIVE_ITEMS) + { + for (edge const &e : m_interactive_edges_x) + LOGMASKED(LOG_INTERACTIVE_ITEMS, "x=%s %c%u\n", e.position(), e.trailing() ? ']' : '[', e.index()); + for (edge const &e : m_interactive_edges_y) + LOGMASKED(LOG_INTERACTIVE_ITEMS, "y=%s %c%u\n", e.position(), e.trailing() ? ']' : '[', e.index()); } + + // additional actions typically supplied by script + if (!m_recomputed.isnull()) + m_recomputed(); +} + + +//------------------------------------------------- +// set_show_pointers - set whether pointers +// should be displayed +//------------------------------------------------- + +void layout_view::set_show_pointers(bool value) noexcept +{ + m_show_ptr = value; + m_exp_show_ptr = value ? 1 : 0; +} + + +//------------------------------------------------- +// set_pointers_time_out - set whether pointers +// should be hidden after inactivity +//------------------------------------------------- + +void layout_view::set_hide_inactive_pointers(bool value) noexcept +{ + m_ptr_time_out = value; +} + + +//------------------------------------------------- +// set_prepare_items_callback - set handler called +// before adding items to render target +//------------------------------------------------- + +void layout_view::set_prepare_items_callback(prepare_items_delegate &&handler) +{ + m_prepare_items = std::move(handler); +} + + +//------------------------------------------------- +// set_preload_callback - set handler called +// after preloading elements +//------------------------------------------------- + +void layout_view::set_preload_callback(preload_delegate &&handler) +{ + m_preload = std::move(handler); +} + + +//------------------------------------------------- +// set_recomputed_callback - set handler called +// after recomputing item bounds +//------------------------------------------------- + +void layout_view::set_recomputed_callback(recomputed_delegate &&handler) +{ + m_recomputed = std::move(handler); +} + + +//------------------------------------------------- +// set_pointer_updated_callback - set handler +// called for pointer input +//------------------------------------------------- + +void layout_view::set_pointer_updated_callback(pointer_updated_delegate &&handler) +{ + m_pointer_updated = std::move(handler); +} + + +//------------------------------------------------- +// set_pointer_left_callback - set handler for +// pointer leaving normally +//------------------------------------------------- + +void layout_view::set_pointer_left_callback(pointer_left_delegate &&handler) +{ + m_pointer_left = std::move(handler); +} + + +//------------------------------------------------- +// set_pointer_aborted_callback - set handler for +// pointer leaving abnormally +//------------------------------------------------- + +void layout_view::set_pointer_aborted_callback(pointer_left_delegate &&handler) +{ + m_pointer_aborted = std::move(handler); +} + + +//------------------------------------------------- +// set_forget_pointers_callback - set handler for +// abandoning pointer input +//------------------------------------------------- + +void layout_view::set_forget_pointers_callback(forget_pointers_delegate &&handler) +{ + m_forget_pointers = std::move(handler); +} + + +//------------------------------------------------- +// preload - perform expensive loading upfront +// for visible elements +//------------------------------------------------- + +void layout_view::preload() +{ + for (item &curitem : m_visible_items) + { + if (curitem.element()) + curitem.element()->preload(); + } + + if (!m_preload.isnull()) + m_preload(); } @@ -3126,7 +4438,7 @@ void layout_view::resolve_tags() void layout_view::add_items( layer_lists &layers, - environment &env, + view_environment &env, util::xml::data_node const &parentnode, element_map &elemmap, group_map &groupmap, @@ -3161,11 +4473,6 @@ void layout_view::add_items( else env.set_repeat_parameter(*itemnode, init); } - else if (!strcmp(itemnode->get_name(), "backdrop")) - { - layers.backdrops.emplace_back(env, *itemnode, elemmap, orientation, trans, color); - m_has_art = true; - } else if (!strcmp(itemnode->get_name(), "screen")) { layers.screens.emplace_back(env, *itemnode, elemmap, orientation, trans, color); @@ -3175,31 +4482,46 @@ void layout_view::add_items( layers.screens.emplace_back(env, *itemnode, elemmap, orientation, trans, color); m_has_art = true; } + else if (!strcmp(itemnode->get_name(), "backdrop")) + { + if (layers.backdrops.empty()) + osd_printf_warning("Warning: layout view '%s' contains deprecated backdrop element\n", name()); + layers.backdrops.emplace_back(env, *itemnode, elemmap, orientation, trans, color); + m_has_art = true; + } else if (!strcmp(itemnode->get_name(), "overlay")) { + if (layers.overlays.empty()) + osd_printf_warning("Warning: layout view '%s' contains deprecated overlay element\n", name()); layers.overlays.emplace_back(env, *itemnode, elemmap, orientation, trans, color); m_has_art = true; } else if (!strcmp(itemnode->get_name(), "bezel")) { + if (layers.bezels.empty()) + osd_printf_warning("Warning: layout view '%s' contains deprecated bezel element\n", name()); layers.bezels.emplace_back(env, *itemnode, elemmap, orientation, trans, color); m_has_art = true; } else if (!strcmp(itemnode->get_name(), "cpanel")) { + if (layers.cpanels.empty()) + osd_printf_warning("Warning: layout view '%s' contains deprecated cpanel element\n", name()); layers.cpanels.emplace_back(env, *itemnode, elemmap, orientation, trans, color); m_has_art = true; } else if (!strcmp(itemnode->get_name(), "marquee")) { + if (layers.marquees.empty()) + osd_printf_warning("Warning: layout view '%s' contains deprecated marquee element\n", name()); layers.marquees.emplace_back(env, *itemnode, elemmap, orientation, trans, color); m_has_art = true; } else if (!strcmp(itemnode->get_name(), "group")) { - char const *ref(env.get_attribute_string(*itemnode, "ref", nullptr)); - if (!ref) - throw layout_syntax_error("nested group must have ref attribute"); + std::string const ref(env.get_attribute_string(*itemnode, "ref")); + if (ref.empty()) + throw layout_syntax_error("group instantiation must have non-empty ref attribute"); group_map::iterator const found(groupmap.find(ref)); if (groupmap.end() == found) @@ -3222,7 +4544,7 @@ void layout_view::add_items( grouptrans = found->second.make_transform(grouporient, trans); } - environment local(env); + view_environment local(env, false); add_items( layers, local, @@ -3231,7 +4553,7 @@ void layout_view::add_items( groupmap, orientation_add(grouporient, orientation), grouptrans, - render_color_multiply(env.parse_color(itemnode->get_child("color")), color), + env.parse_color(itemnode->get_child("color")) * color, false, false, true); @@ -3241,13 +4563,28 @@ void layout_view::add_items( int const count(env.get_attribute_int(*itemnode, "count", -1)); if (0 >= count) throw layout_syntax_error("repeat must have positive integer count attribute"); - environment local(env); + view_environment local(env, false); for (int i = 0; count > i; ++i) { add_items(layers, local, *itemnode, elemmap, groupmap, orientation, trans, color, false, true, !i); local.increment_parameters(); } } + else if (!strcmp(itemnode->get_name(), "collection")) + { + std::string_view const name(env.get_attribute_string(*itemnode, "name")); + if (name.empty()) + throw layout_syntax_error("collection must have non-empty name attribute"); + + auto const found(std::find_if(m_vistoggles.begin(), m_vistoggles.end(), [name] (auto const &x) { return x.name() == name; })); + if (m_vistoggles.end() != found) + throw layout_syntax_error(util::string_format("duplicate collection name '%s'", name)); + + m_defvismask |= u32(env.get_attribute_bool(*itemnode, "visible", true) ? 1 : 0) << m_vistoggles.size(); // TODO: make this less hacky + view_environment local(env, true); + m_vistoggles.emplace_back(std::string(name), local.visibility_mask()); + add_items(layers, local, *itemnode, elemmap, groupmap, orientation, trans, color, false, false, true); + } else { throw layout_syntax_error(util::string_format("unknown view item %s", itemnode->get_name())); @@ -3261,15 +4598,15 @@ void layout_view::add_items( } } -std::string layout_view::make_name(environment &env, util::xml::data_node const &viewnode) +std::string layout_view::make_name(layout_environment &env, util::xml::data_node const &viewnode) { - char const *const name(env.get_attribute_string(viewnode, "name", nullptr)); - if (!name) - throw layout_syntax_error("view must have name attribute"); + std::string_view const name(env.get_attribute_string(viewnode, "name")); + if (name.empty()) + throw layout_syntax_error("view must have non-empty name attribute"); if (env.is_root_device()) { - return name; + return std::string(name); } else { @@ -3287,50 +4624,75 @@ std::string layout_view::make_name(environment &env, util::xml::data_node const //************************************************************************** //------------------------------------------------- -// item - constructor +// layout_view_item - constructor //------------------------------------------------- -layout_view::item::item( - environment &env, +layout_view_item::layout_view_item( + view_environment &env, util::xml::data_node const &itemnode, element_map &elemmap, int orientation, layout_group::transform const &trans, render_color const &color) : m_element(find_element(env, itemnode, elemmap)) - , m_output(env.device(), env.get_attribute_string(itemnode, "name", "")) - , m_have_output(env.get_attribute_string(itemnode, "name", "")[0]) - , m_input_tag(make_input_tag(env, itemnode)) + , m_output(env.device(), std::string(env.get_attribute_string(itemnode, "name"))) + , m_animoutput(env.device(), make_child_output_tag(env, itemnode, "animate")) + , m_scrollxoutput(env.device(), make_child_output_tag(env, itemnode, "xscroll")) + , m_scrollyoutput(env.device(), make_child_output_tag(env, itemnode, "yscroll")) + , m_animinput_port(nullptr) + , m_scrollxinput_port(nullptr) + , m_scrollyinput_port(nullptr) + , m_scrollwrapx(make_child_wrap(env, itemnode, "xscroll")) + , m_scrollwrapy(make_child_wrap(env, itemnode, "yscroll")) + , m_elem_state(m_element ? m_element->default_state() : 0) + , m_scrollsizex(make_child_size(env, itemnode, "xscroll")) + , m_scrollsizey(make_child_size(env, itemnode, "yscroll")) + , m_scrollposx(0.0f) + , m_scrollposy(0.0f) + , m_animmask(make_child_mask(env, itemnode, "animate")) + , m_scrollxmask(make_child_mask(env, itemnode, "xscroll")) + , m_scrollymask(make_child_mask(env, itemnode, "yscroll")) + , m_scrollxmin(make_child_min(env, itemnode, "xscroll")) + , m_scrollymin(make_child_min(env, itemnode, "yscroll")) + , m_scrollxmax(make_child_max(env, itemnode, "xscroll", m_scrollxmask)) + , m_scrollymax(make_child_max(env, itemnode, "yscroll", m_scrollymask)) + , m_animshift(get_state_shift(m_animmask)) + , m_scrollxshift(get_state_shift(m_scrollxmask)) + , m_scrollyshift(get_state_shift(m_scrollymask)) , m_input_port(nullptr) , m_input_field(nullptr) , m_input_mask(env.get_attribute_int(itemnode, "inputmask", 0)) - , m_input_shift(0) - , m_input_raw(0 != env.get_attribute_int(itemnode, "inputraw", 0)) + , m_input_shift(get_state_shift(m_input_mask)) + , m_clickthrough(env.get_attribute_bool(itemnode, "clickthrough", "yes")) , m_screen(nullptr) , m_orientation(orientation_add(env.parse_orientation(itemnode.get_child("orientation")), orientation)) - , m_rawbounds(make_bounds(env, itemnode, trans)) - , m_color(render_color_multiply(env.parse_color(itemnode.get_child("color")), color)) + , m_color(make_color(env, itemnode, color)) , m_blend_mode(get_blend_mode(env, itemnode)) + , m_visibility_mask(env.visibility_mask()) + , m_id(env.get_attribute_string(itemnode, "id")) + , m_input_tag(make_input_tag(env, itemnode)) + , m_animinput_tag(make_child_input_tag(env, itemnode, "animate")) + , m_scrollxinput_tag(make_child_input_tag(env, itemnode, "xscroll")) + , m_scrollyinput_tag(make_child_input_tag(env, itemnode, "yscroll")) + , m_rawbounds(make_bounds(env, itemnode, trans)) + , m_have_output(!env.get_attribute_string(itemnode, "name").empty()) + , m_input_raw(env.get_attribute_bool(itemnode, "inputraw", 0)) + , m_have_animoutput(!make_child_output_tag(env, itemnode, "animate").empty()) + , m_have_scrollxoutput(!make_child_output_tag(env, itemnode, "xscroll").empty()) + , m_have_scrollyoutput(!make_child_output_tag(env, itemnode, "yscroll").empty()) + , m_has_clickthrough(!env.get_attribute_string(itemnode, "clickthrough").empty()) { - // outputs need resolving - if (m_have_output) - m_output.resolve(); - // fetch common data int index = env.get_attribute_int(itemnode, "index", -1); if (index != -1) - m_screen = screen_device_iterator(env.machine().root_device()).byindex(index); - for (u32 mask = m_input_mask; (mask != 0) && (~mask & 1); mask >>= 1) - m_input_shift++; - if (m_have_output && m_element) - m_output = m_element->default_state(); + m_screen = screen_device_enumerator(env.machine().root_device()).byindex(index); // sanity checks if (strcmp(itemnode.get_name(), "screen") == 0) { if (itemnode.has_attribute("tag")) { - char const *const tag(env.get_attribute_string(itemnode, "tag", "")); + std::string_view const tag(env.get_attribute_string(itemnode, "tag")); m_screen = dynamic_cast<screen_device *>(env.device().subdevice(tag)); if (!m_screen) throw layout_reference_error(util::string_format("invalid screen tag '%d'", tag)); @@ -3342,78 +4704,182 @@ layout_view::item::item( } else if (!m_element) { - throw layout_syntax_error(util::string_format("item of type %s require an element tag", itemnode.get_name())); + throw layout_syntax_error(util::string_format("item of type %s requires an element tag", itemnode.get_name())); + } + else if (m_scrollxmin == m_scrollxmax) + { + throw layout_syntax_error(util::string_format("item X scroll minimum and maximum both equal to %u", m_scrollxmin)); } + else if (m_scrollymin == m_scrollymax) + { + throw layout_syntax_error(util::string_format("item Y scroll minimum and maximum both equal to %u", m_scrollymin)); + } + + // this can be called before resolving tags, make it return something valid + m_bounds = m_rawbounds; + m_get_bounds = bounds_delegate(&emu::render::detail::bounds_step::get, &m_bounds.front()); } //------------------------------------------------- -// item - destructor +// layout_view_item - destructor //------------------------------------------------- -layout_view::item::~item() +layout_view_item::~layout_view_item() { } //------------------------------------------------- -// screen_container - retrieve screen container +// set_element_state_callback - set callback to +// obtain element state value //------------------------------------------------- +void layout_view_item::set_element_state_callback(state_delegate &&handler) +{ + if (!handler.isnull()) + m_get_elem_state = std::move(handler); + else + m_get_elem_state = default_get_elem_state(); +} + + +//------------------------------------------------- +// set_animation_state_callback - set callback to +// obtain animation state +//------------------------------------------------- -render_container *layout_view::item::screen_container(running_machine &machine) const +void layout_view_item::set_animation_state_callback(state_delegate &&handler) { - return (m_screen != nullptr) ? &m_screen->container() : nullptr; + if (!handler.isnull()) + m_get_anim_state = std::move(handler); + else + m_get_anim_state = default_get_anim_state(); } //------------------------------------------------- -// state - fetch state based on configured source +// set_bounds_callback - set callback to obtain +// bounds //------------------------------------------------- -int layout_view::item::state() const +void layout_view_item::set_bounds_callback(bounds_delegate &&handler) { - assert(m_element); + if (!handler.isnull()) + m_get_bounds = std::move(handler); + else + m_get_bounds = default_get_bounds(); +} - if (m_have_output) - { - // if configured to track an output, fetch its value - return m_output; - } - else if (!m_input_tag.empty()) - { - // if configured to an input, fetch the input value - if (m_input_port) - { - if (m_input_raw) - { - return (m_input_port->read() & m_input_mask) >> m_input_shift; - } - else - { - ioport_field const *const field(m_input_field ? m_input_field : m_input_port->field(m_input_mask)); - if (field) - return ((m_input_port->read() ^ field->defvalue()) & m_input_mask) ? 1 : 0; - } - } - } - return 0; +//------------------------------------------------- +// set_color_callback - set callback to obtain +// color +//------------------------------------------------- + +void layout_view_item::set_color_callback(color_delegate &&handler) +{ + if (!handler.isnull()) + m_get_color = std::move(handler); + else + m_get_color = default_get_color(); +} + + +//------------------------------------------------- +// set_scroll_size_x_callback - set callback to +// obtain horizontal scroll window size +//------------------------------------------------- + +void layout_view_item::set_scroll_size_x_callback(scroll_size_delegate &&handler) +{ + if (!handler.isnull()) + m_get_scroll_size_x = std::move(handler); + else + m_get_scroll_size_x = default_get_scroll_size_x(); +} + + +//------------------------------------------------- +// set_scroll_size_y_callback - set callback to +// obtain vertical scroll window size +//------------------------------------------------- + +void layout_view_item::set_scroll_size_y_callback(scroll_size_delegate &&handler) +{ + if (!handler.isnull()) + m_get_scroll_size_y = std::move(handler); + else + m_get_scroll_size_y = default_get_scroll_size_y(); +} + + +//------------------------------------------------- +// set_scroll_pos_x_callback - set callback to +// obtain horizontal scroll position +//------------------------------------------------- + +void layout_view_item::set_scroll_pos_x_callback(scroll_pos_delegate &&handler) +{ + if (!handler.isnull()) + m_get_scroll_pos_x = std::move(handler); + else + m_get_scroll_pos_x = default_get_scroll_pos_x(); +} + + +//------------------------------------------------- +// set_scroll_pos_y_callback - set callback to +// obtain vertical scroll position +//------------------------------------------------- + +void layout_view_item::set_scroll_pos_y_callback(scroll_pos_delegate &&handler) +{ + if (!handler.isnull()) + m_get_scroll_pos_y = std::move(handler); + else + m_get_scroll_pos_y = default_get_scroll_pos_y(); } -//--------------------------------------------- +//------------------------------------------------- // resolve_tags - resolve tags, if any are set -//--------------------------------------------- +//------------------------------------------------- -void layout_view::item::resolve_tags() +void layout_view_item::resolve_tags() { + // resolve element state output and set default value + if (m_have_output) + { + m_output.resolve(); + if (m_element) + m_output = m_element->default_state(); + } + + // resolve animation state and scroll outputs + if (m_have_animoutput) + m_animoutput.resolve(); + if (m_have_scrollxoutput) + m_scrollxoutput.resolve(); + if (m_have_scrollyoutput) + m_scrollyoutput.resolve(); + + // resolve animation state and scroll inputs + if (!m_animinput_tag.empty()) + m_animinput_port = m_element->machine().root_device().ioport(m_animinput_tag); + if (!m_scrollxinput_tag.empty()) + m_scrollxinput_port = m_element->machine().root_device().ioport(m_scrollxinput_tag); + if (!m_scrollyinput_tag.empty()) + m_scrollyinput_port = m_element->machine().root_device().ioport(m_scrollyinput_tag); + + // resolve element state input if (!m_input_tag.empty()) { - m_input_port = m_element->machine().root_device().ioport(m_input_tag.c_str()); + m_input_port = m_element->machine().root_device().ioport(m_input_tag); if (m_input_port) { - for (ioport_field &field : m_input_port->fields()) + // if there's a matching unconditional field, cache it + for (ioport_field const &field : m_input_port->fields()) { if (field.mask() & m_input_mask) { @@ -3422,19 +4888,351 @@ void layout_view::item::resolve_tags() break; } } + + // if clickthrough isn't explicitly configured, having an I/O port implies false + if (!m_has_clickthrough) + m_clickthrough = false; } } + + // choose optimal handlers + m_get_elem_state = default_get_elem_state(); + m_get_anim_state = default_get_anim_state(); + m_get_bounds = default_get_bounds(); + m_get_color = default_get_color(); + m_get_scroll_size_x = default_get_scroll_size_x(); + m_get_scroll_size_y = default_get_scroll_size_y(); + m_get_scroll_pos_x = default_get_scroll_pos_x(); + m_get_scroll_pos_y = default_get_scroll_pos_y(); +} + + +//------------------------------------------------- +// default_get_elem_state - get default element +// state handler +//------------------------------------------------- + +layout_view_item::state_delegate layout_view_item::default_get_elem_state() +{ + if (m_have_output) + return state_delegate(&layout_view_item::get_output, this); + else if (!m_input_port) + return state_delegate(&layout_view_item::get_state, this); + else if (m_input_raw) + return state_delegate(&layout_view_item::get_input_raw, this); + else if (m_input_field) + return state_delegate(&layout_view_item::get_input_field_cached, this); + else + return state_delegate(&layout_view_item::get_input_field_conditional, this); +} + + +//------------------------------------------------- +// default_get_anim_state - get default animation +// state handler +//------------------------------------------------- + +layout_view_item::state_delegate layout_view_item::default_get_anim_state() +{ + if (m_have_animoutput) + return state_delegate(&layout_view_item::get_anim_output, this); + else if (m_animinput_port) + return state_delegate(&layout_view_item::get_anim_input, this); + else + return default_get_elem_state(); +} + + +//------------------------------------------------- +// default_get_bounds - get default bounds handler +//------------------------------------------------- + +layout_view_item::bounds_delegate layout_view_item::default_get_bounds() +{ + return (m_bounds.size() == 1U) + ? bounds_delegate(&emu::render::detail::bounds_step::get, &m_bounds.front()) + : bounds_delegate(&layout_view_item::get_interpolated_bounds, this); +} + + +//------------------------------------------------- +// default_get_color - get default color handler +//------------------------------------------------- + +layout_view_item::color_delegate layout_view_item::default_get_color() +{ + return (m_color.size() == 1U) + ? color_delegate(&emu::render::detail::color_step::get, &const_cast<emu::render::detail::color_step &>(m_color.front())) + : color_delegate(&layout_view_item::get_interpolated_color, this); +} + + +//------------------------------------------------- +// default_get_scroll_size_x - get default +// horizontal scroll window size handler +//------------------------------------------------- + +layout_view_item::scroll_size_delegate layout_view_item::default_get_scroll_size_x() +{ + return scroll_size_delegate(&layout_view_item::get_scrollsizex, this); +} + + +//------------------------------------------------- +// default_get_scroll_size_y - get default +// vertical scroll window size handler +//------------------------------------------------- + +layout_view_item::scroll_size_delegate layout_view_item::default_get_scroll_size_y() +{ + return scroll_size_delegate(&layout_view_item::get_scrollsizey, this); +} + + +//------------------------------------------------- +// default_get_scroll_pos_x - get default +// horizontal scroll position handler +//------------------------------------------------- + +layout_view_item::scroll_pos_delegate layout_view_item::default_get_scroll_pos_x() +{ + if (m_have_scrollxoutput) + return scroll_pos_delegate(m_scrollwrapx ? &layout_view_item::get_scrollx_output<true> : &layout_view_item::get_scrollx_output<false>, this); + else if (m_scrollxinput_port) + return scroll_pos_delegate(m_scrollwrapx ? &layout_view_item::get_scrollx_input<true> : &layout_view_item::get_scrollx_input<false>, this); + else + return scroll_pos_delegate(&layout_view_item::get_scrollposx, this); +} + + +//------------------------------------------------- +// default_get_scroll_pos_y - get default +// vertical scroll position handler +//------------------------------------------------- + +layout_view_item::scroll_pos_delegate layout_view_item::default_get_scroll_pos_y() +{ + if (m_have_scrollyoutput) + return scroll_pos_delegate(m_scrollwrapy ? &layout_view_item::get_scrolly_output<true> : &layout_view_item::get_scrolly_output<false>, this); + else if (m_scrollyinput_port) + return scroll_pos_delegate(m_scrollwrapy ? &layout_view_item::get_scrolly_input<true> : &layout_view_item::get_scrolly_input<false>, this); + else + return scroll_pos_delegate(&layout_view_item::get_scrollposy, this); +} + + +//------------------------------------------------- +// get_state - get state when no bindings +//------------------------------------------------- + +int layout_view_item::get_state() const +{ + return m_elem_state; } -//--------------------------------------------- +//------------------------------------------------- +// get_output - get element state output +//------------------------------------------------- + +int layout_view_item::get_output() const +{ + assert(m_have_output); + return int(s32(m_output)); +} + + +//------------------------------------------------- +// get_input_raw - get element state input +//------------------------------------------------- + +int layout_view_item::get_input_raw() const +{ + assert(m_input_port); + return int(std::make_signed_t<ioport_value>((m_input_port->read() & m_input_mask) >> m_input_shift)); +} + + +//------------------------------------------------- +// get_input_field_cached - element state +//------------------------------------------------- + +int layout_view_item::get_input_field_cached() const +{ + assert(m_input_port); + assert(m_input_field); + return ((m_input_port->read() ^ m_input_field->defvalue()) & m_input_mask) ? 1 : 0; +} + + +//------------------------------------------------- +// get_input_field_conditional - element state +//------------------------------------------------- + +int layout_view_item::get_input_field_conditional() const +{ + assert(m_input_port); + assert(!m_input_field); + ioport_field const *const field(m_input_port->field(m_input_mask)); + return (field && ((m_input_port->read() ^ field->defvalue()) & m_input_mask)) ? 1 : 0; +} + + +//------------------------------------------------- +// get_anim_output - get animation output +//------------------------------------------------- + +int layout_view_item::get_anim_output() const +{ + assert(m_have_animoutput); + return int(unsigned((u32(s32(m_animoutput) & m_animmask) >> m_animshift))); +} + + +//------------------------------------------------- +// get_anim_input - get animation input +//------------------------------------------------- + +int layout_view_item::get_anim_input() const +{ + assert(m_animinput_port); + return int(std::make_signed_t<ioport_value>((m_animinput_port->read() & m_animmask) >> m_animshift)); +} + + +//------------------------------------------------- +// get_scrollsizex - get horizontal scroll window +// size +//------------------------------------------------- + +float layout_view_item::get_scrollsizex() const +{ + return m_scrollsizex; +} + + +//------------------------------------------------- +// get_scrollsizey - get vertical scroll window +// size +//------------------------------------------------- + +float layout_view_item::get_scrollsizey() const +{ + return m_scrollsizey; +} + + +//------------------------------------------------- +// get_scrollposx - get horizontal scroll +// position +//------------------------------------------------- + +float layout_view_item::get_scrollposx() const +{ + return m_scrollposx; +} + + +//------------------------------------------------- +// get_scrollposy - get vertical scroll position +//------------------------------------------------- + +float layout_view_item::get_scrollposy() const +{ + return m_scrollposy; +} + + +//------------------------------------------------- +// get_scrollx_output - get scaled horizontal +// scroll output +//------------------------------------------------- + +template <bool Wrap> +float layout_view_item::get_scrollx_output() const +{ + assert(m_have_scrollxoutput); + u32 const unscaled(((u32(s32(m_scrollxoutput)) & m_scrollxmask) >> m_scrollxshift) - m_scrollxmin); + float const range(std::make_signed_t<ioport_value>(m_scrollxmax - m_scrollxmin) + (!Wrap ? 0 : (m_scrollxmin < m_scrollxmax) ? 1 : -1)); + return float(s32(unscaled)) / range; +} + + +//------------------------------------------------- +// get_scrollx_input - get scaled horizontal +// scroll input +//------------------------------------------------- + +template <bool Wrap> +float layout_view_item::get_scrollx_input() const +{ + assert(m_scrollxinput_port); + ioport_value const unscaled(((m_scrollxinput_port->read() & m_scrollxmask) >> m_scrollxshift) - m_scrollxmin); + float const range(std::make_signed_t<ioport_value>(m_scrollxmax - m_scrollxmin) + (!Wrap ? 0 : (m_scrollxmin < m_scrollxmax) ? 1 : -1)); + return float(std::make_signed_t<ioport_value>(unscaled)) / range; +} + + +//------------------------------------------------- +// get_scrolly_output - get scaled vertical +// scroll output +//------------------------------------------------- + +template <bool Wrap> +float layout_view_item::get_scrolly_output() const +{ + assert(m_have_scrollyoutput); + u32 const unscaled(((u32(s32(m_scrollyoutput)) & m_scrollymask) >> m_scrollyshift) - m_scrollymin); + float const range(std::make_signed_t<ioport_value>(m_scrollymax - m_scrollymin) + (!Wrap ? 0 : (m_scrollymin < m_scrollymax) ? 1 : -1)); + return float(s32(unscaled)) / range; +} + + +//------------------------------------------------- +// get_scrolly_input - get scaled vertical scroll +// input +//------------------------------------------------- + +template <bool Wrap> +float layout_view_item::get_scrolly_input() const +{ + assert(m_scrollyinput_port); + ioport_value const unscaled(((m_scrollyinput_port->read() & m_scrollymask) >> m_scrollyshift) - m_scrollymin); + float const range(std::make_signed_t<ioport_value>(m_scrollymax - m_scrollymin) + (!Wrap ? 0 : (m_scrollymin < m_scrollymax) ? 1 : -1)); + return float(std::make_signed_t<ioport_value>(unscaled)) / range; +} + + +//------------------------------------------------- +// get_interpolated_bounds - animated bounds +//------------------------------------------------- + +render_bounds layout_view_item::get_interpolated_bounds() const +{ + assert(m_bounds.size() > 1U); + return interpolate_bounds(m_bounds, m_get_anim_state()); +} + + +//------------------------------------------------- +// get_interpolated_color - animated color +//------------------------------------------------- + +render_color layout_view_item::get_interpolated_color() const +{ + assert(m_color.size() > 1U); + return interpolate_color(m_color, m_get_anim_state()); +} + + +//------------------------------------------------- // find_element - find element definition -//--------------------------------------------- +//------------------------------------------------- -layout_element *layout_view::item::find_element(environment &env, util::xml::data_node const &itemnode, element_map &elemmap) +layout_element *layout_view_item::find_element(view_environment &env, util::xml::data_node const &itemnode, element_map &elemmap) { - char const *const name(env.get_attribute_string(itemnode, !strcmp(itemnode.get_name(), "element") ? "ref" : "element", nullptr)); - if (!name) + std::string const name(env.get_attribute_string(itemnode, !strcmp(itemnode.get_name(), "element") ? "ref" : "element")); + if (name.empty()) return nullptr; // search the list of elements for a match, error if not found @@ -3446,66 +5244,87 @@ layout_element *layout_view::item::find_element(environment &env, util::xml::dat } -//--------------------------------------------- +//------------------------------------------------- // make_bounds - get transformed bounds -//--------------------------------------------- +//------------------------------------------------- -render_bounds layout_view::item::make_bounds( - environment &env, +layout_view_item::bounds_vector layout_view_item::make_bounds( + view_environment &env, util::xml::data_node const &itemnode, layout_group::transform const &trans) { - render_bounds bounds; - env.parse_bounds(itemnode.get_child("bounds"), bounds); - render_bounds_transform(bounds, trans); - if (bounds.x0 > bounds.x1) - std::swap(bounds.x0, bounds.x1); - if (bounds.y0 > bounds.y1) - std::swap(bounds.y0, bounds.y1); - return bounds; + bounds_vector result; + for (util::xml::data_node const *bounds = itemnode.get_child("bounds"); bounds; bounds = bounds->get_next_sibling("bounds")) + { + if (!add_bounds_step(env, result, *bounds)) + { + throw layout_syntax_error( + util::string_format( + "%s item has duplicate bounds for state", + itemnode.get_name())); + } + } + for (emu::render::detail::bounds_step &step : result) + { + render_bounds_transform(step.bounds, trans); + if (step.bounds.x0 > step.bounds.x1) + std::swap(step.bounds.x0, step.bounds.x1); + if (step.bounds.y0 > step.bounds.y1) + std::swap(step.bounds.y0, step.bounds.y1); + } + set_bounds_deltas(result); + return result; } -//--------------------------------------------- -// make_input_tag - get absolute input tag -//--------------------------------------------- +//------------------------------------------------- +// make_color - get color inflection points +//------------------------------------------------- -std::string layout_view::item::make_input_tag(environment &env, util::xml::data_node const &itemnode) +layout_view_item::color_vector layout_view_item::make_color( + view_environment &env, + util::xml::data_node const &itemnode, + render_color const &mult) { - char const *tag(env.get_attribute_string(itemnode, "inputtag", nullptr)); - return tag ? env.device().subtag(tag) : std::string(); + color_vector result; + for (util::xml::data_node const *color = itemnode.get_child("color"); color; color = color->get_next_sibling("color")) + { + if (!add_color_step(env, result, *color)) + { + throw layout_syntax_error( + util::string_format( + "%s item has duplicate color for state", + itemnode.get_name())); + } + } + if (result.empty()) + { + result.emplace_back(emu::render::detail::color_step{ 0, mult, { 0.0F, 0.0F, 0.0F, 0.0F } }); + } + else + { + for (emu::render::detail::color_step &step : result) + step.color *= mult; + set_color_deltas(result); + } + return result; } -//--------------------------------------------- -// get_blend_mode - explicit or implicit blend -//--------------------------------------------- -int layout_view::item::get_blend_mode(environment &env, util::xml::data_node const &itemnode) -{ - // see if there's a blend mode attribute - char const *const mode(env.get_attribute_string(itemnode, "blend", nullptr)); - if (mode) - { - if (!strcmp(mode, "none")) - return BLENDMODE_NONE; - else if (!strcmp(mode, "alpha")) - return BLENDMODE_ALPHA; - else if (!strcmp(mode, "multiply")) - return BLENDMODE_RGB_MULTIPLY; - else if (!strcmp(mode, "add")) - return BLENDMODE_ADD; - else - throw layout_syntax_error(util::string_format("unknown blend mode %s", mode)); - } +//************************************************************************** +// LAYOUT VIEW VISIBILITY TOGGLE +//************************************************************************** - // fall back to implicit blend mode based on element type - if (!strcmp(itemnode.get_name(), "screen")) - return -1; // magic number recognised by render.cpp to allow per-element blend mode - else if (!strcmp(itemnode.get_name(), "overlay")) - return BLENDMODE_RGB_MULTIPLY; - else - return BLENDMODE_ALPHA; +//------------------------------------------------- +// visibility_toggle - constructor +//------------------------------------------------- + +layout_view::visibility_toggle::visibility_toggle(std::string &&name, u32 mask) + : m_name(std::move(name)) + , m_mask(mask) +{ + assert(mask); } @@ -3518,13 +5337,18 @@ int layout_view::item::get_blend_mode(environment &env, util::xml::data_node con // layout_file - constructor //------------------------------------------------- -layout_file::layout_file(device_t &device, util::xml::data_node const &rootnode, char const *dirname) - : m_elemmap() +layout_file::layout_file( + device_t &device, + util::xml::data_node const &rootnode, + char const *searchpath, + char const *dirname) + : m_device(device) + , m_elemmap() , m_viewlist() { try { - environment env(device); + environment env(device, searchpath, dirname); // find the layout node util::xml::data_node const *const mamelayoutnode = rootnode.get_child("mamelayout"); @@ -3538,13 +5362,13 @@ layout_file::layout_file(device_t &device, util::xml::data_node const &rootnode, // parse all the parameters, elements and groups group_map groupmap; - add_elements(dirname, env, *mamelayoutnode, groupmap, false, true); + add_elements(env, *mamelayoutnode, groupmap, false, true); // parse all the views for (util::xml::data_node const *viewnode = mamelayoutnode->get_child("view"); viewnode != nullptr; viewnode = viewnode->get_next_sibling("view")) { // the trouble with allowing errors to propagate here is that it wreaks havoc with screenless systems that use a terminal by default - // e.g. intlc44 and intlc440 have a terminal on the tty port by default and have a view with the front panel with the terminal screen + // e.g. intlc44 and intlc440 have a terminal on the TTY port by default and have a view with the front panel with the terminal screen // however, they have a second view with just the front panel which is very useful if you're using e.g. -tty null_modem with a socket // if the error is allowed to propagate, the entire layout is dropped so you can't select the useful view try @@ -3553,9 +5377,17 @@ layout_file::layout_file(device_t &device, util::xml::data_node const &rootnode, } catch (layout_reference_error const &err) { - osd_printf_warning("Error instantiating layout view %s: %s\n", env.get_attribute_string(*viewnode, "name", ""), err.what()); + osd_printf_warning("Error instantiating layout view %s: %s\n", env.get_attribute_string(*viewnode, "name"), err.what()); } } + + // load the content of the first script node + if (!m_viewlist.empty()) + { + util::xml::data_node const *const scriptnode = mamelayoutnode->get_child("script"); + if (scriptnode) + emulator_info::layout_script_cb(*this, scriptnode->get_value()); + } } catch (layout_syntax_error const &err) { @@ -3574,8 +5406,32 @@ layout_file::~layout_file() } +//------------------------------------------------- +// resolve_tags - resolve tags +//------------------------------------------------- + +void layout_file::resolve_tags() +{ + for (layout_view &view : views()) + view.resolve_tags(); + + if (!m_resolve_tags.isnull()) + m_resolve_tags(); +} + + +//------------------------------------------------- +// set_resolve_tags_callback - set callback for +// additional tasks after resolving tags +//------------------------------------------------- + +void layout_file::set_resolve_tags_callback(resolve_tags_delegate &&handler) +{ + m_resolve_tags = std::move(handler); +} + + void layout_file::add_elements( - char const *dirname, environment &env, util::xml::data_node const &parentnode, group_map &groupmap, @@ -3593,17 +5449,17 @@ void layout_file::add_elements( } else if (!strcmp(childnode->get_name(), "element")) { - char const *const name(env.get_attribute_string(*childnode, "name", nullptr)); - if (!name) - throw layout_syntax_error("element lacks name attribute"); - if (!m_elemmap.emplace(std::piecewise_construct, std::forward_as_tuple(name), std::forward_as_tuple(env, *childnode, dirname)).second) + std::string_view const name(env.get_attribute_string(*childnode, "name")); + if (name.empty()) + throw layout_syntax_error("element must have non-empty name attribute"); + if (!m_elemmap.emplace(std::piecewise_construct, std::forward_as_tuple(name), std::forward_as_tuple(env, *childnode)).second) throw layout_syntax_error(util::string_format("duplicate element name %s", name)); } else if (!strcmp(childnode->get_name(), "group")) { - char const *const name(env.get_attribute_string(*childnode, "name", nullptr)); - if (!name) - throw layout_syntax_error("group lacks name attribute"); + std::string_view const name(env.get_attribute_string(*childnode, "name")); + if (name.empty()) + throw layout_syntax_error("group must have non-empty name attribute"); if (!groupmap.emplace(std::piecewise_construct, std::forward_as_tuple(name), std::forward_as_tuple(*childnode)).second) throw layout_syntax_error(util::string_format("duplicate group name %s", name)); } @@ -3615,7 +5471,7 @@ void layout_file::add_elements( environment local(env); for (int i = 0; count > i; ++i) { - add_elements(dirname, local, *childnode, groupmap, true, !i); + add_elements(local, *childnode, groupmap, true, !i); local.increment_parameters(); } } diff --git a/src/emu/rendlay.h b/src/emu/rendlay.h index d1de55f73ef..23431e7fd92 100644 --- a/src/emu/rendlay.h +++ b/src/emu/rendlay.h @@ -1,5 +1,5 @@ // license:BSD-3-Clause -// copyright-holders:Aaron Giles +// copyright-holders:Aaron Giles, Vas Crabb /*************************************************************************** rendlay.h @@ -13,23 +13,671 @@ #pragma once +#include "rendertypes.h" +#include "screen.h" + +#include "interface/uievents.h" + +#include <array> +#include <functional> +#include <map> +#include <memory> +#include <string> +#include <string_view> +#include <tuple> +#include <unordered_map> +#include <vector> + //************************************************************************** -// GLOBAL VARIABLES +// TYPE DEFINITIONS //************************************************************************** -// no screens layouts -extern const internal_layout layout_noscreens; // for screenless systems +namespace emu::render::detail { + +struct bounds_step +{ + render_bounds get() const { return bounds; } + + int state; + render_bounds bounds; + render_bounds delta; +}; +using bounds_vector = std::vector<bounds_step>; + +struct color_step +{ + render_color get() const { return color; } + + int state; + render_color color; + render_color delta; +}; +using color_vector = std::vector<color_step>; + + +class layout_environment; +class view_environment; + +} // namespace emu::render::detail + + +/// \brief A description of a piece of visible artwork +/// +/// Most view items (except for those referencing screens) have exactly +/// one layout_element which describes the contents of the item. +/// Elements are separate from items because they can be re-used +/// multiple times within a layout. Even though an element can contain +/// a number of components, they are drawn as a single textured quad. +class layout_element +{ +public: + using environment = emu::render::detail::layout_environment; + using draw_delegate = delegate<void (int, bitmap_argb32 &)>; + + // construction/destruction + layout_element(environment &env, util::xml::data_node const &elemnode); + virtual ~layout_element(); + + // getters + running_machine &machine() const { return m_machine; } + int default_state() const { return m_defstate; } + render_texture *state_texture(int state); + + // set handlers + void set_draw_callback(draw_delegate &&handler); + + // operations + void preload(); + void invalidate() { m_invalidated = true; } + void prepare(); + +private: + /// \brief A drawing component within a layout element + /// + /// Each #layout_element contains one or more components. Each + /// component can describe either an image or a rectangle/disk + /// primitive. A component can also have a state mask and value + /// for controlling visibility. If the state of the item + /// instantiating the element matches the component's state value + /// for the bits that are set in the mask, the component is visible. + class component + { + public: + typedef std::unique_ptr<component> ptr; + + // construction/destruction + component(environment &env, util::xml::data_node const &compnode); + virtual ~component() = default; + + // setup + void normalize_bounds(float xoffs, float yoffs, float xscale, float yscale); + + // getters + int statemask() const { return m_statemask; } + int stateval() const { return m_stateval; } + std::pair<int, bool> statewrap() const; + render_bounds overall_bounds() const; + render_bounds bounds(int state) const; + render_color color(int state) const; + + // operations + virtual void preload(running_machine &machine); + virtual void draw(running_machine &machine, bitmap_argb32 &dest, int state); + + protected: + // helpers + virtual int maxstate() const; + virtual void draw_aligned(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state); + + // drawing helpers + static void draw_text(render_font &font, bitmap_argb32 &dest, const rectangle &bounds, std::string_view str, int align, const render_color &color); + static void draw_segment_horizontal_caps(bitmap_argb32 &dest, int minx, int maxx, int midy, int width, int caps, rgb_t color); + static void draw_segment_horizontal(bitmap_argb32 &dest, int minx, int maxx, int midy, int width, rgb_t color); + static void draw_segment_vertical_caps(bitmap_argb32 &dest, int miny, int maxy, int midx, int width, int caps, rgb_t color); + static void draw_segment_vertical(bitmap_argb32 &dest, int miny, int maxy, int midx, int width, rgb_t color); + static void draw_segment_diagonal_1(bitmap_argb32 &dest, int minx, int maxx, int miny, int maxy, int width, rgb_t color); + static void draw_segment_diagonal_2(bitmap_argb32 &dest, int minx, int maxx, int miny, int maxy, int width, rgb_t color); + static void draw_segment_decimal(bitmap_argb32 &dest, int midx, int midy, int width, rgb_t color); + static void draw_segment_comma(bitmap_argb32 &dest, int minx, int maxx, int miny, int maxy, int width, rgb_t color); + static void apply_skew(bitmap_argb32 &dest, int skewwidth); + + private: + using bounds_vector = emu::render::detail::bounds_vector; + using color_vector = emu::render::detail::color_vector; + + // internal state + int const m_statemask; // bits of state used to control visibility + int const m_stateval; // masked state value to make component visible + bounds_vector m_bounds; // bounds of the element + color_vector m_color; // color of the element + }; + + // component implementations + class image_component; + class rect_component; + class disk_component; + class text_component; + class led7seg_component; + class led14seg_component; + class led16seg_component; + class led14segsc_component; + class led16segsc_component; + class simplecounter_component; + class reel_component; + + // a texture encapsulates a texture for a given element in a given state + class texture + { + public: + texture(); + texture(texture const &that) = delete; + texture(texture &&that); + + ~texture(); + + texture &operator=(texture const &that) = delete; + texture &operator=(texture &&that); + + layout_element * m_element; // pointer back to the element + render_texture * m_texture; // texture for this state + int m_state; // associated state number + }; + + typedef component::ptr (*make_component_func)(environment &env, util::xml::data_node const &compnode); + typedef std::map<std::string, make_component_func> make_component_map; + + // internal helpers + static void element_scale(bitmap_argb32 &dest, bitmap_argb32 &source, const rectangle &sbounds, void *param); + template <typename T> static component::ptr make_component(environment &env, util::xml::data_node const &compnode); + + static make_component_map const s_make_component; // maps component XML names to creator functions + + // internal state + running_machine & m_machine; // reference to the owning machine + std::vector<component::ptr> m_complist; // list of components + int const m_defstate; // default state of this element + int m_statemask; // mask to apply to state values + bool m_foldhigh; // whether we need to fold state values above the mask range + std::vector<texture> m_elemtex; // array of element textures used for managing the scaled bitmaps + draw_delegate m_draw; // draw delegate (called after components are drawn) + bool m_invalidated; // force redrawing on next frame if set +}; + + +/// \brief A reusable group of items +/// +/// Views expand/flatten groups into their component elements applying +/// an optional coordinate transform. This is useful for duplicating +/// the same sublayout in multiple views, or grouping related items to +/// simplify overall view arrangement. Groups only exist while parsing +/// a layout file - no information about element grouping is preserved +/// after the views have been built. +class layout_group +{ +public: + using environment = emu::render::detail::layout_environment; + using group_map = std::unordered_map<std::string, layout_group>; + using transform = std::array<std::array<float, 3>, 3>; + + layout_group(util::xml::data_node const &groupnode); + ~layout_group(); + + util::xml::data_node const &get_groupnode() const { return m_groupnode; } + + transform make_transform(int orientation, render_bounds const &dest) const; + transform make_transform(int orientation, transform const &trans) const; + transform make_transform(int orientation, render_bounds const &dest, transform const &trans) const; + + void set_bounds_unresolved(); + void resolve_bounds(environment &env, group_map &groupmap); + +private: + void resolve_bounds(environment &env, group_map &groupmap, std::vector<layout_group const *> &seen); + void resolve_bounds( + environment &env, + util::xml::data_node const &parentnode, + group_map &groupmap, + std::vector<layout_group const *> &seen, + bool &empty, + bool vistoggle, + bool repeat, + bool init); + + util::xml::data_node const & m_groupnode; + render_bounds m_bounds; + bool m_bounds_resolved; +}; + + +/// \brief A single item in a view +/// +/// Each view has a list of item structures describing the visual +/// elements to draw, where they are located, additional blending modes, +/// and bindings for inputs and outputs. +class layout_view_item +{ + friend class layout_view; + +public: + using view_environment = emu::render::detail::view_environment; + using element_map = std::unordered_map<std::string, layout_element>; + using state_delegate = delegate<int ()>; + using bounds_delegate = delegate<render_bounds ()>; + using color_delegate = delegate<render_color ()>; + using scroll_size_delegate = delegate<float ()>; + using scroll_pos_delegate = delegate<float ()>; + + // construction/destruction + layout_view_item( + view_environment &env, + util::xml::data_node const &itemnode, + element_map &elemmap, + int orientation, + layout_group::transform const &trans, + render_color const &color); + ~layout_view_item(); + + // getters + std::string const &id() const { return m_id; } + layout_element *element() const { return m_element; } + screen_device *screen() const { return m_screen; } + bool bounds_animated() const { return m_bounds.size() > 1U; } + bool color_animated() const { return m_color.size() > 1U; } + render_bounds bounds() const { return m_get_bounds(); } + render_color color() const { return m_get_color(); } + bool scroll_wrap_x() const { return m_scrollwrapx; } + bool scroll_wrap_y() const { return m_scrollwrapy; } + float scroll_size_x() const { return m_get_scroll_size_x(); } + float scroll_size_y() const { return m_get_scroll_size_y(); } + float scroll_pos_x() const { return m_get_scroll_pos_x(); } + float scroll_pos_y() const { return m_get_scroll_pos_y(); } + int blend_mode() const { return m_blend_mode; } + u32 visibility_mask() const { return m_visibility_mask; } + int orientation() const { return m_orientation; } + render_container *screen_container() const { return m_screen ? &m_screen->container() : nullptr; } + + // interactivity + bool has_input() const { return bool(m_input_port); } + std::pair<ioport_port *, ioport_value> input_tag_and_mask() const { return std::make_pair(m_input_port, m_input_mask); } + bool clickthrough() const { return m_clickthrough; } + + // fetch state based on configured source + int element_state() const { return m_get_elem_state(); } + int animation_state() const { return m_get_anim_state(); } + + // set state + void set_state(int state) { m_elem_state = state; } + void set_scroll_size_x(float size) { m_scrollsizex = std::clamp(size, 0.01f, 1.0f); } + void set_scroll_size_y(float size) { m_scrollsizey = std::clamp(size, 0.01f, 1.0f); } + void set_scroll_pos_x(float pos) { m_scrollposx = pos; } + void set_scroll_pos_y(float pos) { m_scrollposy = pos; } + + // set handlers + void set_element_state_callback(state_delegate &&handler); + void set_animation_state_callback(state_delegate &&handler); + void set_bounds_callback(bounds_delegate &&handler); + void set_color_callback(color_delegate &&handler); + void set_scroll_size_x_callback(scroll_size_delegate &&handler); + void set_scroll_size_y_callback(scroll_size_delegate &&handler); + void set_scroll_pos_x_callback(scroll_pos_delegate &&handler); + void set_scroll_pos_y_callback(scroll_pos_delegate &&handler); + + // resolve tags, if any + void resolve_tags(); + +private: + using bounds_vector = emu::render::detail::bounds_vector; + using color_vector = emu::render::detail::color_vector; + + state_delegate default_get_elem_state(); + state_delegate default_get_anim_state(); + bounds_delegate default_get_bounds(); + color_delegate default_get_color(); + scroll_size_delegate default_get_scroll_size_x(); + scroll_size_delegate default_get_scroll_size_y(); + scroll_pos_delegate default_get_scroll_pos_x(); + scroll_pos_delegate default_get_scroll_pos_y(); + int get_state() const; + int get_output() const; + int get_input_raw() const; + int get_input_field_cached() const; + int get_input_field_conditional() const; + int get_anim_output() const; + int get_anim_input() const; + float get_scrollsizex() const; + float get_scrollsizey() const; + float get_scrollposx() const; + float get_scrollposy() const; + template <bool Wrap> float get_scrollx_output() const; + template <bool Wrap> float get_scrolly_output() const; + template <bool Wrap> float get_scrollx_input() const; + template <bool Wrap> float get_scrolly_input() const; + render_bounds get_interpolated_bounds() const; + render_color get_interpolated_color() const; + + static layout_element *find_element(view_environment &env, util::xml::data_node const &itemnode, element_map &elemmap); + static bounds_vector make_bounds(view_environment &env, util::xml::data_node const &itemnode, layout_group::transform const &trans); + static color_vector make_color(view_environment &env, util::xml::data_node const &itemnode, render_color const &mult); + + // internal state + layout_element *const m_element; // pointer to the associated element (non-screens only) + state_delegate m_get_elem_state; // resolved element state function + state_delegate m_get_anim_state; // resolved animation state function + bounds_delegate m_get_bounds; // resolved bounds function + color_delegate m_get_color; // resolved color function + scroll_size_delegate m_get_scroll_size_x; // resolved horizontal scroll window size function + scroll_size_delegate m_get_scroll_size_y; // resolved vertical scroll window size function + scroll_pos_delegate m_get_scroll_pos_x; // resolved horizontal scroll position function + scroll_pos_delegate m_get_scroll_pos_y; // resolved vertical scroll position function + output_finder<> m_output; // associated output + output_finder<> m_animoutput; // associated output for animation if different + output_finder<> m_scrollxoutput; // associated output for horizontal scroll position + output_finder<> m_scrollyoutput; // associated output for vertical scroll position + ioport_port * m_animinput_port; // input port used for animation + ioport_port * m_scrollxinput_port; // input port used for horizontal scrolling + ioport_port * m_scrollyinput_port; // input port used for vertical scrolling + bool const m_scrollwrapx; // whether horizontal scrolling works like a loop + bool const m_scrollwrapy; // whether vertical scrolling works like a loop + int m_elem_state; // element state used in absence of bindings + float m_scrollsizex; // horizontal scroll window size used in absence of bindings + float m_scrollsizey; // vertical scroll window size used in absence of bindings + float m_scrollposx; // horizontal scroll position used in absence of bindings + float m_scrollposy; // vertical scroll position used in absence of bindings + ioport_value const m_animmask; // mask for animation state + ioport_value const m_scrollxmask; // mask for horizontal scroll position + ioport_value const m_scrollymask; // mask for vertical scroll position + ioport_value const m_scrollxmin; // minimum value for horizontal scroll position + ioport_value const m_scrollymin; // minimum value for vertical scroll position + ioport_value const m_scrollxmax; // maximum value for horizontal scroll position + ioport_value const m_scrollymax; // maximum value for vertical scroll position + u8 const m_animshift; // shift for animation state + u8 const m_scrollxshift; // shift for horizontal scroll position + u8 const m_scrollyshift; // shift for vertical scroll position + ioport_port * m_input_port; // input port of this item + ioport_field const * m_input_field; // input port field of this item + ioport_value const m_input_mask; // input mask of this item + u8 const m_input_shift; // input mask rightshift for raw (trailing 0s) + bool m_clickthrough; // should click pass through to lower elements + screen_device * m_screen; // pointer to screen + int const m_orientation; // orientation of this item + bounds_vector m_bounds; // bounds of the item + color_vector const m_color; // color of the item + int m_blend_mode; // blending mode to use when drawing + u32 m_visibility_mask; // combined mask of parent visibility groups + + // cold items + std::string const m_id; // optional unique item identifier + std::string const m_input_tag; // input tag of this item + std::string const m_animinput_tag; // tag of input port for animation state + std::string const m_scrollxinput_tag; // tag of input port for horizontal scroll position + std::string const m_scrollyinput_tag; // tag of input port for vertical scroll position + bounds_vector const m_rawbounds; // raw (original) bounds of the item + bool const m_have_output; // whether we actually have an output + bool const m_input_raw; // get raw data from input port + bool const m_have_animoutput; // whether we actually have an output for animation + bool const m_have_scrollxoutput; // whether we actually have an output for horizontal scroll + bool const m_have_scrollyoutput; // whether we actually have an output for vertical scroll + bool const m_has_clickthrough; // whether clickthrough was explicitly configured +}; + + +/// \brief A single view within a #layout_file +/// +/// The view is described using arbitrary coordinates that are scaled to +/// fit within the render target. Pixels within a view are assumed to +/// be square. +class layout_view +{ +public: + using layout_environment = emu::render::detail::layout_environment; + using view_environment = emu::render::detail::view_environment; + using element_map = layout_view_item::element_map; + using group_map = std::unordered_map<std::string, layout_group>; + using screen_ref_vector = std::vector<std::reference_wrapper<screen_device const>>; + using prepare_items_delegate = delegate<void ()>; + using preload_delegate = delegate<void ()>; + using recomputed_delegate = delegate<void ()>; + using pointer_updated_delegate = delegate<void (osd::ui_event_handler::pointer, u16, u16, float, float, u32, u32, u32, s16)>; + using pointer_left_delegate = delegate<void (osd::ui_event_handler::pointer, u16, u16, float, float, u32, s16)>; + using forget_pointers_delegate = delegate<void ()>; + + using item = layout_view_item; + using item_list = std::list<item>; + using item_ref_vector = std::vector<std::reference_wrapper<item> >; + + /// \brief A subset of items in a view that can be hidden or shown + /// + /// Visibility toggles allow the user to show or hide selected parts + /// of a view. + class visibility_toggle + { + public: + // construction/destruction/assignment + visibility_toggle(std::string &&name, u32 mask); + visibility_toggle(visibility_toggle const &) = default; + visibility_toggle(visibility_toggle &&) = default; + visibility_toggle &operator=(visibility_toggle const &) = default; + visibility_toggle &operator=(visibility_toggle &&) = default; + + // getters + std::string const &name() const { return m_name; } + u32 mask() const { return m_mask; } + + private: + std::string m_name; // display name for the toggle + u32 m_mask; // toggle combination to show + }; + using visibility_toggle_vector = std::vector<visibility_toggle>; + + /// \brief An edge of an item in a view + class edge + { + public: + // construction/destruction + constexpr edge(unsigned index, float position, bool trailing) + : m_index(index) + , m_position(position) + , m_trailing(trailing) + { + } + + // getters + constexpr unsigned index() const { return m_index; } + constexpr float position() const { return m_position; } + constexpr bool trailing() const { return m_trailing; } + + // comparison + constexpr bool operator<(edge const &that) const + { + return std::make_tuple(m_position, m_trailing, m_index) < std::make_tuple(that.m_position, that.m_trailing, that.m_index); + } + + private: + unsigned m_index; // index of item in some collection + float m_position; // position of edge on given axis + bool m_trailing; // false for edge at lower position on axis + }; + using edge_vector = std::vector<edge>; + + // construction/destruction + layout_view( + layout_environment &env, + util::xml::data_node const &viewnode, + element_map &elemmap, + group_map &groupmap); + ~layout_view(); + + // getters + item *get_item(std::string const &id); + item_list &items() { return m_items; } + bool has_screen(screen_device const &screen) const; + const std::string &name() const { return m_name; } + const std::string &unqualified_name() const { return m_unqualified_name; } + size_t visible_screen_count() const { return m_screens.size(); } + float effective_aspect() const { return m_effaspect; } + const render_bounds &bounds() const { return m_bounds; } + bool has_visible_screen(screen_device const &screen) const; + const item_ref_vector &visible_items() const { return m_visible_items; } + const item_ref_vector &visible_screen_items() const { return m_screen_items; } + const item_ref_vector &interactive_items() const { return m_interactive_items; } + const edge_vector &interactive_edges_x() const { return m_interactive_edges_x; } + const edge_vector &interactive_edges_y() const { return m_interactive_edges_y; } + const screen_ref_vector &visible_screens() const { return m_screens; } + const visibility_toggle_vector &visibility_toggles() const { return m_vistoggles; } + u32 default_visibility_mask() const { return m_defvismask; } + bool has_art() const { return m_has_art; } + bool show_pointers() const { return m_show_ptr; } + bool hide_inactive_pointers() const { return m_ptr_time_out; } + + // setters + void set_show_pointers(bool value) noexcept; + void set_hide_inactive_pointers(bool value) noexcept ATTR_COLD; + + // set handlers + void set_prepare_items_callback(prepare_items_delegate &&handler) ATTR_COLD; + void set_preload_callback(preload_delegate &&handler) ATTR_COLD; + void set_recomputed_callback(recomputed_delegate &&handler) ATTR_COLD; + void set_pointer_updated_callback(pointer_updated_delegate &&handler) ATTR_COLD; + void set_pointer_left_callback(pointer_left_delegate &&handler) ATTR_COLD; + void set_pointer_aborted_callback(pointer_left_delegate &&handler) ATTR_COLD; + void set_forget_pointers_callback(forget_pointers_delegate &&handler) ATTR_COLD; + + // operations + void prepare_items(); + void recompute(u32 visibility_mask, bool zoom_to_screens); + void preload(); + + // resolve tags, if any + void resolve_tags(); + + // pointer input + void pointer_updated(osd::ui_event_handler::pointer type, u16 ptrid, u16 device, float x, float y, u32 buttons, u32 pressed, u32 released, s16 clicks) + { + if (!m_pointer_updated.isnull()) + m_pointer_updated(type, ptrid, device, x, y, buttons, pressed, released, clicks); + } + void pointer_left(osd::ui_event_handler::pointer type, u16 ptrid, u16 device, float x, float y, u32 released, s16 clicks) + { + if (!m_pointer_left.isnull()) + m_pointer_left(type, ptrid, device, x, y, released, clicks); + } + void pointer_aborted(osd::ui_event_handler::pointer type, u16 ptrid, u16 device, float x, float y, u32 released, s16 clicks) + { + if (!m_pointer_aborted.isnull()) + m_pointer_aborted(type, ptrid, device, x, y, released, clicks); + } + void forget_pointers() + { + if (!m_forget_pointers.isnull()) + m_forget_pointers(); + } + +private: + struct layer_lists; + + using item_id_map = std::unordered_map< + std::reference_wrapper<std::string const>, + item &, + std::hash<std::string>, + std::equal_to<std::string> >; + + // add items, recursing for groups + void add_items( + layer_lists &layers, + view_environment &env, + util::xml::data_node const &parentnode, + element_map &elemmap, + group_map &groupmap, + int orientation, + layout_group::transform const &trans, + render_color const &color, + bool root, + bool repeat, + bool init); + + static std::string make_name(layout_environment &env, util::xml::data_node const &viewnode); + + // internal state + float m_effaspect; // X/Y of the layout in current configuration + render_bounds m_bounds; // computed bounds of the view in current configuration + item_list m_items; // list of layout items + item_ref_vector m_visible_items; // all visible items + item_ref_vector m_screen_items; // visible items that represent screens to draw + item_ref_vector m_interactive_items;// visible items that can accept pointer input + edge_vector m_interactive_edges_x; + edge_vector m_interactive_edges_y; + screen_ref_vector m_screens; // list screens visible in current configuration + + // handlers + prepare_items_delegate m_prepare_items; // prepare items for adding to render container + preload_delegate m_preload; // additional actions when visible items change + recomputed_delegate m_recomputed; // additional actions on resizing/visibility change + pointer_updated_delegate m_pointer_updated; // pointer state updated + pointer_left_delegate m_pointer_left; // pointer left normally + pointer_left_delegate m_pointer_aborted; // pointer left abnormally + forget_pointers_delegate m_forget_pointers; // stop processing pointer input + + // cold items + std::string m_name; // display name for the view + std::string m_unqualified_name; // the name exactly as specified in the layout file + element_map & m_elemmap; // reference to shared elements + item_id_map m_items_by_id; // items with non-empty ID indexed by ID + visibility_toggle_vector m_vistoggles; // collections of items that can be shown/hidden + render_bounds m_expbounds; // explicit bounds of the view + u32 m_defvismask; // default visibility mask + bool m_has_art; // true if the layout contains non-screen elements + bool m_show_ptr; // whether pointers should be displayed + bool m_ptr_time_out; // whether pointers should be hidden after inactivity + s8 m_exp_show_ptr; // explicitly configured pointer visibility +}; + + +/// \brief Layout description file +/// +/// Comprises a list of elements and a list of views. The elements are +/// reusable items that the views reference. +class layout_file +{ +public: + using element_map = std::unordered_map<std::string, layout_element>; + using group_map = std::unordered_map<std::string, layout_group>; + using view_list = std::list<layout_view>; + using resolve_tags_delegate = delegate<void ()>; + + // construction/destruction + layout_file(device_t &device, util::xml::data_node const &rootnode, char const *searchpath, char const *dirname); + ~layout_file(); + + // getters + device_t &device() const { return m_device; } + element_map &elements() { return m_elemmap; } + element_map const &elements() const { return m_elemmap; } + view_list &views() { return m_viewlist; } + view_list const &views() const { return m_viewlist; } + + // resolve tags, if any + void resolve_tags(); + + // set handlers + void set_resolve_tags_callback(resolve_tags_delegate &&handler); -// dual screen layouts -extern const internal_layout layout_dualhsxs; // dual 4:3 screens side-by-side -extern const internal_layout layout_dualhovu; // dual 4:3 screens above and below -extern const internal_layout layout_dualhuov; // dual 4:3 screens below and above +private: + using environment = emu::render::detail::layout_environment; -// triple screen layouts -extern const internal_layout layout_triphsxs; // triple 4:3 screens side-by-side + // add elements and parameters + void add_elements( + environment &env, + util::xml::data_node const &parentnode, + group_map &groupmap, + bool repeat, + bool init); -// quad screen layouts -extern const internal_layout layout_quadhsxs; // quad 4:3 screens side-by-side + // internal state + device_t & m_device; // device that caused file to be loaded + element_map m_elemmap; // list of shared layout elements + view_list m_viewlist; // list of views + resolve_tags_delegate m_resolve_tags; // additional actions after resolving tags +}; #endif // MAME_EMU_RENDLAY_H diff --git a/src/emu/rendutil.cpp b/src/emu/rendutil.cpp index ba8e177f93e..5d09061ab70 100644 --- a/src/emu/rendutil.cpp +++ b/src/emu/rendutil.cpp @@ -2,27 +2,144 @@ // copyright-holders:Aaron Giles /*************************************************************************** - rendutil.c + rendutil.cpp Core rendering utilities. ***************************************************************************/ #include "emu.h" -#include "render.h" #include "rendutil.h" + +#include "msdib.h" #include "png.h" #include "jpeglib.h" +#include "jerror.h" + +#include <csetjmp> +#include <cstdlib> +#include <tuple> + + +namespace { + +struct jpeg_corefile_source : public jpeg_source_mgr +{ + static void source(j_decompress_ptr cinfo, util::random_read &file) noexcept; + +private: + static constexpr unsigned INPUT_BUF_SIZE = 4096; + + static void do_init(j_decompress_ptr cinfo) noexcept + { + jpeg_corefile_source &src = *static_cast<jpeg_corefile_source *>(cinfo->src); + src.start_of_file = true; + } + + static boolean do_fill(j_decompress_ptr cinfo) noexcept + { + jpeg_corefile_source &src = *static_cast<jpeg_corefile_source *>(cinfo->src); + + auto [err, nbytes] = read(*src.infile, src.buffer, INPUT_BUF_SIZE); // TODO: check error return + + if (0 >= nbytes) + { + if (src.start_of_file) + ERREXIT(cinfo, JERR_INPUT_EMPTY); + WARNMS(cinfo, JWRN_JPEG_EOF); + src.buffer[0] = JOCTET(0xff); + src.buffer[1] = JOCTET(JPEG_EOI); + nbytes = 2; + } + + src.next_input_byte = src.buffer; + src.bytes_in_buffer = nbytes; + src.start_of_file = false; + + return TRUE; + } + + static void do_skip(j_decompress_ptr cinfo, long num_bytes) noexcept + { + jpeg_corefile_source &src = *static_cast<jpeg_corefile_source *>(cinfo->src); + + if (0 < num_bytes) + { + while (long(src.bytes_in_buffer) < num_bytes) + { + num_bytes -= long(src.bytes_in_buffer); + std::ignore = (*src.fill_input_buffer)(cinfo); + } + src.next_input_byte += size_t(num_bytes); + src.bytes_in_buffer -= size_t(num_bytes); + } + } + + static void do_term(j_decompress_ptr cinfo) noexcept + { + } + + util::random_read *infile; + JOCTET *buffer; + bool start_of_file; +}; + +void jpeg_corefile_source::source(j_decompress_ptr cinfo, util::random_read &file) noexcept +{ + jpeg_corefile_source *src; + if (!cinfo->src) + { + src = reinterpret_cast<jpeg_corefile_source *>( + (*cinfo->mem->alloc_small)( + reinterpret_cast<j_common_ptr>(cinfo), + JPOOL_PERMANENT, + sizeof(jpeg_corefile_source))); + cinfo->src = src; + src->buffer = reinterpret_cast<JOCTET *>( + (*cinfo->mem->alloc_small)( + reinterpret_cast<j_common_ptr>(cinfo), + JPOOL_PERMANENT, + INPUT_BUF_SIZE * sizeof(JOCTET))); + } + else + { + src = static_cast<jpeg_corefile_source *>(cinfo->src); + } + + src->init_source = &jpeg_corefile_source::do_init; + src->fill_input_buffer = &jpeg_corefile_source::do_fill; + src->skip_input_data = &jpeg_corefile_source::do_skip; + src->resync_to_restart = jpeg_resync_to_restart; + src->term_source = &jpeg_corefile_source::do_term; + src->infile = &file; + src->bytes_in_buffer = 0; + src->next_input_byte = nullptr; +} + + +struct jpeg_setjmp_error_mgr : public jpeg_error_mgr +{ + jpeg_setjmp_error_mgr() noexcept + { + jpeg_std_error(this); + error_exit = [] (j_common_ptr cinfo) { std::longjmp(static_cast<jpeg_setjmp_error_mgr *>(cinfo->err)->m_jump_buffer, 1); }; + } + + std::jmp_buf m_jump_buffer; +}; + +} // anonymous namespace + /*************************************************************************** FUNCTION PROTOTYPES ***************************************************************************/ /* utilities */ -static void resample_argb_bitmap_average(u32 *dest, u32 drowpixels, u32 dwidth, u32 dheight, const u32 *source, u32 srowpixels, u32 swidth, u32 sheight, const render_color &color, u32 dx, u32 dy); -static void resample_argb_bitmap_bilinear(u32 *dest, u32 drowpixels, u32 dwidth, u32 dheight, const u32 *source, u32 srowpixels, u32 swidth, u32 sheight, const render_color &color, u32 dx, u32 dy); -static bool copy_png_alpha_to_bitmap(bitmap_argb32 &bitmap, const png_info &png); +static void resample_argb_bitmap_average(u32 *dest, u32 drowpixels, u32 dwidth, u32 dheight, const u32 *source, u32 srowpixels, u32 swidth, u32 sheight, const render_color &color, u32 dx, u32 dy) noexcept; +static void resample_argb_bitmap_bilinear(u32 *dest, u32 drowpixels, u32 dwidth, u32 dheight, const u32 *source, u32 srowpixels, u32 swidth, u32 sheight, const render_color &color, u32 dx, u32 dy) noexcept; +static bool copy_png_alpha_to_bitmap(bitmap_argb32 &bitmap, const util::png_info &png) noexcept; @@ -35,15 +152,15 @@ static bool copy_png_alpha_to_bitmap(bitmap_argb32 &bitmap, const png_info &png) quality resampling of a texture -------------------------------------------------*/ -void render_resample_argb_bitmap_hq(bitmap_argb32 &dest, bitmap_argb32 &source, const render_color &color, bool force) +void render_resample_argb_bitmap_hq(bitmap_argb32 &dest, bitmap_argb32 &source, const render_color &color, bool force) noexcept { if (dest.width() == 0 || dest.height() == 0) return; - /* adjust the source base */ - const u32 *sbase = &source.pix32(0); + // adjust the source base + const u32 *sbase = &source.pix(0); - /* determine the steppings */ + // determine the steppings u32 swidth = source.width(); u32 sheight = source.height(); u32 dwidth = dest.width(); @@ -51,7 +168,7 @@ void render_resample_argb_bitmap_hq(bitmap_argb32 &dest, bitmap_argb32 &source, u32 dx = (swidth << 12) / dwidth; u32 dy = (sheight << 12) / dheight; - /* if the source is higher res than the target, use full averaging */ + // if the source is higher res than the target, use full averaging if (dx > 0x1000 || dy > 0x1000 || force) resample_argb_bitmap_average(&dest.pix(0), dest.rowpixels(), dwidth, dheight, sbase, source.rowpixels(), swidth, sheight, color, dx, dy); else @@ -65,62 +182,58 @@ void render_resample_argb_bitmap_hq(bitmap_argb32 &dest, bitmap_argb32 &source, all contributing pixels -------------------------------------------------*/ -static void resample_argb_bitmap_average(u32 *dest, u32 drowpixels, u32 dwidth, u32 dheight, const u32 *source, u32 srowpixels, u32 swidth, u32 sheight, const render_color &color, u32 dx, u32 dy) +static void resample_argb_bitmap_average(u32 *dest, u32 drowpixels, u32 dwidth, u32 dheight, const u32 *source, u32 srowpixels, u32 swidth, u32 sheight, const render_color &color, u32 dx, u32 dy) noexcept { - u64 sumscale = u64(dx) * u64(dy); - u32 r, g, b, a; - u32 x, y; - - /* precompute premultiplied R/G/B/A factors */ - r = color.r * color.a * 256.0f; - g = color.g * color.a * 256.0f; - b = color.b * color.a * 256.0f; - a = color.a * 256.0f; - - /* loop over the target vertically */ - for (y = 0; y < dheight; y++) + u64 const sumscale = u64(dx) * u64(dy); + + // precompute premultiplied R/G/B/A factors + u32 const r = color.r * color.a * 256.0f; + u32 const g = color.g * color.a * 256.0f; + u32 const b = color.b * color.a * 256.0f; + u32 const a = color.a * 256.0f; + + // loop over the target vertically + for (u32 y = 0; y < dheight; y++) { - u32 starty = y * dy; + u32 const starty = y * dy; - /* loop over the target horizontally */ - for (x = 0; x < dwidth; x++) + // loop over the target horizontally + for (u32 x = 0; x < dwidth; x++) { u64 sumr = 0, sumg = 0, sumb = 0, suma = 0; - u32 startx = x * dx; - u32 xchunk, ychunk; - u32 curx, cury; + u32 const startx = x * dx; u32 yremaining = dy; + u32 ychunk; - /* accumulate all source pixels that contribute to this pixel */ - for (cury = starty; yremaining; cury += ychunk) + // accumulate all source pixels that contribute to this pixel + for (u32 cury = starty; yremaining; cury += ychunk) { u32 xremaining = dx; + u32 xchunk; - /* determine the Y contribution, clamping to the amount remaining */ + // determine the Y contribution, clamping to the amount remaining ychunk = 0x1000 - (cury & 0xfff); if (ychunk > yremaining) ychunk = yremaining; yremaining -= ychunk; - /* loop over all source pixels in the X direction */ - for (curx = startx; xremaining; curx += xchunk) + // loop over all source pixels in the X direction + for (u32 curx = startx; xremaining; curx += xchunk) { - u32 factor; - - /* determine the X contribution, clamping to the amount remaining */ + // determine the X contribution, clamping to the amount remaining xchunk = 0x1000 - (curx & 0xfff); if (xchunk > xremaining) xchunk = xremaining; xremaining -= xchunk; - /* total contribution = x * y */ - factor = xchunk * ychunk; + // total contribution = x * y + u32 const factor = xchunk * ychunk; - /* fetch the source pixel */ - rgb_t pix = source[(cury >> 12) * srowpixels + (curx >> 12)]; + // fetch the source pixel + rgb_t const pix = source[(cury >> 12) * srowpixels + (curx >> 12)]; - /* accumulate the RGBA values */ + // accumulate the RGBA values sumr += factor * pix.r(); sumg += factor * pix.g(); sumb += factor * pix.b(); @@ -128,23 +241,23 @@ static void resample_argb_bitmap_average(u32 *dest, u32 drowpixels, u32 dwidth, } } - /* apply scaling */ + // apply scaling suma = (suma / sumscale) * a / 256; sumr = (sumr / sumscale) * r / 256; sumg = (sumg / sumscale) * g / 256; sumb = (sumb / sumscale) * b / 256; - /* if we're translucent, add in the destination pixel contribution */ + // if we're translucent, add in the destination pixel contribution if (a < 256) { - rgb_t dpix = dest[y * drowpixels + x]; + rgb_t const dpix = dest[y * drowpixels + x]; suma += dpix.a() * (256 - a); sumr += dpix.r() * (256 - a); sumg += dpix.g() * (256 - a); sumb += dpix.b() * (256 - a); } - /* store the target pixel, dividing the RGBA values by the overall scale factor */ + // store the target pixel, dividing the RGBA values by the overall scale factor dest[y * drowpixels + x] = rgb_t(suma, sumr, sumg, sumb); } } @@ -156,44 +269,37 @@ static void resample_argb_bitmap_average(u32 *dest, u32 drowpixels, u32 dwidth, sampling via a bilinear filter -------------------------------------------------*/ -static void resample_argb_bitmap_bilinear(u32 *dest, u32 drowpixels, u32 dwidth, u32 dheight, const u32 *source, u32 srowpixels, u32 swidth, u32 sheight, const render_color &color, u32 dx, u32 dy) +static void resample_argb_bitmap_bilinear(u32 *dest, u32 drowpixels, u32 dwidth, u32 dheight, const u32 *source, u32 srowpixels, u32 swidth, u32 sheight, const render_color &color, u32 dx, u32 dy) noexcept { - u32 maxx = swidth << 12, maxy = sheight << 12; - u32 r, g, b, a; - u32 x, y; - - /* precompute premultiplied R/G/B/A factors */ - r = color.r * color.a * 256.0f; - g = color.g * color.a * 256.0f; - b = color.b * color.a * 256.0f; - a = color.a * 256.0f; - - /* loop over the target vertically */ - for (y = 0; y < dheight; y++) + u32 const maxx = swidth << 12, maxy = sheight << 12; + + // precompute premultiplied R/G/B/A factors + u32 const r = color.r * color.a * 256.0f; + u32 const g = color.g * color.a * 256.0f; + u32 const b = color.b * color.a * 256.0f; + u32 const a = color.a * 256.0f; + + // loop over the target vertically + for (u32 y = 0; y < dheight; y++) { - u32 starty = y * dy; + u32 const starty = y * dy; - /* loop over the target horizontally */ - for (x = 0; x < dwidth; x++) + // loop over the target horizontally + for (u32 x = 0; x < dwidth; x++) { - u32 startx = x * dx; - rgb_t pix0, pix1, pix2, pix3; - u32 sumr, sumg, sumb, suma; - u32 nextx, nexty; - u32 curx, cury; - u32 factor; + u32 const startx = x * dx; - /* adjust start to the center; note that this math will tend to produce */ - /* negative results on the first pixel, which is why we clamp below */ - curx = startx + dx / 2 - 0x800; - cury = starty + dy / 2 - 0x800; + // adjust start to the center; note that this math will tend to produce + // negative results on the first pixel, which is why we clamp below + u32 curx = startx + dx / 2 - 0x800; + u32 cury = starty + dy / 2 - 0x800; - /* compute the neighboring pixel */ - nextx = curx + 0x1000; - nexty = cury + 0x1000; + // compute the neighboring pixel + u32 const nextx = curx + 0x1000; + u32 const nexty = cury + 0x1000; - /* fetch the four relevant pixels */ - pix0 = pix1 = pix2 = pix3 = 0; + // fetch the four relevant pixels + rgb_t pix0 = 0, pix1 = 0, pix2 = 0, pix3 = 0; if (s32(cury) >= 0 && cury < maxy && s32(curx) >= 0 && curx < maxx) pix0 = source[(cury >> 12) * srowpixels + (curx >> 12)]; if (s32(cury) >= 0 && cury < maxy && s32(nextx) >= 0 && nextx < maxx) @@ -203,173 +309,176 @@ static void resample_argb_bitmap_bilinear(u32 *dest, u32 drowpixels, u32 dwidth, if (s32(nexty) >= 0 && nexty < maxy && s32(nextx) >= 0 && nextx < maxx) pix3 = source[(nexty >> 12) * srowpixels + (nextx >> 12)]; - /* compute the x/y scaling factors */ + // compute the x/y scaling factors curx &= 0xfff; cury &= 0xfff; - /* contributions from pixel 0 (top,left) */ + u32 factor; + u32 sumr, sumg, sumb, suma; + + // contributions from pixel 0 (top,left) factor = (0x1000 - curx) * (0x1000 - cury); sumr = factor * pix0.r(); sumg = factor * pix0.g(); sumb = factor * pix0.b(); suma = factor * pix0.a(); - /* contributions from pixel 1 (top,right) */ + // contributions from pixel 1 (top,right) factor = curx * (0x1000 - cury); sumr += factor * pix1.r(); sumg += factor * pix1.g(); sumb += factor * pix1.b(); suma += factor * pix1.a(); - /* contributions from pixel 2 (bottom,left) */ + // contributions from pixel 2 (bottom,left) factor = (0x1000 - curx) * cury; sumr += factor * pix2.r(); sumg += factor * pix2.g(); sumb += factor * pix2.b(); suma += factor * pix2.a(); - /* contributions from pixel 3 (bottom,right) */ + // contributions from pixel 3 (bottom,right) factor = curx * cury; sumr += factor * pix3.r(); sumg += factor * pix3.g(); sumb += factor * pix3.b(); suma += factor * pix3.a(); - /* apply scaling */ + // apply scaling suma = (suma >> 24) * a / 256; sumr = (sumr >> 24) * r / 256; sumg = (sumg >> 24) * g / 256; sumb = (sumb >> 24) * b / 256; - /* if we're translucent, add in the destination pixel contribution */ + // if we're translucent, add in the destination pixel contribution if (a < 256) { - rgb_t dpix = dest[y * drowpixels + x]; + rgb_t const dpix = dest[y * drowpixels + x]; suma += dpix.a() * (256 - a); sumr += dpix.r() * (256 - a); sumg += dpix.g() * (256 - a); sumb += dpix.b() * (256 - a); } - /* store the target pixel, dividing the RGBA values by the overall scale factor */ + // store the target pixel, dividing the RGBA values by the overall scale factor dest[y * drowpixels + x] = rgb_t(suma, sumr, sumg, sumb); } } } -/*------------------------------------------------- - render_clip_line - clip a line to a rectangle --------------------------------------------------*/ +//------------------------------------------------- +// render_clip_line - clip a line to a rectangle +//------------------------------------------------- -bool render_clip_line(render_bounds *bounds, const render_bounds *clip) +bool render_clip_line(render_bounds &bounds, const render_bounds &clip) { - /* loop until we get a final result */ - while (1) + // loop until we get a final result + while (true) { u8 code0 = 0, code1 = 0; u8 thiscode; float x, y; - /* compute Cohen Sutherland bits for first coordinate */ - if (bounds->y0 > clip->y1) + // compute Cohen Sutherland bits for first coordinate + if (bounds.y0 > clip.y1) code0 |= 1; - if (bounds->y0 < clip->y0) + if (bounds.y0 < clip.y0) code0 |= 2; - if (bounds->x0 > clip->x1) + if (bounds.x0 > clip.x1) code0 |= 4; - if (bounds->x0 < clip->x0) + if (bounds.x0 < clip.x0) code0 |= 8; - /* compute Cohen Sutherland bits for second coordinate */ - if (bounds->y1 > clip->y1) + // compute Cohen Sutherland bits for second coordinate + if (bounds.y1 > clip.y1) code1 |= 1; - if (bounds->y1 < clip->y0) + if (bounds.y1 < clip.y0) code1 |= 2; - if (bounds->x1 > clip->x1) + if (bounds.x1 > clip.x1) code1 |= 4; - if (bounds->x1 < clip->x0) + if (bounds.x1 < clip.x0) code1 |= 8; - /* trivial accept: just return false */ + // trivial accept: just return false if ((code0 | code1) == 0) return false; - /* trivial reject: just return true */ + // trivial reject: just return true if ((code0 & code1) != 0) return true; - /* fix one of the OOB cases */ + // fix one of the OOB cases thiscode = code0 ? code0 : code1; - /* off the bottom */ + // off the bottom if (thiscode & 1) { - x = bounds->x0 + (bounds->x1 - bounds->x0) * (clip->y1 - bounds->y0) / (bounds->y1 - bounds->y0); - y = clip->y1; + x = bounds.x0 + (bounds.x1 - bounds.x0) * (clip.y1 - bounds.y0) / (bounds.y1 - bounds.y0); + y = clip.y1; } - /* off the top */ + // off the top else if (thiscode & 2) { - x = bounds->x0 + (bounds->x1 - bounds->x0) * (clip->y0 - bounds->y0) / (bounds->y1 - bounds->y0); - y = clip->y0; + x = bounds.x0 + (bounds.x1 - bounds.x0) * (clip.y0 - bounds.y0) / (bounds.y1 - bounds.y0); + y = clip.y0; } - /* off the right */ + // off the right else if (thiscode & 4) { - y = bounds->y0 + (bounds->y1 - bounds->y0) * (clip->x1 - bounds->x0) / (bounds->x1 - bounds->x0); - x = clip->x1; + y = bounds.y0 + (bounds.y1 - bounds.y0) * (clip.x1 - bounds.x0) / (bounds.x1 - bounds.x0); + x = clip.x1; } - /* off the left */ + // off the left else { - y = bounds->y0 + (bounds->y1 - bounds->y0) * (clip->x0 - bounds->x0) / (bounds->x1 - bounds->x0); - x = clip->x0; + y = bounds.y0 + (bounds.y1 - bounds.y0) * (clip.x0 - bounds.x0) / (bounds.x1 - bounds.x0); + x = clip.x0; } - /* fix the appropriate coordinate */ + // fix the appropriate coordinate if (thiscode == code0) { - bounds->x0 = x; - bounds->y0 = y; + bounds.x0 = x; + bounds.y0 = y; } else { - bounds->x1 = x; - bounds->y1 = y; + bounds.x1 = x; + bounds.y1 = y; } } } -/*------------------------------------------------- - render_clip_quad - clip a quad to a rectangle --------------------------------------------------*/ +//------------------------------------------------- +// render_clip_quad - clip a quad to a rectangle +//------------------------------------------------- -bool render_clip_quad(render_bounds *bounds, const render_bounds *clip, render_quad_texuv *texcoords) +bool render_clip_quad(render_bounds &bounds, const render_bounds &clip, render_quad_texuv *texcoords) { - /* ensure our assumptions about the bounds are correct */ - assert(bounds->x0 <= bounds->x1); - assert(bounds->y0 <= bounds->y1); + // ensure our assumptions about the bounds are correct + assert(bounds.x0 <= bounds.x1); + assert(bounds.y0 <= bounds.y1); - /* trivial reject */ - if (bounds->y1 < clip->y0) + // trivial reject + if (bounds.y1 < clip.y0) return true; - if (bounds->y0 > clip->y1) + if (bounds.y0 > clip.y1) return true; - if (bounds->x1 < clip->x0) + if (bounds.x1 < clip.x0) return true; - if (bounds->x0 > clip->x1) + if (bounds.x0 > clip.x1) return true; - /* clip top (x0,y0)-(x1,y1) */ - if (bounds->y0 < clip->y0) + // clip top (x0,y0)-(x1,y1) + if (bounds.y0 < clip.y0) { - float frac = (clip->y0 - bounds->y0) / (bounds->y1 - bounds->y0); - bounds->y0 = clip->y0; + float frac = (clip.y0 - bounds.y0) / (bounds.y1 - bounds.y0); + bounds.y0 = clip.y0; if (texcoords != nullptr) { texcoords->tl.u += (texcoords->bl.u - texcoords->tl.u) * frac; @@ -379,11 +488,11 @@ bool render_clip_quad(render_bounds *bounds, const render_bounds *clip, render_q } } - /* clip bottom (x3,y3)-(x2,y2) */ - if (bounds->y1 > clip->y1) + // clip bottom (x3,y3)-(x2,y2) + if (bounds.y1 > clip.y1) { - float frac = (bounds->y1 - clip->y1) / (bounds->y1 - bounds->y0); - bounds->y1 = clip->y1; + float frac = (bounds.y1 - clip.y1) / (bounds.y1 - bounds.y0); + bounds.y1 = clip.y1; if (texcoords != nullptr) { texcoords->bl.u -= (texcoords->bl.u - texcoords->tl.u) * frac; @@ -393,11 +502,11 @@ bool render_clip_quad(render_bounds *bounds, const render_bounds *clip, render_q } } - /* clip left (x0,y0)-(x3,y3) */ - if (bounds->x0 < clip->x0) + // clip left (x0,y0)-(x3,y3) + if (bounds.x0 < clip.x0) { - float frac = (clip->x0 - bounds->x0) / (bounds->x1 - bounds->x0); - bounds->x0 = clip->x0; + float frac = (clip.x0 - bounds.x0) / (bounds.x1 - bounds.x0); + bounds.x0 = clip.x0; if (texcoords != nullptr) { texcoords->tl.u += (texcoords->tr.u - texcoords->tl.u) * frac; @@ -407,11 +516,11 @@ bool render_clip_quad(render_bounds *bounds, const render_bounds *clip, render_q } } - /* clip right (x1,y1)-(x2,y2) */ - if (bounds->x1 > clip->x1) + // clip right (x1,y1)-(x2,y2) + if (bounds.x1 > clip.x1) { - float frac = (bounds->x1 - clip->x1) / (bounds->x1 - bounds->x0); - bounds->x1 = clip->x1; + float frac = (bounds.x1 - clip.x1) / (bounds.x1 - bounds.x0); + bounds.x1 = clip.x1; if (texcoords != nullptr) { texcoords->tr.u -= (texcoords->tr.u - texcoords->tl.u) * frac; @@ -424,14 +533,14 @@ bool render_clip_quad(render_bounds *bounds, const render_bounds *clip, render_q } -/*------------------------------------------------- - render_line_to_quad - convert a line and a - width to four points --------------------------------------------------*/ +//------------------------------------------------- +// render_line_to_quad - convert a line and a +// width to four points +//----------------------------------------------- -void render_line_to_quad(const render_bounds *bounds, float width, float length_extension, render_bounds *bounds0, render_bounds *bounds1) +std::pair<render_bounds, render_bounds> render_line_to_quad(const render_bounds &bounds, float width, float length_extension) { - render_bounds modbounds = *bounds; + render_bounds modbounds = bounds; /* High-level logic -- due to math optimizations, this info is lost below. @@ -478,18 +587,18 @@ void render_line_to_quad(const render_bounds *bounds, float width, float length_ D.y = p1.y - 0.5 * w * u.x */ - /* we only care about the half-width */ + // we only care about the half-width float half_width = width * 0.5f; - /* compute a vector from point 0 to point 1 */ + // compute a vector from point 0 to point 1 float unitx = modbounds.x1 - modbounds.x0; float unity = modbounds.y1 - modbounds.y0; - /* points just use a +1/+1 unit vector; this gives a nice diamond pattern */ + // points just use a +1/+1 unit vector; this gives a nice diamond pattern if (unitx == 0 && unity == 0) { - /* length of a unit vector (1,1) */ - float unit_length = 0.70710678f; + // length of a unit vector (1,1) + constexpr float unit_length = 0.70710678f; unitx = unity = unit_length * half_width; modbounds.x0 -= unitx; @@ -498,12 +607,12 @@ void render_line_to_quad(const render_bounds *bounds, float width, float length_ modbounds.y1 += unity; } - /* lines need to be divided by their length */ + // lines need to be divided by their length else { float length = sqrtf(unitx * unitx + unity * unity); - /* extend line length */ + // extend line length if (length_extension > 0.0f) { float half_length_extension = length_extension *0.5f; @@ -517,106 +626,127 @@ void render_line_to_quad(const render_bounds *bounds, float width, float length_ modbounds.y1 += directiony * half_length_extension; } - /* prescale unitx and unity by the half-width */ + // prescale unitx and unity by the half-width float invlength = half_width / length; unitx *= invlength; unity *= invlength; } - /* rotate the unit vector by 90 degrees and add to point 0 */ - bounds0->x0 = modbounds.x0 - unity; - bounds0->y0 = modbounds.y0 + unitx; + // rotate the unit vector by 90 and -90 degrees and add to points 0 and 1 + return std::make_pair( + render_bounds{ modbounds.x0 - unity, modbounds.y0 + unitx, modbounds.x0 + unity, modbounds.y0 - unitx }, + render_bounds{ modbounds.x1 - unity, modbounds.y1 + unitx, modbounds.x1 + unity, modbounds.y1 - unitx }); +} - /* rotate the unit vector by -90 degrees and add to point 0 */ - bounds0->x1 = modbounds.x0 + unity; - bounds0->y1 = modbounds.y0 - unitx; - /* rotate the unit vector by 90 degrees and add to point 1 */ - bounds1->x0 = modbounds.x1 - unity; - bounds1->y0 = modbounds.y1 + unitx; +/*------------------------------------------------- + render_load_msdib - load a Microsoft DIB file + into a bitmap +-------------------------------------------------*/ + +void render_load_msdib(bitmap_argb32 &bitmap, util::random_read &file) noexcept +{ + // deallocate previous bitmap + bitmap.reset(); - /* rotate the unit vector by -90 degrees and add to point 1 */ - bounds1->x1 = modbounds.x1 + unity; - bounds1->y1 = modbounds.y1 - unitx; + // read the DIB data + util::msdib_error const result = util::msdib_read_bitmap(file, bitmap); + if (result != util::msdib_error::NONE) + { + osd_printf_error("Error reading Microsoft DIB file\n"); + bitmap.reset(); + } } /*------------------------------------------------- - render_load_jpeg - load a JPG file into a + render_load_jpeg - load a JPEG file into a bitmap -------------------------------------------------*/ -void render_load_jpeg(bitmap_argb32 &bitmap, emu_file &file, const char *dirname, const char *filename) +void render_load_jpeg(bitmap_argb32 &bitmap, util::random_read &file) noexcept { // deallocate previous bitmap bitmap.reset(); - // define file's full name - std::string fname; - - if (dirname == nullptr) - fname = filename; - else - fname.assign(dirname).append(PATH_SEPARATOR).append(filename); - - if (file.open(fname.c_str()) != osd_file::error::NONE) - return; - - // define standard JPEG structures + // set up context for error handling jpeg_decompress_struct cinfo; - jpeg_error_mgr jerr; - cinfo.err = jpeg_std_error(&jerr); - jpeg_create_decompress(&cinfo); - - // allocates a buffer for the image - u32 jpg_size = file.size(); - std::unique_ptr<unsigned char[]> jpg_buffer = std::make_unique<unsigned char[]>(jpg_size); + jpeg_setjmp_error_mgr jerr; + cinfo.err = &jerr; + JSAMPARRAY buffer = nullptr; + int w, h, s, row_stride, j, i; + if (setjmp(jerr.m_jump_buffer)) // setjmp must be used in control expression + { + char msg[1024]; + (cinfo.err->format_message)(reinterpret_cast<j_common_ptr>(&cinfo), msg); + osd_printf_error("JPEG error reading data from file: %s\n", msg); + bitmap.reset(); + goto cleanup; // use goto to ensure longjmp can't cross an initialisation + } - // read data from the file and set them in the buffer - file.read(jpg_buffer.get(), jpg_size); - jpeg_mem_src(&cinfo, jpg_buffer.get(), jpg_size); + // create a JPEG source for the file + jpeg_create_decompress(&cinfo); + cinfo.mem->max_memory_to_use = 128 * 1024 * 1024; + jpeg_corefile_source::source(&cinfo, file); // read JPEG header and start decompression jpeg_read_header(&cinfo, TRUE); jpeg_start_decompress(&cinfo); // allocates the destination bitmap - int w = cinfo.output_width; - int h = cinfo.output_height; - int s = cinfo.output_components; + w = cinfo.output_width; + h = cinfo.output_height; + s = cinfo.output_components; bitmap.allocate(w, h); // allocates a buffer to receive the information and copy them into the bitmap - int row_stride = cinfo.output_width * cinfo.output_components; - JSAMPARRAY buffer = (JSAMPARRAY)malloc(sizeof(JSAMPROW)); - buffer[0] = (JSAMPROW)malloc(sizeof(JSAMPLE) * row_stride); + row_stride = cinfo.output_width * cinfo.output_components; + buffer = reinterpret_cast<JSAMPARRAY>(std::malloc(sizeof(JSAMPROW))); + if (buffer) + buffer[0] = reinterpret_cast<JSAMPROW>(std::malloc(sizeof(JSAMPLE) * row_stride)); - while ( cinfo.output_scanline < cinfo.output_height ) + if (bitmap.valid() && buffer && buffer[0]) { - int j = cinfo.output_scanline; - jpeg_read_scanlines(&cinfo, buffer, 1); + while (cinfo.output_scanline < cinfo.output_height) + { + j = cinfo.output_scanline; + jpeg_read_scanlines(&cinfo, buffer, 1); - if (s == 1) - for (int i = 0; i < w; ++i) - bitmap.pix32(j, i) = rgb_t(0xFF, buffer[0][i], buffer[0][i], buffer[0][i]); + if (s == 1) + { + for (i = 0; i < w; ++i) + bitmap.pix(j, i) = rgb_t(0xff, buffer[0][i], buffer[0][i], buffer[0][i]); - else if (s == 3) - for (int i = 0; i < w; ++i) - bitmap.pix32(j, i) = rgb_t(0xFF, buffer[0][i * s], buffer[0][i * s + 1], buffer[0][i * s + 2]); - else - { - osd_printf_error("Cannot read JPEG data from %s file.\n", fname.c_str()); - bitmap.reset(); - break; + } + else if (s == 3) + { + for (i = 0; i < w; ++i) + bitmap.pix(j, i) = rgb_t(0xff, buffer[0][i * s], buffer[0][i * s + 1], buffer[0][i * s + 2]); + } + else + { + osd_printf_error("Cannot read JPEG data from file.\n"); + bitmap.reset(); + break; + } } } + else + { + osd_printf_error("Error allocating memory for JPEG image.\n"); + bitmap.reset(); + } - // finish decompression and frees the memory + // finish decompression and free the memory jpeg_finish_decompress(&cinfo); +cleanup: jpeg_destroy_decompress(&cinfo); - file.close(); - free(buffer[0]); - free(buffer); + if (buffer) + { + if (buffer[0]) + std::free(buffer[0]); + std::free(buffer); + } } @@ -625,36 +755,25 @@ void render_load_jpeg(bitmap_argb32 &bitmap, emu_file &file, const char *dirname bitmap -------------------------------------------------*/ -bool render_load_png(bitmap_argb32 &bitmap, emu_file &file, const char *dirname, const char *filename, bool load_as_alpha_to_existing) +bool render_load_png(bitmap_argb32 &bitmap, util::random_read &file, bool load_as_alpha_to_existing) noexcept { // deallocate if we're not overlaying alpha if (!load_as_alpha_to_existing) bitmap.reset(); - // open the file - std::string fname; - if (dirname) - fname.assign(dirname).append(PATH_SEPARATOR).append(filename); - else - fname.assign(filename); - osd_file::error const filerr = file.open(fname.c_str()); - if (filerr != osd_file::error::NONE) - return false; - // read the PNG data - png_info png; - png_error const result = png.read_file(file); - file.close(); - if (result != PNGERR_NONE) + util::png_info png; + std::error_condition const result = png.read_file(file); + if (result) { - osd_printf_error("%s: Error reading PNG file\n", filename); + osd_printf_error("Error reading PNG file\n"); return false; } // if less than 8 bits, upsample - if (PNGERR_NONE != png.expand_buffer_8bit()) + if (png.expand_buffer_8bit()) { - osd_printf_error("%s: Error upsampling PNG bitmap\n", filename); + osd_printf_error("Error upsampling PNG bitmap\n"); return false; } @@ -662,9 +781,9 @@ bool render_load_png(bitmap_argb32 &bitmap, emu_file &file, const char *dirname, if (!load_as_alpha_to_existing) { // non-alpha case - if (PNGERR_NONE != png.copy_to_bitmap(bitmap, hasalpha)) + if (png.copy_to_bitmap(bitmap, hasalpha)) { - osd_printf_error("%s: Error copying PNG bitmap to MAME bitmap\n", filename); + osd_printf_error("Error copying PNG bitmap to MAME bitmap\n"); return false; } } @@ -673,7 +792,7 @@ bool render_load_png(bitmap_argb32 &bitmap, emu_file &file, const char *dirname, // verify we can handle this PNG if (png.bit_depth > 8) { - osd_printf_error("%s: Unsupported bit depth %d (8 bit max)\n", filename, png.bit_depth); + osd_printf_error("Unsupported bit depth %d (8 bit max)\n", png.bit_depth); return false; } @@ -691,7 +810,7 @@ bool render_load_png(bitmap_argb32 &bitmap, emu_file &file, const char *dirname, to the alpha channel of a bitmap -------------------------------------------------*/ -static bool copy_png_alpha_to_bitmap(bitmap_argb32 &bitmap, const png_info &png) +static bool copy_png_alpha_to_bitmap(bitmap_argb32 &bitmap, const util::png_info &png) noexcept { // FIXME: this function is basically copy/pasted from the PNG code in util, and should be unified with it u8 accumalpha = 0xff; @@ -725,7 +844,7 @@ static bool copy_png_alpha_to_bitmap(bitmap_argb32 &bitmap, const png_info &png) { for (u32 x = 0; width > x; ++x, ++src) { - bitmap_argb32::pixel_t &dest(png.interlace_method ? bitmap.pix32(y_trans(y), x_trans(x)) : bitmap.pix32(y, x)); + bitmap_argb32::pixel_t &dest(png.interlace_method ? bitmap.pix(y_trans(y), x_trans(x)) : bitmap.pix(y, x)); rgb_t const pixel(dest); u8 const alpha(rgb_t(png.palette[*src * 3], png.palette[*src * 3 + 1], png.palette[*src * 3 + 2]).brightness()); accumalpha &= alpha; @@ -740,7 +859,7 @@ static bool copy_png_alpha_to_bitmap(bitmap_argb32 &bitmap, const png_info &png) { for (u32 x = 0; width > x; ++x, ++src) { - bitmap_argb32::pixel_t &dest(png.interlace_method ? bitmap.pix32(y_trans(y), x_trans(x)) : bitmap.pix32(y, x)); + bitmap_argb32::pixel_t &dest(png.interlace_method ? bitmap.pix(y_trans(y), x_trans(x)) : bitmap.pix(y, x)); rgb_t const pixel(dest); accumalpha &= *src; dest = rgb_t(*src, pixel.r(), pixel.g(), pixel.b()); @@ -754,7 +873,7 @@ static bool copy_png_alpha_to_bitmap(bitmap_argb32 &bitmap, const png_info &png) { for (u32 x = 0; width > x; ++x, src += 2) { - bitmap_argb32::pixel_t &dest(png.interlace_method ? bitmap.pix32(y_trans(y), x_trans(x)) : bitmap.pix32(y, x)); + bitmap_argb32::pixel_t &dest(png.interlace_method ? bitmap.pix(y_trans(y), x_trans(x)) : bitmap.pix(y, x)); rgb_t const pixel(dest); accumalpha &= *src; dest = rgb_t(*src, pixel.r(), pixel.g(), pixel.b()); @@ -768,7 +887,7 @@ static bool copy_png_alpha_to_bitmap(bitmap_argb32 &bitmap, const png_info &png) { for (u32 x = 0; width > x; ++x, src += 3) { - bitmap_argb32::pixel_t &dest(png.interlace_method ? bitmap.pix32(y_trans(y), x_trans(x)) : bitmap.pix32(y, x)); + bitmap_argb32::pixel_t &dest(png.interlace_method ? bitmap.pix(y_trans(y), x_trans(x)) : bitmap.pix(y, x)); rgb_t const pixel(dest); u8 const alpha(rgb_t(src[0], src[1], src[2]).brightness()); accumalpha &= alpha; @@ -783,7 +902,7 @@ static bool copy_png_alpha_to_bitmap(bitmap_argb32 &bitmap, const png_info &png) { for (u32 x = 0; width > x; ++x, src += 4) { - bitmap_argb32::pixel_t &dest(png.interlace_method ? bitmap.pix32(y_trans(y), x_trans(x)) : bitmap.pix32(y, x)); + bitmap_argb32::pixel_t &dest(png.interlace_method ? bitmap.pix(y_trans(y), x_trans(x)) : bitmap.pix(y, x)); rgb_t const pixel(dest); u8 const alpha(rgb_t(src[0], src[1], src[2]).brightness()); accumalpha &= alpha; @@ -802,29 +921,45 @@ static bool copy_png_alpha_to_bitmap(bitmap_argb32 &bitmap, const png_info &png) render_detect_image - detect image format -------------------------------------------------*/ -ru_imgformat render_detect_image(emu_file &file, const char *dirname, const char *filename) +ru_imgformat render_detect_image(util::random_read &file) noexcept { - // open the file - std::string fname; - if (dirname) - fname.assign(dirname).append(PATH_SEPARATOR).append(filename); - else - fname.assign(filename); - osd_file::error const filerr = file.open(fname.c_str()); - if (filerr != osd_file::error::NONE) - return RENDUTIL_IMGFORMAT_ERROR; - // PNG: check for valid header - png_error const result = png_info::verify_header(file); - if (result == PNGERR_NONE) { - file.close(); - return RENDUTIL_IMGFORMAT_PNG; + std::error_condition const png = util::png_info::verify_header(file); + file.seek(0, SEEK_SET); // TODO: check error return + if (!png) + return RENDUTIL_IMGFORMAT_PNG; + } + + // JPEG: attempt to read header with libjpeg + { + jpeg_decompress_struct cinfo; + jpeg_setjmp_error_mgr jerr; + cinfo.err = &jerr; + if (setjmp(jerr.m_jump_buffer)) // setjmp must be used in control expression + goto notjpeg; // use goto to ensure longjmp can't cross an initialisation + + jpeg_create_decompress(&cinfo); + cinfo.mem->max_memory_to_use = 128 * 1024 * 1024; + jpeg_corefile_source::source(&cinfo, file); + jpeg_read_header(&cinfo, TRUE); + jpeg_destroy_decompress(&cinfo); + file.seek(0, SEEK_SET); // TODO: check error return + return RENDUTIL_IMGFORMAT_JPEG; + + notjpeg: + jpeg_destroy_decompress(&cinfo); + file.seek(0, SEEK_SET); } - file.seek(0, SEEK_SET); - // TODO: add more when needed + // Microsoft DIB: check for valid header + { + util::msdib_error const msdib = util::msdib_verify_header(file); + file.seek(0, SEEK_SET); + if (util::msdib_error::NONE == msdib) + return RENDUTIL_IMGFORMAT_MSDIB; + } - file.close(); + // TODO: add more as necessary return RENDUTIL_IMGFORMAT_UNKNOWN; } diff --git a/src/emu/rendutil.h b/src/emu/rendutil.h index 1824fb0a34a..71e5bdc95c3 100644 --- a/src/emu/rendutil.h +++ b/src/emu/rendutil.h @@ -13,9 +13,13 @@ #pragma once -#include "render.h" +#include "rendertypes.h" -#include <math.h> +#include "utilfwd.h" + +#include <algorithm> +#include <cmath> +#include <utility> /* ----- image formats ----- */ @@ -23,6 +27,8 @@ enum ru_imgformat { RENDUTIL_IMGFORMAT_PNG, + RENDUTIL_IMGFORMAT_JPEG, + RENDUTIL_IMGFORMAT_MSDIB, RENDUTIL_IMGFORMAT_UNKNOWN, RENDUTIL_IMGFORMAT_ERROR @@ -36,13 +42,14 @@ enum ru_imgformat /* ----- render utilities ----- */ -void render_resample_argb_bitmap_hq(bitmap_argb32 &dest, bitmap_argb32 &source, const render_color &color, bool force = false); -bool render_clip_line(render_bounds *bounds, const render_bounds *clip); -bool render_clip_quad(render_bounds *bounds, const render_bounds *clip, render_quad_texuv *texcoords); -void render_line_to_quad(const render_bounds *bounds, float width, float length_extension, render_bounds *bounds0, render_bounds *bounds1); -void render_load_jpeg(bitmap_argb32 &bitmap, emu_file &file, const char *dirname, const char *filename); -bool render_load_png(bitmap_argb32 &bitmap, emu_file &file, const char *dirname, const char *filename, bool load_as_alpha_to_existing = false); -ru_imgformat render_detect_image(emu_file &file, const char *dirname, const char *filename); +void render_resample_argb_bitmap_hq(bitmap_argb32 &dest, bitmap_argb32 &source, const render_color &color, bool force = false) noexcept; +bool render_clip_line(render_bounds &bounds, const render_bounds &clip); +bool render_clip_quad(render_bounds &bounds, const render_bounds &clip, render_quad_texuv *texcoords); +std::pair<render_bounds, render_bounds> render_line_to_quad(const render_bounds &bounds, float width, float length_extension); +void render_load_msdib(bitmap_argb32 &bitmap, util::random_read &file) noexcept; +void render_load_jpeg(bitmap_argb32 &bitmap, util::random_read &file) noexcept; +bool render_load_png(bitmap_argb32 &bitmap, util::random_read &file, bool load_as_alpha_to_existing = false) noexcept; +ru_imgformat render_detect_image(util::random_read &file) noexcept; @@ -57,76 +64,7 @@ ru_imgformat render_detect_image(emu_file &file, const char *dirname, const char static inline float render_round_nearest(float f) { - return floor(f + 0.5f); -} - - -/*------------------------------------------------- - set_render_bounds_xy - cleaner way to set the - bounds --------------------------------------------------*/ - -static inline void set_render_bounds_xy(render_bounds &bounds, float x0, float y0, float x1, float y1) -{ - bounds.x0 = x0; - bounds.y0 = y0; - bounds.x1 = x1; - bounds.y1 = y1; -} - - -/*------------------------------------------------- - set_render_bounds_wh - cleaner way to set the - bounds --------------------------------------------------*/ - -static inline void set_render_bounds_wh(render_bounds &bounds, float x0, float y0, float width, float height) -{ - bounds.x0 = x0; - bounds.y0 = y0; - bounds.x1 = x0 + width; - bounds.y1 = y0 + height; -} - - -/*------------------------------------------------- - sect_render_bounds - compute the intersection - of two render_bounds --------------------------------------------------*/ - -static inline void sect_render_bounds(render_bounds &dest, const render_bounds &src) -{ - dest.x0 = (std::max)(dest.x0, src.x0); - dest.x1 = (std::min)(dest.x1, src.x1); - dest.y0 = (std::max)(dest.y0, src.y0); - dest.y1 = (std::min)(dest.y1, src.y1); -} - - -/*------------------------------------------------- - union_render_bounds - compute the union of two - render_bounds --------------------------------------------------*/ - -static inline void union_render_bounds(render_bounds &dest, const render_bounds &src) -{ - dest.x0 = (std::min)(dest.x0, src.x0); - dest.x1 = (std::max)(dest.x1, src.x1); - dest.y0 = (std::min)(dest.y0, src.y0); - dest.y1 = (std::max)(dest.y1, src.y1); -} - - -/*------------------------------------------------- - set_render_color - cleaner way to set a color --------------------------------------------------*/ - -static inline void set_render_color(render_color *color, float a, float r, float g, float b) -{ - color->a = a; - color->r = r; - color->g = g; - color->b = b; + return floorf(f + 0.5f); } @@ -135,7 +73,7 @@ static inline void set_render_color(render_color *color, float a, float r, float flip flags -------------------------------------------------*/ -static inline int orientation_swap_flips(int orientation) +constexpr int orientation_swap_flips(int orientation) { return (orientation & ORIENTATION_SWAP_XY) | ((orientation & ORIENTATION_FLIP_X) ? ORIENTATION_FLIP_Y : 0) | @@ -148,7 +86,7 @@ static inline int orientation_swap_flips(int orientation) that will undo another orientation -------------------------------------------------*/ -static inline int orientation_reverse(int orientation) +constexpr int orientation_reverse(int orientation) { /* if not swapping X/Y, then just apply the same transform to reverse */ if (!(orientation & ORIENTATION_SWAP_XY)) @@ -165,7 +103,7 @@ static inline int orientation_reverse(int orientation) after applying two subsequent orientations -------------------------------------------------*/ -static inline int orientation_add(int orientation1, int orientation2) +constexpr int orientation_add(int orientation1, int orientation2) { /* if the 2nd transform doesn't swap, just XOR together */ if (!(orientation2 & ORIENTATION_SWAP_XY)) @@ -192,11 +130,7 @@ static inline float apply_brightness_contrast_gamma_fp(float srcval, float brigh srcval = (srcval * contrast) + brightness - 1.0f; /* clamp and return */ - if (srcval < 0.0f) - srcval = 0.0f; - if (srcval > 1.0f) - srcval = 1.0f; - return srcval; + return std::clamp(srcval, 0.0f, 1.0f); } @@ -213,5 +147,4 @@ static inline u8 apply_brightness_contrast_gamma(u8 src, float brightness, float return u8(result * 255.0f); } - #endif // MAME_EMU_RENDUTIL_H diff --git a/src/emu/resampler.cpp b/src/emu/resampler.cpp new file mode 100644 index 00000000000..d319c4c0eb9 --- /dev/null +++ b/src/emu/resampler.cpp @@ -0,0 +1,439 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert + +// Audio resampler + +#include "emu.h" +#include "resampler.h" + +// How an accurate resampler works ? + +// Resampling uses a number of well-known theorems we are not trying +// to prove here. + +// Samping theorem. A digital signal sampled at frequency fs is +// equivalent to an analog signal where all frequencies are between 0 +// and fs/2. Equivalent here means that the samples are unique given +// the analog signal, the analog sugnal is unique given the samples, +// and going analog -> digital -> analog is perfect. + +// That gives us point one: resampling from fs to ft is, semantically, +// reconstructing the analog signal from the fs sampling, removing all +// frequencies over ft/2, then sampling at ft. + + +// Up-sampling theorem. Take a digital signal at frequency fs, and k +// an integer > 1. Create a new digital signal at frequency fs*k by +// alternatively taking one sample from the original signal and adding +// k-1 zeroes. If one recreates the corresponding analog signal and +// removes all frequencies over fs/2, then it will be identical to the +// original analog signal, up to a constant multiplier on the +// amplitude. For the curious the frequencies over fs/2 get copies of +// the original spectrum with inversions, e.g. the frequency fs/2-a is +// copied at fs/2+a, then it's not inverted at fs..fs*1.5, inverted +// again between fs*1.5 and fs*2, etc. + +// A corollary is that if one starts for an analog signal with no +// frequencies over fs/2, samples it at fs, then up-samples to fs*k by +// adding zeroes, remove (filter) from the upsampled signal all +// frequencies over fs/2 then reconstruct the analog signal you get a +// result identical to the original signal. It's a perfect +// upsampling, assuming the filtering is perfect. + + +// Down-sampling theorem. Take a digital signal at frequency ft*k, +// with k and integer > 1. Create a new digital signal at frequency +// ft by alternatively taking one sample from the original signal and +// dropping k-1 samples. If the original signal had no frequency over +// ft/2, then the reconstructed analog signal is identical to the +// original one, up to a constant multiplier on the amplitude. So it +// is a perfect downsampling assuming the original signal has nothing +// over ft/2. For the curious if there are frequencies over ft/2, +// they end up added to the lower frequencies with inversions. The +// frequency ft/2+a is added to ft/2-a, etc (signal to upsampling, +// only the other way around). + +// The corollary there is that if one starts with a ft*k digital +// signal, filters out everything over ft/2, then keeps only one +// sample every k, then reconstruct the analog signal, you get the +// original analog signal with frequencies over ft/2 removed, which is +// reasonable given they are not representable at sampling frequency +// ft anyway. As such it is called perfect because it's the best +// possible result in any case. + +// Incidentally, the parasite audible frequencies added with the +// wrapping when the original is insufficiently filtered before +// dropping the samples are called aliasing, as in the high barely +// audible frequencies that was there but not noticed gets aliased to +// a very audible and annoying lower frequency. + + +// As a result, the recipe to go from frequency fs to ft for a digital +// signal is: + +// - find a frequency fm = ks*fs = kt*ft with ks and kt integers. +// When fs and ft are integers (our case), the easy solution is +// fm = fs * ft / gcd(fs, ft) + +// - up-sample the original signal x(t) into xm(t) with: +// xm(ks*t) = x(t) +// xm(other) = 0 + +// - filter the resulting fm Hz signal to remove all frequencies above +// fs/2. This is also called "lowpass at fs/2" + +// - lowpass at ft/2 + +// - down-sample the fm signal into the resulting y(t) signal by: +// y(t) = xm(kt*t) + +// And, assuming the filtering is perfect (it isn't, of course), the +// result is a perfect resampling. + +// Now to optimize all that. The first point is that an ideal lowpass +// at fs/2 followed by an ideal lowpass at ft/2 is strictly equivalent +// to an ideal lowpass at min(fs/2, ft/2). So only one filter is +// needed. + +// The second point depends on the type of filter used. In our case +// the filter type known as FIR has a big advantage. A FIR filter +// computes the output signal as a finite ponderated sum on the values +// of the input signal only (also called a convolution). E.g. +// y(t) = sum(k=0, n-1) a[k] * x[t-k] +// where a[0..n-1] are constants called the coefficients of the filter. + +// Why this type of filter is pertinent shows up when building the +// complete computation: + +// y(t) = filter(xm)[kt*t] +// = sum(k=0, n-1) a[k] * xm[kt*t - k] +// = sum(k=0, n-1) a[k] * | x[(kt*t-k)/ks] when kt*t-k is divisible by ks +// | 0 otherwise +// = sum(k=(kt*t) mod ks, n-1, step=ks) a[k] * x[(kt*t-k)/ks] + +// (noting p = (kt*t) mode ks, and a // b integer divide of a by b) +// = sum(k=0, (n-1 - p))//ks) a[k*ks + p] x[(kt*t) // ks) - k] + +// Splitting the filter coefficients in ks phases ap[0..ks-1] where +// ap[p][k] = a[p + ks*k], and noting t0 = (k*kt) // ks: + +// y(t) = sum(k=0, len(ap[p])-1) ap[p][k] * x[t0-k] + +// So we can take a big FIR filter and split it into ks interpolation +// filters and just apply the correct one at each sample. We can make +// things even easier by ensuring that the size of every interpolation +// filter is the same. + +// The art of creating the big FIR filter so that it doesn't change +// the signal too much is complicated enough that entire books have +// been written on the topic. We use here a simple solution which is +// to use a so-called zero-phase filter, which is a symmetrical filter +// which looks into the future to filter out the frequencies without +// changing the phases, and shift it in the past by half its length, +// making it causal (e.g. not looking into the future anymore). It is +// then called linear-phase, and has a latency of exactly half its +// length. The filter itself is made very traditionally, by +// multiplying a sinc by a Hann window. + +// The filter size is selected by maximizing the latency to 5ms and +// capping the length at 400, which experimentally seems to ensure a +// sharp rejection of more than 100dB in every case. + +// Finally, remember that up and downsampling steps multiply the +// amplitude by a constant (upsampling divides by k, downsamply +// multiply by k in fact). To compensate for that and numerical +// errors the easiest way to to normalize each phase-filter +// independently to ensure the sum of their coefficients is 1. It is +// easy to see why it works: a constant input signal must be +// transformed into a constant output signal at the exact same level. +// Having the sum of coefficients being 1 ensures that. + + +audio_resampler_hq::audio_resampler_hq(u32 fs, u32 ft, float latency, u32 max_order_per_lane, u32 max_lanes) +{ + m_ft = ft; + m_fs = fs; + + // Compute the multiplier for fs and ft to reach the common frequency + u32 gcd = compute_gcd(fs, ft); + m_ftm = fs / gcd; + m_fsm = ft / gcd; + + // Compute the per-phase filter length to limit the latency to 5ms and capping it + m_order_per_lane = u32(fs * latency * 2); + if(m_order_per_lane > max_order_per_lane) + m_order_per_lane = max_order_per_lane; + + // Reduce the number of phases to be less than max_lanes + m_phase_shift = 0; + while(((m_fsm - 1) >> m_phase_shift) >= max_lanes) + m_phase_shift ++; + + m_phases = ((m_fsm - 1) >> m_phase_shift) + 1; + + // Compute the global filter length + u32 filter_length = m_order_per_lane * m_phases; + if((filter_length & 1) == 0) + filter_length --; + u32 hlen = filter_length / 2; + + // Prepare the per-phase filters + m_coefficients.resize(m_phases); + for(u32 i = 0; i != m_phases; i++) + m_coefficients[i].resize(m_order_per_lane, 0.0); + + // Select the filter cutoff. Keep it in audible range. + double cutoff = std::min(fs/2.0, ft/2.0); + if(cutoff > 20000) + cutoff = 20000; + + // Compute the filter and send the coefficients to the appropriate phase + auto set_filter = [this](u32 i, float v) { m_coefficients[i % m_phases][i / m_phases] = v; }; + + double wc = 2 * M_PI * cutoff / (double(fs) * m_fsm / (1 << m_phase_shift)); + double a = wc / M_PI; + for(u32 i = 1; i != hlen; i++) { + double win = cos(i*M_PI/hlen/2); + win = win*win; + double s = a * sin(i*wc)/(i*wc) * win; + + set_filter(hlen-1+i, s); + set_filter(hlen-1-i, s); + } + set_filter(hlen-1, a); + + // Normalize the per-phase filters + for(u32 i = 0; i != m_phases; i++) { + float s = 0; + for(u32 j = 0; j != m_order_per_lane; j++) + s += m_coefficients[i][j]; + s = 1/s; + for(u32 j = 0; j != m_order_per_lane; j++) + m_coefficients[i][j] *= s; + } + + // Compute the phase shift from one sample to the next + m_delta = m_ftm % m_fsm; + m_skip = m_ftm / m_fsm; +} + +u32 audio_resampler_hq::compute_gcd(u32 fs, u32 ft) +{ + u32 v1 = fs > ft ? fs : ft; + u32 v2 = fs > ft ? ft : fs; + while(v2) { + u32 v3 = v1 % v2; + v1 = v2; + v2 = v3; + } + return v1; +} + +u32 audio_resampler_hq::history_size() const +{ + return m_order_per_lane; +} + +void audio_resampler_hq::apply(const emu::detail::output_buffer_flat<sample_t> &src, std::vector<sample_t> &dest, u64 dest_sample, u32 srcc, float gain, u32 samples) const +{ + u32 seconds = dest_sample / m_ft; + u32 dsamp = dest_sample % m_ft; + u32 ssamp = (u64(dsamp) * m_fs) / m_ft; + u64 ssample = ssamp + u64(m_fs) * seconds; + u32 phase = (dsamp * m_ftm) % m_fsm; + + const sample_t *s = src.ptrs(srcc, ssample - src.sync_sample()); + sample_t *d = dest.data(); + for(u32 sample = 0; sample != samples; sample++) { + sample_t acc = 0; + const sample_t *s1 = s; + const float *filter = m_coefficients[phase >> m_phase_shift].data(); + for(u32 k = 0; k != m_order_per_lane; k++) + acc += *filter++ * *s1--; + *d++ += acc * gain; + phase += m_delta; + s += m_skip; + while(phase >= m_fsm) { + phase -= m_fsm; + s ++; + } + } +} + +void audio_resampler_hq::apply(const emu::detail::output_buffer_interleaved<s16> &src, std::vector<sample_t> &dest, u64 dest_sample, u32 srcc, float gain, u32 samples) const +{ + u32 seconds = dest_sample / m_ft; + u32 dsamp = dest_sample % m_ft; + u32 ssamp = (u64(dsamp) * m_fs) / m_ft; + u64 ssample = ssamp + u64(m_fs) * seconds; + u32 phase = (dsamp * m_ftm) % m_fsm; + + gain /= 32768; + + const s16 *s = src.ptrs(srcc, ssample - src.sync_sample()); + sample_t *d = dest.data(); + int step = src.channels(); + for(u32 sample = 0; sample != samples; sample++) { + sample_t acc = 0; + const s16 *s1 = s; + const float *filter = m_coefficients[phase >> m_phase_shift].data(); + for(u32 k = 0; k != m_order_per_lane; k++) { + acc += *filter++ * *s1; + s1 -= step; + } + *d++ += acc * gain; + phase += m_delta; + s += m_skip * step; + while(phase >= m_fsm) { + phase -= m_fsm; + s += step; + } + } +} + + +void audio_resampler_hq::apply(const emu::detail::output_buffer_flat<sample_t> &src, std::vector<s16> &dest, u32 destc, int dchannels, u64 dest_sample, u32 srcc, float gain, u32 samples) const +{ + u32 seconds = dest_sample / m_ft; + u32 dsamp = dest_sample % m_ft; + u32 ssamp = (u64(dsamp) * m_fs) / m_ft; + u64 ssample = ssamp + u64(m_fs) * seconds; + u32 phase = (dsamp * m_ftm) % m_fsm; + + gain *= 32768; + + const sample_t *s = src.ptrs(srcc, ssample - src.sync_sample()); + s16 *d = dest.data() + destc; + for(u32 sample = 0; sample != samples; sample++) { + sample_t acc = 0; + const sample_t *s1 = s; + const float *filter = m_coefficients[phase >> m_phase_shift].data(); + for(u32 k = 0; k != m_order_per_lane; k++) + acc += *filter++ * *s1--; + *d += acc * gain; + d += dchannels; + phase += m_delta; + s += m_skip; + while(phase >= m_fsm) { + phase -= m_fsm; + s ++; + } + } +} + + +// Now for the lo-fi version +// +// We mostly forget about filtering, and just try to do a decent +// interpolation. There's a nice 4-point formula used in yamaha +// devices from around 2000: +// f0(t) = (t - t**3)/6 +// f1(t) = t + (t**2 - t**3)/2 +// +// The polynoms are used with the decimal part 'p' (as in phase) of +// the sample position. The computation from the four samples s0..s3 +// is: +// s = - s0 * f0(1-p) + s1 * f1(1-p) + s2 * f1(p) - s3 * f0(p) +// +// The target sample must be between s1 and s2. +// +// When upsampling, that's enough. When downsampling, it feels like a +// good idea to filter a little with a moving average, dividing the +// source frequency by an integer just big enough to make the final +// source frequency lower. + +// Sample interpolation functions f0 and f1 + +const std::array<std::array<float, 0x1001>, 2> audio_resampler_lofi::interpolation_table = []() { + std::array<std::array<float, 0x1001>, 2> result; + + // The exact way of doing the computations replicate the values + // actually used by the chip (which are very probably a rom, of + // course). + + for(u32 i=1; i != 4096; i++) { + float p = i / 4096.0; + result[0][i] = (p - p*p*p) / 6; + } + for(u32 i=1; i != 2049; i++) { + float p = i / 4096.0; + result[1][i] = p + (p*p - p*p*p) / 2; + } + for(u32 i=2049; i != 4096; i++) + // When interpolating, f1 is added and f0 is subtracted, and the total must be 1 + result[1][i] = 1.0 + result[0][i] + result[0][4096-i] - result[1][4096-i]; + + result[0][ 0] = 0.0; + result[0][0x1000] = 0.0; + result[1][ 0] = 0.0; + result[1][0x1000] = 1.0; + return result; +}(); + +audio_resampler_lofi::audio_resampler_lofi(u32 fs, u32 ft) +{ + m_fs = fs; + m_ft = ft; + + m_source_divide = fs <= ft ? 1 : 1+fs/ft; + m_step = u64(fs) * 0x1000 / ft / m_source_divide; +} + + +u32 audio_resampler_lofi::history_size() const +{ + return 5 * m_source_divide; +} + +void audio_resampler_lofi::apply(const emu::detail::output_buffer_flat<sample_t> &src, std::vector<sample_t> &dest, u64 dest_sample, u32 srcc, float gain, u32 samples) const +{ + u32 seconds = dest_sample / m_ft; + u32 dsamp = dest_sample % m_ft; + u64 ssamp = (u64(dsamp) * m_fs * 0x1000) / m_ft; + u64 ssample = (ssamp >> 12) + u64(m_fs) * seconds; + u32 phase = ssamp & 0xfff; + if(m_source_divide > 1) { + u32 delta = ssample % m_source_divide; + phase = (phase | (delta << 12)) / m_source_divide; + ssample -= delta; + } + + // We're getting 2 samples latency, which is small enough + + ssample -= 4*m_source_divide; + + const sample_t *s = src.ptrs(srcc, ssample - src.sync_sample()); + + std::function<sample_t()> reader; + if(m_source_divide == 1) + reader = [s]() mutable -> sample_t { return *s++; }; + else + reader = [s, count = m_source_divide]() mutable -> sample_t { sample_t sm = 0; for(u32 i=0; i != count; i++) { sm += *s++; } return sm / count; }; + + sample_t s0 = reader(); + sample_t s1 = reader(); + sample_t s2 = reader(); + sample_t s3 = reader(); + + sample_t *d = dest.data(); + for(u32 sample = 0; sample != samples; sample++) { + *d++ = gain * (- s0 * interpolation_table[0][0x1000-phase] + s1 * interpolation_table[1][0x1000-phase] + s2 * interpolation_table[1][phase] - s3 * interpolation_table[0][phase]); + + phase += m_step; + if(phase & 0x1000) { + phase &= 0xfff; + s0 = s1; + s1 = s2; + s2 = s3; + s3 = reader(); + } + } +} + +void audio_resampler_lofi::apply(const emu::detail::output_buffer_interleaved<s16> &src, std::vector<sample_t> &dest, u64 dest_sample, u32 srcc, float gain, u32 samples) const +{ +} + +void audio_resampler_lofi::apply(const emu::detail::output_buffer_flat<sample_t> &src, std::vector<s16> &dest, u32 destc, int dchannels, u64 dest_sample, u32 srcc, float gain, u32 samples) const +{ +} diff --git a/src/emu/resampler.h b/src/emu/resampler.h new file mode 100644 index 00000000000..d0758b5ae7e --- /dev/null +++ b/src/emu/resampler.h @@ -0,0 +1,66 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert + +// Audio resampler + +#pragma once + +#ifndef MAME_EMU_RESAMPLER_H +#define MAME_EMU_RESAMPLER_H + +#include "sound.h" + +class audio_resampler +{ +public: + using sample_t = sound_stream::sample_t; + + virtual ~audio_resampler() = default; + + virtual u32 history_size() const = 0; + + virtual void apply(const emu::detail::output_buffer_flat<sample_t> &src, std::vector<sample_t> &dest, u64 dest_sample, u32 srcc, float gain, u32 samples) const = 0; + virtual void apply(const emu::detail::output_buffer_interleaved<s16> &src, std::vector<sample_t> &dest, u64 dest_sample, u32 srcc, float gain, u32 samples) const = 0; + virtual void apply(const emu::detail::output_buffer_flat<sample_t> &src, std::vector<s16> &dest, u32 destc, int dchannels, u64 dest_sample, u32 srcc, float gain, u32 samples) const = 0; +}; + +class audio_resampler_hq : public audio_resampler +{ +public: + audio_resampler_hq(u32 fs, u32 ft, float latency, u32 max_order_per_lane, u32 max_lanes); + virtual ~audio_resampler_hq() = default; + + virtual u32 history_size() const override; + + virtual void apply(const emu::detail::output_buffer_flat<sample_t> &src, std::vector<sample_t> &dest, u64 dest_sample, u32 srcc, float gain, u32 samples) const override; + virtual void apply(const emu::detail::output_buffer_interleaved<s16> &src, std::vector<sample_t> &dest, u64 dest_sample, u32 srcc, float gain, u32 samples) const override; + virtual void apply(const emu::detail::output_buffer_flat<sample_t> &src, std::vector<s16> &dest, u32 destc, int dchannels, u64 dest_sample, u32 srcc, float gain, u32 samples) const override; + +private: + u32 m_order_per_lane, m_ftm, m_fsm, m_ft, m_fs, m_delta, m_skip, m_phases, m_phase_shift; + + std::vector<std::vector<float>> m_coefficients; + + static u32 compute_gcd(u32 fs, u32 ft); +}; + +class audio_resampler_lofi : public audio_resampler +{ +public: + audio_resampler_lofi(u32 fs, u32 ft); + virtual ~audio_resampler_lofi() = default; + + virtual u32 history_size() const override; + + virtual void apply(const emu::detail::output_buffer_flat<sample_t> &src, std::vector<sample_t> &dest, u64 dest_sample, u32 srcc, float gain, u32 samples) const override; + virtual void apply(const emu::detail::output_buffer_interleaved<s16> &src, std::vector<sample_t> &dest, u64 dest_sample, u32 srcc, float gain, u32 samples) const override; + virtual void apply(const emu::detail::output_buffer_flat<sample_t> &src, std::vector<s16> &dest, u32 destc, int dchannels, u64 dest_sample, u32 srcc, float gain, u32 samples) const override; + +private: + static const std::array<std::array<float, 0x1001>, 2> interpolation_table; + u32 m_source_divide, m_fs, m_ft; + u32 m_step; +}; + +#endif + diff --git a/src/emu/romentry.h b/src/emu/romentry.h index ff73e432c65..2a315c2a03e 100644 --- a/src/emu/romentry.h +++ b/src/emu/romentry.h @@ -29,18 +29,17 @@ CONSTANTS #define ROMENTRY_TYPEMASK 0x0000000f /* type of entry */ enum { - ROMENTRYTYPE_ROM = 0, /* this entry is an actual ROM definition */ - ROMENTRYTYPE_REGION, /* this entry marks the start of a region */ - ROMENTRYTYPE_END, /* this entry marks the end of a region */ - ROMENTRYTYPE_RELOAD, /* this entry reloads the previous ROM */ - ROMENTRYTYPE_CONTINUE, /* this entry continues loading the previous ROM */ - ROMENTRYTYPE_FILL, /* this entry fills an area with a constant value */ - ROMENTRYTYPE_COPY, /* this entry copies data from another region/offset */ - ROMENTRYTYPE_CARTRIDGE, /* this entry specifies a cartridge (MESS) */ - ROMENTRYTYPE_IGNORE, /* this entry continues loading the previous ROM but throws the data away */ - ROMENTRYTYPE_SYSTEM_BIOS, /* this entry specifies a bios */ - ROMENTRYTYPE_DEFAULT_BIOS, /* this entry specifies a default bios */ - ROMENTRYTYPE_PARAMETER, /* this entry specifies a per-game parameter */ + ROMENTRYTYPE_ROM = 0, // starts loading a ROM file + ROMENTRYTYPE_REGION, // starts a new ROM region + ROMENTRYTYPE_END, // sentinel marking the end of a ROM definition + ROMENTRYTYPE_RELOAD, // starts loading the current ROM file from the beginning again + ROMENTRYTYPE_CONTINUE, // continues loading the current ROM file at a different offset + ROMENTRYTYPE_FILL, // fills an area with a constant value + ROMENTRYTYPE_COPY, // copies data from another region/offset + ROMENTRYTYPE_IGNORE, // ignores a portion of the current ROM file + ROMENTRYTYPE_SYSTEM_BIOS, // specifies a BIOS option + ROMENTRYTYPE_DEFAULT_BIOS, // specifies the default BIOS option + ROMENTRYTYPE_PARAMETER, // specifies a per-game parameter ROMENTRYTYPE_COUNT }; diff --git a/src/emu/romload.cpp b/src/emu/romload.cpp index c5b03607509..33541aedce4 100644 --- a/src/emu/romload.cpp +++ b/src/emu/romload.cpp @@ -1,20 +1,31 @@ // license:BSD-3-Clause -// copyright-holders:Nicola Salmoria,Paul Priest,Aaron Giles +// copyright-holders:Nicola Salmoria,Paul Priest,Aaron Giles,Vas Crabb /********************************************************************* romload.cpp ROM loading functions. + *********************************************************************/ #include "emu.h" #include "romload.h" -#include "emuopts.h" #include "drivenum.h" +#include "emuopts.h" +#include "fileio.h" +#include "main.h" #include "softlist_dev.h" + #include "ui/uimain.h" +#include "corestr.h" +#include "path.h" + +#include <algorithm> +#include <cstdarg> +#include <set> + #define LOG_LOAD 0 #define LOG(...) do { if (LOG_LOAD) debugload(__VA_ARGS__); } while(0) @@ -27,32 +38,245 @@ #define TEMPBUFFER_MAX_SIZE (1024 * 1024 * 1024) /*************************************************************************** - HELPERS (also used by diimage.cpp) - ***************************************************************************/ + HELPERS +****************************************************************************/ + +namespace { + +/*------------------------------------------------- + debugload - log data to a file +-------------------------------------------------*/ + +void ATTR_PRINTF(1,2) debugload(const char *string, ...) +{ + static int opened = 0; + FILE *const f(fopen("romload.log", opened++ ? "a" : "w")); + if (f) + { + va_list arg; + va_start(arg, string); + vfprintf(f, string, arg); + va_end(arg); + fclose(f); + } +} -static osd_file::error common_process_file(emu_options &options, const char *location, const char *ext, const rom_entry *romp, emu_file &image_file) + +auto next_parent_system(game_driver const &system) { - return (location && *location) - ? image_file.open(location, PATH_SEPARATOR, ROM_GETNAME(romp), ext) - : image_file.open(ROM_GETNAME(romp), ext); + return + [sys = &system, roms = std::vector<rom_entry>()] () mutable -> rom_entry const * + { + if (!sys) + return nullptr; + int const parent(driver_list::find(sys->parent)); + if (0 > parent) + { + sys = nullptr; + roms.clear(); + roms.shrink_to_fit(); + return nullptr; + } + else + { + sys = &driver_list::driver(parent); + roms = rom_build_entries(sys->rom); + return &roms.front(); + } + }; } -std::unique_ptr<emu_file> common_process_file(emu_options &options, const char *location, bool has_crc, u32 crc, const rom_entry *romp, osd_file::error &filerr) + +auto next_parent_device(device_t const &device, emu_options &options) { - auto image_file = std::make_unique<emu_file>(options.media_path(), OPEN_FLAG_READ); + return + [&options, type = &device.type(), roms = std::vector<rom_entry>()] () mutable -> rom_entry const * + { + if (!type) + return nullptr; + type = type->parent_rom_device_type(); + if (!type) + { + roms.clear(); + roms.shrink_to_fit(); + return nullptr; + } + else + { + machine_config config(GAME_NAME(___empty), options); + machine_config::token const tok(config.begin_configuration(config.root_device())); + roms = config.device_add("_tmp", *type, 0)->rom_region_vector(); + config.device_remove("_tmp"); + return &roms.front(); + } + }; +} - if (has_crc) - filerr = image_file->open(location, PATH_SEPARATOR, ROM_GETNAME(romp), crc); - else - filerr = image_file->open(location, PATH_SEPARATOR, ROM_GETNAME(romp)); - if (filerr != osd_file::error::NONE) +auto next_parent_software(std::vector<software_info const *> const &parents) +{ + auto part(parents.front()->parts().end()); + return + [&parents, current = parents.cbegin(), part, end = part] () mutable -> const rom_entry * + { + if (part == end) + { + if (parents.end() == current) + return nullptr; + part = (*current)->parts().cbegin(); + end = (*current)->parts().cend(); + do { ++current; } while ((parents.cend() != current) && (*current)->parts().empty()); + } + return &(*part++).romdata()[0]; + }; +} + + +std::vector<std::string> make_software_searchpath(software_list_device &swlist, software_info const &swinfo, std::vector<software_info const *> &parents) +{ + std::vector<std::string> result; + + // search <rompath>/<list>/<software> following parents + for (software_info const *i = &swinfo; i; ) { - image_file = nullptr; + if (std::find(parents.begin(), parents.end(), i) != parents.end()) + break; + parents.emplace_back(i); + result.emplace_back(util::path_concat(swlist.list_name(), i->shortname())); + i = i->parentname().empty() ? nullptr : swlist.find(i->parentname()); } - return image_file; + + // search <rompath>/<software> following parents + for (software_info const *i : parents) + result.emplace_back(i->shortname()); + + return result; } + +std::error_condition do_open_disk( + emu_options const &options, + std::initializer_list<std::reference_wrapper<std::vector<std::string> const> > searchpath, + rom_entry const *romp, + chd_file &chd, + std::function<rom_entry const * ()> next_parent, + chd_file::open_parent_func const &open_parent) +{ + // hashes are fixed, but we might need to try multiple filenames + std::set<std::string> tried; + util::hash_collection const hashes(romp->hashdata()); + std::string filename, fullpath; + rom_entry const *parent(nullptr); + std::error_condition result(std::errc::no_such_file_or_directory); + while (romp && result) + { + filename = romp->name() + ".chd"; + if (tried.insert(filename).second) + { + // piggyback on emu_file to find the disk image file + std::unique_ptr<emu_file> imgfile; + for (std::vector<std::string> const &paths : searchpath) + { + imgfile.reset(new emu_file(options.media_path(), paths, OPEN_FLAG_READ)); + imgfile->set_restrict_to_mediapath(1); + std::error_condition const filerr(imgfile->open(filename, OPEN_FLAG_READ)); + if (!filerr) + break; + else + imgfile.reset(); + } + + // if we couldn't open a candidate file, report an error; otherwise reopen it as a CHD + if (imgfile) + { + fullpath = imgfile->fullpath(); + imgfile.reset(); + result = chd.open(fullpath, false, nullptr, open_parent); + } + } + + // walk the parents looking for a CHD with the same hashes but a different name + if (result) + { + while (romp) + { + // find a file in a disk region + if (parent) + romp = rom_next_file(romp); + while (!parent || !romp) + { + if (!parent) + { + parent = next_parent(); + if (!parent) + { + romp = nullptr; + break; + } + parent = rom_first_region(parent); + } + else + { + parent = rom_next_region(parent); + } + while (parent && !ROMREGION_ISDISKDATA(parent)) + parent = rom_next_region(parent); + if (parent) + romp = rom_first_file(parent); + } + + // try it if it matches the hashes + if (romp && (util::hash_collection(romp->hashdata()) == hashes)) + break; + } + } + } + return result; +} + + +auto open_parent_disk( + emu_options const &options, + std::initializer_list<std::reference_wrapper<std::vector<std::string> const> > searchpath, + std::function<rom_entry const * ()> const &next_parent) +{ + return + [&options, searchpath, next_parent] (util::sha1_t const &sha1) -> std::unique_ptr<chd_file> + { + util::hash_collection hashes; + hashes.add_sha1(sha1); + std::function<rom_entry const * ()> np(next_parent); // important - copy mutable lambda + for (rom_entry const *parent = np(); parent; parent = np()) + { + parent = rom_first_region(parent); + while (parent) + { + while (parent && !ROMREGION_ISDISKDATA(parent)) + parent = rom_next_region(parent); + if (parent) + { + rom_entry const *romp(rom_first_file(parent)); + while (romp) + { + if (util::hash_collection(romp->hashdata()) == hashes) + { + std::unique_ptr<chd_file> chd(new chd_file); + if (!do_open_disk(options, searchpath, romp, *chd, next_parent, nullptr)) + return chd; + } + romp = rom_next_file(romp); + } + parent = rom_next_region(parent); + } + } + } + return nullptr; + }; +} + +} // anonymous namespace + + /*************************************************************************** ROM LOADING ***************************************************************************/ @@ -64,7 +288,11 @@ std::unique_ptr<emu_file> common_process_file(emu_options &options, const char * const rom_entry *rom_first_region(const device_t &device) { - const rom_entry *romp = &device.rom_region_vector().front(); + return rom_first_region(&device.rom_region_vector().front()); +} + +const rom_entry *rom_first_region(const rom_entry *romp) +{ while (ROMENTRY_ISPARAMETER(romp) || ROMENTRY_ISSYSTEM_BIOS(romp) || ROMENTRY_ISDEFAULT_BIOS(romp)) romp++; return !ROMENTRY_ISEND(romp) ? romp : nullptr; @@ -125,7 +353,7 @@ const rom_entry *rom_first_parameter(const device_t &device) const rom_entry *romp = &device.rom_region_vector().front(); while (romp && !ROMENTRY_ISEND(romp) && !ROMENTRY_ISPARAMETER(romp)) romp++; - return (romp != nullptr && !ROMENTRY_ISEND(romp)) ? romp : nullptr; + return (romp && !ROMENTRY_ISEND(romp)) ? romp : nullptr; } @@ -144,56 +372,23 @@ const rom_entry *rom_next_parameter(const rom_entry *romp) /*------------------------------------------------- - rom_region_name - return the appropriate name - for a rom region --------------------------------------------------*/ - -std::string rom_region_name(const device_t &device, const rom_entry *romp) -{ - return device.subtag(ROM_GETNAME(romp)); -} - - -/*------------------------------------------------- - rom_parameter_name - return the appropriate name - for a per-game parameter --------------------------------------------------*/ - -std::string rom_parameter_name(const device_t &device, const rom_entry *romp) -{ - return device.subtag(romp->name().c_str()); -} - - -/*------------------------------------------------- - rom_parameter_name - return the value for a - per-game parameter --------------------------------------------------*/ - -std::string rom_parameter_value(const rom_entry *romp) -{ - return romp->hashdata(); -} - - -/*------------------------------------------------- rom_file_size - return the expected size of a file given the ROM description -------------------------------------------------*/ u32 rom_file_size(const rom_entry *romp) { - u32 maxlength = 0; + u32 maxlength(0); - /* loop until we run out of reloads */ + // loop until we run out of reloads do { - /* loop until we run out of continues/ignores */ + // loop until we run out of continues/ignores u32 curlength = ROM_GETLENGTH(romp++); while (ROMENTRY_ISCONTINUE(romp) || ROMENTRY_ISIGNORE(romp)) curlength += ROM_GETLENGTH(romp++); - /* track the maximum length */ + // track the maximum length maxlength = std::max(maxlength, curlength); } while (ROMENTRY_ISRELOAD(romp)); @@ -202,27 +397,6 @@ u32 rom_file_size(const rom_entry *romp) } -/*------------------------------------------------- - debugload - log data to a file --------------------------------------------------*/ - -static void CLIB_DECL ATTR_PRINTF(1,2) debugload(const char *string, ...) -{ - static int opened; - va_list arg; - FILE *f; - - f = fopen("romload.log", opened++ ? "a" : "w"); - if (f) - { - va_start(arg, string); - vfprintf(f, string, arg); - va_end(arg); - fclose(f); - } -} - - /*************************************************************************** HARD DISK HANDLING ***************************************************************************/ @@ -232,11 +406,13 @@ static void CLIB_DECL ATTR_PRINTF(1,2) debugload(const char *string, ...) CHD file associated with the given region -------------------------------------------------*/ -chd_file *rom_load_manager::get_disk_handle(const char *region) +chd_file *rom_load_manager::get_disk_handle(std::string_view region) { for (auto &curdisk : m_chd_list) - if (strcmp(curdisk->region(), region) == 0) + { + if (curdisk->region() == region) return &curdisk->chd(); + } return nullptr; } @@ -246,37 +422,36 @@ chd_file *rom_load_manager::get_disk_handle(const char *region) file associated with the given region -------------------------------------------------*/ -int rom_load_manager::set_disk_handle(const char *region, const char *fullpath) +std::error_condition rom_load_manager::set_disk_handle(std::string_view region, std::string_view fullpath) { - auto chd = std::make_unique<open_chd>(region); - auto err = chd->orig_chd().open(fullpath); - if (err == CHDERR_NONE) - m_chd_list.push_back(std::move(chd)); + auto chd(std::make_unique<open_chd>(region)); + std::error_condition const err(chd->orig_chd().open(fullpath)); + if (!err) + m_chd_list.emplace_back(std::move(chd)); return err; } /*------------------------------------------------- determine_bios_rom - determine system_bios - from SystemBios structure and OPTION_BIOS + from SYSTEM_BIOS structure and OPTION_BIOS -------------------------------------------------*/ -void rom_load_manager::determine_bios_rom(device_t &device, const char *specbios) +void rom_load_manager::determine_bios_rom(device_t &device, std::string_view specbios) { // default is applied by the device at config complete time - if (specbios && *specbios && core_stricmp(specbios, "default")) + using namespace std::literals; + if (!specbios.empty() && !util::streqlower(specbios, "default"sv)) { bool found(false); - for (const rom_entry &rom : device.rom_region_vector()) + for (rom_entry const &rom : device.rom_region_vector()) { if (ROMENTRY_ISSYSTEM_BIOS(&rom)) { char const *const biosname = ROM_GETNAME(&rom); int const bios_flags = ROM_GETBIOSFLAGS(&rom); - char bios_number[20]; // Allow '-bios n' to still be used - sprintf(bios_number, "%d", bios_flags - 1); - if (!core_stricmp(bios_number, specbios) || !core_stricmp(biosname, specbios)) + if ((specbios == std::to_string(bios_flags - 1)) || util::streqlower(specbios, biosname)) { found = true; device.set_system_bios(bios_flags); @@ -287,7 +462,10 @@ void rom_load_manager::determine_bios_rom(device_t &device, const char *specbios // if we got neither an empty string nor 'default' then warn the user if (!found) + { m_errorstring.append(util::string_format("%s: invalid BIOS \"%s\", reverting to default\n", device.tag(), specbios)); + m_warnings++; + } } // log final result @@ -302,21 +480,29 @@ void rom_load_manager::determine_bios_rom(device_t &device, const char *specbios void rom_load_manager::count_roms() { - const rom_entry *region, *rom; - - /* start with 0 */ + // start with 0 m_romstotal = 0; m_romstotalsize = 0; - /* loop over regions, then over files */ - for (device_t &device : device_iterator(machine().config().root_device())) - for (region = rom_first_region(device); region != nullptr; region = rom_next_region(region)) - for (rom = rom_first_file(region); rom != nullptr; rom = rom_next_file(rom)) - if (ROM_GETBIOSFLAGS(rom) == 0 || ROM_GETBIOSFLAGS(rom) == device.system_bios()) + // loop over regions, and files within regions + for (device_t &device : device_enumerator(machine().config().root_device())) + { + rom_entry const *region(rom_first_region(device)); + while (region) + { + rom_entry const *rom(rom_first_file(region)); + while (rom) + { + if ((ROM_GETBIOSFLAGS(rom) == 0) || (ROM_GETBIOSFLAGS(rom) == device.system_bios())) { m_romstotal++; m_romstotalsize += rom_file_size(rom); } + rom = rom_next_file(rom); + } + region = rom_next_region(region); + } + } } @@ -337,42 +523,46 @@ void rom_load_manager::fill_random(u8 *base, u32 length) for missing files -------------------------------------------------*/ -void rom_load_manager::handle_missing_file(const rom_entry *romp, std::string tried_file_names, chd_error chderr) +void rom_load_manager::handle_missing_file(const rom_entry *romp, const std::vector<std::string> &tried_file_names, std::error_condition chderr) { - if(tried_file_names.length() != 0) - tried_file_names = " (tried in " + tried_file_names + ")"; - - std::string name(ROM_GETNAME(romp)); + std::string tried; + if (!tried_file_names.empty()) + { + tried = " (tried in"; + for (const std::string &path : tried_file_names) + { + tried += ' '; + tried += path; + } + tried += ')'; + } - bool is_chd = (chderr != CHDERR_NONE); - if (is_chd) - name += ".chd"; + const bool is_chd(chderr); + const std::string name(is_chd ? romp->name() + ".chd" : romp->name()); - bool is_chd_error = (is_chd && chderr != CHDERR_FILE_NOT_FOUND); + const bool is_chd_error(is_chd && (chderr != std::errc::no_such_file_or_directory)); if (is_chd_error) - m_errorstring.append(string_format("%s CHD ERROR: %s\n", name.c_str(), chd_file::error_string(chderr))); + m_errorstring.append(string_format("%s CHD ERROR: %s\n", name, chderr.message())); - /* optional files are okay */ if (ROM_ISOPTIONAL(romp)) { + // optional files are okay if (!is_chd_error) - m_errorstring.append(string_format("OPTIONAL %s NOT FOUND%s\n", name.c_str(), tried_file_names.c_str())); + m_errorstring.append(string_format("OPTIONAL %s NOT FOUND%s\n", name, tried)); m_warnings++; } - - /* no good dumps are okay */ - else if (util::hash_collection(ROM_GETHASHDATA(romp)).flag(util::hash_collection::FLAG_NO_DUMP)) + else if (util::hash_collection(romp->hashdata()).flag(util::hash_collection::FLAG_NO_DUMP)) { + // no good dumps are okay if (!is_chd_error) - m_errorstring.append(string_format("%s NOT FOUND (NO GOOD DUMP KNOWN)%s\n", name, tried_file_names)); + m_errorstring.append(string_format("%s NOT FOUND (NO GOOD DUMP KNOWN)%s\n", name, tried)); m_knownbad++; } - - /* anything else is bad */ else { + // anything else is bad if (!is_chd_error) - m_errorstring.append(string_format("%s NOT FOUND%s\n", name.c_str(), tried_file_names)); + m_errorstring.append(string_format("%s NOT FOUND%s\n", name, tried)); m_errors++; } } @@ -386,8 +576,8 @@ void rom_load_manager::handle_missing_file(const rom_entry *romp, std::string tr void rom_load_manager::dump_wrong_and_correct_checksums(const util::hash_collection &hashes, const util::hash_collection &acthashes) { - m_errorstring.append(string_format(" EXPECTED: %s\n", hashes.macro_string().c_str())); - m_errorstring.append(string_format(" FOUND: %s\n", acthashes.macro_string().c_str())); + m_errorstring.append(string_format(" EXPECTED: %s\n", hashes.macro_string())); + m_errorstring.append(string_format(" FOUND: %s\n", acthashes.macro_string())); } @@ -396,43 +586,46 @@ void rom_load_manager::dump_wrong_and_correct_checksums(const util::hash_collect and hash signatures of a file -------------------------------------------------*/ -void rom_load_manager::verify_length_and_hash(const char *name, u32 explength, const util::hash_collection &hashes) +void rom_load_manager::verify_length_and_hash(emu_file *file, std::string_view name, u32 explength, const util::hash_collection &hashes) { - /* we've already complained if there is no file */ - if (m_file == nullptr) + // we've already complained if there is no file + if (!file) return; - /* verify length */ - u32 actlength = m_file->size(); + // verify length + u64 const actlength(file->size()); if (explength != actlength) { m_errorstring.append(string_format("%s WRONG LENGTH (expected: %08x found: %08x)\n", name, explength, actlength)); m_warnings++; } - /* If there is no good dump known, write it */ - util::hash_collection &acthashes = m_file->hashes(hashes.hash_types().c_str()); if (hashes.flag(util::hash_collection::FLAG_NO_DUMP)) { + // If there is no good dump known, write it m_errorstring.append(string_format("%s NO GOOD DUMP KNOWN\n", name)); m_knownbad++; } - /* verify checksums */ - else if (hashes != acthashes) - { - /* otherwise, it's just bad */ - util::hash_collection &all_acthashes = acthashes.hash_types() == util::hash_collection::HASH_TYPES_ALL - ? acthashes - : m_file->hashes(util::hash_collection::HASH_TYPES_ALL); - m_errorstring.append(string_format("%s WRONG CHECKSUMS:\n", name)); - dump_wrong_and_correct_checksums(hashes, all_acthashes); - m_warnings++; - } - /* If it matches, but it is actually a bad dump, write it */ - else if (hashes.flag(util::hash_collection::FLAG_BAD_DUMP)) + else { - m_errorstring.append(string_format("%s ROM NEEDS REDUMP\n", name)); - m_knownbad++; + // verify checksums + util::hash_collection const &acthashes(file->hashes(hashes.hash_types())); + if (hashes != acthashes) + { + // otherwise, it's just bad + util::hash_collection const &all_acthashes = (acthashes.hash_types() == util::hash_collection::HASH_TYPES_ALL) + ? acthashes + : file->hashes(util::hash_collection::HASH_TYPES_ALL); + m_errorstring.append(string_format("%s WRONG CHECKSUMS:\n", name)); + dump_wrong_and_correct_checksums(hashes, all_acthashes); + m_warnings++; + } + else if (hashes.flag(util::hash_collection::FLAG_BAD_DUMP)) + { + // If it matches, but it is actually a bad dump, write it + m_errorstring.append(string_format("%s ROM NEEDS REDUMP\n", name)); + m_knownbad++; + } } } @@ -444,15 +637,14 @@ void rom_load_manager::verify_length_and_hash(const char *name, u32 explength, c void rom_load_manager::display_loading_rom_message(const char *name, bool from_list) { - char buffer[200]; - - if (name != nullptr) - sprintf(buffer, "%s (%d%%)", from_list ? "Loading Software" : "Loading Machine", u32(100 * u64(m_romsloadedsize) / u64(m_romstotalsize))); + std::string buffer; + if (name) + buffer = util::string_format("%s (%d%%)", from_list ? "Loading Software" : "Loading Machine", u32(100 * m_romsloadedsize / m_romstotalsize)); else - sprintf(buffer, "Loading Complete"); + buffer = "Loading Complete"; if (!machine().ui().is_menu_active()) - machine().ui().set_startup_text(buffer, false); + machine().ui().set_startup_text(buffer.c_str(), false); } @@ -463,22 +655,22 @@ void rom_load_manager::display_loading_rom_message(const char *name, bool from_l void rom_load_manager::display_rom_load_results(bool from_list) { - /* final status display */ + // final status display display_loading_rom_message(nullptr, from_list); - /* if we had errors, they are fatal */ + // if we had errors, they are fatal if (m_errors != 0) { - /* create the error message and exit fatally */ - osd_printf_error("%s", m_errorstring.c_str()); - fatalerror_exitcode(machine(), EMU_ERR_MISSING_FILES, "Required files are missing, the machine cannot be run."); + // create the error message and exit fatally + osd_printf_error("%s", m_errorstring); + throw emu_fatalerror(EMU_ERR_MISSING_FILES, "Required files are missing, the machine cannot be run."); } - /* if we had warnings, output them, but continue */ - if ((m_warnings) || (m_knownbad)) + // if we had warnings, output them, but continue + if (m_warnings || m_knownbad) { m_errorstring.append("WARNING: the machine might not run correctly."); - osd_printf_warning("%s\n", m_errorstring.c_str()); + osd_printf_warning("%s\n", m_errorstring); } } @@ -488,37 +680,36 @@ void rom_load_manager::display_rom_load_results(bool from_list) byte swapping and inverting data as necessary -------------------------------------------------*/ -void rom_load_manager::region_post_process(const char *rgntag, bool invert) +void rom_load_manager::region_post_process(memory_region *region, bool invert) { - memory_region *region = machine().root_device().memregion(rgntag); - u8 *base; - int i, j; - // do nothing if no region - if (region == nullptr) + if (!region) return; - LOG("+ datawidth=%dbit endian=%s\n", region->bitwidth(), - region->endianness() == ENDIANNESS_LITTLE ? "little" : "big"); + LOG("+ datawidth=%dbit endian=%s\n", + region->bitwidth(), + (region->endianness() == ENDIANNESS_LITTLE) ? "little" : "big"); - /* if the region is inverted, do that now */ + // if the region is inverted, do that now if (invert) { LOG("+ Inverting region\n"); - for (i = 0, base = region->base(); i < region->bytes(); i++) + u8 *base(region->base()); + for (int i = 0; i < region->bytes(); i++) *base++ ^= 0xff; } - /* swap the endianness if we need to */ - if (region->bytewidth() > 1 && region->endianness() != ENDIANNESS_NATIVE) + // swap the endianness if we need to + int const datawidth(region->bytewidth()); + if ((datawidth > 1) && (region->endianness() != ENDIANNESS_NATIVE)) { LOG("+ Byte swapping region\n"); - int datawidth = region->bytewidth(); - for (i = 0, base = region->base(); i < region->bytes(); i += datawidth) + u8 *base(region->base()); + for (int i = 0; i < region->bytes(); i += datawidth) { u8 temp[8]; memcpy(temp, base, datawidth); - for (j = datawidth - 1; j >= 0; j--) + for (int j = datawidth - 1; j >= 0; j--) *base++ = temp[j]; } } @@ -530,116 +721,64 @@ void rom_load_manager::region_post_process(const char *rgntag, bool invert) up the parent and loading by checksum -------------------------------------------------*/ -int rom_load_manager::open_rom_file(const char *regiontag, const rom_entry *romp, std::string &tried_file_names, bool from_list) +std::unique_ptr<emu_file> rom_load_manager::open_rom_file( + const std::vector<std::string> &searchpath, + const rom_entry *romp, + std::vector<std::string> &tried_file_names, + bool from_list) { - osd_file::error filerr = osd_file::error::NOT_FOUND; - u32 romsize = rom_file_size(romp); - tried_file_names = ""; + std::error_condition filerr = std::errc::no_such_file_or_directory; + u32 const romsize = rom_file_size(romp); + tried_file_names.clear(); - /* update status display */ + // update status display display_loading_rom_message(ROM_GETNAME(romp), from_list); - /* extract CRC to use for searching */ + // extract CRC to use for searching u32 crc = 0; - bool has_crc = util::hash_collection(ROM_GETHASHDATA(romp)).crc(crc); - - /* attempt reading up the chain through the parents. It automatically also - attempts any kind of load by checksum supported by the archives. */ - m_file = nullptr; - for (int drv = driver_list::find(machine().system()); m_file == nullptr && drv != -1; drv = driver_list::clone(drv)) { - if (tried_file_names.length() != 0) - tried_file_names += " "; - tried_file_names += driver_list::driver(drv).name; - m_file = common_process_file(machine().options(), driver_list::driver(drv).name, has_crc, crc, romp, filerr); - } - - /* if the region is load by name, load the ROM from there */ - if (m_file == nullptr && regiontag != nullptr) - { - // check if we are dealing with softwarelists. if so, locationtag - // is actually a concatenation of: listname + setname + parentname - // separated by '%' (parentname being present only for clones) - std::string tag1(regiontag), tag2, tag3, tag4, tag5; - bool is_list = false; - bool has_parent = false; - - int separator1 = tag1.find_first_of('%'); - if (separator1 != -1) - { - is_list = true; - - // we are loading through softlists, split the listname from the regiontag - tag4.assign(tag1.substr(separator1 + 1, tag1.length() - separator1 + 1)); - tag1.erase(separator1, tag1.length() - separator1); - tag1.append(PATH_SEPARATOR); + bool const has_crc = util::hash_collection(romp->hashdata()).crc(crc); - // check if we are loading a clone (if this is the case also tag1 have a separator '%') - int separator2 = tag4.find_first_of('%'); - if (separator2 != -1) - { - has_parent = true; + // attempt reading up the chain through the parents + // it also automatically attempts any kind of load by checksum supported by the archives. + std::unique_ptr<emu_file> result( + open_rom_file(searchpath, tried_file_names, has_crc, crc, ROM_GETNAME(romp), filerr)); - // we are loading a clone through softlists, split the setname from the parentname - tag5.assign(tag4.substr(separator2 + 1, tag4.length() - separator2 + 1)); - tag4.erase(separator2, tag4.length() - separator2); - } + // update counters + m_romsloaded++; + m_romsloadedsize += romsize; - // prepare locations where we have to load from: list/parentname & list/clonename - std::string swlist(tag1); - tag2.assign(swlist.append(tag4)); - if (has_parent) - { - swlist.assign(tag1); - tag3.assign(swlist.append(tag5)); - } - } + // return the result + if (filerr) + return nullptr; + else + return result; +} - if (tag5.find_first_of('%') != -1) - fatalerror("We do not support clones of clones!\n"); - // try to load from the available location(s): - // - if we are not using lists, we have regiontag only; - // - if we are using lists, we have: list/clonename, list/parentname, clonename, parentname - if (!is_list) - { - tried_file_names += " " + tag1; - m_file = common_process_file(machine().options(), tag1.c_str(), has_crc, crc, romp, filerr); - } - else - { - // try to load from list/setname - if ((m_file == nullptr) && (tag2.c_str() != nullptr)) - { - tried_file_names += " " + tag2; - m_file = common_process_file(machine().options(), tag2.c_str(), has_crc, crc, romp, filerr); - } - // try to load from list/parentname - if ((m_file == nullptr) && has_parent && (tag3.c_str() != nullptr)) - { - tried_file_names += " " + tag3; - m_file = common_process_file(machine().options(), tag3.c_str(), has_crc, crc, romp, filerr); - } - // try to load from setname - if ((m_file == nullptr) && (tag4.c_str() != nullptr)) - { - tried_file_names += " " + tag4; - m_file = common_process_file(machine().options(), tag4.c_str(), has_crc, crc, romp, filerr); - } - // try to load from parentname - if ((m_file == nullptr) && has_parent && (tag5.c_str() != nullptr)) - { - tried_file_names += " " + tag5; - m_file = common_process_file(machine().options(), tag5.c_str(), has_crc, crc, romp, filerr); - } - } - } +std::unique_ptr<emu_file> rom_load_manager::open_rom_file( + const std::vector<std::string> &paths, + std::vector<std::string> &tried, + bool has_crc, + u32 crc, + std::string_view name, + std::error_condition &filerr) +{ + // record the set names we search + tried.insert(tried.end(), paths.begin(), paths.end()); - /* update counters */ - m_romsloaded++; - m_romsloadedsize += romsize; + // attempt to open the file + std::unique_ptr<emu_file> result(new emu_file(machine().options().media_path(), paths, OPEN_FLAG_READ)); + result->set_restrict_to_mediapath(1); + if (has_crc) + filerr = result->open(name, crc); + else + filerr = result->open(name); - /* return the result */ - return (filerr == osd_file::error::NONE); + // don't return anything if unsuccessful + if (filerr) + return nullptr; + else + return result; } @@ -648,14 +787,12 @@ int rom_load_manager::open_rom_file(const char *regiontag, const rom_entry *romp random data for a nullptr file -------------------------------------------------*/ -int rom_load_manager::rom_fread(u8 *buffer, int length, const rom_entry *parent_region) +int rom_load_manager::rom_fread(emu_file *file, u8 *buffer, int length, const rom_entry *parent_region) { - /* files just pass through */ - if (m_file != nullptr) - return m_file->read(buffer, length); + if (file) // files just pass through + return file->read(buffer, length); - /* otherwise, fill with randomness unless it was already specifically erased */ - else if (!ROMREGION_ISERASE(parent_region)) + if (!ROMREGION_ISERASE(parent_region)) // otherwise, fill with randomness unless it was already specifically erased fill_random(buffer, length); return length; @@ -667,109 +804,118 @@ int rom_load_manager::rom_fread(u8 *buffer, int length, const rom_entry *parent_ entry -------------------------------------------------*/ -int rom_load_manager::read_rom_data(const rom_entry *parent_region, const rom_entry *romp) +int rom_load_manager::read_rom_data( + emu_file *file, + memory_region ®ion, + const rom_entry *parent_region, + const rom_entry *romp) { - int datashift = ROM_GETBITSHIFT(romp); - int datamask = ((1 << ROM_GETBITWIDTH(romp)) - 1) << datashift; - int numbytes = ROM_GETLENGTH(romp); - int groupsize = ROM_GETGROUPSIZE(romp); - int skip = ROM_GETSKIPCOUNT(romp); - int reversed = ROM_ISREVERSED(romp); - int numgroups = (numbytes + groupsize - 1) / groupsize; - u8 *base = m_region->base() + ROM_GETOFFSET(romp); - u32 tempbufsize; - int i; + int const datashift(ROM_GETBITSHIFT(romp)); + int const datamask(((1 << ROM_GETBITWIDTH(romp)) - 1) << datashift); + int numbytes(ROM_GETLENGTH(romp)); + int const groupsize(ROM_GETGROUPSIZE(romp)); + int skip(ROM_GETSKIPCOUNT(romp)); + int const reversed(ROM_ISREVERSED(romp)); + int const numgroups((numbytes + groupsize - 1) / groupsize); + u8 *base = region.base() + ROM_GETOFFSET(romp); LOG("Loading ROM data: offs=%X len=%X mask=%02X group=%d skip=%d reverse=%d\n", ROM_GETOFFSET(romp), numbytes, datamask, groupsize, skip, reversed); - /* make sure the length was an even multiple of the group size */ - if (numbytes % groupsize != 0) - osd_printf_warning("Warning in RomModule definition: %s length not an even multiple of group size\n", ROM_GETNAME(romp)); + // make sure the length was a whole multiple of the group size + if ((numbytes % groupsize) != 0) + osd_printf_warning("Warning in RomModule definition: %s length not an even multiple of group size\n", romp->name()); - /* make sure we only fill within the region space */ - if (ROM_GETOFFSET(romp) + numgroups * groupsize + (numgroups - 1) * skip > m_region->bytes()) - fatalerror("Error in RomModule definition: %s out of memory region space\n", ROM_GETNAME(romp)); + // make sure we only fill within the region space + if (ROM_GETOFFSET(romp) + numgroups * groupsize + (numgroups - 1) * skip > region.bytes()) + throw emu_fatalerror("Error in RomModule definition: %s out of memory region space\n", romp->name()); - /* make sure the length was valid */ + // make sure the length was valid if (numbytes == 0) - fatalerror("Error in RomModule definition: %s has an invalid length\n", ROM_GETNAME(romp)); + throw emu_fatalerror("Error in RomModule definition: %s has an invalid length\n", romp->name()); - /* special case for simple loads */ - if (datamask == 0xff && (groupsize == 1 || !reversed) && skip == 0) - return rom_fread(base, numbytes, parent_region); + // special case for simple loads + if ((datamask == 0xff) && ((groupsize == 1) || !reversed) && (skip == 0)) + return rom_fread(file, base, numbytes, parent_region); - /* use a temporary buffer for complex loads */ - tempbufsize = std::min(TEMPBUFFER_MAX_SIZE, numbytes); - std::vector<u8> tempbuf(tempbufsize); + // use a temporary buffer for complex loads + u32 const tempbufsize(std::min(TEMPBUFFER_MAX_SIZE, numbytes)); + std::unique_ptr<u8 []> const tempbuf(new u8 [tempbufsize]); - /* chunky reads for complex loads */ + // chunky reads for complex loads skip += groupsize; while (numbytes > 0) { - int evengroupcount = (tempbufsize / groupsize) * groupsize; - int bytesleft = (numbytes > evengroupcount) ? evengroupcount : numbytes; - u8 *bufptr = &tempbuf[0]; + int evengroupcount((tempbufsize / groupsize) * groupsize); + int bytesleft((numbytes > evengroupcount) ? evengroupcount : numbytes); + u8 *bufptr(tempbuf.get()); - /* read as much as we can */ + // read as much as we can LOG(" Reading %X bytes into buffer\n", bytesleft); - if (rom_fread(bufptr, bytesleft, parent_region) != bytesleft) + if (rom_fread(file, bufptr, bytesleft, parent_region) != bytesleft) return 0; numbytes -= bytesleft; LOG(" Copying to %p\n", base); - /* unmasked cases */ if (datamask == 0xff) { - /* non-grouped data */ + // unmasked cases if (groupsize == 1) - for (i = 0; i < bytesleft; i++, base += skip) + { + // non-grouped data + for (int i = 0; i < bytesleft; i++, base += skip) *base = *bufptr++; - - /* grouped data -- non-reversed case */ + } else if (!reversed) + { + // grouped data -- non-reversed case while (bytesleft) { - for (i = 0; i < groupsize && bytesleft; i++, bytesleft--) + for (int i = 0; i < groupsize && bytesleft; i++, bytesleft--) base[i] = *bufptr++; base += skip; } - - /* grouped data -- reversed case */ + } else + { + // grouped data -- reversed case while (bytesleft) { - for (i = groupsize - 1; i >= 0 && bytesleft; i--, bytesleft--) + for (int i = groupsize - 1; i >= 0 && bytesleft; i--, bytesleft--) base[i] = *bufptr++; base += skip; } + } } - - /* masked cases */ else { - /* non-grouped data */ + // masked cases if (groupsize == 1) - for (i = 0; i < bytesleft; i++, base += skip) + { + // non-grouped data + for (int i = 0; i < bytesleft; i++, base += skip) *base = (*base & ~datamask) | ((*bufptr++ << datashift) & datamask); - - /* grouped data -- non-reversed case */ + } else if (!reversed) + { + // grouped data -- non-reversed case while (bytesleft) { - for (i = 0; i < groupsize && bytesleft; i++, bytesleft--) + for (int i = 0; i < groupsize && bytesleft; i++, bytesleft--) base[i] = (base[i] & ~datamask) | ((*bufptr++ << datashift) & datamask); base += skip; } - - /* grouped data -- reversed case */ + } else + { + // grouped data -- reversed case while (bytesleft) { - for (i = groupsize - 1; i >= 0 && bytesleft; i--, bytesleft--) + for (int i = groupsize - 1; i >= 0 && bytesleft; i--, bytesleft--) base[i] = (base[i] & ~datamask) | ((*bufptr++ << datashift) & datamask); base += skip; } + } } } @@ -782,22 +928,22 @@ int rom_load_manager::read_rom_data(const rom_entry *parent_region, const rom_en fill_rom_data - fill a region of ROM space -------------------------------------------------*/ -void rom_load_manager::fill_rom_data(const rom_entry *romp) +void rom_load_manager::fill_rom_data(memory_region ®ion, const rom_entry *romp) { u32 numbytes = ROM_GETLENGTH(romp); int skip = ROM_GETSKIPCOUNT(romp); - u8 *base = m_region->base() + ROM_GETOFFSET(romp); + u8 *base = region.base() + ROM_GETOFFSET(romp); // make sure we fill within the region space - if (ROM_GETOFFSET(romp) + numbytes > m_region->bytes()) - fatalerror("Error in RomModule definition: FILL out of memory region space\n"); + if (ROM_GETOFFSET(romp) + numbytes > region.bytes()) + throw emu_fatalerror("Error in RomModule definition: FILL out of memory region space\n"); // make sure the length was valid if (numbytes == 0) - fatalerror("Error in RomModule definition: FILL has an invalid length\n"); + throw emu_fatalerror("Error in RomModule definition: FILL has an invalid length\n"); // for fill bytes, the byte that gets filled is the first byte of the hashdata string - u8 fill_byte = u8(strtol(ROM_GETHASHDATA(romp), nullptr, 0)); + u8 fill_byte = u8(strtol(romp->hashdata().c_str(), nullptr, 0)); // fill the data (filling value is stored in place of the hashdata) if(skip != 0) @@ -814,32 +960,32 @@ void rom_load_manager::fill_rom_data(const rom_entry *romp) copy_rom_data - copy a region of ROM space -------------------------------------------------*/ -void rom_load_manager::copy_rom_data(const rom_entry *romp) +void rom_load_manager::copy_rom_data(device_t &device, memory_region ®ion, const rom_entry *romp) { - u8 *base = m_region->base() + ROM_GETOFFSET(romp); - const char *srcrgntag = ROM_GETNAME(romp); + u8 *base = region.base() + ROM_GETOFFSET(romp); + const std::string srcrgntag = device.subtag(romp->name()); u32 numbytes = ROM_GETLENGTH(romp); - u32 srcoffs = u32(strtol(ROM_GETHASHDATA(romp), nullptr, 0)); /* srcoffset in place of hashdata */ + u32 srcoffs = u32(strtol(romp->hashdata().c_str(), nullptr, 0)); /* srcoffset in place of hashdata */ /* make sure we copy within the region space */ - if (ROM_GETOFFSET(romp) + numbytes > m_region->bytes()) - fatalerror("Error in RomModule definition: COPY out of target memory region space\n"); + if (ROM_GETOFFSET(romp) + numbytes > region.bytes()) + throw emu_fatalerror("Error in RomModule definition: COPY out of target memory region space\n"); /* make sure the length was valid */ if (numbytes == 0) - fatalerror("Error in RomModule definition: COPY has an invalid length\n"); + throw emu_fatalerror("Error in RomModule definition: COPY has an invalid length\n"); /* make sure the source was valid */ - memory_region *region = machine().root_device().memregion(srcrgntag); - if (region == nullptr) - fatalerror("Error in RomModule definition: COPY from an invalid region\n"); + memory_region *src = machine().root_device().memregion(srcrgntag); + if (!src) + throw emu_fatalerror("Error in RomModule definition: COPY from an invalid region\n"); /* make sure we find within the region space */ - if (srcoffs + numbytes > region->bytes()) - fatalerror("Error in RomModule definition: COPY out of source memory region space\n"); + if (srcoffs + numbytes > src->bytes()) + throw emu_fatalerror("Error in RomModule definition: COPY out of source memory region space\n"); /* fill the data */ - memcpy(base, region->base() + srcoffs, numbytes); + memcpy(base, src->base() + srcoffs, numbytes); } @@ -848,56 +994,70 @@ void rom_load_manager::copy_rom_data(const rom_entry *romp) for a region -------------------------------------------------*/ -void rom_load_manager::process_rom_entries(const char *regiontag, const rom_entry *parent_region, const rom_entry *romp, device_t *device, bool from_list) +void rom_load_manager::process_rom_entries( + device_t &device, + const std::vector<std::string> &searchpath, + u8 bios, + memory_region ®ion, + const rom_entry *parent_region, + const rom_entry *romp, + bool from_list) { u32 lastflags = 0; + std::vector<std::string> tried_file_names; - /* loop until we hit the end of this region */ + // loop until we hit the end of this region while (!ROMENTRY_ISREGIONEND(romp)) { + tried_file_names.clear(); + if (ROMENTRY_ISCONTINUE(romp)) - fatalerror("Error in RomModule definition: ROM_CONTINUE not preceded by ROM_LOAD\n"); + throw emu_fatalerror("Error in RomModule definition: ROM_CONTINUE not preceded by ROM_LOAD\n"); if (ROMENTRY_ISIGNORE(romp)) - fatalerror("Error in RomModule definition: ROM_IGNORE not preceded by ROM_LOAD\n"); + throw emu_fatalerror("Error in RomModule definition: ROM_IGNORE not preceded by ROM_LOAD\n"); if (ROMENTRY_ISRELOAD(romp)) - fatalerror("Error in RomModule definition: ROM_RELOAD not preceded by ROM_LOAD\n"); + throw emu_fatalerror("Error in RomModule definition: ROM_RELOAD not preceded by ROM_LOAD\n"); if (ROMENTRY_ISFILL(romp)) { - if (!ROM_GETBIOSFLAGS(romp) || ROM_GETBIOSFLAGS(romp) == device->system_bios()) - fill_rom_data(romp); + if (!ROM_GETBIOSFLAGS(romp) || (ROM_GETBIOSFLAGS(romp) == bios)) + fill_rom_data(region, romp); romp++; } else if (ROMENTRY_ISCOPY(romp)) { - copy_rom_data(romp++); + copy_rom_data(device, region, romp++); } else if (ROMENTRY_ISFILE(romp)) { - /* handle files */ - int irrelevantbios = (ROM_GETBIOSFLAGS(romp) != 0 && ROM_GETBIOSFLAGS(romp) != device->system_bios()); - const rom_entry *baserom = romp; + // handle files + bool const irrelevantbios = (ROM_GETBIOSFLAGS(romp) != 0) && (ROM_GETBIOSFLAGS(romp) != bios); + rom_entry const *baserom = romp; int explength = 0; - /* open the file if it is a non-BIOS or matches the current BIOS */ + // open the file if it is a non-BIOS or matches the current BIOS LOG("Opening ROM file: %s\n", ROM_GETNAME(romp)); - std::string tried_file_names; - if (!irrelevantbios && !open_rom_file(regiontag, romp, tried_file_names, from_list)) - handle_missing_file(romp, tried_file_names, CHDERR_NONE); + std::unique_ptr<emu_file> file; + if (!irrelevantbios) + { + file = open_rom_file(searchpath, romp, tried_file_names, from_list); + if (!file) + handle_missing_file(romp, tried_file_names, std::error_condition()); + } - /* loop until we run out of reloads */ + // loop until we run out of reloads do { - /* loop until we run out of continues/ignores */ + // loop until we run out of continues/ignores do { rom_entry modified_romp = *romp++; //int readresult; - /* handle flag inheritance */ + // handle flag inheritance if (!ROM_INHERITSFLAGS(&modified_romp)) lastflags = modified_romp.get_flags(); else @@ -905,33 +1065,33 @@ void rom_load_manager::process_rom_entries(const char *regiontag, const rom_entr explength += ROM_GETLENGTH(&modified_romp); - /* attempt to read using the modified entry */ + // attempt to read using the modified entry if (!ROMENTRY_ISIGNORE(&modified_romp) && !irrelevantbios) - /*readresult = */read_rom_data(parent_region, &modified_romp); + /*readresult = */read_rom_data(file.get(), region, parent_region, &modified_romp); } while (ROMENTRY_ISCONTINUE(romp) || ROMENTRY_ISIGNORE(romp)); - /* if this was the first use of this file, verify the length and CRC */ + // if this was the first use of this file, verify the length and CRC if (baserom) { LOG("Verifying length (%X) and checksums\n", explength); - verify_length_and_hash(ROM_GETNAME(baserom), explength, util::hash_collection(ROM_GETHASHDATA(baserom))); + verify_length_and_hash(file.get(), baserom->name(), explength, util::hash_collection(baserom->hashdata())); LOG("Verify finished\n"); } - /* reseek to the start and clear the baserom so we don't reverify */ - if (m_file != nullptr) - m_file->seek(0, SEEK_SET); + // re-seek to the start and clear the baserom so we don't reverify + if (file) + file->seek(0, SEEK_SET); baserom = nullptr; explength = 0; } while (ROMENTRY_ISRELOAD(romp)); - /* close the file */ - if (m_file != nullptr) + // close the file + if (file) { LOG("Closing ROM file\n"); - m_file = nullptr; + file.reset(); } } else @@ -943,221 +1103,51 @@ void rom_load_manager::process_rom_entries(const char *regiontag, const rom_entr /*------------------------------------------------- - open_disk_image - open a disk image, - searching up the parent and loading by - checksum --------------------------------------------------*/ - -int open_disk_image(emu_options &options, const game_driver *gamedrv, const rom_entry *romp, chd_file &image_chd, const char *locationtag) -{ - emu_file image_file(options.media_path(), OPEN_FLAG_READ); - const rom_entry *region, *rom; - osd_file::error filerr; - chd_error err; - - /* attempt to open the properly named file, scanning up through parent directories */ - filerr = osd_file::error::NOT_FOUND; - for (int searchdrv = driver_list::find(*gamedrv); searchdrv != -1 && filerr != osd_file::error::NONE; searchdrv = driver_list::clone(searchdrv)) - filerr = common_process_file(options, driver_list::driver(searchdrv).name, ".chd", romp, image_file); - - if (filerr != osd_file::error::NONE) - filerr = common_process_file(options, nullptr, ".chd", romp, image_file); - - /* look for the disk in the locationtag too */ - if (filerr != osd_file::error::NONE && locationtag != nullptr) - { - // check if we are dealing with softwarelists. if so, locationtag - // is actually a concatenation of: listname + setname + parentname - // separated by '%' (parentname being present only for clones) - std::string tag1(locationtag), tag2, tag3, tag4, tag5; - bool is_list = false; - bool has_parent = false; - - int separator1 = tag1.find_first_of('%'); - if (separator1 != -1) - { - is_list = true; - - // we are loading through softlists, split the listname from the regiontag - tag4.assign(tag1.substr(separator1 + 1, tag1.length() - separator1 + 1)); - tag1.erase(separator1, tag1.length() - separator1); - tag1.append(PATH_SEPARATOR); - - // check if we are loading a clone (if this is the case also tag1 have a separator '%') - int separator2 = tag4.find_first_of('%'); - if (separator2 != -1) - { - has_parent = true; - - // we are loading a clone through softlists, split the setname from the parentname - tag5.assign(tag4.substr(separator2 + 1, tag4.length() - separator2 + 1)); - tag4.erase(separator2, tag4.length() - separator2); - } - - // prepare locations where we have to load from: list/parentname (if any) & list/clonename - std::string swlist(tag1); - tag2.assign(swlist.append(tag4)); - if (has_parent) - { - swlist.assign(tag1); - tag3.assign(swlist.append(tag5)); - } - } - - if (tag5.find_first_of('%') != -1) - fatalerror("We do not support clones of clones!\n"); - - // try to load from the available location(s): - // - if we are not using lists, we have locationtag only; - // - if we are using lists, we have: list/clonename, list/parentname, clonename, parentname - if (!is_list) - filerr = common_process_file(options, locationtag, ".chd", romp, image_file); - else - { - // try to load from list/setname - if ((filerr != osd_file::error::NONE) && (tag2.c_str() != nullptr)) - filerr = common_process_file(options, tag2.c_str(), ".chd", romp, image_file); - // try to load from list/parentname (if any) - if ((filerr != osd_file::error::NONE) && has_parent && (tag3.c_str() != nullptr)) - filerr = common_process_file(options, tag3.c_str(), ".chd", romp, image_file); - // try to load from setname - if ((filerr != osd_file::error::NONE) && (tag4.c_str() != nullptr)) - filerr = common_process_file(options, tag4.c_str(), ".chd", romp, image_file); - // try to load from parentname (if any) - if ((filerr != osd_file::error::NONE) && has_parent && (tag5.c_str() != nullptr)) - filerr = common_process_file(options, tag5.c_str(), ".chd", romp, image_file); - // only for CHD we also try to load from list/ - if ((filerr != osd_file::error::NONE) && (tag1.c_str() != nullptr)) - { - tag1.erase(tag1.length() - 1, 1); // remove the PATH_SEPARATOR - filerr = common_process_file(options, tag1.c_str(), ".chd", romp, image_file); - } - } - } - - /* did the file open succeed? */ - if (filerr == osd_file::error::NONE) - { - std::string fullpath(image_file.fullpath()); - image_file.close(); - - /* try to open the CHD */ - err = image_chd.open(fullpath.c_str()); - if (err == CHDERR_NONE) - return err; - } - else - err = CHDERR_FILE_NOT_FOUND; - - // Otherwise, look at our parents for a CHD with an identical checksum - // and try to open that - // - // An example of a system that requires this is src/mame/drivers/ksys673.cpp, that has declarations like this: - // ... - // DISK_IMAGE_READONLY("889aa", 0, BAD_DUMP SHA1(0b567bf2f03ee8089e0b021ea502a53b3f6fe7ac)) - // ... - // DISK_IMAGE_READONLY("889ea", 0, BAD_DUMP SHA1(0b567bf2f03ee8089e0b021ea502a53b3f6fe7ac)) - // ... - // DISK_IMAGE_READONLY("889ja", 0, BAD_DUMP SHA1(0b567bf2f03ee8089e0b021ea502a53b3f6fe7ac)) - // ... - // DISK_IMAGE_READONLY("889ua", 0, BAD_DUMP SHA1(0b567bf2f03ee8089e0b021ea502a53b3f6fe7ac)) - // ... - util::hash_collection romphashes(ROM_GETHASHDATA(romp)); - for (int drv = driver_list::find(*gamedrv); drv != -1; drv = driver_list::clone(drv)) - { - const game_driver ¤t_driver(driver_list::driver(drv)); - - // Create a single use emu_option structure for the purposes of this lookup, just - // carrying forward the options that are necessary for CHD lookup. This is because the - // options passed to us may have slot/image configurations that are "poisonous" for these - // other drivers - // - // A side effect of this approach is that the "dragnet" to find CHDs with identical hashes - // will only find CHDs for the default configuration. I believe that this in practice will - // be acceptable. - emu_options driver_specific_options; - driver_specific_options.set_system_name(current_driver.name); - driver_specific_options.set_value(OPTION_MEDIAPATH, options.media_path(), OPTION_PRIORITY_DEFAULT); - driver_specific_options.set_value(OPTION_DIFF_DIRECTORY, options.diff_directory(), OPTION_PRIORITY_DEFAULT); - - // Now that we have an emu_options structure properly set up, we can create a machine_config - machine_config config(current_driver, driver_specific_options); - - for (device_t &device : device_iterator(config.root_device())) - for (region = rom_first_region(device); region != nullptr; region = rom_next_region(region)) - if (ROMREGION_ISDISKDATA(region)) - for (rom = rom_first_file(region); rom != nullptr; rom = rom_next_file(rom)) - - // Look for a differing name but with the same hash data - if (strcmp(ROM_GETNAME(romp), ROM_GETNAME(rom)) != 0 && - romphashes == util::hash_collection(ROM_GETHASHDATA(rom))) - { - // Attempt to open the properly named file, scanning up through parent directories - filerr = osd_file::error::NOT_FOUND; - for (int searchdrv = drv; searchdrv != -1 && filerr != osd_file::error::NONE; searchdrv = driver_list::clone(searchdrv)) - filerr = common_process_file(driver_specific_options, driver_list::driver(searchdrv).name, ".chd", rom, image_file); - - if (filerr != osd_file::error::NONE) - filerr = common_process_file(driver_specific_options, nullptr, ".chd", rom, image_file); - - // Did the file open succeed? - if (filerr == osd_file::error::NONE) - { - std::string fullpath(image_file.fullpath()); - image_file.close(); - - // try to open the CHD - err = image_chd.open(fullpath.c_str()); - if (err == CHDERR_NONE) - return err; - } - } - } - return err; -} - - -/*------------------------------------------------- open_disk_diff - open a DISK diff file -------------------------------------------------*/ -chd_error rom_load_manager::open_disk_diff(emu_options &options, const rom_entry *romp, chd_file &source, chd_file &diff_chd) +std::error_condition rom_load_manager::open_disk_diff( + emu_options &options, + const rom_entry *romp, + chd_file &source, + chd_file &diff_chd) { - std::string fname = std::string(ROM_GETNAME(romp)).append(".dif"); + // TODO: use system name and/or software list name in the path - the current setup doesn't scale + std::string fname = romp->name() + ".dif"; - /* try to open the diff */ + // try to open the diff LOG("Opening differencing image file: %s\n", fname.c_str()); emu_file diff_file(options.diff_directory(), OPEN_FLAG_READ | OPEN_FLAG_WRITE); - osd_file::error filerr = diff_file.open(fname.c_str()); - if (filerr == osd_file::error::NONE) + std::error_condition filerr = diff_file.open(fname); + if (!filerr) { std::string fullpath(diff_file.fullpath()); diff_file.close(); LOG("Opening differencing image file: %s\n", fullpath.c_str()); - return diff_chd.open(fullpath.c_str(), true, &source); + return diff_chd.open(fullpath, true, &source); } - /* didn't work; try creating it instead */ + // didn't work; try creating it instead LOG("Creating differencing image: %s\n", fname.c_str()); diff_file.set_openflags(OPEN_FLAG_READ | OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); - filerr = diff_file.open(fname.c_str()); - if (filerr == osd_file::error::NONE) + filerr = diff_file.open(fname); + if (!filerr) { std::string fullpath(diff_file.fullpath()); diff_file.close(); - /* create the CHD */ + // create the CHD LOG("Creating differencing image file: %s\n", fullpath.c_str()); chd_codec_type compression[4] = { CHD_CODEC_NONE }; - chd_error err = diff_chd.create(fullpath.c_str(), source.logical_bytes(), source.hunk_bytes(), compression, source); - if (err != CHDERR_NONE) + std::error_condition err = diff_chd.create(fullpath, source.logical_bytes(), source.hunk_bytes(), compression, source); + if (err) return err; return diff_chd.clone_all_metadata(source); } - return CHDERR_FILE_NOT_FOUND; + return std::errc::no_such_file_or_directory; } @@ -1166,41 +1156,55 @@ chd_error rom_load_manager::open_disk_diff(emu_options &options, const rom_entry for a region -------------------------------------------------*/ -void rom_load_manager::process_disk_entries(const char *regiontag, const rom_entry *parent_region, const rom_entry *romp, const char *locationtag) +void rom_load_manager::process_disk_entries( + search_paths searchpath, + std::string_view regiontag, + rom_entry const *romp, + std::function<rom_entry const * ()> next_parent, + chd_file::open_parent_func const &open_parent) { - /* remove existing disk entries for this region */ - m_chd_list.erase(std::remove_if(m_chd_list.begin(), m_chd_list.end(), - [regiontag](std::unique_ptr<open_chd> &chd){ return !strcmp(chd->region(), regiontag); }), m_chd_list.end()); - - /* loop until we hit the end of this region */ + // remove existing disk entries for this region + m_chd_list.erase( + std::remove_if( + m_chd_list.begin(), + m_chd_list.end(), + [®iontag] (std::unique_ptr<open_chd> &chd) { return chd->region() == regiontag; }), + m_chd_list.end()); + + // loop until we hit the end of this region for ( ; !ROMENTRY_ISREGIONEND(romp); romp++) { - /* handle files */ + // handle files if (ROMENTRY_ISFILE(romp)) { - auto chd = std::make_unique<open_chd>(regiontag); - - util::hash_collection hashes(ROM_GETHASHDATA(romp)); - chd_error err; + auto chd(std::make_unique<open_chd>(regiontag)); + std::error_condition err; - /* make the filename of the source */ - std::string filename = std::string(ROM_GETNAME(romp)).append(".chd"); + // make the filename of the source + std::string const filename(romp->name() + ".chd"); - /* first open the source drive */ + // first open the reference disk image LOG("Opening disk image: %s\n", filename.c_str()); - err = chd_error(open_disk_image(machine().options(), &machine().system(), romp, chd->orig_chd(), locationtag)); - if (err != CHDERR_NONE) + err = do_open_disk(machine().options(), searchpath, romp, chd->orig_chd(), next_parent, open_parent); + if (!err && chd->orig_chd().parent_missing()) + err = chd_file::error::REQUIRES_PARENT; + if (err) { - handle_missing_file(romp, std::string(), err); - chd = nullptr; + std::vector<std::string> tried; + for (auto const &paths : searchpath) + { + for (std::string const &path : paths.get()) + tried.emplace_back(path); + } + handle_missing_file(romp, tried, err); + chd.reset(); continue; } - /* get the header and extract the SHA1 */ + // get the header and verify the SHA1 digest util::hash_collection acthashes; acthashes.add_sha1(chd->orig_chd().sha1()); - - /* verify the hash */ + util::hash_collection const hashes(romp->hashdata()); if (hashes != acthashes) { m_errorstring.append(string_format("%s WRONG CHECKSUMS:\n", filename)); @@ -1213,34 +1217,100 @@ void rom_load_manager::process_disk_entries(const char *regiontag, const rom_ent m_knownbad++; } - /* if not read-only, make the diff file */ + // if not read-only, open or create the diff file if (!DISK_ISREADONLY(romp)) { - /* try to open or create the diff */ err = open_disk_diff(machine().options(), romp, chd->orig_chd(), chd->diff_chd()); - if (err != CHDERR_NONE) + if (err) { - m_errorstring.append(string_format("%s DIFF CHD ERROR: %s\n", filename, chd_file::error_string(err))); + m_errorstring.append(string_format("%s DIFF CHD ERROR: %s\n", filename, err.message())); m_errors++; - chd = nullptr; + chd.reset(); continue; } } - /* we're okay, add to the list of disks */ + // we're okay, add to the list of disks LOG("Assigning to handle %d\n", DISK_GETINDEX(romp)); - m_chd_list.push_back(std::move(chd)); + m_chd_list.emplace_back(std::move(chd)); } } } /*------------------------------------------------- + get_software_searchpath - get search path + for a software list item +-------------------------------------------------*/ + +std::vector<std::string> rom_load_manager::get_software_searchpath(software_list_device &swlist, const software_info &swinfo) +{ + std::vector<software_info const *> parents; + return make_software_searchpath(swlist, swinfo, parents); +} + + +/*------------------------------------------------- + open_disk_image - open a disk image for a + device +-------------------------------------------------*/ + +std::error_condition rom_load_manager::open_disk_image( + emu_options &options, + const device_t &device, + const rom_entry *romp, + chd_file &image_chd) +{ + const std::vector<std::string> searchpath(device.searchpath()); + + driver_device const *const driver(dynamic_cast<driver_device const *>(&device)); + std::function<const rom_entry * ()> next_parent; + if (driver) + next_parent = next_parent_system(driver->system()); + else + next_parent = next_parent_device(device, options); + chd_file::open_parent_func open_parent(open_parent_disk(options, { searchpath }, next_parent)); + std::error_condition const err( + do_open_disk(options, { searchpath }, romp, image_chd, std::move(next_parent), open_parent)); + if (!err && image_chd.parent_missing()) + return chd_file::error::REQUIRES_PARENT; + else + return err; +} + + +/*------------------------------------------------- + open_disk_image - open a disk image for a + software item +-------------------------------------------------*/ + +std::error_condition rom_load_manager::open_disk_image( + emu_options &options, + software_list_device &swlist, + const software_info &swinfo, + const rom_entry *romp, + chd_file &image_chd) +{ + std::vector<software_info const *> parents; + std::vector<std::string> searchpath(make_software_searchpath(swlist, swinfo, parents)); + searchpath.emplace_back(swlist.list_name()); // look for loose disk images in software list directory + std::function<const rom_entry * ()> next_parent(next_parent_software(parents)); + chd_file::open_parent_func open_parent(open_parent_disk(options, { searchpath }, next_parent)); + std::error_condition const err( + do_open_disk(options, { searchpath }, romp, image_chd, std::move(next_parent), open_parent)); + if (!err && image_chd.parent_missing()) + return chd_file::error::REQUIRES_PARENT; + else + return err; +} + + +/*------------------------------------------------- normalize_flags_for_device - modify the region flags for the given device -------------------------------------------------*/ -void rom_load_manager::normalize_flags_for_device(const char *rgntag, u8 &width, endianness_t &endian) +void rom_load_manager::normalize_flags_for_device(std::string_view rgntag, u8 &width, endianness_t &endian) { device_t *device = machine().root_device().subdevice(rgntag); device_memory_interface *memory; @@ -1275,18 +1345,14 @@ void rom_load_manager::normalize_flags_for_device(const char *rgntag, u8 &width, /*------------------------------------------------- load_software_part_region - load a software part - This is used by MESS when loading a piece of + This is used by MAME when loading a piece of software. The code should be merged with process_region_list or updated to use a slight more general process_region_list. -------------------------------------------------*/ -void rom_load_manager::load_software_part_region(device_t &device, software_list_device &swlist, const char *swname, const rom_entry *start_region) +void rom_load_manager::load_software_part_region(device_t &device, software_list_device &swlist, std::string_view swname, const rom_entry *start_region) { - std::string locationtag(swlist.list_name()), breakstr("%"); - const rom_entry *region; - std::string regiontag; - m_errorstring.clear(); m_softwarningstring.clear(); @@ -1294,100 +1360,105 @@ void rom_load_manager::load_software_part_region(device_t &device, software_list m_romstotalsize = 0; m_romsloadedsize = 0; - const software_info *swinfo = swlist.find(swname); - if (swinfo != nullptr) + std::vector<const software_info *> parents; + std::vector<std::string> swsearch, disksearch; + const software_info *const swinfo = swlist.find(std::string(swname)); + if (swinfo) { - u32 supported = swinfo->supported(); - if (supported == SOFTWARE_SUPPORTED_PARTIAL) + // display a warning for unsupported software + // TODO: list supported clones like we do for machines? + if (swinfo->supported() == software_support::PARTIALLY_SUPPORTED) { m_errorstring.append(string_format("WARNING: support for software %s (in list %s) is only partial\n", swname, swlist.list_name())); m_softwarningstring.append(string_format("Support for software %s (in list %s) is only partial\n", swname, swlist.list_name())); } - if (supported == SOFTWARE_SUPPORTED_NO) + else if (swinfo->supported() == software_support::UNSUPPORTED) { m_errorstring.append(string_format("WARNING: support for software %s (in list %s) is only preliminary\n", swname, swlist.list_name())); m_softwarningstring.append(string_format("Support for software %s (in list %s) is only preliminary\n", swname, swlist.list_name())); } - // attempt reading up the chain through the parents and create a locationtag std::string in the format - // " swlist % clonename % parentname " - // open_rom_file contains the code to split the elements and to create paths to load from - - locationtag.append(breakstr); - - while (swinfo != nullptr) - { - locationtag.append(swinfo->shortname()).append(breakstr); - swinfo = !swinfo->parentname().empty() ? swlist.find(swinfo->parentname().c_str()) : nullptr; - } - // strip the final '%' - locationtag.erase(locationtag.length() - 1, 1); + // walk the chain of parents and add them to the search path + swsearch = make_software_searchpath(swlist, *swinfo, parents); + } + else + { + swsearch.emplace_back(util::string_format("%s" PATH_SEPARATOR "%s", swlist.list_name(), swname)); + swsearch.emplace_back(swname); } + // this is convenient for CD-only lists so you don't need an extra level of directories containing one file each + disksearch.emplace_back(swlist.list_name()); - /* loop until we hit the end */ - for (region = start_region; region != nullptr; region = rom_next_region(region)) + // loop until we hit the end + std::function<const rom_entry * ()> next_parent; + chd_file::open_parent_func open_parent; + for (const rom_entry *region = start_region; region != nullptr; region = rom_next_region(region)) { u32 regionlength = ROMREGION_GETLENGTH(region); - regiontag = device.subtag(ROMREGION_GETTAG(region)); + std::string regiontag = device.subtag(region->name()); LOG("Processing region \"%s\" (length=%X)\n", regiontag.c_str(), regionlength); - /* the first entry must be a region */ + // the first entry must be a region assert(ROMENTRY_ISREGION(region)); - /* if this is a device region, override with the device width and endianness */ + // if this is a device region, override with the device width and endianness endianness_t endianness = ROMREGION_ISBIGENDIAN(region) ? ENDIANNESS_BIG : ENDIANNESS_LITTLE; u8 width = ROMREGION_GETWIDTH(region) / 8; - memory_region *memregion = machine().root_device().memregion(regiontag.c_str()); - if (memregion != nullptr) + memory_region *memregion = machine().root_device().memregion(regiontag); + if (memregion) { - normalize_flags_for_device(regiontag.c_str(), width, endianness); + normalize_flags_for_device(regiontag, width, endianness); - /* clear old region (todo: should be moved to an image unload function) */ + // clear old region (TODO: should be moved to an image unload function) machine().memory().region_free(memregion->name()); } - /* remember the base and length */ - m_region = machine().memory().region_alloc(regiontag.c_str(), regionlength, width, endianness); - LOG("Allocated %X bytes @ %p\n", m_region->bytes(), m_region->base()); - - /* clear the region if it's requested */ - if (ROMREGION_ISERASE(region)) - memset(m_region->base(), ROMREGION_GETERASEVAL(region), m_region->bytes()); - - /* or if it's sufficiently small (<= 4MB) */ - else if (m_region->bytes() <= 0x400000) - memset(m_region->base(), 0, m_region->bytes()); + // remember the base and length + memregion = machine().memory().region_alloc(regiontag, regionlength, width, endianness); + LOG("Allocated %X bytes @ %p\n", memregion->bytes(), memregion->base()); + if (ROMREGION_ISERASE(region)) // clear the region if it's requested + memset(memregion->base(), ROMREGION_GETERASEVAL(region), memregion->bytes()); + else if (memregion->bytes() <= 0x400000) // or if it's sufficiently small (<= 4MB) + memset(memregion->base(), 0, memregion->bytes()); #ifdef MAME_DEBUG - /* if we're debugging, fill region with random data to catch errors */ - else - fill_random(m_region->base(), m_region->bytes()); + else // if we're debugging, fill region with random data to catch errors + fill_random(memregion->base(), memregion->bytes()); #endif - /* update total number of roms */ + // update total number of roms for (const rom_entry *rom = rom_first_file(region); rom != nullptr; rom = rom_next_file(rom)) { m_romstotal++; m_romstotalsize += rom_file_size(rom); } - /* now process the entries in the region */ + // now process the entries in the region if (ROMREGION_ISROMDATA(region)) - process_rom_entries(locationtag.c_str(), region, region + 1, &device, true); + { + process_rom_entries(device, swsearch, 0U, *memregion, region, region + 1, true); + } else if (ROMREGION_ISDISKDATA(region)) - process_disk_entries(regiontag.c_str(), region, region + 1, locationtag.c_str()); + { + if (!next_parent) + { + if (!parents.empty()) + next_parent = next_parent_software(parents); + else + next_parent = [] () { return nullptr; }; + open_parent = open_parent_disk(machine().options(), { swsearch, disksearch }, next_parent); + } + process_disk_entries({ swsearch, disksearch }, regiontag, region + 1, next_parent, open_parent); + } } - /* now go back and post-process all the regions */ - for (region = start_region; region != nullptr; region = rom_next_region(region)) - { - regiontag = device.subtag(ROMREGION_GETTAG(region)); - region_post_process(regiontag.c_str(), ROMREGION_ISINVERTED(region)); - } + // now go back and post-process all the regions + for (const rom_entry *region = start_region; region != nullptr; region = rom_next_region(region)) + region_post_process(device.memregion(region->name()), ROMREGION_ISINVERTED(region)); - /* display the results and exit */ + // display the results and exit display_rom_load_results(true); } @@ -1398,68 +1469,83 @@ void rom_load_manager::load_software_part_region(device_t &device, software_list void rom_load_manager::process_region_list() { - std::string regiontag; - - /* loop until we hit the end */ - device_iterator deviter(machine().root_device()); + // loop until we hit the end + device_enumerator deviter(machine().root_device()); + std::vector<std::string> searchpath; for (device_t &device : deviter) + { + searchpath.clear(); + std::function<const rom_entry * ()> next_parent; + chd_file::open_parent_func open_parent; for (const rom_entry *region = rom_first_region(device); region != nullptr; region = rom_next_region(region)) { u32 regionlength = ROMREGION_GETLENGTH(region); - regiontag = rom_region_name(device, region); + std::string regiontag = device.subtag(region->name()); LOG("Processing region \"%s\" (length=%X)\n", regiontag.c_str(), regionlength); - /* the first entry must be a region */ + // the first entry must be a region assert(ROMENTRY_ISREGION(region)); if (ROMREGION_ISROMDATA(region)) { - /* if this is a device region, override with the device width and endianness */ + // if this is a device region, override with the device width and endianness u8 width = ROMREGION_GETWIDTH(region) / 8; endianness_t endianness = ROMREGION_ISBIGENDIAN(region) ? ENDIANNESS_BIG : ENDIANNESS_LITTLE; - normalize_flags_for_device(regiontag.c_str(), width, endianness); - - /* remember the base and length */ - m_region = machine().memory().region_alloc(regiontag.c_str(), regionlength, width, endianness); - LOG("Allocated %X bytes @ %p\n", m_region->bytes(), m_region->base()); + normalize_flags_for_device(regiontag, width, endianness); - /* clear the region if it's requested */ - if (ROMREGION_ISERASE(region)) - memset(m_region->base(), ROMREGION_GETERASEVAL(region), m_region->bytes()); - - /* or if it's sufficiently small (<= 4MB) */ - else if (m_region->bytes() <= 0x400000) - memset(m_region->base(), 0, m_region->bytes()); + // remember the base and length + memory_region *const memregion = machine().memory().region_alloc(regiontag, regionlength, width, endianness); + LOG("Allocated %X bytes @ %p\n", memregion->bytes(), memregion->base()); + if (ROMREGION_ISERASE(region)) // clear the region if it's requested + memset(memregion->base(), ROMREGION_GETERASEVAL(region), memregion->bytes()); + else if (memregion->bytes() <= 0x400000) // or if it's sufficiently small (<= 4MB) + memset(memregion->base(), 0, memregion->bytes()); #ifdef MAME_DEBUG - /* if we're debugging, fill region with random data to catch errors */ - else - fill_random(m_region->base(), m_region->bytes()); + else // if we're debugging, fill region with random data to catch errors + fill_random(memregion->base(), memregion->bytes()); #endif - /* now process the entries in the region */ - process_rom_entries(device.shortname(), region, region + 1, &device, false); + // now process the entries in the region + if (searchpath.empty()) + searchpath = device.searchpath(); + assert(!searchpath.empty()); + process_rom_entries(device, searchpath, device.system_bios(), *memregion, region, region + 1, false); } else if (ROMREGION_ISDISKDATA(region)) - process_disk_entries(regiontag.c_str(), region, region + 1, nullptr); + { + if (searchpath.empty()) + searchpath = device.searchpath(); + assert(!searchpath.empty()); + if (!next_parent) + { + driver_device const *const driver(dynamic_cast<driver_device const *>(&device)); + if (driver) + next_parent = next_parent_system(driver->system()); + else + next_parent = next_parent_device(device, machine().options()); + open_parent = open_parent_disk(machine().options(), { searchpath }, next_parent); + } + process_disk_entries({ searchpath }, regiontag, region + 1, next_parent, open_parent); + } } + } - /* now go back and post-process all the regions */ + // now go back and post-process all the regions for (device_t &device : deviter) for (const rom_entry *region = rom_first_region(device); region != nullptr; region = rom_next_region(region)) - { - regiontag = rom_region_name(device, region); - region_post_process(regiontag.c_str(), ROMREGION_ISINVERTED(region)); - } + region_post_process(device.memregion(region->name()), ROMREGION_ISINVERTED(region)); - /* and finally register all per-game parameters */ + // and finally register all per-game parameters for (device_t &device : deviter) + { for (const rom_entry *param = rom_first_parameter(device); param != nullptr; param = rom_next_parameter(param)) { - regiontag = rom_parameter_name(device, param); - machine().parameters().add(regiontag, rom_parameter_value(param)); + std::string regiontag = device.subtag(param->name()); + machine().parameters().add(regiontag, param->hashdata()); } + } } @@ -1470,10 +1556,20 @@ void rom_load_manager::process_region_list() rom_load_manager::rom_load_manager(running_machine &machine) : m_machine(machine) + , m_warnings(0) + , m_knownbad(0) + , m_errors(0) + , m_romsloaded(0) + , m_romstotal(0) + , m_romsloadedsize(0) + , m_romstotalsize(0) + , m_chd_list() + , m_errorstring() + , m_softwarningstring() { // figure out which BIOS we are using - std::map<std::string, std::string> card_bios; - for (device_t &device : device_iterator(machine.config().root_device())) + std::map<std::string_view, std::string> card_bios; + for (device_t &device : device_enumerator(machine.config().root_device())) { device_slot_interface const *const slot(dynamic_cast<device_slot_interface *>(&device)); if (slot) @@ -1481,7 +1577,7 @@ rom_load_manager::rom_load_manager(running_machine &machine) device_t const *const card(slot->get_card_device()); slot_option const &slot_opt(machine.options().slot_option(slot->slot_name())); if (card && !slot_opt.bios().empty()) - card_bios.emplace(std::make_pair(std::string(card->tag()), slot_opt.bios())); + card_bios.emplace(card->tag(), slot_opt.bios()); } if (device.rom_region()) @@ -1500,7 +1596,7 @@ rom_load_manager::rom_load_manager(running_machine &machine) card_bios.erase(found); } } - determine_bios_rom(device, specbios.c_str()); + determine_bios_rom(device, specbios); } } diff --git a/src/emu/romload.h b/src/emu/romload.h index ae78d409932..f655b81bf78 100644 --- a/src/emu/romload.h +++ b/src/emu/romload.h @@ -1,20 +1,28 @@ // license:BSD-3-Clause -// copyright-holders:Nicola Salmoria,Aaron Giles +// copyright-holders:Nicola Salmoria,Aaron Giles,Vas Crabb /********************************************************************* romload.h ROM loading functions. -*********************************************************************/ -#pragma once +*********************************************************************/ #ifndef MAME_EMU_ROMLOAD_H #define MAME_EMU_ROMLOAD_H +#pragma once + #include "chd.h" +#include <functional> +#include <initializer_list> +#include <memory> +#include <string> +#include <string_view> +#include <system_error> #include <type_traits> +#include <vector> /*************************************************************************** @@ -46,7 +54,6 @@ template <typename T> inline bool ROMENTRY_ISPARAMETER(T const &r) { return template <typename T> inline bool ROMENTRY_ISREGIONEND(T const &r) { return ROMENTRY_ISREGION(r) || ROMENTRY_ISPARAMETER(r) || ROMENTRY_ISEND(r); } /* ----- per-region macros ----- */ -#define ROMREGION_GETTAG(r) ((r)->name().c_str()) template <typename T> inline u32 ROMREGION_GETLENGTH(T const &r) { return ROMENTRY_UNWRAP(r).get_length(); } template <typename T> inline u32 ROMREGION_GETFLAGS(T const &r) { return ROMENTRY_UNWRAP(r).get_flags(); } template <typename T> inline u32 ROMREGION_GETWIDTH(T const &r) { return 8 << ((ROMREGION_GETFLAGS(r) & ROMREGION_WIDTHMASK) >> 8); } @@ -66,7 +73,6 @@ template <typename T> inline bool ROMREGION_ISDISKDATA(T const &r) { return template <typename T> inline u32 ROM_GETOFFSET(T const &r) { return ROMENTRY_UNWRAP(r).get_offset(); } template <typename T> inline u32 ROM_GETLENGTH(T const &r) { return ROMENTRY_UNWRAP(r).get_length(); } template <typename T> inline u32 ROM_GETFLAGS(T const &r) { return ROMENTRY_UNWRAP(r).get_flags(); } -#define ROM_GETHASHDATA(r) ((r)->hashdata().c_str()) template <typename T> inline bool ROM_ISOPTIONAL(T const &r) { return (ROM_GETFLAGS(r) & ROM_OPTIONALMASK) == ROM_OPTIONAL; } template <typename T> inline u32 ROM_GETGROUPSIZE(T const &r) { return ((ROM_GETFLAGS(r) & ROM_GROUPMASK) >> 8) + 1; } template <typename T> inline u32 ROM_GETSKIPCOUNT(T const &r) { return (ROM_GETFLAGS(r) & ROM_SKIPMASK) >> 12; } @@ -98,11 +104,11 @@ protected: const_entry_iterator &operator=(const_entry_iterator &&) noexcept = default; public: - typedef T value_type; - typedef value_type const *pointer; - typedef value_type const &reference; - typedef std::ptrdiff_t difference_type; - typedef std::forward_iterator_tag iterator_category; + using value_type = T; + using pointer = value_type const *; + using reference = value_type const &; + using difference_type = std::ptrdiff_t; + using iterator_category = std::forward_iterator_tag; reference operator*() const noexcept { return reinterpret_cast<reference>(*m_data); } pointer operator->() const noexcept { return reinterpret_cast<pointer>(m_data); } @@ -382,17 +388,18 @@ class rom_load_manager class open_chd { public: - open_chd(const char *region) : m_region(region) { } + open_chd(std::string_view region) : m_region(region) { } + open_chd(std::string &®ion) : m_region(std::move(region)) { } - const char *region() const { return m_region.c_str(); } + std::string_view region() const { return m_region; } chd_file &chd() { return m_diffchd.opened() ? m_diffchd : m_origchd; } chd_file &orig_chd() { return m_origchd; } chd_file &diff_chd() { return m_diffchd; } private: - std::string m_region; /* disk region we came from */ - chd_file m_origchd; /* handle to the original CHD */ - chd_file m_diffchd; /* handle to the diff CHD */ + std::string m_region; // disk region we came from + chd_file m_origchd; // handle to the original CHD + chd_file m_diffchd; // handle to the diff CHD }; public: @@ -413,35 +420,70 @@ public: /* ----- disk handling ----- */ /* return a pointer to the CHD file associated with the given region */ - chd_file *get_disk_handle(const char *region); + chd_file *get_disk_handle(std::string_view region); /* set a pointer to the CHD file associated with the given region */ - int set_disk_handle(const char *region, const char *fullpath); + std::error_condition set_disk_handle(std::string_view region, std::string_view fullpath); + + void load_software_part_region(device_t &device, software_list_device &swlist, std::string_view swname, const rom_entry *start_region); - void load_software_part_region(device_t &device, software_list_device &swlist, const char *swname, const rom_entry *start_region); + /* get search path for a software item */ + static std::vector<std::string> get_software_searchpath(software_list_device &swlist, const software_info &swinfo); + + /* open a disk image, searching up the parent and loading by checksum */ + static std::error_condition open_disk_image(emu_options &options, const device_t &device, const rom_entry *romp, chd_file &image_chd); + static std::error_condition open_disk_image(emu_options &options, software_list_device &swlist, const software_info &swinfo, const rom_entry *romp, chd_file &image_chd); private: - void determine_bios_rom(device_t &device, const char *specbios); + using search_paths = std::initializer_list<std::reference_wrapper<const std::vector<std::string> > >; + + void determine_bios_rom(device_t &device, std::string_view specbios); void count_roms(); void fill_random(u8 *base, u32 length); - void handle_missing_file(const rom_entry *romp, std::string tried_file_names, chd_error chderr); + void handle_missing_file(const rom_entry *romp, const std::vector<std::string> &tried_file_names, std::error_condition chderr); void dump_wrong_and_correct_checksums(const util::hash_collection &hashes, const util::hash_collection &acthashes); - void verify_length_and_hash(const char *name, u32 explength, const util::hash_collection &hashes); + void verify_length_and_hash(emu_file *file, std::string_view name, u32 explength, const util::hash_collection &hashes); void display_loading_rom_message(const char *name, bool from_list); void display_rom_load_results(bool from_list); - void region_post_process(const char *rgntag, bool invert); - int open_rom_file(const char *regiontag, const rom_entry *romp, std::string &tried_file_names, bool from_list); - int rom_fread(u8 *buffer, int length, const rom_entry *parent_region); - int read_rom_data(const rom_entry *parent_region, const rom_entry *romp); - void fill_rom_data(const rom_entry *romp); - void copy_rom_data(const rom_entry *romp); - void process_rom_entries(const char *regiontag, const rom_entry *parent_region, const rom_entry *romp, device_t *device, bool from_list); - chd_error open_disk_diff(emu_options &options, const rom_entry *romp, chd_file &source, chd_file &diff_chd); - void process_disk_entries(const char *regiontag, const rom_entry *parent_region, const rom_entry *romp, const char *locationtag); - void normalize_flags_for_device(const char *rgntag, u8 &width, endianness_t &endian); + void region_post_process(memory_region *region, bool invert); + std::unique_ptr<emu_file> open_rom_file( + const std::vector<std::string> &searchpath, + const rom_entry *romp, + std::vector<std::string> &tried_file_names, + bool from_list); + std::unique_ptr<emu_file> open_rom_file( + const std::vector<std::string> &paths, + std::vector<std::string> &tried, + bool has_crc, + u32 crc, + std::string_view name, + std::error_condition &filerr); + int rom_fread(emu_file *file, u8 *buffer, int length, const rom_entry *parent_region); + int read_rom_data(emu_file *file, memory_region ®ion, const rom_entry *parent_region, const rom_entry *romp); + void fill_rom_data(memory_region ®ion, const rom_entry *romp); + void copy_rom_data(device_t &device, memory_region ®ion, const rom_entry *romp); + void process_rom_entries( + device_t &device, + const std::vector<std::string> &searchpath, + u8 bios, + memory_region ®ion, + const rom_entry *parent_region, + const rom_entry *romp, + bool from_list); + std::error_condition open_disk_diff( + emu_options &options, + const rom_entry *romp, + chd_file &source, + chd_file &diff_chd); + void process_disk_entries( + search_paths searchpath, + std::string_view regiontag, + const rom_entry *romp, + std::function<const rom_entry * ()> next_parent, + const chd_file::open_parent_func &open_parent); + void normalize_flags_for_device(std::string_view rgntag, u8 &width, endianness_t &endian); void process_region_list(); - // internal state running_machine & m_machine; // reference to our machine @@ -451,14 +493,11 @@ private: int m_romsloaded; // current ROMs loaded count int m_romstotal; // total number of ROMs to read - u32 m_romsloadedsize; // total size of ROMs loaded so far - u32 m_romstotalsize; // total size of ROMs to read + u64 m_romsloadedsize; // total size of ROMs loaded so far + u64 m_romstotalsize; // total size of ROMs to read - std::unique_ptr<emu_file> m_file; /* current file */ std::vector<std::unique_ptr<open_chd>> m_chd_list; /* disks */ - memory_region * m_region; // info about current region - std::string m_errorstring; // error string std::string m_softwarningstring; // software warning string }; @@ -466,10 +505,9 @@ private: /* ----- Helpers ----- */ -std::unique_ptr<emu_file> common_process_file(emu_options &options, const char *location, bool has_crc, u32 crc, const rom_entry *romp, osd_file::error &filerr); - /* return pointer to the first ROM region within a source */ const rom_entry *rom_first_region(const device_t &device); +const rom_entry *rom_first_region(const rom_entry *romp); /* return pointer to the next ROM region within a source */ const rom_entry *rom_next_region(const rom_entry *romp); @@ -483,26 +521,13 @@ const rom_entry *rom_next_file(const rom_entry *romp); /* return the expected size of a file given the ROM description */ u32 rom_file_size(const rom_entry *romp); -/* return the appropriate name for a rom region */ -std::string rom_region_name(const device_t &device, const rom_entry *romp); - /* return pointer to the first per-game parameter */ const rom_entry *rom_first_parameter(const device_t &device); /* return pointer to the next per-game parameter */ const rom_entry *rom_next_parameter(const rom_entry *romp); -/* return the appropriate name for a per-game parameter */ -std::string rom_parameter_name(const device_t &device, const rom_entry *romp); - -/* return the value for a per-game parameter */ -std::string rom_parameter_value(const rom_entry *romp); - // builds a rom_entry vector from a tiny_rom_entry array std::vector<rom_entry> rom_build_entries(const tiny_rom_entry *tinyentries); - -/* open a disk image, searching up the parent and loading by checksum */ -int open_disk_image(emu_options &options, const game_driver *gamedrv, const rom_entry *romp, chd_file &image_chd, const char *locationtag); - #endif // MAME_EMU_ROMLOAD_H diff --git a/src/emu/save.cpp b/src/emu/save.cpp index a8a59c90817..35599c2e679 100644 --- a/src/emu/save.cpp +++ b/src/emu/save.cpp @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles /*************************************************************************** - save.c + save.cpp Save state management functions. @@ -24,7 +24,11 @@ #include "emu.h" #include "emuopts.h" -#include "coreutil.h" + +#include "main.h" + +#include "util/ioprocs.h" +#include "util/ioprocsfilter.h" //************************************************************************** @@ -63,7 +67,7 @@ enum save_manager::save_manager(running_machine &machine) : m_machine(machine) , m_reg_allowed(true) - , m_illegal_regs(0) + , m_supported(false) { m_rewind = std::make_unique<rewinder>(*this); } @@ -82,14 +86,14 @@ void save_manager::allow_registration(bool allowed) { // look for duplicates std::sort(m_entry_list.begin(), m_entry_list.end(), - [](std::unique_ptr<state_entry> const& a, std::unique_ptr<state_entry> const& b) { return a->m_name < b->m_name; }); + [] (std::unique_ptr<state_entry> const& a, std::unique_ptr<state_entry> const& b) { return a->m_name < b->m_name; }); int dupes_found = 0; - for (int i = 0; i < m_entry_list.size() - 1; i++) + for (int i = 1; i < m_entry_list.size(); i++) { - if (m_entry_list[i]->m_name == m_entry_list[i + 1]->m_name) + if (m_entry_list[i - 1]->m_name == m_entry_list[i]->m_name) { - osd_printf_error("Duplicate save state registration entry (%s)\n", m_entry_list[i]->m_name.c_str()); + osd_printf_error("Duplicate save state registration entry (%s)\n", m_entry_list[i]->m_name); dupes_found++; } } @@ -97,6 +101,16 @@ void save_manager::allow_registration(bool allowed) if (dupes_found) fatalerror("%d duplicate save state entries found.\n", dupes_found); + m_supported = true; + for (device_t &device : device_enumerator(machine().root_device())) + { + if (device.type().emulation_flags() & device_t::flags::SAVE_UNSUPPORTED) + { + m_supported = false; + break; + } + } + dump_registry(); // everything is registered by now, evaluate the savestate size @@ -110,7 +124,7 @@ void save_manager::allow_registration(bool allowed) // index //------------------------------------------------- -const char *save_manager::indexed_item(int index, void *&base, u32 &valsize, u32 &valcount) const +const char *save_manager::indexed_item(int index, void *&base, u32 &valsize, u32 &valcount, u32 &blockcount, u32 &stride) const { if (index >= m_entry_list.size() || index < 0) return nullptr; @@ -119,6 +133,8 @@ const char *save_manager::indexed_item(int index, void *&base, u32 &valsize, u32 base = entry->m_data; valsize = entry->m_typesize; valcount = entry->m_typecount; + blockcount = entry->m_blockcount; + stride = entry->m_stride; return entry->m_name.c_str(); } @@ -171,29 +187,28 @@ void save_manager::register_postload(save_prepost_delegate func) // memory //------------------------------------------------- -void save_manager::save_memory(device_t *device, const char *module, const char *tag, u32 index, const char *name, void *val, u32 valsize, u32 valcount) +void save_manager::save_memory(device_t *device, const char *module, const char *tag, u32 index, const char *name, void *val, u32 valsize, u32 valcount, u32 blockcount, u32 stride) { assert(valsize == 1 || valsize == 2 || valsize == 4 || valsize == 8); + assert(((blockcount <= 1) && (stride == 0)) || (stride >= valcount)); // check for invalid timing if (!m_reg_allowed) { machine().logerror("Attempt to register save state entry after state registration is closed!\nModule %s tag %s name %s\n", module, tag, name); - if (machine().system().flags & machine_flags::SUPPORTS_SAVE) - fatalerror("Attempt to register save state entry after state registration is closed!\nModule %s tag %s name %s\n", module, tag, name); - m_illegal_regs++; + fatalerror("Attempt to register save state entry after state registration is closed!\nModule %s tag %s name %s\n", module, tag, name); return; } // create the full name std::string totalname; - if (tag != nullptr) + if (tag) totalname = string_format("%s/%s/%X/%s", module, tag, index, name); else totalname = string_format("%s/%X/%s", module, index, name); // insert us into the list - m_entry_list.emplace_back(std::make_unique<state_entry>(val, totalname.c_str(), device, module, tag ? tag : "", index, valsize, valcount)); + m_entry_list.emplace_back(std::make_unique<state_entry>(val, std::move(totalname), device, module, tag ? tag : "", index, valsize, valcount, blockcount, stride)); } @@ -202,20 +217,20 @@ void save_manager::save_memory(device_t *device, const char *module, const char // state //------------------------------------------------- -save_error save_manager::check_file(running_machine &machine, emu_file &file, const char *gamename, void (CLIB_DECL *errormsg)(const char *fmt, ...)) +save_error save_manager::check_file(running_machine &machine, util::core_file &file, const char *gamename, void (CLIB_DECL *errormsg)(const char *fmt, ...)) { // if we want to validate the signature, compute it u32 sig; sig = machine.save().signature(); // seek to the beginning and read the header - file.compress(FCOMPRESS_NONE); file.seek(0, SEEK_SET); u8 header[HEADER_SIZE]; - if (file.read(header, sizeof(header)) != sizeof(header)) + auto const [err, actual] = read(file, header, sizeof(header)); + if (err || (actual != sizeof(header))) { if (errormsg != nullptr) - (*errormsg)("Could not read %s save file header",emulator_info::get_appname()); + (*errormsg)("Could not read %s save file header", emulator_info::get_appname()); return STATERR_READ_ERROR; } @@ -237,71 +252,173 @@ void save_manager::dispatch_postload() //------------------------------------------------- +// dispatch_presave - invoke all registered +// presave callbacks for updates +//------------------------------------------------- + +void save_manager::dispatch_presave() +{ + for (auto &func : m_presave_list) + func->m_func(); +} + + +//------------------------------------------------- +// write_file - writes the data to a file +//------------------------------------------------- + +save_error save_manager::write_file(util::core_file &file) +{ + util::write_stream::ptr writer; + save_error err = do_write( + [] (size_t total_size) { return true; }, + [&writer] (const void *data, size_t size) + { + auto const [filerr, written] = write(*writer, data, size); + return !filerr; + }, + [&file, &writer] () + { + if (file.seek(0, SEEK_SET)) + return false; + util::core_file::ptr proxy; + std::error_condition filerr = util::core_file::open_proxy(file, proxy); + writer = std::move(proxy); + return !filerr && writer; + }, + [&file, &writer] () + { + writer = util::zlib_write(file, 6, 16384); + return bool(writer); + }); + return (STATERR_NONE != err) ? err : writer->finalize() ? STATERR_WRITE_ERROR : STATERR_NONE; +} + + +//------------------------------------------------- // read_file - read the data from a file //------------------------------------------------- -save_error save_manager::read_file(emu_file &file) +save_error save_manager::read_file(util::core_file &file) { - // if we have illegal registrations, return an error - if (m_illegal_regs > 0) - return STATERR_ILLEGAL_REGISTRATIONS; + util::read_stream::ptr reader; + return do_read( + [] (size_t total_size) { return true; }, + [&reader] (void *data, size_t size) + { + auto const [filerr, actual] = read(*reader, data, size); + return !filerr && (actual == size); + }, + [&file, &reader] () + { + if (file.seek(0, SEEK_SET)) + return false; + util::core_file::ptr proxy; + std::error_condition filerr = util::core_file::open_proxy(file, proxy); + reader = std::move(proxy); + return !filerr && reader; + }, + [&file, &reader] () + { + reader = util::zlib_read(file, 16384); + return bool(reader); + }); +} - // read the header and turn on compression for the rest of the file - file.compress(FCOMPRESS_NONE); - file.seek(0, SEEK_SET); - u8 header[HEADER_SIZE]; - if (file.read(header, sizeof(header)) != sizeof(header)) - return STATERR_READ_ERROR; - file.compress(FCOMPRESS_MEDIUM); - // verify the header and report an error if it doesn't match - u32 sig = signature(); - if (validate_header(header, machine().system().name, sig, nullptr, "Error: ") != STATERR_NONE) - return STATERR_INVALID_HEADER; +//------------------------------------------------- +// write_stream - write the current machine state +// to an output stream +//------------------------------------------------- - // determine whether or not to flip the data when done - bool flip = NATIVE_ENDIAN_VALUE_LE_BE((header[9] & SS_MSB_FIRST) != 0, (header[9] & SS_MSB_FIRST) == 0); +save_error save_manager::write_stream(std::ostream &str) +{ + return do_write( + [] (size_t total_size) { return true; }, + [&str] (const void *data, size_t size) + { + return bool(str.write(reinterpret_cast<const char *>(data), size)); + }, + [] () { return true; }, + [] () { return true; }); +} - // read all the data, flipping if necessary - for (auto &entry : m_entry_list) - { - u32 totalsize = entry->m_typesize * entry->m_typecount; - if (file.read(entry->m_data, totalsize) != totalsize) - return STATERR_READ_ERROR; - // handle flipping - if (flip) - entry->flip_data(); - } +//------------------------------------------------- +// read_stream - restore the machine state from +// an input stream +//------------------------------------------------- - // call the post-load functions - dispatch_postload(); +save_error save_manager::read_stream(std::istream &str) +{ + return do_read( + [] (size_t total_size) { return true; }, + [&str] (void *data, size_t size) + { + return bool(str.read(reinterpret_cast<char *>(data), size)); + }, + [] () { return true; }, + [] () { return true; }); +} - return STATERR_NONE; + +//------------------------------------------------- +// write_buffer - write the current machine state +// to an allocated buffer +//------------------------------------------------- + +save_error save_manager::write_buffer(void *buf, size_t size) +{ + return do_write( + [size] (size_t total_size) { return size == total_size; }, + [ptr = reinterpret_cast<u8 *>(buf)] (const void *data, size_t size) mutable + { + memcpy(ptr, data, size); + ptr += size; + return true; + }, + [] () { return true; }, + [] () { return true; }); } //------------------------------------------------- -// dispatch_presave - invoke all registered -// presave callbacks for updates +// read_buffer - restore the machine state from a +// buffer //------------------------------------------------- -void save_manager::dispatch_presave() +save_error save_manager::read_buffer(const void *buf, size_t size) { - for (auto &func : m_presave_list) - func->m_func(); + const u8 *ptr = reinterpret_cast<const u8 *>(buf); + const u8 *const end = ptr + size; + return do_read( + [size] (size_t total_size) { return size == total_size; }, + [&ptr, &end] (void *data, size_t size) -> bool + { + if ((ptr + size) > end) + return false; + memcpy(data, ptr, size); + ptr += size; + return true; + }, + [] () { return true; }, + [] () { return true; }); } //------------------------------------------------- -// write_file - writes the data to a file +// do_write - serialisation logic //------------------------------------------------- -save_error save_manager::write_file(emu_file &file) +template <typename T, typename U, typename V, typename W> +inline save_error save_manager::do_write(T check_space, U write_block, V start_header, W start_data) { - // if we have illegal registrations, return an error - if (m_illegal_regs > 0) - return STATERR_ILLEGAL_REGISTRATIONS; + // check for sufficient space + size_t total_size = HEADER_SIZE; + for (const auto &entry : m_entry_list) + total_size += entry->m_typesize * entry->m_typecount * entry->m_blockcount; + if (!check_space(total_size)) + return STATERR_WRITE_ERROR; // generate the header u8 header[HEADER_SIZE]; @@ -313,11 +430,8 @@ save_error save_manager::write_file(emu_file &file) *(u32 *)&header[0x1c] = little_endianize_int32(sig); // write the header and turn on compression for the rest of the file - file.compress(FCOMPRESS_NONE); - file.seek(0, SEEK_SET); - if (file.write(header, sizeof(header)) != sizeof(header)) + if (!start_header() || !write_block(header, sizeof(header)) || !start_data()) return STATERR_WRITE_ERROR; - file.compress(FCOMPRESS_MEDIUM); // call the pre-save functions dispatch_presave(); @@ -325,10 +439,60 @@ save_error save_manager::write_file(emu_file &file) // then write all the data for (auto &entry : m_entry_list) { - u32 totalsize = entry->m_typesize * entry->m_typecount; - if (file.write(entry->m_data, totalsize) != totalsize) - return STATERR_WRITE_ERROR; + const u32 blocksize = entry->m_typesize * entry->m_typecount; + const u8 *data = reinterpret_cast<const u8 *>(entry->m_data); + for (u32 b = 0; entry->m_blockcount > b; ++b, data += entry->m_stride) + if (!write_block(data, blocksize)) + return STATERR_WRITE_ERROR; + } + return STATERR_NONE; +} + + +//------------------------------------------------- +// do_read - deserialisation logic +//------------------------------------------------- + +template <typename T, typename U, typename V, typename W> +inline save_error save_manager::do_read(T check_length, U read_block, V start_header, W start_data) +{ + // check for sufficient space + size_t total_size = HEADER_SIZE; + for (const auto &entry : m_entry_list) + total_size += entry->m_typesize * entry->m_typecount * entry->m_blockcount; + if (!check_length(total_size)) + return STATERR_READ_ERROR; + + // read the header and turn on compression for the rest of the file + u8 header[HEADER_SIZE]; + if (!start_header() || !read_block(header, sizeof(header)) || !start_data()) + return STATERR_READ_ERROR; + + // verify the header and report an error if it doesn't match + u32 sig = signature(); + if (validate_header(header, machine().system().name, sig, nullptr, "Error: ") != STATERR_NONE) + return STATERR_INVALID_HEADER; + + // determine whether or not to flip the data when done + const bool flip = NATIVE_ENDIAN_VALUE_LE_BE((header[9] & SS_MSB_FIRST) != 0, (header[9] & SS_MSB_FIRST) == 0); + + // read all the data, flipping if necessary + for (auto &entry : m_entry_list) + { + const u32 blocksize = entry->m_typesize * entry->m_typecount; + u8 *data = reinterpret_cast<u8 *>(entry->m_data); + for (u32 b = 0; entry->m_blockcount > b; ++b, data += entry->m_stride) + if (!read_block(data, blocksize)) + return STATERR_READ_ERROR; + + // handle flipping + if (flip) + entry->flip_data(); } + + // call the post-load functions + dispatch_postload(); + return STATERR_NONE; } @@ -341,19 +505,21 @@ save_error save_manager::write_file(emu_file &file) u32 save_manager::signature() const { // iterate over entries - u32 crc = 0; + util::crc32_creator crc; for (auto &entry : m_entry_list) { // add the entry name to the CRC - crc = core_crc32(crc, (u8 *)entry->m_name.c_str(), entry->m_name.length()); + crc.append(entry->m_name.data(), entry->m_name.length()); // add the type and size to the CRC - u32 temp[2]; - temp[0] = little_endianize_int32(entry->m_typecount); - temp[1] = little_endianize_int32(entry->m_typesize); - crc = core_crc32(crc, (u8 *)&temp[0], sizeof(temp)); + u32 temp[4]; + temp[0] = little_endianize_int32(entry->m_typesize); + temp[1] = little_endianize_int32(entry->m_typecount); + temp[2] = little_endianize_int32(entry->m_blockcount); + temp[3] = 0; + crc.append(&temp[0], sizeof(temp)); } - return crc; + return crc.finish(); } @@ -365,7 +531,7 @@ u32 save_manager::signature() const void save_manager::dump_registry() const { for (auto &entry : m_entry_list) - LOG(("%s: %d x %d\n", entry->m_name.c_str(), entry->m_typesize, entry->m_typecount)); + LOG(("%s: %u x %u x %u (%u)\n", entry->m_name.c_str(), entry->m_typesize, entry->m_typecount, entry->m_blockcount, entry->m_stride)); } @@ -381,7 +547,7 @@ save_error save_manager::validate_header(const u8 *header, const char *gamename, if (memcmp(header, STATE_MAGIC_NUM, 8)) { if (errormsg != nullptr) - (*errormsg)("%sThis is not a %s save file", error_prefix,emulator_info::get_appname()); + (*errormsg)("%sThis is not a %s save file", error_prefix, emulator_info::get_appname()); return STATERR_INVALID_HEADER; } @@ -454,9 +620,7 @@ size_t ram_state::get_size(save_manager &save) size_t totalsize = 0; for (auto &entry : save.m_entry_list) - { - totalsize += entry->m_typesize * entry->m_typecount; - } + totalsize += entry->m_typesize * entry->m_typecount * entry->m_blockcount; return totalsize + HEADER_SIZE; } @@ -473,39 +637,10 @@ save_error ram_state::save() m_valid = false; m_data.seekp(0); - // if we have illegal registrations, return an error - if (m_save.m_illegal_regs > 0) - return STATERR_ILLEGAL_REGISTRATIONS; - - // generate the header - u8 header[HEADER_SIZE]; - memcpy(&header[0], STATE_MAGIC_NUM, 8); - header[8] = SAVE_VERSION; - header[9] = NATIVE_ENDIAN_VALUE_LE_BE(0, SS_MSB_FIRST); - strncpy((char *)&header[0x0a], m_save.machine().system().name, 0x1c - 0x0a); - u32 sig = m_save.signature(); - *(u32 *)&header[0x1c] = little_endianize_int32(sig); - - // write the header - m_data.write((char *)header, sizeof(header)); - - // check for any errors - if (!m_data) - return STATERR_WRITE_ERROR; - - // call the pre-save functions - m_save.dispatch_presave(); - - // write all the data - for (auto &entry : m_save.m_entry_list) - { - u32 totalsize = entry->m_typesize * entry->m_typecount; - m_data.write((char *)entry->m_data, totalsize); - - // check for any errors - if (!m_data) - return STATERR_WRITE_ERROR; - } + // get the save manager to write state + const save_error err = m_save.write_stream(m_data); + if (err != STATERR_NONE) + return err; // final confirmation m_valid = true; @@ -525,45 +660,8 @@ save_error ram_state::load() // initialize m_data.seekg(0); - // if we have illegal registrations, return an error - if (m_save.m_illegal_regs > 0) - return STATERR_ILLEGAL_REGISTRATIONS; - - // read the header - u8 header[HEADER_SIZE]; - m_data.read((char *)header, sizeof(header)); - - // check for any errors - if (!m_data) - return STATERR_READ_ERROR; - - // verify the header and report an error if it doesn't match - u32 sig = m_save.signature(); - if (m_save.validate_header(header, m_save.machine().system().name, sig, nullptr, "Error: ") != STATERR_NONE) - return STATERR_INVALID_HEADER; - - // determine whether or not to flip the data when done - bool flip = NATIVE_ENDIAN_VALUE_LE_BE((header[9] & SS_MSB_FIRST) != 0, (header[9] & SS_MSB_FIRST) == 0); - - // read all the data, flipping if necessary - for (auto &entry : m_save.m_entry_list) - { - u32 totalsize = entry->m_typesize * entry->m_typecount; - m_data.read((char *)entry->m_data, totalsize); - - // check for any errors - if (!m_data) - return STATERR_READ_ERROR; - - // handle flipping - if (flip) - entry->flip_data(); - } - - // call the post-load functions - m_save.dispatch_postload(); - - return STATERR_NONE; + // get the save manager to load state + return m_save.read_stream(m_data); } @@ -811,11 +909,6 @@ void rewinder::report_error(save_error error, rewind_operation operation) switch (error) { // internal saveload failures - case STATERR_ILLEGAL_REGISTRATIONS: - m_save.machine().logerror("Rewind error: Unable to %s state due to illegal registrations.", opname); - m_save.machine().popmessage("Rewind error occured. See error.log for details."); - break; - case STATERR_INVALID_HEADER: m_save.machine().logerror("Rewind error: Unable to %s state due to an invalid header. " "Make sure the save state is correct for this machine.\n", opname); @@ -852,7 +945,7 @@ void rewinder::report_error(save_error error, rewind_operation operation) // success case STATERR_NONE: { - const u64 supported = m_save.machine().system().flags & MACHINE_SUPPORTS_SAVE; + const u64 supported = m_save.supported(); const char *const warning = supported || !m_first_time_warning ? "" : "Rewind warning: Save states are not officially supported for this machine.\n"; const char *const opnamed = (operation == rewind_operation::LOAD) ? "loaded" : "captured"; @@ -880,16 +973,20 @@ void rewinder::report_error(save_error error, rewind_operation operation) // state_entry - constructor //------------------------------------------------- -state_entry::state_entry(void *data, const char *name, device_t *device, const char *module, const char *tag, int index, u8 size, u32 count) +save_manager::state_entry::state_entry( + void *data, + std::string &&name, device_t *device, std::string &&module, std::string &&tag, int index, + u8 size, u32 valcount, u32 blockcount, u32 stride) : m_data(data) - , m_name(name) + , m_name(std::move(name)) , m_device(device) - , m_module(module) - , m_tag(tag) + , m_module(std::move(module)) + , m_tag(std::move(tag)) , m_index(index) , m_typesize(size) - , m_typecount(count) - , m_offset(0) + , m_typecount(valcount) + , m_blockcount(blockcount) + , m_stride(stride) { } @@ -899,31 +996,34 @@ state_entry::state_entry(void *data, const char *name, device_t *device, const c // block of data //------------------------------------------------- -void state_entry::flip_data() +void save_manager::state_entry::flip_data() { - u16 *data16; - u32 *data32; - u64 *data64; - int count; - - switch (m_typesize) + u8 *data = reinterpret_cast<u8 *>(m_data); + for (u32 b = 0; m_blockcount > b; ++b, data += m_stride) { + u16 *data16; + u32 *data32; + u64 *data64; + + switch (m_typesize) + { case 2: - data16 = (u16 *)m_data; - for (count = 0; count < m_typecount; count++) + data16 = reinterpret_cast<u16 *>(data); + for (u32 count = 0; count < m_typecount; count++) data16[count] = swapendian_int16(data16[count]); break; case 4: - data32 = (u32 *)m_data; - for (count = 0; count < m_typecount; count++) + data32 = reinterpret_cast<u32 *>(data); + for (u32 count = 0; count < m_typecount; count++) data32[count] = swapendian_int32(data32[count]); break; case 8: - data64 = (u64 *)m_data; - for (count = 0; count < m_typecount; count++) + data64 = reinterpret_cast<u64 *>(data); + for (u32 count = 0; count < m_typecount; count++) data64[count] = swapendian_int64(data64[count]); break; + } } } diff --git a/src/emu/save.h b/src/emu/save.h index 2b4798b9d4c..066f20f3330 100644 --- a/src/emu/save.h +++ b/src/emu/save.h @@ -18,6 +18,11 @@ #define MAME_EMU_SAVE_H #include <array> +#include <cassert> +#include <memory> +#include <string> +#include <type_traits> +#include <vector> @@ -29,7 +34,6 @@ enum save_error { STATERR_NONE, STATERR_NOT_FOUND, - STATERR_ILLEGAL_REGISTRATIONS, STATERR_INVALID_HEADER, STATERR_READ_ERROR, STATERR_WRITE_ERROR, @@ -46,43 +50,43 @@ enum save_error typedef named_delegate<void ()> save_prepost_delegate; -// use this to declare a given type is a simple, non-pointer type that can be -// saved; in general, this is intended only to be used for specific enum types -// defined by your device +/// \brief Declare a type as safe to automatically save/restore +/// +/// Use this to declare that a given type is a simple, non-pointer type +/// that can be saved and restored. In general, this should only be +/// be used for specific enum types that have fixed width integer types +/// as their storage classes. +/// \param TYPE The name of the type to declare safe to save. +/// \sa ALLOW_SAVE_TYPE_AND_VECTOR #define ALLOW_SAVE_TYPE(TYPE) \ - template<> struct save_manager::type_checker<TYPE> { static const bool is_atom = true; static const bool is_pointer = false; } - -// use this as above, but also to declare that std::vector<TYPE> is safe as well -#define ALLOW_SAVE_TYPE_AND_ARRAY(TYPE) \ - ALLOW_SAVE_TYPE(TYPE); \ - template<> inline void save_manager::save_item(device_t *device, const char *module, const char *tag, int index, std::vector<TYPE> &value, const char *name) { save_memory(device, module, tag, index, name, &value[0], sizeof(TYPE), value.size()); } + template <> struct save_manager::is_atom<TYPE> : public std::true_type { }; + +/// \brief Declare a type as safe to automatically save/restore, +/// including in \c std::vector instances +/// +/// Used the same way as #ALLOW_SAVE_TYPE, but also declares that +/// \c std::vector instances containing the type are safe to save. +/// that can be saved and restored. This must not be used if +/// \c std::vector is specialised in an incompatible way. +/// \param TYPE The name of the type to declare safe to save. +/// \sa ALLOW_SAVE_TYPE +#define ALLOW_SAVE_TYPE_AND_VECTOR(TYPE) \ + ALLOW_SAVE_TYPE(TYPE) \ + template <> struct save_manager::is_vector_safe<TYPE> : public std::true_type { }; + +/// \brief Helper for referring to members of structures in arrays +/// +/// Expands to the necessary reference, pointer to member and name to +/// refer to a structure member. +/// \param s Reference to a C array or \c std::array of structures. +/// \param m Name of the structure member to refer to. +#define STRUCT_MEMBER(s, m) s, &save_manager::pointer_unwrap<decltype(s)>::underlying_type::m, #s "." #m //************************************************************************** // TYPE DEFINITIONS //************************************************************************** -class state_entry -{ -public: - // construction/destruction - state_entry(void *data, const char *name, device_t *device, const char *module, const char *tag, int index, u8 size, u32 count); - - // helpers - void flip_data(); - - // state - void * m_data; // pointer to the memory to save/restore - std::string m_name; // full name - device_t * m_device; // associated device, nullptr if none - std::string m_module; // module name - std::string m_tag; // tag name - int m_index; // index - u8 m_typesize; // size of the raw data type - u32 m_typecount; // number of items - u32 m_offset; // offset within the final structure -}; - class ram_state; class rewinder; @@ -96,7 +100,7 @@ class save_manager static constexpr std::size_t SIZE = sizeof(underlying_type); static underlying_type *ptr(T &value) { return &value; } }; - template <typename T, std::size_t N> struct array_unwrap<T[N]> + template <typename T, std::size_t N> struct array_unwrap<T [N]> { using underlying_type = typename array_unwrap<T>::underlying_type; static constexpr std::size_t SAVE_COUNT = N * array_unwrap<T>::SAVE_COUNT; @@ -111,14 +115,42 @@ class save_manager static underlying_type *ptr(std::array<T, N> &value) { return array_unwrap<T>::ptr(value[0]); } }; - // type_checker is a set of templates to identify valid save types - template<typename ItemType> struct type_checker { static const bool is_atom = false; static const bool is_pointer = false; }; - template<typename ItemType> struct type_checker<ItemType*> { static const bool is_atom = false; static const bool is_pointer = true; }; + // set of templates to identify valid save types + template <typename ItemType> struct is_atom : public std::false_type { }; + template <typename ItemType> struct is_vector_safe : public std::false_type { }; + + class state_entry + { + public: + // construction/destruction + state_entry(void *data, std::string &&name, device_t *device, std::string &&module, std::string &&tag, int index, u8 size, u32 valcount, u32 blockcount, u32 stride); + + // helpers + void flip_data(); + + // state + void * m_data; // pointer to the memory to save/restore + std::string m_name; // full name + device_t * m_device; // associated device, nullptr if none + std::string m_module; // module name + std::string m_tag; // tag name + int m_index; // index + u8 m_typesize; // size of the raw data type + u32 m_typecount; // number of items in each block + u32 m_blockcount; // number of blocks of items + u32 m_stride; // stride between blocks of items in units of item size + }; friend class ram_state; friend class rewinder; public: + // stuff to allow STRUCT_MEMBER to work with pointers + template <typename T> struct pointer_unwrap { using underlying_type = typename array_unwrap<T>::underlying_type; }; + template <typename T> struct pointer_unwrap<T &> { using underlying_type = typename pointer_unwrap<std::remove_cv_t<T> >::underlying_type; }; + template <typename T> struct pointer_unwrap<T *> { using underlying_type = typename array_unwrap<T>::underlying_type; }; + template <typename T> struct pointer_unwrap<std::unique_ptr<T []> > { using underlying_type = typename array_unwrap<T>::underlying_type; }; + // construction/destruction save_manager(running_machine &machine); @@ -127,10 +159,11 @@ public: rewinder *rewind() { return m_rewind.get(); } int registration_count() const { return m_entry_list.size(); } bool registration_allowed() const { return m_reg_allowed; } + bool supported() const { return m_supported; } // registration control void allow_registration(bool allowed = true); - const char *indexed_item(int index, void *&base, u32 &valsize, u32 &valcount) const; + const char *indexed_item(int index, void *&base, u32 &valsize, u32 &valcount, u32 &blockcount, u32 &stride) const; // function registration void register_presave(save_prepost_delegate func); @@ -141,54 +174,143 @@ public: void dispatch_postload(); // generic memory registration - void save_memory(device_t *device, const char *module, const char *tag, u32 index, const char *name, void *val, u32 valsize, u32 valcount = 1); + void save_memory(device_t *device, const char *module, const char *tag, u32 index, const char *name, void *val, u32 valsize, u32 valcount = 1, u32 blockcount = 1, u32 stride = 0); // templatized wrapper for general objects and arrays - template<typename ItemType> - void save_item(device_t *device, const char *module, const char *tag, int index, ItemType &value, const char *valname) + template <typename ItemType> + std::enable_if_t<is_atom<typename array_unwrap<ItemType>::underlying_type>::value> save_item(device_t *device, const char *module, const char *tag, int index, ItemType &value, const char *valname) { - if (type_checker<ItemType>::is_pointer) - throw emu_fatalerror("Called save_item on a pointer with no count!"); - if (!type_checker<typename array_unwrap<ItemType>::underlying_type>::is_atom) - throw emu_fatalerror("Called save_item on a non-fundamental type!"); + static_assert(!std::is_pointer<ItemType>::value, "Called save_item on a pointer with no count!"); save_memory(device, module, tag, index, valname, array_unwrap<ItemType>::ptr(value), array_unwrap<ItemType>::SIZE, array_unwrap<ItemType>::SAVE_COUNT); } + // templatized wrapper for structure members + template <typename ItemType, typename StructType, typename ElementType> + void save_item(device_t *device, const char *module, const char *tag, int index, ItemType &value, ElementType StructType::*element, const char *valname) + { + static_assert(std::is_base_of<StructType, typename array_unwrap<ItemType>::underlying_type>::value, "Called save_item on a non-matching struct member pointer!"); + static_assert(!std::is_pointer<ElementType>::value, "Called save_item on a struct member pointer!"); + static_assert(is_atom<typename array_unwrap<ElementType>::underlying_type>::value, "Called save_item on a non-fundamental type!"); + save_memory(device, module, tag, index, valname, array_unwrap<ElementType>::ptr(array_unwrap<ItemType>::ptr(value)->*element), array_unwrap<ElementType>::SIZE, array_unwrap<ElementType>::SAVE_COUNT, array_unwrap<ItemType>::SAVE_COUNT, sizeof(typename array_unwrap<ItemType>::underlying_type)); + } + // templatized wrapper for pointers - template<typename ItemType> - void save_pointer(device_t *device, const char *module, const char *tag, int index, ItemType *value, const char *valname, u32 count) + template <typename ItemType> + std::enable_if_t<is_atom<typename array_unwrap<ItemType>::underlying_type>::value> save_pointer(device_t *device, const char *module, const char *tag, int index, ItemType *value, const char *valname, u32 count) { - if (!type_checker<typename array_unwrap<ItemType>::underlying_type>::is_atom) - throw emu_fatalerror("Called save_item on a non-fundamental type!"); save_memory(device, module, tag, index, valname, array_unwrap<ItemType>::ptr(value[0]), array_unwrap<ItemType>::SIZE, array_unwrap<ItemType>::SAVE_COUNT * count); } + template <typename ItemType, typename StructType, typename ElementType> + void save_pointer(device_t *device, const char *module, const char *tag, int index, ItemType *value, ElementType StructType::*element, const char *valname, u32 count) + { + static_assert(std::is_base_of<StructType, typename array_unwrap<ItemType>::underlying_type>::value, "Called save_pointer on a non-matching struct member pointer!"); + static_assert(!std::is_pointer<ElementType>::value, "Called save_pointer on a struct member pointer!"); + static_assert(is_atom<typename array_unwrap<ElementType>::underlying_type>::value, "Called save_pointer on a non-fundamental type!"); + save_memory(device, module, tag, index, valname, array_unwrap<ElementType>::ptr(array_unwrap<ItemType>::ptr(value[0])->*element), array_unwrap<ElementType>::SIZE, array_unwrap<ElementType>::SAVE_COUNT, array_unwrap<ItemType>::SAVE_COUNT * count, sizeof(typename array_unwrap<ItemType>::underlying_type)); + } + // templatized wrapper for std::unique_ptr - template<typename ItemType> - void save_pointer(device_t *device, const char *module, const char *tag, int index, std::unique_ptr<ItemType[]> &value, const char *valname, u32 count) + template <typename ItemType> + std::enable_if_t<is_atom<typename array_unwrap<ItemType>::underlying_type>::value> save_pointer(device_t *device, const char *module, const char *tag, int index, const std::unique_ptr<ItemType []> &value, const char *valname, u32 count) { - if (!type_checker<typename array_unwrap<ItemType>::underlying_type>::is_atom) - throw emu_fatalerror("Called save_item on a non-fundamental type!"); save_memory(device, module, tag, index, valname, array_unwrap<ItemType>::ptr(value[0]), array_unwrap<ItemType>::SIZE, array_unwrap<ItemType>::SAVE_COUNT * count); } + template <typename ItemType, typename StructType, typename ElementType> + void save_pointer(device_t *device, const char *module, const char *tag, int index, const std::unique_ptr<ItemType []> &value, ElementType StructType::*element, const char *valname, u32 count) + { + static_assert(std::is_base_of<StructType, typename array_unwrap<ItemType>::underlying_type>::value, "Called save_pointer on a non-matching struct member pointer!"); + static_assert(!std::is_pointer<ElementType>::value, "Called save_pointer on a struct member pointer!"); + static_assert(is_atom<typename array_unwrap<ElementType>::underlying_type>::value, "Called save_pointer on a non-fundamental type!"); + save_memory(device, module, tag, index, valname, array_unwrap<ElementType>::ptr(array_unwrap<ItemType>::ptr(value[0])->*element), array_unwrap<ElementType>::SIZE, array_unwrap<ElementType>::SAVE_COUNT, array_unwrap<ItemType>::SAVE_COUNT * count, sizeof(typename array_unwrap<ItemType>::underlying_type)); + } + + // templatized wrapper for std::vector + template <typename ItemType> + std::enable_if_t<is_vector_safe<typename array_unwrap<ItemType>::underlying_type>::value> save_item(device_t *device, const char *module, const char *tag, int index, std::vector<ItemType> &value, const char *valname) + { + save_pointer(device, module, tag, index, &value[0], valname, value.size()); + } + + // specializations for bitmaps + void save_item(device_t *device, const char *module, const char *tag, int index, bitmap_ind8 &value, const char *valname) + { + save_memory(device, module, tag, index, valname, &value.pix(0), value.bpp() / 8, value.rowpixels() * value.height()); + } + + void save_item(device_t *device, const char *module, const char *tag, int index, bitmap_ind16 &value, const char *valname) + { + save_memory(device, module, tag, index, valname, &value.pix(0), value.bpp() / 8, value.rowpixels() * value.height()); + } + + void save_item(device_t *device, const char *module, const char *tag, int index, bitmap_ind32 &value, const char *valname) + { + save_memory(device, module, tag, index, valname, &value.pix(0), value.bpp() / 8, value.rowpixels() * value.height()); + } + + void save_item(device_t *device, const char *module, const char *tag, int index, bitmap_rgb32 &value, const char *valname) + { + save_memory(device, module, tag, index, valname, &value.pix(0), value.bpp() / 8, value.rowpixels() * value.height()); + } + + // specializations for attotimes + template <typename ItemType> + std::enable_if_t<std::is_same<typename save_manager::array_unwrap<ItemType>::underlying_type, attotime>::value> save_item(device_t *device, const char *module, const char *tag, int index, ItemType &value, const char *valname) + { + std::string tempstr; + tempstr.assign(valname).append(".attoseconds"); + save_item(device, module, tag, index, value, &attotime::m_attoseconds, tempstr.c_str()); + tempstr.assign(valname).append(".seconds"); + save_item(device, module, tag, index, value, &attotime::m_seconds, tempstr.c_str()); + } + + template <typename ItemType> + std::enable_if_t<std::is_same<typename save_manager::array_unwrap<ItemType>::underlying_type, attotime>::value> save_pointer(device_t *device, const char *module, const char *tag, int index, ItemType *value, const char *valname, u32 count) + { + std::string tempstr; + tempstr.assign(valname).append(".attoseconds"); + save_item(device, module, tag, index, value, &attotime::m_attoseconds, tempstr.c_str(), count); + tempstr.assign(valname).append(".seconds"); + save_item(device, module, tag, index, value, &attotime::m_seconds, tempstr.c_str(), count); + } + + template <typename ItemType> + std::enable_if_t<std::is_same<typename save_manager::array_unwrap<ItemType>::underlying_type, attotime>::value> save_pointer(device_t *device, const char *module, const char *tag, int index, const std::unique_ptr<ItemType []> &value, const char *valname, u32 count) + { + std::string tempstr; + tempstr.assign(valname).append(".attoseconds"); + save_item(device, module, tag, index, value, &attotime::m_attoseconds, tempstr.c_str(), count); + tempstr.assign(valname).append(".seconds"); + save_item(device, module, tag, index, value, &attotime::m_seconds, tempstr.c_str(), count); + } + // global memory registration - template<typename ItemType> - void save_item(ItemType &value, const char *valname, int index = 0) { save_item(nullptr, "global", nullptr, index, value, valname); } - template<typename ItemType> - void save_pointer(ItemType *value, const char *valname, u32 count, int index = 0) { save_pointer(nullptr, "global", nullptr, index, value, valname, count); } + template <typename ItemType> + void save_item(ItemType &value, const char *valname, int index = 0) + { save_item(nullptr, "global", nullptr, index, value, valname); } + template <typename ItemType, typename StructType, typename ElementType> + void save_item(ItemType &value, ElementType StructType::*element, const char *valname, int index = 0) + { save_item(nullptr, "global", nullptr, index, value, element, valname); } + template <typename ItemType> + void save_pointer(ItemType &&value, const char *valname, u32 count, int index = 0) + { save_pointer(nullptr, "global", nullptr, index, std::forward<ItemType>(value), valname, count); } + template <typename ItemType, typename StructType, typename ElementType> + void save_pointer(ItemType &&value, ElementType StructType::*element, const char *valname, u32 count, int index = 0) + { save_pointer(nullptr, "global", nullptr, index, std::forward<ItemType>(value), element, valname, count); } // file processing - static save_error check_file(running_machine &machine, emu_file &file, const char *gamename, void (CLIB_DECL *errormsg)(const char *fmt, ...)); - save_error write_file(emu_file &file); - save_error read_file(emu_file &file); + static save_error check_file(running_machine &machine, util::core_file &file, const char *gamename, void (CLIB_DECL *errormsg)(const char *fmt, ...)); + save_error write_file(util::core_file &file); + save_error read_file(util::core_file &file); -private: - // internal helpers - u32 signature() const; - void dump_registry() const; - static save_error validate_header(const u8 *header, const char *gamename, u32 signature, void (CLIB_DECL *errormsg)(const char *fmt, ...), const char *error_prefix); + save_error write_stream(std::ostream &str); + save_error read_stream(std::istream &str); + + save_error write_buffer(void *buf, size_t size); + save_error read_buffer(const void *buf, size_t size); +private: // state callback item class state_callback { @@ -199,11 +321,20 @@ private: save_prepost_delegate m_func; // delegate }; + // internal helpers + template <typename T, typename U, typename V, typename W> + save_error do_write(T check_space, U write_block, V start_header, W start_data); + template <typename T, typename U, typename V, typename W> + save_error do_read(T check_length, U read_block, V start_header, W start_data); + u32 signature() const; + void dump_registry() const; + static save_error validate_header(const u8 *header, const char *gamename, u32 signature, void (CLIB_DECL *errormsg)(const char *fmt, ...), const char *error_prefix); + // internal state running_machine & m_machine; // reference to our machine std::unique_ptr<rewinder> m_rewind; // rewinder bool m_reg_allowed; // are registrations allowed? - s32 m_illegal_regs; // number of illegal registrations + bool m_supported; // are saved states supported? std::vector<std::unique_ptr<state_entry>> m_entry_list; // list of registered entries std::vector<std::unique_ptr<ram_state>> m_ramstate_list; // list of ram states @@ -265,70 +396,22 @@ public: // template specializations to enumerate the fundamental atomic types you are allowed to save -ALLOW_SAVE_TYPE_AND_ARRAY(char) -ALLOW_SAVE_TYPE (bool); // std::vector<bool> may be packed internally -ALLOW_SAVE_TYPE_AND_ARRAY(osd::s8) -ALLOW_SAVE_TYPE_AND_ARRAY(osd::u8) -ALLOW_SAVE_TYPE_AND_ARRAY(osd::s16) -ALLOW_SAVE_TYPE_AND_ARRAY(osd::u16) -ALLOW_SAVE_TYPE_AND_ARRAY(osd::s32) -ALLOW_SAVE_TYPE_AND_ARRAY(osd::u32) -ALLOW_SAVE_TYPE_AND_ARRAY(osd::s64) -ALLOW_SAVE_TYPE_AND_ARRAY(osd::u64) -ALLOW_SAVE_TYPE_AND_ARRAY(PAIR) -ALLOW_SAVE_TYPE_AND_ARRAY(PAIR64) -ALLOW_SAVE_TYPE_AND_ARRAY(float) -ALLOW_SAVE_TYPE_AND_ARRAY(double) -ALLOW_SAVE_TYPE_AND_ARRAY(endianness_t) -ALLOW_SAVE_TYPE_AND_ARRAY(rgb_t) - - - -//************************************************************************** -// INLINE FUNCTIONS -//************************************************************************** - -//------------------------------------------------- -// save_item - specialized save_item for bitmaps -//------------------------------------------------- - -template<> -inline void save_manager::save_item(device_t *device, const char *module, const char *tag, int index, bitmap_ind8 &value, const char *name) -{ - save_memory(device, module, tag, index, name, &value.pix(0), value.bpp() / 8, value.rowpixels() * value.height()); -} - -template<> -inline void save_manager::save_item(device_t *device, const char *module, const char *tag, int index, bitmap_ind16 &value, const char *name) -{ - save_memory(device, module, tag, index, name, &value.pix(0), value.bpp() / 8, value.rowpixels() * value.height()); -} - -template<> -inline void save_manager::save_item(device_t *device, const char *module, const char *tag, int index, bitmap_ind32 &value, const char *name) -{ - save_memory(device, module, tag, index, name, &value.pix(0), value.bpp() / 8, value.rowpixels() * value.height()); -} - -template<> -inline void save_manager::save_item(device_t *device, const char *module, const char *tag, int index, bitmap_rgb32 &value, const char *name) -{ - save_memory(device, module, tag, index, name, &value.pix(0), value.bpp() / 8, value.rowpixels() * value.height()); -} - - -//------------------------------------------------- -// save_item - specialized save_item for attotimes -//------------------------------------------------- - -template<> -inline void save_manager::save_item(device_t *device, const char *module, const char *tag, int index, attotime &value, const char *name) -{ - std::string tempstr = std::string(name).append(".attoseconds"); - save_memory(device, module, tag, index, tempstr.c_str(), &value.m_attoseconds, sizeof(value.m_attoseconds)); - tempstr.assign(name).append(".seconds"); - save_memory(device, module, tag, index, tempstr.c_str(), &value.m_seconds, sizeof(value.m_seconds)); -} - - -#endif /* MAME_EMU_SAVE_H */ +ALLOW_SAVE_TYPE_AND_VECTOR(char) +ALLOW_SAVE_TYPE (bool) // std::vector<bool> may be packed internally +ALLOW_SAVE_TYPE_AND_VECTOR(osd::s8) +ALLOW_SAVE_TYPE_AND_VECTOR(osd::u8) +ALLOW_SAVE_TYPE_AND_VECTOR(osd::s16) +ALLOW_SAVE_TYPE_AND_VECTOR(osd::u16) +ALLOW_SAVE_TYPE_AND_VECTOR(osd::s32) +ALLOW_SAVE_TYPE_AND_VECTOR(osd::u32) +ALLOW_SAVE_TYPE_AND_VECTOR(osd::s64) +ALLOW_SAVE_TYPE_AND_VECTOR(osd::u64) +ALLOW_SAVE_TYPE_AND_VECTOR(PAIR) +ALLOW_SAVE_TYPE_AND_VECTOR(PAIR64) +ALLOW_SAVE_TYPE_AND_VECTOR(float) +ALLOW_SAVE_TYPE_AND_VECTOR(double) +ALLOW_SAVE_TYPE_AND_VECTOR(endianness_t) +ALLOW_SAVE_TYPE_AND_VECTOR(rgb_t) + + +#endif // MAME_EMU_SAVE_H diff --git a/src/emu/schedule.cpp b/src/emu/schedule.cpp index e2b1497995f..a475fd9b515 100644 --- a/src/emu/schedule.cpp +++ b/src/emu/schedule.cpp @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles /*************************************************************************** - schedule.c + schedule.cpp Core device execution and scheduling engine. @@ -17,7 +17,7 @@ #define VERBOSE 0 -#define LOG(x) do { if (VERBOSE) machine().logerror x; } while (0) +#define LOG(...) do { if (VERBOSE) machine().logerror(__VA_ARGS__); } while (0) #define PRECISION @@ -44,19 +44,16 @@ enum // emu_timer - constructor //------------------------------------------------- -emu_timer::emu_timer() - : m_machine(nullptr), - m_next(nullptr), - m_prev(nullptr), - m_param(0), - m_ptr(nullptr), - m_enabled(false), - m_temporary(false), - m_period(attotime::zero), - m_start(attotime::zero), - m_expire(attotime::never), - m_device(nullptr), - m_id(0) +inline emu_timer::emu_timer() noexcept : + m_scheduler(nullptr), + m_next(nullptr), + m_prev(nullptr), + m_param(0), + m_enabled(false), + m_temporary(false), + m_period(attotime::zero), + m_start(attotime::zero), + m_expire(attotime::never) { } @@ -75,74 +72,35 @@ emu_timer::~emu_timer() // re-allocated as a non-device timer //------------------------------------------------- -emu_timer &emu_timer::init(running_machine &machine, timer_expired_delegate callback, void *ptr, bool temporary) +inline emu_timer &emu_timer::init( + running_machine &machine, + timer_expired_delegate &&callback, + attotime start_delay, + s32 param, + bool temporary) { // ensure the entire timer state is clean - m_machine = &machine; + m_scheduler = &machine.scheduler(); m_next = nullptr; m_prev = nullptr; - m_callback = callback; - m_param = 0; - m_ptr = ptr; - m_enabled = false; + m_callback = std::move(callback); + m_param = param; m_temporary = temporary; m_period = attotime::never; - m_start = machine.time(); - m_expire = attotime::never; - m_device = nullptr; - m_id = 0; - - // if we're not temporary, register ourselves with the save state system - if (!m_temporary) - register_save(); - - // insert into the list - machine.scheduler().timer_list_insert(*this); - return *this; -} - - -//------------------------------------------------- -// init - completely initialize the state when -// re-allocated as a device timer -//------------------------------------------------- -emu_timer &emu_timer::init(device_t &device, device_timer_id id, void *ptr, bool temporary) -{ - // ensure the entire timer state is clean - m_machine = &device.machine(); - m_next = nullptr; - m_prev = nullptr; - m_callback = timer_expired_delegate(); - m_param = 0; - m_ptr = ptr; - m_enabled = false; - m_temporary = temporary; - m_period = attotime::never; - m_start = machine().time(); - m_expire = attotime::never; - m_device = &device; - m_id = id; + m_start = m_scheduler->time(); + m_expire = m_start + start_delay; + m_enabled = !m_expire.is_never(); // if we're not temporary, register ourselves with the save state system if (!m_temporary) - register_save(); + register_save(machine.save()); // insert into the list - machine().scheduler().timer_list_insert(*this); - return *this; -} - + m_scheduler->timer_list_insert(*this); + if (this == m_scheduler->first_timer()) + m_scheduler->abort_timeslice(); -//------------------------------------------------- -// release - release us from the global list -// management when deallocating -//------------------------------------------------- - -emu_timer &emu_timer::release() -{ - // unhook us from the global list - machine().scheduler().timer_list_remove(*this); return *this; } @@ -151,18 +109,20 @@ emu_timer &emu_timer::release() // enable - enable/disable a timer //------------------------------------------------- -bool emu_timer::enable(bool enable) +bool emu_timer::enable(bool enable) noexcept { + assert(m_scheduler); + // reschedule only if the state has changed - bool old = m_enabled; + const bool old = m_enabled; if (old != enable) { // set the enable flag m_enabled = enable; // remove the timer and insert back into the list - machine().scheduler().timer_list_remove(*this); - machine().scheduler().timer_list_insert(*this); + m_scheduler->timer_list_remove(*this); + m_scheduler->timer_list_insert(*this); } return old; } @@ -174,12 +134,13 @@ bool emu_timer::enable(bool enable) // firings //------------------------------------------------- -void emu_timer::adjust(attotime start_delay, s32 param, const attotime &period) +void emu_timer::adjust(attotime start_delay, s32 param, const attotime &period) noexcept { + assert(m_scheduler); + // if this is the callback timer, mark it modified - device_scheduler &scheduler = machine().scheduler(); - if (scheduler.m_callback_timer == this) - scheduler.m_callback_timer_modified = true; + if (m_scheduler->m_callback_timer == this) + m_scheduler->m_callback_timer_modified = true; // compute the time of the next firing and insert into the list m_param = param; @@ -190,17 +151,17 @@ void emu_timer::adjust(attotime start_delay, s32 param, const attotime &period) start_delay = attotime::zero; // set the start and expire times - m_start = scheduler.time(); + m_start = m_scheduler->time(); m_expire = m_start + start_delay; m_period = period; // remove and re-insert the timer in its new order - scheduler.timer_list_remove(*this); - scheduler.timer_list_insert(*this); + m_scheduler->timer_list_remove(*this); + m_scheduler->timer_list_insert(*this); // if this was inserted as the head, abort the current timeslice and resync - if (this == scheduler.first_timer()) - scheduler.abort_timeslice(); + if (this == m_scheduler->first_timer()) + m_scheduler->abort_timeslice(); } @@ -209,9 +170,11 @@ void emu_timer::adjust(attotime start_delay, s32 param, const attotime &period) // timer was started //------------------------------------------------- -attotime emu_timer::elapsed() const +attotime emu_timer::elapsed() const noexcept { - return machine().time() - m_start; + assert(m_scheduler); + + return m_scheduler->time() - m_start; } @@ -220,9 +183,11 @@ attotime emu_timer::elapsed() const // remaining until the timer expires //------------------------------------------------- -attotime emu_timer::remaining() const +attotime emu_timer::remaining() const noexcept { - attotime curtime = machine().time(); + assert(m_scheduler); + + const attotime curtime = m_scheduler->time(); if (curtime >= m_expire) return attotime::zero; return m_expire - curtime; @@ -234,41 +199,38 @@ attotime emu_timer::remaining() const // state system //------------------------------------------------- -void emu_timer::register_save() +void emu_timer::register_save(save_manager &manager) { - // determine our instance number and name + // determine our instance number - timers are indexed based on the callback function name int index = 0; - std::string name; - - // for non-device timers, it is an index based on the callback function name - if (m_device == nullptr) + std::string name = m_callback.name() ? m_callback.name() : "unnamed"; + for (const emu_timer *curtimer = m_scheduler->first_timer(); curtimer; curtimer = curtimer->m_next) { - name = m_callback.name() ? m_callback.name() : "unnamed"; - for (emu_timer *curtimer = machine().scheduler().first_timer(); curtimer != nullptr; curtimer = curtimer->next()) - if (!curtimer->m_temporary && curtimer->m_device == nullptr) - { - if (curtimer->m_callback.name() != nullptr && m_callback.name() != nullptr && strcmp(curtimer->m_callback.name(), m_callback.name()) == 0) - index++; - else if (curtimer->m_callback.name() == nullptr && m_callback.name() == nullptr) - index++; - } + if (!curtimer->m_temporary) + { + if (curtimer->m_callback.name() && m_callback.name() && !strcmp(curtimer->m_callback.name(), m_callback.name())) + index++; + else if (!curtimer->m_callback.name() && !m_callback.name()) + index++; + } } - - // for device timers, it is an index based on the device and timer ID - else + for (const emu_timer *curtimer = m_scheduler->m_inactive_timers; curtimer; curtimer = curtimer->m_next) { - name = string_format("%s/%d", m_device->tag(), m_id); - for (emu_timer *curtimer = machine().scheduler().first_timer(); curtimer != nullptr; curtimer = curtimer->next()) - if (!curtimer->m_temporary && curtimer->m_device != nullptr && curtimer->m_device == m_device && curtimer->m_id == m_id) - index++; + assert(!curtimer->m_temporary); + + if (curtimer->m_callback.name() && m_callback.name() && !strcmp(curtimer->m_callback.name(), m_callback.name())) + index++; + else if (!curtimer->m_callback.name() && !m_callback.name()) + index++; } // save the bits - machine().save().save_item(m_device, "timer", name.c_str(), index, NAME(m_param)); - machine().save().save_item(m_device, "timer", name.c_str(), index, NAME(m_enabled)); - machine().save().save_item(m_device, "timer", name.c_str(), index, NAME(m_period)); - machine().save().save_item(m_device, "timer", name.c_str(), index, NAME(m_start)); - machine().save().save_item(m_device, "timer", name.c_str(), index, NAME(m_expire)); + manager.save_item(nullptr, "timer", name.c_str(), index, NAME(m_param)); + manager.save_item(nullptr, "timer", name.c_str(), index, NAME(m_enabled)); + manager.save_item(nullptr, "timer", name.c_str(), index, NAME(m_period)); + manager.save_item(nullptr, "timer", name.c_str(), index, NAME(m_start)); + manager.save_item(nullptr, "timer", name.c_str(), index, NAME(m_expire)); + manager.save_item(nullptr, "timer", name.c_str(), index, NAME(m_index)); } @@ -277,16 +239,17 @@ void emu_timer::register_save() // period //------------------------------------------------- -inline void emu_timer::schedule_next_period() +inline void emu_timer::schedule_next_period() noexcept { + assert(m_scheduler); + // advance by one period m_start = m_expire; m_expire += m_period; // remove and re-insert us - device_scheduler &scheduler = machine().scheduler(); - scheduler.timer_list_remove(*this); - scheduler.timer_list_insert(*this); + m_scheduler->timer_list_remove(*this); + m_scheduler->timer_list_insert(*this); } @@ -297,14 +260,13 @@ inline void emu_timer::schedule_next_period() void emu_timer::dump() const { - machine().logerror("%p: en=%d temp=%d exp=%15s start=%15s per=%15s param=%d ptr=%p", this, m_enabled, m_temporary, m_expire.as_string(PRECISION), m_start.as_string(PRECISION), m_period.as_string(PRECISION), m_param, m_ptr); - if (m_device == nullptr) - if (m_callback.name() == nullptr) - machine().logerror(" cb=NULL\n"); - else - machine().logerror(" cb=%s\n", m_callback.name()); + assert(m_scheduler); + + m_scheduler->machine().logerror("%p: en=%d temp=%d exp=%15s start=%15s per=%15s param=%d", this, m_enabled, m_temporary, m_expire.as_string(PRECISION), m_start.as_string(PRECISION), m_period.as_string(PRECISION), m_param); + if (!m_callback.name()) + m_scheduler->machine().logerror(" cb=NULL\n"); else - machine().logerror(" dev=%s id=%d\n", m_device->tag(), m_id); + m_scheduler->machine().logerror(" cb=%s\n", m_callback.name()); } @@ -323,6 +285,7 @@ device_scheduler::device_scheduler(running_machine &machine) : m_execute_list(nullptr), m_basetime(attotime::zero), m_timer_list(nullptr), + m_inactive_timers(nullptr), m_callback_timer(nullptr), m_callback_timer_modified(false), m_callback_timer_expire_time(attotime::zero), @@ -330,8 +293,13 @@ device_scheduler::device_scheduler(running_machine &machine) : m_quantum_minimum(ATTOSECONDS_IN_NSEC(1) / 1000) { // append a single never-expiring timer so there is always one in the list - m_timer_list = &m_timer_allocator.alloc()->init(machine, timer_expired_delegate(), nullptr, true); - m_timer_list->adjust(attotime::never); + // need to subvert it because it would naturally be inserted in the inactive list + m_timer_list = &timer_list_remove(m_timer_allocator.alloc()->init(machine, timer_expired_delegate(), attotime::never, 0, true)); + + assert(m_timer_list); + assert(!m_timer_list->m_prev); + assert(!m_timer_list->m_next); + assert(!m_inactive_timers); // register global states machine.save().save_item(NAME(m_basetime)); @@ -347,8 +315,10 @@ device_scheduler::device_scheduler(running_machine &machine) : device_scheduler::~device_scheduler() { // remove all timers - while (m_timer_list != nullptr) - m_timer_allocator.reclaim(m_timer_list->release()); + while (m_inactive_timers) + m_timer_allocator.reclaim(timer_list_remove(*m_inactive_timers)); + while (m_timer_list) + m_timer_allocator.reclaim(timer_list_remove(*m_timer_list)); } @@ -356,7 +326,7 @@ device_scheduler::~device_scheduler() // time - return the current time //------------------------------------------------- -attotime device_scheduler::time() const +attotime device_scheduler::time() const noexcept { // if we're currently in a callback, use the timer's expiration time as a base if (m_callback_timer != nullptr) @@ -376,13 +346,15 @@ attotime device_scheduler::time() const bool device_scheduler::can_save() const { // if any live temporary timers exit, fail - for (emu_timer *timer = m_timer_list; timer != nullptr; timer = timer->next()) + for (emu_timer *timer = m_timer_list; timer; timer = timer->m_next) + { if (timer->m_temporary && !timer->expire().is_never()) { machine().logerror("Failed save state attempt due to anonymous timers:\n"); dump_timers(); return false; } + } // otherwise, we're good return true; @@ -440,8 +412,8 @@ void device_scheduler::timeslice() if (m_timer_list->m_expire < target) target = m_timer_list->m_expire; - LOG(("------------------\n")); - LOG(("cpu_timeslice: target = %s\n", target.as_string(PRECISION))); + LOG("------------------\n"); + LOG("cpu_timeslice: target = %s\n", target.as_string(PRECISION)); // do we have pending suspension changes? if (m_suspend_changes_pending) @@ -469,12 +441,12 @@ void device_scheduler::timeslice() { // compute how many cycles we want to execute int ran = exec->m_cycles_running = divu_64x32(u64(delta) >> exec->m_divshift, exec->m_divisor); - LOG((" cpu '%s': %d (%d cycles)\n", exec->device().tag(), delta, exec->m_cycles_running)); + LOG(" cpu '%s': %d (%d cycles)\n", exec->device().tag(), delta, exec->m_cycles_running); // if we're not suspended, actually execute if (exec->m_suspend == 0) { - g_profiler.start(exec->m_profiler); + auto profile = g_profiler.start(exec->m_profiler); // note that this global variable cycles_stolen can be modified // via the call to cpu_execute @@ -495,7 +467,6 @@ void device_scheduler::timeslice() ran -= *exec->m_icountptr; assert(ran >= exec->m_cycles_stolen); ran -= exec->m_cycles_stolen; - g_profiler.stop(); } // account for these cycles @@ -503,23 +474,23 @@ void device_scheduler::timeslice() // update the local time for this CPU attotime deltatime; - if (ran < exec->m_cycles_per_second) + if (EXPECTED(ran < exec->m_cycles_per_second)) deltatime = attotime(0, exec->m_attoseconds_per_cycle * ran); else { u32 remainder; - s32 secs = divu_64x32_rem(ran, exec->m_cycles_per_second, &remainder); + s32 secs = divu_64x32_rem(ran, exec->m_cycles_per_second, remainder); deltatime = attotime(secs, u64(remainder) * exec->m_attoseconds_per_cycle); } assert(deltatime >= attotime::zero); exec->m_localtime += deltatime; - LOG((" %d ran, %d total, time = %s\n", ran, s32(exec->m_totalcycles), exec->m_localtime.as_string(PRECISION))); + LOG(" %d ran, %d total, time = %s\n", ran, s32(exec->m_totalcycles), exec->m_localtime.as_string(PRECISION)); // if the new local CPU time is less than our target, move the target up, but not before the base if (exec->m_localtime < target) { target = std::max(exec->m_localtime, m_basetime); - LOG((" (new target)\n")); + LOG(" (new target)\n"); } } } @@ -540,7 +511,7 @@ void device_scheduler::timeslice() // current timeslice //------------------------------------------------- -void device_scheduler::abort_timeslice() +void device_scheduler::abort_timeslice() noexcept { if (m_executing_device != nullptr) m_executing_device->abort_timeslice(); @@ -557,74 +528,117 @@ void device_scheduler::trigger(int trigid, const attotime &after) if (m_execute_list == nullptr) rebuild_execute_list(); - // if we have a non-zero time, schedule a timer if (after != attotime::zero) + { + // if we have a non-zero time, schedule a timer timer_set(after, timer_expired_delegate(FUNC(device_scheduler::timed_trigger), this), trigid); - - // send the trigger to everyone who cares + } else - for (device_execute_interface *exec = m_execute_list; exec != nullptr; exec = exec->m_nextexec) + { + // send the trigger to everyone who cares + for (device_execute_interface *exec = m_execute_list; exec; exec = exec->m_nextexec) exec->trigger(trigid); + } } //------------------------------------------------- -// boost_interleave - temporarily boosts the -// interleave factor +// add_quantum - add a scheduling quantum; +// the smallest active one is the one that is in use //------------------------------------------------- -void device_scheduler::boost_interleave(const attotime ×lice_time, const attotime &boost_duration) +void device_scheduler::add_quantum(const attotime &quantum, const attotime &duration) { - // ignore timeslices > 1 second - if (timeslice_time.seconds() > 0) - return; - add_scheduling_quantum(timeslice_time, boost_duration); + assert(quantum.seconds() == 0); + + attotime curtime = time(); + attotime expire = curtime + duration; + const attoseconds_t quantum_attos = quantum.attoseconds(); + + // figure out where to insert ourselves, expiring any quanta that are out-of-date + quantum_slot *insert_after = nullptr; + quantum_slot *next; + for (quantum_slot *quant = m_quantum_list.first(); quant != nullptr; quant = next) + { + // if this quantum is expired, nuke it + next = quant->next(); + if (curtime >= quant->m_expire) + m_quantum_allocator.reclaim(m_quantum_list.detach(*quant)); + + // if this quantum is shorter than us, we need to be inserted afterwards + else if (quant->m_requested <= quantum_attos) + insert_after = quant; + } + + // if we found an exact match, just take the maximum expiry time + if (insert_after != nullptr && insert_after->m_requested == quantum_attos) + insert_after->m_expire = std::max(insert_after->m_expire, expire); + + // otherwise, allocate a new quantum and insert it after the one we picked + else + { + quantum_slot &quant = *m_quantum_allocator.alloc(); + quant.m_requested = quantum_attos; + quant.m_actual = std::max(quantum_attos, m_quantum_minimum); + quant.m_expire = expire; + m_quantum_list.insert_after(quant, insert_after); + } } //------------------------------------------------- -// timer_alloc - allocate a global non-device -// timer and return a pointer +// perfect_quantum - add a (temporary) minimum +// scheduling quantum to boost the interleave //------------------------------------------------- -emu_timer *device_scheduler::timer_alloc(timer_expired_delegate callback, void *ptr) +void device_scheduler::perfect_quantum(const attotime &duration) { - return &m_timer_allocator.alloc()->init(machine(), callback, ptr, false); + add_quantum(attotime::zero, duration); } //------------------------------------------------- -// timer_set - allocate an anonymous non-device -// timer and set it to go off after the given -// amount of time +// timer_alloc - allocate a global non-device +// timer and return a pointer //------------------------------------------------- -void device_scheduler::timer_set(const attotime &duration, timer_expired_delegate callback, int param, void *ptr) +emu_timer *device_scheduler::timer_alloc(timer_expired_delegate callback) { - m_timer_allocator.alloc()->init(machine(), callback, ptr, true).adjust(duration, param); + return &m_timer_allocator.alloc()->init(machine(), std::move(callback), attotime::never, 0, false); } //------------------------------------------------- -// timer_alloc - allocate a global device timer -// and return a pointer +// timer_set - allocate an anonymous non-device +// timer and set it to go off after the given +// amount of time //------------------------------------------------- -emu_timer *device_scheduler::timer_alloc(device_t &device, device_timer_id id, void *ptr) +void device_scheduler::timer_set(const attotime &duration, timer_expired_delegate callback, s32 param) { - return &m_timer_allocator.alloc()->init(device, id, ptr, false); + [[maybe_unused]] emu_timer &timer = m_timer_allocator.alloc()->init( + machine(), + std::move(callback), + duration, + param, + true); + assert(!timer.m_expire.is_never()); // this is not handled } //------------------------------------------------- -// timer_set - allocate an anonymous device timer -// and set it to go off after the given amount of -// time +// synchronize - allocate an anonymous non-device +// timer and set it to go off as soon as possible //------------------------------------------------- -void device_scheduler::timer_set(const attotime &duration, device_t &device, device_timer_id id, int param, void *ptr) +void device_scheduler::synchronize(timer_expired_delegate callback, s32 param) { - m_timer_allocator.alloc()->init(device, id, ptr, true).adjust(duration, param); + m_timer_allocator.alloc()->init( + machine(), + std::move(callback), + attotime::zero, + param, + true); } @@ -645,7 +659,7 @@ void device_scheduler::eat_all_cycles() // given amount of time //------------------------------------------------- -void device_scheduler::timed_trigger(void *ptr, s32 param) +void device_scheduler::timed_trigger(s32 param) { trigger(param); } @@ -658,7 +672,10 @@ void device_scheduler::timed_trigger(void *ptr, s32 param) void device_scheduler::presave() { // report the timer state after a log - LOG(("Prior to saving state:\n")); + LOG("Prior to saving state:\n"); + u32 index = 0; + for (emu_timer *timer = m_timer_list; timer; timer = timer->m_next) + timer->m_index = index++; #if VERBOSE dump_timers(); #endif @@ -672,30 +689,53 @@ void device_scheduler::presave() void device_scheduler::postload() { // remove all timers and make a private list of permanent ones - simple_list<emu_timer> private_list; - while (m_timer_list != nullptr) + emu_timer *private_list = nullptr; + while (m_inactive_timers) + { + emu_timer &timer = *m_inactive_timers; + assert(!timer.m_temporary); + + timer_list_remove(timer).m_next = private_list; + private_list = &timer; + } + while (m_timer_list->m_next) { emu_timer &timer = *m_timer_list; - // temporary timers go away entirely (except our special never-expiring one) - if (timer.m_temporary && !timer.expire().is_never()) - m_timer_allocator.reclaim(timer.release()); + if (timer.m_temporary) + { + assert(!timer.expire().is_never()); - // permanent ones get added to our private list + // temporary timers go away entirely (except our special never-expiring one) + timer.m_callback.reset(); + m_timer_allocator.reclaim(timer_list_remove(timer)); + } else - private_list.append(timer_list_remove(timer)); + { + // permanent ones get added to our private list + timer_list_remove(timer).m_next = private_list; + private_list = &timer; + } } + // special dummy timer + assert(!m_timer_list->m_enabled); + assert(m_timer_list->m_temporary); + assert(m_timer_list->m_expire.is_never()); + // now re-insert them; this effectively re-sorts them by time - emu_timer *timer; - while ((timer = private_list.detach_head()) != nullptr) - timer_list_insert(*timer); + while (private_list) + { + emu_timer &timer = *private_list; + private_list = timer.m_next; + timer_list_insert<true>(timer); + } m_suspend_changes_pending = true; rebuild_execute_list(); // report the timer state after a log - LOG(("After resetting/reordering timers:\n")); + LOG("After resetting/reordering timers:\n"); #if VERBOSE dump_timers(); #endif @@ -756,32 +796,16 @@ void device_scheduler::rebuild_execute_list() // if we haven't yet set a scheduling quantum, do it now if (m_quantum_list.empty()) { - // set the core scheduling quantum - attotime min_quantum = machine().config().m_minimum_quantum; - - // if none specified default to 60Hz - if (min_quantum.is_zero()) - min_quantum = attotime::from_hz(60); + // set the core scheduling quantum, ensuring it's no longer than 60Hz + attotime min_quantum = machine().config().maximum_quantum(attotime::from_hz(60)); // if the configuration specifies a device to make perfect, pick that as the minimum - if (!machine().config().m_perfect_cpu_quantum.empty()) - { - device_t *device = machine().root_device().subdevice(machine().config().m_perfect_cpu_quantum.c_str()); - if (device == nullptr) - fatalerror("Device '%s' specified for perfect interleave is not present!\n", machine().config().m_perfect_cpu_quantum.c_str()); - - device_execute_interface *exec; - if (!device->interface(exec)) - fatalerror("Device '%s' specified for perfect interleave is not an executing device!\n", machine().config().m_perfect_cpu_quantum.c_str()); - - min_quantum = std::min(attotime(0, exec->minimum_quantum()), min_quantum); - } - - // make sure it's no higher than 60Hz - min_quantum = std::min(min_quantum, attotime::from_hz(60)); + device_execute_interface *const exec(machine().config().perfect_quantum_device()); + if (exec) + min_quantum = (std::min)(attotime(0, exec->minimum_quantum()), min_quantum); // inform the timer system of our decision - add_scheduling_quantum(min_quantum, attotime::never); + add_quantum(min_quantum, attotime::never); } // start with an empty list @@ -793,7 +817,7 @@ void device_scheduler::rebuild_execute_list() device_execute_interface **suspend_tailptr = &suspend_list; // iterate over all devices - for (device_execute_interface &exec : execute_interface_iterator(machine().root_device())) + for (device_execute_interface &exec : execute_interface_enumerator(machine().root_device())) { // append to the appropriate list exec.m_nextexec = nullptr; @@ -819,40 +843,56 @@ void device_scheduler::rebuild_execute_list() // the list at the appropriate location //------------------------------------------------- -emu_timer &device_scheduler::timer_list_insert(emu_timer &timer) +template <bool CheckIndex> +inline emu_timer &device_scheduler::timer_list_insert(emu_timer &timer) { - // disabled timers sort to the end - const attotime &expire = timer.m_enabled ? timer.m_expire : attotime::never; - - // loop over the timer list - emu_timer *prevtimer = nullptr; - for (emu_timer *curtimer = m_timer_list; curtimer != nullptr; prevtimer = curtimer, curtimer = curtimer->next()) + // disabled timers never expire + if (!timer.m_expire.is_never() && timer.m_enabled) { - // if the current list entry expires after us, we should be inserted before it - if (curtimer->m_expire > expire) + // loop over the timer list + emu_timer *prevtimer = nullptr; + for (emu_timer *curtimer = m_timer_list; curtimer; prevtimer = curtimer, curtimer = curtimer->m_next) { - // link the new guy in before the current list entry - timer.m_prev = curtimer->m_prev; - timer.m_next = curtimer; + // if the current list entry expires after us, we should be inserted before it + bool const here = + (curtimer->m_expire > timer.m_expire) || + (CheckIndex && !(curtimer->m_expire < timer.m_expire) && (curtimer->m_index > timer.m_index)); + if (here) + { + // link the new guy in before the current list entry + timer.m_prev = prevtimer; + timer.m_next = curtimer; - if (curtimer->m_prev != nullptr) - curtimer->m_prev->m_next = &timer; - else - m_timer_list = &timer; + if (prevtimer) + prevtimer->m_next = &timer; + else + m_timer_list = &timer; - curtimer->m_prev = &timer; - return timer; + curtimer->m_prev = &timer; + return timer; + } } - } - // need to insert after the last one - if (prevtimer != nullptr) - prevtimer->m_next = &timer; + // need to insert after the last one + if (prevtimer) + prevtimer->m_next = &timer; + else + m_timer_list = &timer; + + timer.m_prev = prevtimer; + timer.m_next = nullptr; + } else - m_timer_list = &timer; + { + // keep inactive timers in a separate list + if (m_inactive_timers) + m_inactive_timers->m_prev = &timer; + + timer.m_next = m_inactive_timers; + timer.m_prev = nullptr; - timer.m_prev = prevtimer; - timer.m_next = nullptr; + m_inactive_timers = &timer; + } return timer; } @@ -862,15 +902,24 @@ emu_timer &device_scheduler::timer_list_insert(emu_timer &timer) // linked list //------------------------------------------------- -emu_timer &device_scheduler::timer_list_remove(emu_timer &timer) +inline emu_timer &device_scheduler::timer_list_remove(emu_timer &timer) { // remove it from the list - if (timer.m_prev != nullptr) + if (timer.m_prev) + { timer.m_prev->m_next = timer.m_next; - else + } + else if (&timer == m_timer_list) + { m_timer_list = timer.m_next; + } + else + { + assert(&timer == m_inactive_timers); + m_inactive_timers = timer.m_next; + } - if (timer.m_next != nullptr) + if (timer.m_next) timer.m_next->m_prev = timer.m_prev; return timer; @@ -883,7 +932,7 @@ emu_timer &device_scheduler::timer_list_remove(emu_timer &timer) inline void device_scheduler::execute_timers() { - LOG(("execute_timers: new=%s head->expire=%s\n", m_basetime.as_string(PRECISION), m_timer_list->m_expire.as_string(PRECISION))); + LOG("execute_timers: new=%s head->expire=%s\n", m_basetime.as_string(PRECISION), m_timer_list->m_expire.as_string(PRECISION)); // now process any timers that are overdue while (m_timer_list->m_expire <= m_basetime) @@ -902,82 +951,34 @@ inline void device_scheduler::execute_timers() // call the callback if (was_enabled) { - g_profiler.start(PROFILER_TIMER_CALLBACK); + auto profile = g_profiler.start(PROFILER_TIMER_CALLBACK); - if (timer.m_device != nullptr) + if (!timer.m_callback.isnull()) { - LOG(("execute_timers: timer device %s timer %d\n", timer.m_device->tag(), timer.m_id)); - timer.m_device->timer_expired(timer, timer.m_id, timer.m_param, timer.m_ptr); + LOG("execute_timers: timer callback %s\n", timer.m_callback.name()); + timer.m_callback(timer.m_param); } - else if (!timer.m_callback.isnull()) - { - LOG(("execute_timers: timer callback %s\n", timer.m_callback.name())); - timer.m_callback(timer.m_ptr, timer.m_param); - } - - g_profiler.stop(); } - // clear the callback timer global - m_callback_timer = nullptr; - // reset or remove the timer, but only if it wasn't modified during the callback if (!m_callback_timer_modified) { - // if the timer is temporary, remove it now - if (timer.m_temporary) - m_timer_allocator.reclaim(timer.release()); - - // otherwise, reschedule it - else + if (!timer.m_temporary) + { + // if the timer is not temporary, reschedule it timer.schedule_next_period(); + } + else + { + // otherwise, remove it now + timer.m_callback.reset(); + m_timer_allocator.reclaim(timer_list_remove(timer)); + } } } -} - - -//------------------------------------------------- -// add_scheduling_quantum - add a scheduling -// quantum; the smallest active one is the one -// that is in use -//------------------------------------------------- - -void device_scheduler::add_scheduling_quantum(const attotime &quantum, const attotime &duration) -{ - assert(quantum.seconds() == 0); - - attotime curtime = time(); - attotime expire = curtime + duration; - const attoseconds_t quantum_attos = quantum.attoseconds(); - - // figure out where to insert ourselves, expiring any quanta that are out-of-date - quantum_slot *insert_after = nullptr; - quantum_slot *next; - for (quantum_slot *quant = m_quantum_list.first(); quant != nullptr; quant = next) - { - // if this quantum is expired, nuke it - next = quant->next(); - if (curtime >= quant->m_expire) - m_quantum_allocator.reclaim(m_quantum_list.detach(*quant)); - - // if this quantum is shorter than us, we need to be inserted afterwards - else if (quant->m_requested <= quantum_attos) - insert_after = quant; - } - // if we found an exact match, just take the maximum expiry time - if (insert_after != nullptr && insert_after->m_requested == quantum_attos) - insert_after->m_expire = std::max(insert_after->m_expire, expire); - - // otherwise, allocate a new quantum and insert it after the one we picked - else - { - quantum_slot &quant = *m_quantum_allocator.alloc(); - quant.m_requested = quantum_attos; - quant.m_actual = std::max(quantum_attos, m_quantum_minimum); - quant.m_expire = expire; - m_quantum_list.insert_after(quant, insert_after); - } + // clear the callback timer global + m_callback_timer = nullptr; } @@ -989,7 +990,9 @@ void device_scheduler::dump_timers() const { machine().logerror("=============================================\n"); machine().logerror("Timer Dump: Time = %15s\n", time().as_string(PRECISION)); - for (emu_timer *timer = first_timer(); timer != nullptr; timer = timer->next()) + for (emu_timer *timer = m_timer_list; timer; timer = timer->m_next) + timer->dump(); + for (emu_timer *timer = m_inactive_timers; timer; timer = timer->m_next) timer->dump(); machine().logerror("=============================================\n"); } diff --git a/src/emu/schedule.h b/src/emu/schedule.h index f5631a6f816..b8515c0a00d 100644 --- a/src/emu/schedule.h +++ b/src/emu/schedule.h @@ -22,18 +22,7 @@ // MACROS //************************************************************************** -#define TIMER_CALLBACK_MEMBER(name) void name(void *ptr, s32 param) - -// macro for the RC time constant on a 74LS123 with C > 1000pF -// R is in ohms, C is in farads -#define TIME_OF_74LS123(r,c) (0.45 * (double)(r) * (double)(c)) - -// macros for the RC time constant on a 555 timer IC -// R is in ohms, C is in farads -#define PERIOD_OF_555_MONOSTABLE_NSEC(r,c) ((attoseconds_t)(1100000000 * (double)(r) * (double)(c))) -#define PERIOD_OF_555_ASTABLE_NSEC(r1,r2,c) ((attoseconds_t)( 693000000 * ((double)(r1) + 2.0 * (double)(r2)) * (double)(c))) -#define PERIOD_OF_555_MONOSTABLE(r,c) attotime::from_nsec(PERIOD_OF_555_MONOSTABLE_NSEC(r,c)) -#define PERIOD_OF_555_ASTABLE(r1,r2,c) attotime::from_nsec(PERIOD_OF_555_ASTABLE_NSEC(r1,r2,c)) +#define TIMER_CALLBACK_MEMBER(name) void name(s32 param) //************************************************************************** @@ -41,70 +30,66 @@ //************************************************************************** // timer callbacks look like this -typedef named_delegate<void (void *, s32)> timer_expired_delegate; +typedef named_delegate<void (s32)> timer_expired_delegate; // ======================> emu_timer class emu_timer { - friend class device_scheduler; - friend class simple_list<emu_timer>; - friend class fixed_allocator<emu_timer>; - friend class resource_pool_object<emu_timer>; - - // construction/destruction - emu_timer(); - ~emu_timer(); - - // allocation and re-use - emu_timer &init(running_machine &machine, timer_expired_delegate callback, void *ptr, bool temporary); - emu_timer &init(device_t &device, device_timer_id id, void *ptr, bool temporary); - emu_timer &release(); - public: // getters - emu_timer *next() const { return m_next; } - running_machine &machine() const { assert(m_machine != nullptr); return *m_machine; } - bool enabled() const { return m_enabled; } - int param() const { return m_param; } - void *ptr() const { return m_ptr; } + bool enabled() const noexcept { return m_enabled; } + s32 param() const noexcept { return m_param; } // setters - bool enable(bool enable = true); - void set_param(int param) { m_param = param; } - void set_ptr(void *ptr) { m_ptr = ptr; } + bool enable(bool enable = true) noexcept; + void set_param(s32 param) noexcept { m_param = param; } // control - void reset(const attotime &duration = attotime::never) { adjust(duration, m_param, m_period); } - void adjust(attotime start_delay, s32 param = 0, const attotime &periodicity = attotime::never); + void reset(const attotime &duration = attotime::never) noexcept { adjust(duration, m_param, m_period); } + void adjust(attotime start_delay, s32 param = 0, const attotime &periodicity = attotime::never) noexcept; // timing queries - attotime elapsed() const; - attotime remaining() const; - attotime start() const { return m_start; } - attotime expire() const { return m_expire; } - attotime period() const { return m_period; } + attotime elapsed() const noexcept; + attotime remaining() const noexcept; + attotime start() const noexcept { return m_start; } + attotime expire() const noexcept { return m_expire; } + attotime period() const noexcept { return m_period; } private: + // construction/destruction + emu_timer() noexcept; + ~emu_timer(); + + // allocation and re-use + emu_timer &init( + running_machine &machine, + timer_expired_delegate &&callback, + attotime start_delay, + s32 param, + bool temporary); + // internal helpers - void register_save(); - void schedule_next_period(); + void register_save(save_manager &manager) ATTR_COLD; + void schedule_next_period() noexcept; void dump() const; // internal state - running_machine * m_machine; // reference to the owning machine + device_scheduler * m_scheduler; // reference to the owning machine emu_timer * m_next; // next timer in order in the list emu_timer * m_prev; // previous timer in order in the list timer_expired_delegate m_callback; // callback function s32 m_param; // integer parameter - void * m_ptr; // pointer parameter bool m_enabled; // is the timer enabled? bool m_temporary; // is the timer temporary? attotime m_period; // the repeat frequency of the timer attotime m_start; // time when the timer was started attotime m_expire; // time when the timer will expire - device_t * m_device; // for device timers, a pointer to the device - device_timer_id m_id; // for device timers, the ID of the timer + u32 m_index; // needed to restore timers scheduled at the same time in correct order + + friend class device_scheduler; + friend class fixed_allocator<emu_timer>; + friend class simple_list<emu_timer>; // FIXME: fixed_allocator requires this }; @@ -121,27 +106,25 @@ public: ~device_scheduler(); // getters - running_machine &machine() const { return m_machine; } - attotime time() const; - emu_timer *first_timer() const { return m_timer_list; } - device_execute_interface *currently_executing() const { return m_executing_device; } + running_machine &machine() const noexcept { return m_machine; } + attotime time() const noexcept; + emu_timer *first_timer() const noexcept { return m_timer_list; } + device_execute_interface *currently_executing() const noexcept { return m_executing_device; } bool can_save() const; // execution void timeslice(); - void abort_timeslice(); + void abort_timeslice() noexcept; void trigger(int trigid, const attotime &after = attotime::zero); - void boost_interleave(const attotime ×lice_time, const attotime &boost_duration); + void add_quantum(const attotime &quantum, const attotime &duration); + void perfect_quantum(const attotime &duration); void suspend_resume_changed() { m_suspend_changes_pending = true; } // timers, specified by callback/name - emu_timer *timer_alloc(timer_expired_delegate callback, void *ptr = nullptr); - void timer_set(const attotime &duration, timer_expired_delegate callback, int param = 0, void *ptr = nullptr); - void synchronize(timer_expired_delegate callback = timer_expired_delegate(), int param = 0, void *ptr = nullptr) { timer_set(attotime::zero, callback, param, ptr); } - - // timers, specified by device/id; generally devices should use the device_t methods instead - emu_timer *timer_alloc(device_t &device, device_timer_id id = 0, void *ptr = nullptr); - void timer_set(const attotime &duration, device_t &device, device_timer_id id = 0, int param = 0, void *ptr = nullptr); + emu_timer *timer_alloc(timer_expired_delegate callback); + [[deprecated("timer_set is deprecated; please avoid anonymous timers. Use an allocated emu_timer instead.")]] + void timer_set(const attotime &duration, timer_expired_delegate callback, s32 param = 0); + void synchronize(timer_expired_delegate callback = timer_expired_delegate(), s32 param = 0); // debugging void dump_timers() const; @@ -151,7 +134,7 @@ public: private: // callbacks - void timed_trigger(void *ptr, s32 param); + void timed_trigger(s32 param); void presave(); void postload(); @@ -159,10 +142,9 @@ private: void compute_perfect_interleave(); void rebuild_execute_list(); void apply_suspend_changes(); - void add_scheduling_quantum(const attotime &quantum, const attotime &duration); // timer helpers - emu_timer &timer_list_insert(emu_timer &timer); + template <bool CheckIndex = false> emu_timer &timer_list_insert(emu_timer &timer); emu_timer &timer_list_remove(emu_timer &timer); void execute_timers(); @@ -174,6 +156,7 @@ private: // list of active timers emu_timer * m_timer_list; // head of the active list + emu_timer * m_inactive_timers; // head of the inactive timer list fixed_allocator<emu_timer> m_timer_allocator; // allocator for timers // other internal states diff --git a/src/emu/screen.cpp b/src/emu/screen.cpp index 103230ba114..0d8e35d6e4c 100644 --- a/src/emu/screen.cpp +++ b/src/emu/screen.cpp @@ -2,21 +2,27 @@ // copyright-holders:Aaron Giles /*************************************************************************** - screen.c + screen.cpp Core MAME screen device. ***************************************************************************/ #include "emu.h" +#include "screen.h" + #include "emuopts.h" -#include "png.h" +#include "fileio.h" +#include "main.h" +#include "render.h" #include "rendutil.h" -#include <nanosvg/src/nanosvg.h> -#include <nanosvg/src/nanosvgrast.h> +#include "nanosvg.h" +#include "png.h" + #include <set> + //************************************************************************** // DEBUGGING //************************************************************************** @@ -40,7 +46,6 @@ u32 screen_device::m_id_counter = 0; class screen_device::svg_renderer { public: svg_renderer(memory_region *region); - ~svg_renderer(); int width() const; int height() const; @@ -66,10 +71,10 @@ private: int x0, y0, x1, y1; }; - NSVGimage *m_image; - NSVGrasterizer *m_rasterizer; + util::nsvg_image_ptr m_image; + util::nsvg_rasterizer_ptr m_rasterizer; std::vector<bool> m_key_state; - std::vector<std::list<NSVGshape *>> m_keyed_shapes; + std::vector<std::vector<NSVGshape *>> m_keyed_shapes; std::unordered_map<std::string, int> m_key_ids; int m_key_count; @@ -91,23 +96,21 @@ private: screen_device::svg_renderer::svg_renderer(memory_region *region) { - char *s = new char[region->bytes()+1]; - memcpy(s, region->base(), region->bytes()); + const std::unique_ptr<char []> s(new char[region->bytes() + 1]); + memcpy(s.get(), region->base(), region->bytes()); s[region->bytes()] = 0; - m_image = nsvgParse(s, "px", 72); - delete[] s; - m_rasterizer = nsvgCreateRasterizer(); + m_image.reset(nsvgParse(s.get(), "px", 72)); + m_rasterizer.reset(nsvgCreateRasterizer()); m_key_count = 0; - for (NSVGshape *shape = m_image->shapes; shape != nullptr; shape = shape->next) + for (NSVGshape *shape = m_image->shapes; shape; shape = shape->next) if(shape->title[0]) { - auto it = m_key_ids.find(shape->title); + const auto it = m_key_ids.find(shape->title); if(it != m_key_ids.end()) m_keyed_shapes[it->second].push_back(shape); else { - int id = m_key_count; - m_key_count++; + const int id = m_key_count++; m_keyed_shapes.resize(m_key_count); m_keyed_shapes[id].push_back(shape); m_key_ids[shape->title] = id; @@ -122,12 +125,6 @@ screen_device::svg_renderer::svg_renderer(memory_region *region) osd_printf_verbose("Parsed SVG '%s', aspect ratio %f\n", region->name(), (m_image->height == 0.0f) ? 0 : m_image->width / m_image->height); } -screen_device::svg_renderer::~svg_renderer() -{ - nsvgDeleteRasterizer(m_rasterizer); - nsvgDelete(m_image); -} - int screen_device::svg_renderer::width() const { return int(m_image->width + 0.5); @@ -149,7 +146,7 @@ void screen_device::svg_renderer::render_state(std::vector<u32> &dest, const std s->flags &= ~NSVG_FLAGS_VISIBLE; } - nsvgRasterize(m_rasterizer, m_image, 0, 0, m_scale, (unsigned char *)&dest[0], m_sx, m_sy, m_sx*4); + nsvgRasterize(m_rasterizer.get(), m_image.get(), 0, 0, m_scale, (unsigned char *)&dest[0], m_sx, m_sy, m_sx*4); // Nanosvg generates non-premultiplied alpha, so remultiply by // alpha to "blend" against a black background. Plus align the @@ -542,12 +539,15 @@ screen_device::screen_device(const machine_config &mconfig, const char *tag, dev , m_yoffset(0.0f) , m_xscale(1.0f) , m_yscale(1.0f) + , m_screen_update_ind16(*this) + , m_screen_update_rgb32(*this) , m_screen_vblank(*this) , m_scanline_cb(*this) , m_palette(*this, finder_base::DUMMY_TAG) , m_video_attributes(0) - , m_svg_region(tag) + , m_svg_region(*this, DEVICE_SELF) , m_container(nullptr) + , m_max_width(100) , m_width(100) , m_height(100) , m_visarea(0, 99, 0, 99) @@ -555,6 +555,7 @@ screen_device::screen_device(const machine_config &mconfig, const char *tag, dev , m_curbitmap(0) , m_curtexture(0) , m_changed(true) + , m_last_partial_reset(attotime::zero) , m_last_partial_scan(0) , m_partial_scan_hpos(0) , m_color(rgb_t(0xff, 0xff, 0xff, 0xff)) @@ -584,10 +585,81 @@ screen_device::screen_device(const machine_config &mconfig, const char *tag, dev screen_device::~screen_device() { + destroy_scan_bitmaps(); } //------------------------------------------------- +// destroy_scan_bitmaps - destroy per-scanline +// bitmaps if applicable +//------------------------------------------------- + +void screen_device::destroy_scan_bitmaps() +{ + if (m_video_attributes & VIDEO_VARIABLE_WIDTH) + { + const bool screen16 = !m_screen_update_ind16.isnull(); + for (int j = 0; j < 2; j++) + { + for (bitmap_t* bitmap : m_scan_bitmaps[j]) + { + if (screen16) + delete (bitmap_ind16*)bitmap; + else + delete (bitmap_rgb32*)bitmap; + } + m_scan_bitmaps[j].clear(); + } + } +} + + +//------------------------------------------------- +// allocate_scan_bitmaps - allocate per-scanline +// bitmaps if applicable +//------------------------------------------------- + +void screen_device::allocate_scan_bitmaps() +{ + if (m_video_attributes & VIDEO_VARIABLE_WIDTH) + { + const bool screen16 = !m_screen_update_ind16.isnull(); + s32 effwidth = std::max(m_max_width, m_visarea.right() + 1); + const s32 old_height = (s32)m_scan_widths.size(); + s32 effheight = std::max(m_height, m_visarea.bottom() + 1); + if (old_height < effheight) + { + for (int i = old_height; i < effheight; i++) + { + for (int j = 0; j < 2; j++) + { + if (screen16) + m_scan_bitmaps[j].push_back(new bitmap_ind16(effwidth, 1)); + else + m_scan_bitmaps[j].push_back(new bitmap_rgb32(effwidth, 1)); + } + m_scan_widths.push_back(effwidth); + } + } + else + { + for (int i = old_height - 1; i >= effheight; i--) + { + for (int j = 0; j < 2; j++) + { + if (screen16) + delete (bitmap_ind16 *)m_scan_bitmaps[j][i]; + else + delete (bitmap_rgb32 *)m_scan_bitmaps[j][i]; + m_scan_bitmaps[j].erase(m_scan_bitmaps[j].begin() + i); + } + m_scan_widths.erase(m_scan_widths.begin() + i); + } + } + } +} + +//------------------------------------------------- // device_validity_check - verify device // configuration //------------------------------------------------- @@ -608,10 +680,15 @@ void screen_device::device_validity_check(validity_checker &valid) const if (m_screen_update_ind16.isnull() && m_screen_update_rgb32.isnull()) osd_printf_error("Missing SCREEN_UPDATE function\n"); } + else + { + if (m_video_attributes & VIDEO_VARIABLE_WIDTH) + osd_printf_error("Non-raster display cannot have a variable width\n"); + } - // check for zero frame rate - if (m_refresh == 0) - osd_printf_error("Invalid (zero) refresh rate\n"); + // check for invalid frame rate + if (m_refresh == 0 || m_refresh > ATTOSECONDS_PER_SECOND) + osd_printf_error("Invalid (under 1Hz) refresh rate\n"); texture_format texformat = !m_screen_update_ind16.isnull() ? TEXFORMAT_PALETTE16 : TEXFORMAT_RGB32; if (m_palette.finder_tag() != finder_base::DUMMY_TAG) @@ -694,10 +771,8 @@ std::pair<unsigned, unsigned> screen_device::physical_aspect() const void screen_device::device_resolve_objects() { // bind our handlers - m_screen_update_ind16.bind_relative_to(*owner()); - m_screen_update_rgb32.bind_relative_to(*owner()); - m_screen_vblank.resolve_safe(); - m_scanline_cb.resolve(); + m_screen_update_ind16.resolve(); + m_screen_update_rgb32.resolve(); // assign our format to the palette before it starts if (m_palette) @@ -717,11 +792,10 @@ void screen_device::device_start() if (m_type == SCREEN_TYPE_SVG) { - memory_region *reg = owner()->memregion(m_svg_region); - if (!reg) - fatalerror("%s: SVG region \"%s\" does not exist\n", tag(), m_svg_region); - m_svg = std::make_unique<svg_renderer>(reg); - machine().output().set_notifier(nullptr, svg_renderer::output_notifier, m_svg.get()); + if (!m_svg_region) + fatalerror("%s: SVG region \"%s\" does not exist\n", tag(), m_svg_region.finder_tag()); + m_svg = std::make_unique<svg_renderer>(m_svg_region); + machine().output().set_global_notifier(svg_renderer::output_notifier, m_svg.get()); // don't do this - SVG units are arbitrary and interpreting them as pixels causes bad things to happen // just render at the size/aspect ratio supplied by the driver @@ -736,7 +810,8 @@ void screen_device::device_start() // configure bitmap formats and allocate screen bitmaps // svg is RGB32 too, and doesn't have any update method - texture_format texformat = !m_screen_update_ind16.isnull() ? TEXFORMAT_PALETTE16 : TEXFORMAT_RGB32; + const bool screen16 = !m_screen_update_ind16.isnull(); + texture_format texformat = screen16 ? TEXFORMAT_PALETTE16 : TEXFORMAT_RGB32; for (auto & elem : m_bitmap) { @@ -752,8 +827,7 @@ void screen_device::device_start() m_texture[1]->set_id((u64(m_unique_id) << 57) | 1); // configure the default cliparea - render_container::user_settings settings; - m_container->get_user_settings(settings); + render_container::user_settings settings = m_container->get_user_settings(); settings.m_xoffset = m_xoffset; settings.m_yoffset = m_yoffset; settings.m_xscale = m_xscale; @@ -761,15 +835,15 @@ void screen_device::device_start() m_container->set_user_settings(settings); // allocate the VBLANK timers - m_vblank_begin_timer = timer_alloc(TID_VBLANK_START); - m_vblank_end_timer = timer_alloc(TID_VBLANK_END); + m_vblank_begin_timer = timer_alloc(FUNC(screen_device::vblank_begin), this); + m_vblank_end_timer = timer_alloc(FUNC(screen_device::vblank_end), this); // allocate a timer to reset partial updates - m_scanline0_timer = timer_alloc(TID_SCANLINE0); + m_scanline0_timer = timer_alloc(FUNC(screen_device::first_scanline_tick), this); // allocate a timer to generate per-scanline updates - if ((m_video_attributes & VIDEO_UPDATE_SCANLINE) != 0 || m_scanline_cb) - m_scanline_timer = timer_alloc(TID_SCANLINE); + if ((m_video_attributes & VIDEO_UPDATE_SCANLINE) != 0 || !m_scanline_cb.isunset()) + m_scanline_timer = timer_alloc(FUNC(screen_device::scanline_tick), this); // configure the screen with the default parameters configure(m_width, m_height, m_visarea, m_refresh); @@ -779,7 +853,7 @@ void screen_device::device_start() m_vblank_end_time = attotime(0, m_vblank_period); // start the timer to generate per-scanline updates - if ((m_video_attributes & VIDEO_UPDATE_SCANLINE) != 0 || m_scanline_cb) + if ((m_video_attributes & VIDEO_UPDATE_SCANLINE) != 0 || !m_scanline_cb.isunset()) m_scanline_timer->adjust(time_until_pos(0)); // create burn-in bitmap @@ -804,6 +878,7 @@ void screen_device::device_start() save_item(NAME(m_visarea.min_y)); save_item(NAME(m_visarea.max_x)); save_item(NAME(m_visarea.max_y)); + save_item(NAME(m_last_partial_reset)); save_item(NAME(m_last_partial_scan)); save_item(NAME(m_frame_period)); save_item(NAME(m_brightness)); @@ -814,9 +889,9 @@ void screen_device::device_start() save_item(NAME(m_vblank_end_time)); save_item(NAME(m_frame_number)); if (m_oldstyle_vblank_supplied) - logerror("%s: Deprecated legacy Old Style screen configured (MCFG_SCREEN_VBLANK_TIME), please use MCFG_SCREEN_RAW_PARAMS instead.\n",this->tag()); + logerror("%s: Deprecated legacy Old Style screen configured (set_vblank_time), please use set_raw instead.\n",this->tag()); - m_is_primary_screen = (this == screen_device_iterator(machine().root_device()).first()); + m_is_primary_screen = (this == screen_device_enumerator(machine().root_device()).first()); } @@ -857,46 +932,39 @@ void screen_device::device_post_load() //------------------------------------------------- -// device_timer - called whenever a device timer -// fires +// timer events //------------------------------------------------- -void screen_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +TIMER_CALLBACK_MEMBER(screen_device::first_scanline_tick) { - switch (id) + // first scanline + reset_partial_updates(); + if (m_video_attributes & VIDEO_VARIABLE_WIDTH) { - // signal VBLANK start - case TID_VBLANK_START: - vblank_begin(); - break; - - // signal VBLANK end - case TID_VBLANK_END: - vblank_end(); - break; - - // first scanline - case TID_SCANLINE0: - reset_partial_updates(); - break; + pre_update_scanline(0); + } +} - // subsequent scanlines when scanline updates are enabled - case TID_SCANLINE: - if (m_video_attributes & VIDEO_UPDATE_SCANLINE) - { - // force a partial update to the current scanline - update_partial(param); - } - if (m_scanline_cb) - m_scanline_cb(param); - - // compute the next visible scanline - param++; - if (param > m_visarea.bottom()) - param = m_visarea.top(); - m_scanline_timer->adjust(time_until_pos(param), param); - break; +TIMER_CALLBACK_MEMBER(screen_device::scanline_tick) +{ + // subsequent scanlines when scanline updates are enabled + if (m_video_attributes & VIDEO_VARIABLE_WIDTH) + { + pre_update_scanline(param); + } + if (m_video_attributes & VIDEO_UPDATE_SCANLINE) + { + // force a partial update to the current scanline + update_partial(param); } + if (!m_scanline_cb.isunset()) + m_scanline_cb(param); + + // compute the next visible scanline + param++; + if (param > m_visarea.bottom()) + param = m_visarea.top(); + m_scanline_timer->adjust(time_until_pos(param), param); } @@ -918,11 +986,12 @@ void screen_device::configure(int width, int height, const rectangle &visarea, a assert(frame_period > 0); // fill in the new parameters + m_max_width = std::max(m_max_width, width); m_width = width; m_height = height; m_visarea = visarea; - // reallocate bitmap if necessary + // reallocate bitmap(s) if necessary realloc_screen_bitmaps(); // compute timing parameters @@ -944,7 +1013,7 @@ void screen_device::configure(int width, int height, const rectangle &visarea, a // call the VBLANK start timer now; otherwise, adjust it for the future attoseconds_t delta = (machine().time() - m_vblank_start_time).as_attoseconds(); if (delta >= m_frame_period) - vblank_begin(); + vblank_begin(0); else m_vblank_begin_timer->adjust(time_until_vblank_start()); @@ -972,19 +1041,41 @@ void screen_device::reset_origin(int beamy, int beamx) m_vblank_end_time = curtime - attotime(0, beamy * m_scantime + beamx * m_pixeltime); m_vblank_start_time = m_vblank_end_time - attotime(0, m_vblank_period); + // if we are resetting relative to (visarea.bottom() + 1, 0) == VBLANK start, + // call the VBLANK start timer now; otherwise, adjust it for the future + if (beamy == ((m_visarea.bottom() + 1) % m_height) && beamx == 0) + vblank_begin(0); + else + m_vblank_begin_timer->adjust(time_until_vblank_start()); + // if we are resetting relative to (0,0) == VBLANK end, call the // scanline 0 timer by hand now; otherwise, adjust it for the future if (beamy == 0 && beamx == 0) reset_partial_updates(); else m_scanline0_timer->adjust(time_until_pos(0)); +} - // if we are resetting relative to (visarea.bottom() + 1, 0) == VBLANK start, - // call the VBLANK start timer now; otherwise, adjust it for the future - if (beamy == ((m_visarea.bottom() + 1) % m_height) && beamx == 0) - vblank_begin(); - else - m_vblank_begin_timer->adjust(time_until_vblank_start()); + +//------------------------------------------------- +// update_scan_bitmap_size - reallocate the +// bitmap for a specific scanline +//------------------------------------------------- + +void screen_device::update_scan_bitmap_size(int y) +{ + // don't update this line if it exceeds the allocated size, which can happen on initial configuration + if (y >= m_scan_widths.size()) + return; + + // determine effective size to allocate + s32 effwidth = std::max(m_max_width, m_visarea.right() + 1); + + if (m_scan_widths[y] == effwidth) + return; + + m_scan_bitmaps[m_curbitmap][y]->resize(effwidth, 1); + m_scan_widths[y] = effwidth; } @@ -1000,10 +1091,11 @@ void screen_device::realloc_screen_bitmaps() return; // determine effective size to allocate - s32 effwidth = std::max(m_width, m_visarea.right() + 1); + const bool per_scanline = (m_video_attributes & VIDEO_VARIABLE_WIDTH); + s32 effwidth = std::max(per_scanline ? m_max_width : m_width, m_visarea.right() + 1); s32 effheight = std::max(m_height, m_visarea.bottom() + 1); - // reize all registered screen bitmaps + // resize all registered screen bitmaps for (auto &item : m_auto_bitmap_list) item->m_bitmap.resize(effwidth, effheight); @@ -1015,6 +1107,19 @@ void screen_device::realloc_screen_bitmaps() } m_texture[0]->set_bitmap(m_bitmap[0], m_visarea, m_bitmap[0].texformat()); m_texture[1]->set_bitmap(m_bitmap[1], m_visarea, m_bitmap[1].texformat()); + + allocate_scan_bitmaps(); +} + + +//------------------------------------------------- +// pre_update_scanline - check if the bitmap for +// a specific scanline needs its size updated +//------------------------------------------------- + +void screen_device::pre_update_scanline(int y) +{ + update_scan_bitmap_size(y); } @@ -1065,11 +1170,17 @@ bool screen_device::update_partial(int scanline) return false; } + // skip if we already rendered this frame + // this can happen if a cpu timeslice that called update_partial is in the previous frame while scanline 0 already started + if (m_last_partial_scan == 0 && m_last_partial_reset > machine().time()) + { + LOG_PARTIAL_UPDATES(("skipped because frame was already rendered\n")); + return false; + } + // set the range of scanlines to render rectangle clip(m_visarea); - clip.sety( - (std::max)(clip.top(), m_last_partial_scan), - (std::min)(clip.bottom(), scanline)); + clip.sety((std::max)(clip.top(), m_last_partial_scan), (std::min)(clip.bottom(), scanline)); // skip if entirely outside of visible area if (clip.top() > clip.bottom()) @@ -1080,32 +1191,56 @@ bool screen_device::update_partial(int scanline) // otherwise, render LOG_PARTIAL_UPDATES(("updating %d-%d\n", clip.top(), clip.bottom())); - g_profiler.start(PROFILER_VIDEO); - u32 flags; - if (m_type != SCREEN_TYPE_SVG) + u32 flags = 0; { - screen_bitmap &curbitmap = m_bitmap[m_curbitmap]; - switch (curbitmap.format()) + auto profile = g_profiler.start(PROFILER_VIDEO); + if (m_video_attributes & VIDEO_VARIABLE_WIDTH) { - default: - case BITMAP_FORMAT_IND16: flags = m_screen_update_ind16(*this, curbitmap.as_ind16(), clip); break; - case BITMAP_FORMAT_RGB32: flags = m_screen_update_rgb32(*this, curbitmap.as_rgb32(), clip); break; + rectangle scan_clip(clip); + for (int y = clip.top(); y <= clip.bottom(); y++) + { + scan_clip.sety(y, y); + pre_update_scanline(y); + + screen_bitmap &curbitmap = m_bitmap[m_curbitmap]; + switch (curbitmap.format()) + { + default: + case BITMAP_FORMAT_IND16: flags |= m_screen_update_ind16(*this, *(bitmap_ind16 *)m_scan_bitmaps[m_curbitmap][y], scan_clip); break; + case BITMAP_FORMAT_RGB32: flags |= m_screen_update_rgb32(*this, *(bitmap_rgb32 *)m_scan_bitmaps[m_curbitmap][y], scan_clip); break; + } + + m_partial_updates_this_frame++; + } } + else + { + if (m_type != SCREEN_TYPE_SVG) + { + screen_bitmap &curbitmap = m_bitmap[m_curbitmap]; + switch (curbitmap.format()) + { + default: + case BITMAP_FORMAT_IND16: flags = m_screen_update_ind16(*this, curbitmap.as_ind16(), clip); break; + case BITMAP_FORMAT_RGB32: flags = m_screen_update_rgb32(*this, curbitmap.as_rgb32(), clip); break; + } + } + else + { + flags = m_svg->render(*this, m_bitmap[m_curbitmap].as_rgb32(), clip); + } + m_partial_updates_this_frame++; + } + // stop profiling } - else - { - flags = m_svg->render(*this, m_bitmap[m_curbitmap].as_rgb32(), clip); - } - - m_partial_updates_this_frame++; - g_profiler.stop(); // if we modified the bitmap, we have to commit m_changed |= ~flags & UPDATE_HAS_NOT_CHANGED; // remember where we left off m_last_partial_scan = scanline + 1; + m_partial_scan_hpos = 0; return true; } @@ -1139,6 +1274,28 @@ void screen_device::update_now() int current_hpos = hpos(); rectangle clip = m_visarea; + // skip if we already rendered this line + if (current_vpos < m_last_partial_scan) + { + LOG_PARTIAL_UPDATES(("skipped because line was already rendered\n")); + return; + } + + // if beam position is the same, there's nothing to update + if (current_vpos == m_last_partial_scan && current_hpos == m_partial_scan_hpos) + { + LOG_PARTIAL_UPDATES(("skipped because beam position is unchanged\n")); + return; + } + + // skip if we already rendered this frame + // this can happen if a cpu timeslice that called update_now is in the previous frame while scanline 0 already started + if (m_last_partial_scan == 0 && m_partial_scan_hpos == 0 && m_last_partial_reset > machine().time()) + { + LOG_PARTIAL_UPDATES(("skipped because frame was already rendered\n")); + return; + } + LOG_PARTIAL_UPDATES(("update_now(): Y=%d, X=%d, last partial %d, partial hpos %d (vis %d %d)\n", current_vpos, current_hpos, m_last_partial_scan, m_partial_scan_hpos, m_visarea.right(), m_visarea.bottom())); // start off by doing a partial update up to the line before us, in case that was necessary @@ -1148,30 +1305,46 @@ void screen_device::update_now() if (m_partial_scan_hpos > 0) { // now finish the previous partial scanline - clip.set( - (std::max)(clip.left(), m_partial_scan_hpos), - clip.right(), - (std::max)(clip.top(), m_last_partial_scan), - (std::min)(clip.bottom(), m_last_partial_scan)); + clip.set((std::max)(clip.left(), m_partial_scan_hpos), + clip.right(), + (std::max)(clip.top(), m_last_partial_scan), + (std::min)(clip.bottom(), m_last_partial_scan)); // if there's something to draw, do it if (!clip.empty()) { - g_profiler.start(PROFILER_VIDEO); + auto profile = g_profiler.start(PROFILER_VIDEO); + u32 flags = 0; screen_bitmap &curbitmap = m_bitmap[m_curbitmap]; - switch (curbitmap.format()) + if (m_video_attributes & VIDEO_VARIABLE_WIDTH) { - default: - case BITMAP_FORMAT_IND16: m_screen_update_ind16(*this, curbitmap.as_ind16(), clip); break; - case BITMAP_FORMAT_RGB32: m_screen_update_rgb32(*this, curbitmap.as_rgb32(), clip); break; + pre_update_scanline(m_last_partial_scan); + switch (curbitmap.format()) + { + default: + case BITMAP_FORMAT_IND16: flags = m_screen_update_ind16(*this, *(bitmap_ind16 *)m_scan_bitmaps[m_curbitmap][m_last_partial_scan], clip); break; + case BITMAP_FORMAT_RGB32: flags = m_screen_update_rgb32(*this, *(bitmap_rgb32 *)m_scan_bitmaps[m_curbitmap][m_last_partial_scan], clip); break; + } + } + else + { + switch (curbitmap.format()) + { + default: + case BITMAP_FORMAT_IND16: flags = m_screen_update_ind16(*this, curbitmap.as_ind16(), clip); break; + case BITMAP_FORMAT_RGB32: flags = m_screen_update_rgb32(*this, curbitmap.as_rgb32(), clip); break; + } } m_partial_updates_this_frame++; - g_profiler.stop(); - m_partial_scan_hpos = 0; - m_last_partial_scan++; + + // if we modified the bitmap, we have to commit + m_changed |= ~flags & UPDATE_HAS_NOT_CHANGED; } + + m_partial_scan_hpos = 0; + m_last_partial_scan++; } if (current_vpos > m_last_partial_scan) { @@ -1180,47 +1353,54 @@ void screen_device::update_now() } // now draw this partial scanline - clip = m_visarea; - - clip.set( - (std::max)(clip.left(), m_partial_scan_hpos), - (std::min)(clip.right(), current_hpos), - (std::max)(clip.top(), current_vpos), - (std::min)(clip.bottom(), current_vpos)); - - // and if there's something to draw, do it - if (!clip.empty()) + if (current_hpos > 0) { - g_profiler.start(PROFILER_VIDEO); + clip = m_visarea; - LOG_PARTIAL_UPDATES(("doing scanline partial draw: Y %d X %d-%d\n", clip.bottom(), clip.left(), clip.right())); + clip.set((std::max)(clip.left(), m_partial_scan_hpos), + (std::min)(clip.right(), current_hpos - 1), + (std::max)(clip.top(), current_vpos), + (std::min)(clip.bottom(), current_vpos)); - u32 flags; - screen_bitmap &curbitmap = m_bitmap[m_curbitmap]; - switch (curbitmap.format()) + // and if there's something to draw, do it + if (!clip.empty()) { - default: - case BITMAP_FORMAT_IND16: flags = m_screen_update_ind16(*this, curbitmap.as_ind16(), clip); break; - case BITMAP_FORMAT_RGB32: flags = m_screen_update_rgb32(*this, curbitmap.as_rgb32(), clip); break; - } + auto profile = g_profiler.start(PROFILER_VIDEO); - m_partial_updates_this_frame++; - g_profiler.stop(); + LOG_PARTIAL_UPDATES(("doing scanline partial draw: Y %d X %d-%d\n", clip.bottom(), clip.left(), clip.right())); - // if we modified the bitmap, we have to commit - m_changed |= ~flags & UPDATE_HAS_NOT_CHANGED; + u32 flags = 0; + screen_bitmap &curbitmap = m_bitmap[m_curbitmap]; + if (m_video_attributes & VIDEO_VARIABLE_WIDTH) + { + pre_update_scanline(current_vpos); + switch (curbitmap.format()) + { + default: + case BITMAP_FORMAT_IND16: flags = m_screen_update_ind16(*this, *(bitmap_ind16 *)m_scan_bitmaps[m_curbitmap][current_vpos], clip); break; + case BITMAP_FORMAT_RGB32: flags = m_screen_update_rgb32(*this, *(bitmap_rgb32 *)m_scan_bitmaps[m_curbitmap][current_vpos], clip); break; + } + } + else + { + switch (curbitmap.format()) + { + default: + case BITMAP_FORMAT_IND16: flags = m_screen_update_ind16(*this, curbitmap.as_ind16(), clip); break; + case BITMAP_FORMAT_RGB32: flags = m_screen_update_rgb32(*this, curbitmap.as_rgb32(), clip); break; + } + } - // remember where we left off - m_partial_scan_hpos = current_hpos; - m_last_partial_scan = current_vpos; + m_partial_updates_this_frame++; - // if we completed the line, mark it so - if (current_hpos >= m_visarea.right()) - { - m_partial_scan_hpos = 0; - m_last_partial_scan = current_vpos + 1; + // if we modified the bitmap, we have to commit + m_changed |= ~flags & UPDATE_HAS_NOT_CHANGED; } } + + // remember where we left off + m_partial_scan_hpos = current_hpos; + m_last_partial_scan = current_vpos; } @@ -1231,6 +1411,7 @@ void screen_device::update_now() void screen_device::reset_partial_updates() { + m_last_partial_reset = machine().time(); m_last_partial_scan = 0; m_partial_scan_hpos = 0; m_partial_updates_this_frame = 0; @@ -1255,21 +1436,28 @@ u32 screen_device::pixel(s32 x, s32 y) if (x < 0 || y < 0 || x >= srcwidth || y >= srcheight) return 0; + const bool per_scanline = (m_video_attributes & VIDEO_VARIABLE_WIDTH); + switch (curbitmap.format()) { case BITMAP_FORMAT_IND16: { - bitmap_ind16 &srcbitmap = curbitmap.as_ind16(); - const u16 src = srcbitmap.pix(y, x); + bitmap_ind16 &srcbitmap = per_scanline ? *(bitmap_ind16 *)m_scan_bitmaps[m_curbitmap][y] : curbitmap.as_ind16(); + const u16 src = per_scanline ? srcbitmap.pix(0, x) : srcbitmap.pix(y, x); const rgb_t *palette = m_palette->palette()->entry_list_adjusted(); return (u32)palette[src]; } case BITMAP_FORMAT_RGB32: { - // iterate over rows in the destination - bitmap_rgb32 &srcbitmap = curbitmap.as_rgb32(); - return (u32)srcbitmap.pix(y, x); + if (per_scanline) + { + return (u32)(*(bitmap_rgb32 *)m_scan_bitmaps[m_curbitmap][y]).pix(0, x); + } + else + { + return (u32)curbitmap.as_rgb32().pix(y, x); + } } default: @@ -1291,15 +1479,17 @@ void screen_device::pixels(u32 *buffer) const rectangle &visarea = visible_area(); + const bool per_scanline = (m_video_attributes & VIDEO_VARIABLE_WIDTH); + switch (curbitmap.format()) { case BITMAP_FORMAT_IND16: { - bitmap_ind16 &srcbitmap = curbitmap.as_ind16(); const rgb_t *palette = m_palette->palette()->entry_list_adjusted(); for (int y = visarea.min_y; y <= visarea.max_y; y++) { - const u16 *src = &srcbitmap.pix(y, visarea.min_x); + bitmap_ind16 &srcbitmap = per_scanline ? *(bitmap_ind16 *)m_scan_bitmaps[m_curbitmap][y] : curbitmap.as_ind16(); + const u16 *src = &srcbitmap.pix(per_scanline ? 0 : y, visarea.min_x); for (int x = visarea.min_x; x <= visarea.max_x; x++) { *buffer++ = palette[*src++]; @@ -1310,10 +1500,10 @@ void screen_device::pixels(u32 *buffer) case BITMAP_FORMAT_RGB32: { - bitmap_rgb32 &srcbitmap = curbitmap.as_rgb32(); for (int y = visarea.min_y; y <= visarea.max_y; y++) { - const u32 *src = &srcbitmap.pix(y, visarea.min_x); + bitmap_rgb32 &srcbitmap = per_scanline ? *(bitmap_rgb32 *)m_scan_bitmaps[m_curbitmap][y] : curbitmap.as_rgb32(); + const u32 *src = &srcbitmap.pix(per_scanline ? 0 : y, visarea.min_x); for (int x = visarea.min_x; x <= visarea.max_x; x++) { *buffer++ = *src++; @@ -1462,7 +1652,7 @@ void screen_device::register_screen_bitmap(bitmap_t &bitmap) // signal the VBLANK period has begun //------------------------------------------------- -void screen_device::vblank_begin() +TIMER_CALLBACK_MEMBER(screen_device::vblank_begin) { // reset the starting VBLANK time m_vblank_start_time = machine().time(); @@ -1482,7 +1672,7 @@ void screen_device::vblank_begin() // if no VBLANK period, call the VBLANK end callback immediately, otherwise reset the timer if (m_vblank_period == 0) - vblank_end(); + vblank_end(0); else m_vblank_end_timer->adjust(time_until_vblank_end()); } @@ -1493,7 +1683,7 @@ void screen_device::vblank_begin() // signal the VBLANK period has ended //------------------------------------------------- -void screen_device::vblank_end() +TIMER_CALLBACK_MEMBER(screen_device::vblank_end) { // call the screen specific callbacks for (auto &item : m_callback_list) @@ -1510,6 +1700,58 @@ void screen_device::vblank_end() //------------------------------------------------- +// create_composited_bitmap - composite scanline +// bitmaps into the output bitmap +//------------------------------------------------- + +void screen_device::create_composited_bitmap() +{ + screen_bitmap &curbitmap = m_bitmap[m_curtexture]; + if (!curbitmap.valid()) + return; + + s32 dstwidth = std::max(m_max_width, m_visarea.right() + 1); + int dstheight = curbitmap.height(); + + switch (curbitmap.format()) + { + default: + case BITMAP_FORMAT_IND16: + { + for (int y = 0; y < dstheight; y++) + { + const bitmap_ind16 &srcbitmap = *(bitmap_ind16 *)m_scan_bitmaps[m_curbitmap][y]; + u16 *dst = &curbitmap.as_ind16().pix(y); + const u16 *src = &srcbitmap.pix(0); + const int dx = (m_scan_widths[y] << 15) / dstwidth; + for (int x = 0; x < m_scan_widths[y]; x += dx) + { + *dst++ = src[x >> 15]; + } + } + break; + } + + case BITMAP_FORMAT_RGB32: + { + for (int y = 0; y < dstheight; y++) + { + const bitmap_rgb32 &srcbitmap = *(bitmap_rgb32 *)m_scan_bitmaps[m_curbitmap][y]; + u32 *dst = &curbitmap.as_rgb32().pix(y); + const u32 *src = &srcbitmap.pix(0); + const int dx = (m_scan_widths[y] << 15) / dstwidth; + for (int x = 0; x < dstwidth << 15; x += dx) + { + *dst++ = src[x >> 15]; + } + } + break; + } + } +} + + +//------------------------------------------------- // update_quads - set up the quads for this // screen //------------------------------------------------- @@ -1525,6 +1767,10 @@ bool screen_device::update_quads() // if we're not skipping the frame and if the screen actually changed, then update the texture if (!machine().video().skip_this_frame() && m_changed) { + if (m_video_attributes & VIDEO_VARIABLE_WIDTH) + { + create_composited_bitmap(); + } m_texture[m_curbitmap]->set_bitmap(m_bitmap[m_curbitmap], m_visarea, m_bitmap[m_curbitmap].texformat()); m_curtexture = m_curbitmap; m_curbitmap = 1 - m_curbitmap; @@ -1552,7 +1798,8 @@ bool screen_device::update_quads() void screen_device::update_burnin() { -// TODO: other than being unnecessary (we should use our rand function first off), this is a simplification of how analog signals really works! +// TODO: other than being unnecessary, this is a simplification of how analog signals really works! +// It's important not to use machine().rand() here, it can cause machine().rand() used in emulation to desync. #undef rand if (!m_burnin.valid()) return; @@ -1569,8 +1816,8 @@ void screen_device::update_burnin() int ystep = (srcheight << 16) / dstheight; int xstart = (u32(rand()) % 32767) * xstep / 32767; int ystart = (u32(rand()) % 32767) * ystep / 32767; - int srcx, srcy; - int x, y; + + bool per_scanline = (m_video_attributes & VIDEO_VARIABLE_WIDTH); switch (curbitmap.format()) { @@ -1578,13 +1825,13 @@ void screen_device::update_burnin() case BITMAP_FORMAT_IND16: { // iterate over rows in the destination - bitmap_ind16 &srcbitmap = curbitmap.as_ind16(); - for (y = 0, srcy = ystart; y < dstheight; y++, srcy += ystep) + for (int y = 0, srcy = ystart; y < dstheight; y++, srcy += ystep) { - u64 *dst = &m_burnin.pix64(y); - const u16 *src = &srcbitmap.pix16(srcy >> 16); - const rgb_t *palette = m_palette->palette()->entry_list_adjusted(); - for (x = 0, srcx = xstart; x < dstwidth; x++, srcx += xstep) + const bitmap_ind16 &srcbitmap = per_scanline ? *(bitmap_ind16 *)m_scan_bitmaps[m_curbitmap][y] : curbitmap.as_ind16(); + u64 *const dst = &m_burnin.pix(y); + u16 const *const src = &srcbitmap.pix(per_scanline ? 0 : (srcy >> 16)); + rgb_t const *const palette = m_palette->palette()->entry_list_adjusted(); + for (int x = 0, srcx = xstart; x < dstwidth; x++, srcx += xstep) { rgb_t pixel = palette[src[srcx >> 16]]; dst[x] += pixel.g() + pixel.r() + pixel.b(); @@ -1596,12 +1843,12 @@ void screen_device::update_burnin() case BITMAP_FORMAT_RGB32: { // iterate over rows in the destination - bitmap_rgb32 &srcbitmap = curbitmap.as_rgb32(); - for (y = 0, srcy = ystart; y < dstheight; y++, srcy += ystep) + for (int y = 0, srcy = ystart; y < dstheight; y++, srcy += ystep) { - u64 *dst = &m_burnin.pix64(y); - const u32 *src = &srcbitmap.pix32(srcy >> 16); - for (x = 0, srcx = xstart; x < dstwidth; x++, srcx += xstep) + const bitmap_rgb32 &srcbitmap = per_scanline ? *(bitmap_rgb32 *)m_scan_bitmaps[m_curbitmap][y] : curbitmap.as_rgb32(); + u64 *const dst = &m_burnin.pix(y); + u32 const *const src = &srcbitmap.pix(per_scanline ? 0 : (srcy >> 16)); + for (int x = 0, srcx = xstart; x < dstwidth; x++, srcx += xstep) { rgb_t pixel = src[srcx >> 16]; dst[x] += pixel.g() + pixel.r() + pixel.b(); @@ -1629,7 +1876,7 @@ void screen_device::finalize_burnin() m_visarea.top() * m_burnin.height() / m_height, m_visarea.bottom() * m_burnin.height() / m_height); - // wrap a bitmap around the memregion we care about + // wrap a bitmap around the subregion we care about bitmap_argb32 finalmap(scaledvis.width(), scaledvis.height()); int srcwidth = m_burnin.width(); int srcheight = m_burnin.height(); @@ -1643,7 +1890,7 @@ void screen_device::finalize_burnin() u64 maxval = 0; for (int y = 0; y < srcheight; y++) { - u64 *src = &m_burnin.pix64(y); + u64 const *const src = &m_burnin.pix(y); for (int x = 0; x < srcwidth; x++) { minval = std::min(minval, src[x]); @@ -1657,8 +1904,8 @@ void screen_device::finalize_burnin() // now normalize and convert to RGB for (int y = 0, srcy = 0; y < dstheight; y++, srcy += ystep) { - u64 *src = &m_burnin.pix64(srcy >> 16); - u32 *dst = &finalmap.pix32(y); + u64 const *const src = &m_burnin.pix(srcy >> 16); + u32 *const dst = &finalmap.pix(y); for (int x = 0, srcx = 0; x < dstwidth; x++, srcx += xstep) { int brightness = u64(maxval - src[srcx >> 16]) * 255 / (maxval - minval); @@ -1670,26 +1917,23 @@ void screen_device::finalize_burnin() // compute the name and create the file emu_file file(machine().options().snapshot_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); - osd_file::error filerr = file.open(machine().basename(), PATH_SEPARATOR "burnin-", this->tag()+1, ".png") ; - if (filerr == osd_file::error::NONE) + std::error_condition const filerr = file.open(util::string_format("%s" PATH_SEPARATOR "burnin-%s.png", machine().basename(), tag() + 1)); + if (!filerr) { - png_info pnginfo; - char text[256]; + util::png_info pnginfo; // add two text entries describing the image - sprintf(text,"%s %s", emulator_info::get_appname(), emulator_info::get_build_version()); - pnginfo.add_text("Software", text); - sprintf(text, "%s %s", machine().system().manufacturer, machine().system().type.fullname()); - pnginfo.add_text("System", text); + pnginfo.add_text("Software", util::string_format("%s %s", emulator_info::get_appname(), emulator_info::get_build_version())); + pnginfo.add_text("System", util::string_format("%s %s", machine().system().manufacturer, machine().system().type.fullname())); // now do the actual work - png_write_bitmap(file, &pnginfo, finalmap, 0, nullptr); + util::png_write_bitmap(file, &pnginfo, finalmap, 0, nullptr); } } //------------------------------------------------- -// finalize_burnin - finalize the burnin bitmap +// load_effect_overlay - //------------------------------------------------- void screen_device::load_effect_overlay(const char *filename) @@ -1702,10 +1946,15 @@ void screen_device::load_effect_overlay(const char *filename) fullname.append(".png"); // load the file + m_screen_overlay_bitmap.reset(); emu_file file(machine().options().art_path(), OPEN_FLAG_READ); - render_load_png(m_screen_overlay_bitmap, file, nullptr, fullname.c_str()); + if (!file.open(fullname)) + { + render_load_png(m_screen_overlay_bitmap, file); + file.close(); + } if (m_screen_overlay_bitmap.valid()) m_container->set_overlay(&m_screen_overlay_bitmap); else - osd_printf_warning("Unable to load effect PNG file '%s'\n", fullname.c_str()); + osd_printf_warning("Unable to load effect PNG file '%s'\n", fullname); } diff --git a/src/emu/screen.h b/src/emu/screen.h index 0ae11f85b9b..9100ae5bacc 100644 --- a/src/emu/screen.h +++ b/src/emu/screen.h @@ -7,11 +7,15 @@ Core MAME screen device. ***************************************************************************/ + #ifndef MAME_EMU_SCREEN_H #define MAME_EMU_SCREEN_H #pragma once +#include "rendertypes.h" + +#include <type_traits> #include <utility> @@ -29,16 +33,6 @@ enum screen_type_enum SCREEN_TYPE_SVG }; -// texture formats -enum texture_format -{ - TEXFORMAT_UNDEFINED = 0, // require a format to be specified - TEXFORMAT_PALETTE16, // 16bpp palettized, no alpha - TEXFORMAT_RGB32, // 32bpp 8-8-8 RGB - TEXFORMAT_ARGB32, // 32bpp 8-8-8-8 ARGB - TEXFORMAT_YUY16 // 16bpp 8-8 Y/Cb, Y/Cr in sequence -}; - // screen_update callback flags constexpr u32 UPDATE_HAS_NOT_CHANGED = 0x0001; // the video has not changed @@ -63,6 +57,9 @@ constexpr u32 UPDATE_HAS_NOT_CHANGED = 0x0001; // the video has not changed @def VIDEO_UPDATE_SCANLINE calls VIDEO_UPDATE for every visible scanline, even for skipped frames + @def VIDEO_VARIABLE_WIDTH + causes the screen to construct its final bitmap from a composite upscale of individual scanline bitmaps + @} */ @@ -72,6 +69,7 @@ constexpr u32 VIDEO_UPDATE_AFTER_VBLANK = 0x0004; constexpr u32 VIDEO_SELF_RENDER = 0x0008; constexpr u32 VIDEO_ALWAYS_UPDATE = 0x0080; constexpr u32 VIDEO_UPDATE_SCANLINE = 0x0100; +constexpr u32 VIDEO_VARIABLE_WIDTH = 0x0200; //************************************************************************** @@ -207,68 +205,169 @@ public: void set_orientation(int orientation) { assert(!configured()); m_orientation = orientation; } void set_physical_aspect(unsigned x, unsigned y) { assert(!configured()); m_phys_aspect = std::make_pair(x, y); } void set_native_aspect() { assert(!configured()); m_phys_aspect = std::make_pair(~0U, ~0U); } - void set_raw(u32 pixclock, u16 htotal, u16 hbend, u16 hbstart, u16 vtotal, u16 vbend, u16 vbstart) + + /// \brief Configure screen parameters + /// + /// \param [in] pixclock Pixel clock frequency in Hertz. + /// \param [in] htotal Total pixel clocks per line, including + /// horizontal blanking period. + /// \param [in] hbend Index of first visible pixel after horizontal + /// blanking period ends. + /// \param [in] hbstart Index of first pixel in horzontal blanking + /// period after visible pixels. + /// \param [in] vtotal Total lines per frame, including vertical + /// blanking period. + /// \param [in] vbend Index of first visible line after vertical + /// blanking period ends. + /// \param [in] vbstart Index of first line in vertical blanking + /// period after visible lines. + /// \return Reference to device for method chaining. + screen_device &set_raw(u32 pixclock, u16 htotal, u16 hbend, u16 hbstart, u16 vtotal, u16 vbend, u16 vbstart) { assert(pixclock != 0); - m_clock = pixclock; + set_clock(pixclock); m_refresh = HZ_TO_ATTOSECONDS(pixclock) * htotal * vtotal; m_vblank = m_refresh / vtotal * (vtotal - (vbstart - vbend)); m_width = htotal; m_height = vtotal; - m_visarea.set(hbend, hbstart - 1, vbend, vbstart - 1); + m_visarea.set(hbend, hbstart ? hbstart - 1 : htotal - 1, vbend, vbstart - 1); + return *this; + } + screen_device &set_raw(const XTAL &xtal, u16 htotal, u16 hbend, u16 hbstart, u16 vtotal, u16 vbend, u16 vbstart) + { + xtal.validate(std::string("Configuring screen ") + tag()); + return set_raw(xtal.value(), htotal, hbend, hbstart, vtotal, vbend, vbstart); + } + screen_device &set_raw(const XTAL &xtal, u16 htotal, u16 vtotal, rectangle visarea) + { + return set_raw(xtal, htotal, visarea.left(), visarea.right() + 1, vtotal, visarea.top(), visarea.bottom() + 1); } - void set_raw(const XTAL &xtal, u16 htotal, u16 hbend, u16 hbstart, u16 vtotal, u16 vbend, u16 vbstart) { set_raw(xtal.value(), htotal, hbend, hbstart, vtotal, vbend, vbstart); } void set_refresh(attoseconds_t rate) { m_refresh = rate; } - template <typename T> void set_refresh_hz(T &&hz) { set_refresh(HZ_TO_ATTOSECONDS(std::forward<T>(hz))); } - void set_vblank_time(attoseconds_t time) { m_vblank = time; m_oldstyle_vblank_supplied = true; } - void set_size(u16 width, u16 height) { m_width = width; m_height = height; } - void set_visarea(s16 minx, s16 maxx, s16 miny, s16 maxy) { m_visarea.set(minx, maxx, miny, maxy); } - void set_visarea_full() { m_visarea.set(0, m_width - 1, 0, m_height - 1); } // call after set_size - void set_default_position(double xscale, double xoffs, double yscale, double yoffs) { - m_xscale = xscale; - m_xoffset = xoffs; - m_yscale = yscale; - m_yoffset = yoffs; + + /// \brief Set refresh rate in Hertz + /// + /// Sets refresh rate in Hertz (frames per second). Used in + /// conjunction with #set_vblank_time, #set_size and #set_visarea. + /// For raster displays, please use #set_raw to configure screen + /// parameters in terms of pixel clock. + /// \param [in] hz Desired refresh rate. + /// \return Reference to device for method chaining. + template <typename T> screen_device &set_refresh_hz(T &&hz) + { + set_refresh(HZ_TO_ATTOSECONDS(std::forward<T>(hz))); + return *this; } - // FIXME: these should be aware of current device for resolving the tag - template <class FunctionClass> - void set_screen_update(u32 (FunctionClass::*callback)(screen_device &, bitmap_ind16 &, const rectangle &), const char *name) + /// \brief Set vertical blanking interval time + /// + /// Sets vertical blanking interval period. Used in conjunction + /// with #set_refresh_hz, #set_size and #set_visarea. For raster + /// displays, please use #set_raw to configure screen parameters in + /// terms of pixel clock. + /// \param [in] time Length of vertical blanking interval. + /// \return Reference to device for method chaining. + screen_device &set_vblank_time(attoseconds_t time) { - set_screen_update(screen_update_ind16_delegate(callback, name, nullptr, static_cast<FunctionClass *>(nullptr))); + m_vblank = time; + m_oldstyle_vblank_supplied = true; + return *this; } - template <class FunctionClass> - void set_screen_update(u32 (FunctionClass::*callback)(screen_device &, bitmap_rgb32 &, const rectangle &), const char *name) + + /// \brief Set total screen size + /// + /// Set the total screen size in pixels, including blanking areas if + /// applicable. This sets the size of the screen bitmap. Used in + /// conjunction with #set_refresh_hz, #set_vblank_time and + /// #set_visarea. For raster displays, please use #set_raw to + /// configure screen parameters in terms of pixel clock. + /// \param [in] width Total width in pixels, including horizontal + /// blanking period if applicable. + /// \param [in] height Total height in lines, including vertical + /// blanking period if applicable. + /// \return Reference to device for method chaining. + screen_device &set_size(u16 width, u16 height) { - set_screen_update(screen_update_rgb32_delegate(callback, name, nullptr, static_cast<FunctionClass *>(nullptr))); + m_width = width; + m_height = height; + return *this; } - template <class FunctionClass> - void set_screen_update(const char *devname, u32 (FunctionClass::*callback)(screen_device &, bitmap_ind16 &, const rectangle &), const char *name) + + /// \brief Set visible screen area + /// + /// Set visible screen area. This should fit within the total + /// screen area. Used in conjunction with #set_refresh_hz, + /// #set_vblank_time and #set_size. For raster displays, please + /// use #set_raw to configure screen parameters in terms of pixel + /// clock. + /// \param [in] minx First visible pixel index after horizontal + /// blanking period ends. + /// \param [in] maxx Last visible pixel index before horizontal + /// blanking period starts. + /// \param [in] miny First visible line index after vertical + /// blanking period ends. + /// \param [in] maxy Last visible line index before vertical + /// blanking period starts. + /// \return Reference to device for method chaining. + screen_device &set_visarea(s16 minx, s16 maxx, s16 miny, s16 maxy) { - set_screen_update(screen_update_ind16_delegate(callback, name, devname, static_cast<FunctionClass *>(nullptr))); + m_visarea.set(minx, maxx, miny, maxy); + return *this; } - template <class FunctionClass> - void set_screen_update(const char *devname, u32 (FunctionClass::*callback)(screen_device &, bitmap_rgb32 &, const rectangle &), const char *name) + + /// \brief Set visible area to full area + /// + /// Set visible screen area to the full screen area (i.e. no + /// horizontal or vertical blanking period). This is generally not + /// possible for raster displays, but is useful for other display + /// simulations. Must be called after calling #set_size. + /// \return Reference to device for method chaining. + /// \sa set_visarea + screen_device &set_visarea_full() { - set_screen_update(screen_update_rgb32_delegate(callback, name, devname, static_cast<FunctionClass *>(nullptr))); + m_visarea.set(0, m_width - 1, 0, m_height - 1); + return *this; } - void set_screen_update(screen_update_ind16_delegate callback) + + void set_default_position(double xscale, double xoffs, double yscale, double yoffs) { - m_screen_update_ind16 = callback; - m_screen_update_rgb32 = screen_update_rgb32_delegate(); + m_xscale = xscale; + m_xoffset = xoffs; + m_yscale = yscale; + m_yoffset = yoffs; + } + + template <typename F> + std::enable_if_t<screen_update_ind16_delegate::supports_callback<F>::value> set_screen_update(F &&callback, const char *name) + { + m_screen_update_ind16.set(std::forward<F>(callback), name); + m_screen_update_rgb32 = screen_update_rgb32_delegate(*this); } - void set_screen_update(screen_update_rgb32_delegate callback) + template <typename F> + std::enable_if_t<screen_update_rgb32_delegate::supports_callback<F>::value> set_screen_update(F &&callback, const char *name) { - m_screen_update_ind16 = screen_update_ind16_delegate(); - m_screen_update_rgb32 = callback; + m_screen_update_ind16 = screen_update_ind16_delegate(*this); + m_screen_update_rgb32.set(std::forward<F>(callback), name); + } + template <typename T, typename F> + std::enable_if_t<screen_update_ind16_delegate::supports_callback<F>::value> set_screen_update(T &&target, F &&callback, const char *name) + { + m_screen_update_ind16.set(std::forward<T>(target), std::forward<F>(callback), name); + m_screen_update_rgb32 = screen_update_rgb32_delegate(*this); + } + template <typename T, typename F> + std::enable_if_t<screen_update_rgb32_delegate::supports_callback<F>::value> set_screen_update(T &&target, F &&callback, const char *name) + { + m_screen_update_ind16 = screen_update_ind16_delegate(*this); + m_screen_update_rgb32.set(std::forward<T>(target), std::forward<F>(callback), name); } auto screen_vblank() { return m_screen_vblank.bind(); } auto scanline() { m_video_attributes |= VIDEO_UPDATE_SCANLINE; return m_scanline_cb.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; } - void set_svg_region(const char *region) { m_svg_region = region; } // default region is device tag + template <typename T> screen_device &set_palette(T &&tag) { m_palette.set_tag(std::forward<T>(tag)); return *this; } + screen_device &set_no_palette() { m_palette.set_tag(finder_base::DUMMY_TAG); return *this; } + screen_device &set_video_attributes(u32 flags) { m_video_attributes = flags; return *this; } + screen_device &set_color(rgb_t color) { m_color = color; return *this; } + template <typename T> screen_device &set_svg_region(T &&tag) { m_svg_region.set_tag(std::forward<T>(tag)); return *this; } // default region is device tag // information getters render_container &container() const { assert(m_container != nullptr); return *m_container; } @@ -286,8 +385,8 @@ public: // beam positioning and state int vpos() const; int hpos() const; - 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.left() || curpos > m_visarea.right()) ? 1 : 0; } + int vblank() const { return (machine().time() < m_vblank_end_time) ? 1 : 0; } + int hblank() const { int const curpos = hpos(); return (curpos < m_visarea.left() || curpos > m_visarea.right()) ? 1 : 0; } // timing attotime time_until_pos(int vpos, int hpos = 0) const; @@ -295,6 +394,7 @@ public: attotime time_until_vblank_end() const; attotime time_until_update() const { return (m_video_attributes & VIDEO_UPDATE_AFTER_VBLANK) ? time_until_vblank_end() : time_until_vblank_start(); } attotime scan_period() const { return attotime(0, m_scantime); } + attotime pixel_period() const { return attotime(0, m_pixeltime); } attotime frame_period() const { return attotime(0, m_frame_period); } u64 frame_number() const { return m_frame_number; } @@ -304,6 +404,7 @@ public: // updating int partial_updates() const { return m_partial_updates_this_frame; } + int partial_scan_hpos() const { return m_partial_scan_hpos; } bool update_partial(int scanline); void update_now(); void reset_partial_updates(); @@ -323,32 +424,29 @@ public: private: class svg_renderer; - // timer IDs - enum - { - TID_VBLANK_START, - TID_VBLANK_END, - TID_SCANLINE0, - TID_SCANLINE - }; - // device-level overrides virtual void device_validity_check(validity_checker &valid) const override; virtual void device_config_complete() override; - virtual void device_resolve_objects() override; - virtual void device_start() override; - virtual void device_reset() override; - virtual void device_stop() override; + virtual void device_resolve_objects() override ATTR_COLD; + virtual void device_start() override ATTR_COLD; + virtual void device_reset() override ATTR_COLD; + virtual void device_stop() override ATTR_COLD; virtual void device_post_load() override; - virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; // internal helpers void set_container(render_container &container) { m_container = &container; } void realloc_screen_bitmaps(); - void vblank_begin(); - void vblank_end(); + TIMER_CALLBACK_MEMBER(vblank_begin); + TIMER_CALLBACK_MEMBER(vblank_end); + TIMER_CALLBACK_MEMBER(first_scanline_tick); + TIMER_CALLBACK_MEMBER(scanline_tick); void finalize_burnin(); void load_effect_overlay(const char *filename); + void update_scan_bitmap_size(int y); + void pre_update_scanline(int y); + void create_composited_bitmap(); + void destroy_scan_bitmaps(); + void allocate_scan_bitmaps(); // inline configuration data screen_type_enum m_type; // type of screen @@ -363,27 +461,31 @@ private: screen_update_rgb32_delegate m_screen_update_rgb32; // screen update callback (32-bit RGB) devcb_write_line m_screen_vblank; // screen vblank line callback devcb_write32 m_scanline_cb; // screen scanline callback - optional_device<device_palette_interface> m_palette; // our palette + optional_device<device_palette_interface> m_palette; // our palette u32 m_video_attributes; // flags describing the video system - const char * m_svg_region; // the region in which the svg data is in + optional_memory_region m_svg_region; // the region in which the svg data is in // internal state render_container * m_container; // pointer to our container std::unique_ptr<svg_renderer> m_svg; // the svg renderer // dimensions + int m_max_width; // maximum width encountered int m_width; // current width (HTOTAL) int m_height; // current height (VTOTAL) rectangle m_visarea; // current visible area (HBLANK end/start, VBLANK end/start) + std::vector<int> m_scan_widths; // current width, in samples, of each individual scanline // textures and bitmaps texture_format m_texformat; // texture format render_texture * m_texture[2]; // 2x textures for the screen bitmap screen_bitmap m_bitmap[2]; // 2x bitmaps for rendering + std::vector<bitmap_t *> m_scan_bitmaps[2]; // 2x bitmaps for each individual scanline bitmap_ind8 m_priority; // priority bitmap bitmap_ind64 m_burnin; // burn-in bitmap u8 m_curbitmap; // current bitmap index u8 m_curtexture; // current texture index bool m_changed; // has this bitmap changed? + attotime m_last_partial_reset; // last time partial updates were reset s32 m_last_partial_scan; // scanline of last partial update s32 m_partial_scan_hpos; // horizontal pixel last rendered on this partial scanline bitmap_argb32 m_screen_overlay_bitmap; // screen overlay bitmap @@ -403,7 +505,7 @@ private: emu_timer * m_scanline0_timer; // scanline 0 timer emu_timer * m_scanline_timer; // scanline timer u64 m_frame_number; // the current frame number - u32 m_partial_updates_this_frame;// partial update counter this frame + u32 m_partial_updates_this_frame; // partial update counter this frame bool m_is_primary_screen; @@ -416,7 +518,7 @@ private: vblank_state_delegate m_callback; }; - std::vector<std::unique_ptr<callback_item>> m_callback_list; // list of VBLANK callbacks + std::vector<std::unique_ptr<callback_item>> m_callback_list; // list of VBLANK callbacks // auto-sizing bitmaps class auto_bitmap_item @@ -437,81 +539,6 @@ private: DECLARE_DEVICE_TYPE(SCREEN, screen_device) // iterator helper -typedef device_type_iterator<screen_device> screen_device_iterator; - -/*! - @defgroup Screen device configuration functions - @{ - @def set_type - Modify the screen device type - @see screen_type_enum - - @def set_raw - Configures screen parameters for the given screen. - @remark It's better than using @see set_refresh_hz and @see set_vblank_time but still not enough. - - @param _pixclock - Pixel Clock frequency value - - @param _htotal - Total number of horizontal pixels, including hblank period. - - @param _hbend - Horizontal pixel position for HBlank end event, also first pixel where screen rectangle is visible. - - @param _hbstart - Horizontal pixel position for HBlank start event, also last pixel where screen rectangle is visible. - - @param _vtotal - Total number of vertical pixels, including vblank period. - - @param _vbend - Vertical pixel position for VBlank end event, also first pixel where screen rectangle is visible. - - @param _vbstart - Vertical pixel position for VBlank start event, also last pixel where screen rectangle is visible. - - @def set_refresh_hz - Sets the number of Frames Per Second for this screen - @remarks Please use @see set_raw instead. Gives imprecise timings. - - @param _rate - FPS number - - @def set_vblank_time - Sets the vblank time of the given screen - @remarks Please use @see MCFG_SCREEN_RAW_PARAMS instead. Gives imprecise timings. - - @param _time - Time parameter, in attotime value - - @def set_size - Sets total screen size, including H/V-Blanks - @remarks Please use @see set_raw instead. Gives imprecise timings. - - @param _width - Screen horizontal size - - @param _height - Screen vertical size - - @def set_visarea - Sets screen visible area - @remarks Please use @see set_raw instead. Gives imprecise timings. - - @param _minx - Screen left border - - @param _maxx - Screen right border, must be in N-1 format - - @param _miny - Screen top border - - @param _maxx - Screen bottom border, must be in N-1 format - - @} - */ +typedef device_type_enumerator<screen_device> screen_device_enumerator; #endif // MAME_EMU_SCREEN_H diff --git a/src/emu/softlist.cpp b/src/emu/softlist.cpp index 04302f6b514..c6bb7161be3 100644 --- a/src/emu/softlist.cpp +++ b/src/emu/softlist.cpp @@ -15,6 +15,8 @@ #include "expat.h" +#include <array> +#include <cstring> #include <regex> @@ -23,7 +25,7 @@ // STATIC VARIABLES //************************************************************************** -static std::regex s_potenial_softlist_regex("\\w+(\\:\\w+)*"); +static std::regex s_potential_softlist_regex("\\w+(\\:\\w+)*"); //************************************************************************** @@ -31,11 +33,10 @@ static std::regex s_potenial_softlist_regex("\\w+(\\:\\w+)*"); //************************************************************************** //------------------------------------------------- -// feature_list_item - constructor +// software_info_item - constructor //------------------------------------------------- -feature_list_item::feature_list_item(const std::string &name, const std::string &value) - : m_next(nullptr), +software_info_item::software_info_item(const std::string &name, const std::string &value) : m_name(name), m_value(value) { @@ -43,11 +44,10 @@ feature_list_item::feature_list_item(const std::string &name, const std::string //------------------------------------------------- -// feature_list_item - constructor +// software_info_item - constructor //------------------------------------------------- -feature_list_item::feature_list_item(std::string &&name, std::string &&value) - : m_next(nullptr), +software_info_item::software_info_item(std::string &&name, std::string &&value) : m_name(std::move(name)), m_value(std::move(value)) { @@ -62,11 +62,10 @@ feature_list_item::feature_list_item(std::string &&name, std::string &&value) // software_part - constructor //------------------------------------------------- -software_part::software_part(software_info &info, std::string &&name, std::string &&interface) - : m_next(nullptr), - m_info(info), - m_name(std::move(name)), - m_interface(std::move(interface)) +software_part::software_part(software_info &info, std::string &&name, std::string &&interface) : + m_info(info), + m_name(std::move(name)), + m_interface(std::move(interface)) { } @@ -76,17 +75,11 @@ software_part::software_part(software_info &info, std::string &&name, std::strin // feature, if specified //------------------------------------------------- -const char *software_part::feature(const std::string &feature_name) const +const char *software_part::feature(std::string_view feature_name) const noexcept { // scan the feature list for an entry matching feature_name and return the value - auto iter = std::find_if( - m_featurelist.begin(), - m_featurelist.end(), - [&feature_name](const feature_list_item &feature) { return feature.name() == feature_name; }); - - return iter != m_featurelist.end() - ? iter->value().c_str() - : nullptr; + auto const iter = m_features.find(feature_name); + return (iter != m_features.end()) ? iter->value().c_str() : nullptr; } @@ -95,18 +88,25 @@ const char *software_part::feature(const std::string &feature_name) const // an interface in the provided list //------------------------------------------------- -bool software_part::matches_interface(const char *interface_list) const +bool software_part::matches_interface(const char *interface_list) const noexcept { // if we have no interface, then we match by default if (m_interface.empty()) return true; - // copy the comma-delimited interface list and ensure it ends with a final comma - std::string interfaces = std::string(interface_list).append(","); - - // then add a comma to the end of our interface and return true if we find it in the list string - std::string our_interface = std::string(m_interface).append(","); - return (interfaces.find(our_interface) != -1); + // find our interface at the beginning of the list or immediately following a comma + while (true) + { + char const *const found(std::strstr(interface_list, m_interface.c_str())); + if (!found) + return false; + if (((found == interface_list) || (',' == found[-1])) && ((',' == found[m_interface.size()]) || !found[m_interface.size()])) + return true; + interface_list = std::strchr(interface_list, ','); + if (!interface_list) + return false; + ++interface_list; + } } @@ -118,16 +118,16 @@ bool software_part::matches_interface(const char *interface_list) const // software_info - constructor //------------------------------------------------- -software_info::software_info(std::string &&name, std::string &&parent, const std::string &supported) - : m_supported(SOFTWARE_SUPPORTED_YES), - m_shortname(std::move(name)), - m_parentname(std::move(parent)) +software_info::software_info(std::string &&name, std::string &&parent, std::string_view supported) : + m_supported(software_support::SUPPORTED), + m_shortname(std::move(name)), + m_parentname(std::move(parent)) { // handle the supported flag if provided if (supported == "partial") - m_supported = SOFTWARE_SUPPORTED_PARTIAL; + m_supported = software_support::PARTIALLY_SUPPORTED; else if (supported == "no") - m_supported = SOFTWARE_SUPPORTED_NO; + m_supported = software_support::UNSUPPORTED; } @@ -136,13 +136,13 @@ software_info::software_info(std::string &&name, std::string &&parent, const std // optional interface match //------------------------------------------------- -const software_part *software_info::find_part(const std::string &part_name, const char *interface) const +const software_part *software_info::find_part(std::string_view part_name, const char *interface) const { // look for the part by name and match against the interface if provided auto iter = std::find_if( m_partdata.begin(), m_partdata.end(), - [&](const software_part &part) + [&part_name, interface] (const software_part &part) { // try to match the part_name (or all parts if part_name is empty), and then try // to match the interface (or all interfaces if interface is nullptr) @@ -176,29 +176,105 @@ bool software_info::has_multiple_parts(const char *interface) const } +namespace detail { + //************************************************************************** // SOFTWARE LIST PARSER //************************************************************************** +class softlist_parser +{ +public: + // construction (== execution) + softlist_parser( + util::read_stream &file, + std::string_view filename, + std::string &listname, + std::string &description, + std::list<software_info> &infolist, + std::ostream &errors); + +private: + enum parse_position + { + POS_ROOT, + POS_MAIN, + POS_SOFT, + POS_PART, + POS_DATA + }; + + // internal parsing helpers + const char *infoname() const { return m_current_info ? m_current_info->shortname().c_str() : "???"; } + int line() const { return XML_GetCurrentLineNumber(m_parser); } + int column() const { return XML_GetCurrentColumnNumber(m_parser); } + const char *parser_error() const { return XML_ErrorString(XML_GetErrorCode(m_parser)); } + + // internal error helpers + template <typename Format, typename... Params> void parse_error(Format &&fmt, Params &&... args); + void unknown_tag(const char *tagname) { parse_error("Unknown tag: %s", tagname); } + void unknown_attribute(const char *attrname) { parse_error("Unknown attribute: %s", attrname); } + + // internal helpers + template <size_t N> std::array<std::string_view, N> parse_attributes(const char **attributes, const char *const (&attrlist)[N]); + bool parse_name_and_value(const char **attributes, std::string_view &name, std::string_view &value); + void add_rom_entry(std::string &&name, std::string &&hashdata, u32 offset, u32 length, u32 flags); + + // expat callbacks + static void start_handler(void *data, const char *tagname, const char **attributes); + static void data_handler(void *data, const char *s, int len); + static void end_handler(void *data, const char *name); + + // internal parsing + void parse_root_start(const char *tagname, const char **attributes); + void parse_main_start(const char *tagname, const char **attributes); + void parse_soft_start(const char *tagname, const char **attributes); + void parse_part_start(const char *tagname, const char **attributes); + void parse_data_start(const char *tagname, const char **attributes); + void parse_main_end(const char *tagname); + void parse_soft_end(const char *name); + + // internal parsing state + const std::string_view m_filename; + std::list<software_info> & m_infolist; + std::ostream & m_errors; + struct XML_ParserStruct * m_parser; + std::string & m_listname; + std::string & m_description; + bool m_data_accum_expected; + bool m_ignore_cdata; + std::string m_data_accum; + software_info * m_current_info; + software_part * m_current_part; + parse_position m_pos; +}; + + //------------------------------------------------- // softlist_parser - constructor //------------------------------------------------- -softlist_parser::softlist_parser(util::core_file &file, const std::string &filename, std::string &description, std::list<software_info> &infolist, std::ostringstream &errors) - : m_file(file), - m_filename(filename), - m_infolist(infolist), - m_errors(errors), - m_done(false), - m_description(description), - m_data_accum_expected(false), - m_current_info(nullptr), - m_current_part(nullptr), - m_pos(POS_ROOT) +softlist_parser::softlist_parser( + util::read_stream &file, + std::string_view filename, + std::string &listname, + std::string &description, + std::list<software_info> &infolist, + std::ostream &errors) : + m_filename(filename), + m_infolist(infolist), + m_errors(errors), + m_listname(listname), + m_description(description), + m_data_accum_expected(false), + m_ignore_cdata(false), + m_current_info(nullptr), + m_current_part(nullptr), + m_pos(POS_ROOT) { // create the parser m_parser = XML_ParserCreate_MM(nullptr, nullptr, nullptr); - if (m_parser == nullptr) + if (!m_parser) throw std::bad_alloc(); // set the handlers @@ -207,13 +283,13 @@ softlist_parser::softlist_parser(util::core_file &file, const std::string &filen XML_SetCharacterDataHandler(m_parser, &softlist_parser::data_handler); // parse the file contents - m_file.seek(0, SEEK_SET); char buffer[1024]; - while (!m_done) + for (bool done = false; !done; ) { - u32 length = m_file.read(buffer, sizeof(buffer)); - m_done = m_file.eof(); - if (XML_Parse(m_parser, buffer, length, m_done) == XML_STATUS_ERROR) + auto const [err, length] = read(file, buffer, sizeof(buffer)); // TODO: better error handling + if (!length) + done = true; + if (XML_Parse(m_parser, buffer, length, done) == XML_STATUS_ERROR) { parse_error("%s", parser_error()); break; @@ -226,36 +302,6 @@ softlist_parser::softlist_parser(util::core_file &file, const std::string &filen //------------------------------------------------- -// line -//------------------------------------------------- - -int softlist_parser::line() const -{ - return XML_GetCurrentLineNumber(m_parser); -} - - -//------------------------------------------------- -// column -//------------------------------------------------- - -int softlist_parser::column() const -{ - return XML_GetCurrentColumnNumber(m_parser); -} - - -//------------------------------------------------- -// parser_error -//------------------------------------------------- - -const char *softlist_parser::parser_error() const -{ - return XML_ErrorString(XML_GetErrorCode(m_parser)); -} - - -//------------------------------------------------- // parse_error - append a parsing error with // filename, line and column information //------------------------------------------------- @@ -279,10 +325,10 @@ inline void softlist_parser::parse_error(Format &&fmt, Params &&... args) // attributes into a list of strings //------------------------------------------------- -template <typename T> -std::vector<std::string> softlist_parser::parse_attributes(const char **attributes, const T &attrlist) +template <size_t N> +std::array<std::string_view, N> softlist_parser::parse_attributes(const char **attributes, const char *const (&attrlist)[N]) { - std::vector<std::string> outlist(std::distance(std::begin(attrlist), std::end(attrlist))); + std::array<std::string_view, N> outlist; // iterate over attribute/value pairs for( ; attributes[0]; attributes += 2) @@ -315,7 +361,7 @@ std::vector<std::string> softlist_parser::parse_attributes(const char **attribut // latter to be defined as an empty string) //------------------------------------------------- -bool softlist_parser::parse_name_and_value(const char **attributes, std::string &name, std::string &value) +bool softlist_parser::parse_name_and_value(const char **attributes, std::string_view &name, std::string_view &value) { bool found_value = false; @@ -422,6 +468,7 @@ void softlist_parser::end_handler(void *data, const char *name) break; case POS_MAIN: + state->parse_main_end(name); state->m_current_info = nullptr; break; @@ -439,6 +486,7 @@ void softlist_parser::end_handler(void *data, const char *name) // stop accumulating state->m_data_accum_expected = false; + state->m_ignore_cdata = false; state->m_data_accum.clear(); } @@ -451,18 +499,25 @@ void softlist_parser::data_handler(void *data, const char *s, int len) { softlist_parser *state = reinterpret_cast<softlist_parser *>(data); - // if we have an std::string to accumulate data in, do it - if (state->m_data_accum_expected) + if (state->m_ignore_cdata) + { + // allowed, but we don't use it + } + else if (state->m_data_accum_expected) + { + // if we have an std::string to accumulate data in, do it state->m_data_accum.append(s, len); - - // otherwise, report an error if the data is non-blank + } else + { + // otherwise, report an error if the data is non-blank for (int i = 0; i < len; i++) if (!isspace(s[i])) { state->parse_error("Unexpected content"); break; } + } } @@ -478,6 +533,9 @@ void softlist_parser::parse_root_start(const char *tagname, const char **attribu static char const *const attrnames[] = { "name", "description" }; const auto attrvalues = parse_attributes(attributes, attrnames); + if (!attrvalues[0].empty()) + m_listname = attrvalues[0]; + if (!attrvalues[1].empty()) m_description = attrvalues[1]; } @@ -493,27 +551,37 @@ void softlist_parser::parse_root_start(const char *tagname, const char **attribu void softlist_parser::parse_main_start(const char *tagname, const char **attributes) { - // <software name='' cloneof='' supported=''> if (strcmp(tagname, "software") == 0) { + // <software name='' cloneof='' supported=''> static char const *const attrnames[] = { "name", "cloneof", "supported" }; auto attrvalues = parse_attributes(attributes, attrnames); if (!attrvalues[0].empty()) { - m_infolist.emplace_back(std::move(attrvalues[0]), std::move(attrvalues[1]), attrvalues[2].c_str()); + m_infolist.emplace_back(std::string(attrvalues[0]), std::string(attrvalues[1]), attrvalues[2]); m_current_info = &m_infolist.back(); } else parse_error("No name defined for item"); } + else if (strcmp(tagname, "notes") == 0) + { + // <notes> + m_ignore_cdata = true; + } else unknown_tag(tagname); } +void softlist_parser::parse_main_end(const char *tagname) +{ +} + + //------------------------------------------------- -// parse_main_start - handle tag start within +// parse_soft_start - handle tag start within // a software tag //------------------------------------------------- @@ -538,13 +606,17 @@ void softlist_parser::parse_soft_start(const char *tagname, const char **attribu else if (strcmp(tagname, "publisher") == 0) m_data_accum_expected = true; + // <notes> + else if (strcmp(tagname, "notes") == 0) + m_ignore_cdata = true; + // <info name='' value=''> else if (strcmp(tagname, "info") == 0) { - std::string infoname, infovalue; + std::string_view infoname, infovalue; if (parse_name_and_value(attributes, infoname, infovalue)) - m_current_info->m_other_info.emplace_back(std::move(infoname), std::move(infovalue)); + m_current_info->m_info.emplace_back(std::string(infoname), std::string(infovalue)); else parse_error("Incomplete other_info definition"); } @@ -552,12 +624,17 @@ void softlist_parser::parse_soft_start(const char *tagname, const char **attribu // <sharedfeat name='' value=''> else if (strcmp(tagname, "sharedfeat") == 0) { - std::string featname, featvalue; + std::string_view featname, featvalue; if (parse_name_and_value(attributes, featname, featvalue)) - m_current_info->m_shared_info.emplace_back(std::move(featname), std::move(featvalue)); + { + if (!m_current_info->m_shared_features.emplace(std::string(featname), std::string(featvalue)).second) + parse_error("Duplicate sharedfeat name"); + } else + { parse_error("Incomplete sharedfeat definition"); + } } // <part name='' interface=''> @@ -568,7 +645,7 @@ void softlist_parser::parse_soft_start(const char *tagname, const char **attribu if (!attrvalues[0].empty() && !attrvalues[1].empty()) { - m_current_info->m_partdata.emplace_back(*m_current_info, std::move(attrvalues[0]), std::move(attrvalues[1])); + m_current_info->m_partdata.emplace_back(*m_current_info, std::string(attrvalues[0]), std::string(attrvalues[1])); m_current_part = &m_current_info->m_partdata.back(); } else @@ -602,8 +679,8 @@ void softlist_parser::parse_part_start(const char *tagname, const char **attribu if (!attrvalues[0].empty() && !attrvalues[1].empty()) { // handle region attributes - const std::string &width = attrvalues[2]; - const std::string &endianness = attrvalues[3]; + const auto &width = attrvalues[2]; + const auto &endianness = attrvalues[3]; u32 regionflags = ROMENTRYTYPE_REGION; if (!width.empty()) @@ -629,7 +706,7 @@ void softlist_parser::parse_part_start(const char *tagname, const char **attribu parse_error("Invalid dataarea endianness"); } - add_rom_entry(std::move(attrvalues[0]), "", 0, strtol(attrvalues[1].c_str(), nullptr, 0), regionflags); + add_rom_entry(std::string(attrvalues[0]), "", 0, strtol(attrvalues[1].data(), nullptr, 0), regionflags); } else parse_error("Incomplete dataarea definition"); @@ -642,7 +719,7 @@ void softlist_parser::parse_part_start(const char *tagname, const char **attribu auto attrvalues = parse_attributes(attributes, attrnames); if (!attrvalues[0].empty()) - add_rom_entry(std::move(attrvalues[0]), "", 0, 1, ROMENTRYTYPE_REGION | ROMREGION_DATATYPEDISK); + add_rom_entry(std::string(attrvalues[0]), "", 0, 1, ROMENTRYTYPE_REGION | ROMREGION_DATATYPEDISK); else parse_error("Incomplete diskarea definition"); } @@ -650,12 +727,17 @@ void softlist_parser::parse_part_start(const char *tagname, const char **attribu // <feature name='' value=''> else if (strcmp(tagname, "feature") == 0) { - std::string featname, featvalue; + std::string_view featname, featvalue; if (parse_name_and_value(attributes, featname, featvalue)) - m_current_part->m_featurelist.emplace_back(std::move(featname), std::move(featvalue)); + { + if (!m_current_part->m_features.emplace(std::string(featname), std::string(featvalue)).second) + parse_error("Duplicate feature name"); + } else + { parse_error("Incomplete feature definition"); + } } // <dipswitch> @@ -686,18 +768,18 @@ void softlist_parser::parse_data_start(const char *tagname, const char **attribu static char const *const attrnames[] = { "name", "size", "crc", "sha1", "offset", "value", "status", "loadflag" }; auto attrvalues = parse_attributes(attributes, attrnames); - std::string &name = attrvalues[0]; - const std::string &sizestr = attrvalues[1]; - const std::string &crc = attrvalues[2]; - const std::string &sha1 = attrvalues[3]; - const std::string &offsetstr = attrvalues[4]; - std::string &value = attrvalues[5]; - const std::string &status = attrvalues[6]; - const std::string &loadflag = attrvalues[7]; + const std::string_view &name = attrvalues[0]; + const std::string_view &sizestr = attrvalues[1]; + const std::string_view &crc = attrvalues[2]; + const std::string_view &sha1 = attrvalues[3]; + const std::string_view &offsetstr = attrvalues[4]; + const std::string_view &value = attrvalues[5]; + const std::string_view &status = attrvalues[6]; + const std::string_view &loadflag = attrvalues[7]; if (!sizestr.empty()) { - u32 length = strtol(sizestr.c_str(), nullptr, 0); - u32 offset = offsetstr.empty() ? 0 : strtol(offsetstr.c_str(), nullptr, 0); + u32 length = strtol(sizestr.data(), nullptr, 0); + u32 offset = offsetstr.empty() ? 0 : strtol(offsetstr.data(), nullptr, 0); if (loadflag == "reload") add_rom_entry("", "", offset, length, ROMENTRYTYPE_RELOAD | ROM_INHERITFLAGS); @@ -706,7 +788,7 @@ void softlist_parser::parse_data_start(const char *tagname, const char **attribu else if (loadflag == "continue") add_rom_entry("", "", offset, length, ROMENTRYTYPE_CONTINUE | ROM_INHERITFLAGS); else if (loadflag == "fill") - add_rom_entry("", std::move(value), offset, length, ROMENTRYTYPE_FILL); + add_rom_entry("", std::string(value), offset, length, ROMENTRYTYPE_FILL); else if (loadflag == "ignore") add_rom_entry("", "", 0, length, ROMENTRYTYPE_IGNORE | ROM_INHERITFLAGS); else if (!name.empty()) @@ -742,7 +824,7 @@ void softlist_parser::parse_data_start(const char *tagname, const char **attribu else if (loadflag == "load32_byte") romflags = ROM_SKIP(3); - add_rom_entry(std::move(name), std::move(hashdata), offset, length, ROMENTRYTYPE_ROM | romflags); + add_rom_entry(std::string(name), std::move(hashdata), offset, length, ROMENTRYTYPE_ROM | romflags); } else parse_error("Rom name missing"); @@ -757,10 +839,10 @@ void softlist_parser::parse_data_start(const char *tagname, const char **attribu static char const *const attrnames[] = { "name", "sha1", "status", "writeable" }; auto attrvalues = parse_attributes(attributes, attrnames); - std::string &name = attrvalues[0]; - const std::string &sha1 = attrvalues[1]; - const std::string &status = attrvalues[2]; - const std::string &writeablestr = attrvalues[3]; + const std::string_view &name = attrvalues[0]; + const std::string_view &sha1 = attrvalues[1]; + const std::string_view &status = attrvalues[2]; + const std::string_view &writeablestr = attrvalues[3]; if (!name.empty() && !sha1.empty()) { const bool baddump = (status == "baddump"); @@ -768,7 +850,7 @@ void softlist_parser::parse_data_start(const char *tagname, const char **attribu const bool writeable = (writeablestr == "yes"); std::string hashdata = string_format("%c%s%s", util::hash_collection::HASH_SHA1, sha1, (nodump ? NO_DUMP : (baddump ? BAD_DUMP : ""))); - add_rom_entry(std::move(name), std::move(hashdata), 0, 0, ROMENTRYTYPE_ROM | (writeable ? DISK_READWRITE : DISK_READONLY)); + add_rom_entry(std::string(name), std::move(hashdata), 0, 0, ROMENTRYTYPE_ROM | (writeable ? DISK_READWRITE : DISK_READONLY)); } else if (status != "nodump") // a no_dump chd is not an incomplete entry parse_error("Incomplete disk definition"); @@ -793,15 +875,15 @@ void softlist_parser::parse_soft_end(const char *tagname) // <description> if (strcmp(tagname, "description") == 0) - m_current_info->m_longname = m_data_accum; + m_current_info->m_longname = std::move(m_data_accum); // <year> else if (strcmp(tagname, "year") == 0) - m_current_info->m_year = m_data_accum; + m_current_info->m_year = std::move(m_data_accum); // <publisher> else if (strcmp(tagname, "publisher") == 0) - m_current_info->m_publisher = m_data_accum; + m_current_info->m_publisher = std::move(m_data_accum); // </part> else if (strcmp(tagname, "part") == 0) @@ -815,14 +897,27 @@ void softlist_parser::parse_soft_end(const char *tagname) if (!m_current_part->m_romdata.empty()) add_rom_entry("", "", 0, 0, ROMENTRYTYPE_END); - // get the info; if present, copy shared data (we assume name/value strings live - // in the string pool and don't need to be reallocated) + // get the info; if present, copy shared data if (m_current_info != nullptr) - for (const feature_list_item &item : m_current_info->shared_info()) - m_current_part->m_featurelist.emplace_back(item.name(), item.value()); + for (const software_info_item &item : m_current_info->shared_features()) + m_current_part->m_features.emplace(item.name(), item.value()); } } +} // namespace detail + + +void parse_software_list( + util::read_stream &file, + std::string_view filename, + std::string &listname, + std::string &description, + std::list<software_info> &infolist, + std::ostream &errors) +{ + detail::softlist_parser(file, filename, listname, description, infolist, errors); +} + //------------------------------------------------- // software_name_parse - helper that splits a @@ -840,10 +935,10 @@ void softlist_parser::parse_soft_end(const char *tagname) // case. //------------------------------------------------- -bool software_name_parse(const std::string &identifier, std::string *list_name, std::string *software_name, std::string *part_name) +bool software_name_parse(std::string_view identifier, std::string *list_name, std::string *software_name, std::string *part_name) { // first, sanity check the arguments - if (!std::regex_match(identifier, s_potenial_softlist_regex)) + if (!std::regex_match(identifier.begin(), identifier.end(), s_potential_softlist_regex)) return false; // reset all output parameters (if specified of course) @@ -856,7 +951,7 @@ bool software_name_parse(const std::string &identifier, std::string *list_name, // if no colon, this is the swname by itself auto split1 = identifier.find_first_of(':'); - if (split1 == std::string::npos) + if (split1 == std::string_view::npos) { if (software_name != nullptr) *software_name = identifier; @@ -865,7 +960,7 @@ bool software_name_parse(const std::string &identifier, std::string *list_name, // if one colon, it is the swname and swpart alone auto split2 = identifier.find_first_of(':', split1 + 1); - if (split2 == std::string::npos) + if (split2 == std::string_view::npos) { if (software_name != nullptr) *software_name = identifier.substr(0, split1); diff --git a/src/emu/softlist.h b/src/emu/softlist.h index feab67448d3..634055a4bac 100644 --- a/src/emu/softlist.h +++ b/src/emu/softlist.h @@ -19,54 +19,71 @@ #include "corefile.h" #include <list> +#include <set> +#include <string> +#include <string_view> + + +//************************************************************************** +// FORWARD DECLARATIONS +//************************************************************************** + +namespace detail { class softlist_parser; } //************************************************************************** // CONSTANTS //************************************************************************** -#define SOFTWARE_SUPPORTED_YES 0 -#define SOFTWARE_SUPPORTED_PARTIAL 1 -#define SOFTWARE_SUPPORTED_NO 2 +enum class software_support +{ + SUPPORTED, + PARTIALLY_SUPPORTED, + UNSUPPORTED +}; //************************************************************************** // TYPE DEFINITIONS //************************************************************************** -// ======================> feature_list_item - // an item in a list of name/value pairs -class feature_list_item +class software_info_item { public: // construction/destruction - feature_list_item(std::string &&name, std::string &&value); - feature_list_item(const std::string &name, const std::string &value); - feature_list_item(feature_list_item const &) = delete; - feature_list_item(feature_list_item &&) = delete; - feature_list_item& operator=(feature_list_item const &) = delete; - feature_list_item& operator=(feature_list_item &&) = delete; + software_info_item(std::string &&name, std::string &&value); + software_info_item(const std::string &name, const std::string &value); + software_info_item(software_info_item const &) = default; + software_info_item(software_info_item &&) = default; + software_info_item& operator=(software_info_item const &) = default; + software_info_item& operator=(software_info_item &&) = default; // getters - feature_list_item *next() const { return m_next; } - const std::string &name() const { return m_name; } - const std::string &value() const { return m_value; } + const std::string &name() const noexcept { return m_name; } + const std::string &value() const noexcept { return m_value; } + + // collection + struct compare + { + using is_transparent = void; + bool operator()(const software_info_item &a, const software_info_item &b) const noexcept { return a.m_name < b.m_name; } + bool operator()(const software_info_item &a, const std::string_view &b) const noexcept { return a.m_name < b; } + bool operator()(const std::string_view &a, const software_info_item &b) const noexcept { return a < b.m_name; } + }; + using set = std::set<software_info_item, compare>; private: // internal state - feature_list_item * m_next; std::string m_name; std::string m_value; }; -// ======================> software_part - // a single part of a software item class software_part { - friend class softlist_parser; + friend class detail::softlist_parser; public: // construction/destruction @@ -77,38 +94,34 @@ public: software_part& operator=(software_part &&) = delete; // getters - software_part *next() const { return m_next; } - software_info &info() const { return m_info; } - const std::string &name() const { return m_name; } - const std::string &interface() const { return m_interface; } - const std::list<feature_list_item> &featurelist() const { return m_featurelist; } - const std::vector<rom_entry> &romdata() const { return m_romdata; } + software_info &info() const noexcept { return m_info; } + const std::string &name() const noexcept { return m_name; } + const std::string &interface() const noexcept { return m_interface; } + const software_info_item::set &features() const noexcept { return m_features; } + const std::vector<rom_entry> &romdata() const noexcept { return m_romdata; } // helpers - bool matches_interface(const char *interface) const; - const char *feature(const std::string &feature_name) const; + bool matches_interface(const char *interface_list) const noexcept; + const char *feature(std::string_view feature_name) const noexcept; private: // internal state - software_part * m_next; - software_info & m_info; - std::string m_name; - std::string m_interface; - std::list<feature_list_item> m_featurelist; - std::vector<rom_entry> m_romdata; + software_info & m_info; + std::string m_name; + std::string m_interface; + software_info_item::set m_features; + std::vector<rom_entry> m_romdata; }; -// ======================> software_info - // a single software item class software_info { - friend class softlist_parser; + friend class detail::softlist_parser; public: // construction/destruction - software_info(std::string &&name, std::string &&parent, const std::string &supported); + software_info(std::string &&name, std::string &&parent, std::string_view supported); software_info(software_info const &) = delete; software_info(software_info &&) = delete; software_info& operator=(software_info const &) = delete; @@ -120,96 +133,42 @@ public: const std::string &parentname() const { return m_parentname; } const std::string &year() const { return m_year; } const std::string &publisher() const { return m_publisher; } - const std::list<feature_list_item> &other_info() const { return m_other_info; } - const std::list<feature_list_item> &shared_info() const { return m_shared_info; } - u32 supported() const { return m_supported; } + const std::list<software_info_item> &info() const { return m_info; } + const software_info_item::set &shared_features() const { return m_shared_features; } + software_support supported() const { return m_supported; } const std::list<software_part> &parts() const { return m_partdata; } // additional operations - const software_part *find_part(const std::string &part_name, const char *interface = nullptr) const; + const software_part *find_part(std::string_view part_name, const char *interface = nullptr) const; bool has_multiple_parts(const char *interface) const; private: // internal state - u32 m_supported; - std::string m_shortname; - std::string m_longname; - std::string m_parentname; - std::string m_year; // Copyright year on title screen, actual release dates can be tracked in external resources - std::string m_publisher; - std::list<feature_list_item> m_other_info; // Here we store info like developer, serial #, etc. which belong to the software entry as a whole - std::list<feature_list_item> m_shared_info; // Here we store info like TV standard compatibility, or add-on requirements, etc. which get inherited - // by each part of this software entry (after loading these are stored in partdata->featurelist) + software_support m_supported; + std::string m_shortname; + std::string m_longname; + std::string m_parentname; + std::string m_year; // Copyright year on title screen, actual release dates can be tracked in external resources + std::string m_publisher; + std::list<software_info_item> m_info; // Here we store info like developer, serial #, etc. which belong to the software entry as a whole + software_info_item::set m_shared_features; // Here we store info like TV standard compatibility, or add-on requirements, etc. which get inherited + // by each part of this software entry (after loading these are stored in partdata->features) std::list<software_part> m_partdata; }; -// ======================> softlist_parser - -class softlist_parser -{ -public: - // construction (== execution) - softlist_parser(util::core_file &file, const std::string &filename, std::string &description, std::list<software_info> &infolist, std::ostringstream &errors); - -private: - enum parse_position - { - POS_ROOT, - POS_MAIN, - POS_SOFT, - POS_PART, - POS_DATA - }; - - // internal parsing helpers - const char *infoname() const { return (m_current_info != nullptr) ? m_current_info->shortname().c_str() : "???"; } - int line() const; - int column() const; - const char *parser_error() const; - - // internal error helpers - template <typename Format, typename... Params> void parse_error(Format &&fmt, Params &&... args); - void unknown_tag(const char *tagname) { parse_error("Unknown tag: %s", tagname); } - void unknown_attribute(const char *attrname) { parse_error("Unknown attribute: %s", attrname); } - - // internal helpers - template <typename T> std::vector<std::string> parse_attributes(const char **attributes, const T &attrlist); - bool parse_name_and_value(const char **attributes, std::string &name, std::string &value); - void add_rom_entry(std::string &&name, std::string &&hashdata, u32 offset, u32 length, u32 flags); - - // expat callbacks - static void start_handler(void *data, const char *tagname, const char **attributes); - static void data_handler(void *data, const char *s, int len); - static void end_handler(void *data, const char *name); - - // internal parsing - void parse_root_start(const char *tagname, const char **attributes); - void parse_main_start(const char *tagname, const char **attributes); - void parse_soft_start(const char *tagname, const char **attributes); - void parse_part_start(const char *tagname, const char **attributes); - void parse_data_start(const char *tagname, const char **attributes); - void parse_soft_end(const char *name); - - // internal parsing state - util::core_file & m_file; - std::string m_filename; - std::list<software_info> & m_infolist; - std::ostringstream & m_errors; - struct XML_ParserStruct * m_parser; - bool m_done; - std::string & m_description; - bool m_data_accum_expected; - std::string m_data_accum; - software_info * m_current_info; - software_part * m_current_part; - parse_position m_pos; -}; - - // ----- Helpers ----- +// parses a software list +void parse_software_list( + util::read_stream &file, + std::string_view filename, + std::string &listname, + std::string &description, + std::list<software_info> &infolist, + std::ostream &errors); + // parses a software identifier (e.g. - 'apple2e:agentusa:flop1') into its constituent parts (returns false if cannot parse) -bool software_name_parse(const std::string &identifier, std::string *list_name = nullptr, std::string *software_name = nullptr, std::string *part_name = nullptr); +bool software_name_parse(std::string_view identifier, std::string *list_name = nullptr, std::string *software_name = nullptr, std::string *part_name = nullptr); #endif // MAME_EMU_SOFTLIST_H diff --git a/src/emu/softlist_dev.cpp b/src/emu/softlist_dev.cpp index 831fc3f6734..b65f5f28300 100644 --- a/src/emu/softlist_dev.cpp +++ b/src/emu/softlist_dev.cpp @@ -9,13 +9,18 @@ ***************************************************************************/ #include "emu.h" -#include "emuopts.h" +#include "softlist_dev.h" + #include "diimage.h" +#include "emuopts.h" +#include "fileio.h" #include "romload.h" -#include "softlist_dev.h" #include "validity.h" -#include <ctype.h> +#include "corestr.h" +#include "unicode.h" + +#include <cctype> //************************************************************************** @@ -45,9 +50,9 @@ image_software_list_loader image_software_list_loader::s_instance; // false_software_list_loader::load_software //------------------------------------------------- -bool false_software_list_loader::load_software(device_image_interface &image, software_list_device &swlist, const char *swname, const rom_entry *start_entry) const +std::error_condition false_software_list_loader::load_software(device_image_interface &image, software_list_device &swlist, std::string_view swname, const rom_entry *start_entry) const { - return false; + return image_error::UNSUPPORTED; } @@ -55,10 +60,10 @@ bool false_software_list_loader::load_software(device_image_interface &image, so // rom_software_list_loader::load_software //------------------------------------------------- -bool rom_software_list_loader::load_software(device_image_interface &image, software_list_device &swlist, const char *swname, const rom_entry *start_entry) const +std::error_condition rom_software_list_loader::load_software(device_image_interface &image, software_list_device &swlist, std::string_view swname, const rom_entry *start_entry) const { swlist.machine().rom_load().load_software_part_region(image.device(), swlist, swname, start_entry); - return true; + return std::error_condition(); } @@ -66,7 +71,7 @@ bool rom_software_list_loader::load_software(device_image_interface &image, soft // image_software_list_loader::load_software //------------------------------------------------- -bool image_software_list_loader::load_software(device_image_interface &image, software_list_device &swlist, const char *swname, const rom_entry *start_entry) const +std::error_condition image_software_list_loader::load_software(device_image_interface &image, software_list_device &swlist, std::string_view swname, const rom_entry *start_entry) const { return image.load_software(swlist, swname, start_entry); } @@ -80,12 +85,11 @@ bool image_software_list_loader::load_software(device_image_interface &image, so // software_list_device - constructor //------------------------------------------------- -software_list_device::software_list_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) - : device_t(mconfig, SOFTWARE_LIST, tag, owner, clock), - m_list_type(SOFTWARE_LIST_ORIGINAL_SYSTEM), +software_list_device::software_list_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : + device_t(mconfig, SOFTWARE_LIST, tag, owner, clock), + m_list_type(softlist_type::ORIGINAL_SYSTEM), m_filter(nullptr), m_parsed(false), - m_file(mconfig.options().hash_path(), OPEN_FLAG_READ), m_description("") { } @@ -106,7 +110,7 @@ void software_list_device::device_start() // and optional interface //------------------------------------------------- -void software_list_device::find_approx_matches(const std::string &name, int matches, const software_info **list, const char *interface) +void software_list_device::find_approx_matches(std::string_view name, int matches, const software_info **list, const char *interface) { // if no name, return if (name.empty()) @@ -172,8 +176,10 @@ void software_list_device::find_approx_matches(const std::string &name, int matc void software_list_device::release() { - osd_printf_verbose("Resetting %s\n", m_file.filename()); + osd_printf_verbose("%s: Resetting %s\n", tag(), m_list_name); m_parsed = false; + m_filename.clear(); + m_shortname.clear(); m_description.clear(); m_errors.clear(); m_infolist.clear(); @@ -185,10 +191,10 @@ void software_list_device::release() // across all software list devices //------------------------------------------------- -software_list_device *software_list_device::find_by_name(const machine_config &config, const std::string &name) +software_list_device *software_list_device::find_by_name(const machine_config &config, std::string_view name) { // iterate over each device in the system and find a match - for (software_list_device &swlistdev : software_list_device_iterator(config.root_device())) + for (software_list_device &swlistdev : software_list_device_enumerator(config.root_device())) if (swlistdev.list_name() == name) return &swlistdev; return nullptr; @@ -201,35 +207,35 @@ software_list_device *software_list_device::find_by_name(const machine_config &c // name, across all software list devices //------------------------------------------------- -void software_list_device::display_matches(const machine_config &config, const char *interface, const std::string &name) +void software_list_device::display_matches(const machine_config &config, const char *interface, std::string_view name) { // check if there is at least one software list - software_list_device_iterator deviter(config.root_device()); + software_list_device_enumerator deviter(config.root_device()); if (deviter.first() != nullptr) osd_printf_error("\n\"%s\" approximately matches the following\n" - "supported software items (best match first):\n\n", name.c_str()); + "supported software items (best match first):\n\n", name); // iterate through lists for (software_list_device &swlistdev : deviter) { // get the top 16 approximate matches for the selected device interface (i.e. only carts for cartslot, etc.) const software_info *matches[16] = { nullptr }; - swlistdev.find_approx_matches(name, ARRAY_LENGTH(matches), matches, interface); + swlistdev.find_approx_matches(name, std::size(matches), matches, interface); // if we found some, print them if (matches[0] != nullptr) { // different output depending on original system or compatible - if (swlistdev.list_type() == SOFTWARE_LIST_ORIGINAL_SYSTEM) - osd_printf_error("* Software list \"%s\" (%s) matches: \n", swlistdev.list_name().c_str(), swlistdev.description().c_str()); + if (swlistdev.is_original()) + osd_printf_error("* Software list \"%s\" (%s) matches: \n", swlistdev.list_name(), swlistdev.description()); else - osd_printf_error("* Compatible software list \"%s\" (%s) matches: \n", swlistdev.list_name().c_str(), swlistdev.description().c_str()); + osd_printf_error("* Compatible software list \"%s\" (%s) matches: \n", swlistdev.list_name(), swlistdev.description()); // print them out for (auto &match : matches) { if (match != nullptr) - osd_printf_error("%-18s%s\n", match->shortname().c_str(), match->longname().c_str()); + osd_printf_error("%-18s%s\n", match->shortname(), match->longname()); } osd_printf_error("\n"); @@ -244,7 +250,7 @@ void software_list_device::display_matches(const machine_config &config, const c // from an intermediate point //------------------------------------------------- -const software_info *software_list_device::find(const std::string &look_for) +const software_info *software_list_device::find(std::string_view look_for) { // empty search returns nothing if (look_for.empty()) @@ -259,9 +265,9 @@ const software_info *software_list_device::find(const std::string &look_for) info_list.end(), [&look_for, iswild] (const software_info &info) { - const char *shortname = info.shortname().c_str(); - return (iswild && core_strwildcmp(look_for.c_str(), shortname) == 0) - || core_stricmp(look_for.c_str(), shortname) == 0; + std::string_view shortname = info.shortname(); + return (iswild && core_strwildcmp(look_for, shortname) == 0) + || util::streqlower(look_for, shortname); }); return iter != info_list.end() ? &*iter : nullptr; @@ -282,17 +288,25 @@ void software_list_device::parse() m_errors.clear(); // attempt to open the file - osd_file::error filerr = m_file.open(m_list_name.c_str(), ".xml"); - if (filerr == osd_file::error::NONE) + emu_file file(mconfig().options().hash_path(), OPEN_FLAG_READ); + const std::error_condition filerr = file.open(m_list_name + ".xml"); + m_filename = file.filename(); + if (!filerr) { // parse if no error std::ostringstream errs; - softlist_parser parser(m_file, m_file.filename(), m_description, m_infolist, errs); - m_file.close(); + parse_software_list(file, m_filename, m_shortname, m_description, m_infolist, errs); + file.close(); m_errors = errs.str(); } + else if (std::errc::no_such_file_or_directory == filerr) + { + osd_printf_verbose("%s: Software list %s not found\n", tag(), m_filename); + } else - m_errors = string_format("Error opening file: %s\n", filename()); + { + m_errors = string_format("Error opening file: %s\n", m_filename); + } // indicate that we've been parsed m_parsed = true; @@ -360,7 +374,7 @@ device_image_interface *software_list_device::find_mountable_image(const machine if (mount != nullptr && strcmp(mount, "no") == 0) return nullptr; - for (device_image_interface &image : image_interface_iterator(mconfig.root_device())) + for (device_image_interface &image : image_interface_enumerator(mconfig.root_device())) { const char *interface = image.image_interface(); if (interface != nullptr && part.matches_interface(interface) && filter(image)) @@ -384,7 +398,7 @@ device_image_interface *software_list_device::find_mountable_image(const machine // When softlists were refactored in MAME 0.183, this was changed to build a "plan" for what needs to be loaded, so // it was incorrect to check the image slot. This is why an overload for find_mountable_image() was created that // takes an std::function. This overload is being preserved for compatibility with existing code, but I regard the - // continued existance of this overload is a red flag. + // continued existence of this overload as a red flag. return find_mountable_image( mconfig, part, @@ -404,12 +418,9 @@ void software_list_device::device_validity_check(validity_checker &valid) const if (valid.already_checked(std::string("softlist/").append(m_list_name).c_str())) return; - // do device validation only in case of validate command - if (!valid.validate_all()) - return; - - // actually do the validate - const_cast<software_list_device *>(this)->internal_validity_check(valid); + // skip in case of quick validation + if (!valid.quick()) + const_cast<software_list_device *>(this)->internal_validity_check(valid); } @@ -420,118 +431,187 @@ void software_list_device::device_validity_check(validity_checker &valid) const void software_list_device::internal_validity_check(validity_checker &valid) { - enum { NAME_LEN_PARENT = 16, NAME_LEN_CLONE = 16 }; + enum { NAME_LEN_LIST = 24, NAME_LEN_PARENT = 16, NAME_LEN_CLONE = 16, NAME_LEN_PART = 16 }; + auto const valid_name_char = [] (char ch) { return ((ch >= '0') && (ch <= '9')) || ((ch >= 'a') && (ch <= 'z')) || (ch == '_'); }; + auto const valid_tag_char = [] (char ch) { return ((ch >= '0') && (ch <= '9')) || ((ch >= 'a') && (ch <= 'z')) || strchr("$.:_", u8(ch)); }; + auto const valid_year_char = [] (char ch) { return isdigit(u8(ch)) || (ch == '?') || (ch == '+'); }; + auto const valid_label_char = [] (char ch) { return (ch >= ' ') && (ch <= '~') && !strchr("!$%/:\\", u8(ch)); }; + + // first parse and output core errors if any + auto const &info(get_info()); + if (!m_errors.empty()) + { + osd_printf_error("%s: Errors parsing software list:\n%s", m_filename, m_errors); + release(); + return; + } + // ignore empty shortname to work around ignoring missing software lists + if (!m_shortname.empty()) + { + if (m_list_name != m_shortname) + osd_printf_error("%s: Software list name %s does not match filename %s\n", m_filename, m_shortname, m_list_name); + + if (m_shortname.length() > NAME_LEN_LIST) + osd_printf_error("%s: %s software list name must be %d characters or less\n", m_filename, m_shortname, NAME_LEN_LIST); + + if (std::find_if_not(m_shortname.begin(), m_shortname.end(), valid_name_char) != m_shortname.end()) + osd_printf_error("%s: %s software list name contains invalid characters\n", m_filename, m_shortname); + } + + // now check the software items softlist_map names; softlist_map descriptions; - for (const software_info &swinfo : get_info()) + for (const software_info &swinfo : info) { std::string const &shortname(swinfo.shortname()); - // first parse and output core errors if any - if (m_errors.length() > 0) - { - osd_printf_error("%s: Errors parsing software list:\n%s", filename(), errors_string()); - break; - } - - // Now check if the xml data is valid: - - // Did we lost any description? if (swinfo.longname().empty()) - { - osd_printf_error("%s: %s has no description\n", filename(), shortname.c_str()); - break; - } + osd_printf_error("%s: %s has no description\n", m_filename, shortname); - // Did we lost any year? if (swinfo.year().empty()) - { - osd_printf_error("%s: %s has no year\n", filename(), shortname.c_str()); - break; - } + osd_printf_error("%s: %s has no year\n", m_filename, shortname); - // Did we lost any publisher? if (swinfo.publisher().empty()) - { - osd_printf_error("%s: %s has no publisher\n", filename(), shortname.c_str()); - break; - } + osd_printf_error("%s: %s has no publisher\n", m_filename, shortname); // Did we lost the software parts? if (swinfo.parts().empty()) - { - osd_printf_error("%s: %s has no part\n", filename(), shortname.c_str()); - break; - } + osd_printf_error("%s: %s has no parts\n", m_filename, shortname); // Second, since the xml is fine, run additional checks: // check for duplicate names - if (!names.insert(std::make_pair(shortname, &swinfo)).second) - { - const software_info *match = names.find(shortname)->second; - osd_printf_error("%s: %s is a duplicate name (%s)\n", filename(), shortname.c_str(), match->shortname().c_str()); - } + auto const dupname(names.emplace(shortname, &swinfo)); + if (!dupname.second) + osd_printf_error("%s: %s is a duplicate name (%s)\n", m_filename, shortname, dupname.first->second->shortname()); // check for duplicate descriptions - std::string longname(swinfo.longname()); - if (!descriptions.insert(std::make_pair(strmakelower(longname), &swinfo)).second) - osd_printf_error("%s: %s is a duplicate description (%s)\n", filename(), swinfo.longname().c_str(), shortname.c_str()); + auto const dupdesc(descriptions.emplace(swinfo.longname(), &swinfo)); + if (!dupdesc.second) + osd_printf_error("%s: %s has duplicate description '%s' (%s)\n", m_filename, shortname, swinfo.longname(), dupdesc.first->second->shortname()); bool const is_clone(!swinfo.parentname().empty()); if (is_clone) { if (swinfo.parentname() == shortname) { - osd_printf_error("%s: %s is set as a clone of itself\n", filename(), shortname.c_str()); - break; + osd_printf_error("%s: %s is set as a clone of itself\n", m_filename, shortname); + } + else + { + software_info const *const parent = find(swinfo.parentname()); + if (!parent) + osd_printf_error("%s: %s is a clone of non-existent parent %s\n", m_filename, shortname, swinfo.parentname()); + else if (!parent->parentname().empty()) + osd_printf_error("%s: %s is a clone %s which is a clone of %s\n", m_filename, shortname, swinfo.parentname(), parent->parentname()); } - - // make sure the parent exists - const software_info *swinfo2 = find(swinfo.parentname().c_str()); - - if (swinfo2 == nullptr) - osd_printf_error("%s: parent '%s' software for '%s' not found\n", filename(), swinfo.parentname().c_str(), shortname.c_str()); - else if (!swinfo2->parentname().empty()) - osd_printf_error("%s: %s is a clone of a clone\n", filename(), shortname.c_str()); } // make sure the driver name isn't too long if (shortname.length() > (is_clone ? NAME_LEN_CLONE : NAME_LEN_PARENT)) - osd_printf_error("%s: %s %s software name must be %d characters or less\n", filename(), shortname.c_str(), + { + osd_printf_error( + "%s: %s %s software name must be %d characters or less\n", + m_filename, + shortname, is_clone ? "clone" : "parent", is_clone ? NAME_LEN_CLONE : NAME_LEN_PARENT); + } // make sure the driver name doesn't contain invalid characters - for (char ch : shortname) - if (((ch < '0') || (ch > '9')) && ((ch < 'a') || (ch > 'z')) && (ch != '_')) - { - osd_printf_error("%s: %s contains invalid characters\n", filename(), shortname.c_str()); - break; - } + if (std::find_if_not(shortname.begin(), shortname.end(), valid_name_char) != shortname.end()) + osd_printf_error("%s: %s contains invalid characters\n", m_filename, shortname); // make sure the year is only digits, '?' or '+' - for (char ch : swinfo.year()) - if (!isdigit(u8(ch)) && (ch != '?') && (ch != '+')) - { - osd_printf_error("%s: %s has an invalid year '%s'\n", filename(), shortname.c_str(), swinfo.year().c_str()); - break; - } + if (std::find_if_not(swinfo.year().begin(), swinfo.year().end(), valid_year_char) != swinfo.year().end()) + osd_printf_error("%s: %s has an invalid year '%s'\n", m_filename, shortname, swinfo.year()); - softlist_map part_names; - for (const software_part &part : swinfo.parts()) + std::set<std::string> part_names; + for (software_part const &part : swinfo.parts()) { if (part.interface().empty()) - osd_printf_error("%s: %s has a part (%s) without interface\n", filename(), shortname.c_str(), part.name().c_str()); + osd_printf_error("%s: %s part %s has no interface\n", m_filename, shortname, part.name()); if (part.romdata().empty()) - osd_printf_error("%s: %s has a part (%s) with no data\n", filename(), shortname.c_str(), part.name().c_str()); + osd_printf_error("%s: %s part %s has no data areas\n", m_filename, shortname, part.name()); - if (!part_names.insert(std::make_pair(part.name(), &swinfo)).second) - osd_printf_error("%s: %s has a part (%s) whose name is duplicate\n", filename(), shortname.c_str(), part.name().c_str()); + if (!part_names.emplace(part.name()).second) + osd_printf_error("%s: %s part %s has duplicate name\n", m_filename, shortname, part.name()); + + if (part.name().length() > NAME_LEN_PART) + osd_printf_error("%s: %s part %s name must be %d characters or less\n", m_filename, shortname, part.name(), NAME_LEN_PART); + + if (std::find_if_not(part.name().begin(), part.name().end(), valid_name_char) != part.name().end()) + osd_printf_error("%s: %s part %s contains invalid characters\n", m_filename, shortname, part.name()); + + // validate data areas + // based on ROM validation code from validity.cpp but adapted to work with rom_entry and ignore unavailable features like BIOS + if (!part.romdata().empty()) + { + std::map<std::string, u32> data_area_map; + char const *last_region_name = "???"; + char const *last_name = "???"; + u32 current_length = 0; + int items_since_region = 1; + for (rom_entry const *romp = &part.romdata().front(); romp && !ROMENTRY_ISEND(romp); ++romp) + { + if (ROMENTRY_ISREGION(romp)) // if this is a region, make sure it's valid, and record the length + { + // if we haven't seen any items since the last region, print a warning + if (!items_since_region) + osd_printf_verbose("%s: %s part %s has empty data area '%s' (warning)\n", m_filename, shortname, part.name(), last_region_name); + + // reset our region tracking states + items_since_region = (ROMREGION_ISERASE(romp) || ROMREGION_ISDISKDATA(romp)) ? 1 : 0; + last_region_name = romp->name().c_str(); + + // check for a valid tag + if (romp->name().size() < MIN_TAG_LENGTH) + osd_printf_error("%s: %s part %s data area name '%s' is too short (must be at least %d characters)\n", m_filename, shortname, part.name(), romp->name(), MIN_TAG_LENGTH); + + if (std::find_if_not(romp->name().begin(), romp->name().end(), valid_tag_char) != romp->name().end()) + osd_printf_error("%s: %s part %s data area name '%s' contains invalid characters\n", m_filename, shortname, part.name(), romp->name()); + + // attempt to add it to the map, reporting duplicates as errors + current_length = ROMREGION_GETLENGTH(romp); + if (!data_area_map.emplace(romp->name(), current_length).second) + osd_printf_error("%s: %s part %s data area has duplicate name '%s'\n", m_filename, shortname, part.name(), romp->name()); + if (current_length == 0) + osd_printf_error("%s: %s part %s data area '%s' has zero length\n", m_filename, shortname, part.name(), romp->name()); + } + else if (ROMENTRY_ISFILE(romp)) // if this is a file, make sure it is properly formatted + { + // track the last ROM label we found + last_name = romp->name().c_str(); + + // validate the name + if (romp->name().length() > 127) + osd_printf_error("%s: %s part %s ROM label '%s' exceeds maximum 127 characters\n", m_filename, shortname, part.name(), romp->name()); + if (std::find_if_not(romp->name().begin(), romp->name().end(), valid_label_char) != romp->name().end()) + osd_printf_error("%s: %s part %s ROM label '%s' contains invalid characters\n", m_filename, shortname, part.name(), romp->name()); + + // make sure the hash is valid + util::hash_collection hashes; + if (!hashes.from_internal_string(romp->hashdata())) + osd_printf_error("%s: %s part %s ROM '%s' has invalid hash string '%s'\n", m_filename, shortname, part.name(), romp->name(), romp->hashdata()); + } + + // for any non-region ending entries, make sure they don't extend past the end + if (!ROMENTRY_ISREGIONEND(romp) && current_length > 0) + { + items_since_region++; + if (!ROMENTRY_ISIGNORE(romp) && (ROM_GETOFFSET(romp) + ROM_GETLENGTH(romp) > current_length)) + osd_printf_error("%s: %s part %s ROM '%s' extends past the defined data area\n", m_filename, shortname, part.name(), last_name); + } + } + + // if we haven't seen any items since the last region, print a warning + if (!items_since_region) + osd_printf_verbose("%s: %s part %s has empty data area '%s' (warning)\n", m_filename, shortname, part.name(), last_region_name); + } } } - // release all the memory + // discard parsed info release(); } diff --git a/src/emu/softlist_dev.h b/src/emu/softlist_dev.h index 4370ec8a20d..41c92b4f3c0 100644 --- a/src/emu/softlist_dev.h +++ b/src/emu/softlist_dev.h @@ -25,12 +25,6 @@ #define SOFTWARE_SUPPORTED_PARTIAL 1 #define SOFTWARE_SUPPORTED_NO 2 -enum softlist_type -{ - SOFTWARE_LIST_ORIGINAL_SYSTEM, - SOFTWARE_LIST_COMPATIBLE_SYSTEM -}; - enum software_compatibility { SOFTWARE_IS_COMPATIBLE, @@ -49,7 +43,7 @@ enum software_compatibility class software_list_loader { public: - virtual bool load_software(device_image_interface &image, software_list_device &swlist, const char *swname, const rom_entry *start_entry) const = 0; + virtual std::error_condition load_software(device_image_interface &image, software_list_device &swlist, std::string_view swname, const rom_entry *start_entry) const = 0; }; @@ -58,7 +52,7 @@ public: class false_software_list_loader : public software_list_loader { public: - virtual bool load_software(device_image_interface &image, software_list_device &swlist, const char *swname, const rom_entry *start_entry) const override; + virtual std::error_condition load_software(device_image_interface &image, software_list_device &swlist, std::string_view swname, const rom_entry *start_entry) const override; static const software_list_loader &instance() { return s_instance; } private: @@ -71,7 +65,7 @@ private: class rom_software_list_loader : public software_list_loader { public: - virtual bool load_software(device_image_interface &image, software_list_device &swlist, const char *swname, const rom_entry *start_entry) const override; + virtual std::error_condition load_software(device_image_interface &image, software_list_device &swlist, std::string_view swname, const rom_entry *start_entry) const override; static const software_list_loader &instance() { return s_instance; } private: @@ -84,7 +78,7 @@ private: class image_software_list_loader : public software_list_loader { public: - virtual bool load_software(device_image_interface &image, software_list_device &swlist, const char *swname, const rom_entry *start_entry) const override; + virtual std::error_condition load_software(device_image_interface &image, software_list_device &swlist, std::string_view swname, const rom_entry *start_entry) const override; static const software_list_loader &instance() { return s_instance; } private: @@ -97,23 +91,28 @@ private: // device representing a software list class software_list_device : public device_t { - friend class softlist_parser; - public: + enum class softlist_type + { + ORIGINAL_SYSTEM, + COMPATIBLE_SYSTEM + }; + // construction/destruction software_list_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock = 0); // inline configuration helpers software_list_device &set_type(const char *list, softlist_type list_type) { m_list_name.assign(list); m_list_type = list_type; return *this; } - software_list_device &set_original(const char *list) { return set_type(list, SOFTWARE_LIST_ORIGINAL_SYSTEM); } - software_list_device &set_compatible(const char *list) { return set_type(list, SOFTWARE_LIST_COMPATIBLE_SYSTEM); } + software_list_device &set_original(const char *list) { return set_type(list, softlist_type::ORIGINAL_SYSTEM); } + software_list_device &set_compatible(const char *list) { return set_type(list, softlist_type::COMPATIBLE_SYSTEM); } software_list_device &set_filter(const char *filter) { m_filter = filter; return *this; } // getters const std::string &list_name() const { return m_list_name; } softlist_type list_type() const { return m_list_type; } + bool is_original() const { return softlist_type::ORIGINAL_SYSTEM == m_list_type; } + bool is_compatible() const { return softlist_type::COMPATIBLE_SYSTEM == m_list_type; } const char *filter() const { return m_filter; } - const char *filename() { return m_file.filename(); } // getters that may trigger a parse const std::string &description() { if (!m_parsed) parse(); return m_description; } @@ -122,20 +121,20 @@ public: const std::list<software_info> &get_info() { if (!m_parsed) parse(); return m_infolist; } // operations - const software_info *find(const std::string &look_for); - void find_approx_matches(const std::string &name, int matches, const software_info **list, const char *interface); + const software_info *find(std::string_view look_for); + void find_approx_matches(std::string_view name, int matches, const software_info **list, const char *interface); void release(); software_compatibility is_compatible(const software_part &part) const; // static helpers - static software_list_device *find_by_name(const machine_config &mconfig, const std::string &name); - static void display_matches(const machine_config &config, const char *interface, const std::string &name); + static software_list_device *find_by_name(const machine_config &mconfig, std::string_view name); + static void display_matches(const machine_config &config, const char *interface, std::string_view name); static device_image_interface *find_mountable_image(const machine_config &mconfig, const software_part &part, std::function<bool (const device_image_interface &)> filter); static device_image_interface *find_mountable_image(const machine_config &mconfig, const software_part &part); protected: // device-level overrides - virtual void device_start() override; + virtual void device_start() override ATTR_COLD; virtual void device_validity_check(validity_checker &valid) const override ATTR_COLD; private: @@ -150,7 +149,8 @@ private: // internal state bool m_parsed; - emu_file m_file; + std::string m_filename; + std::string m_shortname; std::string m_description; std::string m_errors; std::list<software_info> m_infolist; @@ -161,7 +161,7 @@ private: DECLARE_DEVICE_TYPE(SOFTWARE_LIST, software_list_device) // device type iterator -typedef device_type_iterator<software_list_device> software_list_device_iterator; +typedef device_type_enumerator<software_list_device> software_list_device_enumerator; #endif // MAME_EMU_SOFTLIST_DEV_H diff --git a/src/emu/sound.cpp b/src/emu/sound.cpp index 805a6920fed..e64941f558c 100644 --- a/src/emu/sound.cpp +++ b/src/emu/sound.cpp @@ -1,1174 +1,2546 @@ // license:BSD-3-Clause -// copyright-holders:Aaron Giles +// copyright-holders:O. Galibert, Aaron Giles /*************************************************************************** - sound.c + sound.cpp Core sound functions and definitions. ***************************************************************************/ #include "emu.h" -#include "speaker.h" -#include "emuopts.h" -#include "osdepend.h" + +#include "audio_effects/aeffect.h" +#include "resampler.h" + #include "config.h" +#include "emuopts.h" +#include "main.h" +#include "speaker.h" + #include "wavwrite.h" +#include "xmlfile.h" + +#include "osdepend.h" +#include "util/language.h" +#include <algorithm> //************************************************************************** // DEBUGGING //************************************************************************** -#define VERBOSE (0) +#define LOG_OUTPUT_FUNC m_machine.logerror -#define VPRINTF(x) do { if (VERBOSE) osd_printf_debug x; } while (0) +#define LOG_OSD_INFO (1U << 1) +#define LOG_MAPPING (1U << 2) +#define LOG_OSD_STREAMS (1U << 3) +#define LOG_ORDER (1U << 4) +#define VERBOSE -1 - -//************************************************************************** -// CONSTANTS -//************************************************************************** +#include "logmacro.h" -//************************************************************************** -// GLOBAL VARIABLES -//************************************************************************** - const attotime sound_manager::STREAMS_UPDATE_ATTOTIME = attotime::from_hz(STREAMS_UPDATE_FREQUENCY); +//**// Output buffer management -//************************************************************************** -// INITIALIZATION -//************************************************************************** +// Output buffers store samples produced every system-wide update. +// They give access to a window of samples produced before the update, +// and ensure that enough space is available to fit the update. -//------------------------------------------------- -// sound_stream - constructor -//------------------------------------------------- -sound_stream::sound_stream(device_t &device, int inputs, int outputs, int sample_rate, stream_update_delegate callback) - : m_device(device), - m_next(nullptr), - m_sample_rate(sample_rate), - m_new_sample_rate(0xffffffff), - m_attoseconds_per_sample(0), - m_max_samples_per_update(0), - m_input(inputs), - m_input_array(inputs), - m_resample_bufalloc(0), - m_output(outputs), - m_output_array(outputs), - m_output_bufalloc(0), - m_output_sampindex(0), - m_output_update_sampindex(0), - m_output_base_sampindex(0), - m_callback(std::move(callback)) -{ - // get the device's sound interface - device_sound_interface *sound; - if (!device.interface(sound)) - throw emu_fatalerror("Attempted to create a sound_stream with a non-sound device"); - - if(m_callback.isnull()) - m_callback = stream_update_delegate(&device_sound_interface::sound_stream_update,(device_sound_interface *)sound); +template<typename S> emu::detail::output_buffer_interleaved<S>::output_buffer_interleaved(u32 buffer_size, u32 channels) : + m_buffer(channels*buffer_size, 0), + m_sync_sample(0), + m_write_position(0), + m_sync_position(0), + m_history(0), + m_channels(channels) +{ +} - // create a unique tag for saving - std::string state_tag = string_format("%d", m_device.machine().sound().m_stream_list.size()); - m_device.machine().save().save_item(&m_device, "stream", state_tag.c_str(), 0, NAME(m_sample_rate)); - m_device.machine().save().register_postload(save_prepost_delegate(FUNC(sound_stream::postload), this)); +template<typename S> void emu::detail::output_buffer_interleaved<S>::set_buffer_size(u32 buffer_size) +{ + m_buffer.resize(m_channels*buffer_size, 0); +} - // save the gain of each input and output - for (unsigned int inputnum = 0; inputnum < m_input.size(); inputnum++) - { - m_device.machine().save().save_item(&m_device, "stream", state_tag.c_str(), inputnum, NAME(m_input[inputnum].m_gain)); - m_device.machine().save().save_item(&m_device, "stream", state_tag.c_str(), inputnum, NAME(m_input[inputnum].m_user_gain)); - } - for (unsigned int outputnum = 0; outputnum < m_output.size(); outputnum++) - { - m_output[outputnum].m_stream = this; - m_device.machine().save().save_item(&m_device, "stream", state_tag.c_str(), outputnum, NAME(m_output[outputnum].m_gain)); - } +template<typename S> void emu::detail::output_buffer_interleaved<S>::prepare_space(u32 samples) +{ + if(!m_channels) + return; - // Mark synchronous streams as such - m_synchronous = m_sample_rate == STREAM_SYNC; - if (m_synchronous) - { - m_sample_rate = 0; - m_sync_timer = m_device.machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(sound_stream::sync_update), this)); + // Check if potential overflow, bring data back up front if needed + u32 buffer_size = m_buffer.size() / m_channels; + if(m_write_position + samples > buffer_size) { + u32 source_start = (m_sync_position - m_history) * m_channels; + u32 source_end = m_write_position * m_channels; + std::copy(m_buffer.begin() + source_start, m_buffer.begin() + source_end, m_buffer.begin()); + m_write_position -= m_sync_position - m_history; + m_sync_position = m_history; } - else - m_sync_timer = nullptr; - // force an update to the sample rates; this will cause everything to be recomputed - // and will generate the initial resample buffers for our inputs - recompute_sample_rate_data(); + // Clear the destination range + u32 fill_start = m_write_position * m_channels; + u32 fill_end = (m_write_position + samples) * m_channels; + std::fill(m_buffer.begin() + fill_start, m_buffer.begin() + fill_end, 0.0); +} - // set up the initial output buffer positions now that we have data - m_output_base_sampindex = -m_max_samples_per_update; +template<typename S> void emu::detail::output_buffer_interleaved<S>::commit(u32 samples) +{ + m_write_position += samples; } +template<typename S> void emu::detail::output_buffer_interleaved<S>::sync() +{ + m_sync_sample += m_write_position - m_sync_position; + m_sync_position = m_write_position; +} -//------------------------------------------------- -// sample_time - return the emulation time of the -// next sample to be generated on the stream -//------------------------------------------------- +template<typename S> emu::detail::output_buffer_flat<S>::output_buffer_flat(u32 buffer_size, u32 channels) : + m_buffer(channels), + m_sync_sample(0), + m_write_position(0), + m_sync_position(0), + m_history(0), + m_channels(channels) +{ + for(auto &b : m_buffer) + b.resize(buffer_size, 0); +} -attotime sound_stream::sample_time() const +template<typename S> void emu::detail::output_buffer_flat<S>::register_save_state(device_t &device, const char *id1, const char *id2) { - return attotime(m_device.machine().sound().last_update().seconds(), 0) + attotime(0, m_output_sampindex * m_attoseconds_per_sample); + auto &save = device.machine().save(); + + for(unsigned int i=0; i != m_buffer.size(); i++) + save.save_item(&device, id1, id2, i, NAME(m_buffer[i])); + + save.save_item(&device, id1, id2, 0, NAME(m_sync_sample)); + save.save_item(&device, id1, id2, 0, NAME(m_write_position)); + save.save_item(&device, id1, id2, 0, NAME(m_sync_position)); + save.save_item(&device, id1, id2, 0, NAME(m_history)); } +template<typename S> void emu::detail::output_buffer_flat<S>::set_buffer_size(u32 buffer_size) +{ + for(auto &b : m_buffer) + b.resize(buffer_size, 0); +} -//------------------------------------------------- -// user_gain - return the user-controllable gain -// on a given stream's input -//------------------------------------------------- +template<typename S> void emu::detail::output_buffer_flat<S>::prepare_space(u32 samples) +{ + if(!m_channels) + return; -float sound_stream::user_gain(int inputnum) const + // Check if potential overflow, bring data back up front if needed + u32 buffer_size = m_buffer[0].size(); + if(m_write_position + samples > buffer_size) { + u32 source_start = m_sync_position - m_history; + u32 source_end = m_write_position; + for(u32 channel = 0; channel != m_channels; channel++) + std::copy(m_buffer[channel].begin() + source_start, m_buffer[channel].begin() + source_end, m_buffer[channel].begin()); + m_write_position -= source_start; + m_sync_position = m_history; + } + + // Clear the destination range + u32 fill_start = m_write_position; + u32 fill_end = m_write_position + samples; + for(u32 channel = 0; channel != m_channels; channel++) + std::fill(m_buffer[channel].begin() + fill_start, m_buffer[channel].begin() + fill_end, 0.0); +} + +template<typename S> void emu::detail::output_buffer_flat<S>::commit(u32 samples) { - assert(inputnum >= 0 && inputnum < m_input.size()); - return float(m_input[inputnum].m_user_gain) / 256.0f; + m_write_position += samples; } +template<typename S> void emu::detail::output_buffer_flat<S>::sync() +{ + m_sync_sample += m_write_position - m_sync_position; + m_sync_position = m_write_position; +} -//------------------------------------------------- -// input_gain - return the input gain on a -// given stream's input -//------------------------------------------------- +template<typename S> void emu::detail::output_buffer_flat<S>::set_history(u32 history) +{ + m_history = history; + if(m_sync_position < m_history) { + u32 delta = m_history - m_sync_position; + if(m_write_position) + for(u32 channel = 0; channel != m_channels; channel++) { + std::copy_backward(m_buffer[channel].begin(), m_buffer[channel].begin() + m_write_position, m_buffer[channel].begin() + m_write_position + delta); + std::fill(m_buffer[channel].begin() + 1, m_buffer[channel].begin() + delta, m_buffer[channel][0]); + } + else + for(u32 channel = 0; channel != m_channels; channel++) + std::fill(m_buffer[channel].begin(), m_buffer[channel].begin() + m_history, 0.0); -float sound_stream::input_gain(int inputnum) const + m_write_position += delta; + m_sync_position = m_history; + } +} + +template<typename S> void emu::detail::output_buffer_flat<S>::resample(u32 previous_rate, u32 next_rate, attotime sync_time, attotime now) { - assert(inputnum >= 0 && inputnum < m_input.size()); - return float(m_input[inputnum].m_gain) / 256.0f; + if(!m_write_position) + return; + + auto si = [](attotime time, u32 rate) -> s64 { + return time.m_seconds * rate + ((time.m_attoseconds / 100000000) * rate) / 10000000000LL; + }; + + auto cv = [](u32 source_rate, u32 dest_rate, s64 time) -> std::pair<s64, double> { + s64 sec = time / source_rate; + s64 prem = time % source_rate; + double nrem = double(prem * dest_rate) / double(source_rate); + s64 cyc = s64(nrem); + return std::make_pair(sec * dest_rate + cyc, nrem - cyc); + }; + + // Compute what will be the new start, sync and write positions (if it fits) + s64 nsync = si(sync_time, next_rate); + s64 nwrite = si(now, next_rate); + s64 pbase = m_sync_sample - m_sync_position; // Beware, pbase can be negative at startup due to history size + auto [nbase, nbase_dec] = cv(previous_rate, next_rate, pbase < 0 ? 0 : pbase); + nbase += 1; + if(nbase > nsync) + nbase = nsync; + + u32 space = m_buffer[0].size(); + if(nwrite - nbase > space) { + nbase = nwrite - space; + if(nbase > nsync) + fatalerror("Stream buffer too small, can't proceed, rate change %d -> %d, space=%d\n", previous_rate, next_rate, space); + } + + auto [ppos, pdec] = cv(next_rate, previous_rate, nbase); + if(ppos < pbase || ppos > pbase + m_write_position) + fatalerror("Something went very wrong, ppos=%d, pbase=%d, pbase+wp=%d\n", ppos, pbase, pbase + m_write_position); + + double step = double(previous_rate) / double(next_rate); + u32 pindex = ppos - pbase; + u32 nend = nwrite - nbase; + + // Warning: don't try to be too clever, the m_buffer storage is + // registered in the save state system, so it must not move or + // change size + + std::vector<S> copy(m_write_position); + for(u32 channel = 0; channel != m_channels; channel++) { + std::copy(m_buffer[channel].begin(), m_buffer[channel].begin() + m_write_position, copy.begin()); + + // Interpolate the buffer contents + + for(u32 nindex = 0; nindex != nend; nindex++) { + u32 pi0 = std::clamp(pindex, 0U, m_write_position - 1); + u32 pi1 = std::clamp(pindex + 1, 0U, m_write_position - 1); + m_buffer[channel][nindex] = copy[pi0] * (1-pdec) + copy[pi1] * pdec; + + pdec += step; + if(pdec >= 1) { + int s = s32(pdec); + pindex += s; + pdec -= s; + } + } + } + + m_sync_sample = nsync; + m_sync_position = m_sync_sample - nbase; + m_write_position = nend; + + // history and the associated resizes are taken into account later } +template class emu::detail::output_buffer_flat<sound_stream::sample_t>; +template class emu::detail::output_buffer_interleaved<s16>; -//------------------------------------------------- -// output_gain - return the output gain on a -// given stream's output -//------------------------------------------------- -float sound_stream::output_gain(int outputnum) const +// Not inline because with the unique_ptr it would require audio_effect in emu.h + +sound_manager::effect_step::effect_step(u32 buffer_size, u32 channels) : m_buffer(buffer_size, channels) { - assert(outputnum >= 0 && outputnum < m_output.size()); - return float(m_output[outputnum].m_gain) / 256.0f; } -//------------------------------------------------- -// input_name - return the original input gain -// on a given stream's input -//------------------------------------------------- +//**// Streams and routes + +sound_stream::sound_stream(device_t &device, u32 inputs, u32 outputs, u32 sample_rate, stream_update_delegate callback, sound_stream_flags flags) : + m_device(device), + m_output_buffer(0, outputs), + m_sample_rate(sample_rate == SAMPLE_RATE_INPUT_ADAPTIVE || sample_rate == SAMPLE_RATE_OUTPUT_ADAPTIVE || sample_rate == SAMPLE_RATE_ADAPTIVE ? 0 : sample_rate), + m_input_count(inputs), + m_output_count(outputs), + m_input_adaptive(sample_rate == SAMPLE_RATE_INPUT_ADAPTIVE || sample_rate == SAMPLE_RATE_ADAPTIVE), + m_output_adaptive(sample_rate == SAMPLE_RATE_OUTPUT_ADAPTIVE || sample_rate == SAMPLE_RATE_ADAPTIVE), + m_synchronous((flags & STREAM_SYNCHRONOUS) != 0), + m_started(false), + m_in_update(false), + m_sync_timer(nullptr), + m_callback(std::move(callback)) +{ + sound_assert(outputs > 0 || inputs > 0); + + // create a name + m_name = m_device.name(); + m_name += " '"; + m_name += m_device.tag(); + m_name += "'"; + + // create an update timer for synchronous streams + if(synchronous()) + m_sync_timer = m_device.timer_alloc(FUNC(sound_stream::sync_update), this); + + // create the gain vectors + m_input_channel_gain.resize(m_input_count, 1.0); + m_output_channel_gain.resize(m_output_count, 1.0); + m_user_output_channel_gain.resize(m_output_count, 1.0); + m_user_output_gain = 1.0; +} + +sound_stream::~sound_stream() +{ +} -std::string sound_stream::input_name(int inputnum) const +void sound_stream::add_bw_route(sound_stream *source, int output, int input, float gain) { - std::ostringstream str; + m_bw_routes.emplace_back(route_bw(source, output, input, gain)); +} - // start with our device name and tag - assert(inputnum >= 0 && inputnum < m_input.size()); - util::stream_format(str, "%s '%s': ", m_device.name(), m_device.tag()); +void sound_stream::add_fw_route(sound_stream *target, int input, int output) +{ + m_fw_routes.emplace_back(route_fw(target, input, output)); +} - // if we have a source, indicate where the sound comes from by device name and tag - if (m_input[inputnum].m_source != nullptr && m_input[inputnum].m_source->m_stream != nullptr) - { - device_t &source = m_input[inputnum].m_source->m_stream->device(); - util::stream_format(str, "%s '%s'", source.name(), source.tag()); - - // get the sound interface; if there is more than 1 output we need to figure out which one - device_sound_interface *sound; - if (source.interface(sound) && sound->outputs() > 1) - { - // iterate over outputs until we find the stream that matches our source - // then look for a match on the output number - sound_stream *outstream; - int streamoutputnum; - for (int outputnum = 0; (outstream = sound->output_to_stream_output(outputnum, streamoutputnum)) != nullptr; outputnum++) - if (outstream == m_input[inputnum].m_source->m_stream && m_input[inputnum].m_source == &outstream->m_output[streamoutputnum]) - { - util::stream_format(str, " Ch.%d", outputnum); - break; - } +bool sound_stream::set_route_gain(sound_stream *source, int source_channel, int target_channel, float gain) +{ + for(auto &r : m_bw_routes) + if(r.m_source == source && r.m_output == source_channel && r.m_input == target_channel) { + r.m_gain = gain; + return true; } - } - return str.str(); + return false; } +std::vector<sound_stream *> sound_stream::sources() const +{ + std::vector<sound_stream *> streams; + for(const route_bw &route : m_bw_routes) { + sound_stream *stream = route.m_source; + for(const sound_stream *s : streams) + if(s == stream) + goto already; + streams.push_back(stream); + already:; + } + return streams; +} -//------------------------------------------------- -// input_source_device - return the device -// attached as a given input's source -//------------------------------------------------- +std::vector<sound_stream *> sound_stream::targets() const +{ + std::vector<sound_stream *> streams; + for(const route_fw &route : m_fw_routes) { + sound_stream *stream = route.m_target; + for(const sound_stream *s : streams) + if(s == stream) + goto already; + streams.push_back(stream); + already:; + } + return streams; +} -device_t *sound_stream::input_source_device(int inputnum) const +void sound_stream::register_state() { - assert(inputnum >= 0 && inputnum < m_input.size()); - return (m_input[inputnum].m_source != nullptr) ? &m_input[inputnum].m_source->m_stream->device() : nullptr; + // create a unique tag for saving + m_state_tag = string_format("%d", m_device.machine().sound().unique_id()); + auto &save = m_device.machine().save(); + + save.save_item(&m_device, "stream.sound_stream", m_state_tag.c_str(), 0, NAME(m_sync_time)); + save.save_item(&m_device, "stream.sound_stream", m_state_tag.c_str(), 0, NAME(m_sample_rate)); + if(m_input_count) + save.save_item(&m_device, "stream.sound_stream", m_state_tag.c_str(), 0, NAME(m_input_channel_gain)); + if(m_output_count) + save.save_item(&m_device, "stream.sound_stream", m_state_tag.c_str(), 0, NAME(m_output_channel_gain)); + // user gains go to .cfg files, not state files + + m_output_buffer.register_save_state(m_device, "stream.sound_stream.output_buffer", m_state_tag.c_str()); + + for(unsigned int i=0; i != m_bw_routes.size(); i++) + save.save_item(&m_device, "stream.sound_stream", m_state_tag.c_str(), i, m_bw_routes[i].m_gain, "route_gain"); } -//------------------------------------------------- -// input_source_device - return the output number -// attached as a given input's source -//------------------------------------------------- +void sound_stream::compute_dependants() +{ + m_dependant_streams.clear(); + for(const route_bw &r : m_bw_routes) + r.m_source->add_dependants(m_dependant_streams); +} -int sound_stream::input_source_outputnum(int inputnum) const +void sound_stream::add_dependants(std::vector<sound_stream *> &deps) { - assert(inputnum >= 0 && inputnum < m_input.size()); - return (m_input[inputnum].m_source != nullptr) ? (m_input[inputnum].m_source - &m_input[inputnum].m_source->m_stream->m_output[0]) : -1; + for(const route_bw &r : m_bw_routes) + r.m_source->add_dependants(deps); + for(sound_stream *dep : deps) + if(dep == this) + return; + deps.push_back(this); } -//------------------------------------------------- -// set_input - configure a stream's input -//------------------------------------------------- +//**// Stream sample rate -void sound_stream::set_input(int index, sound_stream *input_stream, int output_index, float gain) +void sound_stream::set_sample_rate(u32 new_rate) { - VPRINTF(("stream_set_input(%p, '%s', %d, %p, %d, %f)\n", (void *)this, m_device.tag(), - index, (void *)input_stream, output_index, (double) gain)); + m_input_adaptive = m_output_adaptive = false; + internal_set_sample_rate(new_rate); +} - // make sure it's a valid input - if (index >= m_input.size()) - fatalerror("stream_set_input attempted to configure nonexistent input %d (%d max)\n", index, int(m_input.size())); +void sound_stream::internal_set_sample_rate(u32 new_rate) +{ + if(m_started) { + update(); + m_output_buffer.resample(m_sample_rate, new_rate, m_sync_time, m_device.machine().time()); + m_sample_rate = new_rate; + for(const route_fw &r : m_fw_routes) + r.m_target->create_resamplers(); + create_resamplers(); + lookup_history_sizes(); + + } else + m_sample_rate = new_rate; +} - // make sure it's a valid output - if (input_stream != nullptr && output_index >= input_stream->m_output.size()) - fatalerror("stream_set_input attempted to use a nonexistent output %d (%d max)\n", output_index, int(m_output.size())); +bool sound_stream::try_solving_frequency() +{ + if(frequency_is_solved()) + return false; + + if(input_adaptive() && !output_adaptive()) { + u32 freq = 0; + for(const route_bw &r : m_bw_routes) { + if(!r.m_source->frequency_is_solved()) + return false; + if(freq < r.m_source->sample_rate()) + freq = r.m_source->sample_rate(); + } + m_sample_rate = freq; + return true; + + } else if(output_adaptive() && !input_adaptive()) { + u32 freq = 0; + for(const route_fw &r : m_fw_routes) { + if(!r.m_target->frequency_is_solved()) + return false; + if(freq < r.m_target->sample_rate()) + freq = r.m_target->sample_rate(); + } + m_sample_rate = freq; + return true; + + } else { + u32 freqbw = 0; + for(const route_bw &r : m_bw_routes) { + if(!r.m_source->frequency_is_solved()) { + freqbw = 0; + break; + } + if(freqbw < r.m_source->sample_rate()) + freqbw = r.m_source->sample_rate(); + } + u32 freqfw = 0; + for(const route_fw &r : m_fw_routes) { + if(!r.m_target->frequency_is_solved()) { + freqfw = 0; + break; + } + if(freqfw < r.m_target->sample_rate()) + freqfw = r.m_target->sample_rate(); + } + if(!freqbw && !freqfw) + return false; - // if this input is already wired, update the dependent info - stream_input &input = m_input[index]; - if (input.m_source != nullptr) - input.m_source->m_dependents--; + m_sample_rate = freqfw > freqbw ? freqfw : freqbw; + return true; + } +} - // wire it up - input.m_source = (input_stream != nullptr) ? &input_stream->m_output[output_index] : nullptr; - input.m_gain = int(0x100 * gain); - input.m_user_gain = 0x100; - // update the dependent info - if (input.m_source != nullptr) - input.m_source->m_dependents++; +//**// Stream flow and updates - // update sample rates now that we know the input - recompute_sample_rate_data(); +void sound_stream::init() +{ + // Ensure the buffer size is non-zero, since a stream can be started at any time + u32 bsize = m_sample_rate ? m_sample_rate : 48000; + m_input_buffer.resize(m_input_count); + for(auto &b : m_input_buffer) + b.resize(bsize); + + m_output_buffer.set_buffer_size(bsize); + + m_samples_to_update = 0; + m_started = true; + if(synchronous()) + reprime_sync_timer(); } - -//------------------------------------------------- -// update - force a stream to update to -// the current emulated time -//------------------------------------------------- +u64 sound_stream::get_current_sample_index() const +{ + attotime now = m_device.machine().time(); + return now.m_seconds * m_sample_rate + ((now.m_attoseconds / 1'000'000'000) * m_sample_rate) / 1'000'000'000; +} void sound_stream::update() { - if (!m_attoseconds_per_sample) + if(!is_active() || m_in_update) return; - // determine the number of samples since the start of this second - attotime time = m_device.machine().time(); - s32 update_sampindex = s32(time.attoseconds() / m_attoseconds_per_sample); + // Find out where we are and how much we have to do + u64 idx = get_current_sample_index(); + m_samples_to_update = idx - m_output_buffer.write_sample(); - // if we're ahead of the last update, then adjust upwards - attotime last_update = m_device.machine().sound().last_update(); - if (time.seconds() > last_update.seconds()) - { - assert(time.seconds() == last_update.seconds() + 1); - update_sampindex += m_sample_rate; - } + if(m_samples_to_update > 0) { + m_in_update = true; - // if we're behind the last update, then adjust downwards - if (time.seconds() < last_update.seconds()) - { - assert(time.seconds() == last_update.seconds() - 1); - update_sampindex -= m_sample_rate; + // If there's anything to do, well, do it, starting with the dependencies + for(auto &stream : m_dependant_streams) + stream->update_nodeps(); + + do_update(); + m_in_update = false; } + m_samples_to_update = 0; +} - if (update_sampindex <= m_output_sampindex) +void sound_stream::update_nodeps() +{ + if(!is_active() || m_in_update) return; - // generate samples to get us up to the appropriate time - g_profiler.start(PROFILER_SOUND); - assert(m_output_sampindex - m_output_base_sampindex >= 0); - assert(update_sampindex - m_output_base_sampindex <= m_output_bufalloc); - generate_samples(update_sampindex - m_output_sampindex); - g_profiler.stop(); + // Find out where we are and how much we have to do + u64 idx = get_current_sample_index(); + m_samples_to_update = idx - m_output_buffer.write_sample(); - // remember this info for next time - m_output_sampindex = update_sampindex; -} + if(m_samples_to_update > 0) { + m_in_update = true; + // If there's anything to do, well, do it + do_update(); + m_in_update = false; + } + m_samples_to_update = 0; +} -void sound_stream::sync_update(void *, s32) +void sound_stream::create_resamplers() { - update(); - attotime time = m_device.machine().time(); - attoseconds_t next_edge = m_attoseconds_per_sample - (time.attoseconds() % m_attoseconds_per_sample); - m_sync_timer->adjust(attotime(0, next_edge)); + if(!is_active()) { + for(auto &r : m_bw_routes) + r.m_resampler = nullptr; + return; + } + + for(auto &r : m_bw_routes) + if(r.m_source->is_active() && r.m_source->sample_rate() != m_sample_rate) + r.m_resampler = m_device.machine().sound().get_resampler(r.m_source->sample_rate(), m_sample_rate); + else + r.m_resampler = nullptr; } +void sound_stream::lookup_history_sizes() +{ + u32 history = 0; + for(auto &r : m_fw_routes) { + u32 h = r.m_target->get_history_for_bw_route(this, r.m_output); + if(h > history) + history = h; + } -//------------------------------------------------- -// output_since_last_update - return a pointer to -// the output buffer and the number of samples -// since the last global update -//------------------------------------------------- + m_output_buffer.set_history(history); +} -const stream_sample_t *sound_stream::output_since_last_update(int outputnum, int &numsamples) +u32 sound_stream::get_history_for_bw_route(const sound_stream *source, u32 channel) const { - // force an update on the stream - update(); - - // compute the number of samples and a pointer to the output buffer - numsamples = m_output_sampindex - m_output_update_sampindex; - return &m_output[outputnum].m_buffer[m_output_update_sampindex - m_output_base_sampindex]; + u32 history = 0; + for(auto &r : m_bw_routes) + if(r.m_source == source && r.m_output == channel && r.m_resampler) { + u32 h = r.m_resampler->history_size(); + if(h > history) + history = h; + } + return history; } +void sound_stream::do_update() +{ + // Mix in all the inputs (if any) + if(m_input_count) { + for(auto &b : m_input_buffer) + std::fill(b.begin(), b.begin() + m_samples_to_update, 0.0); + for(const auto &r : m_bw_routes) { + if(!r.m_source->is_active()) + continue; + + float gain = r.m_source->m_user_output_gain * r.m_source->m_output_channel_gain[r.m_output] * r.m_source->m_user_output_channel_gain[r.m_output] * r.m_gain * m_input_channel_gain[r.m_input]; + auto &db = m_input_buffer[r.m_input]; + if(r.m_resampler) + r.m_resampler->apply(r.m_source->m_output_buffer, db, m_output_buffer.write_sample(), r.m_output, gain, m_samples_to_update); + + else { + const sample_t *sb = r.m_source->m_output_buffer.ptrs(r.m_output, m_output_buffer.write_sample() - r.m_source->m_output_buffer.sync_sample()); + for(u32 i = 0; i != m_samples_to_update; i++) + db[i] += sb[i] * gain; + } + } + } + + // Prepare the output space (if any) + m_output_buffer.prepare_space(m_samples_to_update); -//------------------------------------------------- -// set_sample_rate - set the sample rate on a -// given stream -//------------------------------------------------- + // Call the callback + m_callback(*this); -void sound_stream::set_sample_rate(int new_rate) + // Update the indexes + m_output_buffer.commit(m_samples_to_update); +} + +void sound_stream::sync(attotime now) { - // we will update this on the next global update - if (new_rate != sample_rate()) - m_new_sample_rate = new_rate; + m_sync_time = now; + m_output_buffer.sync(); } -//------------------------------------------------- -// set_user_gain - set the user-controllable gain -// on a given stream's input -//------------------------------------------------- -void sound_stream::set_user_gain(int inputnum, float gain) + +attotime sound_stream::sample_to_time(u64 index) const { - update(); - assert(inputnum >= 0 && inputnum < m_input.size()); - m_input[inputnum].m_user_gain = int(0x100 * gain); + attotime res = attotime::zero; + res.m_seconds = index / m_sample_rate; + u64 remain = index % m_sample_rate; + res.m_attoseconds = ((remain * 1'000'000'000) / m_sample_rate) * 1'000'000'000; + return res; } -//------------------------------------------------- -// set_input_gain - set the input gain on a -// given stream's input -//------------------------------------------------- +//**// Synchronous stream updating -void sound_stream::set_input_gain(int inputnum, float gain) +void sound_stream::reprime_sync_timer() +{ + if(!is_active()) + return; + + u64 next_sample = m_output_buffer.write_sample() + 1; + attotime next_time = sample_to_time(next_sample); + next_time.m_attoseconds += 1'000'000'000; // Go to the next nanosecond ' + m_sync_timer->adjust(next_time - m_device.machine().time()); +} + +void sound_stream::sync_update(s32) { update(); - assert(inputnum >= 0 && inputnum < m_input.size()); - m_input[inputnum].m_gain = int(0x100 * gain); + reprime_sync_timer(); } -//------------------------------------------------- -// set_output_gain - set the output gain on a -// given stream's output -//------------------------------------------------- +//**// Sound manager and stream allocation +sound_manager::sound_manager(running_machine &machine) : + m_machine(machine), + m_update_timer(nullptr), + m_last_sync_time(attotime::zero), + m_effects_thread(nullptr), + m_effects_done(false), + m_master_gain(1.0), + m_muted(0), + m_nosound_mode(machine.osd().no_sound()), + m_unique_id(0), + m_wavfile(), + m_resampler_type(RESAMPLER_LOFI), + m_resampler_hq_latency(0.005), + m_resampler_hq_length(400), + m_resampler_hq_phases(200) +{ + // register callbacks + machine.configuration().config_register( + "mixer", + configuration_manager::load_delegate(&sound_manager::config_load, this), + configuration_manager::save_delegate(&sound_manager::config_save, this)); + machine.add_notifier(MACHINE_NOTIFY_PAUSE, machine_notify_delegate(&sound_manager::pause, this)); + machine.add_notifier(MACHINE_NOTIFY_RESUME, machine_notify_delegate(&sound_manager::resume, this)); + machine.add_notifier(MACHINE_NOTIFY_RESET, machine_notify_delegate(&sound_manager::reset, this)); + machine.add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(&sound_manager::stop_recording, this)); + + // register global states + machine.save().save_item(NAME(m_last_sync_time)); + + // start the periodic update flushing timer + m_update_timer = machine.scheduler().timer_alloc(timer_expired_delegate(FUNC(sound_manager::update), this)); + m_update_timer->adjust(STREAMS_UPDATE_ATTOTIME, 0, STREAMS_UPDATE_ATTOTIME); + + // mark the generation as "just starting" + m_osd_info.m_generation = 0xffffffff; +} -void sound_stream::set_output_gain(int outputnum, float gain) +sound_manager::~sound_manager() { - update(); - assert(outputnum >= 0 && outputnum < m_output.size()); - m_output[outputnum].m_gain = int(0x100 * gain); + if(m_effects_thread) { + m_effects_done = true; + m_effects_condition.notify_all(); + m_effects_thread->join(); + m_effects_thread = nullptr; + } +} + +sound_stream *sound_manager::stream_alloc(device_t &device, u32 inputs, u32 outputs, u32 sample_rate, stream_update_delegate callback, sound_stream_flags flags) +{ + m_stream_list.push_back(std::make_unique<sound_stream>(device, inputs, outputs, sample_rate, callback, flags)); + return m_stream_list.back().get(); } -//------------------------------------------------- -// update_with_accounting - do a regular update, -// but also do periodic accounting -//------------------------------------------------- +//**// Sound system initialization -void sound_stream::update_with_accounting(bool second_tick) +void sound_manager::before_devices_init() { - // do the normal update - update(); + // Inform the targets of the existence of the routes + for(device_sound_interface &sound : sound_interface_enumerator(machine().root_device())) + sound.sound_before_devices_init(); - // if we've ticked over another second, adjust all the counters that are relative to - // the current second - s32 output_bufindex = m_output_sampindex - m_output_base_sampindex; - if (second_tick) - { - m_output_sampindex -= m_sample_rate; - m_output_base_sampindex -= m_sample_rate; + m_machine.save().register_postload(save_prepost_delegate(FUNC(sound_manager::postload), this)); +} + +void sound_manager::postload() +{ + std::unique_lock<std::mutex> lock(m_effects_mutex); + attotime now = machine().time(); + for(osd_output_stream &stream : m_osd_output_streams) { + stream.m_last_sync = rate_and_time_to_index(now, stream.m_rate); + stream.m_samples = 0; } +} - // note our current output sample - m_output_update_sampindex = m_output_sampindex; +void sound_manager::after_devices_init() +{ + // Link all the streams together + for(device_sound_interface &sound : sound_interface_enumerator(machine().root_device())) + sound.sound_after_devices_init(); + + // Resolve the frequencies + int need_to_solve = 0; + for(auto &stream : m_stream_list) + if(!stream->frequency_is_solved()) + need_to_solve ++; + + while(need_to_solve) { + int prev_need_to_solve = need_to_solve; + for(auto &stream : m_stream_list) + if(!stream->frequency_is_solved() && stream->try_solving_frequency()) + need_to_solve --; + if(need_to_solve == prev_need_to_solve) + break; + } - // if we don't have enough output buffer space to hold two updates' worth of samples, - // we need to shuffle things down - if (m_output_bufalloc - output_bufindex < 2 * m_max_samples_per_update) - { - s32 samples_to_lose = output_bufindex - m_max_samples_per_update; - if (samples_to_lose > 0) - { - // if we have samples to move, do so for each output - if (output_bufindex > 0) - for (auto & output : m_output) - { - memmove(&output.m_buffer[0], &output.m_buffer[samples_to_lose], sizeof(output.m_buffer[0]) * (output_bufindex - samples_to_lose)); - } + if(need_to_solve) { + u32 def = machine().sample_rate(); + for(auto &stream : m_stream_list) + if(!stream->frequency_is_solved()) + stream->internal_set_sample_rate(def); + } - // update the base position - m_output_base_sampindex += samples_to_lose; + // Have all streams create their buffers and other initializations + for(auto &stream : m_stream_list) + stream->init(); + + // Detect loops and order streams for full update at the same time + // Check the number of sources for each stream + std::map<sound_stream *, int> depcounts; + for(auto &stream : m_stream_list) + depcounts[stream.get()] = stream->sources().size(); + + // Start from all the ones that don't depend on anything + std::vector<sound_stream *> ready_streams; + for(auto &dpc : depcounts) + if(dpc.second == 0) + ready_streams.push_back(dpc.first); + + // Handle all the ready streams in a lifo matter (better for cache when generating sound) + while(!ready_streams.empty()) { + sound_stream *stream = ready_streams.back(); + // add the stream to the update order + m_ordered_streams.push_back(stream); + ready_streams.resize(ready_streams.size() - 1); + // reduce the depcount for all the streams that depend on the updated stream + for(sound_stream *target : stream->targets()) + if(!--depcounts[target]) + // when the depcount is zero, a stream is ready to be updated + ready_streams.push_back(target); + } + + // If not all streams ended up in the sorted list, we have a loop + if(m_ordered_streams.size() != m_stream_list.size()) { + // Apply the same algorithm from the other side to the + // remaining streams to only keep the ones in the loop + + std::map<sound_stream *, int> inverted_depcounts; + for(auto &dpc : depcounts) + if(dpc.second) + inverted_depcounts[dpc.first] = dpc.first->targets().size(); + for(auto &dpc : inverted_depcounts) + if(dpc.second == 0) + ready_streams.push_back(dpc.first); + while(!ready_streams.empty()) { + sound_stream *stream = ready_streams.back(); + ready_streams.resize(ready_streams.size() - 1); + for(sound_stream *source : stream->sources()) + if(!--inverted_depcounts[source]) + ready_streams.push_back(source); } + std::string stream_names; + for(auto &dpc : inverted_depcounts) + if(dpc.second) + stream_names += ' ' + dpc.first->name(); + fatalerror("Loop detected in stream routes:%s", stream_names); } -} + if(VERBOSE & LOG_ORDER) { + LOG_OUTPUT_FUNC("Order:\n"); + for(sound_stream *s : m_ordered_streams) + LOG_OUTPUT_FUNC("- %s (%d)\n", s->name().c_str(), s->sample_rate()); + } -//------------------------------------------------- -// apply_sample_rate_changes - if there is a -// pending sample rate change, apply it now -//------------------------------------------------- + // Registrations for state saving + for(auto &stream : m_stream_list) + stream->register_state(); + + // Compute all the per-stream orders for update() + for(auto &stream : m_stream_list) + stream->compute_dependants(); + + // Create the default effect chain + for(u32 effect = 0; effect != audio_effect::COUNT; effect++) + m_default_effects.emplace_back(audio_effect::create(effect, machine().sample_rate(), nullptr)); + + // Inventory speakers and microphones + m_outputs_count = 0; + for(speaker_device &dev : speaker_device_enumerator(machine().root_device())) { + dev.set_id(m_speakers.size()); + m_speakers.emplace_back(speaker_info(dev, machine().sample_rate(), m_outputs_count)); + for(u32 effect = 0; effect != audio_effect::COUNT; effect++) + m_speakers.back().m_effects[effect].m_effect.reset(audio_effect::create(effect, machine().sample_rate(), m_default_effects[effect].get())); + m_outputs_count += dev.inputs(); + } -void sound_stream::apply_sample_rate_changes() -{ - // skip if nothing to do - if (m_new_sample_rate == 0xffffffff) - return; + for(microphone_device &dev : microphone_device_enumerator(machine().root_device())) { + dev.set_id(m_microphones.size()); + m_microphones.emplace_back(microphone_info(dev)); + } - // update to the new rate and remember the old rate - u32 old_rate = m_sample_rate; - m_sample_rate = m_new_sample_rate; - m_new_sample_rate = 0xffffffff; + // Allocate the buffer to pass for recording + m_record_buffer.resize(m_outputs_count * machine().sample_rate(), 0); + m_record_samples = 0; - // recompute all the data - recompute_sample_rate_data(); + // Have all streams create their initial resamplers + for(auto &stream : m_stream_list) + stream->create_resamplers(); - // reset our sample indexes to the current time - if (old_rate) - { - m_output_sampindex = s64(m_output_sampindex) * s64(m_sample_rate) / old_rate; - m_output_update_sampindex = s64(m_output_update_sampindex) * s64(m_sample_rate) / old_rate; - } - else - { - m_output_sampindex = m_attoseconds_per_sample ? m_device.machine().sound().last_update().attoseconds() / m_attoseconds_per_sample : 0; - m_output_update_sampindex = m_output_sampindex; - } + // Then get the initial history sizes + for(auto &stream : m_stream_list) + stream->lookup_history_sizes(); - m_output_base_sampindex = m_output_sampindex - m_max_samples_per_update; + m_effects_done = false; - // clear out the buffer - if (m_max_samples_per_update) - for (auto & elem : m_output) - memset(&elem.m_buffer[0], 0, m_max_samples_per_update * sizeof(elem.m_buffer[0])); + m_effects_thread = std::make_unique<std::thread>( + [this]{ run_effects(); }); } -//------------------------------------------------- -// recompute_sample_rate_data - recompute sample -// rate data, and all streams that are affected -// by this stream -//------------------------------------------------- +//**// Effects, input and output management -void sound_stream::recompute_sample_rate_data() +void sound_manager::input_get(int id, sound_stream &stream) { - if (m_synchronous) - { - m_sample_rate = 0; - // When synchronous, pick the sample rate for the inputs, if any - for (auto & input : m_input) - { - if (input.m_source != nullptr) - { - if (!m_sample_rate) - m_sample_rate = input.m_source->m_stream->m_sample_rate; - else if (m_sample_rate != input.m_source->m_stream->m_sample_rate) - throw emu_fatalerror("Incompatible sample rates as input of a synchronous stream: %d and %d\n", m_sample_rate, input.m_source->m_stream->m_sample_rate); + u32 samples = stream.samples(); + u64 end_pos = stream.sample_index(); + u32 skip = stream.output_count(); + + for(const auto &step : m_microphones[id].m_input_mixing_steps) { + auto get_source = [&istream = m_osd_input_streams[step.m_osd_index], this](u32 samples, u64 end_pos, u32 channel) -> const s16 * { + if(istream.m_buffer.write_sample() < end_pos) { + u32 needed = end_pos - istream.m_buffer.write_sample(); + istream.m_buffer.prepare_space(needed); + machine().osd().sound_stream_source_update(istream.m_id, istream.m_buffer.ptrw(0, 0), needed); + istream.m_buffer.commit(needed); + } + return istream.m_buffer.ptrs(channel, end_pos - samples - istream.m_buffer.sync_sample()); + }; + + switch(step.m_mode) { + case mixing_step::CLEAR: + case mixing_step::COPY: + fatalerror("Impossible step encountered in input\n"); + + case mixing_step::ADD: { + const s16 *src = get_source(samples, end_pos, step.m_osd_channel); + float gain = step.m_linear_volume / 32768.0; + for(u32 sample = 0; sample != samples; sample++) { + stream.add(step.m_device_channel, sample, *src * gain); + src += skip; } + break; + } } } +} +void sound_manager::output_push(int id, sound_stream &stream) +{ + auto &spk = m_speakers[id]; + auto &out = spk.m_buffer; + auto &inp = stream.m_input_buffer; + int samples = stream.samples(); + int channels = stream.input_count(); + out.prepare_space(samples); + for(int channel = 0; channel != channels; channel ++) + std::copy(inp[channel].begin(), inp[channel].begin() + samples, out.ptrw(channel, 0)); + out.commit(samples); + + m_record_samples = samples; + s16 *outb = m_record_buffer.data() + spk.m_first_output; + for(int channel = 0; channel != channels; channel ++) { + s16 *outb1 = outb; + const float *inb = inp[channel].data(); + for(int sample = 0; sample != samples; sample++) { + *outb1 = std::clamp(int(*inb++ * 32768), -32768, 32767); + outb1 += m_outputs_count; + } + } +} - // recompute the timing parameters - attoseconds_t update_attoseconds = m_device.machine().sound().update_attoseconds(); +void sound_manager::run_effects() +{ + std::unique_lock<std::mutex> lock(m_effects_mutex); + for(;;) { + m_effects_condition.wait(lock); + if(m_effects_done) + return; + + // Apply the effects + for(auto &si : m_speakers) + for(u32 i=0; i != si.m_effects.size(); i++) { + auto &source = i ? si.m_effects[i-1].m_buffer : si.m_buffer; + si.m_effects[i].m_effect->apply(source, si.m_effects[i].m_buffer); + source.sync(); + } - if (m_sample_rate) - { - m_attoseconds_per_sample = ATTOSECONDS_PER_SECOND / m_sample_rate; - m_max_samples_per_update = (update_attoseconds + m_attoseconds_per_sample - 1) / m_attoseconds_per_sample; - } - else - { - m_attoseconds_per_sample = 0; - m_max_samples_per_update = 0; - } + // Apply the mixing steps + for(const auto &step : m_output_mixing_steps) { + const sample_t *src = step.m_mode == mixing_step::CLEAR ? nullptr : m_speakers[step.m_device_index].m_effects.back().m_buffer.ptrs(step.m_device_channel, 0); - // update resample and output buffer sizes - allocate_resample_buffers(); - allocate_output_buffers(); + auto &ostream = m_osd_output_streams[step.m_osd_index]; + u32 samples = ostream.m_samples; + s16 *dest = ostream.m_buffer.data() + step.m_osd_channel; + u32 skip = ostream.m_channels; - // iterate over each input - for (auto & input : m_input) - { - // if we have a source, see if its sample rate changed - - if (input.m_source != nullptr && input.m_source->m_stream->m_sample_rate) - { - // okay, we have a new sample rate; recompute the latency to be the maximum - // sample period between us and our input - attoseconds_t new_attosecs_per_sample = ATTOSECONDS_PER_SECOND / input.m_source->m_stream->m_sample_rate; - attoseconds_t latency = std::max(new_attosecs_per_sample, m_attoseconds_per_sample); - - // if the input stream's sample rate is lower, we will use linear interpolation - // this requires an extra sample from the source - if (input.m_source->m_stream->m_sample_rate < m_sample_rate) - latency += new_attosecs_per_sample; - - // if our sample rates match exactly, we don't need any latency - else if (input.m_source->m_stream->m_sample_rate == m_sample_rate) - latency = 0; - - // we generally don't want to tweak the latency, so we just keep the greatest - // one we've computed thus far - input.m_latency_attoseconds = std::max(input.m_latency_attoseconds, latency); - assert(input.m_latency_attoseconds < update_attoseconds); - } - else - { - input.m_latency_attoseconds = 0; - } - } + switch(step.m_mode) { + case mixing_step::CLEAR: + for(u32 sample = 0; sample != samples; sample++) { + *dest = 0; + dest += skip; + } + break; - // If synchronous, prime the timer - if (m_synchronous) - { - attotime time = m_device.machine().time(); - if (m_attoseconds_per_sample) - { - attoseconds_t next_edge = m_attoseconds_per_sample - (time.attoseconds() % m_attoseconds_per_sample); - m_sync_timer->adjust(attotime(0, next_edge)); + case mixing_step::COPY: { + float gain = 32768 * step.m_linear_volume * m_master_gain; + for(u32 sample = 0; sample != samples; sample++) { + *dest = std::clamp(int(*src++ * gain), -32768, 32767); + dest += skip; + } + break; + } + + case mixing_step::ADD: { + float gain = 32768 * step.m_linear_volume * m_master_gain; + for(u32 sample = 0; sample != samples; sample++) { + *dest = std::clamp(int(*src++ * gain) + *dest, -32768, 32767); + dest += skip; + } + break; + } + } } - else - m_sync_timer->adjust(attotime::never); + + for(auto &si : m_speakers) + si.m_effects.back().m_buffer.sync(); + + // Send the result to the osd + for(auto &stream : m_osd_output_streams) + if(stream.m_samples) + machine().osd().sound_stream_sink_update(stream.m_id, stream.m_buffer.data(), stream.m_samples); } } +std::string sound_manager::effect_chain_tag(s32 index) const +{ + return m_speakers[index].m_dev.tag(); +} -//------------------------------------------------- -// allocate_resample_buffers - recompute the -// resample buffer sizes and expand if necessary -//------------------------------------------------- +std::vector<audio_effect *> sound_manager::effect_chain(s32 index) const +{ + std::vector<audio_effect *> res; + for(const auto &e : m_speakers[index].m_effects) + res.push_back(e.m_effect.get()); + return res; +} -void sound_stream::allocate_resample_buffers() +std::vector<audio_effect *> sound_manager::default_effect_chain() const { - // compute the target number of samples - s32 bufsize = 2 * m_max_samples_per_update; + std::vector<audio_effect *> res; + for(const auto &e : m_default_effects) + res.push_back(e.get()); + return res; +} - // if we don't have enough room, allocate more - if (m_resample_bufalloc < bufsize) - { - // this becomes the new allocation size - m_resample_bufalloc = bufsize; - - // iterate over outputs and realloc their buffers - for (auto & elem : m_input) { - unsigned int old_size = elem.m_resample.size(); - elem.m_resample.resize(m_resample_bufalloc); - memset(&elem.m_resample[old_size], 0, (m_resample_bufalloc - old_size)*sizeof(elem.m_resample[0])); - } - } +void sound_manager::default_effect_changed(u32 entry) +{ + u32 type = m_default_effects[entry]->type(); + for(const auto &s : m_speakers) + for(const auto &e : s.m_effects) + if(e.m_effect->type() == type) + e.m_effect->default_changed(); } + + //------------------------------------------------- -// allocate_output_buffers - recompute the -// output buffer sizes and expand if necessary +// start_recording - begin audio recording //------------------------------------------------- -void sound_stream::allocate_output_buffers() +bool sound_manager::start_recording(std::string_view filename) { - // if we don't have enough room, allocate more - s32 bufsize = OUTPUT_BUFFER_UPDATES * m_max_samples_per_update; - if (m_output_bufalloc < bufsize) - { - // this becomes the new allocation size - m_output_bufalloc = bufsize; - - // iterate over outputs and realloc their buffers - for (auto & elem : m_output) { - unsigned int old_size = elem.m_buffer.size(); - elem.m_buffer.resize(m_output_bufalloc); - memset(&elem.m_buffer[old_size], 0, (m_output_bufalloc - old_size)*sizeof(elem.m_buffer[0])); - } - } + if(m_wavfile) + return false; + m_wavfile = util::wav_open(filename, machine().sample_rate(), m_outputs_count); + return bool(m_wavfile); +} + +bool sound_manager::start_recording() +{ + // open the output WAV file if specified + char const *const filename = machine().options().wav_write(); + return *filename ? start_recording(filename) : false; } //------------------------------------------------- -// postload - save/restore callback +// stop_recording - end audio recording //------------------------------------------------- -void sound_stream::postload() +void sound_manager::stop_recording() { - // recompute the same rate information - recompute_sample_rate_data(); - - // make sure our output buffers are fully cleared - for (auto & elem : m_output) - memset(&elem.m_buffer[0], 0, m_output_bufalloc * sizeof(elem.m_buffer[0])); - - // recompute the sample indexes to make sense - m_output_sampindex = m_attoseconds_per_sample ? m_device.machine().sound().last_update().attoseconds() / m_attoseconds_per_sample : 0; - m_output_update_sampindex = m_output_sampindex; - m_output_base_sampindex = m_output_sampindex - m_max_samples_per_update; + // close any open WAV file + m_wavfile.reset(); } //------------------------------------------------- -// generate_samples - generate the requested -// number of samples for a stream, making sure -// all inputs have the appropriate number of -// samples generated +// mute - mute sound output //------------------------------------------------- -void sound_stream::generate_samples(int samples) +void sound_manager::mute(bool mute, u8 reason) { - stream_sample_t **inputs = nullptr; - stream_sample_t **outputs = nullptr; + if(mute) + m_muted |= reason; + else + m_muted &= ~reason; +} - VPRINTF(("generate_samples(%p, %d)\n", (void *) this, samples)); - assert(samples > 0); - // ensure all inputs are up to date and generate resampled data - for (unsigned int inputnum = 0; inputnum < m_input.size(); inputnum++) - { - // update the stream to the current time - stream_input &input = m_input[inputnum]; - if (input.m_source != nullptr) - input.m_source->m_stream->update(); +//------------------------------------------------- +// reset - reset all sound chips +//------------------------------------------------- - // generate the resampled data - m_input_array[inputnum] = generate_resampled_data(input, samples); - } +sound_manager::speaker_info::speaker_info(speaker_device &dev, u32 rate, u32 first_output) : m_dev(dev), m_first_output(first_output), m_buffer(rate, dev.inputs()) +{ + m_channels = dev.inputs(); + m_stream = dev.stream(); + for(u32 i=0; i != audio_effect::COUNT; i++) + m_effects.emplace_back(effect_step(rate, dev.inputs())); +} - if (!m_input.empty()) - { - inputs = &m_input_array[0]; - } +sound_manager::microphone_info::microphone_info(microphone_device &dev) : m_dev(dev) +{ + m_channels = dev.outputs(); +} - // loop over all outputs and compute the output pointer - for (unsigned int outputnum = 0; outputnum < m_output.size(); outputnum++) - { - stream_output &output = m_output[outputnum]; - m_output_array[outputnum] = &output.m_buffer[m_output_sampindex - m_output_base_sampindex]; - } +void sound_manager::reset() +{ + LOG_OUTPUT_FUNC("Sound reset\n"); +} - if (!m_output.empty()) - { - outputs = &m_output_array[0]; - } - // run the callback - VPRINTF((" callback(%p, %d)\n", (void *)this, samples)); - m_callback(*this, inputs, outputs, samples); - VPRINTF((" callback done\n")); +//------------------------------------------------- +// pause - pause sound output +//------------------------------------------------- + +void sound_manager::pause() +{ + mute(true, MUTE_REASON_PAUSE); } //------------------------------------------------- -// generate_resampled_data - generate the -// resample buffer for a given input +// resume - resume sound output //------------------------------------------------- -stream_sample_t *sound_stream::generate_resampled_data(stream_input &input, u32 numsamples) +void sound_manager::resume() { - // if we don't have an output to pull data from, generate silence - stream_sample_t *dest = &input.m_resample[0]; - if (input.m_source == nullptr || input.m_source->m_stream->m_attoseconds_per_sample == 0) - { - memset(dest, 0, numsamples * sizeof(*dest)); - return &input.m_resample[0]; - } - - // grab data from the output - stream_output &output = *input.m_source; - sound_stream &input_stream = *output.m_stream; - s64 gain = (input.m_gain * input.m_user_gain * output.m_gain) >> 16; + mute(false, MUTE_REASON_PAUSE); +} - // determine the time at which the current sample begins, accounting for the - // latency we calculated between the input and output streams - attoseconds_t basetime = m_output_sampindex * m_attoseconds_per_sample - input.m_latency_attoseconds; - // now convert that time into a sample in the input stream - s32 basesample; - if (basetime >= 0) - basesample = basetime / input_stream.m_attoseconds_per_sample; - else - basesample = -(-basetime / input_stream.m_attoseconds_per_sample) - 1; +//**// Configuration management - // compute a source pointer to the first sample - assert(basesample >= input_stream.m_output_base_sampindex); - stream_sample_t *source = &output.m_buffer[basesample - input_stream.m_output_base_sampindex]; +void sound_manager::config_load(config_type cfg_type, config_level cfg_level, util::xml::data_node const *parentnode) +{ + // If no config file, ignore + if(!parentnode) + return; - // determine the current fraction of a sample, expressed as a fraction of FRAC_ONE - // (Note: this formula is valid as long as input_stream.m_attoseconds_per_sample significantly exceeds FRAC_ONE > attoseconds = 4.2E-12 s) - u32 basefrac = (basetime - basesample * input_stream.m_attoseconds_per_sample) / ((input_stream.m_attoseconds_per_sample + FRAC_ONE - 1) >> FRAC_BITS); - assert(basefrac < FRAC_ONE); + switch(cfg_type) { + case config_type::INIT: + break; - // compute the stepping fraction - u32 step = (u64(input_stream.m_sample_rate) << FRAC_BITS) / m_sample_rate; + case config_type::CONTROLLER: + break; - // if we have equal sample rates, we just need to copy - if (step == FRAC_ONE) - { - while (numsamples--) - { - // compute the sample - s64 sample = *source++; - *dest++ = (sample * gain) >> 8; - } - } + case config_type::DEFAULT: { + // In the global config, get the default effect chain configuration - // input is undersampled: point sample except where our sample period covers a boundary - else if (step < FRAC_ONE) - { - while (numsamples != 0) - { - // fill in with point samples until we hit a boundary - int nextfrac; - while ((nextfrac = basefrac + step) < FRAC_ONE && numsamples--) - { - *dest++ = (source[0] * gain) >> 8; - basefrac = nextfrac; + util::xml::data_node const *efl_node = parentnode->get_child("default_audio_effects"); + if(efl_node) { + for(util::xml::data_node const *ef_node = efl_node->get_child("effect"); ef_node != nullptr; ef_node = ef_node->get_next_sibling("effect")) { + unsigned int id = ef_node->get_attribute_int("step", 0); + std::string type = ef_node->get_attribute_string("type", ""); + if(id >= 1 && id <= m_default_effects.size() && audio_effect::effect_names[m_default_effects[id-1]->type()] == type) + m_default_effects[id-1]->config_load(ef_node); } + } - // if we're done, we're done - if (s32(numsamples--) < 0) - break; + // and the resampler configuration + util::xml::data_node const *rs_node = parentnode->get_child("resampler"); + if(rs_node) { + m_resampler_type = rs_node->get_attribute_int("type", RESAMPLER_LOFI); + m_resampler_hq_latency = rs_node->get_attribute_float("hq_latency", 0.0050); + m_resampler_hq_length = rs_node->get_attribute_int("hq_length", 400); + m_resampler_hq_phases = rs_node->get_attribute_int("hq_phases", 200); + } + break; + } - // compute starting and ending fractional positions - int startfrac = basefrac >> (FRAC_BITS - 12); - int endfrac = nextfrac >> (FRAC_BITS - 12); + case config_type::SYSTEM: { + // In the per-driver file, get the specific configuration for everything + + // Effects configuration + for(util::xml::data_node const *efl_node = parentnode->get_child("audio_effects"); efl_node != nullptr; efl_node = efl_node->get_next_sibling("audio_effects")) { + std::string speaker_tag = efl_node->get_attribute_string("tag", ""); + for(auto &speaker : m_speakers) + if(speaker.m_dev.tag() == speaker_tag) { + auto &eff = speaker.m_effects; + for(util::xml::data_node const *ef_node = efl_node->get_child("effect"); ef_node != nullptr; ef_node = ef_node->get_next_sibling("effect")) { + unsigned int id = ef_node->get_attribute_int("step", 0); + std::string type = ef_node->get_attribute_string("type", ""); + if(id >= 1 && id <= m_default_effects.size() && audio_effect::effect_names[eff[id-1].m_effect->type()] == type) + eff[id-1].m_effect->config_load(ef_node); + } + break; + } + } - // blend between the two samples accordingly - s64 sample = (s64(source[0]) * (0x1000 - startfrac) + s64(source[1]) * (endfrac - 0x1000)) / (endfrac - startfrac); - *dest++ = (sample * gain) >> 8; + // All levels + const util::xml::data_node *lv_node = parentnode->get_child("master_volume"); + if(lv_node) + m_master_gain = lv_node->get_attribute_float("gain", 1.0); - // advance - basefrac = nextfrac & FRAC_MASK; - source++; + for(lv_node = parentnode->get_child("device_volume"); lv_node != nullptr; lv_node = lv_node->get_next_sibling("device_volume")) { + std::string device_tag = lv_node->get_attribute_string("device", ""); + device_sound_interface *intf = dynamic_cast<device_sound_interface *>(m_machine.root_device().subdevice(device_tag)); + if(intf) + intf->set_user_output_gain(lv_node->get_attribute_float("gain", 1.0)); } - } - // input is oversampled: sum the energy - else - { - // use 8 bits to allow some extra headroom - int smallstep = step >> (FRAC_BITS - 8); - while (numsamples--) - { - s64 remainder = smallstep; - int tpos = 0; - - // compute the sample - s64 scale = (FRAC_ONE - basefrac) >> (FRAC_BITS - 8); - s64 sample = s64(source[tpos++]) * scale; - remainder -= scale; - while (remainder > 0x100) - { - sample += s64(source[tpos++]) * s64(0x100); - remainder -= 0x100; - } - sample += s64(source[tpos]) * remainder; - sample /= smallstep; + for(lv_node = parentnode->get_child("device_channel_volume"); lv_node != nullptr; lv_node = lv_node->get_next_sibling("device_channel_volume")) { + std::string device_tag = lv_node->get_attribute_string("device", ""); + int channel = lv_node->get_attribute_int("channel", -1); + device_sound_interface *intf = dynamic_cast<device_sound_interface *>(m_machine.root_device().subdevice(device_tag)); + if(intf && channel >= 0 && channel < intf->outputs()) + intf->set_user_output_gain(channel, lv_node->get_attribute_float("gain", 1.0)); + } - *dest++ = (sample * gain) >> 8; - // advance - basefrac += step; - source += basefrac >> FRAC_BITS; - basefrac &= FRAC_MASK; + // Mapping configuration + m_configs.clear(); + for(util::xml::data_node const *node = parentnode->get_child("sound_map"); node != nullptr; node = node->get_next_sibling("sound_map")) { + m_configs.emplace_back(config_mapping { node->get_attribute_string("tag", "") }); + auto &config = m_configs.back(); + for(util::xml::data_node const *nmap = node->get_child("node_mapping"); nmap != nullptr; nmap = nmap->get_next_sibling("node_mapping")) + config.m_node_mappings.emplace_back(std::pair<std::string, float>(nmap->get_attribute_string("node", ""), nmap->get_attribute_float("db", 0))); + for(util::xml::data_node const *cmap = node->get_child("channel_mapping"); cmap != nullptr; cmap = cmap->get_next_sibling("channel_mapping")) + config.m_channel_mappings.emplace_back(std::tuple<u32, std::string, u32, float>(cmap->get_attribute_int("guest_channel", 0), + cmap->get_attribute_string("node", ""), + cmap->get_attribute_int("node_channel", 0), + cmap->get_attribute_float("db", 0))); } + break; } - return &input.m_resample[0]; + case config_type::FINAL: + break; + } } - -//************************************************************************** -// STREAM INPUT -//************************************************************************** - //------------------------------------------------- -// stream_input - constructor +// config_save - save data to the configuration +// file //------------------------------------------------- -sound_stream::stream_input::stream_input() - : m_source(nullptr), - m_latency_attoseconds(0), - m_gain(0x100), - m_user_gain(0x100) +void sound_manager::config_save(config_type cfg_type, util::xml::data_node *parentnode) { -} - + switch(cfg_type) { + case config_type::INIT: + break; + + case config_type::CONTROLLER: + break; + + case config_type::DEFAULT: { + // In the global config, save the default effect chain configuration + util::xml::data_node *const efl_node = parentnode->add_child("default_audio_effects", nullptr); + for(u32 ei = 0; ei != m_default_effects.size(); ei++) { + const audio_effect *e = m_default_effects[ei].get(); + util::xml::data_node *const ef_node = efl_node->add_child("effect", nullptr); + ef_node->set_attribute_int("step", ei+1); + ef_node->set_attribute("type", audio_effect::effect_names[e->type()]); + e->config_save(ef_node); + } + util::xml::data_node *const rs_node = parentnode->add_child("resampler", nullptr); + rs_node->set_attribute_int("type", m_resampler_type); + rs_node->set_attribute_float("hq_latency", m_resampler_hq_latency); + rs_node->set_attribute_int("hq_length", m_resampler_hq_length); + rs_node->set_attribute_int("hq_phases", m_resampler_hq_phases); + break; + } -//************************************************************************** -// STREAM OUTPUT -//************************************************************************** + case config_type::SYSTEM: { + // In the per-driver file, save the specific configuration for everything + + // Effects configuration + for(const auto &speaker : m_speakers) { + util::xml::data_node *const efl_node = parentnode->add_child("audio_effects", nullptr); + efl_node->set_attribute("tag", speaker.m_dev.tag()); + for(u32 ei = 0; ei != speaker.m_effects.size(); ei++) { + const audio_effect *e = speaker.m_effects[ei].m_effect.get(); + util::xml::data_node *const ef_node = efl_node->add_child("effect", nullptr); + ef_node->set_attribute_int("step", ei+1); + ef_node->set_attribute("type", audio_effect::effect_names[e->type()]); + e->config_save(ef_node); + } + } -//------------------------------------------------- -// stream_output - constructor -//------------------------------------------------- + // All levels + if(m_master_gain != 1.0) { + util::xml::data_node *const lv_node = parentnode->add_child("master_volume", nullptr); + lv_node->set_attribute_float("gain", m_master_gain); + } + for(device_sound_interface &snd : sound_interface_enumerator(m_machine.root_device())) { + // Don't add microphones, speakers or devices without outputs + if(dynamic_cast<sound_io_device *>(&snd) || !snd.outputs()) + continue; + if(snd.user_output_gain() != 1.0) { + util::xml::data_node *const lv_node = parentnode->add_child("device_volume", nullptr); + lv_node->set_attribute("device", snd.device().tag()); + lv_node->set_attribute_float("gain", snd.user_output_gain()); + } + for(int channel = 0; channel != snd.outputs(); channel ++) + if(snd.user_output_gain(channel) != 1.0) { + util::xml::data_node *const lv_node = parentnode->add_child("device_channel_volume", nullptr); + lv_node->set_attribute("device", snd.device().tag()); + lv_node->set_attribute_int("channel", channel); + lv_node->set_attribute_float("gain", snd.user_output_gain(channel)); + } + } -sound_stream::stream_output::stream_output() - : m_stream(nullptr), - m_dependents(0), - m_gain(0x100) -{ -} + // Mapping configuration + auto output_one = [this, parentnode](sound_io_device &dev) { + for(const auto &config : m_configs) + if(config.m_name == dev.tag()) { + util::xml::data_node *const sp_node = parentnode->add_child("sound_map", nullptr); + sp_node->set_attribute("tag", dev.tag()); + for(const auto &nmap : config.m_node_mappings) { + util::xml::data_node *const node = sp_node->add_child("node_mapping", nullptr); + node->set_attribute("node", nmap.first.c_str()); + node->set_attribute_float("db", nmap.second); + } + for(const auto &cmap : config.m_channel_mappings) { + util::xml::data_node *const node = sp_node->add_child("channel_mapping", nullptr); + node->set_attribute_int("guest_channel", std::get<0>(cmap)); + node->set_attribute("node", std::get<1>(cmap).c_str()); + node->set_attribute_int("node_channel", std::get<2>(cmap)); + node->set_attribute_float("db", std::get<3>(cmap)); + } + return; + } + }; + for(auto &spk : m_speakers) + output_one(spk.m_dev); + for(auto &mic : m_microphones) + output_one(mic.m_dev); + break; + } + case config_type::FINAL: + break; + } +} -//************************************************************************** -// SOUND MANAGER -//************************************************************************** -//------------------------------------------------- -// sound_manager - constructor -//------------------------------------------------- -sound_manager::sound_manager(running_machine &machine) - : m_machine(machine), - m_update_timer(nullptr), - m_finalmix_leftover(0), - m_finalmix(machine.sample_rate()), - m_leftmix(machine.sample_rate()), - m_rightmix(machine.sample_rate()), - m_samples_this_update(0), - m_muted(0), - m_attenuation(0), - m_nosound_mode(machine.osd().no_sound()), - m_wavfile(nullptr), - m_update_attoseconds(STREAMS_UPDATE_ATTOTIME.attoseconds()), - m_last_update(attotime::zero) -{ - // get filename for WAV file or AVI file if specified - const char *wavfile = machine.options().wav_write(); - const char *avifile = machine.options().avi_write(); - - // handle -nosound and lower sample rate if not recording WAV or AVI - if (m_nosound_mode && wavfile[0] == 0 && avifile[0] == 0) - machine.m_sample_rate = 11025; - - // count the mixers -#if VERBOSE - mixer_interface_iterator iter(machine.root_device()); - VPRINTF(("total mixers = %d\n", iter.count())); -#endif +//**// Mapping between speakers/microphones and OSD endpoints - // register callbacks - machine.configuration().config_register("mixer", config_load_delegate(&sound_manager::config_load, this), config_save_delegate(&sound_manager::config_save, this)); - machine.add_notifier(MACHINE_NOTIFY_PAUSE, machine_notify_delegate(&sound_manager::pause, this)); - machine.add_notifier(MACHINE_NOTIFY_RESUME, machine_notify_delegate(&sound_manager::resume, this)); - machine.add_notifier(MACHINE_NOTIFY_RESET, machine_notify_delegate(&sound_manager::reset, this)); - machine.add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(&sound_manager::stop_recording, this)); +sound_manager::config_mapping &sound_manager::config_get_sound_io(sound_io_device *dev) +{ + for(auto &config : m_configs) + if(config.m_name == dev->tag()) + return config; + m_configs.emplace_back(config_mapping { dev->tag() }); + return m_configs.back(); +} - // register global states - machine.save().save_item(NAME(m_last_update)); +void sound_manager::config_add_sound_io_connection_node(sound_io_device *dev, std::string name, float db) +{ + internal_config_add_sound_io_connection_node(dev, name, db); + m_osd_info.m_generation --; +} - // set the starting attenuation - set_attenuation(machine.options().volume()); +void sound_manager::internal_config_add_sound_io_connection_node(sound_io_device *dev, std::string name, float db) +{ + auto &config = config_get_sound_io(dev); + for(auto &nmap : config.m_node_mappings) + if(nmap.first == name) + return; + config.m_node_mappings.emplace_back(std::pair<std::string, float>(name, db)); +} - // start the periodic update flushing timer - m_update_timer = machine.scheduler().timer_alloc(timer_expired_delegate(FUNC(sound_manager::update), this)); - m_update_timer->adjust(STREAMS_UPDATE_ATTOTIME, 0, STREAMS_UPDATE_ATTOTIME); +void sound_manager::config_add_sound_io_connection_default(sound_io_device *dev, float db) +{ + internal_config_add_sound_io_connection_default(dev, db); + m_osd_info.m_generation --; } +void sound_manager::internal_config_add_sound_io_connection_default(sound_io_device *dev, float db) +{ + auto &config = config_get_sound_io(dev); + for(auto &nmap : config.m_node_mappings) + if(nmap.first == "") + return; + config.m_node_mappings.emplace_back(std::pair<std::string, float>("", db)); +} -//------------------------------------------------- -// sound_manager - destructor -//------------------------------------------------- +void sound_manager::config_remove_sound_io_connection_node(sound_io_device *dev, std::string name) +{ + internal_config_remove_sound_io_connection_node(dev, name); + m_osd_info.m_generation --; +} -sound_manager::~sound_manager() +void sound_manager::internal_config_remove_sound_io_connection_node(sound_io_device *dev, std::string name) { + auto &config = config_get_sound_io(dev); + for(auto i = config.m_node_mappings.begin(); i != config.m_node_mappings.end(); i++) + if(i->first == name) { + config.m_node_mappings.erase(i); + return; + } } +void sound_manager::config_remove_sound_io_connection_default(sound_io_device *dev) +{ + internal_config_remove_sound_io_connection_default(dev); + m_osd_info.m_generation --; +} -//------------------------------------------------- -// start_recording - begin audio recording -//------------------------------------------------- +void sound_manager::internal_config_remove_sound_io_connection_default(sound_io_device *dev) +{ + auto &config = config_get_sound_io(dev); + for(auto i = config.m_node_mappings.begin(); i != config.m_node_mappings.end(); i++) + if(i->first == "") { + config.m_node_mappings.erase(i); + return; + } +} -void sound_manager::start_recording() +void sound_manager::config_set_volume_sound_io_connection_node(sound_io_device *dev, std::string name, float db) { - // open the output WAV file if specified - const char *wavfile = machine().options().wav_write(); - if (wavfile[0] != 0 && m_wavfile == nullptr) - m_wavfile = wav_open(wavfile, machine().sample_rate(), 2); + internal_config_set_volume_sound_io_connection_node(dev, name, db); + m_osd_info.m_generation --; } +void sound_manager::internal_config_set_volume_sound_io_connection_node(sound_io_device *dev, std::string name, float db) +{ + auto &config = config_get_sound_io(dev); + for(auto &nmap : config.m_node_mappings) + if(nmap.first == name) { + nmap.second = db; + return; + } +} -//------------------------------------------------- -// stop_recording - end audio recording -//------------------------------------------------- +void sound_manager::config_set_volume_sound_io_connection_default(sound_io_device *dev, float db) +{ + internal_config_set_volume_sound_io_connection_default(dev, db); + m_osd_info.m_generation --; +} -void sound_manager::stop_recording() +void sound_manager::internal_config_set_volume_sound_io_connection_default(sound_io_device *dev, float db) { - // close any open WAV file - if (m_wavfile != nullptr) - wav_close(m_wavfile); - m_wavfile = nullptr; + auto &config = config_get_sound_io(dev); + for(auto &nmap : config.m_node_mappings) + if(nmap.first == "") { + nmap.second = db; + return; + } } -//------------------------------------------------- -// stream_alloc - allocate a new stream -//------------------------------------------------- +void sound_manager::config_add_sound_io_channel_connection_node(sound_io_device *dev, u32 guest_channel, std::string name, u32 node_channel, float db) +{ + internal_config_add_sound_io_channel_connection_node(dev, guest_channel, name, node_channel, db); + m_osd_info.m_generation --; +} -sound_stream *sound_manager::stream_alloc(device_t &device, int inputs, int outputs, int sample_rate, stream_update_delegate callback) +void sound_manager::internal_config_add_sound_io_channel_connection_node(sound_io_device *dev, u32 guest_channel, std::string name, u32 node_channel, float db) { - m_stream_list.push_back(std::make_unique<sound_stream>(device, inputs, outputs, sample_rate, callback)); - return m_stream_list.back().get(); + auto &config = config_get_sound_io(dev); + for(auto &cmap : config.m_channel_mappings) + if(std::get<0>(cmap) == guest_channel && std::get<1>(cmap) == name && std::get<2>(cmap) == node_channel) + return; + config.m_channel_mappings.emplace_back(std::tuple<u32, std::string, u32, float>(guest_channel, name, node_channel, db)); } +void sound_manager::config_add_sound_io_channel_connection_default(sound_io_device *dev, u32 guest_channel, u32 node_channel, float db) +{ + internal_config_add_sound_io_channel_connection_default(dev, guest_channel, node_channel, db); + m_osd_info.m_generation --; +} -//------------------------------------------------- -// set_attenuation - set the global volume -//------------------------------------------------- +void sound_manager::internal_config_add_sound_io_channel_connection_default(sound_io_device *dev, u32 guest_channel, u32 node_channel, float db) +{ + auto &config = config_get_sound_io(dev); + for(auto &cmap : config.m_channel_mappings) + if(std::get<0>(cmap) == guest_channel && std::get<1>(cmap) == "" && std::get<2>(cmap) == node_channel) + return; + config.m_channel_mappings.emplace_back(std::tuple<u32, std::string, u32, float>(guest_channel, "", node_channel, db)); +} -void sound_manager::set_attenuation(int attenuation) +void sound_manager::config_remove_sound_io_channel_connection_node(sound_io_device *dev, u32 guest_channel, std::string name, u32 node_channel) { - m_attenuation = attenuation; - machine().osd().set_mastervolume(m_muted ? -32 : m_attenuation); + internal_config_remove_sound_io_channel_connection_node(dev, guest_channel, name, node_channel); + m_osd_info.m_generation --; } +void sound_manager::internal_config_remove_sound_io_channel_connection_node(sound_io_device *dev, u32 guest_channel, std::string name, u32 node_channel) +{ + auto &config = config_get_sound_io(dev); + for(auto i = config.m_channel_mappings.begin(); i != config.m_channel_mappings.end(); i++) + if(std::get<0>(*i) == guest_channel && std::get<1>(*i) == name && std::get<2>(*i) == node_channel) { + config.m_channel_mappings.erase(i); + return; + } +} -//------------------------------------------------- -// indexed_mixer_input - return the mixer -// device and input index of the global mixer -// input -//------------------------------------------------- +void sound_manager::config_remove_sound_io_channel_connection_default(sound_io_device *dev, u32 guest_channel, u32 node_channel) +{ + internal_config_remove_sound_io_channel_connection_default(dev, guest_channel, node_channel); + m_osd_info.m_generation --; +} -bool sound_manager::indexed_mixer_input(int index, mixer_input &info) const +void sound_manager::internal_config_remove_sound_io_channel_connection_default(sound_io_device *dev, u32 guest_channel, u32 node_channel) { - // scan through the mixers until we find the indexed input - for (device_mixer_interface &mixer : mixer_interface_iterator(machine().root_device())) - { - if (index < mixer.inputs()) - { - info.mixer = &mixer; - info.stream = mixer.input_to_stream_input(index, info.inputnum); - assert(info.stream != nullptr); - return true; + auto &config = config_get_sound_io(dev); + for(auto i = config.m_channel_mappings.begin(); i != config.m_channel_mappings.end(); i++) + if(std::get<0>(*i) == guest_channel && std::get<1>(*i) == "" && std::get<2>(*i) == node_channel) { + config.m_channel_mappings.erase(i); + return; } - index -= mixer.inputs(); - } +} - // didn't locate - info.mixer = nullptr; - return false; +void sound_manager::config_set_volume_sound_io_channel_connection_node(sound_io_device *dev, u32 guest_channel, std::string name, u32 node_channel, float db) +{ + internal_config_set_volume_sound_io_channel_connection_node(dev, guest_channel, name, node_channel, db); + m_osd_info.m_generation --; } +void sound_manager::internal_config_set_volume_sound_io_channel_connection_node(sound_io_device *dev, u32 guest_channel, std::string name, u32 node_channel, float db) +{ + auto &config = config_get_sound_io(dev); + for(auto &cmap : config.m_channel_mappings) + if(std::get<0>(cmap) == guest_channel && std::get<1>(cmap) == name && std::get<2>(cmap) == node_channel) { + std::get<3>(cmap) = db; + return; + } +} -//------------------------------------------------- -// mute - mute sound output -//------------------------------------------------- +void sound_manager::config_set_volume_sound_io_channel_connection_default(sound_io_device *dev, u32 guest_channel, u32 node_channel, float db) +{ + internal_config_set_volume_sound_io_channel_connection_default(dev, guest_channel, node_channel, db); + m_osd_info.m_generation --; +} -void sound_manager::mute(bool mute, u8 reason) +void sound_manager::internal_config_set_volume_sound_io_channel_connection_default(sound_io_device *dev, u32 guest_channel, u32 node_channel, float db) { - if (mute) - m_muted |= reason; - else - m_muted &= ~reason; - set_attenuation(m_attenuation); + auto &config = config_get_sound_io(dev); + for(auto &cmap : config.m_channel_mappings) + if(std::get<0>(cmap) == guest_channel && std::get<1>(cmap) == "" && std::get<2>(cmap) == node_channel) { + std::get<3>(cmap) = db; + return; + } } +void sound_manager::startup_cleanups() +{ + auto osd_info = machine().osd().sound_get_information(); + + // for every sound_io device that does not have a configuration entry, add a + // mapping to default + auto default_one = [this](sound_io_device &dev) { + for(const auto &config : m_configs) + if(config.m_name == dev.tag()) + return; + m_configs.emplace_back(config_mapping { dev.tag() }); + m_configs.back().m_node_mappings.emplace_back(std::pair<std::string, float>("", 0.0)); + }; + + for(sound_io_device &dev : speaker_device_enumerator(machine().root_device())) + default_one(dev); + for(sound_io_device &dev : microphone_device_enumerator(machine().root_device())) + default_one(dev); + + // If there's no default sink replace all the default sink config + // entries into the first sink available + if(!osd_info.m_default_sink) { + std::string first_sink_name; + for(const auto &node : osd_info.m_nodes) + if(node.m_sinks) { + first_sink_name = node.name(); + break; + } -//------------------------------------------------- -// reset - reset all sound chips -//------------------------------------------------- + if(first_sink_name != "") + for(auto &config : m_configs) { + for(auto &nmap : config.m_node_mappings) + if(nmap.first == "") + nmap.first = first_sink_name; + for(auto &cmap : config.m_channel_mappings) + if(std::get<1>(cmap) == "") + std::get<1>(cmap) = first_sink_name; + } + } -void sound_manager::reset() -{ - // reset all the sound chips - for (device_sound_interface &sound : sound_interface_iterator(machine().root_device())) - sound.device().reset(); + + // If there's no default source replace all the default source config + // entries into the first source available + if(!osd_info.m_default_source) { + std::string first_source_name; + for(const auto &node : osd_info.m_nodes) + if(node.m_sources) { + first_source_name = node.name(); + break; + } + + if(first_source_name != "") + for(auto &config : m_configs) { + for(auto &nmap : config.m_node_mappings) + if(nmap.first == "") + nmap.first = first_source_name; + for(auto &cmap : config.m_channel_mappings) + if(std::get<1>(cmap) == "") + std::get<1>(cmap) = first_source_name; + } + } } +template<bool is_output, typename S> void sound_manager::apply_osd_changes(std::vector<S> &streams) +{ + // Apply host system volume and routing changes to the internal structures + for(S &stream : streams) { + u32 sidx; + for(sidx = 0; sidx != m_osd_info.m_streams.size() && m_osd_info.m_streams[sidx].m_id != stream.m_id; sidx++); + // If the stream has been lost, continue. It will be cleared in update_osd_streams. + if(sidx == m_osd_info.m_streams.size()) + continue; + + // Check if the target and/or the volumes changed + bool node_changed = stream.m_node != m_osd_info.m_streams[sidx].m_node; + bool volume_changed = !std::equal(stream.m_volumes.begin(), stream.m_volumes.end(), m_osd_info.m_streams[sidx].m_volumes.begin(), m_osd_info.m_streams[sidx].m_volumes.end()); + + if(node_changed || volume_changed) { + // Check if a node change is just tracking the system default + bool system_default_tracking = node_changed && stream.m_is_system_default && m_osd_info.m_streams[sidx].m_node == (is_output ? m_osd_info.m_default_sink : m_osd_info.m_default_source); + + // Find the config entry for the sound_io + config_mapping *config = nullptr; + for(auto &conf : m_configs) + if(conf.m_name == stream.m_dev->tag()) { + config = &conf; + break; + } + if(!config) + continue; + + // Retrieve the old node name, and, if it's different, the new node name + std::string old_node_name = stream.m_node_name; + std::string new_node_name; + if(node_changed) { + for(const auto &node : m_osd_info.m_nodes) + if(node.m_id == m_osd_info.m_streams[sidx].m_node) { + new_node_name = node.name(); + break; + } + // That's really, really not supposed to happen + if(new_node_name.empty()) + continue; + } else + new_node_name = old_node_name; + + // Separate the cases on full mapping vs. channel mapping + if(!stream.m_is_channel_mapping) { + // Full mapping + // Find the index of the config mapping entry that generated the stream, if there's still one. + // Note that a default system stream has the empty string as a name + u32 index; + for(index = 0; index != config->m_node_mappings.size(); index++) + if(config->m_node_mappings[index].first == old_node_name) + break; + if(index == config->m_node_mappings.size()) + continue; + + // If the target node changed, write it down + if(node_changed) { + if(!system_default_tracking) { + config->m_node_mappings[index].first = new_node_name; + stream.m_node_name = new_node_name; + stream.m_is_system_default = false; + } + stream.m_node = m_osd_info.m_streams[sidx].m_node; + } -//------------------------------------------------- -// pause - pause sound output -//------------------------------------------------- + // If the volume changed, there are two + // possibilities: either the channels split, or + // they didn't. + if(volume_changed) { + // Check is all the channel volumes are the same + float new_volume = m_osd_info.m_streams[sidx].m_volumes[0]; + bool same = true; + for(u32 i = 1; i != m_osd_info.m_streams[sidx].m_volumes.size(); i++) + if(m_osd_info.m_streams[sidx].m_volumes[i] != new_volume) { + same = false; + break; + } + if(same) { + // All the same volume, just note down the new volume + stream.m_volumes = m_osd_info.m_streams[sidx].m_volumes; + config->m_node_mappings[index].second = new_volume; + + } else { + const osd::audio_info::node_info *node = nullptr; + for(const auto &n : m_osd_info.m_nodes) + if(n.m_id == stream.m_node) { + node = &n; + break; + } + for(u32 channel = 0; channel != stream.m_channels; channel++) { + std::vector<u32> targets = find_channel_mapping(stream.m_dev->get_position(channel), node); + for(u32 tchannel : targets) + if(stream.m_node_name == "") + internal_config_add_sound_io_channel_connection_default(stream.m_dev, channel, tchannel, m_osd_info.m_streams[sidx].m_volumes[tchannel]); + else + internal_config_add_sound_io_channel_connection_node(stream.m_dev, channel, stream.m_node_name, tchannel, m_osd_info.m_streams[sidx].m_volumes[tchannel]); + } + config->m_node_mappings.erase(config->m_node_mappings.begin() + index); + } + } + } else { + // Channel mapping + for(u32 channel = 0; channel != stream.m_channels; channel++) { + if(stream.m_unused_channels_mask & (1 << channel)) + continue; + + // Find the index of the config mapping entry that generated the stream channel, if there's still one. + // Note that a default system stream has the empty string as a name + u32 index; + for(index = 0; index != config->m_channel_mappings.size(); index++) + if(std::get<1>(config->m_channel_mappings[index]) == old_node_name && + std::get<2>(config->m_channel_mappings[index]) == channel) + break; + if(index == config->m_channel_mappings.size()) + continue; + + // If the target node changed, write it down + if(node_changed) { + if(!system_default_tracking) { + std::get<1>(config->m_channel_mappings[index]) = new_node_name; + stream.m_node_name = new_node_name; + stream.m_is_system_default = false; + } + stream.m_node = m_osd_info.m_streams[sidx].m_node; + } + + // If the volume changed, write in down too + if(volume_changed) { + std::get<3>(config->m_channel_mappings[index]) = m_osd_info.m_streams[sidx].m_volumes[channel]; + stream.m_volumes[channel] = m_osd_info.m_streams[sidx].m_volumes[channel]; + } + } + } + } + } +} -void sound_manager::pause() +void sound_manager::osd_information_update() { - mute(true, MUTE_REASON_PAUSE); + // Get a snapshot of the current information + m_osd_info = machine().osd().sound_get_information(); + + // Analyze the streams to see if anything changed, but only in the + // split stream case. + if(machine().osd().sound_split_streams_per_source()) { + apply_osd_changes<false, osd_input_stream >(m_osd_input_streams ); + apply_osd_changes<false, osd_output_stream>(m_osd_output_streams); + } } +void sound_manager::generate_mapping() +{ + auto find_node = [this](std::string name) -> u32 { + for(const auto &node : m_osd_info.m_nodes) + if(node.name() == name) + return node.m_id; + return 0; + }; + + m_mappings.clear(); + for(speaker_info &speaker : m_speakers) { + auto &config = config_get_sound_io(&speaker.m_dev); + m_mappings.emplace_back(mapping { &speaker.m_dev }); + auto &omap = m_mappings.back(); + + std::vector<std::string> node_to_remove; + for(auto &nmap : config.m_node_mappings) { + if(nmap.first == "") { + if(m_osd_info.m_default_sink) + omap.m_node_mappings.emplace_back(mapping::node_mapping { m_osd_info.m_default_sink, nmap.second, true }); + } else { + u32 node_id = find_node(nmap.first); + if(node_id != 0) + omap.m_node_mappings.emplace_back(mapping::node_mapping { node_id, nmap.second, false }); + else + node_to_remove.push_back(nmap.first); + } + } -//------------------------------------------------- -// resume - resume sound output -//------------------------------------------------- + for(auto &nmap: node_to_remove) + internal_config_remove_sound_io_connection_node(&speaker.m_dev, nmap); + + std::vector<std::tuple<u32, std::string, u32>> channel_map_to_remove; + for(auto &cmap : config.m_channel_mappings) { + if(std::get<1>(cmap) == "") { + if(m_osd_info.m_default_sink) + omap.m_channel_mappings.emplace_back(mapping::channel_mapping { std::get<0>(cmap), m_osd_info.m_default_sink, std::get<2>(cmap), std::get<3>(cmap), true }); + } else { + u32 node_id = find_node(std::get<1>(cmap)); + if(node_id != 0) + omap.m_channel_mappings.emplace_back(mapping::channel_mapping { std::get<0>(cmap), node_id, std::get<2>(cmap), std::get<3>(cmap), false }); + else + channel_map_to_remove.push_back(std::tuple<u32, std::string, u32>(std::get<0>(cmap), std::get<1>(cmap), std::get<2>(cmap))); + } + } -void sound_manager::resume() -{ - mute(false, MUTE_REASON_PAUSE); + for(auto &cmap : channel_map_to_remove) + internal_config_remove_sound_io_channel_connection_node(&speaker.m_dev, std::get<0>(cmap), std::get<1>(cmap), std::get<2>(cmap)); + } + + for(microphone_info &mic : m_microphones) { + auto &config = config_get_sound_io(&mic.m_dev); + m_mappings.emplace_back(mapping { &mic.m_dev }); + auto &omap = m_mappings.back(); + + std::vector<std::string> node_to_remove; + for(auto &nmap : config.m_node_mappings) { + if(nmap.first == "") { + if(m_osd_info.m_default_source) + omap.m_node_mappings.emplace_back(mapping::node_mapping { m_osd_info.m_default_source, nmap.second, true }); + } else { + u32 node_id = find_node(nmap.first); + if(node_id != 0) + omap.m_node_mappings.emplace_back(mapping::node_mapping { node_id, nmap.second, false }); + else + node_to_remove.push_back(nmap.first); + } + } + + for(auto &nmap: node_to_remove) + internal_config_remove_sound_io_connection_node(&mic.m_dev, nmap); + + std::vector<std::tuple<u32, std::string, u32>> channel_map_to_remove; + for(auto &cmap : config.m_channel_mappings) { + if(std::get<1>(cmap) == "") { + if(m_osd_info.m_default_source) + omap.m_channel_mappings.emplace_back(mapping::channel_mapping { std::get<0>(cmap), m_osd_info.m_default_source, std::get<2>(cmap), std::get<3>(cmap), true }); + } else { + u32 node_id = find_node(std::get<1>(cmap)); + if(node_id != 0) + omap.m_channel_mappings.emplace_back(mapping::channel_mapping { std::get<0>(cmap), node_id, std::get<2>(cmap), std::get<3>(cmap), false }); + else + channel_map_to_remove.push_back(std::tuple<u32, std::string, u32>(std::get<0>(cmap), std::get<1>(cmap), std::get<2>(cmap))); + } + } + + for(auto &cmap : channel_map_to_remove) + internal_config_remove_sound_io_channel_connection_node(&mic.m_dev, std::get<0>(cmap), std::get<1>(cmap), std::get<2>(cmap)); + } } +// Find where to map a sound_io channel into a node's channels depending on their positions + +std::vector<u32> sound_manager::find_channel_mapping(const std::array<double, 3> &position, const osd::audio_info::node_info *node) +{ + std::vector<u32> result; + if(position[0] == 0 && position[1] == 0 && position[2] == 0) + return result; + double best_dist = -1; + for(u32 port = 0; port != node->m_port_positions.size(); port++) + if(node->m_port_positions[port][0] || node->m_port_positions[port][1] || node->m_port_positions[port][2]) { + double dx = position[0] - node->m_port_positions[port][0]; + double dy = position[1] - node->m_port_positions[port][1]; + double dz = position[2] - node->m_port_positions[port][2]; + double dist = dx*dx + dy*dy + dz*dz; + if(best_dist == -1 || dist < best_dist) { + best_dist = dist; + result.clear(); + result.push_back(port); + } else if(best_dist == dist) + result.push_back(port); + } + return result; +} -//------------------------------------------------- -// config_load - read and apply data from the -// configuration file -//------------------------------------------------- -void sound_manager::config_load(config_type cfg_type, util::xml::data_node const *parentnode) +void sound_manager::update_osd_streams() { - // we only care about game files - if (cfg_type != config_type::GAME) - return; + std::unique_lock<std::mutex> lock(m_effects_mutex); + auto current_input_streams = std::move(m_osd_input_streams); + auto current_output_streams = std::move(m_osd_output_streams); + m_osd_input_streams.clear(); + m_osd_output_streams.clear(); + + // Find the index of a sound_io_device in the speaker_info vector or the microphone_info vector + + auto find_sound_io_index = [this](sound_io_device *dev) -> u32 { + for(u32 si = 0; si != m_speakers.size(); si++) + if(&m_speakers[si].m_dev == dev) + return si; + for(u32 si = 0; si != m_microphones.size(); si++) + if(&m_microphones[si].m_dev == dev) + return si; + return 0; // Can't happen + }; + + + // Find a pointer to a node_info from the node id + auto find_node_info = [this](u32 node) -> const osd::audio_info::node_info * { + for(const auto &ni : m_osd_info.m_nodes) { + if(ni.m_id == node) + return ∋ + } + // Can't happen + return nullptr; + }; + + // Two possible mapping methods depending on the osd capabilities + + for(auto &m : m_microphones) + m.m_input_mixing_steps.clear(); + m_output_mixing_steps.clear(); + + auto &osd = machine().osd(); + if(osd.sound_split_streams_per_source()) { + auto get_input_stream_for_node_and_device = [this, ¤t_input_streams] (const osd::audio_info::node_info *node, sound_io_device *dev, bool is_system_default, bool is_channel_mapping = false) -> u32 { + // Check if the osd stream already exists to pick it up in case. + // Clear the id in the current_streams structure to show it has been picked up, reset the unused mask. + // Clear the volumes + // m_dev will already be correct + + for(auto &os : current_input_streams) + if(os.m_id && os.m_node == node->m_id && os.m_dev == dev) { + u32 sid = m_osd_input_streams.size(); + m_osd_input_streams.emplace_back(std::move(os)); + os.m_id = 0; + auto &nos = m_osd_input_streams[sid]; + nos.m_is_channel_mapping = is_channel_mapping; + nos.m_unused_channels_mask = util::make_bitmask<u32>(node->m_sources); + nos.m_volumes.clear(); + nos.m_is_system_default = is_system_default; + return sid; + } - // might not have any data - if (parentnode == nullptr) - return; + // If none exists, create one + u32 sid = m_osd_input_streams.size(); + u32 rate = machine().sample_rate(); + m_osd_input_streams.emplace_back(osd_input_stream(node->m_id, is_system_default ? "" : node->m_name, node->m_sources, rate, is_system_default, dev)); + osd_input_stream &nos = m_osd_input_streams.back(); + nos.m_id = machine().osd().sound_stream_source_open(node->m_id, dev->tag(), rate); + nos.m_is_channel_mapping = is_channel_mapping; + nos.m_buffer.set_sync_sample(rate_and_last_sync_to_index(rate)); + return sid; + }; + + auto get_output_stream_for_node_and_device = [this, ¤t_output_streams] (const osd::audio_info::node_info *node, sound_io_device *dev, bool is_system_default, bool is_channel_mapping = false) -> u32 { + // Check if the osd stream already exists to pick it up in case. + // Clear the id in the current_streams structure to show it has been picked up, reset the unused mask. + // Clear the volumes + // m_dev will already be correct + + for(auto &os : current_output_streams) + if(os.m_id && os.m_node == node->m_id && os.m_dev == dev) { + u32 sid = m_osd_output_streams.size(); + m_osd_output_streams.emplace_back(std::move(os)); + os.m_id = 0; + auto &nos = m_osd_output_streams[sid]; + nos.m_is_channel_mapping = is_channel_mapping; + nos.m_volumes.clear(); + nos.m_unused_channels_mask = util::make_bitmask<u32>(node->m_sinks); + nos.m_is_system_default = is_system_default; + return sid; + } - // iterate over channel nodes - for (util::xml::data_node const *channelnode = parentnode->get_child("channel"); channelnode != nullptr; channelnode = channelnode->get_next_sibling("channel")) - { - mixer_input info; - if (indexed_mixer_input(channelnode->get_attribute_int("index", -1), info)) - { - float defvol = channelnode->get_attribute_float("defvol", 1.0f); - float newvol = channelnode->get_attribute_float("newvol", -1000.0f); - if (newvol != -1000.0f) - info.stream->set_user_gain(info.inputnum, newvol / defvol); + // If none exists, create one + u32 sid = m_osd_output_streams.size(); + u32 rate = machine().sample_rate(); + m_osd_output_streams.emplace_back(osd_output_stream(node->m_id, is_system_default ? "" : node->m_name, node->m_sinks, rate, is_system_default, dev)); + osd_output_stream &nos = m_osd_output_streams.back(); + nos.m_id = machine().osd().sound_stream_sink_open(node->m_id, dev->tag(), rate); + nos.m_is_channel_mapping = is_channel_mapping; + nos.m_last_sync = rate_and_last_sync_to_index(rate); + return sid; + }; + + auto get_input_stream_for_node_and_channel = [this, &get_input_stream_for_node_and_device] (const osd::audio_info::node_info *node, u32 node_channel, sound_io_device *dev, bool is_system_default) -> u32 { + // First check if there's an active stream + for(u32 sid = 0; sid != m_osd_input_streams.size(); sid++) { + auto &os = m_osd_input_streams[sid]; + if(os.m_node == node->m_id && os.m_dev == dev && os.m_unused_channels_mask & (1 << node_channel) && os.m_is_channel_mapping) + return sid; + } + + // Otherwise use the default method + return get_input_stream_for_node_and_device(node, dev, is_system_default, true); + }; + + + auto get_output_stream_for_node_and_channel = [this, &get_output_stream_for_node_and_device] (const osd::audio_info::node_info *node, u32 node_channel, sound_io_device *dev, bool is_system_default) -> u32 { + // First check if there's an active stream with the correct channel not used yet + for(u32 sid = 0; sid != m_osd_output_streams.size(); sid++) { + auto &os = m_osd_output_streams[sid]; + if(os.m_node == node->m_id && os.m_dev == dev && os.m_unused_channels_mask & (1 << node_channel) && os.m_is_channel_mapping) + return sid; + } + + // Otherwise use the default method + return get_output_stream_for_node_and_device(node, dev, is_system_default, true); + }; + + // Create/retrieve streams to apply the decided mapping + for(const auto &omap : m_mappings) { + u32 dev_index = find_sound_io_index(omap.m_dev); + bool is_output = omap.m_dev->is_output(); + if(is_output) { + std::vector<mixing_step> &mixing_steps = m_output_mixing_steps; + u32 dchannels = omap.m_dev->inputs(); + for(const auto &nm : omap.m_node_mappings) { + const auto *node = find_node_info(nm.m_node); + u32 osd_index = get_output_stream_for_node_and_device(node, omap.m_dev, nm.m_is_system_default); + auto &stream = m_osd_output_streams[osd_index]; + u32 umask = stream.m_unused_channels_mask; + float linear_volume = 1.0; + + if(osd.sound_external_per_channel_volume()) { + stream.m_volumes.clear(); + stream.m_volumes.resize(stream.m_channels, nm.m_db); + + } else + linear_volume = osd::db_to_linear(nm.m_db); + + for(u32 channel = 0; channel != dchannels; channel++) { + std::vector<u32> targets = find_channel_mapping(omap.m_dev->get_position(channel), node); + for(u32 tchannel : targets) { + // If the channel is output and in the to + // clear mask, use load, otherwise use add. + // Apply the volume too if needed + mixing_steps.emplace_back(mixing_step { + (umask & (1 << tchannel)) ? mixing_step::COPY : mixing_step::ADD, + osd_index, + tchannel, + dev_index, + channel, + linear_volume + }); + umask &= ~(1 << tchannel); + } + } + stream.m_unused_channels_mask = umask; + } + + for(const auto &cm : omap.m_channel_mappings) { + const auto *node = find_node_info(cm.m_node); + u32 osd_index = get_output_stream_for_node_and_channel(node, cm.m_node_channel, omap.m_dev, cm.m_is_system_default); + auto &stream = m_osd_output_streams[osd_index]; + float linear_volume = 1.0; + + if(osd.sound_external_per_channel_volume()) { + if(stream.m_volumes.empty()) + stream.m_volumes.resize(stream.m_channels, -96); + stream.m_volumes[cm.m_node_channel] = cm.m_db; + + } else + linear_volume = osd::db_to_linear(cm.m_db); + + mixing_steps.emplace_back(mixing_step { + (stream.m_unused_channels_mask & (1 << cm.m_node_channel)) ? + mixing_step::COPY : mixing_step::ADD, + osd_index, + cm.m_node_channel, + dev_index, + cm.m_guest_channel, + linear_volume + }); + stream.m_unused_channels_mask &= ~(1 << cm.m_node_channel); + } + + + } else { + std::vector<mixing_step> &mixing_steps = m_microphones[dev_index].m_input_mixing_steps; + u32 dchannels = omap.m_dev->outputs(); + for(const auto &nm : omap.m_node_mappings) { + const auto *node = find_node_info(nm.m_node); + u32 osd_index = get_input_stream_for_node_and_device(node, omap.m_dev, nm.m_is_system_default); + auto &stream = m_osd_input_streams[osd_index]; + u32 umask = stream.m_unused_channels_mask; + float linear_volume = 1.0; + + if(osd.sound_external_per_channel_volume()) { + stream.m_volumes.clear(); + stream.m_volumes.resize(stream.m_channels, nm.m_db); + + } else + linear_volume = osd::db_to_linear(nm.m_db); + + for(u32 channel = 0; channel != dchannels; channel++) { + std::vector<u32> targets = find_channel_mapping(omap.m_dev->get_position(channel), node); + for(u32 tchannel : targets) { + // If the channel is output and in the to + // clear mask, use load, otherwise use add. + // Apply the volume too if needed + mixing_steps.emplace_back(mixing_step { + mixing_step::ADD, + osd_index, + tchannel, + dev_index, + channel, + linear_volume + }); + umask &= ~(1 << tchannel); + } + } + stream.m_unused_channels_mask = umask; + } + + for(const auto &cm : omap.m_channel_mappings) { + const auto *node = find_node_info(cm.m_node); + u32 osd_index = get_input_stream_for_node_and_channel(node, cm.m_node_channel, omap.m_dev, cm.m_is_system_default); + auto &stream = m_osd_input_streams[osd_index]; + float linear_volume = 1.0; + + if(osd.sound_external_per_channel_volume()) { + if(stream.m_volumes.empty()) + stream.m_volumes.resize(stream.m_channels, -96); + stream.m_volumes[cm.m_node_channel] = cm.m_db; + + } else + linear_volume = osd::db_to_linear(cm.m_db); + + mixing_steps.emplace_back(mixing_step { + mixing_step::ADD, + osd_index, + cm.m_node_channel, + dev_index, + cm.m_guest_channel, + linear_volume + }); + stream.m_unused_channels_mask &= ~(1 << cm.m_node_channel); + } + } + } + + } else { + // All sources need to be merged per-destination, max one stream per destination + + std::map<u32, u32> stream_per_node; + + // Retrieve or create the one osd stream for a given + // destination. First check if we already have it, then + // whether it was previously created, then otherwise create + // it. + + auto get_input_stream_for_node = [this, ¤t_input_streams, &stream_per_node] (const osd::audio_info::node_info *node, bool is_system_default) -> u32 { + // Pick up the existing stream if there's one + auto si = stream_per_node.find(node->m_id); + if(si != stream_per_node.end()) + return si->second; + + // Create the default unused mask + u32 channels = node->m_sources; + u32 umask = util::make_bitmask<u32>(channels); + + // Check if the osd stream already exists to pick it up in case. + // Clear the id in the current_streams structure to show it has been picked up, reset the unused mask. + // m_speaker will already be nullptr, m_source_channels and m_volumes empty. + + for(auto &os : current_input_streams) + if(os.m_id && os.m_node == node->m_id) { + u32 sid = m_osd_input_streams.size(); + m_osd_input_streams.emplace_back(std::move(os)); + os.m_id = 0; + m_osd_input_streams.back().m_unused_channels_mask = umask; + m_osd_input_streams.back().m_is_system_default = is_system_default; + stream_per_node[node->m_id] = sid; + return sid; + } + + // If none exists, create one + u32 sid = m_osd_input_streams.size(); + u32 rate = machine().sample_rate(); + m_osd_input_streams.emplace_back(osd_input_stream(node->m_id, is_system_default ? "" : node->m_name, channels, rate, is_system_default, nullptr)); + osd_input_stream &stream = m_osd_input_streams.back(); + stream.m_id = machine().osd().sound_stream_source_open(node->m_id, machine().system().name, rate); + stream.m_buffer.set_sync_sample(rate_and_last_sync_to_index(rate)); + stream_per_node[node->m_id] = sid; + return sid; + }; + + auto get_output_stream_for_node = [this, ¤t_output_streams, &stream_per_node] (const osd::audio_info::node_info *node, bool is_system_default) -> u32 { + // Pick up the existing stream if there's one + auto si = stream_per_node.find(node->m_id); + if(si != stream_per_node.end()) + return si->second; + + // Create the default unused mask + u32 channels = node->m_sinks; + u32 umask = util::make_bitmask<u32>(channels); + + // Check if the osd stream already exists to pick it up in case. + // Clear the id in the current_streams structure to show it has been picked up, reset the unused mask. + // m_speaker will already be nullptr, m_source_channels and m_volumes empty. + + for(auto &os : current_output_streams) + if(os.m_id && os.m_node == node->m_id) { + u32 sid = m_osd_output_streams.size(); + m_osd_output_streams.emplace_back(std::move(os)); + os.m_id = 0; + m_osd_output_streams.back().m_unused_channels_mask = umask; + m_osd_output_streams.back().m_is_system_default = is_system_default; + stream_per_node[node->m_id] = sid; + return sid; + } + + // If none exists, create one + u32 sid = m_osd_output_streams.size(); + u32 rate = machine().sample_rate(); + m_osd_output_streams.emplace_back(osd_output_stream(node->m_id, is_system_default ? "" : node->m_name, channels, rate, is_system_default, nullptr)); + osd_output_stream &stream = m_osd_output_streams.back(); + stream.m_id = machine().osd().sound_stream_sink_open(node->m_id, machine().system().name, rate); + stream.m_last_sync = rate_and_last_sync_to_index(rate); + stream_per_node[node->m_id] = sid; + return sid; + }; + + + // Create/retrieve streams to apply the decided mapping + + for(const auto &omap : m_mappings) { + u32 dev_index = find_sound_io_index(omap.m_dev); + bool is_output = omap.m_dev->is_output(); + if(is_output) { + u32 channels = m_speakers[dev_index].m_channels; + std::vector<mixing_step> &mixing_steps = m_output_mixing_steps; + for(const auto &nm : omap.m_node_mappings) { + const auto *node = find_node_info(nm.m_node); + u32 osd_index = get_output_stream_for_node(node, nm.m_is_system_default); + u32 umask = m_osd_output_streams[osd_index].m_unused_channels_mask; + float linear_volume = osd::db_to_linear(nm.m_db); + + for(u32 channel = 0; channel != channels; channel++) { + std::vector<u32> targets = find_channel_mapping(omap.m_dev->get_position(channel), node); + for(u32 tchannel : targets) { + // If the channel is in the to clear mask, use load, otherwise use add + // Apply the volume too + mixing_steps.emplace_back(mixing_step { + (umask & (1 << tchannel)) ? mixing_step::COPY : mixing_step::ADD, + osd_index, + tchannel, + dev_index, + channel, + linear_volume + }); + umask &= ~(1 << tchannel); + } + } + m_osd_output_streams[osd_index].m_unused_channels_mask = umask; + } + + for(const auto &cm : omap.m_channel_mappings) { + const auto *node = find_node_info(cm.m_node); + u32 osd_index = get_output_stream_for_node(node, false); + u32 umask = m_osd_output_streams[osd_index].m_unused_channels_mask; + + // If the channel is in the to clear mask, use load, otherwise use add + // Apply the volume too + mixing_steps.emplace_back(mixing_step { + (umask & (1 << cm.m_node_channel)) ? mixing_step::COPY : mixing_step::ADD, + osd_index, + cm.m_node_channel, + dev_index, + cm.m_guest_channel, + osd::db_to_linear(cm.m_db) + }); + m_osd_output_streams[osd_index].m_unused_channels_mask = umask & ~(1 << cm.m_node_channel); + } + + } else { + u32 channels = m_microphones[dev_index].m_channels; + std::vector<mixing_step> &mixing_steps = m_microphones[dev_index].m_input_mixing_steps; + for(const auto &nm : omap.m_node_mappings) { + const auto *node = find_node_info(nm.m_node); + u32 osd_index = get_input_stream_for_node(node, nm.m_is_system_default); + float linear_volume = osd::db_to_linear(nm.m_db); + + for(u32 channel = 0; channel != channels; channel++) { + std::vector<u32> targets = find_channel_mapping(omap.m_dev->get_position(channel), node); + for(u32 tchannel : targets) { + // If the channel is in the to clear mask, use load, otherwise use add + // Apply the volume too + mixing_steps.emplace_back(mixing_step { + mixing_step::ADD, + osd_index, + tchannel, + dev_index, + channel, + linear_volume + }); + m_osd_input_streams[osd_index].m_unused_channels_mask &= ~(1 << tchannel); + } + } + } + + for(const auto &cm : omap.m_channel_mappings) { + const auto *node = find_node_info(cm.m_node); + u32 osd_index = get_input_stream_for_node(node, false); + + // If the channel is in the to clear mask, use load, otherwise use add + // Apply the volume too + mixing_steps.emplace_back(mixing_step { + mixing_step::ADD, + osd_index, + cm.m_node_channel, + dev_index, + cm.m_guest_channel, + osd::db_to_linear(cm.m_db) + }); + m_osd_input_streams[osd_index].m_unused_channels_mask &= ~(1 << cm.m_node_channel); + } + } } } -} + // Add a clear step for all output streams that need it + // Also set the volumes if supported + for(u32 stream_index = 0; stream_index != m_osd_output_streams.size(); stream_index++) { + auto &stream = m_osd_output_streams[stream_index]; + if(stream.m_unused_channels_mask) { + for(u32 channel = 0; channel != stream.m_channels; channel ++) + if(stream.m_unused_channels_mask & (1 << channel)) + m_output_mixing_steps.emplace_back(mixing_step { mixing_step::CLEAR, 0, 0, stream_index, channel, 0.0 }); + } + if(!stream.m_volumes.empty()) + osd.sound_stream_set_volumes(stream.m_id, stream.m_volumes); + } -//------------------------------------------------- -// config_save - save data to the configuration -// file -//------------------------------------------------- + // If supported, set the volumes for the input streams + for(u32 stream_index = 0; stream_index != m_osd_input_streams.size(); stream_index++) { + auto &stream = m_osd_input_streams[stream_index]; + if(!stream.m_volumes.empty()) + osd.sound_stream_set_volumes(stream.m_id, stream.m_volumes); + } -void sound_manager::config_save(config_type cfg_type, util::xml::data_node *parentnode) + // Close all previous streams that haven't been picked up + for(const auto &stream : current_input_streams) + if(stream.m_id) + machine().osd().sound_stream_close(stream.m_id); + for(const auto &stream : current_output_streams) + if(stream.m_id) + machine().osd().sound_stream_close(stream.m_id); +} + +void sound_manager::mapping_update() { - // we only care about game files - if (cfg_type != config_type::GAME) - return; + auto &osd = machine().osd(); + while(m_osd_info.m_generation != osd.sound_get_generation()) { + osd_information_update(); + + if(VERBOSE & LOG_OSD_INFO) { + LOG_OUTPUT_FUNC("OSD information:\n"); + LOG_OUTPUT_FUNC("- generation %u\n", m_osd_info.m_generation); + LOG_OUTPUT_FUNC("- default sink %u\n", m_osd_info.m_default_sink); + LOG_OUTPUT_FUNC("- default source %u\n", m_osd_info.m_default_source); + LOG_OUTPUT_FUNC("- nodes:\n"); + for(const auto &node : m_osd_info.m_nodes) { + LOG_OUTPUT_FUNC(" * %3u %s [%d %d-%d]\n", node.m_id, node.name().c_str(), node.m_rate.m_default_rate, node.m_rate.m_min_rate, node.m_rate.m_max_rate); + uint32_t port_count = node.m_sinks; + if(port_count < node.m_sources) + port_count = node.m_sources; + for(uint32_t port = 0; port != port_count; port++) + LOG_OUTPUT_FUNC(" %s %s [%g %g %g]\n", + port < node.m_sinks ? port < node.m_sources ? "<>" : ">" : "<", + node.m_port_names[port].c_str(), + node.m_port_positions[port][0], + node.m_port_positions[port][1], + node.m_port_positions[port][2]); + } + LOG_OUTPUT_FUNC("- streams:\n"); + for(const auto &stream : m_osd_info.m_streams) { + LOG_OUTPUT_FUNC(" * %3u node %u", stream.m_id, stream.m_node); + if(!stream.m_volumes.empty()) { + LOG_OUTPUT_FUNC(" volumes"); + for(float v : stream.m_volumes) + LOG_OUTPUT_FUNC(" %g", v); + } + LOG_OUTPUT_FUNC("\n"); + } + } - // iterate over mixer channels - if (parentnode != nullptr) - for (int mixernum = 0; ; mixernum++) - { - mixer_input info; - if (!indexed_mixer_input(mixernum, info)) - break; - float newvol = info.stream->user_gain(info.inputnum); - - if (newvol != 1.0f) - { - util::xml::data_node *const channelnode = parentnode->add_child("channel", nullptr); - if (channelnode != nullptr) - { - channelnode->set_attribute_int("index", mixernum); - channelnode->set_attribute_float("newvol", newvol); + generate_mapping(); + + if(VERBOSE & LOG_MAPPING) { + LOG_OUTPUT_FUNC("MAPPING:\n"); + for(const auto &omap : m_mappings) { + LOG_OUTPUT_FUNC("- sound_io %s\n", omap.m_dev->tag()); + for(const auto &nm : omap.m_node_mappings) + LOG_OUTPUT_FUNC(" * node %u volume %g%s\n", nm.m_node, nm.m_db, nm.m_is_system_default ? " (default)" : ""); + for(const auto &cm : omap.m_channel_mappings) + LOG_OUTPUT_FUNC(" * channel %u <-> node %u:%i volume %g\n", cm.m_guest_channel, cm.m_node, cm.m_node_channel, cm.m_db); + } + } + + update_osd_streams(); + + if(VERBOSE & LOG_OSD_STREAMS) { + LOG_OUTPUT_FUNC("OSD input streams:\n"); + for(const auto &os : m_osd_input_streams) { + if(machine().osd().sound_split_streams_per_source()) { + LOG_OUTPUT_FUNC("- %3u %s node %u", os.m_id, os.m_dev ? os.m_dev->tag() : "-", os.m_node); + if(!os.m_is_channel_mapping) + LOG_OUTPUT_FUNC(" channels"); + if(machine().osd().sound_external_per_channel_volume()) { + LOG_OUTPUT_FUNC(" dB"); + for(u32 i = 0; i != os.m_channels; i++) + LOG_OUTPUT_FUNC(" %g", os.m_volumes[i]); + } + LOG_OUTPUT_FUNC("\n"); + } else + LOG_OUTPUT_FUNC("- %3u node %u\n", os.m_id, os.m_node); + } + LOG_OUTPUT_FUNC("Input mixing steps:\n"); + for(const auto &m : m_microphones) { + LOG_OUTPUT_FUNC(" %s:\n", m.m_dev.tag()); + for(const auto &ms : m.m_input_mixing_steps) { + static const char *const modes[5] = { "clear", "copy", "copy+vol", "add", "add+vol" }; + LOG_OUTPUT_FUNC(" - %s osd %u:%u -> device %u:%u level %g\n", modes[ms.m_mode], ms.m_osd_index, ms.m_osd_channel, ms.m_device_index, ms.m_device_channel, ms.m_linear_volume); } } + LOG_OUTPUT_FUNC("OSD output streams:\n"); + for(const auto &os : m_osd_output_streams) { + if(machine().osd().sound_split_streams_per_source()) { + LOG_OUTPUT_FUNC("- %3u %s node %u", os.m_id, os.m_dev ? os.m_dev->tag() : "-", os.m_node); + if(!os.m_is_channel_mapping) + LOG_OUTPUT_FUNC(" channels"); + if(machine().osd().sound_external_per_channel_volume()) { + LOG_OUTPUT_FUNC(" dB"); + for(u32 i = 0; i != os.m_channels; i++) + LOG_OUTPUT_FUNC(" %g", os.m_volumes[i]); + } + LOG_OUTPUT_FUNC("\n"); + } else + LOG_OUTPUT_FUNC("- %3u node %u\n", os.m_id, os.m_node); + } + LOG_OUTPUT_FUNC("Output mixing steps:\n"); + for(const auto &ms : m_output_mixing_steps) { + static const char *const modes[5] = { "clear", "copy", "copy+vol", "add", "add+vol" }; + LOG_OUTPUT_FUNC("- %s device %u:%u -> osd %u:%u level %g\n", modes[ms.m_mode], ms.m_device_index, ms.m_device_channel, ms.m_osd_index, ms.m_osd_channel, ms.m_linear_volume); + } } + } } -//------------------------------------------------- -// update - mix everything down to its final form -// and send it to the OSD layer -//------------------------------------------------- -void sound_manager::update(void *ptr, int param) + +//**// Global sound system update + +u64 sound_manager::rate_and_time_to_index(attotime time, u32 sample_rate) const { - VPRINTF(("sound_update\n")); + return time.m_seconds * sample_rate + ((time.m_attoseconds / 100'000'000) * sample_rate) / 10'000'000'000LL; +} - g_profiler.start(PROFILER_SOUND); +void sound_manager::update(s32) +{ + auto profile = g_profiler.start(PROFILER_SOUND); - // force all the speaker streams to generate the proper number of samples - m_samples_this_update = 0; - for (speaker_device &speaker : speaker_device_iterator(machine().root_device())) - speaker.mix(&m_leftmix[0], &m_rightmix[0], m_samples_this_update, (m_muted & MUTE_REASON_SYSTEM)); + if(m_osd_info.m_generation == 0xffffffff) + startup_cleanups(); - // now downmix the final result - u32 finalmix_step = machine().video().speed_factor(); - u32 finalmix_offset = 0; - s16 *finalmix = &m_finalmix[0]; - int sample; - for (sample = m_finalmix_leftover; sample < m_samples_this_update * 1000; sample += finalmix_step) - { - int sampindex = sample / 1000; - - // clamp the left side - s32 samp = m_leftmix[sampindex]; - if (samp < -32768) - samp = -32768; - else if (samp > 32767) - samp = 32767; - finalmix[finalmix_offset++] = samp; - - // clamp the right side - samp = m_rightmix[sampindex]; - if (samp < -32768) - samp = -32768; - else if (samp > 32767) - samp = 32767; - finalmix[finalmix_offset++] = samp; - } - m_finalmix_leftover = sample - m_samples_this_update * 1000; + mapping_update(); + streams_update(); + + m_last_sync_time = machine().time(); +} - // play the result - if (finalmix_offset > 0) +void sound_manager::streams_update() +{ + attotime now = machine().time(); { - if (!m_nosound_mode) - machine().osd().update_audio_stream(finalmix, finalmix_offset / 2); - machine().osd().add_audio_to_recording(finalmix, finalmix_offset / 2); - machine().video().add_sound_to_recording(finalmix, finalmix_offset / 2); - if (m_wavfile != nullptr) - wav_add_data_16(m_wavfile, finalmix, finalmix_offset); + std::unique_lock<std::mutex> lock(m_effects_mutex); + for(osd_output_stream &stream : m_osd_output_streams) { + u64 next_sync = rate_and_time_to_index(now, stream.m_rate); + stream.m_samples = next_sync - stream.m_last_sync; + stream.m_last_sync = next_sync; + } + + for(sound_stream *stream : m_ordered_streams) + stream->update_nodeps(); } - // see if we ticked over to the next second - attotime curtime = machine().time(); - bool second_tick = false; - if (curtime.seconds() != m_last_update.seconds()) + // Send the hooked samples to lua { - assert(curtime.seconds() == m_last_update.seconds() + 1); - second_tick = true; + std::map<std::string, std::vector<std::pair<const float *, int>>> sound_data; + for(device_sound_interface &sound : sound_interface_enumerator(machine().root_device())) + if(sound.get_sound_hook()) { + std::vector<std::pair<const float *, int>> buffers; + if(sound.device().type() == SPEAKER) { + const emu::detail::output_buffer_flat<sample_t> &buffer = m_speakers[static_cast<speaker_device &>(sound.device()).get_id()].m_buffer; + int samples = buffer.available_samples(); + for(int channel = 0; channel != sound.inputs(); channel++) + buffers.emplace_back(std::make_pair(buffer.ptrs(channel, 0), samples)); + + } else { + for(int channel = 0; channel != sound.outputs(); channel++) { + std::pair<sound_stream *, int> info = sound.output_to_stream_output(channel); + const emu::detail::output_buffer_flat<sample_t> &buffer = info.first->m_output_buffer; + buffers.emplace_back(std::make_pair(buffer.ptrs(info.second, 0), buffer.available_samples())); + } + } + sound_data.emplace(sound.device().tag(), std::move(buffers)); + } + + emulator_info::sound_hook(sound_data); } - // iterate over all the streams and update them - for (auto &stream : m_stream_list) - stream->update_with_accounting(second_tick); + for(sound_stream *stream : m_ordered_streams) + if(stream->device().type() != SPEAKER) + stream->sync(now); - // remember the update time - m_last_update = curtime; + for(osd_input_stream &stream : m_osd_input_streams) + stream.m_buffer.sync(); - // update sample rates if they have changed - for (auto &stream : m_stream_list) - stream->apply_sample_rate_changes(); + machine().osd().add_audio_to_recording(m_record_buffer.data(), m_record_samples); + machine().video().add_sound_to_recording(m_record_buffer.data(), m_record_samples); + if(m_wavfile) + util::wav_add_data_16(*m_wavfile, m_record_buffer.data(), m_record_samples); - // notify that new samples have been generated - emulator_info::sound_hook(); + m_effects_condition.notify_all(); +} - g_profiler.stop(); +//**// Resampler management +const audio_resampler *sound_manager::get_resampler(u32 fs, u32 ft) +{ + auto key = std::make_pair(fs, ft); + auto i = m_resamplers.find(key); + if(i != m_resamplers.end()) + return i->second.get(); + audio_resampler *res; + if(m_resampler_type == RESAMPLER_HQ) + res = new audio_resampler_hq(fs, ft, m_resampler_hq_latency, m_resampler_hq_length, m_resampler_hq_phases); + else + res = new audio_resampler_lofi(fs, ft); + m_resamplers[key].reset(res); + return res; } +void sound_manager::rebuild_all_resamplers() +{ + m_resamplers.clear(); -//------------------------------------------------- -// samples - fills the specified buffer with -// 16-bit stereo audio samples generated during -// the current frame -//------------------------------------------------- + for(auto &stream : m_stream_list) + stream->create_resamplers(); + + for(auto &stream : m_stream_list) + stream->lookup_history_sizes(); +} -void sound_manager::samples(s16 *buffer) +void sound_manager::set_resampler_type(u32 type) { - for (int sample = 0; sample < m_samples_this_update * 2; sample++) - { - *buffer++ = m_finalmix[sample]; - } + m_resampler_type = type; + rebuild_all_resamplers(); +} + +void sound_manager::set_resampler_hq_latency(double latency) +{ + m_resampler_hq_latency = latency; + rebuild_all_resamplers(); +} + +void sound_manager::set_resampler_hq_length(u32 length) +{ + m_resampler_hq_length = length; + rebuild_all_resamplers(); +} + +void sound_manager::set_resampler_hq_phases(u32 phases) +{ + m_resampler_hq_phases = phases; + rebuild_all_resamplers(); +} + +const char *sound_manager::resampler_type_names(u32 type) const +{ + using util::lang_translate; + + if(type == RESAMPLER_HQ) + return _("HQ"); + else + return _("LoFi"); } diff --git a/src/emu/sound.h b/src/emu/sound.h index 7301a4d69ba..6d699dbb8d9 100644 --- a/src/emu/sound.h +++ b/src/emu/sound.h @@ -1,11 +1,55 @@ // license:BSD-3-Clause -// copyright-holders:Aaron Giles +// copyright-holders:O. Galibert, Aaron Giles /*************************************************************************** sound.h Core sound interface functions and definitions. +**************************************************************************** + + In MAME, sound is represented as a graph of sound "streams". Each + stream has a fixed number of inputs and outputs, and is responsible + for producing sound on demand. + + The graph is driven from the outputs, which are speaker devices. + These devices are updated on a regular basis (~50 times per second), + and when an update occurs, the graph is walked from the speaker + through each input, until all connected streams are up to date. + + Individual streams can also be updated manually. This is important + for sound chips and CPU-driven devices, who should force any + affected streams to update prior to making changes. + + Sound streams are *not* part of the device execution model. This is + very important to understand. If the process of producing the ouput + stream affects state that might be consumed by an executing device + (e.g., a CPU), then care must be taken to ensure that the stream is + updated frequently enough + + The model for timing sound samples is very important and explained + here. Each stream source has a clock (aka sample rate). Each clock + edge represents a sample that is held for the duration of one clock + period. This model has interesting effects: + + For example, if you have a 10Hz clock, and call stream.update() at + t=0.91, it will compute 10 samples (for clock edges 0.0, 0.1, 0.2, + ..., 0.7, 0.8, and 0.9). And then if you ask the stream what its + current end time is (via stream.sample_time()), it will say t=1.0, + which is in the future, because it knows it will hold that last + sample until 1.0s. + + Sound generation callbacks are presented with a std::vector of inputs + and outputs. The vectors contain objects of read_stream_view and + write_stream_view respectively, which wrap access to a circular buffer + of samples. Sound generation callbacks are expected to fill all the + samples described by the outputs' write_stream_view objects. At the + moment, all outputs have the same sample rate, so the number of samples + that need to be generated will be consistent across all outputs. + + By default, the inputs will have been resampled to match the output + sample rate, unless otherwise specified. + ***************************************************************************/ #pragma once @@ -17,161 +61,308 @@ #ifndef MAME_EMU_SOUND_H #define MAME_EMU_SOUND_H +#include "wavwrite.h" +#include "interface/audio.h" +#include <mutex> +#include <thread> +#include <condition_variable> //************************************************************************** // CONSTANTS //************************************************************************** -constexpr int STREAM_SYNC = -1; // special rate value indicating a one-sample-at-a-time stream - // with actual rate defined by its input - -//************************************************************************** -// MACROS -//************************************************************************** - -typedef delegate<void (sound_stream &, stream_sample_t **inputs, stream_sample_t **outputs, int samples)> stream_update_delegate; +// special sample-rate values +constexpr u32 SAMPLE_RATE_INPUT_ADAPTIVE = 0xffffffff; +constexpr u32 SAMPLE_RATE_OUTPUT_ADAPTIVE = 0xfffffffe; +constexpr u32 SAMPLE_RATE_ADAPTIVE = 0xfffffffd; //************************************************************************** -// TYPE DEFINITIONS +// DEBUGGING //************************************************************************** +// turn this on to enable aggressive assertions and other checks +#ifdef MAME_DEBUG +#define SOUND_DEBUG (1) +#else +#define SOUND_DEBUG (1) +#endif -// structure describing an indexed mixer -struct mixer_input -{ - device_mixer_interface *mixer; // owning device interface - sound_stream * stream; // stream within the device - int inputnum; // input on the stream -}; +// if SOUND_DEBUG is on, make assertions fire regardless of MAME_DEBUG +#if (SOUND_DEBUG) +#define sound_assert(x) do { if (!(x)) { osd_printf_error("sound_assert: " #x "\n"); osd_break_into_debugger("sound_assert: " #x "\n"); } } while (0) +#else +#define sound_assert assert +#endif +using stream_update_delegate = delegate<void (sound_stream &stream)>; +class audio_effect; +class audio_resampler; -// ======================> sound_stream +// ======================> sound_stream_flags -class sound_stream +enum sound_stream_flags : u32 { - friend class sound_manager; + // default is no special flags + STREAM_DEFAULT_FLAGS = 0x00, - typedef void (*stream_update_func)(device_t *device, sound_stream *stream, void *param, stream_sample_t **inputs, stream_sample_t **outputs, int samples); + // specify that updates should be forced to one sample at a time, in real time + // this implicitly creates a timer that runs at the stream's output frequency + // so only use when strictly necessary + STREAM_SYNCHRONOUS = 0x01 +}; - // stream output class - class stream_output - { +namespace emu::detail { + template<typename S> class output_buffer_interleaved { public: - // construction/destruction - stream_output(); - stream_output &operator=(const stream_output &rhs) { assert(false); return *this; } - - // internal state - sound_stream * m_stream; // owning stream - std::vector<stream_sample_t> m_buffer; // output buffer - int m_dependents; // number of dependents - s16 m_gain; // gain to apply to the output + output_buffer_interleaved(u32 buffer_size, u32 channels); + + void set_buffer_size(u32 buffer_size); + + u32 channels() const { return m_channels; } + u64 sync_sample() const { return m_sync_sample; } + void set_sync_sample(u64 sample) { m_sync_sample = sample; } + u64 write_sample() const { return m_sync_sample + m_write_position - m_sync_position; } + void prepare_space(u32 samples); + void commit(u32 samples); + void sync(); + + void ensure_size(u32 buffer_size); + void set_history(u32 history); + + u32 available_samples() const { return m_write_position - m_sync_position; } + S *ptrw(u32 channel, s32 index) { return &m_buffer[(m_write_position + index) * m_channels + channel]; } + const S *ptrw(u32 channel, s32 index) const { return &m_buffer[(m_write_position + index) * m_channels + channel]; } + const S *ptrs(u32 channel, s32 index) const { return &m_buffer[(m_sync_position + index) * m_channels + channel]; } + + private: + std::vector<S> m_buffer; + u64 m_sync_sample; + u32 m_write_position; + u32 m_sync_position; + u32 m_history; + u32 m_channels; }; - // stream input class - class stream_input - { + template<typename S> class output_buffer_flat { public: - // construction/destruction - stream_input(); - stream_input &operator=(const stream_input &rhs) { assert(false); return *this; } - - // internal state - stream_output * m_source; // pointer to the sound_output for this source - std::vector<stream_sample_t> m_resample; // buffer for resampling to the stream's sample rate - attoseconds_t m_latency_attoseconds; // latency between this stream and the input stream - s16 m_gain; // gain to apply to this input - s16 m_user_gain; // user-controlled gain to apply to this input + output_buffer_flat(u32 buffer_size, u32 channels); + + void set_buffer_size(u32 buffer_size); + + u32 channels() const { return m_channels; } + u64 sync_sample() const { return m_sync_sample; } + void set_sync_sample(u64 sample) { m_sync_sample = sample; } + u64 write_sample() const { return m_sync_sample + m_write_position - m_sync_position; } + + void prepare_space(u32 samples); + void commit(u32 samples); + void sync(); + + void ensure_size(u32 buffer_size); + void set_history(u32 history); + + void resample(u32 previous_rate, u32 next_rate, attotime sync_time, attotime now); + + void register_save_state(device_t &device, const char *id1, const char *id2); + + u32 available_samples() const { return m_write_position - m_sync_position; } + S *ptrw(u32 channel, s32 index) { return &m_buffer[channel][m_write_position + index]; } + const S *ptrw(u32 channel, s32 index) const { return &m_buffer[channel][m_write_position + index]; } + const S *ptrs(u32 channel, s32 index) const { return &m_buffer[channel][m_sync_position + index]; } + + private: + std::vector<std::vector<S>> m_buffer; + u64 m_sync_sample; + u32 m_write_position; + u32 m_sync_position; + u32 m_history; + u32 m_channels; }; +} - // constants - static constexpr int OUTPUT_BUFFER_UPDATES = 5; - static constexpr u32 FRAC_BITS = 22; - static constexpr u32 FRAC_ONE = 1 << FRAC_BITS; - static constexpr u32 FRAC_MASK = FRAC_ONE - 1; +// ======================> sound_stream +class sound_stream +{ public: + friend class sound_manager; + using sample_t = float; + // construction/destruction - sound_stream(device_t &device, int inputs, int outputs, int sample_rate, stream_update_delegate callback); + sound_stream(device_t &device, u32 inputs, u32 outputs, u32 sample_rate, stream_update_delegate callback, sound_stream_flags flags = sound_stream_flags::STREAM_DEFAULT_FLAGS); + virtual ~sound_stream(); - // getters - sound_stream *next() const { return m_next; } + // simple getters device_t &device() const { return m_device; } - int sample_rate() const { return (m_new_sample_rate != 0xffffffff) ? m_new_sample_rate : m_sample_rate; } - attotime sample_time() const; - attotime sample_period() const { return attotime(0, m_attoseconds_per_sample); } - int input_count() const { return m_input.size(); } - int output_count() const { return m_output.size(); } - std::string input_name(int inputnum) const; - device_t *input_source_device(int inputnum) const; - int input_source_outputnum(int inputnum) const; - float user_gain(int inputnum) const; - float input_gain(int inputnum) const; - float output_gain(int outputnum) const; - - // operations - void set_input(int inputnum, sound_stream *input_stream, int outputnum = 0, float gain = 1.0f); + std::string name() const { return m_name; } + bool input_adaptive() const { return m_input_adaptive; } + bool output_adaptive() const { return m_output_adaptive; } + bool synchronous() const { return m_synchronous; } + bool is_active() const { return m_sample_rate != 0; } + + // input and output getters + u32 input_count() const { return m_input_count; } + u32 output_count() const { return m_output_count; } + + // sample rate and timing getters + u32 sample_rate() const { return m_sample_rate; } + attotime sample_period() const { return attotime::from_hz(m_sample_rate); } + + // sample id and timing of the first and last sample of the current update block, and first of the next sample block + u64 start_index() const { return m_output_buffer.write_sample(); } + u64 end_index() const { return m_output_buffer.write_sample() + samples() - 1; } + u64 sample_index() const { return m_output_buffer.write_sample() + samples(); } + attotime start_time() const { return sample_to_time(start_index()); } + attotime end_time() const { return sample_to_time(end_index()); } + attotime sample_time() const { return sample_to_time(sample_index()); } + + // convert from absolute sample index to time + attotime sample_to_time(u64 index) const; + + // gain management + float user_output_gain() const { return m_user_output_gain; } + void set_user_output_gain(float gain) { update(); m_user_output_gain = gain; } + float user_output_gain(s32 output) const { return m_user_output_channel_gain[output]; } + void set_user_output_gain(s32 output, float gain) { update(); m_user_output_channel_gain[output] = gain; } + + float input_gain(s32 input) const { return m_input_channel_gain[input]; } + void set_input_gain(s32 input, float gain) { update(); m_input_channel_gain[input] = gain; } + void apply_input_gain(s32 input, float gain) { update(); m_input_channel_gain[input] *= gain; } + float output_gain(s32 output) const { return m_output_channel_gain[output]; } + void set_output_gain(s32 output, float gain) { update(); m_output_channel_gain[output] = gain; } + void apply_output_gain(s32 output, float gain) { update(); m_output_channel_gain[output] *= gain; } + + // set the sample rate of the stream + void set_sample_rate(u32 sample_rate); + + // force an update to the current time void update(); - const stream_sample_t *output_since_last_update(int outputnum, int &numsamples); - // timing - void set_sample_rate(int sample_rate); - void set_user_gain(int inputnum, float gain); - void set_input_gain(int inputnum, float gain); - void set_output_gain(int outputnum, float gain); + // number of samples to handle + s32 samples() const { return m_samples_to_update; } + + // write a sample to the buffer + void put(s32 output, s32 index, sample_t sample) { *m_output_buffer.ptrw(output, index) = sample; } + + // write a sample to the buffer, clamping to +/- the clamp value + void put_clamp(s32 output, s32 index, sample_t sample, sample_t clamp = 1.0) { put(output, index, std::clamp(sample, -clamp, clamp)); } + + // write a sample to the buffer, converting from an integer with the given maximum + void put_int(s32 output, s32 index, s32 sample, s32 max) { put(output, index, double(sample)/max); } + + // write a sample to the buffer, converting from an integer with the given maximum + void put_int_clamp(s32 output, s32 index, s32 sample, s32 maxclamp) { put_int(output, index, std::clamp(sample, -maxclamp, maxclamp-1), maxclamp); } + + // safely add a sample to the buffer + void add(s32 output, s32 index, sample_t sample) { *m_output_buffer.ptrw(output, index) += sample; } + + // add a sample to the buffer, converting from an integer with the given maximum + void add_int(s32 output, s32 index, s32 sample, s32 max) { add(output, index, double(sample)/max); } + + // fill part of the view with the given value + void fill(s32 output, sample_t value, s32 start, s32 count) { std::fill(m_output_buffer.ptrw(output, start), m_output_buffer.ptrw(output, start+count), value); } + void fill(s32 output, sample_t value, s32 start) { std::fill(m_output_buffer.ptrw(output, start), m_output_buffer.ptrw(output, samples()), value); } + void fill(s32 output, sample_t value) { std::fill(m_output_buffer.ptrw(output, 0), m_output_buffer.ptrw(output, samples()), value); } + + // copy data from the input + void copy(s32 output, s32 input, s32 start, s32 count) { std::copy(m_input_buffer[input].begin() + start, m_input_buffer[input].begin() + start + count, m_output_buffer.ptrw(output, start)); } + void copy(s32 output, s32 input, s32 start) { std::copy(m_input_buffer[input].begin() + start, m_input_buffer[input].begin() + samples(), m_output_buffer.ptrw(output, start)); } + void copy(s32 output, s32 input) { std::copy(m_input_buffer[input].begin(), m_input_buffer[input].begin() + samples(), m_output_buffer.ptrw(output, 0)); } + + // fetch a sample from the input buffer + sample_t get(s32 input, s32 index) const { return m_input_buffer[input][index]; } + + // fetch a sample from the output buffer + sample_t get_output(s32 output, s32 index) const { return *m_output_buffer.ptrw(output, index); } + + void add_bw_route(sound_stream *source, int output, int input, float gain); + void add_fw_route(sound_stream *target, int input, int output); + std::vector<sound_stream *> sources() const; + std::vector<sound_stream *> targets() const; + + bool set_route_gain(sound_stream *source, int source_channel, int target_channel, float gain); private: - // helpers called by our friends only - void update_with_accounting(bool second_tick); - void apply_sample_rate_changes(); - - // internal helpers - void recompute_sample_rate_data(); - void allocate_resample_buffers(); - void allocate_output_buffers(); - void postload(); - void generate_samples(int samples); - stream_sample_t *generate_resampled_data(stream_input &input, u32 numsamples); - void sync_update(void *, s32); + struct route_bw { + sound_stream *m_source; + int m_output; + int m_input; + float m_gain; + const audio_resampler *m_resampler; + + route_bw(sound_stream *source, int output, int input, float gain) : m_source(source), m_output(output), m_input(input), m_gain(gain), m_resampler(nullptr) {} + }; - // linking information - device_t & m_device; // owning device - sound_stream * m_next; // next stream in the chain + struct route_fw { + sound_stream *m_target; + int m_input; + int m_output; + + route_fw(sound_stream *target, int input, int output) : m_target(target), m_input(input), m_output(output) {} + }; - // general information - u32 m_sample_rate; // sample rate of this stream - u32 m_new_sample_rate; // newly-set sample rate for the stream - bool m_synchronous; // synchronous stream that runs at the rate of its input - // timing information - attoseconds_t m_attoseconds_per_sample; // number of attoseconds per sample - s32 m_max_samples_per_update; // maximum samples per update - emu_timer * m_sync_timer; // update timer for synchronous streams + // perform most of the initialization here + void init(); - // input information - std::vector<stream_input> m_input; // list of streams we directly depend upon - std::vector<stream_sample_t *> m_input_array; // array of inputs for passing to the callback + // re-print the synchronization timer + void reprime_sync_timer(); - // resample buffer information - u32 m_resample_bufalloc; // allocated size of each resample buffer + // timer callback for synchronous streams + void sync_update(s32); - // output information - std::vector<stream_output> m_output; // list of streams which directly depend upon us - std::vector<stream_sample_t *> m_output_array; // array of outputs for passing to the callback + void update_nodeps(); + void sync(attotime now); + u64 get_current_sample_index() const; + void do_update(); - // output buffer information - u32 m_output_bufalloc; // allocated size of each output buffer - s32 m_output_sampindex; // current position within each output buffer - s32 m_output_update_sampindex; // position at time of last global update - s32 m_output_base_sampindex; // sample at base of buffer, relative to the current emulated second + bool frequency_is_solved() const { return (!(m_input_adaptive || m_output_adaptive)) || m_sample_rate != 0; } + bool try_solving_frequency(); + void register_state(); + void add_dependants(std::vector<sound_stream *> &deps); + void compute_dependants(); + void create_resamplers(); + void lookup_history_sizes(); + u32 get_history_for_bw_route(const sound_stream *source, u32 channel) const; + void internal_set_sample_rate(u32 sample_rate); + + std::string m_name; // name of this stream + std::string m_state_tag; + + // linking information + device_t &m_device; // owning device + std::vector<route_bw> m_bw_routes; + std::vector<route_fw> m_fw_routes; + std::vector<sound_stream *> m_dependant_streams; + + // buffers + std::vector<std::vector<sample_t>> m_input_buffer; + emu::detail::output_buffer_flat<sample_t> m_output_buffer; + attotime m_sync_time; + s32 m_samples_to_update; + + // gains + std::vector<float> m_input_channel_gain; + std::vector<float> m_output_channel_gain; + std::vector<float> m_user_output_channel_gain; + float m_user_output_gain; + + // general information + u32 m_sample_rate; // current sample rate + u32 m_input_count; + u32 m_output_count; + bool m_input_adaptive; // adaptive stream that runs at the sample rate of its input + bool m_output_adaptive; // adaptive stream that runs at the sample rate of its output + bool m_synchronous; // synchronous stream that runs at the rate of its input + bool m_started; + bool m_in_update; + emu_timer *m_sync_timer; // update timer for synchronous streams // callback information - stream_update_delegate m_callback; // callback function + stream_update_delegate m_callback; // update callback function }; -// ======================> sound_manager - class sound_manager { friend class sound_stream; @@ -186,6 +377,32 @@ class sound_manager static const attotime STREAMS_UPDATE_ATTOTIME; public: + using sample_t = sound_stream::sample_t; + + enum { + RESAMPLER_LOFI, + RESAMPLER_HQ + }; + + struct mapping { + struct node_mapping { + u32 m_node; + float m_db; + bool m_is_system_default; + }; + + struct channel_mapping { + u32 m_guest_channel; + u32 m_node; + u32 m_node_channel; + float m_db; + bool m_is_system_default; + }; + sound_io_device *m_dev; + std::vector<node_mapping> m_node_mappings; + std::vector<channel_mapping> m_channel_mappings; + }; + static constexpr int STREAMS_UPDATE_FREQUENCY = 50; // construction/destruction @@ -194,59 +411,259 @@ public: // getters running_machine &machine() const { return m_machine; } - int attenuation() const { return m_attenuation; } const std::vector<std::unique_ptr<sound_stream>> &streams() const { return m_stream_list; } - attotime last_update() const { return m_last_update; } - attoseconds_t update_attoseconds() const { return m_update_attoseconds; } - int sample_count() const { return m_samples_this_update; } - void samples(s16 *buffer); + int unique_id() { return m_unique_id++; } - // stream creation - sound_stream *stream_alloc(device_t &device, int inputs, int outputs, int sample_rate, stream_update_delegate callback = stream_update_delegate()); + const typename osd::audio_info &get_osd_info() const { return m_osd_info; } + const std::vector<mapping> &get_mappings() const { return m_mappings; } - // global controls - void start_recording(); + // allocate a new stream + sound_stream *stream_alloc(device_t &device, u32 inputs, u32 outputs, u32 sample_rate, stream_update_delegate callback, sound_stream_flags flags); + + // WAV recording + bool is_recording() const { return bool(m_wavfile); } + bool start_recording(); + bool start_recording(std::string_view filename); void stop_recording(); - void set_attenuation(int attenuation); - void ui_mute(bool turn_off = true) { mute(turn_off, MUTE_REASON_UI); } - void debugger_mute(bool turn_off = true) { mute(turn_off, MUTE_REASON_DEBUGGER); } - void system_mute(bool turn_off = true) { mute(turn_off, MUTE_REASON_SYSTEM); } - void system_enable(bool turn_on = true) { mute(!turn_on, MUTE_REASON_SYSTEM); } + u32 outputs_count() const { return m_outputs_count; } + + // manage the sound_io mapping and volume configuration + void config_add_sound_io_connection_node(sound_io_device *dev, std::string name, float db); + void config_add_sound_io_connection_default(sound_io_device *dev, float db); + void config_remove_sound_io_connection_node(sound_io_device *dev, std::string name); + void config_remove_sound_io_connection_default(sound_io_device *dev); + void config_set_volume_sound_io_connection_node(sound_io_device *dev, std::string name, float db); + void config_set_volume_sound_io_connection_default(sound_io_device *dev, float db); + void config_add_sound_io_channel_connection_node(sound_io_device *dev, u32 guest_channel, std::string name, u32 node_channel, float db); + void config_add_sound_io_channel_connection_default(sound_io_device *dev, u32 guest_channel, u32 node_channel, float db); + void config_remove_sound_io_channel_connection_node(sound_io_device *dev, u32 guest_channel, std::string name, u32 node_channel); + void config_remove_sound_io_channel_connection_default(sound_io_device *dev, u32 guest_channel, u32 node_channel); + void config_set_volume_sound_io_channel_connection_node(sound_io_device *dev, u32 guest_channel, std::string name, u32 node_channel, float db); + void config_set_volume_sound_io_channel_connection_default(sound_io_device *dev, u32 guest_channel, u32 node_channel, float db); + + // mute sound for one of various independent reasons + bool muted() const { return bool(m_muted); } + bool ui_mute() const { return bool(m_muted & MUTE_REASON_UI); } + bool debugger_mute() const { return bool(m_muted & MUTE_REASON_DEBUGGER); } + bool system_mute() const { return bool(m_muted & MUTE_REASON_SYSTEM); } + void ui_mute(bool turn_off) { mute(turn_off, MUTE_REASON_UI); } + void debugger_mute(bool turn_off) { mute(turn_off, MUTE_REASON_DEBUGGER); } + void system_mute(bool turn_off) { mute(turn_off, MUTE_REASON_SYSTEM); } + + // master gain + float master_gain() const { return m_master_gain; } + void set_master_gain(float gain) { m_master_gain = gain; } + + void before_devices_init(); + void after_devices_init(); + void postload(); + + void input_get(int m_id, sound_stream &stream); + void output_push(int m_id, sound_stream &stream); + const audio_resampler *get_resampler(u32 fs, u32 ft); + + u32 effect_chains() const { return m_speakers.size(); } + std::string effect_chain_tag(s32 index) const; + std::vector<audio_effect *> effect_chain(s32 index) const; + std::vector<audio_effect *> default_effect_chain() const; + void default_effect_changed(u32 entry); + + void mapping_update(); + + const char *resampler_type_names(u32 type) const; - // user gain controls - bool indexed_mixer_input(int index, mixer_input &info) const; + u32 resampler_type() const { return m_resampler_type; } + double resampler_hq_latency() const { return m_resampler_hq_latency; } + u32 resampler_hq_length() const { return m_resampler_hq_length; } + u32 resampler_hq_phases() const { return m_resampler_hq_phases; } + + void set_resampler_type(u32 type); + void set_resampler_hq_latency(double latency); + void set_resampler_hq_length(u32 length); + void set_resampler_hq_phases(u32 phases); private: - // internal helpers + struct effect_step { + std::unique_ptr<audio_effect> m_effect; + emu::detail::output_buffer_flat<sample_t> m_buffer; + effect_step(u32 buffer_size, u32 channels); + }; + + struct mixing_step { + enum : u32 { CLEAR, COPY, ADD }; + u32 m_mode; + u32 m_osd_index; + u32 m_osd_channel; + u32 m_device_index; + u32 m_device_channel; + float m_linear_volume; + }; + + struct speaker_info { + speaker_device &m_dev; + sound_stream *m_stream; + u32 m_channels; + u32 m_first_output; + + emu::detail::output_buffer_flat<sample_t> m_buffer; + + std::vector<effect_step> m_effects; + + speaker_info(speaker_device &dev, u32 rate, u32 first_output); + }; + + struct microphone_info { + microphone_device &m_dev; + u32 m_channels; + + std::vector<mixing_step> m_input_mixing_steps; // actions to take to fill the buffer + std::vector<sample_t> m_buffer; + microphone_info(microphone_device &dev); + }; + + struct osd_stream { + u32 m_id; + u32 m_node; + std::string m_node_name; + u32 m_channels; + u32 m_rate; + u32 m_unused_channels_mask; + bool m_is_system_default; + bool m_is_channel_mapping; + sound_io_device *m_dev; + std::vector<float> m_volumes; + + osd_stream(u32 node, std::string node_name, u32 channels, u32 rate, bool is_system_default, sound_io_device *dev) : + m_id(0), + m_node(node), + m_node_name(node_name), + m_channels(channels), + m_rate(rate), + m_unused_channels_mask(util::make_bitmask<u32>(channels)), + m_is_system_default(is_system_default), + m_is_channel_mapping(false), + m_dev(dev) + { } + }; + + struct osd_input_stream : public osd_stream { + emu::detail::output_buffer_interleaved<s16> m_buffer; + osd_input_stream(u32 node, std::string node_name, u32 channels, u32 rate, bool is_system_default, sound_io_device *dev) : + osd_stream(node, node_name, channels, rate, is_system_default, dev), + m_buffer(rate, channels) + { } + }; + + struct osd_output_stream : public osd_stream { + u64 m_last_sync; + u32 m_samples; + std::vector<s16> m_buffer; + osd_output_stream(u32 node, std::string node_name, u32 channels, u32 rate, bool is_system_default, sound_io_device *dev) : + osd_stream(node, node_name, channels, rate, is_system_default, dev), + m_last_sync(0), + m_samples(0), + m_buffer(channels*rate, 0) + { } + }; + + struct config_mapping { + std::string m_name; + // "" to indicates default node + std::vector<std::pair<std::string, float>> m_node_mappings; + std::vector<std::tuple<u32, std::string, u32, float>> m_channel_mappings; + }; + + // set/reset the mute state for the given reason void mute(bool mute, u8 reason); + + // reset all sound chips void reset(); + + // pause/resume sound output void pause(); void resume(); - void config_load(config_type cfg_type, util::xml::data_node const *parentnode); - void config_save(config_type cfg_type, util::xml::data_node *parentnode); - - void update(void *ptr = nullptr, s32 param = 0); - // internal state - running_machine & m_machine; // reference to our machine - emu_timer * m_update_timer; // timer to drive periodic updates + // handle configuration load/save + void config_load(config_type cfg_type, config_level cfg_lvl, util::xml::data_node const *parentnode); + void config_save(config_type cfg_type, util::xml::data_node *parentnode); - u32 m_finalmix_leftover; - std::vector<s16> m_finalmix; - std::vector<s32> m_leftmix; - std::vector<s32> m_rightmix; - int m_samples_this_update; + // periodic sound update, called STREAMS_UPDATE_FREQUENCY per second + void update(s32); + + // handle mixing mapping update if needed + static std::vector<u32> find_channel_mapping(const std::array<double, 3> &position, const osd::audio_info::node_info *node); + void startup_cleanups(); + void streams_update(); + template<bool is_output, typename S> void apply_osd_changes(std::vector<S> &streams); + void osd_information_update(); + void generate_mapping(); + void update_osd_streams(); + void update_osd_input(); + void speakers_update(attotime endtime); + void rebuild_all_resamplers(); + void run_effects(); + + u64 rate_and_time_to_index(attotime time, u32 sample_rate) const; + u64 rate_and_last_sync_to_index(u32 sample_rate) const { return rate_and_time_to_index(m_last_sync_time, sample_rate); } + + // manage the sound_io mapping and volume configuration, + // but don't change generation because we're in the update process + + config_mapping &config_get_sound_io(sound_io_device *dev); + void internal_config_add_sound_io_connection_node(sound_io_device *dev, std::string name, float db); + void internal_config_add_sound_io_connection_default(sound_io_device *dev, float db); + void internal_config_remove_sound_io_connection_node(sound_io_device *dev, std::string name); + void internal_config_remove_sound_io_connection_default(sound_io_device *dev); + void internal_config_set_volume_sound_io_connection_node(sound_io_device *dev, std::string name, float db); + void internal_config_set_volume_sound_io_connection_default(sound_io_device *dev, float db); + void internal_config_add_sound_io_channel_connection_node(sound_io_device *dev, u32 guest_channel, std::string name, u32 node_channel, float db); + void internal_config_add_sound_io_channel_connection_default(sound_io_device *dev, u32 guest_channel, u32 node_channel, float db); + void internal_config_remove_sound_io_channel_connection_node(sound_io_device *dev, u32 guest_channel, std::string name, u32 node_channel); + void internal_config_remove_sound_io_channel_connection_default(sound_io_device *dev, u32 guest_channel, u32 node_channel); + void internal_config_set_volume_sound_io_channel_connection_node(sound_io_device *dev, u32 guest_channel, std::string name, u32 node_channel, float db); + void internal_config_set_volume_sound_io_channel_connection_default(sound_io_device *dev, u32 guest_channel, u32 node_channel, float db); - u8 m_muted; - int m_attenuation; - int m_nosound_mode; - wav_file * m_wavfile; + // internal state + running_machine &m_machine; // reference to the running machine + emu_timer *m_update_timer; // timer that runs the update function + attotime m_last_sync_time; + std::vector<speaker_info> m_speakers; + std::vector<microphone_info> m_microphones; + + std::vector<s16> m_record_buffer; // pre-effects speaker samples for recording + u32 m_record_samples; // how many samples for the next update + osd::audio_info m_osd_info; // current state of the osd information + std::vector<mapping> m_mappings; // current state of the mappings + std::vector<osd_input_stream> m_osd_input_streams; // currently active osd streams + std::vector<osd_output_stream> m_osd_output_streams; // currently active osd streams + std::vector<mixing_step> m_output_mixing_steps; // actions to take to fill the osd streams buffers + std::vector<config_mapping> m_configs; // mapping user configuration + + std::mutex m_effects_mutex; + std::condition_variable m_effects_condition; + std::unique_ptr<std::thread> m_effects_thread; + std::vector<std::unique_ptr<audio_effect>> m_default_effects; + bool m_effects_done; + + float m_master_gain; + + std::map<std::pair<u32, u32>, std::unique_ptr<audio_resampler>> m_resamplers; + + u8 m_muted; // bitmask of muting reasons + bool m_nosound_mode; // true if we're in "nosound" mode + int m_unique_id; // unique ID used for stream identification + util::wav_file_ptr m_wavfile; // WAV file for streaming // streams data - std::vector<std::unique_ptr<sound_stream>> m_stream_list; // list of streams - attoseconds_t m_update_attoseconds; // attoseconds between global updates - attotime m_last_update; // last update time + std::vector<std::unique_ptr<sound_stream>> m_stream_list; // list of streams + std::vector<sound_stream *> m_ordered_streams; // Streams in update order + u32 m_outputs_count; + + // resampler data + u32 m_resampler_type; + double m_resampler_hq_latency; + u32 m_resampler_hq_length, m_resampler_hq_phases; }; diff --git a/src/emu/speaker.cpp b/src/emu/speaker.cpp index de2ad1127a0..d57f9119c24 100644 --- a/src/emu/speaker.cpp +++ b/src/emu/speaker.cpp @@ -5,128 +5,90 @@ speaker.cpp Speaker output sound device. + Microphone input sound device. ***************************************************************************/ #include "emu.h" +#include "emuopts.h" #include "speaker.h" -//************************************************************************** -// GLOBAL VARIABLES -//************************************************************************** - -// device type definition DEFINE_DEVICE_TYPE(SPEAKER, speaker_device, "speaker", "Speaker") +DEFINE_DEVICE_TYPE(MICROPHONE, microphone_device, "microphone", "Microphone") + +const sound_io_device::position_name_mapping sound_io_device::position_name_mappings[] = { + { 0.0, 0.0, 1.0, "Front center" }, + { -0.2, 0.0, 1.0, "Front left" }, + { 0.0, -0.5, 1.0, "Front floor" }, + { 0.2, 0.0, 1.0, "Front right" }, + { 0.0, 0.0, -0.5, "Rear center" }, + { -0.2, 0.0, -0.5, "Rear left" }, + { 0.2, 0.0, -0.5, "Read right" }, + { 0.0, 0.0, -0.1, "Headrest center" }, + { -0.1, 0.0, -0.1, "Headrest left" }, + { 0.1, 0.0, -0.1, "Headrest right" }, + { 0.0, -0.5, 0.0, "Seat" }, + { 0.0, -0.2, 0.1, "Backrest" }, + { } +}; + +std::string sound_io_device::get_position_name(u32 channel) const +{ + for(unsigned int i = 0; position_name_mappings[i].m_name; i++) + if(m_positions[channel][0] == position_name_mappings[i].m_x && m_positions[channel][1] == position_name_mappings[i].m_y && m_positions[channel][2] == position_name_mappings[i].m_z) + return position_name_mappings[i].m_name; + return util::string_format("#%d", channel); +} +sound_io_device &sound_io_device::set_position(u32 channel, double x, double y, double z) +{ + if(channel >= m_positions.size()) + fatalerror("%s: Requested channel %d on %d channel device\n", tag(), channel, m_positions.size()); + m_positions[channel][0] = x; + m_positions[channel][1] = y; + m_positions[channel][2] = z; + return *this; +} - -//************************************************************************** -// LIVE SPEAKER DEVICE -//************************************************************************** - -//------------------------------------------------- -// speaker_device - constructor -//------------------------------------------------- - -speaker_device::speaker_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) - : device_t(mconfig, SPEAKER, tag, owner, clock) - , device_mixer_interface(mconfig, *this) - , m_x(0.0) - , m_y(0.0) - , m_z(0.0) -#ifdef MAME_DEBUG - , m_max_sample(0) - , m_clipped_samples(0) - , m_total_samples(0) -#endif +sound_io_device::sound_io_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 channels) : + device_t(mconfig, type, tag, owner, 0), + device_sound_interface(mconfig, *this), + m_positions(channels ? channels : 1) { } -//------------------------------------------------- -// ~speaker_device - destructor -//------------------------------------------------- +sound_io_device::~sound_io_device() +{ +} speaker_device::~speaker_device() { -#ifdef MAME_DEBUG - // log the maximum sample values for all speakers - if (m_max_sample > 0) - osd_printf_debug("Speaker \"%s\" - max = %d (gain *= %f) - %d%% samples clipped\n", tag(), m_max_sample, 32767.0 / (m_max_sample ? m_max_sample : 1), (int)((double)m_clipped_samples * 100.0 / m_total_samples)); -#endif } +microphone_device::~microphone_device() +{ +} -//------------------------------------------------- -// mix - mix in samples from the speaker's stream -//------------------------------------------------- -void speaker_device::mix(s32 *leftmix, s32 *rightmix, int &samples_this_update, bool suppress) +void speaker_device::device_start() { - // skip if no stream - if (m_mixer_stream == nullptr) - return; - - // update the stream, getting the start/end pointers around the operation - int numsamples; - const stream_sample_t *stream_buf = m_mixer_stream->output_since_last_update(0, numsamples); - - // set or assert that all streams have the same count - if (samples_this_update == 0) - { - samples_this_update = numsamples; - - // reset the mixing streams - memset(leftmix, 0, samples_this_update * sizeof(*leftmix)); - memset(rightmix, 0, samples_this_update * sizeof(*rightmix)); - } - assert(samples_this_update == numsamples); - -#ifdef MAME_DEBUG - // debug version: keep track of the maximum sample - for (int sample = 0; sample < samples_this_update; sample++) - { - if (stream_buf[sample] > m_max_sample) - m_max_sample = stream_buf[sample]; - else if (-stream_buf[sample] > m_max_sample) - m_max_sample = -stream_buf[sample]; - if (stream_buf[sample] > 32767 || stream_buf[sample] < -32768) - m_clipped_samples++; - m_total_samples++; - } -#endif - - // mix if sound is enabled - if (!suppress) - { - // if the speaker is centered, send to both left and right - if (m_x == 0) - for (int sample = 0; sample < samples_this_update; sample++) - { - leftmix[sample] += stream_buf[sample]; - rightmix[sample] += stream_buf[sample]; - } - - // if the speaker is to the left, send only to the left - else if (m_x < 0) - for (int sample = 0; sample < samples_this_update; sample++) - leftmix[sample] += stream_buf[sample]; - - // if the speaker is to the right, send only to the right - else - for (int sample = 0; sample < samples_this_update; sample++) - rightmix[sample] += stream_buf[sample]; - } + m_stream = stream_alloc(m_positions.size(), 0, machine().sample_rate()); } +void microphone_device::device_start() +{ + m_stream = stream_alloc(0, m_positions.size(), machine().sample_rate()); +} -//------------------------------------------------- -// device_start - handle device startup -//------------------------------------------------- +void speaker_device::sound_stream_update(sound_stream &stream) +{ + machine().sound().output_push(m_id, stream); +} -void speaker_device::device_start() +void microphone_device::sound_stream_update(sound_stream &stream) { - // dummy save to make device.c happy + machine().sound().input_get(m_id, stream); } diff --git a/src/emu/speaker.h b/src/emu/speaker.h index 47ad0f6b7d2..4674c0bf08d 100644 --- a/src/emu/speaker.h +++ b/src/emu/speaker.h @@ -5,8 +5,9 @@ speaker.h Speaker output sound device. + Microphone input sound device. - Speakers have (x, y, z) coordinates in 3D space: + They have (x, y, z) coordinates in 3D space: * Observer is at position (0, 0, 0) * Positive x is to the right of the observer * Negative x is to the left of the observer @@ -15,9 +16,6 @@ * Positive z is in front of the observer * Negative z is behind the observer - Currently, MAME only considers the sign of the x coordinate (not its - magnitude), and completely ignores the y and z coordinates. - ***************************************************************************/ #ifndef MAME_EMU_SPEAKER_H @@ -32,6 +30,7 @@ // device type definition DECLARE_DEVICE_TYPE(SPEAKER, speaker_device) +DECLARE_DEVICE_TYPE(MICROPHONE, microphone_device) @@ -39,53 +38,103 @@ DECLARE_DEVICE_TYPE(SPEAKER, speaker_device) // TYPE DEFINITIONS //************************************************************************** -// ======================> speaker_device +class sound_io_device : public device_t, public device_sound_interface +{ +public: + virtual ~sound_io_device(); + + // configuration helpers + sound_io_device &set_position(u32 channel, double x, double y, double z); + sound_io_device &front_center(u32 channel = 0) { return set_position(channel, 0.0, 0.0, 1.0); } + sound_io_device &front_left(u32 channel = 0) { return set_position(channel, -0.2, 0.0, 1.0); } + sound_io_device &front_floor(u32 channel = 0) { return set_position(channel, 0.0, -0.5, 1.0); } + sound_io_device &front_right(u32 channel = 0) { return set_position(channel, 0.2, 0.0, 1.0); } + sound_io_device &rear_center(u32 channel = 0) { return set_position(channel, 0.0, 0.0, -0.5); } + sound_io_device &rear_left(u32 channel = 0) { return set_position(channel, -0.2, 0.0, -0.5); } + sound_io_device &rear_right(u32 channel = 0) { return set_position(channel, 0.2, 0.0, -0.5); } + sound_io_device &headrest_center(u32 channel = 0) { return set_position(channel, 0.0, 0.0, -0.1); } + sound_io_device &headrest_left(u32 channel = 0) { return set_position(channel, -0.1, 0.0, -0.1); } + sound_io_device &headrest_right(u32 channel = 0) { return set_position(channel, 0.1, 0.0, -0.1); } + sound_io_device &seat(u32 channel = 0) { return set_position(channel, 0.0, -0.5, 0.0); } + sound_io_device &backrest(u32 channel = 0) { return set_position(channel, 0.0, -0.2, 0.1); } + sound_io_device &front() { return front_left(0).front_right(1); } + sound_io_device &rear() { return rear_left(0).rear_right(1); } + sound_io_device &corners() { return front_left(0).front_right(1).rear_left(2).rear_right(3); } + int channels() const { return m_positions.size(); } + std::array<double, 3> get_position(u32 channel) const { return m_positions[channel]; } + std::string get_position_name(u32 channel) const; + + virtual bool is_output() const = 0; + void set_id(int id) { m_id = id; } + int get_id() const { return m_id; } + + sound_stream *stream() const { return m_stream; } + +protected: + struct position_name_mapping { + double m_x, m_y, m_z; + const char *m_name; + }; + + static const position_name_mapping position_name_mappings[]; + + // configuration state + std::vector<std::array<double, 3>> m_positions; + sound_stream *m_stream; + int m_id; -class speaker_device : public device_t, public device_mixer_interface + sound_io_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, double x, double y, double z) + : sound_io_device(mconfig, type, tag, owner, 1) + { + set_position(0, x, y, z); + } + sound_io_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 channels); // Collides with clock, but not important +}; + +class speaker_device : public sound_io_device { public: // construction/destruction speaker_device(const machine_config &mconfig, const char *tag, device_t *owner, double x, double y, double z) - : speaker_device(mconfig, tag, owner, 0) - { - set_position(x, y, z); - } - speaker_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock = 0); - virtual ~speaker_device(); + : sound_io_device(mconfig, SPEAKER, tag, owner, x, y, z) {} + speaker_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 channels = 1) + : sound_io_device(mconfig, SPEAKER, tag, owner, channels) {} - // inline configuration helpers - speaker_device &set_position(double x, double y, double z) { m_x = x; m_y = y; m_z = z; return *this; } - speaker_device &front_center() { set_position(0.0, 0.0, 1.0); return *this; } - speaker_device &front_left() { set_position(-0.2, 0.0, 1.0); return *this; } - speaker_device &front_right() { set_position(0.2, 0.0, 1.0); return *this; } - speaker_device &rear_center() { set_position(0.0, 0.0, -0.5); return *this; } - speaker_device &rear_left() { set_position(-0.2, 0.0, -0.5); return *this; } - speaker_device &rear_right() { set_position(0.2, 0.0, -0.5); return *this; } - speaker_device &subwoofer() { set_position(0.0, 0.0, 0.0); return *this; } + virtual ~speaker_device(); - // internally for use by the sound system - void mix(s32 *leftmix, s32 *rightmix, int &samples_this_update, bool suppress); + virtual bool is_output() const override { return true; } protected: + // device-level overrides virtual void device_start() override ATTR_COLD; - // inline configuration state - double m_x; - double m_y; - double m_z; - - // internal state -#ifdef MAME_DEBUG - s32 m_max_sample; // largest sample value we've seen - s32 m_clipped_samples; // total number of clipped samples - s32 m_total_samples; // total number of samples -#endif + virtual void sound_stream_update(sound_stream &stream) override; }; +class microphone_device : public sound_io_device +{ +public: + // construction/destruction + microphone_device(const machine_config &mconfig, const char *tag, device_t *owner, double x, double y, double z) + : sound_io_device(mconfig, MICROPHONE, tag, owner, x, y, z) {} + microphone_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 channels = 1) + : sound_io_device(mconfig, MICROPHONE, tag, owner, channels) {} + + virtual ~microphone_device(); + + virtual bool is_output() const override { return false; } + +protected: + + // device-level overrides + virtual void device_start() override ATTR_COLD; + + virtual void sound_stream_update(sound_stream &stream) override; +}; -// speaker device iterator -typedef device_type_iterator<speaker_device> speaker_device_iterator; +using speaker_device_enumerator = device_type_enumerator<speaker_device>; +using microphone_device_enumerator = device_type_enumerator<microphone_device>; #endif // MAME_EMU_SPEAKER_H diff --git a/src/emu/tilemap.cpp b/src/emu/tilemap.cpp index 083b4b4ef5a..8c4e20e158f 100644 --- a/src/emu/tilemap.cpp +++ b/src/emu/tilemap.cpp @@ -336,7 +336,9 @@ inline void tilemap_t::scanline_draw_masked_rgb32_alpha(u32 *dest, const u16 *so // tilemap_t - constructor //------------------------------------------------- -tilemap_t::tilemap_t() +tilemap_t::tilemap_t(device_t &owner) + : m_mapper(owner) + , m_tile_get_info(owner) { // until init() is called, data is floating; this is deliberate } @@ -346,11 +348,18 @@ tilemap_t::tilemap_t() // init - initialize the tilemap //------------------------------------------------- -tilemap_t &tilemap_t::init(tilemap_manager &manager, device_gfx_interface &decoder, tilemap_get_info_delegate tile_get_info, tilemap_mapper_delegate mapper, u16 tilewidth, u16 tileheight, u32 cols, u32 rows) +void tilemap_t::init_common( + tilemap_manager &manager, + device_gfx_interface &decoder, + tilemap_get_info_delegate tile_get_info, + u16 tilewidth, + u16 tileheight, + u32 cols, + u32 rows) { // populate managers and devices m_manager = &manager; - m_device = dynamic_cast<tilemap_device *>(this); + m_device = dynamic_cast<device_t *>(this); m_palette = &decoder.palette(); m_next = nullptr; m_user_data = nullptr; @@ -363,9 +372,6 @@ tilemap_t &tilemap_t::init(tilemap_manager &manager, device_gfx_interface &decod m_width = cols * tilewidth; m_height = rows * tileheight; - // populate logical <-> memory mappings - m_mapper = mapper; - // initialize tile information geters m_tile_get_info = tile_get_info; @@ -426,6 +432,52 @@ tilemap_t &tilemap_t::init(tilemap_manager &manager, device_gfx_interface &decod // reset everything after a load machine().save().register_postload(save_prepost_delegate(FUNC(tilemap_t::postload), this)); +} + +tilemap_t &tilemap_t::init( + tilemap_manager &manager, + device_gfx_interface &decoder, + tilemap_get_info_delegate tile_get_info, + tilemap_mapper_delegate mapper, + u16 tilewidth, + u16 tileheight, + u32 cols, + u32 rows) +{ + // populate logical <-> memory mappings + m_mapper = mapper; + + init_common(manager, decoder, tile_get_info, tilewidth, tileheight, cols, rows); + + return *this; +} + +tilemap_t &tilemap_t::init( + tilemap_manager &manager, + device_gfx_interface &decoder, + tilemap_get_info_delegate tile_get_info, + tilemap_standard_mapper mapper, + u16 tilewidth, + u16 tileheight, + u32 cols, + u32 rows) +{ + // populate logical <-> memory mappings + switch (mapper) + { + case TILEMAP_SCAN_ROWS: m_mapper.set(*this, FUNC(tilemap_t::scan_rows)); break; + case TILEMAP_SCAN_ROWS_FLIP_X: m_mapper.set(*this, FUNC(tilemap_t::scan_rows_flip_x)); break; + case TILEMAP_SCAN_ROWS_FLIP_Y: m_mapper.set(*this, FUNC(tilemap_t::scan_rows_flip_y)); break; + case TILEMAP_SCAN_ROWS_FLIP_XY: m_mapper.set(*this, FUNC(tilemap_t::scan_rows_flip_xy)); break; + case TILEMAP_SCAN_COLS: m_mapper.set(*this, FUNC(tilemap_t::scan_cols)); break; + case TILEMAP_SCAN_COLS_FLIP_X: m_mapper.set(*this, FUNC(tilemap_t::scan_cols_flip_x)); break; + case TILEMAP_SCAN_COLS_FLIP_Y: m_mapper.set(*this, FUNC(tilemap_t::scan_cols_flip_y)); break; + case TILEMAP_SCAN_COLS_FLIP_XY: m_mapper.set(*this, FUNC(tilemap_t::scan_cols_flip_xy)); break; + default: throw emu_fatalerror("Tilemap init unknown mapper %d", mapper); + } + + init_common(manager, decoder, tile_get_info, tilewidth, tileheight, cols, rows); + return *this; } @@ -720,7 +772,7 @@ void tilemap_t::pixmap_update() if (m_all_tiles_clean) return; -g_profiler.start(PROFILER_TILEMAP_DRAW); + auto profile = g_profiler.start(PROFILER_TILEMAP_DRAW); // flush the dirty state to all tiles as appropriate realize_all_dirty_tiles(); @@ -734,8 +786,6 @@ g_profiler.start(PROFILER_TILEMAP_DRAW); // mark it all clean m_all_tiles_clean = true; - -g_profiler.stop(); } @@ -745,7 +795,7 @@ g_profiler.stop(); void tilemap_t::tile_update(logical_index logindex, u32 col, u32 row) { -g_profiler.start(PROFILER_TILEMAP_UPDATE); + auto profile = g_profiler.start(PROFILER_TILEMAP_UPDATE); // call the get info callback for the associated memory index tilemap_memory_index memindex = m_logical_to_memory[logindex]; @@ -770,8 +820,6 @@ g_profiler.start(PROFILER_TILEMAP_UPDATE); m_gfx_used |= 1 << m_tileinfo.gfxnum; m_gfx_dirtyseq[m_tileinfo.gfxnum] = m_tileinfo.decoder->gfx(m_tileinfo.gfxnum)->dirtyseq(); } - -g_profiler.stop(); } @@ -808,8 +856,8 @@ u8 tilemap_t::tile_draw(const u8 *pendata, u32 x0, u32 y0, u32 palette_base, u8 u8 andmask = ~0, ormask = 0; for (u16 ty = 0; ty < m_tileheight; ty++) { - u16 *pixptr = &m_pixmap.pix16(y0, x0); - u8 *flagsptr = &m_flagsmap.pix8(y0, x0); + u16 *pixptr = &m_pixmap.pix(y0, x0); + u8 *flagsptr = &m_flagsmap.pix(y0, x0); // pre-advance to the next row y0 += dy0; @@ -861,7 +909,7 @@ u8 tilemap_t::tile_apply_bitmask(const u8 *maskdata, u32 x0, u32 y0, u8 category for (u16 ty = 0; ty < m_tileheight; ty++) { // pre-advance to the next row - u8 *flagsptr = &m_flagsmap.pix8(y0, x0); + u8 *flagsptr = &m_flagsmap.pix(y0, x0); y0 += dy0; // anywhere the bitmask is 0 should be transparent @@ -945,12 +993,13 @@ void tilemap_t::draw_common(screen_device &screen, _BitmapClass &dest, const rec if (!m_enable) return; -g_profiler.start(PROFILER_TILEMAP_DRAW); + auto profile = g_profiler.start(PROFILER_TILEMAP_DRAW); + // configure the blit parameters based on the input parameters - assert(dest.cliprect().contains(cliprect)); - assert(screen.cliprect().contains(cliprect)); blit_parameters blit; configure_blit_parameters(blit, screen.priority(), cliprect, flags, priority, priority_mask); + assert(dest.cliprect().contains(cliprect)); + assert(screen.cliprect().contains(cliprect) || blit.tilemap_priority_code == 0xff00); // flush the dirty state to all tiles as appropriate realize_all_dirty_tiles(); @@ -1043,7 +1092,6 @@ g_profiler.start(PROFILER_TILEMAP_DRAW); } } } -g_profiler.stop(); } void tilemap_t::draw(screen_device &screen, bitmap_ind16 &dest, const rectangle &cliprect, u32 flags, u8 priority, u8 priority_mask) @@ -1082,19 +1130,19 @@ void tilemap_t::draw_roz_common(screen_device &screen, _BitmapClass &dest, const return; } -g_profiler.start(PROFILER_TILEMAP_DRAW_ROZ); + auto profile = g_profiler.start(PROFILER_TILEMAP_DRAW_ROZ); + // configure the blit parameters - assert(dest.cliprect().contains(cliprect)); - assert(screen.cliprect().contains(cliprect)); blit_parameters blit; configure_blit_parameters(blit, screen.priority(), cliprect, flags, priority, priority_mask); + assert(dest.cliprect().contains(cliprect)); + assert(screen.cliprect().contains(cliprect) || blit.tilemap_priority_code == 0xff00); // get the full pixmap for the tilemap pixmap(); // then do the roz copy draw_roz_core(screen, dest, blit, startx, starty, incxx, incxy, incyx, incyy, wraparound); -g_profiler.stop(); } void tilemap_t::draw_roz(screen_device &screen, bitmap_ind16 &dest, const rectangle &cliprect, @@ -1130,7 +1178,16 @@ void tilemap_t::draw_instance(screen_device &screen, _BitmapClass &dest, const b // look up priority and destination base addresses for y1 bitmap_ind8 &priority_bitmap = *blit.priority; - u8 *priority_baseaddr = &priority_bitmap.pix8(y1, xpos); + u8 *priority_baseaddr = nullptr; + int prio_rowpixels = 0; + if (priority_bitmap.valid()) + { + prio_rowpixels = priority_bitmap.rowpixels(); + priority_baseaddr = &priority_bitmap.pix(y1, xpos); + } + else + assert((blit.tilemap_priority_code & 0xffff) == 0xff00); + typename _BitmapClass::pixel_t *dest_baseaddr = nullptr; int dest_rowpixels = 0; if (dest.valid()) @@ -1146,8 +1203,8 @@ void tilemap_t::draw_instance(screen_device &screen, _BitmapClass &dest, const b y2 -= ypos; // get tilemap pixels - const u16 *source_baseaddr = &m_pixmap.pix16(y1); - const u8 *mask_baseaddr = &m_flagsmap.pix8(y1); + const u16 *source_baseaddr = &m_pixmap.pix(y1); + const u8 *mask_baseaddr = &m_flagsmap.pix(y1); // get start/stop columns, rounding outward int mincol = x1 / m_tilewidth; @@ -1208,7 +1265,7 @@ void tilemap_t::draw_instance(screen_device &screen, _BitmapClass &dest, const b { const u16 *source0 = source_baseaddr + x_start; typename _BitmapClass::pixel_t *dest0 = dest_baseaddr + x_start; - u8 *pmap0 = priority_baseaddr + x_start; + u8 *pmap0 = priority_baseaddr ? (priority_baseaddr + x_start) : nullptr; // if we were opaque, use the opaque renderer if (prev_trans == WHOLLY_OPAQUE) @@ -1226,7 +1283,7 @@ void tilemap_t::draw_instance(screen_device &screen, _BitmapClass &dest, const b dest0 += dest_rowpixels; source0 += m_pixmap.rowpixels(); - pmap0 += priority_bitmap.rowpixels(); + pmap0 += prio_rowpixels; } } @@ -1248,7 +1305,7 @@ void tilemap_t::draw_instance(screen_device &screen, _BitmapClass &dest, const b dest0 += dest_rowpixels; source0 += m_pixmap.rowpixels(); mask0 += m_flagsmap.rowpixels(); - pmap0 += priority_bitmap.rowpixels(); + pmap0 += prio_rowpixels; } } } @@ -1263,7 +1320,7 @@ void tilemap_t::draw_instance(screen_device &screen, _BitmapClass &dest, const b break; // advance to the next row on all our bitmaps - priority_baseaddr += priority_bitmap.rowpixels() * (nexty - y); + priority_baseaddr += prio_rowpixels * (nexty - y); source_baseaddr += m_pixmap.rowpixels() * (nexty - y); mask_baseaddr += m_flagsmap.rowpixels() * (nexty - y); dest_baseaddr += dest_rowpixels * (nexty - y); @@ -1303,7 +1360,7 @@ void tilemap_t::draw_roz_core(screen_device &screen, _BitmapClass &destbitmap, c const int ymask = m_pixmap.height() - 1; const int widthshifted = m_pixmap.width() << 16; const int heightshifted = m_pixmap.height() << 16; - u32 priority = blit.tilemap_priority_code; + const u32 priority = blit.tilemap_priority_code; u8 mask = blit.mask; u8 value = blit.value; u8 alpha = blit.alpha; @@ -1344,9 +1401,9 @@ void tilemap_t::draw_roz_core(screen_device &screen, _BitmapClass &destbitmap, c u32 cy = starty >> 16; // get source and priority pointers - u8 *pri = &priority_bitmap.pix8(sy, sx); - const u16 *src = &m_pixmap.pix16(cy); - const u8 *maskptr = &m_flagsmap.pix8(cy); + u8 *pri = (priority != 0xff00) ? &priority_bitmap.pix(sy, sx) : nullptr; + const u16 *src = &m_pixmap.pix(cy); + const u8 *maskptr = &m_flagsmap.pix(cy); typename _BitmapClass::pixel_t *dest = &destbitmap.pix(sy, sx); // loop over columns @@ -1356,14 +1413,16 @@ void tilemap_t::draw_roz_core(screen_device &screen, _BitmapClass &destbitmap, c if ((maskptr[cx >> 16] & mask) == value) { ROZ_PLOT_PIXEL(src[cx >> 16]); - *pri = (*pri & (priority >> 8)) | priority; + if (priority != 0xff00) + *pri = (*pri & (priority >> 8)) | priority; } // advance in X cx += incxx; x++; ++dest; - pri++; + if (priority != 0xff00) + pri++; } } @@ -1386,16 +1445,17 @@ void tilemap_t::draw_roz_core(screen_device &screen, _BitmapClass &destbitmap, c // get dest and priority pointers typename _BitmapClass::pixel_t *dest = &destbitmap.pix(sy, sx); - u8 *pri = &priority_bitmap.pix8(sy, sx); + u8 *pri = (priority != 0xff00) ? &priority_bitmap.pix(sy, sx) : nullptr; // loop over columns while (x <= ex) { // plot if we match the mask - if ((m_flagsmap.pix8((cy >> 16) & ymask, (cx >> 16) & xmask) & mask) == value) + if ((m_flagsmap.pix((cy >> 16) & ymask, (cx >> 16) & xmask) & mask) == value) { - ROZ_PLOT_PIXEL(m_pixmap.pix16((cy >> 16) & ymask, (cx >> 16) & xmask)); - *pri = (*pri & (priority >> 8)) | priority; + ROZ_PLOT_PIXEL(m_pixmap.pix((cy >> 16) & ymask, (cx >> 16) & xmask)); + if (priority != 0xff00) + *pri = (*pri & (priority >> 8)) | priority; } // advance in X @@ -1403,7 +1463,8 @@ void tilemap_t::draw_roz_core(screen_device &screen, _BitmapClass &destbitmap, c cy += incxy; x++; ++dest; - pri++; + if (priority != 0xff00) + pri++; } // advance in Y @@ -1426,17 +1487,18 @@ void tilemap_t::draw_roz_core(screen_device &screen, _BitmapClass &destbitmap, c // get dest and priority pointers typename _BitmapClass::pixel_t *dest = &destbitmap.pix(sy, sx); - u8 *pri = &priority_bitmap.pix8(sy, sx); + u8 *pri = (priority != 0xff00) ? &priority_bitmap.pix(sy, sx) : nullptr; // loop over columns while (x <= ex) { // plot if we're within the bitmap and we match the mask if (cx < widthshifted && cy < heightshifted) - if ((m_flagsmap.pix8(cy >> 16, cx >> 16) & mask) == value) + if ((m_flagsmap.pix(cy >> 16, cx >> 16) & mask) == value) { - ROZ_PLOT_PIXEL(m_pixmap.pix16(cy >> 16, cx >> 16)); - *pri = (*pri & (priority >> 8)) | priority; + ROZ_PLOT_PIXEL(m_pixmap.pix(cy >> 16, cx >> 16)); + if (priority != 0xff00) + *pri = (*pri & (priority >> 8)) | priority; } // advance in X @@ -1444,7 +1506,8 @@ void tilemap_t::draw_roz_core(screen_device &screen, _BitmapClass &destbitmap, c cy += incxy; x++; ++dest; - pri++; + if (priority != 0xff00) + pri++; } // advance in Y @@ -1493,6 +1556,10 @@ void tilemap_t::draw_debug(screen_device &screen, bitmap_rgb32 &dest, u32 scroll void tilemap_t::get_info_debug(u32 col, u32 row, u8 &gfxnum, u32 &code, u32 &color) { // first map to the memory index + if (m_attributes & TILEMAP_FLIPX) + col = (m_cols - 1) - col; + if (m_attributes & TILEMAP_FLIPY) + row = (m_rows - 1) - row; tilemap_memory_index memindex = memory_index(col, row); // next invoke the get info callback @@ -1501,10 +1568,14 @@ void tilemap_t::get_info_debug(u32 col, u32 row, u8 &gfxnum, u32 &code, u32 &col // get the GFX number and code gfxnum = m_tileinfo.gfxnum; code = m_tileinfo.code; + color = m_tileinfo.palette_base; - // work back from the palette base to get the color - const gfx_element &gfx = *m_tileinfo.decoder->gfx(gfxnum); - color = (m_tileinfo.palette_base - gfx.colorbase()) / gfx.granularity(); + if (gfxnum != 0xff) + { + // work back from the palette base to get the color + const gfx_element &gfx = *m_tileinfo.decoder->gfx(gfxnum); + color = (color - gfx.colorbase()) / gfx.granularity(); + } } @@ -1529,14 +1600,13 @@ tilemap_manager::tilemap_manager(running_machine &machine) tilemap_manager::~tilemap_manager() { - // detach all device tilemaps since they will be destroyed - // as subdevices elsewhere + // detach all device tilemaps since they will be destroyed as subdevices elsewhere bool found = true; while (found) { found = false; for (tilemap_t &tmap : m_tilemap_list) - if (tmap.device() != nullptr) + if (tmap.m_device) { found = true; m_tilemap_list.detach(tmap); @@ -1547,34 +1617,21 @@ tilemap_manager::~tilemap_manager() //------------------------------------------------- -// set_flip_all - set a global flip for all the -// tilemaps +// create - allocate a tilemap //------------------------------------------------- tilemap_t &tilemap_manager::create(device_gfx_interface &decoder, tilemap_get_info_delegate tile_get_info, tilemap_mapper_delegate mapper, u16 tilewidth, u16 tileheight, u32 cols, u32 rows, tilemap_t *allocated) { - if (allocated == nullptr) - allocated = global_alloc(tilemap_t); + if (!allocated) + allocated = new tilemap_t(machine().root_device()); return m_tilemap_list.append(allocated->init(*this, decoder, tile_get_info, mapper, tilewidth, tileheight, cols, rows)); } tilemap_t &tilemap_manager::create(device_gfx_interface &decoder, tilemap_get_info_delegate tile_get_info, tilemap_standard_mapper mapper, u16 tilewidth, u16 tileheight, u32 cols, u32 rows, tilemap_t *allocated) { - if (allocated == nullptr) - allocated = global_alloc(tilemap_t); - - switch (mapper) - { - case TILEMAP_SCAN_ROWS :return m_tilemap_list.append(allocated->init(*this, decoder, tile_get_info, tilemap_mapper_delegate(FUNC(tilemap_t::scan_rows), allocated), tilewidth, tileheight, cols, rows)); - case TILEMAP_SCAN_ROWS_FLIP_X:return m_tilemap_list.append(allocated->init(*this, decoder, tile_get_info, tilemap_mapper_delegate(FUNC(tilemap_t::scan_rows_flip_x), allocated), tilewidth, tileheight, cols, rows)); - case TILEMAP_SCAN_ROWS_FLIP_Y:return m_tilemap_list.append(allocated->init(*this, decoder, tile_get_info, tilemap_mapper_delegate(FUNC(tilemap_t::scan_rows_flip_y), allocated), tilewidth, tileheight, cols, rows)); - case TILEMAP_SCAN_ROWS_FLIP_XY:return m_tilemap_list.append(allocated->init(*this, decoder, tile_get_info, tilemap_mapper_delegate(FUNC(tilemap_t::scan_rows_flip_xy),allocated), tilewidth, tileheight, cols, rows)); - case TILEMAP_SCAN_COLS:return m_tilemap_list.append(allocated->init(*this, decoder, tile_get_info, tilemap_mapper_delegate(FUNC(tilemap_t::scan_cols),allocated), tilewidth, tileheight, cols, rows)); - case TILEMAP_SCAN_COLS_FLIP_X:return m_tilemap_list.append(allocated->init(*this, decoder, tile_get_info, tilemap_mapper_delegate(FUNC(tilemap_t::scan_cols_flip_x),allocated), tilewidth, tileheight, cols, rows)); - case TILEMAP_SCAN_COLS_FLIP_Y:return m_tilemap_list.append(allocated->init(*this, decoder, tile_get_info, tilemap_mapper_delegate(FUNC(tilemap_t::scan_cols_flip_y),allocated), tilewidth, tileheight, cols, rows)); - case TILEMAP_SCAN_COLS_FLIP_XY:return m_tilemap_list.append(allocated->init(*this, decoder, tile_get_info, tilemap_mapper_delegate(FUNC(tilemap_t::scan_cols_flip_xy),allocated), tilewidth, tileheight, cols, rows)); - default: throw emu_fatalerror("Tilemap manager create unknown mapper %d", mapper); - } + if (!allocated) + allocated = new tilemap_t(machine().root_device()); + return m_tilemap_list.append(allocated->init(*this, decoder, tile_get_info, mapper, tilewidth, tileheight, cols, rows)); } @@ -1615,16 +1672,19 @@ DEFINE_DEVICE_TYPE(TILEMAP, tilemap_device, "tilemap", "Tilemap") //------------------------------------------------- tilemap_device::tilemap_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) - : device_t(mconfig, TILEMAP, tag, owner, clock), - m_gfxdecode(*this, finder_base::DUMMY_TAG), - m_standard_mapper(TILEMAP_STANDARD_COUNT), - m_bytes_per_entry(0), - m_tile_width(8), - m_tile_height(8), - m_num_columns(64), - m_num_rows(64), - m_transparent_pen_set(false), - m_transparent_pen(0) + : device_t(mconfig, TILEMAP, tag, owner, clock) + , tilemap_t(static_cast<device_t &>(*this)) + , m_gfxdecode(*this, finder_base::DUMMY_TAG) + , m_get_info(*this) + , m_standard_mapper(TILEMAP_STANDARD_COUNT) + , m_mapper(*this) + , m_bytes_per_entry(0) + , m_tile_width(8) + , m_tile_height(8) + , m_num_columns(64) + , m_num_rows(64) + , m_transparent_pen_set(false) + , m_transparent_pen(0) { } @@ -1719,14 +1779,14 @@ void tilemap_device::device_start() throw device_missing_dependencies(); // bind our callbacks - m_get_info.bind_relative_to(*owner()); - m_mapper.bind_relative_to(*owner()); + m_get_info.resolve(); + m_mapper.resolve(); // allocate the tilemap if (m_standard_mapper == TILEMAP_STANDARD_COUNT) - machine().tilemap().create(*m_gfxdecode, m_get_info, m_mapper, m_tile_width, m_tile_height, m_num_columns, m_num_rows, this); + machine().tilemap().create(*m_gfxdecode, m_get_info, m_mapper, m_tile_width, m_tile_height, m_num_columns, m_num_rows, *this); else - machine().tilemap().create(*m_gfxdecode, m_get_info, m_standard_mapper, m_tile_width, m_tile_height, m_num_columns, m_num_rows, this); + machine().tilemap().create(*m_gfxdecode, m_get_info, m_standard_mapper, m_tile_width, m_tile_height, m_num_columns, m_num_rows, *this); // find the memory, if present const memory_share *share = memshare(tag()); @@ -1736,7 +1796,7 @@ void tilemap_device::device_start() // look for an extension entry std::string tag_ext = std::string(tag()).append("_ext"); - share = memshare(tag_ext.c_str()); + share = memshare(tag_ext); if (share != nullptr) m_extmem.set(*share, m_bytes_per_entry); } diff --git a/src/emu/tilemap.h b/src/emu/tilemap.h index a116f019c6c..3fc1a353315 100644 --- a/src/emu/tilemap.h +++ b/src/emu/tilemap.h @@ -303,6 +303,11 @@ #pragma once +#include "memarray.h" +#include <type_traits> +#include <utility> +#include <vector> + //************************************************************************** // CONSTANTS @@ -405,7 +410,6 @@ class tilemap_t friend class tilemap_device; friend class tilemap_manager; friend class simple_list<tilemap_t>; - friend resource_pool_object<tilemap_t>::~resource_pool_object(); // logical index typedef u32 logical_index; @@ -419,17 +423,19 @@ class tilemap_t // maximum index in each array static const pen_t MAX_PEN_TO_FLAGS = 256; + void init_common(tilemap_manager &manager, device_gfx_interface &decoder, tilemap_get_info_delegate tile_get_info, u16 tilewidth, u16 tileheight, u32 cols, u32 rows); + protected: // tilemap_manager controls our allocations - tilemap_t(); + tilemap_t(device_t &owner); virtual ~tilemap_t(); tilemap_t &init(tilemap_manager &manager, device_gfx_interface &decoder, tilemap_get_info_delegate tile_get_info, tilemap_mapper_delegate mapper, u16 tilewidth, u16 tileheight, u32 cols, u32 rows); + tilemap_t &init(tilemap_manager &manager, device_gfx_interface &decoder, tilemap_get_info_delegate tile_get_info, tilemap_standard_mapper mapper, u16 tilewidth, u16 tileheight, u32 cols, u32 rows); public: // getters running_machine &machine() const; - tilemap_device *device() const { return m_device; } device_palette_interface &palette() const { return *m_palette; } device_gfx_interface &decoder() const { return *m_tileinfo.decoder; } @@ -481,10 +487,10 @@ public: void configure_groups(gfx_element &gfx, indirect_pen_t transcolor); // drawing - void draw(screen_device &screen, bitmap_ind16 &dest, const rectangle &cliprect, u32 flags, u8 priority = 0, u8 priority_mask = 0xff); - void draw(screen_device &screen, bitmap_rgb32 &dest, const rectangle &cliprect, u32 flags, u8 priority = 0, u8 priority_mask = 0xff); - void draw_roz(screen_device &screen, bitmap_ind16 &dest, const rectangle &cliprect, u32 startx, u32 starty, int incxx, int incxy, int incyx, int incyy, bool wraparound, u32 flags, u8 priority = 0, u8 priority_mask = 0xff); - void draw_roz(screen_device &screen, bitmap_rgb32 &dest, const rectangle &cliprect, u32 startx, u32 starty, int incxx, int incxy, int incyx, int incyy, bool wraparound, u32 flags, u8 priority = 0, u8 priority_mask = 0xff); + void draw(screen_device &screen, bitmap_ind16 &dest, const rectangle &cliprect, u32 flags = TILEMAP_DRAW_ALL_CATEGORIES, u8 priority = 0, u8 priority_mask = 0xff); + void draw(screen_device &screen, bitmap_rgb32 &dest, const rectangle &cliprect, u32 flags = TILEMAP_DRAW_ALL_CATEGORIES, u8 priority = 0, u8 priority_mask = 0xff); + void draw_roz(screen_device &screen, bitmap_ind16 &dest, const rectangle &cliprect, u32 startx, u32 starty, int incxx, int incxy, int incyx, int incyy, bool wraparound, u32 flags = TILEMAP_DRAW_ALL_CATEGORIES, u8 priority = 0, u8 priority_mask = 0xff); + void draw_roz(screen_device &screen, bitmap_rgb32 &dest, const rectangle &cliprect, u32 startx, u32 starty, int incxx, int incxy, int incyx, int incyy, bool wraparound, u32 flags = TILEMAP_DRAW_ALL_CATEGORIES, u8 priority = 0, u8 priority_mask = 0xff); void draw_debug(screen_device &screen, bitmap_rgb32 &dest, u32 scrollx, u32 scrolly, u32 flags = TILEMAP_DRAW_ALL_CATEGORIES); // mappers @@ -554,7 +560,7 @@ private: // managers and devices tilemap_manager * m_manager; // reference to the owning manager - tilemap_device * m_device; // pointer to our owning device + device_t * m_device; // pointer to our owning device device_palette_interface * m_palette; // palette used for drawing tilemap_t * m_next; // pointer to next tilemap void * m_user_data; // user data value @@ -621,8 +627,12 @@ public: running_machine &machine() const { return m_machine; } // tilemap creation - tilemap_t &create(device_gfx_interface &decoder, tilemap_get_info_delegate tile_get_info, tilemap_mapper_delegate mapper, u16 tilewidth, u16 tileheight, u32 cols, u32 rows, tilemap_t *allocated = nullptr); - tilemap_t &create(device_gfx_interface &decoder, tilemap_get_info_delegate tile_get_info, tilemap_standard_mapper mapper, u16 tilewidth, u16 tileheight, u32 cols, u32 rows, tilemap_t *allocated = nullptr); + template <typename T, typename U> + tilemap_t &create(device_gfx_interface &decoder, T &&tile_get_info, U &&mapper, u16 tilewidth, u16 tileheight, u32 cols, u32 rows) + { return create(decoder, std::forward<T>(tile_get_info), std::forward<U>(mapper), tilewidth, tileheight, cols, rows, nullptr); } + template <typename T, typename U, class V> + std::enable_if_t<std::is_base_of<device_t, V>::value, tilemap_t &> create(device_gfx_interface &decoder, T &&tile_get_info, U &&mapper, u16 tilewidth, u16 tileheight, u32 cols, u32 rows, V &allocated) + { return create(decoder, std::forward<T>(tile_get_info), std::forward<U>(mapper), tilewidth, tileheight, cols, rows, &static_cast<tilemap_t &>(allocated)); } // tilemap list information tilemap_t *find(int index) { return m_tilemap_list.find(index); } @@ -633,6 +643,10 @@ public: void set_flip_all(u32 attributes); private: + // tilemap creation + tilemap_t &create(device_gfx_interface &decoder, tilemap_get_info_delegate tile_get_info, tilemap_mapper_delegate mapper, u16 tilewidth, u16 tileheight, u32 cols, u32 rows, tilemap_t *allocated); + tilemap_t &create(device_gfx_interface &decoder, tilemap_get_info_delegate tile_get_info, tilemap_standard_mapper mapper, u16 tilewidth, u16 tileheight, u32 cols, u32 rows, tilemap_t *allocated); + // allocate an instance index int alloc_instance() { return ++m_instance; } @@ -689,29 +703,28 @@ public: template <typename T> void set_gfxdecode(T &&tag) { m_gfxdecode.set_tag(std::forward<T>(tag)); } void set_bytes_per_entry(int bpe) { m_bytes_per_entry = bpe; } - void set_info_callback(tilemap_get_info_delegate tile_get_info) { m_get_info = tile_get_info; } - template <class FunctionClass> void set_info_callback(const char *devname, void (FunctionClass::*callback)(tilemap_t &, tile_data &, tilemap_memory_index), const char *name) - { - set_info_callback(tilemap_get_info_delegate(callback, name, devname, static_cast<FunctionClass *>(nullptr))); - } - template <class FunctionClass> void set_info_callback(void (FunctionClass::*callback)(tilemap_t &, tile_data &, tilemap_memory_index), const char *name) - { - set_info_callback(tilemap_get_info_delegate(callback, name, nullptr, static_cast<FunctionClass *>(nullptr))); - } + template <typename... T> void set_info_callback(T &&... args) { m_get_info.set(std::forward<T>(args)...); } - template <class FunctionClass> void set_layout(tilemap_memory_index (FunctionClass::*callback)(u32, u32, u32, u32), const char *name, u32 columns, u32 rows) + void set_layout(tilemap_standard_mapper mapper, u32 columns, u32 rows) { - set_layout(tilemap_mapper_delegate(callback, name, nullptr, static_cast<FunctionClass *>(nullptr)), columns, rows); - } - - void set_layout(tilemap_standard_mapper mapper, u32 columns, u32 rows) { + assert(TILEMAP_STANDARD_COUNT > mapper); m_standard_mapper = mapper; m_num_columns = columns; m_num_rows = rows; } - void set_layout(tilemap_mapper_delegate mapper, u32 columns, u32 rows) { + template <typename F> + void set_layout(F &&callback, const char *name, u32 columns, u32 rows) + { + m_standard_mapper = TILEMAP_STANDARD_COUNT; + m_mapper.set(std::forward<F>(callback), name); + m_num_columns = columns; + m_num_rows = rows; + } + template <typename T, typename F> + void set_layout(T &&target, F &&callback, const char *name, u32 columns, u32 rows) + { m_standard_mapper = TILEMAP_STANDARD_COUNT; - m_mapper = mapper; + m_mapper.set(std::forward<T>(target), std::forward<F>(callback), name); m_num_columns = columns; m_num_rows = rows; } @@ -741,7 +754,7 @@ public: protected: // device-level overrides - virtual void device_start() override; + virtual void device_start() override ATTR_COLD; private: // devices @@ -780,14 +793,11 @@ private: // function definition for a logical-to-memory mapper #define TILEMAP_MAPPER_MEMBER(_name) tilemap_memory_index _name(u32 col, u32 row, u32 num_cols, u32 num_rows) -// useful macro inside of a TILE_GET_INFO callback to set tile information -#define SET_TILE_INFO_MEMBER(GFX,CODE,COLOR,FLAGS) tileinfo.set(GFX, CODE, COLOR, FLAGS) - -// Macros for setting tile attributes in the TILE_GET_INFO callback: +// Helpers for setting tile attributes in the TILE_GET_INFO callback: // TILE_FLIP_YX assumes that flipy is in bit 1 and flipx is in bit 0 // TILE_FLIP_XY assumes that flipy is in bit 0 and flipx is in bit 1 -#define TILE_FLIPYX(YX) ((YX) & 3) -#define TILE_FLIPXY(XY) ((((XY) & 2) >> 1) | (((XY) & 1) << 1)) +template <typename T> constexpr u8 TILE_FLIPYX(T yx) { return u8(yx & 3); } +template <typename T> constexpr u8 TILE_FLIPXY(T xy) { return u8(((xy & 2) >> 1) | ((xy & 1) << 1)); } diff --git a/src/emu/ui/cmddata.h b/src/emu/ui/cmddata.h index be636f19dc4..4f63cd4e58f 100644 --- a/src/emu/ui/cmddata.h +++ b/src/emu/ui/cmddata.h @@ -10,32 +10,6 @@ #pragma once -#define BUTTON_COLOR_RED rgb_t(255,64,64) -#define BUTTON_COLOR_YELLOW rgb_t(255,238,0) -#define BUTTON_COLOR_GREEN rgb_t(0,255,64) -#define BUTTON_COLOR_BLUE rgb_t(0,170,255) -#define BUTTON_COLOR_PURPLE rgb_t(170,0,255) -#define BUTTON_COLOR_PINK rgb_t(255,0,170) -#define BUTTON_COLOR_AQUA rgb_t(0,255,204) -#define BUTTON_COLOR_SILVER rgb_t(255,0,255) -#define BUTTON_COLOR_NAVY rgb_t(255,160,0) -#define BUTTON_COLOR_LIME rgb_t(190,190,190) - -enum -{ - B_COLOR_RED, - B_COLOR_YELLOW, - B_COLOR_GREEN, - B_COLOR_BLUE, - B_COLOR_PURPLE, - B_COLOR_PINK, - B_COLOR_AQUA, - B_COLOR_SILVER, - B_COLOR_NAVY, - B_COLOR_LIME, - MAX_COLORTABLE -}; - // command.dat symbols assigned to Unicode PUA U+E000 #define COMMAND_UNICODE (0xe000) #define MAX_GLYPH_FONT (150) @@ -49,102 +23,6 @@ enum // Define Simple Game Command ShortCut #define COMMAND_CONVERT_TEXT '@' -// Defined Game Command Font Color Array -static rgb_t const color_table[] = -{ - 0, // dummy - BUTTON_COLOR_RED, // BTN_A - BUTTON_COLOR_YELLOW, // BTN_B - BUTTON_COLOR_GREEN, // BTN_C - BUTTON_COLOR_BLUE, // BTN_D - BUTTON_COLOR_PINK, // BTN_E - BUTTON_COLOR_PURPLE, // BTN_F - BUTTON_COLOR_AQUA, // BTN_G - BUTTON_COLOR_SILVER, // BTN_H - BUTTON_COLOR_NAVY, // BTN_I - BUTTON_COLOR_LIME, // BTN_J - BUTTON_COLOR_RED, // BTN_K - BUTTON_COLOR_YELLOW, // BTN_L - BUTTON_COLOR_GREEN, // BTN_M - BUTTON_COLOR_BLUE, // BTN_N - BUTTON_COLOR_PINK, // BTN_O - BUTTON_COLOR_PURPLE, // BTN_P - BUTTON_COLOR_AQUA, // BTN_Q - BUTTON_COLOR_SILVER, // BTN_R - BUTTON_COLOR_NAVY, // BTN_S - BUTTON_COLOR_LIME, // BTN_T - BUTTON_COLOR_RED, // BTN_U - BUTTON_COLOR_YELLOW, // BTN_V - BUTTON_COLOR_GREEN, // BTN_W - BUTTON_COLOR_BLUE, // BTN_X - BUTTON_COLOR_PINK, // BTN_Y - BUTTON_COLOR_PURPLE, // BTN_Z - BUTTON_COLOR_RED, // BTN_1 - BUTTON_COLOR_YELLOW, // BTN_2 - BUTTON_COLOR_GREEN, // BTN_3 - BUTTON_COLOR_BLUE, // BTN_4 - BUTTON_COLOR_PINK, // BTN_5 - BUTTON_COLOR_PURPLE, // BTN_6 - BUTTON_COLOR_AQUA, // BTN_7 - BUTTON_COLOR_SILVER, // BTN_8 - BUTTON_COLOR_NAVY, // BTN_9 - BUTTON_COLOR_LIME, // BTN_10 - BUTTON_COLOR_BLUE, // BTN_DEC - BUTTON_COLOR_RED, // BTN_INC - 0, // BTN_+ - 0, // DIR_... - 0, // DIR_1 - 0, // DIR_2 - 0, // DIR_3 - 0, // DIR_4 - BUTTON_COLOR_RED, // Joystick Ball - 0, // DIR_6 - 0, // DIR_7 - 0, // DIR_8 - 0, // DIR_9 - 0, // DIR_N - BUTTON_COLOR_RED, // BTN_START - BUTTON_COLOR_YELLOW, // BTN_SELECT - BUTTON_COLOR_PINK, // BTN_PUNCH - BUTTON_COLOR_PURPLE, // BTN_KICK - BUTTON_COLOR_BLUE, // BTN_GUARD - 0, - BUTTON_COLOR_YELLOW, // Light Punch - BUTTON_COLOR_NAVY, // Middle Punch - BUTTON_COLOR_RED, // Strong Punch - BUTTON_COLOR_LIME, // Light Kick - BUTTON_COLOR_AQUA, // Middle Kick - BUTTON_COLOR_BLUE, // Strong Kick - BUTTON_COLOR_PURPLE, // 3 Kick - BUTTON_COLOR_PINK, // 3 Punch - BUTTON_COLOR_PURPLE, // 2 Kick - BUTTON_COLOR_PINK, // 2 Punch - BUTTON_COLOR_RED, // CUSTOM_1 - BUTTON_COLOR_YELLOW, // CUSTOM_2 - BUTTON_COLOR_GREEN, // CUSTOM_3 - BUTTON_COLOR_BLUE, // CUSTOM_4 - BUTTON_COLOR_PINK, // CUSTOM_5 - BUTTON_COLOR_PURPLE, // CUSTOM_6 - BUTTON_COLOR_AQUA, // CUSTOM_7 - BUTTON_COLOR_SILVER, // CUSTOM_8 - BUTTON_COLOR_RED, // (Cursor Up) - BUTTON_COLOR_YELLOW, // (Cursor Down) - BUTTON_COLOR_GREEN, // (Cursor Left) - BUTTON_COLOR_BLUE, // (Cursor Right) - 0, // Non Player Lever - BUTTON_COLOR_LIME, // Gray Color Lever - BUTTON_COLOR_RED, // 1 Player Lever - BUTTON_COLOR_YELLOW, // 2 Player Lever - BUTTON_COLOR_GREEN, // 3 Player Lever - BUTTON_COLOR_BLUE, // 4 Player Lever - BUTTON_COLOR_PINK, // 5 Player Lever - BUTTON_COLOR_PURPLE, // 6 Player Lever - BUTTON_COLOR_AQUA, // 7 Player Lever - BUTTON_COLOR_SILVER // 8 Player Lever -}; - -#define COLOR_BUTTONS ARRAY_LENGTH(color_table) - struct fix_command_t { char glyph_char; @@ -154,9 +32,8 @@ struct fix_command_t struct fix_strings_t { - char const *glyph_str; - int const glyph_code; - unsigned glyph_str_len; + std::string_view glyph_str; + int glyph_code; }; static fix_command_t const default_text[] = @@ -286,117 +163,117 @@ static fix_command_t const expand_text[] = { 0, 0 } // end of array }; -static fix_strings_t convert_text[] = +static const fix_strings_t convert_text[] = { // Alphabetic Buttons: A~Z - { "A-button", 1, 0 }, // BTN_A - { "B-button", 2, 0 }, // BTN_B - { "C-button", 3, 0 }, // BTN_C - { "D-button", 4, 0 }, // BTN_D - { "E-button", 5, 0 }, // BTN_E - { "F-button", 6, 0 }, // BTN_F - { "G-button", 7, 0 }, // BTN_G - { "H-button", 8, 0 }, // BTN_H - { "I-button", 9, 0 }, // BTN_I - { "J-button", 10, 0 }, // BTN_J - { "K-button", 11, 0 }, // BTN_K - { "L-button", 12, 0 }, // BTN_L - { "M-button", 13, 0 }, // BTN_M - { "N-button", 14, 0 }, // BTN_N - { "O-button", 15, 0 }, // BTN_O - { "P-button", 16, 0 }, // BTN_P - { "Q-button", 17, 0 }, // BTN_Q - { "R-button", 18, 0 }, // BTN_R - { "S-button", 19, 0 }, // BTN_S - { "T-button", 20, 0 }, // BTN_T - { "U-button", 21, 0 }, // BTN_U - { "V-button", 22, 0 }, // BTN_V - { "W-button", 23, 0 }, // BTN_W - { "X-button", 24, 0 }, // BTN_X - { "Y-button", 25, 0 }, // BTN_Y - { "Z-button", 26, 0 }, // BTN_Z + { "A-button", 1 }, // BTN_A + { "B-button", 2 }, // BTN_B + { "C-button", 3 }, // BTN_C + { "D-button", 4 }, // BTN_D + { "E-button", 5 }, // BTN_E + { "F-button", 6 }, // BTN_F + { "G-button", 7 }, // BTN_G + { "H-button", 8 }, // BTN_H + { "I-button", 9 }, // BTN_I + { "J-button", 10 }, // BTN_J + { "K-button", 11 }, // BTN_K + { "L-button", 12 }, // BTN_L + { "M-button", 13 }, // BTN_M + { "N-button", 14 }, // BTN_N + { "O-button", 15 }, // BTN_O + { "P-button", 16 }, // BTN_P + { "Q-button", 17 }, // BTN_Q + { "R-button", 18 }, // BTN_R + { "S-button", 19 }, // BTN_S + { "T-button", 20 }, // BTN_T + { "U-button", 21 }, // BTN_U + { "V-button", 22 }, // BTN_V + { "W-button", 23 }, // BTN_W + { "X-button", 24 }, // BTN_X + { "Y-button", 25 }, // BTN_Y + { "Z-button", 26 }, // BTN_Z // Special Moves and Buttons - { "decrease", 37, 0 }, // BTN_DEC - { "increase", 38, 0 }, // BTN_INC - { "BALL", 45, 0 }, // Joystick Ball - { "start", 51, 0 }, // BTN_START - { "select", 52, 0 }, // BTN_SELECT - { "punch", 53, 0 }, // BTN_PUNCH - { "kick", 54, 0 }, // BTN_KICK - { "guard", 55, 0 }, // BTN_GUARD - { "L-punch", 57, 0 }, // Light Punch - { "M-punch", 58, 0 }, // Middle Punch - { "S-punch", 59, 0 }, // Strong Punch - { "L-kick", 60, 0 }, // Light Kick - { "M-kick", 61, 0 }, // Middle Kick - { "S-kick", 62, 0 }, // Strong Kick - { "3-kick", 63, 0 }, // 3 Kick - { "3-punch", 64, 0 }, // 3 Punch - { "2-kick", 65, 0 }, // 2 Kick - { "2-punch", 66, 0 }, // 2 Pick + { "decrease", 37 }, // BTN_DEC + { "increase", 38 }, // BTN_INC + { "BALL", 45 }, // Joystick Ball + { "start", 51 }, // BTN_START + { "select", 52 }, // BTN_SELECT + { "punch", 53 }, // BTN_PUNCH + { "kick", 54 }, // BTN_KICK + { "guard", 55 }, // BTN_GUARD + { "L-punch", 57 }, // Light Punch + { "M-punch", 58 }, // Middle Punch + { "S-punch", 59 }, // Strong Punch + { "L-kick", 60 }, // Light Kick + { "M-kick", 61 }, // Middle Kick + { "S-kick", 62 }, // Strong Kick + { "3-kick", 63 }, // 3 Kick + { "3-punch", 64 }, // 3 Punch + { "2-kick", 65 }, // 2 Kick + { "2-punch", 66 }, // 2 Pick // Custom Buttons and Cursor Buttons - { "custom1", 67, 0 }, // CUSTOM_1 - { "custom2", 68, 0 }, // CUSTOM_2 - { "custom3", 69, 0 }, // CUSTOM_3 - { "custom4", 70, 0 }, // CUSTOM_4 - { "custom5", 71, 0 }, // CUSTOM_5 - { "custom6", 72, 0 }, // CUSTOM_6 - { "custom7", 73, 0 }, // CUSTOM_7 - { "custom8", 74, 0 }, // CUSTOM_8 - { "up", 75, 0 }, // (Cursor Up) - { "down", 76, 0 }, // (Cursor Down) - { "left", 77, 0 }, // (Cursor Left) - { "right", 78, 0 }, // (Cursor Right) + { "custom1", 67 }, // CUSTOM_1 + { "custom2", 68 }, // CUSTOM_2 + { "custom3", 69 }, // CUSTOM_3 + { "custom4", 70 }, // CUSTOM_4 + { "custom5", 71 }, // CUSTOM_5 + { "custom6", 72 }, // CUSTOM_6 + { "custom7", 73 }, // CUSTOM_7 + { "custom8", 74 }, // CUSTOM_8 + { "up", 75 }, // (Cursor Up) + { "down", 76 }, // (Cursor Down) + { "left", 77 }, // (Cursor Left) + { "right", 78 }, // (Cursor Right) // Player Lever - { "lever", 79, 0 }, // Non Player Lever - { "nplayer", 80, 0 }, // Gray Color Lever - { "1player", 81, 0 }, // 1 Player Lever - { "2player", 82, 0 }, // 2 Player Lever - { "3player", 83, 0 }, // 3 Player Lever - { "4player", 84, 0 }, // 4 Player Lever - { "5player", 85, 0 }, // 5 Player Lever - { "6player", 86, 0 }, // 6 Player Lever - { "7player", 87, 0 }, // 7 Player Lever - { "8player", 88, 0 }, // 8 Player Lever + { "lever", 79 }, // Non Player Lever + { "nplayer", 80 }, // Gray Color Lever + { "1player", 81 }, // 1 Player Lever + { "2player", 82 }, // 2 Player Lever + { "3player", 83 }, // 3 Player Lever + { "4player", 84 }, // 4 Player Lever + { "5player", 85 }, // 5 Player Lever + { "6player", 86 }, // 6 Player Lever + { "7player", 87 }, // 7 Player Lever + { "8player", 88 }, // 8 Player Lever // Composition of Arrow Directions - { "-->", 90, 0 }, // Arrow - { "==>", 91, 0 }, // Continue Arrow - { "hcb", 100, 0 }, // Half Circle Back - { "huf", 101, 0 }, // Half Circle Front Up - { "hcf", 102, 0 }, // Half Circle Front - { "hub", 103, 0 }, // Half Circle Back Up - { "qfd", 104, 0 }, // 1/4 Cir For 2 Down - { "qdb", 105, 0 }, // 1/4 Cir Down 2 Back - { "qbu", 106, 0 }, // 1/4 Cir Back 2 Up - { "quf", 107, 0 }, // 1/4 Cir Up 2 For - { "qbd", 108, 0 }, // 1/4 Cir Back 2 Down - { "qdf", 109, 0 }, // 1/4 Cir Down 2 For - { "qfu", 110, 0 }, // 1/4 Cir For 2 Up - { "qub", 111, 0 }, // 1/4 Cir Up 2 Back - { "fdf", 112, 0 }, // Full Clock Forward - { "fub", 113, 0 }, // Full Clock Back - { "fuf", 114, 0 }, // Full Count Forward - { "fdb", 115, 0 }, // Full Count Back - { "xff", 116, 0 }, // 2x Forward - { "xbb", 117, 0 }, // 2x Back - { "dsf", 118, 0 }, // Dragon Screw Forward - { "dsb", 119, 0 }, // Dragon Screw Back + { "-->", 90 }, // Arrow + { "==>", 91 }, // Continue Arrow + { "hcb", 100 }, // Half Circle Back + { "huf", 101 }, // Half Circle Front Up + { "hcf", 102 }, // Half Circle Front + { "hub", 103 }, // Half Circle Back Up + { "qfd", 104 }, // 1/4 Cir For 2 Down + { "qdb", 105 }, // 1/4 Cir Down 2 Back + { "qbu", 106 }, // 1/4 Cir Back 2 Up + { "quf", 107 }, // 1/4 Cir Up 2 For + { "qbd", 108 }, // 1/4 Cir Back 2 Down + { "qdf", 109 }, // 1/4 Cir Down 2 For + { "qfu", 110 }, // 1/4 Cir For 2 Up + { "qub", 111 }, // 1/4 Cir Up 2 Back + { "fdf", 112 }, // Full Clock Forward + { "fub", 113 }, // Full Clock Back + { "fuf", 114 }, // Full Count Forward + { "fdb", 115 }, // Full Count Back + { "xff", 116 }, // 2x Forward + { "xbb", 117 }, // 2x Back + { "dsf", 118 }, // Dragon Screw Forward + { "dsb", 119 }, // Dragon Screw Back // Big letter Text - { "AIR", 121, 0 }, // AIR - { "DIR", 122, 0 }, // DIR - { "MAX", 123, 0 }, // MAX - { "TAP", 124, 0 }, // TAP + { "AIR", 121 }, // AIR + { "DIR", 122 }, // DIR + { "MAX", 123 }, // MAX + { "TAP", 124 }, // TAP // Condition of Positions - { "jump", 125, 0 }, // Jump - { "hold", 126, 0 }, // Hold - { "air", 127, 0 }, // Air - { "sit", 128, 0 }, // Squatting - { "close", 129, 0 }, // Close - { "away", 130, 0 }, // Away - { "charge", 131, 0 }, // Charge - { "tap", 132, 0 }, // Serious Tap - { "button", 133, 0 }, // Any Button - { nullptr, 0, 0 } // end of array + { "jump", 125 }, // Jump + { "hold", 126 }, // Hold + { "air", 127 }, // Air + { "sit", 128 }, // Squatting + { "close", 129 }, // Close + { "away", 130 }, // Away + { "charge", 131 }, // Charge + { "tap", 132 }, // Serious Tap + { "button", 133 }, // Any Button + { "", 0 } // end of array }; #endif // MAME_EMU_UI_CMDDATA_H diff --git a/src/emu/ui/uimain.h b/src/emu/ui/uimain.h index ecbbcf695ca..e45c71f9468 100644 --- a/src/emu/ui/uimain.h +++ b/src/emu/ui/uimain.h @@ -13,6 +13,8 @@ #pragma once +#include <functional> + /*************************************************************************** TYPE DEFINITIONS @@ -22,7 +24,7 @@ class ui_manager { public: // construction/destruction - ui_manager(running_machine &machine) : m_machine(machine),m_show_timecode_counter(false),m_show_timecode_total(false) { } + ui_manager(running_machine &machine) : m_machine(machine) { } virtual ~ui_manager() { } @@ -31,22 +33,17 @@ public: // is a menuing system active? we want to disable certain keyboard/mouse inputs under such context virtual bool is_menu_active() { return false; } - void set_show_timecode_counter(bool value) { m_show_timecode_counter = value; m_show_timecode_total = true; } - - bool show_timecode_counter() const { return m_show_timecode_counter; } - bool show_timecode_total() const { return m_show_timecode_total; } - virtual void popup_time_string(int seconds, std::string message) { } virtual void menu_reset() { } + virtual bool set_ui_event_handler(std::function<bool ()> &&handler) { return false; } + template <typename Format, typename... Params> void popup_time(int seconds, Format &&fmt, Params &&... args); protected: // instance variables running_machine & m_machine; - bool m_show_timecode_counter; - bool m_show_timecode_total; }; /*************************************************************************** diff --git a/src/emu/uiinput.cpp b/src/emu/uiinput.cpp index e6b0920e926..12ba8a08bb0 100644 --- a/src/emu/uiinput.cpp +++ b/src/emu/uiinput.cpp @@ -5,6 +5,7 @@ uiinput.cpp Internal MAME user interface input state. + ***************************************************************************/ #include "emu.h" @@ -35,18 +36,11 @@ enum ui_input_manager::ui_input_manager(running_machine &machine) : m_machine(machine) , m_presses_enabled(true) - , m_current_mouse_target(nullptr) - , m_current_mouse_down(false) - , m_current_mouse_field(nullptr) , m_events_start(0) , m_events_end(0) { - // create the private data - m_current_mouse_x = -1; - m_current_mouse_y = -1; - - // add a frame callback to poll inputs - machine.add_notifier(MACHINE_NOTIFY_FRAME, machine_notify_delegate(&ui_input_manager::frame_update, this)); + std::fill(std::begin(m_next_repeat), std::end(m_next_repeat), 0); + std::fill(std::begin(m_seqpressed), std::end(m_seqpressed), 0); } @@ -56,12 +50,12 @@ ui_input_manager::ui_input_manager(running_machine &machine) ***************************************************************************/ /*------------------------------------------------- - frame_update - looks through pressed - input as per events pushed our way and posts + check_ui_inputs - looks through pressed input + as per events pushed our way and posts corresponding IPT_UI_* events -------------------------------------------------*/ -void ui_input_manager::frame_update() +void ui_input_manager::check_ui_inputs() { // update the state of all the UI keys for (ioport_type code = ioport_type(IPT_UI_FIRST + 1); code < IPT_UI_LAST; ++code) @@ -78,22 +72,6 @@ void ui_input_manager::frame_update() m_seqpressed[code] = false; } } - - // perform mouse hit testing - ioport_field *mouse_field = m_current_mouse_down ? find_mouse_field() : nullptr; - if (m_current_mouse_field != mouse_field) - { - // clear the old field if there was one - if (m_current_mouse_field != nullptr) - m_current_mouse_field->set_value(0); - - // set the new field if it exists and isn't already being pressed - if (mouse_field != nullptr && !mouse_field->digital_value()) - mouse_field->set_value(1); - - // update internal state - m_current_mouse_field = mouse_field; - } } @@ -104,43 +82,12 @@ void ui_input_manager::frame_update() bool ui_input_manager::push_event(ui_event evt) { - // some pre-processing (this is an icky place to do this stuff!) - switch (evt.event_type) - { - case ui_event::MOUSE_MOVE: - m_current_mouse_target = evt.target; - m_current_mouse_x = evt.mouse_x; - m_current_mouse_y = evt.mouse_y; - break; - - case ui_event::MOUSE_LEAVE: - if (m_current_mouse_target == evt.target) - { - m_current_mouse_target = nullptr; - m_current_mouse_x = -1; - m_current_mouse_y = -1; - } - break; - - case ui_event::MOUSE_DOWN: - m_current_mouse_down = true; - break; - - case ui_event::MOUSE_UP: - m_current_mouse_down = false; - break; - - default: - /* do nothing */ - break; - } - // is the queue filled up? - if ((m_events_end + 1) % ARRAY_LENGTH(m_events) == m_events_start) + if ((m_events_end + 1) % std::size(m_events) == m_events_start) return false; m_events[m_events_end++] = evt; - m_events_end %= ARRAY_LENGTH(m_events); + m_events_end %= std::size(m_events); return true; } @@ -154,7 +101,7 @@ bool ui_input_manager::pop_event(ui_event *evt) if (m_events_start != m_events_end) { *evt = m_events[m_events_start++]; - m_events_start %= ARRAY_LENGTH(m_events); + m_events_start %= std::size(m_events); return true; } else @@ -184,64 +131,12 @@ void ui_input_manager::reset() } -/*------------------------------------------------- - find_mouse - retrieves the current - location of the mouse --------------------------------------------------*/ - -render_target *ui_input_manager::find_mouse(s32 *x, s32 *y, bool *button) const -{ - if (x != nullptr) - *x = m_current_mouse_x; - if (y != nullptr) - *y = m_current_mouse_y; - if (button != nullptr) - *button = m_current_mouse_down; - return m_current_mouse_target; -} - - -/*------------------------------------------------- - find_mouse_field - retrieves the input field - the mouse is currently pointing at --------------------------------------------------*/ - -ioport_field *ui_input_manager::find_mouse_field() const -{ - // map the point and determine what was hit - if (m_current_mouse_target != nullptr) - { - ioport_port *port = nullptr; - ioport_value mask; - float x, y; - if (m_current_mouse_target->map_point_input(m_current_mouse_x, m_current_mouse_y, port, mask, x, y)) - { - if (port != nullptr) - return port->field(mask); - } - } - return nullptr; -} - - /*************************************************************************** USER INTERFACE SEQUENCE READING ***************************************************************************/ /*------------------------------------------------- - pressed - return true if a key down - for the given user interface sequence is - detected --------------------------------------------------*/ - -bool ui_input_manager::pressed(int code) -{ - return pressed_repeat(code, 0); -} - - -/*------------------------------------------------- pressed_repeat - return true if a key down for the given user interface sequence is detected, or if autorepeat at the given speed @@ -252,10 +147,10 @@ bool ui_input_manager::pressed_repeat(int code, int speed) { bool pressed; -g_profiler.start(PROFILER_INPUT); + auto profile = g_profiler.start(PROFILER_INPUT); /* get the status of this key (assumed to be only in the defaults) */ - assert(code >= IPT_UI_CONFIGURE && code <= IPT_OSD_16); + assert(code > IPT_UI_FIRST && code < IPT_UI_LAST); pressed = (m_seqpressed[code] == SEQ_PRESSED_TRUE); /* if down, handle it specially */ @@ -270,7 +165,7 @@ g_profiler.start(PROFILER_INPUT); /* if this is an autorepeat case, set a 1x delay and leave pressed = 1 */ else if (speed > 0 && (osd_ticks() + tps - m_next_repeat[code]) >= tps) { - // In the autorepeatcase, we need to double check the key is still pressed + // In the autorepeat case, we need to double-check the key is still pressed // as there can be a delay between the key polling and our processing of the event m_seqpressed[code] = machine().ioport().type_pressed(ioport_type(code)); pressed = (m_seqpressed[code] == SEQ_PRESSED_TRUE); @@ -287,111 +182,114 @@ g_profiler.start(PROFILER_INPUT); else m_next_repeat[code] = 0; -g_profiler.stop(); - return pressed; } /*------------------------------------------------- - push_mouse_move_event - pushes a mouse - move event to the specified render_target --------------------------------------------------*/ - -void ui_input_manager::push_mouse_move_event(render_target* target, s32 x, s32 y) -{ - ui_event event = { ui_event::NONE }; - event.event_type = ui_event::MOUSE_MOVE; - event.target = target; - event.mouse_x = x; - event.mouse_y = y; - push_event(event); -} - -/*------------------------------------------------- - push_mouse_leave_event - pushes a - mouse leave event to the specified render_target --------------------------------------------------*/ - -void ui_input_manager::push_mouse_leave_event(render_target* target) -{ - ui_event event = { ui_event::NONE }; - event.event_type = ui_event::MOUSE_LEAVE; - event.target = target; - push_event(event); -} - -/*------------------------------------------------- - push_mouse_down_event - pushes a mouse - down event to the specified render_target + push_window_focus_event - pushes a focus + event to the specified render_target -------------------------------------------------*/ -void ui_input_manager::push_mouse_down_event(render_target* target, s32 x, s32 y) +void ui_input_manager::push_window_focus_event(render_target *target) { - ui_event event = { ui_event::NONE }; - event.event_type = ui_event::MOUSE_DOWN; + ui_event event = { ui_event::type::NONE }; + event.event_type = ui_event::type::WINDOW_FOCUS; event.target = target; - event.mouse_x = x; - event.mouse_y = y; push_event(event); } /*------------------------------------------------- - push_mouse_down_event - pushes a mouse - down event to the specified render_target + push_window_defocus_event - pushes a defocus + event to the specified render_target -------------------------------------------------*/ -void ui_input_manager::push_mouse_up_event(render_target* target, s32 x, s32 y) +void ui_input_manager::push_window_defocus_event(render_target *target) { - ui_event event = { ui_event::NONE }; - event.event_type = ui_event::MOUSE_UP; + ui_event event = { ui_event::type::NONE }; + event.event_type = ui_event::type::WINDOW_DEFOCUS; event.target = target; - event.mouse_x = x; - event.mouse_y = y; push_event(event); } /*------------------------------------------------- -push_mouse_down_event - pushes a mouse -down event to the specified render_target + push_pointer_update - pushes a pointer update + event to the specified render_target -------------------------------------------------*/ -void ui_input_manager::push_mouse_rdown_event(render_target* target, s32 x, s32 y) +void ui_input_manager::push_pointer_update( + render_target *target, + pointer type, + u16 ptrid, + u16 device, + s32 x, + s32 y, + u32 buttons, + u32 pressed, + u32 released, + s16 clicks) { - ui_event event = { ui_event::NONE }; - event.event_type = ui_event::MOUSE_RDOWN; + ui_event event = { ui_event::type::NONE }; + event.event_type = ui_event::type::POINTER_UPDATE; event.target = target; - event.mouse_x = x; - event.mouse_y = y; + event.pointer_type = type; + event.pointer_id = ptrid; + event.pointer_device = device; + event.pointer_x = x; + event.pointer_y = y; + event.pointer_buttons = buttons; + event.pointer_pressed = pressed; + event.pointer_released = released; + event.pointer_clicks = clicks; push_event(event); } -/*------------------------------------------------- -push_mouse_down_event - pushes a mouse -down event to the specified render_target --------------------------------------------------*/ - -void ui_input_manager::push_mouse_rup_event(render_target* target, s32 x, s32 y) +void ui_input_manager::push_pointer_leave( + render_target *target, + pointer type, + u16 ptrid, + u16 device, + s32 x, + s32 y, + u32 released, + s16 clicks) { - ui_event event = { ui_event::NONE }; - event.event_type = ui_event::MOUSE_RUP; + ui_event event = { ui_event::type::NONE }; + event.event_type = ui_event::type::POINTER_LEAVE; event.target = target; - event.mouse_x = x; - event.mouse_y = y; + event.pointer_type = type; + event.pointer_id = ptrid; + event.pointer_device = device; + event.pointer_x = x; + event.pointer_y = y; + event.pointer_buttons = 0U; + event.pointer_pressed = 0U; + event.pointer_released = released; + event.pointer_clicks = clicks; push_event(event); } -/*------------------------------------------------- - push_mouse_double_click_event - pushes - a mouse double-click event to the specified - render_target --------------------------------------------------*/ -void ui_input_manager::push_mouse_double_click_event(render_target* target, s32 x, s32 y) +void ui_input_manager::push_pointer_abort( + render_target *target, + pointer type, + u16 ptrid, + u16 device, + s32 x, + s32 y, + u32 released, + s16 clicks) { - ui_event event = { ui_event::NONE }; - event.event_type = ui_event::MOUSE_DOUBLE_CLICK; + ui_event event = { ui_event::type::NONE }; + event.event_type = ui_event::type::POINTER_ABORT; event.target = target; - event.mouse_x = x; - event.mouse_y = y; + event.pointer_type = type; + event.pointer_id = ptrid; + event.pointer_device = device; + event.pointer_x = x; + event.pointer_y = y; + event.pointer_buttons = 0U; + event.pointer_pressed = 0U; + event.pointer_released = released; + event.pointer_clicks = clicks; push_event(event); } @@ -399,10 +297,10 @@ void ui_input_manager::push_mouse_double_click_event(render_target* target, s32 push_char_event - pushes a char event to the specified render_target -------------------------------------------------*/ -void ui_input_manager::push_char_event(render_target* target, char32_t ch) +void ui_input_manager::push_char_event(render_target *target, char32_t ch) { - ui_event event = { ui_event::NONE }; - event.event_type = ui_event::IME_CHAR; + ui_event event = { ui_event::type::NONE }; + event.event_type = ui_event::type::IME_CHAR; event.target = target; event.ch = ch; push_event(event); @@ -413,15 +311,15 @@ void ui_input_manager::push_char_event(render_target* target, char32_t ch) wheel event to the specified render_target -------------------------------------------------*/ -void ui_input_manager::push_mouse_wheel_event(render_target *target, s32 x, s32 y, short delta, int ucNumLines) +void ui_input_manager::push_mouse_wheel_event(render_target *target, s32 x, s32 y, short delta, int lines) { - ui_event event = { ui_event::NONE }; - event.event_type = ui_event::MOUSE_WHEEL; + ui_event event = { ui_event::type::NONE }; + event.event_type = ui_event::type::MOUSE_WHEEL; event.target = target; event.mouse_x = x; event.mouse_y = y; event.zdelta = delta; - event.num_lines = ucNumLines; + event.num_lines = lines; push_event(event); } diff --git a/src/emu/uiinput.h b/src/emu/uiinput.h index d62f0c8a9b9..54170bad7ec 100644 --- a/src/emu/uiinput.h +++ b/src/emu/uiinput.h @@ -5,6 +5,7 @@ uiinput.h Internal MAME user interface input state. + ***************************************************************************/ #ifndef MAME_EMU_UIINPUT_H @@ -12,12 +13,8 @@ #pragma once +#include "interface/uievents.h" -/*************************************************************************** - CONSTANTS -***************************************************************************/ - -#define EVENT_QUEUE_SIZE 128 /*************************************************************************** TYPE DEFINITIONS @@ -25,20 +22,20 @@ struct ui_event { - enum type + enum class type { NONE, - MOUSE_MOVE, - MOUSE_LEAVE, - MOUSE_DOWN, - MOUSE_UP, - MOUSE_RDOWN, - MOUSE_RUP, - MOUSE_DOUBLE_CLICK, + WINDOW_FOCUS, + WINDOW_DEFOCUS, MOUSE_WHEEL, + POINTER_UPDATE, + POINTER_LEAVE, + POINTER_ABORT, IME_CHAR }; + using pointer = osd::ui_event_handler::pointer; + type event_type; render_target * target; s32 mouse_x; @@ -47,82 +44,80 @@ struct ui_event char32_t ch; short zdelta; int num_lines; + + pointer pointer_type; // type of input controlling this pointer + u16 pointer_id; // pointer ID - will be recycled aggressively + u16 pointer_device; // for grouping pointers for multi-touch gesture recognition + s32 pointer_x; // pointer X coordinate + s32 pointer_y; // pointer Y coordinate + u32 pointer_buttons; // currently depressed buttons + u32 pointer_pressed; // buttons pressed since last update (primary action in LSB) + u32 pointer_released; // buttons released since last update (primary action in LSB) + s16 pointer_clicks; // positive for multi-click, negative on release if turned into hold or drag }; // ======================> ui_input_manager -class ui_input_manager +class ui_input_manager final : public osd::ui_event_handler { public: // construction/destruction ui_input_manager(running_machine &machine); - void frame_update(); + void check_ui_inputs(); - /* pushes a single event onto the queue */ + // pushes a single event onto the queue bool push_event(ui_event event); - /* pops an event off of the queue */ + // pops an event off of the queue bool pop_event(ui_event *event); - /* check the next event type without removing it */ - ui_event::type peek_event_type() const { return (m_events_start != m_events_end) ? m_events[m_events_start].event_type : ui_event::NONE; } + // check the next event type without removing it + ui_event::type peek_event_type() const { return (m_events_start != m_events_end) ? m_events[m_events_start].event_type : ui_event::type::NONE; } - /* clears all outstanding events */ + // clears all outstanding events void reset(); - /* retrieves the current location of the mouse */ - render_target *find_mouse(s32 *x, s32 *y, bool *button) const; - ioport_field *find_mouse_field() const; - - /* return true if a key down for the given user interface sequence is detected */ - bool pressed(int code); - // enable/disable UI key presses bool presses_enabled() const { return m_presses_enabled; } void set_presses_enabled(bool enabled) { m_presses_enabled = enabled; } - /* return true if a key down for the given user interface sequence is detected, or if - autorepeat at the given speed is triggered */ + // return true if a key down for the given user interface sequence is detected + bool pressed(int code) { return pressed_repeat(code, 0); } + + // return true if a key down for the given user interface sequence is detected, or if autorepeat at the given speed is triggered bool pressed_repeat(int code, int speed); // getters running_machine &machine() const { return m_machine; } - - void push_mouse_move_event(render_target* target, s32 x, s32 y); - void push_mouse_leave_event(render_target* target); - void push_mouse_down_event(render_target* target, s32 x, s32 y); - void push_mouse_up_event(render_target* target, s32 x, s32 y); - void push_mouse_rdown_event(render_target* target, s32 x, s32 y); - void push_mouse_rup_event(render_target* target, s32 x, s32 y); - void push_mouse_double_click_event(render_target* target, s32 x, s32 y); - void push_char_event(render_target* target, char32_t ch); - void push_mouse_wheel_event(render_target *target, s32 x, s32 y, short delta, int ucNumLines); + // queueing events + virtual void push_window_focus_event(render_target *target) override; + virtual void push_window_defocus_event(render_target *target) override; + virtual void push_mouse_wheel_event(render_target *target, s32 x, s32 y, short delta, int lines) override; + virtual void push_pointer_update(render_target *target, pointer type, u16 ptrid, u16 device, s32 x, s32 y, u32 buttons, u32 pressed, u32 released, s16 clicks) override; + virtual void push_pointer_leave(render_target *target, pointer type, u16 ptrid, u16 device, s32 x, s32 y, u32 released, s16 clicks) override; + virtual void push_pointer_abort(render_target *target, pointer type, u16 ptrid, u16 device, s32 x, s32 y, u32 released, s16 clicks) override; + virtual void push_char_event(render_target *target, char32_t ch) override; void mark_all_as_pressed(); private: + // constants + static constexpr unsigned EVENT_QUEUE_SIZE = 256; // internal state - running_machine & m_machine; // reference to our machine - - /* pressed states; retrieved with ui_input_pressed() */ - bool m_presses_enabled; - osd_ticks_t m_next_repeat[IPT_COUNT]; - u8 m_seqpressed[IPT_COUNT]; - - /* mouse position/info */ - render_target * m_current_mouse_target; - s32 m_current_mouse_x; - s32 m_current_mouse_y; - bool m_current_mouse_down; - ioport_field * m_current_mouse_field; - - /* popped states; ring buffer of ui_events */ - ui_event m_events[EVENT_QUEUE_SIZE]; - int m_events_start; - int m_events_end; + running_machine & m_machine; + + // pressed states; retrieved with pressed() or pressed_repeat() + bool m_presses_enabled; + osd_ticks_t m_next_repeat[IPT_COUNT]; + u8 m_seqpressed[IPT_COUNT]; + + // ring buffer of ui_events + ui_event m_events[EVENT_QUEUE_SIZE]; + int m_events_start; + int m_events_end; }; #endif // MAME_EMU_UIINPUT_H diff --git a/src/emu/validity.cpp b/src/emu/validity.cpp index 8e8519ecffa..92b141f269e 100644 --- a/src/emu/validity.cpp +++ b/src/emu/validity.cpp @@ -12,343 +12,141 @@ #include "validity.h" #include "emuopts.h" +#include "main.h" #include "romload.h" +#include "speaker.h" #include "video/rgbutil.h" -#include <ctype.h> +#include "corestr.h" +#include "path.h" +#include "unicode.h" + +#include <cctype> +#include <sstream> #include <type_traits> #include <typeinfo> -//************************************************************************** -// TYPE DEFINITIONS -//************************************************************************** - -//************************************************************************** -// INLINE FUNCTIONS -//************************************************************************** - -//------------------------------------------------- -// ioport_string_from_index - return an indexed -// string from the I/O port system -//------------------------------------------------- - -inline const char *validity_checker::ioport_string_from_index(u32 index) -{ - return ioport_configurer::string_from_token((const char *)(uintptr_t)index); -} - +namespace { //------------------------------------------------- -// get_defstr_index - return the index of the -// string assuming it is one of the default -// strings +// diamond_inheritance - forward declaration of a +// class to force MSVC to use unknown inheritance +// form of pointers to member functions //------------------------------------------------- -inline int validity_checker::get_defstr_index(const char *string, bool suppress_error) -{ - // check for strings that should be DEF_STR - auto strindex = m_defstr_map.find(string); - if (!suppress_error && strindex != m_defstr_map.end() && string != ioport_string_from_index(strindex->second)) - osd_printf_error("Must use DEF_STR( %s )\n", string); - return (strindex != m_defstr_map.end()) ? strindex->second : 0; -} +class diamond_inheritance; //------------------------------------------------- -// random_u64 -// random_s64 -// random_u32 -// random_s32 +// test_delegate - a delegate that can return a +// result in a register //------------------------------------------------- -#undef rand -inline s32 validity_checker::random_i32() { return s32(random_u32()); } -inline u32 validity_checker::random_u32() { return rand() ^ (rand() << 15); } -inline s64 validity_checker::random_i64() { return s64(random_u64()); } -inline u64 validity_checker::random_u64() { return u64(random_u32()) ^ (u64(random_u32()) << 30); } +using test_delegate = delegate<char (void const *&)>; //------------------------------------------------- -// validate_tag - ensure that the given tag -// meets the general requirements +// make_diamond_class_delegate - make a delegate +// bound to an instance of an incomplete class +// type //------------------------------------------------- -void validity_checker::validate_tag(const char *tag) +test_delegate make_diamond_class_delegate(char (diamond_inheritance::*func)(void const *&), diamond_inheritance *obj) { - // some common names that are now deprecated - if (strcmp(tag, "main") == 0 || strcmp(tag, "audio") == 0 || strcmp(tag, "sound") == 0 || strcmp(tag, "left") == 0 || strcmp(tag, "right") == 0) - osd_printf_error("Invalid generic tag '%s' used\n", tag); - - // scan for invalid characters - static char const *const validchars = "abcdefghijklmnopqrstuvwxyz0123456789_.:^$"; - for (const char *p = tag; *p != 0; p++) - { - // only lower-case permitted - if (*p != tolower(u8(*p))) - { - osd_printf_error("Tag '%s' contains upper-case characters\n", tag); - break; - } - if (*p == ' ') - { - osd_printf_error("Tag '%s' contains spaces\n", tag); - break; - } - if (strchr(validchars, *p) == nullptr) - { - osd_printf_error("Tag '%s' contains invalid character '%c'\n", tag, *p); - break; - } - } - - // find the start of the final tag - const char *begin = strrchr(tag, ':'); - if (begin == nullptr) - begin = tag; - else - begin += 1; - - // 0-length = bad - if (*begin == 0) - osd_printf_error("Found 0-length tag\n"); - - // too short/too long = bad - if (strlen(begin) < MIN_TAG_LENGTH) - osd_printf_error("Tag '%s' is too short (must be at least %d characters)\n", tag, MIN_TAG_LENGTH); + return test_delegate(func, obj); } - -//************************************************************************** -// VALIDATION FUNCTIONS -//************************************************************************** - //------------------------------------------------- -// validity_checker - constructor +// virtual_base - simple class that will be used +// as the top vertex of the diamond //------------------------------------------------- -validity_checker::validity_checker(emu_options &options) - : m_drivlist(options) - , m_errors(0) - , m_warnings(0) - , m_print_verbose(options.verbose()) - , m_current_driver(nullptr) - , m_current_config(nullptr) - , m_current_device(nullptr) - , m_current_ioport(nullptr) - , m_validate_all(false) +struct virtual_base { - // pre-populate the defstr map with all the default strings - for (int strnum = 1; strnum < INPUT_STRING_COUNT; strnum++) - { - const char *string = ioport_string_from_index(strnum); - if (string != nullptr) - m_defstr_map.insert(std::make_pair(string, strnum)); - } -} + char get_base(void const *&p) { p = this; return 'x'; } + int x; +}; -//------------------------------------------------- -// validity_checker - destructor -//------------------------------------------------- - -validity_checker::~validity_checker() -{ - validate_end(); -} //------------------------------------------------- -// check_driver - check a single driver +// virtual_derived_a - first class derived from +// virtual base //------------------------------------------------- -void validity_checker::check_driver(const game_driver &driver) +struct virtual_derived_a : virtual virtual_base { - // simply validate the one driver - validate_begin(); - validate_one(driver); - validate_end(); -} + char get_derived_a(void const *&p) { p = this; return 'a'; } + int a; +}; //------------------------------------------------- -// check_shared_source - check all drivers that -// share the same source file as the given driver +// virtual_derived_b - second class derived from +// virtual base //------------------------------------------------- -void validity_checker::check_shared_source(const game_driver &driver) +struct virtual_derived_b : virtual virtual_base { - // initialize - validate_begin(); - - // then iterate over all drivers and check the ones that share the same source file - m_drivlist.reset(); - while (m_drivlist.next()) - if (strcmp(driver.type.source(), m_drivlist.driver().type.source()) == 0) - validate_one(m_drivlist.driver()); - - // cleanup - validate_end(); -} + char get_derived_b(void const *&p) { p = this; return 'b'; } + int b; +}; //------------------------------------------------- -// check_all_matching - check all drivers whose -// names match the given string +// diamond_inheritance - actual definition of +// class with diamond inheritance //------------------------------------------------- -bool validity_checker::check_all_matching(const char *string) +class diamond_inheritance : public virtual_derived_a, public virtual_derived_b { - // start by checking core stuff - validate_begin(); - validate_core(); - validate_inlines(); - validate_rgb(); - - // if we had warnings or errors, output - if (m_errors > 0 || m_warnings > 0 || !m_verbose_text.empty()) - { - output_via_delegate(OSD_OUTPUT_CHANNEL_ERROR, "Core: %d errors, %d warnings\n", m_errors, m_warnings); - if (m_errors > 0) - output_indented_errors(m_error_text, "Errors"); - if (m_warnings > 0) - output_indented_errors(m_warning_text, "Warnings"); - if (!m_verbose_text.empty()) - output_indented_errors(m_verbose_text, "Messages"); - output_via_delegate(OSD_OUTPUT_CHANNEL_ERROR, "\n"); - } - - // then iterate over all drivers and check them - m_drivlist.reset(); - bool validated_any = false; - while (m_drivlist.next()) - { - if (m_drivlist.matches(string, m_drivlist.driver().name)) - { - validate_one(m_drivlist.driver()); - validated_any = true; - } - } - - // validate devices - if (!string) - validate_device_types(); - - // cleanup - validate_end(); - - // if we failed to match anything, it - if (string && !validated_any) - throw emu_fatalerror(EMU_ERR_NO_SUCH_GAME, "No matching systems found for '%s'", string); - - return !(m_errors > 0 || m_warnings > 0); -} +}; //------------------------------------------------- -// validate_begin - prepare for validation by -// taking over the output callbacks and resetting -// our internal state +// pure_virtual_base - abstract class with a +// vtable //------------------------------------------------- -void validity_checker::validate_begin() +struct pure_virtual_base { - // take over error and warning outputs - osd_output::push(this); - - // reset all our maps - m_names_map.clear(); - m_descriptions_map.clear(); - m_roms_map.clear(); - m_defstr_map.clear(); - m_region_map.clear(); - - // reset internal state - m_errors = 0; - m_warnings = 0; - m_already_checked.clear(); -} + virtual ~pure_virtual_base() = default; + virtual char operator()(void const *&p) const = 0; +}; //------------------------------------------------- -// validate_end - restore output callbacks and -// clean up +// ioport_string_from_index - return an indexed +// string from the I/O port system //------------------------------------------------- -void validity_checker::validate_end() +inline char const *ioport_string_from_index(u32 index) { - // restore the original output callbacks - osd_output::pop(this); + return ioport_configurer::string_from_token(reinterpret_cast<char const *>(uintptr_t(index))); } //------------------------------------------------- -// validate_drivers - master validity checker +// random_u64 +// random_s64 +// random_u32 +// random_s32 //------------------------------------------------- - -void validity_checker::validate_one(const game_driver &driver) -{ - // help verbose validation detect configuration-related crashes - if (m_print_verbose) - output_via_delegate(OSD_OUTPUT_CHANNEL_ERROR, "Validating driver %s (%s)...\n", driver.name, core_filename_extract_base(driver.type.source()).c_str()); - - // set the current driver - m_current_driver = &driver; - m_current_config = nullptr; - m_current_device = nullptr; - m_current_ioport = nullptr; - m_region_map.clear(); - - // reset error/warning state - int start_errors = m_errors; - int start_warnings = m_warnings; - m_error_text.clear(); - m_warning_text.clear(); - m_verbose_text.clear(); - - // wrap in try/except to catch fatalerrors - try - { - machine_config config(driver, m_blank_options); - m_current_config = &config; - validate_driver(); - validate_roms(m_current_config->root_device()); - validate_inputs(); - validate_devices(); - m_current_config = nullptr; - } - catch (emu_fatalerror &err) - { - osd_printf_error("Fatal error %s", err.string()); - } - - // if we had warnings or errors, output - if (m_errors > start_errors || m_warnings > start_warnings || !m_verbose_text.empty()) - { - if (!m_print_verbose) - output_via_delegate(OSD_OUTPUT_CHANNEL_ERROR, "Driver %s (file %s): ", driver.name, core_filename_extract_base(driver.type.source()).c_str()); - output_via_delegate(OSD_OUTPUT_CHANNEL_ERROR, "%d errors, %d warnings\n", m_errors - start_errors, m_warnings - start_warnings); - if (m_errors > start_errors) - output_indented_errors(m_error_text, "Errors"); - if (m_warnings > start_warnings) - output_indented_errors(m_warning_text, "Warnings"); - if (!m_verbose_text.empty()) - output_indented_errors(m_verbose_text, "Messages"); - output_via_delegate(OSD_OUTPUT_CHANNEL_ERROR, "\n"); - } - - // reset the driver/device - m_current_driver = nullptr; - m_current_config = nullptr; - m_current_device = nullptr; - m_current_ioport = nullptr; -} +#undef rand +inline u32 random_u32() { return rand() ^ (rand() << 15); } +inline s32 random_i32() { return s32(random_u32()); } +inline u64 random_u64() { return u64(random_u32()) ^ (u64(random_u32()) << 30); } +inline s64 random_i64() { return s64(random_u64()); } //------------------------------------------------- -// validate_core - validate core internal systems +// validate_integer_semantics - validate that +// integers behave as expected, particularly +// with regards to overflow and shifting //------------------------------------------------- -void validity_checker::validate_core() +void validate_integer_semantics() { // basic system checks if (~0 != -1) osd_printf_error("Machine must be two's complement\n"); @@ -377,13 +175,6 @@ void validity_checker::validate_core() if (a32 >> 1 != -2) osd_printf_error("s32 right shift must be arithmetic\n"); if (a64 >> 1 != -2) osd_printf_error("s64 right shift must be arithmetic\n"); - // check pointer size -#ifdef PTR64 - static_assert(sizeof(void *) == 8, "PTR64 flag enabled, but was compiled for 32-bit target\n"); -#else - static_assert(sizeof(void *) == 4, "PTR64 flag not enabled, but was compiled for 64-bit target\n"); -#endif - // TODO: check if this is actually working // check endianness definition u16 lsbtest = 0; @@ -401,7 +192,7 @@ void validity_checker::validate_core() // behaviors //------------------------------------------------- -void validity_checker::validate_inlines() +void validate_inlines() { volatile u64 testu64a = random_u64(); volatile s64 testi64a = random_i64(); @@ -430,32 +221,32 @@ void validity_checker::validate_inlines() resulti64 = mul_32x32(testi32a, testi32b); expectedi64 = s64(testi32a) * s64(testi32b); if (resulti64 != expectedi64) - osd_printf_error("Error testing mul_32x32 (%08X x %08X) = %08X%08X (expected %08X%08X)\n", testi32a, testi32b, u32(resulti64 >> 32), u32(resulti64), u32(expectedi64 >> 32), u32(expectedi64)); + osd_printf_error("Error testing mul_32x32 (%08X x %08X) = %16X (expected %16X)\n", s32(testi32a), s32(testi32b), resulti64, expectedi64); resultu64 = mulu_32x32(testu32a, testu32b); expectedu64 = u64(testu32a) * u64(testu32b); if (resultu64 != expectedu64) - osd_printf_error("Error testing mulu_32x32 (%08X x %08X) = %08X%08X (expected %08X%08X)\n", testu32a, testu32b, u32(resultu64 >> 32), u32(resultu64), u32(expectedu64 >> 32), u32(expectedu64)); + osd_printf_error("Error testing mulu_32x32 (%08X x %08X) = %16X (expected %16X)\n", u32(testu32a), u32(testu32b), resultu64, expectedu64); resulti32 = mul_32x32_hi(testi32a, testi32b); expectedi32 = (s64(testi32a) * s64(testi32b)) >> 32; if (resulti32 != expectedi32) - osd_printf_error("Error testing mul_32x32_hi (%08X x %08X) = %08X (expected %08X)\n", testi32a, testi32b, resulti32, expectedi32); + osd_printf_error("Error testing mul_32x32_hi (%08X x %08X) = %08X (expected %08X)\n", s32(testi32a), s32(testi32b), resulti32, expectedi32); resultu32 = mulu_32x32_hi(testu32a, testu32b); expectedu32 = (s64(testu32a) * s64(testu32b)) >> 32; if (resultu32 != expectedu32) - osd_printf_error("Error testing mulu_32x32_hi (%08X x %08X) = %08X (expected %08X)\n", testu32a, testu32b, resultu32, expectedu32); + osd_printf_error("Error testing mulu_32x32_hi (%08X x %08X) = %08X (expected %08X)\n", u32(testu32a), u32(testu32b), resultu32, expectedu32); resulti32 = mul_32x32_shift(testi32a, testi32b, 7); expectedi32 = (s64(testi32a) * s64(testi32b)) >> 7; if (resulti32 != expectedi32) - osd_printf_error("Error testing mul_32x32_shift (%08X x %08X) >> 7 = %08X (expected %08X)\n", testi32a, testi32b, resulti32, expectedi32); + osd_printf_error("Error testing mul_32x32_shift (%08X x %08X) >> 7 = %08X (expected %08X)\n", s32(testi32a), s32(testi32b), resulti32, expectedi32); resultu32 = mulu_32x32_shift(testu32a, testu32b, 7); expectedu32 = (s64(testu32a) * s64(testu32b)) >> 7; if (resultu32 != expectedu32) - osd_printf_error("Error testing mulu_32x32_shift (%08X x %08X) >> 7 = %08X (expected %08X)\n", testu32a, testu32b, resultu32, expectedu32); + osd_printf_error("Error testing mulu_32x32_shift (%08X x %08X) >> 7 = %08X (expected %08X)\n", u32(testu32a), u32(testu32b), resultu32, expectedu32); while (s64(testi32a) * s64(0x7fffffff) < testi64a) testi64a /= 2; @@ -465,34 +256,34 @@ void validity_checker::validate_inlines() resulti32 = div_64x32(testi64a, testi32a); expectedi32 = testi64a / s64(testi32a); if (resulti32 != expectedi32) - osd_printf_error("Error testing div_64x32 (%08X%08X / %08X) = %08X (expected %08X)\n", u32(testi64a >> 32), u32(testi64a), testi32a, resulti32, expectedi32); + osd_printf_error("Error testing div_64x32 (%16X / %08X) = %08X (expected %08X)\n", s64(testi64a), s32(testi32a), resulti32, expectedi32); resultu32 = divu_64x32(testu64a, testu32a); expectedu32 = testu64a / u64(testu32a); if (resultu32 != expectedu32) - osd_printf_error("Error testing divu_64x32 (%08X%08X / %08X) = %08X (expected %08X)\n", u32(testu64a >> 32), u32(testu64a), testu32a, resultu32, expectedu32); + osd_printf_error("Error testing divu_64x32 (%16X / %08X) = %08X (expected %08X)\n", u64(testu64a), u32(testu32a), resultu32, expectedu32); - resulti32 = div_64x32_rem(testi64a, testi32a, &remainder); + resulti32 = div_64x32_rem(testi64a, testi32a, remainder); expectedi32 = testi64a / s64(testi32a); expremainder = testi64a % s64(testi32a); if (resulti32 != expectedi32 || remainder != expremainder) - osd_printf_error("Error testing div_64x32_rem (%08X%08X / %08X) = %08X,%08X (expected %08X,%08X)\n", u32(testi64a >> 32), u32(testi64a), testi32a, resulti32, remainder, expectedi32, expremainder); + osd_printf_error("Error testing div_64x32_rem (%16X / %08X) = %08X,%08X (expected %08X,%08X)\n", s64(testi64a), s32(testi32a), resulti32, remainder, expectedi32, expremainder); - resultu32 = divu_64x32_rem(testu64a, testu32a, &uremainder); + resultu32 = divu_64x32_rem(testu64a, testu32a, uremainder); expectedu32 = testu64a / u64(testu32a); expuremainder = testu64a % u64(testu32a); if (resultu32 != expectedu32 || uremainder != expuremainder) - osd_printf_error("Error testing divu_64x32_rem (%08X%08X / %08X) = %08X,%08X (expected %08X,%08X)\n", u32(testu64a >> 32), u32(testu64a), testu32a, resultu32, uremainder, expectedu32, expuremainder); + osd_printf_error("Error testing divu_64x32_rem (%16X / %08X) = %08X,%08X (expected %08X,%08X)\n", u64(testu64a), u32(testu32a), resultu32, uremainder, expectedu32, expuremainder); resulti32 = mod_64x32(testi64a, testi32a); expectedi32 = testi64a % s64(testi32a); if (resulti32 != expectedi32) - osd_printf_error("Error testing mod_64x32 (%08X%08X / %08X) = %08X (expected %08X)\n", u32(testi64a >> 32), u32(testi64a), testi32a, resulti32, expectedi32); + osd_printf_error("Error testing mod_64x32 (%16X / %08X) = %08X (expected %08X)\n", s64(testi64a), s32(testi32a), resulti32, expectedi32); resultu32 = modu_64x32(testu64a, testu32a); expectedu32 = testu64a % u64(testu32a); if (resultu32 != expectedu32) - osd_printf_error("Error testing modu_64x32 (%08X%08X / %08X) = %08X (expected %08X)\n", u32(testu64a >> 32), u32(testu64a), testu32a, resultu32, expectedu32); + osd_printf_error("Error testing modu_64x32 (%16X / %08X) = %08X (expected %08X)\n", u64(testu64a), u32(testu32a), resultu32, expectedu32); while (s64(testi32a) * s64(0x7fffffff) < (s32(testi64a) << 3)) testi64a /= 2; @@ -502,12 +293,12 @@ void validity_checker::validate_inlines() resulti32 = div_32x32_shift(s32(testi64a), testi32a, 3); expectedi32 = (s64(s32(testi64a)) << 3) / s64(testi32a); if (resulti32 != expectedi32) - osd_printf_error("Error testing div_32x32_shift (%08X << 3) / %08X = %08X (expected %08X)\n", s32(testi64a), testi32a, resulti32, expectedi32); + osd_printf_error("Error testing div_32x32_shift (%08X << 3) / %08X = %08X (expected %08X)\n", s32(testi64a), s32(testi32a), resulti32, expectedi32); resultu32 = divu_32x32_shift(u32(testu64a), testu32a, 3); expectedu32 = (u64(u32(testu64a)) << 3) / u64(testu32a); if (resultu32 != expectedu32) - osd_printf_error("Error testing divu_32x32_shift (%08X << 3) / %08X = %08X (expected %08X)\n", u32(testu64a), testu32a, resultu32, expectedu32); + osd_printf_error("Error testing divu_32x32_shift (%08X << 3) / %08X = %08X (expected %08X)\n", u32(testu64a), u32(testu32a), resultu32, expectedu32); if (fabsf(recip_approx(100.0f) - 0.01f) > 0.0001f) osd_printf_error("Error testing recip_approx\n"); @@ -515,14 +306,42 @@ void validity_checker::validate_inlines() for (int i = 0; i <= 32; i++) { u32 t = i < 32 ? (1 << (31 - i) | testu32a >> i) : 0; - u8 resultu8 = count_leading_zeros(t); + u8 resultu8 = count_leading_zeros_32(t); if (resultu8 != i) - osd_printf_error("Error testing count_leading_zeros %08x=%02x (expected %02x)\n", t, resultu8, i); + osd_printf_error("Error testing count_leading_zeros_32 %08x=%02x (expected %02x)\n", t, resultu8, i); t ^= 0xffffffff; - resultu8 = count_leading_ones(t); + resultu8 = count_leading_ones_32(t); if (resultu8 != i) - osd_printf_error("Error testing count_leading_ones %08x=%02x (expected %02x)\n", t, resultu8, i); + osd_printf_error("Error testing count_leading_ones_32 %08x=%02x (expected %02x)\n", t, resultu8, i); + } + + u32 expected32 = testu32a << 1 | testu32a >> 31; + for (int i = -33; i <= 33; i++) + { + u32 resultu32r = rotr_32(testu32a, i); + u32 resultu32l = rotl_32(testu32a, -i); + + if (resultu32r != expected32) + osd_printf_error("Error testing rotr_32 %08x, %d=%08x (expected %08x)\n", u32(testu32a), i, resultu32r, expected32); + if (resultu32l != expected32) + osd_printf_error("Error testing rotl_32 %08x, %d=%08x (expected %08x)\n", u32(testu32a), -i, resultu32l, expected32); + + expected32 = expected32 >> 1 | expected32 << 31; + } + + u64 expected64 = testu64a << 1 | testu64a >> 63; + for (int i = -65; i <= 65; i++) + { + u64 resultu64r = rotr_64(testu64a, i); + u64 resultu64l = rotl_64(testu64a, -i); + + if (resultu64r != expected64) + osd_printf_error("Error testing rotr_64 %016x, %d=%016x (expected %016x)\n", u64(testu64a), i, resultu64r, expected64); + if (resultu64l != expected64) + osd_printf_error("Error testing rotl_64 %016x, %d=%016x (expected %016x)\n", u64(testu64a), -i, resultu64l, expected64); + + expected64 = expected64 >> 1 | expected64 << 63; } } @@ -532,7 +351,7 @@ void validity_checker::validate_inlines() // class //------------------------------------------------- -void validity_checker::validate_rgb() +void validate_rgb() { /* This performs cursory tests of most of the vector-optimised RGB @@ -560,33 +379,33 @@ void validity_checker::validate_rgb() scale2_add_and_clamp(const rgbaint_t&, const rgbaint_t&, const rgbaint_t&) scale_add_and_clamp(const rgbaint_t&, const rgbaint_t&); scale_imm_add_and_clamp(const s32, const rgbaint_t&); - static bilinear_filter(u32, u32, u32, u32, u8, u8) - bilinear_filter_rgbaint(u32, u32, u32, u32, u8, u8) */ - auto random_i32_nolimit = [this] - { - s32 result; - do { result = random_i32(); } while ((result == std::numeric_limits<s32>::min()) || (result == std::numeric_limits<s32>::max())); - return result; - }; + auto random_i32_nolimit = + [] () + { + s32 result; + do { result = random_i32(); } while ((result == std::numeric_limits<s32>::min()) || (result == std::numeric_limits<s32>::max())); + return result; + }; volatile s32 expected_a, expected_r, expected_g, expected_b; volatile s32 actual_a, actual_r, actual_g, actual_b; volatile s32 imm; rgbaint_t rgb, other; rgb_t packed; - auto check_expected = [&] (const char *desc) - { - const volatile s32 a = rgb.get_a32(); - const volatile s32 r = rgb.get_r32(); - const volatile s32 g = rgb.get_g32(); - const volatile s32 b = rgb.get_b32(); - if (a != expected_a) osd_printf_error("Error testing %s get_a32() = %d (expected %d)\n", desc, a, expected_a); - if (r != expected_r) osd_printf_error("Error testing %s get_r32() = %d (expected %d)\n", desc, r, expected_r); - if (g != expected_g) osd_printf_error("Error testing %s get_g32() = %d (expected %d)\n", desc, g, expected_g); - if (b != expected_b) osd_printf_error("Error testing %s get_b32() = %d (expected %d)\n", desc, b, expected_b); - }; + auto check_expected = + [&] (const char *desc) + { + const volatile s32 a = rgb.get_a32(); + const volatile s32 r = rgb.get_r32(); + const volatile s32 g = rgb.get_g32(); + const volatile s32 b = rgb.get_b32(); + if (a != expected_a) osd_printf_error("Error testing %s get_a32() = %d (expected %d)\n", desc, s32(a), s32(expected_a)); + if (r != expected_r) osd_printf_error("Error testing %s get_r32() = %d (expected %d)\n", desc, s32(r), s32(expected_r)); + if (g != expected_g) osd_printf_error("Error testing %s get_g32() = %d (expected %d)\n", desc, s32(g), s32(expected_g)); + if (b != expected_b) osd_printf_error("Error testing %s get_b32() = %d (expected %d)\n", desc, s32(b), s32(expected_b)); + }; // check set/get expected_a = random_i32(); @@ -901,10 +720,10 @@ void validity_checker::validate_rgb() actual_r = s32(u32(rgb.get_r())); actual_g = s32(u32(rgb.get_g())); actual_b = s32(u32(rgb.get_b())); - if (actual_a != expected_a) osd_printf_error("Error testing rgbaint_t::get_a() = %d (expected %d)\n", actual_a, expected_a); - if (actual_r != expected_r) osd_printf_error("Error testing rgbaint_t::get_r() = %d (expected %d)\n", actual_r, expected_r); - if (actual_g != expected_g) osd_printf_error("Error testing rgbaint_t::get_g() = %d (expected %d)\n", actual_g, expected_g); - if (actual_b != expected_b) osd_printf_error("Error testing rgbaint_t::get_b() = %d (expected %d)\n", actual_b, expected_b); + if (actual_a != expected_a) osd_printf_error("Error testing rgbaint_t::get_a() = %d (expected %d)\n", s32(actual_a), s32(expected_a)); + if (actual_r != expected_r) osd_printf_error("Error testing rgbaint_t::get_r() = %d (expected %d)\n", s32(actual_r), s32(expected_r)); + if (actual_g != expected_g) osd_printf_error("Error testing rgbaint_t::get_g() = %d (expected %d)\n", s32(actual_g), s32(expected_g)); + if (actual_b != expected_b) osd_printf_error("Error testing rgbaint_t::get_b() = %d (expected %d)\n", s32(actual_b), s32(expected_b)); // test set from packed RGBA imm = random_i32(); @@ -1009,6 +828,15 @@ void validity_checker::validate_rgb() rgb.shl(rgbaint_t(19, 3, 21, 6)); check_expected("rgbaint_t::shl"); + // test shift left out of range + expected_a = (actual_a = random_i32()) & 0; + expected_r = (actual_r = random_i32()) & 0; + expected_g = (actual_g = random_i32()) & 0; + expected_b = (actual_b = random_i32()) & 0; + rgb.set(actual_a, actual_r, actual_g, actual_b); + rgb.shl(rgbaint_t(-19, 32, -21, 38)); + check_expected("rgbaint_t::shl"); + // test shift left immediate expected_a = (actual_a = random_i32()) << 7; expected_r = (actual_r = random_i32()) << 7; @@ -1018,6 +846,15 @@ void validity_checker::validate_rgb() rgb.shl_imm(7); check_expected("rgbaint_t::shl_imm"); + // test shift left immediate out of range + expected_a = (actual_a = random_i32()) & 0; + expected_r = (actual_r = random_i32()) & 0; + expected_g = (actual_g = random_i32()) & 0; + expected_b = (actual_b = random_i32()) & 0; + rgb.set(actual_a, actual_r, actual_g, actual_b); + rgb.shl_imm(32); + check_expected("rgbaint_t::shl_imm"); + // test logical shift right expected_a = s32(u32(actual_a = random_i32()) >> 8); expected_r = s32(u32(actual_r = random_i32()) >> 18); @@ -1036,6 +873,15 @@ void validity_checker::validate_rgb() rgb.shr(rgbaint_t(21, 13, 11, 17)); check_expected("rgbaint_t::shr"); + // test logical shift right out of range + expected_a = (actual_a = random_i32()) & 0; + expected_r = (actual_r = random_i32()) & 0; + expected_g = (actual_g = random_i32()) & 0; + expected_b = (actual_b = random_i32()) & 0; + rgb.set(actual_a, actual_r, actual_g, actual_b); + rgb.shr(rgbaint_t(40, -18, -26, 32)); + check_expected("rgbaint_t::shr"); + // test logical shift right immediate expected_a = s32(u32(actual_a = random_i32()) >> 5); expected_r = s32(u32(actual_r = random_i32()) >> 5); @@ -1054,6 +900,15 @@ void validity_checker::validate_rgb() rgb.shr_imm(15); check_expected("rgbaint_t::shr_imm"); + // test logical shift right immediate out of range + expected_a = (actual_a = random_i32()) & 0; + expected_r = (actual_r = random_i32()) & 0; + expected_g = (actual_g = random_i32()) & 0; + expected_b = (actual_b = random_i32()) & 0; + rgb.set(actual_a, actual_r, actual_g, actual_b); + rgb.shr_imm(35); + check_expected("rgbaint_t::shr_imm"); + // test arithmetic shift right expected_a = (actual_a = random_i32()) >> 16; expected_r = (actual_r = random_i32()) >> 20; @@ -1072,6 +927,15 @@ void validity_checker::validate_rgb() rgb.sra(rgbaint_t(1, 29, 10, 22)); check_expected("rgbaint_t::sra"); + // test arithmetic shift right out of range + expected_a = (actual_a = random_i32()) >> 31; + expected_r = (actual_r = random_i32()) >> 31; + expected_g = (actual_g = random_i32()) >> 31; + expected_b = (actual_b = random_i32()) >> 31; + rgb.set(actual_a, actual_r, actual_g, actual_b); + rgb.sra(rgbaint_t(-16, -20, 46, 32)); + check_expected("rgbaint_t::sra"); + // test arithmetic shift right immediate (method) expected_a = (actual_a = random_i32()) >> 12; expected_r = (actual_r = random_i32()) >> 12; @@ -1090,6 +954,15 @@ void validity_checker::validate_rgb() rgb.sra_imm(9); check_expected("rgbaint_t::sra_imm"); + // test arithmetic shift right immediate out of range (method) + expected_a = (actual_a = random_i32()) >> 31; + expected_r = (actual_r = random_i32()) >> 31; + expected_g = (actual_g = random_i32()) >> 31; + expected_b = (actual_b = random_i32()) >> 31; + rgb.set(actual_a, actual_r, actual_g, actual_b); + rgb.sra_imm(38); + check_expected("rgbaint_t::sra_imm"); + // test arithmetic shift right immediate (operator) expected_a = (actual_a = random_i32()) >> 7; expected_r = (actual_r = random_i32()) >> 7; @@ -1108,6 +981,15 @@ void validity_checker::validate_rgb() rgb >>= 11; check_expected("rgbaint_t::operator>>="); + // test arithmetic shift right immediate out of range (operator) + expected_a = (actual_a = random_i32()) >> 31; + expected_r = (actual_r = random_i32()) >> 31; + expected_g = (actual_g = random_i32()) >> 31; + expected_b = (actual_b = random_i32()) >> 31; + rgb.set(actual_a, actual_r, actual_g, actual_b); + rgb >>= 41; + check_expected("rgbaint_t::operator>>="); + // test RGB equality comparison actual_a = random_i32_nolimit(); actual_r = random_i32_nolimit(); @@ -1392,6 +1274,767 @@ void validity_checker::validate_rgb() rgb.set(actual_a, actual_r, actual_g, actual_b); rgb.cmplt_imm_rgba(actual_a + 1, std::numeric_limits<s32>::min(), std::numeric_limits<s32>::max(), actual_b); check_expected("rgbaint_t::cmplt_imm_rgba"); + + // test bilinear_filter and bilinear_filter_rgbaint + // SSE implementation carries more internal precision between the bilinear stages +#if defined(MAME_RGB_HIGH_PRECISION) + const int first_shift = 1; +#else + const int first_shift = 8; +#endif + for (int index = 0; index < 1000; index++) + { + u8 u, v; + rgbaint_t rgb_point[4]; + u32 top_row, bottom_row; + + for (int i = 0; i < 4; i++) + { + rgb_point[i].set(random_u32()); + } + + switch (index) + { + case 0: u = 0; v = 0; break; + case 1: u = 255; v = 255; break; + case 2: u = 0; v = 255; break; + case 3: u = 255; v = 0; break; + case 4: u = 128; v = 128; break; + case 5: u = 63; v = 32; break; + default: + u = random_u32() & 0xff; + v = random_u32() & 0xff; + break; + } + + top_row = (rgb_point[0].get_a() * (256 - u) + rgb_point[1].get_a() * u) >> first_shift; + bottom_row = (rgb_point[2].get_a() * (256 - u) + rgb_point[3].get_a() * u) >> first_shift; + expected_a = (top_row * (256 - v) + bottom_row * v) >> (16 - first_shift); + + top_row = (rgb_point[0].get_r() * (256 - u) + rgb_point[1].get_r() * u) >> first_shift; + bottom_row = (rgb_point[2].get_r() * (256 - u) + rgb_point[3].get_r() * u) >> first_shift; + expected_r = (top_row * (256 - v) + bottom_row * v) >> (16 - first_shift); + + top_row = (rgb_point[0].get_g() * (256 - u) + rgb_point[1].get_g() * u) >> first_shift; + bottom_row = (rgb_point[2].get_g() * (256 - u) + rgb_point[3].get_g() * u) >> first_shift; + expected_g = (top_row * (256 - v) + bottom_row * v) >> (16 - first_shift); + + top_row = (rgb_point[0].get_b() * (256 - u) + rgb_point[1].get_b() * u) >> first_shift; + bottom_row = (rgb_point[2].get_b() * (256 - u) + rgb_point[3].get_b() * u) >> first_shift; + expected_b = (top_row * (256 - v) + bottom_row * v) >> (16 - first_shift); + + imm = rgbaint_t::bilinear_filter(rgb_point[0].to_rgba(), rgb_point[1].to_rgba(), rgb_point[2].to_rgba(), rgb_point[3].to_rgba(), u, v); + rgb.set(imm); + check_expected("rgbaint_t::bilinear_filter"); + + rgb.bilinear_filter_rgbaint(rgb_point[0].to_rgba(), rgb_point[1].to_rgba(), rgb_point[2].to_rgba(), rgb_point[3].to_rgba(), u, v); + check_expected("rgbaint_t::bilinear_filter_rgbaint"); + } +} + + +//------------------------------------------------- +// validate_delegates_mfp - test delegate member +// function functionality +//------------------------------------------------- + +void validate_delegates_mfp() +{ + struct base_a + { + virtual ~base_a() = default; + char get_a(void const *&p) { p = this; return 'a'; } + virtual char get_a_v(void const *&p) { p = this; return 'A'; } + int a; + }; + + struct base_b + { + virtual ~base_b() = default; + char get_b(void const *&p) { p = this; return 'b'; } + virtual char get_b_v(void const *&p) { p = this; return 'B'; } + int b; + }; + + struct multiple_inheritance : base_a, base_b + { + }; + + struct overridden : base_a, base_b + { + virtual char get_a_v(void const *&p) override { p = this; return 'x'; } + virtual char get_b_v(void const *&p) override { p = this; return 'y'; } + }; + + multiple_inheritance mi; + overridden o; + diamond_inheritance d; + char ch; + void const *addr; + + // test non-virtual member functions and "this" pointer adjustment + test_delegate cb1(&multiple_inheritance::get_a, &mi); + test_delegate cb2(&multiple_inheritance::get_b, &mi); + + addr = nullptr; + ch = cb1(addr); + if ('a' != ch) + osd_printf_error("Error testing delegate non-virtual member function dispatch\n"); + if (static_cast<base_a *>(&mi) != addr) + osd_printf_error("Error testing delegate this pointer adjustment %p -> %p (expected %p)\n", static_cast<void const *>(&mi), addr, static_cast<void const *>(static_cast<base_a *>(&mi))); + + addr = nullptr; + ch = cb2(addr); + if ('b' != ch) + osd_printf_error("Error testing delegate non-virtual member function dispatch\n"); + if (static_cast<base_b *>(&mi) != addr) + osd_printf_error("Error testing delegate this pointer adjustment %p -> %p (expected %p)\n", static_cast<void const *>(&mi), addr, static_cast<void const *>(static_cast<base_b *>(&mi))); + + // test that "this" pointer adjustment survives copy construction + test_delegate cb3(cb1); + test_delegate cb4(cb2); + + addr = nullptr; + ch = cb3(addr); + if ('a' != ch) + osd_printf_error("Error testing copy constructed delegate non-virtual member function dispatch\n"); + if (static_cast<base_a *>(&mi) != addr) + osd_printf_error("Error testing copy constructed delegate this pointer adjustment %p -> %p (expected %p)\n", static_cast<void const *>(&mi), addr, static_cast<void const *>(static_cast<base_a *>(&mi))); + + addr = nullptr; + ch = cb4(addr); + if ('b' != ch) + osd_printf_error("Error testing copy constructed delegate non-virtual member function dispatch\n"); + if (static_cast<base_b *>(&mi) != addr) + osd_printf_error("Error testing copy constructed delegate this pointer adjustment %p -> %p (expected %p)\n", static_cast<void const *>(&mi), addr, static_cast<void const *>(static_cast<base_b *>(&mi))); + + // test that "this" pointer adjustment survives assignment and doesn't suffer generational loss + cb1 = cb4; + cb2 = cb3; + + addr = nullptr; + ch = cb1(addr); + if ('b' != ch) + osd_printf_error("Error testing assigned delegate non-virtual member function dispatch\n"); + if (static_cast<base_b *>(&mi) != addr) + osd_printf_error("Error testing assigned delegate this pointer adjustment %p -> %p (expected %p)\n", static_cast<void const *>(&mi), addr, static_cast<void const *>(static_cast<base_b *>(&mi))); + + addr = nullptr; + ch = cb2(addr); + if ('a' != ch) + osd_printf_error("Error testing assigned delegate non-virtual member function dispatch\n"); + if (static_cast<base_a *>(&mi) != addr) + osd_printf_error("Error testing assigned delegate this pointer adjustment %p -> %p (expected %p)\n", static_cast<void const *>(&mi), addr, static_cast<void const *>(static_cast<base_a *>(&mi))); + + // test virtual member functions and "this" pointer adjustment + cb1 = test_delegate(&multiple_inheritance::get_a_v, &mi); + cb2 = test_delegate(&multiple_inheritance::get_b_v, &mi); + + addr = nullptr; + ch = cb1(addr); + if ('A' != ch) + osd_printf_error("Error testing delegate virtual member function dispatch\n"); + if (static_cast<base_a *>(&mi) != addr) + osd_printf_error("Error testing delegate this pointer adjustment for virtual member function %p -> %p (expected %p)\n", static_cast<void const *>(&mi), addr, static_cast<void const *>(static_cast<base_a *>(&mi))); + + addr = nullptr; + ch = cb2(addr); + if ('B' != ch) + osd_printf_error("Error testing delegate virtual member function dispatch\n"); + if (static_cast<base_b *>(&mi) != addr) + osd_printf_error("Error testing delegate this pointer adjustment for virtual member function %p -> %p (expected %p)\n", static_cast<void const *>(&mi), addr, static_cast<void const *>(static_cast<base_b *>(&mi))); + + // test that virtual member functions survive copy construction + test_delegate cb5(cb1); + test_delegate cb6(cb2); + + addr = nullptr; + ch = cb5(addr); + if ('A' != ch) + osd_printf_error("Error testing copy constructed delegate virtual member function dispatch\n"); + if (static_cast<base_a *>(&mi) != addr) + osd_printf_error("Error testing copy constructed delegate this pointer adjustment for virtual member function %p -> %p (expected %p)\n", static_cast<void const *>(&mi), addr, static_cast<void const *>(static_cast<base_a *>(&mi))); + + addr = nullptr; + ch = cb6(addr); + if ('B' != ch) + osd_printf_error("Error testing copy constructed delegate virtual member function dispatch\n"); + if (static_cast<base_b *>(&mi) != addr) + osd_printf_error("Error testing copy constructed delegate this pointer adjustment for virtual member function %p -> %p (expected %p)\n", static_cast<void const *>(&mi), addr, static_cast<void const *>(static_cast<base_b *>(&mi))); + + // test virtual member function dispatch through base pointer + cb1 = test_delegate(&base_a::get_a_v, static_cast<base_a *>(&o)); + cb2 = test_delegate(&base_b::get_b_v, static_cast<base_b *>(&o)); + + addr = nullptr; + ch = cb1(addr); + if ('x' != ch) + osd_printf_error("Error testing delegate virtual member function dispatch through base class pointer\n"); + if (&o != addr) + osd_printf_error("Error testing delegate this pointer adjustment for virtual member function through base class pointer %p -> %p (expected %p)\n", static_cast<void const *>(static_cast<base_a *>(&o)), addr, static_cast<void const *>(&o)); + + addr = nullptr; + ch = cb2(addr); + if ('y' != ch) + osd_printf_error("Error testing delegate virtual member function dispatch through base class pointer\n"); + if (&o != addr) + osd_printf_error("Error testing delegate this pointer adjustment for virtual member function through base class pointer %p -> %p (expected %p)\n", static_cast<void const *>(static_cast<base_b *>(&o)), addr, static_cast<void const *>(&o)); + + // test creating delegates for a forward-declared class + cb1 = make_diamond_class_delegate(&diamond_inheritance::get_derived_a, &d); + cb2 = make_diamond_class_delegate(&diamond_inheritance::get_derived_b, &d); + + addr = nullptr; + ch = cb1(addr); + if ('a' != ch) + osd_printf_error("Error testing delegate non-virtual member function dispatch for incomplete class\n"); + if (static_cast<virtual_derived_a *>(&d) != addr) + osd_printf_error("Error testing delegate this pointer adjustment for incomplete class %p -> %p (expected %p)\n", static_cast<void const *>(&d), addr, static_cast<void const *>(static_cast<virtual_derived_b *>(&d))); + + addr = nullptr; + ch = cb2(addr); + if ('b' != ch) + osd_printf_error("Error testing delegate non-virtual member function dispatch for incomplete class\n"); + if (static_cast<virtual_derived_b *>(&d) != addr) + osd_printf_error("Error testing delegate this pointer adjustment for incomplete class %p -> %p (expected %p)\n", static_cast<void const *>(&d), addr, static_cast<void const *>(static_cast<virtual_derived_b *>(&d))); + +#if defined(_MSC_VER) && !defined(__clang__) + // test MSVC extension allowing casting member pointer types across virtual inheritance relationships + cb1 = make_diamond_class_delegate(&diamond_inheritance::get_base, &d); + + addr = nullptr; + ch = cb1(addr); + if ('x' != ch) + osd_printf_error("Error testing delegate non-virtual member function dispatch for incomplete class\n"); + if (static_cast<virtual_base *>(&d) != addr) + osd_printf_error("Error testing delegate this pointer adjustment for incomplete class %p -> %p (expected %p)\n", static_cast<void const *>(&d), addr, static_cast<void const *>(static_cast<virtual_base *>(&d))); +#endif // defined(_MSC_VER) && !defined(__clang__) +} + + +//------------------------------------------------- +// validate_delegates_latebind - test binding a +// delegate to an object after the function is +// set +//------------------------------------------------- + +void validate_delegates_latebind() +{ + struct derived_a : pure_virtual_base, delegate_late_bind + { + virtual char operator()(void const *&p) const override { p = this; return 'a'; } + }; + + struct derived_b : pure_virtual_base, delegate_late_bind + { + virtual char operator()(void const *&p) const override { p = this; return 'b'; } + }; + + struct unrelated : delegate_late_bind + { + }; + + char ch; + void const *addr; + derived_a a; + derived_b b; + unrelated u; + + // delegate with no target object + test_delegate cb1(&pure_virtual_base::operator(), static_cast<pure_virtual_base *>(nullptr)); + + // test late bind on construction + test_delegate cb2(cb1, a); + addr = nullptr; + ch = cb2(addr); + if (('a' != ch) || (&a != addr)) + osd_printf_error("Error testing delegate late bind on construction\n"); + + // test explicit late bind + cb1.late_bind(b); + ch = cb1(addr); + if (('b' != ch) || (&b != addr)) + osd_printf_error("Error testing delegate explicit late bind\n"); + + // test late bind when object is set + cb1.late_bind(a); + ch = cb1(addr); + if (('a' != ch) || (&a != addr)) + osd_printf_error("Error testing delegate explicit late bind when object is set\n"); + + // test late bind on copy of delegate with target set + test_delegate cb3(cb1, b); + addr = nullptr; + ch = cb3(addr); + if (('b' != ch) || (&b != addr)) + osd_printf_error("Error testing delegate late bind on construction using template with object set\n"); + + // test late bind exception + ch = '-'; + try + { + cb1.late_bind(u); + } + catch (binding_type_exception const &e) + { + if ((e.target_type() != typeid(pure_virtual_base)) || (e.actual_type() != typeid(unrelated))) + { + osd_printf_error( + "Error testing delegate late bind type error %s -> %s (expected %s -> %s)\n", + e.actual_type().name(), + e.target_type().name(), + typeid(unrelated).name(), + typeid(pure_virtual_base).name()); + } + ch = '+'; + } + if ('+' != ch) + osd_printf_error("Error testing delegate late bind type error\n"); + + // test syntax for creating delegate with alternate late bind base + delegate<char (void const *&), pure_virtual_base> cb4( + [] (auto &o, void const *&p) { p = &o; return 'l'; }, + static_cast<unrelated *>(nullptr)); + try { cb1.late_bind(a); } + catch (binding_type_exception const &) { } +} + + +//------------------------------------------------- +// validate_delegates_functoid - test delegate +// functoid functionality +//------------------------------------------------- + +void validate_delegates_functoid() +{ + using void_delegate = delegate<void (void const *&)>; + struct const_only + { + char operator()(void const *&p) const { return 'C'; } + }; + + struct const_or_not + { + char operator()(void const *&p) { return 'n'; } + char operator()(void const *&p) const { return 'c'; } + }; + + struct noncopyable + { + noncopyable() = default; + noncopyable(noncopyable const &) = delete; + noncopyable &operator=(noncopyable const &) = delete; + + char operator()(void const *&p) { p = this; return '*'; } + }; + + noncopyable n; + char ch; + void const *addr = nullptr; + + // test that const call operators are supported + test_delegate cb1{ const_only() }; + if ('C' != cb1(addr)) + osd_printf_error("Error testing delegate functoid dispatch\n"); + + // test that non-const call operators are preferred + cb1 = test_delegate{ const_or_not() }; + if ('n' != cb1(addr)) + osd_printf_error("Error testing delegate functoid dispatch\n"); + + // test that functoids are implicitly mutable + cb1 = test_delegate{ [a = &addr, c = '0'] (void const *&p) mutable { p = a; return c++; } }; + + addr = nullptr; + ch = cb1(addr); + if (('0' != ch) || (&addr != addr)) + osd_printf_error("Error testing delegate functoid %c (expected 0)\n", ch); + + addr = nullptr; + ch = cb1(addr); + if (('1' != ch) || (&addr != addr)) + osd_printf_error("Error testing delegate functoid %c (expected 1)\n", ch); + + // test that functoids survive copy construction + test_delegate cb2(cb1); + + addr = nullptr; + ch = cb2(addr); + if (('2' != ch) || (&addr != addr)) + osd_printf_error("Error testing delegate functoid %c (expected 2)\n", ch); + + addr = nullptr; + ch = cb2(addr); + if (('3' != ch) || (&addr != addr)) + osd_printf_error("Error testing delegate functoid %c (expected 3)\n", ch); + + addr = nullptr; + ch = cb1(addr); + if (('2' != ch) || (&addr != addr)) + osd_printf_error("Error testing delegate functoid %c (expected 2)\n", ch); + + // test that functoids survive assignment + cb1 = cb2; + + addr = nullptr; + ch = cb1(addr); + if (('4' != ch) || (&addr != addr)) + osd_printf_error("Error testing delegate functoid %c (expected 4)\n", ch); + + addr = nullptr; + ch = cb1(addr); + if (('5' != ch) || (&addr != addr)) + osd_printf_error("Error testing delegate functoid %c (expected 5)\n", ch); + + addr = nullptr; + ch = cb2(addr); + if (('4' != ch) || (&addr != addr)) + osd_printf_error("Error testing delegate functoid %c (expected 4)\n", ch); + + // test that std::ref can be used with non-copyable functoids + test_delegate cb3(std::ref(n)); + + addr = nullptr; + ch = cb3(addr); + if (('*' != ch) || (&n != addr)) + osd_printf_error("Error testing delegate with functoid reference wrapper %p (expected %p)\n", addr, static_cast<void const *>(&n)); + + // test that std::ref survives copy construction and assignment + cb2 = cb3; + test_delegate cb4(cb3); + + addr = nullptr; + ch = cb2(addr); + if (('*' != ch) || (&n != addr)) + osd_printf_error("Error testing delegate with functoid reference wrapper %p (expected %p)\n", addr, static_cast<void const *>(&n)); + + addr = nullptr; + ch = cb4(addr); + if (('*' != ch) || (&n != addr)) + osd_printf_error("Error testing delegate with functoid reference wrapper %p (expected %p)\n", addr, static_cast<void const *>(&n)); + + // test discarding return value for delegates returning void + void_delegate void_cb1{ [&cb1] (void const *&p) { p = &cb1; return 123; } }; + void_delegate void_cb2{ std::ref(n) }; + + addr = nullptr; + void_cb1(addr); + if (&cb1 != addr) + osd_printf_error("Error testing delegate with functoid requiring adapter %p (expected %p)\n", addr, static_cast<void const *>(&cb1)); + + addr = nullptr; + void_cb2(addr); + if (&n != addr) + osd_printf_error("Error testing delegate with functoid requiring adapter %p (expected %p)\n", addr, static_cast<void const *>(&n)); + + // test that adaptor is generated after assignment + void_cb2 = void_cb1; + + addr = nullptr; + void_cb2(addr); + if (&cb1 != addr) + osd_printf_error("Error testing delegate with functoid requiring adapter %p (expected %p)\n", addr, static_cast<void const *>(&cb1)); +} + +} // anonymous namespace + + + +//------------------------------------------------- +// get_defstr_index - return the index of the +// string assuming it is one of the default +// strings +//------------------------------------------------- + +inline int validity_checker::get_defstr_index(const char *string, bool suppress_error) +{ + // check for strings that should be DEF_STR + auto strindex = m_defstr_map.find(string); + if (!suppress_error && strindex != m_defstr_map.end() && string != ioport_string_from_index(strindex->second)) + osd_printf_error("Must use DEF_STR( %s )\n", string); + return (strindex != m_defstr_map.end()) ? strindex->second : 0; +} + + + +//------------------------------------------------- +// validate_tag - ensure that the given tag +// meets the general requirements +//------------------------------------------------- + +void validity_checker::validate_tag(const char *tag) +{ + // some common names that are now deprecated + if (strcmp(tag, "main") == 0 || strcmp(tag, "audio") == 0 || strcmp(tag, "sound") == 0 || strcmp(tag, "left") == 0 || strcmp(tag, "right") == 0) + osd_printf_error("Invalid generic tag '%s' used\n", tag); + + // scan for invalid characters + static char const *const validchars = "abcdefghijklmnopqrstuvwxyz0123456789_.:^$"; + for (char const *p = tag; *p; ++p) + { + // only lower-case permitted + if (*p != tolower(u8(*p))) + { + osd_printf_error("Tag '%s' contains upper-case characters\n", tag); + break; + } + if (*p == ' ') + { + osd_printf_error("Tag '%s' contains spaces\n", tag); + break; + } + if (!strchr(validchars, *p)) + { + osd_printf_error("Tag '%s' contains invalid character '%c'\n", tag, *p); + break; + } + } + + // find the start of the final tag + const char *begin = strrchr(tag, ':'); + if (begin == nullptr) + begin = tag; + else + begin += 1; + + // 0-length = bad + if (*begin == 0) + osd_printf_error("Found 0-length tag\n"); + + // too short/too long = bad + if (strlen(begin) < MIN_TAG_LENGTH) + osd_printf_error("Tag '%s' is too short (must be at least %d characters)\n", tag, MIN_TAG_LENGTH); +} + + + +//************************************************************************** +// VALIDATION FUNCTIONS +//************************************************************************** + +//------------------------------------------------- +// validity_checker - constructor +//------------------------------------------------- + +validity_checker::validity_checker(emu_options &options, bool quick) + : m_drivlist(options) + , m_errors(0) + , m_warnings(0) + , m_print_verbose(options.verbose()) + , m_current_driver(nullptr) + , m_current_device(nullptr) + , m_current_ioport(nullptr) + , m_checking_card(false) + , m_quick(quick) +{ + // pre-populate the defstr map with all the default strings + for (int strnum = 1; strnum < INPUT_STRING_COUNT; strnum++) + { + const char *string = ioport_string_from_index(strnum); + if (string != nullptr) + m_defstr_map.insert(std::make_pair(string, strnum)); + } +} + +//------------------------------------------------- +// validity_checker - destructor +//------------------------------------------------- + +validity_checker::~validity_checker() +{ + validate_end(); +} + +//------------------------------------------------- +// check_driver - check a single driver +//------------------------------------------------- + +void validity_checker::check_driver(const game_driver &driver) +{ + // simply validate the one driver + validate_begin(); + validate_one(driver); + validate_end(); +} + + +//------------------------------------------------- +// check_shared_source - check all drivers that +// share the same source file as the given driver +//------------------------------------------------- + +void validity_checker::check_shared_source(const game_driver &driver) +{ + // initialize + validate_begin(); + + // then iterate over all drivers and check the ones that share the same source file + m_drivlist.reset(); + while (m_drivlist.next()) + if (strcmp(driver.type.source(), m_drivlist.driver().type.source()) == 0) + validate_one(m_drivlist.driver()); + + // cleanup + validate_end(); +} + + +//------------------------------------------------- +// check_all_matching - check all drivers whose +// names match the given string +//------------------------------------------------- + +bool validity_checker::check_all_matching(const char *string) +{ + // start by checking core stuff + validate_begin(); + validate_integer_semantics(); + validate_inlines(); + validate_rgb(); + validate_delegates_mfp(); + validate_delegates_latebind(); + validate_delegates_functoid(); + + // if we had warnings or errors, output + if (m_errors > 0 || m_warnings > 0 || !m_verbose_text.empty()) + { + output_via_delegate(OSD_OUTPUT_CHANNEL_ERROR, "Core: %d errors, %d warnings\n", m_errors, m_warnings); + if (m_errors > 0) + output_indented_errors(m_error_text, "Errors"); + if (m_warnings > 0) + output_indented_errors(m_warning_text, "Warnings"); + if (!m_verbose_text.empty()) + output_indented_errors(m_verbose_text, "Messages"); + output_via_delegate(OSD_OUTPUT_CHANNEL_ERROR, "\n"); + } + + // then iterate over all drivers and check them + m_drivlist.reset(); + bool validated_any = false; + while (m_drivlist.next()) + { + if (driver_list::matches(string, m_drivlist.driver().name)) + { + validate_one(m_drivlist.driver()); + validated_any = true; + } + } + + // validate devices + if (!string) + validate_device_types(); + + // cleanup + validate_end(); + + // if we failed to match anything, it + if (string && !validated_any) + throw emu_fatalerror(EMU_ERR_NO_SUCH_SYSTEM, "No matching systems found for '%s'", string); + + return !(m_errors > 0 || m_warnings > 0); +} + + +//------------------------------------------------- +// validate_begin - prepare for validation by +// taking over the output callbacks and resetting +// our internal state +//------------------------------------------------- + +void validity_checker::validate_begin() +{ + // take over error and warning outputs + osd_output::push(this); + + // reset all our maps + m_names_map.clear(); + m_descriptions_map.clear(); + m_roms_map.clear(); + m_defstr_map.clear(); + m_region_map.clear(); + m_ioport_set.clear(); + m_slotcard_set.clear(); + + // reset internal state + m_errors = 0; + m_warnings = 0; + m_already_checked.clear(); +} + + +//------------------------------------------------- +// validate_end - restore output callbacks and +// clean up +//------------------------------------------------- + +void validity_checker::validate_end() +{ + // restore the original output callbacks + osd_output::pop(this); +} + + +//------------------------------------------------- +// validate_drivers - master validity checker +//------------------------------------------------- + +void validity_checker::validate_one(const game_driver &driver) +{ + // help verbose validation detect configuration-related crashes + if (m_print_verbose) + output_via_delegate(OSD_OUTPUT_CHANNEL_ERROR, "Validating driver %s (%s)...\n", driver.name, core_filename_extract_base(driver.type.source())); + + // set the current driver + m_current_driver = &driver; + m_current_device = nullptr; + m_current_ioport = nullptr; + m_region_map.clear(); + m_ioport_set.clear(); + m_checking_card = false; + + // reset error/warning state + int start_errors = m_errors; + int start_warnings = m_warnings; + m_error_text.clear(); + m_warning_text.clear(); + m_verbose_text.clear(); + + // wrap in try/catch to catch fatalerrors + try + { + machine_config config(driver, m_blank_options); + validate_driver(config.root_device()); + validate_roms(config.root_device()); + validate_inputs(config.root_device()); + validate_devices(config); + } + catch (emu_fatalerror const &err) + { + osd_printf_error("Fatal error %s", err.what()); + } + + // if we had warnings or errors, output + if (m_errors > start_errors || m_warnings > start_warnings || !m_verbose_text.empty()) + { + if (!m_print_verbose) + output_via_delegate(OSD_OUTPUT_CHANNEL_ERROR, "Driver %s (file %s): ", driver.name, core_filename_extract_base(driver.type.source())); + output_via_delegate(OSD_OUTPUT_CHANNEL_ERROR, "%d errors, %d warnings\n", m_errors - start_errors, m_warnings - start_warnings); + if (m_errors > start_errors) + output_indented_errors(m_error_text, "Errors"); + if (m_warnings > start_warnings) + output_indented_errors(m_warning_text, "Warnings"); + if (!m_verbose_text.empty()) + output_indented_errors(m_verbose_text, "Messages"); + output_via_delegate(OSD_OUTPUT_CHANNEL_ERROR, "\n"); + } + + // reset the driver/device + m_current_driver = nullptr; + m_current_device = nullptr; + m_current_ioport = nullptr; + m_region_map.clear(); + m_ioport_set.clear(); + m_checking_card = false; } @@ -1400,26 +2043,26 @@ void validity_checker::validate_rgb() // information //------------------------------------------------- -void validity_checker::validate_driver() +void validity_checker::validate_driver(device_t &root) { // check for duplicate names if (!m_names_map.insert(std::make_pair(m_current_driver->name, m_current_driver)).second) { const game_driver *match = m_names_map.find(m_current_driver->name)->second; - osd_printf_error("Driver name is a duplicate of %s(%s)\n", core_filename_extract_base(match->type.source()).c_str(), match->name); + osd_printf_error("Driver name is a duplicate of %s(%s)\n", core_filename_extract_base(match->type.source()), match->name); } // check for duplicate descriptions if (!m_descriptions_map.insert(std::make_pair(m_current_driver->type.fullname(), m_current_driver)).second) { const game_driver *match = m_descriptions_map.find(m_current_driver->type.fullname())->second; - osd_printf_error("Driver description is a duplicate of %s(%s)\n", core_filename_extract_base(match->type.source()).c_str(), match->name); + osd_printf_error("Driver description is a duplicate of %s(%s)\n", core_filename_extract_base(match->type.source()), match->name); } // determine if we are a clone bool is_clone = (strcmp(m_current_driver->parent, "0") != 0); - int clone_of = m_drivlist.clone(*m_current_driver); - if (clone_of != -1 && (m_drivlist.driver(clone_of).flags & machine_flags::IS_BIOS_ROOT)) + int clone_of = driver_list::clone(*m_current_driver); + if (clone_of != -1 && (driver_list::driver(clone_of).flags & machine_flags::IS_BIOS_ROOT)) is_clone = false; // if we have at least 100 drivers, validate the clone @@ -1428,13 +2071,17 @@ void validity_checker::validate_driver() osd_printf_error("Driver is a clone of nonexistent driver %s\n", m_current_driver->parent); // look for recursive cloning - if (clone_of != -1 && &m_drivlist.driver(clone_of) == m_current_driver) + if (clone_of != -1 && &driver_list::driver(clone_of) == m_current_driver) osd_printf_error("Driver is a clone of itself\n"); // look for clones that are too deep - if (clone_of != -1 && (clone_of = m_drivlist.non_bios_clone(clone_of)) != -1) + if (clone_of != -1 && (clone_of = driver_list::non_bios_clone(clone_of)) != -1) osd_printf_error("Driver is a clone of a clone\n"); + // look for drivers specifying a parent ROM device type + if (root.type().parent_rom_device_type()) + osd_printf_error("Driver has parent ROM device type '%s'\n", root.type().parent_rom_device_type()->shortname()); + // make sure the driver name is not too long if (!is_clone && strlen(m_current_driver->name) > 16) osd_printf_error("Parent driver name must be 16 characters or less\n"); @@ -1463,16 +2110,16 @@ void validity_checker::validate_driver() compatible_with = nullptr; // check for this driver being compatible with a nonexistent driver - if (compatible_with != nullptr && m_drivlist.find(m_current_driver->compatible_with) == -1) + if (compatible_with != nullptr && driver_list::find(m_current_driver->compatible_with) == -1) osd_printf_error("Driver is listed as compatible with nonexistent driver %s\n", m_current_driver->compatible_with); // check for clone_of and compatible_with being specified at the same time - if (m_drivlist.clone(*m_current_driver) != -1 && compatible_with != nullptr) + if (driver_list::clone(*m_current_driver) != -1 && compatible_with != nullptr) osd_printf_error("Driver cannot be both a clone and listed as compatible with another system\n"); // find any recursive dependencies on the current driver - for (int other_drv = m_drivlist.compatible_with(*m_current_driver); other_drv != -1; other_drv = m_drivlist.compatible_with(other_drv)) - if (m_current_driver == &m_drivlist.driver(other_drv)) + for (int other_drv = driver_list::compatible_with(*m_current_driver); other_drv != -1; other_drv = driver_list::compatible_with(other_drv)) + if (m_current_driver == &driver_list::driver(other_drv)) { osd_printf_error("Driver is recursively compatible with itself\n"); break; @@ -1483,20 +2130,63 @@ void validity_checker::validate_driver() device_t::feature_type const imperfect(m_current_driver->type.imperfect_features()); if (!(m_current_driver->flags & (machine_flags::IS_BIOS_ROOT | machine_flags::NO_SOUND_HW)) && !(unemulated & device_t::feature::SOUND)) { - sound_interface_iterator iter(m_current_config->root_device()); + speaker_device_enumerator iter(root); if (!iter.first()) osd_printf_error("Driver is missing MACHINE_NO_SOUND or MACHINE_NO_SOUND_HW flag\n"); } // catch invalid flag combinations if (unemulated & ~device_t::feature::ALL) - osd_printf_error("Driver has invalid unemulated feature flags (0x%08lX)\n", static_cast<unsigned long>(unemulated & ~device_t::feature::ALL)); + osd_printf_error("Driver has invalid unemulated feature flags (0x%08X)\n", util::underlying_value(unemulated & ~device_t::feature::ALL)); if (imperfect & ~device_t::feature::ALL) - osd_printf_error("Driver has invalid imperfect feature flags (0x%08lX)\n", static_cast<unsigned long>(imperfect & ~device_t::feature::ALL)); + osd_printf_error("Driver has invalid imperfect feature flags (0x%08X)\n", util::underlying_value(imperfect & ~device_t::feature::ALL)); if (unemulated & imperfect) - osd_printf_error("Driver cannot have features that are both unemulated and imperfect (0x%08lX)\n", static_cast<unsigned long>(unemulated & imperfect)); + osd_printf_error("Driver cannot have features that are both unemulated and imperfect (0x%08X)\n", util::underlying_value(unemulated & imperfect)); if ((m_current_driver->flags & machine_flags::NO_SOUND_HW) && ((unemulated | imperfect) & device_t::feature::SOUND)) osd_printf_error("Machine without sound hardware cannot have unemulated/imperfect sound\n"); + + // catch systems marked as supporting save states that contain devices that don't support save states + if (!(m_current_driver->type.emulation_flags() & device_t::flags::SAVE_UNSUPPORTED)) + { + std::set<std::add_pointer_t<device_type> > nosave; + device_enumerator iter(root); + std::string_view cardtag; + for (auto &device : iter) + { + // ignore any children of a slot card + if (!cardtag.empty()) + { + std::string_view tag(device.tag()); + if ((tag.length() > cardtag.length()) && (tag.substr(0, cardtag.length()) == cardtag) && tag[cardtag.length()] == ':') + continue; + else + cardtag = std::string_view(); + } + + // check to see if this is a slot card + device_t *const parent(device.owner()); + if (parent) + { + device_slot_interface *slot; + parent->interface(slot); + if (slot && (slot->get_card_device() == &device)) + { + cardtag = device.tag(); + continue; + } + } + + if (device.type().emulation_flags() & device_t::flags::SAVE_UNSUPPORTED) + nosave.emplace(&device.type()); + } + if (!nosave.empty()) + { + std::ostringstream buf; + for (auto const &devtype : nosave) + util::stream_format(buf, "%s(%s) %s\n", core_filename_extract_base(devtype->source()), devtype->shortname(), devtype->fullname()); + osd_printf_error("Machine is marked as supporting save states but uses devices that lack save state support:\n%s", std::move(buf).str()); + } + } } @@ -1507,7 +2197,7 @@ void validity_checker::validate_driver() void validity_checker::validate_roms(device_t &root) { // iterate, starting with the driver's ROMs and continuing with device ROMs - for (device_t &device : device_iterator(root)) + for (device_t &device : device_enumerator(root)) { // track the current device m_current_device = &device; @@ -1518,7 +2208,6 @@ void validity_checker::validate_roms(device_t &root) u32 current_length = 0; int items_since_region = 1; int last_bios = 0, max_bios = 0; - int total_files = 0; std::unordered_map<std::string, int> bios_names; std::unordered_map<std::string, std::string> bios_descs; char const *defbios = nullptr; @@ -1550,36 +2239,48 @@ void validity_checker::validate_roms(device_t &root) // attempt to add it to the map, reporting duplicates as errors current_length = ROMREGION_GETLENGTH(romp); - if (!m_region_map.insert(std::make_pair(fulltag, current_length)).second) - osd_printf_error("Multiple ROM_REGIONs with the same tag '%s' defined\n", fulltag.c_str()); + if (!m_region_map.emplace(fulltag, current_length).second) + osd_printf_error("Multiple ROM_REGIONs with the same tag '%s' defined\n", fulltag); + if (current_length == 0) + osd_printf_error("ROM region '%s' has zero length\n", fulltag); } else if (ROMENTRY_ISSYSTEM_BIOS(romp)) // If this is a system bios, make sure it is using the next available bios number { int const bios_flags = ROM_GETBIOSFLAGS(romp); char const *const biosname = romp->name; if (bios_flags != last_bios + 1) - osd_printf_error("Non-sequential BIOS %s (specified as %d, expected to be %d)\n", biosname, bios_flags - 1, last_bios); + osd_printf_error("Non-sequential BIOS %s (specified as %d, expected to be %d)\n", biosname ? biosname : "UNNAMED", bios_flags - 1, last_bios); last_bios = bios_flags; // validate the name - if (strlen(biosname) > 16) - osd_printf_error("BIOS name %s exceeds maximum 16 characters\n", biosname); - for (char const *s = biosname; *s; ++s) + if (!biosname || biosname[0] == 0) + osd_printf_error("BIOS %d is missing a name\n", bios_flags - 1); + else { - if (((*s < '0') || (*s > '9')) && ((*s < 'a') || (*s > 'z')) && (*s != '.') && (*s != '_') && (*s != '-')) + if (strlen(biosname) > 16) + osd_printf_error("BIOS name %s exceeds maximum 16 characters\n", biosname); + for (char const *s = biosname; *s; ++s) { - osd_printf_error("BIOS name %s contains invalid characters\n", biosname); - break; + if (((*s < '0') || (*s > '9')) && ((*s < 'a') || (*s > 'z')) && (*s != '.') && (*s != '_') && (*s != '-')) + { + osd_printf_error("BIOS name %s contains invalid characters\n", biosname); + break; + } } - } - // check for duplicate names/descriptions - auto const nameins = bios_names.emplace(biosname, bios_flags); - if (!nameins.second) - osd_printf_error("Duplicate BIOS name %s specified (%d and %d)\n", biosname, nameins.first->second, bios_flags - 1); - auto const descins = bios_descs.emplace(romp->hashdata, biosname); - if (!descins.second) - osd_printf_error("BIOS %s has duplicate description '%s' (was %s)\n", biosname, romp->hashdata, descins.first->second.c_str()); + // check for duplicate names/descriptions + auto const nameins = bios_names.emplace(biosname, bios_flags); + if (!nameins.second) + osd_printf_error("Duplicate BIOS name %s specified (%d and %d)\n", biosname, nameins.first->second, bios_flags - 1); + if (!romp->hashdata || romp->hashdata[0] == 0) + osd_printf_error("BIOS %s has empty description\n", biosname); + else + { + auto const descins = bios_descs.emplace(romp->hashdata, biosname); + if (!descins.second) + osd_printf_error("BIOS %s has duplicate description '%s' (was %s)\n", biosname, romp->hashdata, descins.first->second); + } + } } else if (ROMENTRY_ISDEFAULT_BIOS(romp)) // if this is a default BIOS setting, remember it so it to check at the end { @@ -1589,7 +2290,6 @@ void validity_checker::validate_roms(device_t &root) { // track the last filename we found last_name = romp->name; - total_files++; max_bios = std::max<int>(max_bios, ROM_GETBIOSFLAGS(romp)); // validate the name @@ -1619,11 +2319,15 @@ void validity_checker::validate_roms(device_t &root) } } + // if we haven't seen any items since the last region, print a warning + if (items_since_region == 0) + osd_printf_warning("Empty ROM region '%s' (warning)\n", last_region_name); + // check that default BIOS exists if (defbios && (bios_names.find(defbios) == bios_names.end())) osd_printf_error("Default BIOS '%s' not found\n", defbios); if (!device.get_default_bios_tag().empty() && (bios_names.find(device.get_default_bios_tag()) == bios_names.end())) - osd_printf_error("Configured BIOS '%s' not found\n", device.get_default_bios_tag().c_str()); + osd_printf_error("Configured BIOS '%s' not found\n", device.get_default_bios_tag()); // check that there aren't ROMs for a non-existent BIOS option if (max_bios > last_bios) @@ -1644,7 +2348,7 @@ void validity_checker::validate_roms(device_t &root) // analog input field //------------------------------------------------- -void validity_checker::validate_analog_input_field(ioport_field &field) +void validity_checker::validate_analog_input_field(const ioport_field &field) { // analog ports must have a valid sensitivity if (field.sensitivity() == 0) @@ -1725,54 +2429,56 @@ void validity_checker::validate_analog_input_field(ioport_field &field) // setting //------------------------------------------------- -void validity_checker::validate_dip_settings(ioport_field &field) +void validity_checker::validate_dip_settings(const ioport_field &field) { - const char *demo_sounds = ioport_string_from_index(INPUT_STRING_Demo_Sounds); - const char *flipscreen = ioport_string_from_index(INPUT_STRING_Flip_Screen); + char const *const demo_sounds = ioport_string_from_index(INPUT_STRING_Demo_Sounds); + char const *const flipscreen = ioport_string_from_index(INPUT_STRING_Flip_Screen); + char const *const name = field.specific_name() ? field.specific_name() : "UNNAMED"; u8 coin_list[__input_string_coinage_end + 1 - __input_string_coinage_start] = { 0 }; bool coin_error = false; // iterate through the settings - for (ioport_setting &setting : field.settings()) + for (auto setting = field.settings().begin(); field.settings().end() != setting; ++setting) { // note any coinage strings - int strindex = get_defstr_index(setting.name()); + int strindex = get_defstr_index(setting->name()); if (strindex >= __input_string_coinage_start && strindex <= __input_string_coinage_end) coin_list[strindex - __input_string_coinage_start] = 1; // make sure demo sounds default to on - if (field.name() == demo_sounds && strindex == INPUT_STRING_On && field.defvalue() != setting.value()) + if (name == demo_sounds && strindex == INPUT_STRING_On && field.defvalue() != setting->value()) osd_printf_error("Demo Sounds must default to On\n"); // check for bad demo sounds options - if (field.name() == demo_sounds && (strindex == INPUT_STRING_Yes || strindex == INPUT_STRING_No)) - osd_printf_error("Demo Sounds option must be Off/On, not %s\n", setting.name()); + if (name == demo_sounds && (strindex == INPUT_STRING_Yes || strindex == INPUT_STRING_No)) + osd_printf_error("Demo Sounds option must be Off/On, not %s\n", setting->name()); // check for bad flip screen options - if (field.name() == flipscreen && (strindex == INPUT_STRING_Yes || strindex == INPUT_STRING_No)) - osd_printf_error("Flip Screen option must be Off/On, not %s\n", setting.name()); + if (name == flipscreen && (strindex == INPUT_STRING_Yes || strindex == INPUT_STRING_No)) + osd_printf_error("Flip Screen option must be Off/On, not %s\n", setting->name()); // if we have a neighbor, compare ourselves to him - if (setting.next() != nullptr) + auto const nextsetting = std::next(setting); + if (field.settings().end() != nextsetting) { - // check for inverted off/on dispswitch order - int next_strindex = get_defstr_index(setting.next()->name(), true); + // check for inverted off/on DIP switch order + int next_strindex = get_defstr_index(nextsetting->name(), true); if (strindex == INPUT_STRING_On && next_strindex == INPUT_STRING_Off) - osd_printf_error("%s option must have Off/On options in the order: Off, On\n", field.name()); + osd_printf_error("%s option must have Off/On options in the order: Off, On\n", name); - // check for inverted yes/no dispswitch order + // check for inverted yes/no DIP switch order else if (strindex == INPUT_STRING_Yes && next_strindex == INPUT_STRING_No) - osd_printf_error("%s option must have Yes/No options in the order: No, Yes\n", field.name()); + osd_printf_error("%s option must have Yes/No options in the order: No, Yes\n", name); - // check for inverted upright/cocktail dispswitch order + // check for inverted upright/cocktail DIP switch order else if (strindex == INPUT_STRING_Cocktail && next_strindex == INPUT_STRING_Upright) - osd_printf_error("%s option must have Upright/Cocktail options in the order: Upright, Cocktail\n", field.name()); + osd_printf_error("%s option must have Upright/Cocktail options in the order: Upright, Cocktail\n", name); // check for proper coin ordering else if (strindex >= __input_string_coinage_start && strindex <= __input_string_coinage_end && next_strindex >= __input_string_coinage_start && next_strindex <= __input_string_coinage_end && - strindex >= next_strindex && setting.condition() == setting.next()->condition()) + strindex >= next_strindex && setting->condition() == nextsetting->condition()) { - osd_printf_error("%s option has unsorted coinage %s > %s\n", field.name(), setting.name(), setting.next()->name()); + osd_printf_error("%s option has unsorted coinage %s > %s\n", name, setting->name(), nextsetting->name()); coin_error = true; } } @@ -1782,7 +2488,7 @@ void validity_checker::validate_dip_settings(ioport_field &field) if (coin_error) { output_via_delegate(OSD_OUTPUT_CHANNEL_ERROR, " Note proper coin sort order should be:\n"); - for (int entry = 0; entry < ARRAY_LENGTH(coin_list); entry++) + for (int entry = 0; entry < std::size(coin_list); entry++) if (coin_list[entry]) output_via_delegate(OSD_OUTPUT_CHANNEL_ERROR, " %s\n", ioport_string_from_index(__input_string_coinage_start + entry)); } @@ -1794,11 +2500,16 @@ void validity_checker::validate_dip_settings(ioport_field &field) // stored within an ioport field or setting //------------------------------------------------- -void validity_checker::validate_condition(ioport_condition &condition, device_t &device, std::unordered_set<std::string> &port_map) +void validity_checker::validate_condition(const ioport_condition &condition, device_t &device) { - // resolve the tag - // then find a matching port - if (port_map.find(device.subtag(condition.tag())) == port_map.end()) + if (condition.tag() == nullptr) + { + osd_printf_error("Condition referencing null ioport tag\n"); + return; + } + + // resolve the tag, then find a matching port + if (m_ioport_set.find(device.subtag(condition.tag())) == m_ioport_set.end()) osd_printf_error("Condition referencing non-existent ioport tag '%s'\n", condition.tag()); } @@ -1807,12 +2518,10 @@ void validity_checker::validate_condition(ioport_condition &condition, device_t // validate_inputs - validate input configuration //------------------------------------------------- -void validity_checker::validate_inputs() +void validity_checker::validate_inputs(device_t &root) { - std::unordered_set<std::string> port_map; - // iterate over devices - for (device_t &device : device_iterator(m_current_config->root_device())) + for (device_t &device : device_enumerator(root)) { // see if this device has ports; if not continue if (device.input_ports() == nullptr) @@ -1823,16 +2532,17 @@ void validity_checker::validate_inputs() // allocate the input ports ioport_list portlist; - std::string errorbuf; - portlist.append(device, errorbuf); - - // report any errors during construction - if (!errorbuf.empty()) - osd_printf_error("I/O port error during construction:\n%s\n", errorbuf.c_str()); + { + // report any errors during construction + std::ostringstream errorbuf; + portlist.append(device, errorbuf); + if (errorbuf.tellp()) + osd_printf_error("I/O port error during construction:\n%s\n", std::move(errorbuf).str()); + } // do a first pass over ports to add their names and find duplicates for (auto &port : portlist) - if (!port_map.insert(port.second->tag()).second) + if (!m_ioport_set.insert(port.second->tag()).second) osd_printf_error("Multiple I/O ports with the same tag '%s' defined\n", port.second->tag()); // iterate over ports @@ -1840,41 +2550,66 @@ void validity_checker::validate_inputs() { m_current_ioport = port.second->tag(); + // scan for invalid characters + static char const *const validchars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.:^$"; + for (char const *p = m_current_ioport; *p; ++p) + { + if (*p == ' ') + { + osd_printf_error("Tag '%s' contains spaces\n", m_current_ioport); + break; + } + if (!strchr(validchars, *p)) + { + osd_printf_error("Tag '%s' contains invalid character '%c'\n", m_current_ioport, *p); + break; + } + } + // iterate through the fields on this port - for (ioport_field &field : port.second->fields()) + for (ioport_field const &field : port.second->fields()) { // verify analog inputs if (field.is_analog()) validate_analog_input_field(field); - // look for invalid (0) types which should be mapped to IPT_OTHER - if (field.type() == IPT_INVALID) + // checks based on field type + if ((field.type() > IPT_UI_FIRST) && (field.type() < IPT_UI_LAST)) + { + osd_printf_error("Field has invalid UI control type\n"); + } + else if (field.type() == IPT_INVALID) + { osd_printf_error("Field has an invalid type (0); use IPT_OTHER instead\n"); - - if (field.type() == IPT_SPECIAL) + } + else if (field.type() == IPT_SPECIAL) + { osd_printf_error("Field has an invalid type IPT_SPECIAL\n"); - - // verify dip switches - if (field.type() == IPT_DIPSWITCH) + } + else if (field.type() == IPT_DIPSWITCH) { - // dip switch fields must have a specific name + // DIP switch fields must have a specific name if (field.specific_name() == nullptr) osd_printf_error("DIP switch has no specific name\n"); // verify the settings list validate_dip_settings(field); } - - // verify config settings - if (field.type() == IPT_CONFIG) + else if (field.type() == IPT_CONFIG) { // config fields must have a specific name if (field.specific_name() == nullptr) osd_printf_error("Config switch has no specific name\n"); } + else if (field.type() == IPT_ADJUSTER) + { + // adjuster fields must have a specific name + if (field.specific_name() == nullptr) + osd_printf_error("Adjuster has no specific name\n"); + } // verify names - const char *name = field.specific_name(); + char const *const name = field.specific_name(); if (name != nullptr) { // check for empty string @@ -1888,19 +2623,16 @@ void validity_checker::validate_inputs() // check for invalid UTF-8 if (!utf8_is_valid_string(name)) osd_printf_error("Field '%s' has invalid characters\n", name); - - // look up the string and print an error if default strings are not used - /*strindex =get_defstr_index(defstr_map, name, driver, &error);*/ } // verify conditions on the field if (!field.condition().none()) - validate_condition(field.condition(), device, port_map); + validate_condition(field.condition(), device); // verify conditions on the settings - for (ioport_setting &setting : field.settings()) + for (ioport_setting const &setting : field.settings()) if (!setting.condition().none()) - validate_condition(setting.condition(), device, port_map); + validate_condition(setting.condition(), device); // verify natural keyboard codes for (int which = 0; which < 1 << (UCHAR_SHIFT_END - UCHAR_SHIFT_BEGIN + 1); which++) @@ -1911,9 +2643,9 @@ void validity_checker::validate_inputs() if (!uchar_isvalid(code)) { osd_printf_error("Field '%s' has non-character U+%04X in PORT_CHAR(%d)\n", - name, - (unsigned)code, - (int)code); + name, + (unsigned)code, + (int)code); } } } @@ -1934,17 +2666,17 @@ void validity_checker::validate_inputs() // checks //------------------------------------------------- -void validity_checker::validate_devices() +void validity_checker::validate_devices(machine_config &config) { std::unordered_set<std::string> device_map; - for (device_t &device : device_iterator(m_current_config->root_device())) + for (device_t &device : device_enumerator(config.root_device())) { // track the current device m_current_device = &device; // validate auto-finders - device.findit(true); + device.findit(this); // validate the device tag validate_tag(device.basetag()); @@ -1970,10 +2702,15 @@ void validity_checker::validate_devices() if (slot->default_option() != nullptr && option.first == slot->default_option()) continue; + // if we need to save time, instantiate and validate each slot card type at most once + if (m_quick && !m_slotcard_set.insert(option.second->devtype().shortname()).second) + continue; + + m_checking_card = true; device_t *card; { - machine_config::token const tok(m_current_config->begin_configuration(slot->device())); - card = m_current_config->device_add(option.second->name(), option.second->devtype(), option.second->clock()); + machine_config::token const tok(config.begin_configuration(slot->device())); + card = config.device_add(option.second->name(), option.second->devtype(), option.second->clock()); const char *const def_bios = option.second->default_bios(); if (def_bios) @@ -1983,7 +2720,7 @@ void validity_checker::validate_devices() additions(card); } - for (device_slot_interface &subslot : slot_interface_iterator(*card)) + for (device_slot_interface &subslot : slot_interface_enumerator(*card)) { if (subslot.fixed()) { @@ -1991,8 +2728,8 @@ void validity_checker::validate_devices() device_slot_interface::slot_option const *suboption = subslot.option(subslot.default_option()); if (suboption) { - machine_config::token const tok(m_current_config->begin_configuration(subslot.device())); - device_t *const sub_card = m_current_config->device_add(suboption->name(), suboption->devtype(), suboption->clock()); + machine_config::token const tok(config.begin_configuration(subslot.device())); + device_t *const sub_card = config.device_add(suboption->name(), suboption->devtype(), suboption->clock()); const char *const sub_bios = suboption->default_bios(); if (sub_bios) sub_card->set_default_bios_tag(sub_bios); @@ -2003,20 +2740,22 @@ void validity_checker::validate_devices() } } - for (device_t &card_dev : device_iterator(*card)) + for (device_t &card_dev : device_enumerator(*card)) card_dev.config_complete(); validate_roms(*card); - for (device_t &card_dev : device_iterator(*card)) + for (device_t &card_dev : device_enumerator(*card)) { m_current_device = &card_dev; - card_dev.findit(true); + card_dev.findit(this); + validate_tag(card_dev.basetag()); card_dev.validity_check(*this); m_current_device = nullptr; } - machine_config::token const tok(m_current_config->begin_configuration(slot->device())); - m_current_config->device_remove(option.second->name()); + machine_config::token const tok(config.begin_configuration(slot->device())); + config.device_remove(option.second->name()); + m_checking_card = false; } } } @@ -2042,18 +2781,18 @@ void validity_checker::validate_device_types() machine_config::token const tok(config.begin_configuration(config.root_device())); for (device_type type : registered_device_types) { - device_t *const dev = config.device_add("_tmp", type, 0); + device_t *const dev = config.device_add(type.shortname(), type, 0); char const *name((dev->shortname() && *dev->shortname()) ? dev->shortname() : type.type().name()); - std::string const description((dev->source() && *dev->source()) ? util::string_format("%s(%s)", core_filename_extract_base(dev->source()).c_str(), name) : name); + std::string const description((dev->source() && *dev->source()) ? util::string_format("%s(%s)", core_filename_extract_base(dev->source()), name) : name); if (m_print_verbose) - output_via_delegate(OSD_OUTPUT_CHANNEL_ERROR, "Validating device %s...\n", description.c_str()); + output_via_delegate(OSD_OUTPUT_CHANNEL_ERROR, "Validating device %s...\n", description); // ensure shortname exists if (!dev->shortname() || !*dev->shortname()) { - osd_printf_error("Device %s does not have short name defined\n", description.c_str()); + osd_printf_error("Device %s does not have short name defined\n", description); } else { @@ -2066,7 +2805,7 @@ void validity_checker::validate_device_types() { if (((*s < '0') || (*s > '9')) && ((*s < 'a') || (*s > 'z')) && (*s != '_')) { - osd_printf_error("Device %s short name contains invalid characters\n", description.c_str()); + osd_printf_error("Device %s short name contains invalid characters\n", description); break; } } @@ -2078,12 +2817,12 @@ void validity_checker::validate_device_types() if (m_names_map.end() != drvname) { game_driver const &dup(*drvname->second); - osd_printf_error("Device %s short name is a duplicate of %s(%s)\n", description.c_str(), core_filename_extract_base(dup.type.source()).c_str(), dup.name); + osd_printf_error("Device %s short name is a duplicate of %s(%s)\n", description, core_filename_extract_base(dup.type.source()), dup.name); } else if (!devname.second) { device_t *const dup = config.device_add("_dup", *devname.first->second, 0); - osd_printf_error("Device %s short name is a duplicate of %s(%s)\n", description.c_str(), core_filename_extract_base(dup->source()).c_str(), dup->shortname()); + osd_printf_error("Device %s short name is a duplicate of %s(%s)\n", description, core_filename_extract_base(dup->source()), dup->shortname()); config.device_remove("_dup"); } } @@ -2091,7 +2830,7 @@ void validity_checker::validate_device_types() // ensure name exists if (!dev->name() || !*dev->name()) { - osd_printf_error("Device %s does not have name defined\n", description.c_str()); + osd_printf_error("Device %s does not have name defined\n", description); } else { @@ -2102,35 +2841,57 @@ void validity_checker::validate_device_types() if (m_descriptions_map.end() != drvdesc) { game_driver const &dup(*drvdesc->second); - osd_printf_error("Device %s name '%s' is a duplicate of %s(%s)\n", description.c_str(), dev->name(), core_filename_extract_base(dup.type.source()).c_str(), dup.name); + osd_printf_error("Device %s name '%s' is a duplicate of %s(%s)\n", description, dev->name(), core_filename_extract_base(dup.type.source()), dup.name); } else if (!devdesc.second) { device_t *const dup = config.device_add("_dup", *devdesc.first->second, 0); - osd_printf_error("Device %s name '%s' is a duplicate of %s(%s)\n", description.c_str(), dev->name(), core_filename_extract_base(dup->source()).c_str(), dup->shortname()); + osd_printf_error("Device %s name '%s' is a duplicate of %s(%s)\n", description, dev->name(), core_filename_extract_base(dup->source()), dup->shortname()); config.device_remove("_dup"); } } // ensure source exists if (!dev->source() || !*dev->source()) - osd_printf_error("Device %s does not have source defined\n", description.c_str()); + osd_printf_error("Device %s does not have source defined\n", description); // check that reported type matches supplied type if (dev->type().type() != type.type()) - osd_printf_error("Device %s reports type '%s' (created with '%s')\n", description.c_str(), dev->type().type().name(), type.type().name()); + osd_printf_error("Device %s reports type '%s' (created with '%s')\n", description, dev->type().type().name(), type.type().name()); // catch invalid flag combinations device_t::feature_type const unemulated(dev->type().unemulated_features()); device_t::feature_type const imperfect(dev->type().imperfect_features()); if (unemulated & ~device_t::feature::ALL) - osd_printf_error("Device has invalid unemulated feature flags (0x%08lX)\n", static_cast<unsigned long>(unemulated & ~device_t::feature::ALL)); + osd_printf_error("Device has invalid unemulated feature flags (0x%08X)\n", util::underlying_value(unemulated & ~device_t::feature::ALL)); if (imperfect & ~device_t::feature::ALL) - osd_printf_error("Device has invalid imperfect feature flags (0x%08lX)\n", static_cast<unsigned long>(imperfect & ~device_t::feature::ALL)); + osd_printf_error("Device has invalid imperfect feature flags (0x%08X)\n", util::underlying_value(imperfect & ~device_t::feature::ALL)); if (unemulated & imperfect) - osd_printf_error("Device cannot have features that are both unemulated and imperfect (0x%08lX)\n", static_cast<unsigned long>(unemulated & imperfect)); + osd_printf_error("Device cannot have features that are both unemulated and imperfect (0x%08X)\n", util::underlying_value(unemulated & imperfect)); + + // check that parents are only ever one generation deep + auto const parent(dev->type().parent_rom_device_type()); + if (parent) + { + auto const grandparent(parent->parent_rom_device_type()); + if ((dev->type() == *parent) || !strcmp(parent->shortname(), name)) + osd_printf_error("Device has parent ROM set that identical to its type\n"); + if (grandparent) + osd_printf_error("Device has parent ROM set '%s' which has parent ROM set '%s'\n", parent->shortname(), grandparent->shortname()); + } - config.device_remove("_tmp"); + // give devices some of the same scrutiny that drivers get - necessary for cards not default for any slots + validate_roms(*dev); + validate_inputs(*dev); + + // reset the device + m_current_device = nullptr; + m_current_ioport = nullptr; + m_region_map.clear(); + m_ioport_set.clear(); + + // remove the device in preparation for re-using the machine configuration + config.device_remove(type.shortname()); } // if we had warnings or errors, output @@ -2154,18 +2915,15 @@ void validity_checker::validate_device_types() // and device //------------------------------------------------- -void validity_checker::build_output_prefix(std::string &str) +void validity_checker::build_output_prefix(std::ostream &str) const { - // start empty - str.clear(); - // if we have a current (non-root) device, indicate that - if (m_current_device != nullptr && m_current_device->owner() != nullptr) - str.append(m_current_device->name()).append(" device '").append(m_current_device->tag() + 1).append("': "); + if (m_current_device && m_current_device->owner()) + util::stream_format(str, "%s device '%s': ", m_current_device->name(), m_current_device->tag() + 1); // if we have a current port, indicate that as well - if (m_current_ioport != nullptr) - str.append("ioport '").append(m_current_ioport).append("': "); + if (m_current_ioport) + util::stream_format(str, "ioport '%s': ", m_current_ioport); } @@ -2173,9 +2931,9 @@ void validity_checker::build_output_prefix(std::string &str) // error_output - error message output override //------------------------------------------------- -void validity_checker::output_callback(osd_output_channel channel, const char *msg, va_list args) +void validity_checker::output_callback(osd_output_channel channel, const util::format_argument_pack<char> &args) { - std::string output; + std::ostringstream output; switch (channel) { case OSD_OUTPUT_CHANNEL_ERROR: @@ -2186,8 +2944,8 @@ void validity_checker::output_callback(osd_output_channel channel, const char *m build_output_prefix(output); // generate the string - strcatvprintf(output, msg, args); - m_error_text.append(output); + util::stream_format(output, args); + m_error_text.append(output.str()); break; case OSD_OUTPUT_CHANNEL_WARNING: @@ -2198,8 +2956,8 @@ void validity_checker::output_callback(osd_output_channel channel, const char *m build_output_prefix(output); // generate the string and output to the original target - strcatvprintf(output, msg, args); - m_warning_text.append(output); + util::stream_format(output, args); + m_warning_text.append(output.str()); break; case OSD_OUTPUT_CHANNEL_VERBOSE: @@ -2210,12 +2968,12 @@ void validity_checker::output_callback(osd_output_channel channel, const char *m build_output_prefix(output); // generate the string and output to the original target - strcatvprintf(output, msg, args); - m_verbose_text.append(output); + util::stream_format(output, args); + m_verbose_text.append(output.str()); break; default: - chain_output(channel, msg, args); + chain_output(channel, args); break; } } @@ -2226,14 +2984,11 @@ void validity_checker::output_callback(osd_output_channel channel, const char *m // can be forwarded onto the given delegate //------------------------------------------------- -void validity_checker::output_via_delegate(osd_output_channel channel, const char *format, ...) +template <typename Format, typename... Params> +void validity_checker::output_via_delegate(osd_output_channel channel, Format &&fmt, Params &&...args) { - va_list argptr; - // call through to the delegate with the proper parameters - va_start(argptr, format); - chain_output(channel, format, argptr); - va_end(argptr); + chain_output(channel, util::make_format_argument_pack(std::forward<Format>(fmt), std::forward<Params>(args)...)); } //------------------------------------------------- @@ -2246,5 +3001,5 @@ void validity_checker::output_indented_errors(std::string &text, const char *hea if (text[text.size()-1] == '\n') text.erase(text.size()-1, 1); strreplace(text, "\n", "\n "); - output_via_delegate(OSD_OUTPUT_CHANNEL_ERROR, "%s:\n %s\n", header, text.c_str()); + output_via_delegate(OSD_OUTPUT_CHANNEL_ERROR, "%s:\n %s\n", header, text); } diff --git a/src/emu/validity.h b/src/emu/validity.h index 2c8c38e4c8d..fbf5f239323 100644 --- a/src/emu/validity.h +++ b/src/emu/validity.h @@ -25,17 +25,16 @@ class validity_checker : public osd_output { public: - validity_checker(emu_options &options); + validity_checker(emu_options &options, bool quick); ~validity_checker(); // getters int errors() const { return m_errors; } int warnings() const { return m_warnings; } - bool validate_all() const { return m_validate_all; } + bool quick() const { return m_quick; } // setter void set_verbose(bool verbose) { m_print_verbose = verbose; } - void set_validate_all(bool all) { m_validate_all = all; } // operations void check_driver(const game_driver &driver); @@ -44,23 +43,23 @@ public: // helpers for devices void validate_tag(const char *tag); - int region_length(const char *tag) { auto i = m_region_map.find(tag); return i == m_region_map.end() ? 0 : i->second; } + int region_length(const char *tag) { auto const i = m_region_map.find(tag); return (i == m_region_map.end()) ? 0 : i->second; } + bool ioport_missing(const char *tag) { return !m_checking_card && (m_ioport_set.find(tag) == m_ioport_set.end()); } // generic registry of already-checked stuff bool already_checked(const char *string) { return !m_already_checked.insert(string).second; } - // osd_output interface - protected: - virtual void output_callback(osd_output_channel channel, const char *msg, va_list args) override; + // osd_output interface + virtual void output_callback(osd_output_channel channel, const util::format_argument_pack<char> &args) override; private: // internal map types - typedef std::unordered_map<std::string,const game_driver *> game_driver_map; - typedef std::unordered_map<std::string,uintptr_t> int_map; + using game_driver_map = std::unordered_map<std::string, game_driver const *>; + using int_map = std::unordered_map<std::string, uintptr_t>; + using string_set = std::unordered_set<std::string>; // internal helpers - const char *ioport_string_from_index(u32 index); int get_defstr_index(const char *string, bool suppress_error = false); // core helpers @@ -69,29 +68,20 @@ private: void validate_one(const game_driver &driver); // internal sub-checks - void validate_core(); - void validate_inlines(); - void validate_rgb(); - void validate_driver(); + void validate_driver(device_t &root); void validate_roms(device_t &root); - void validate_analog_input_field(ioport_field &field); - void validate_dip_settings(ioport_field &field); - void validate_condition(ioport_condition &condition, device_t &device, std::unordered_set<std::string> &port_map); - void validate_inputs(); - void validate_devices(); + void validate_analog_input_field(const ioport_field &field); + void validate_dip_settings(const ioport_field &field); + void validate_condition(const ioport_condition &condition, device_t &device); + void validate_inputs(device_t &root); + void validate_devices(machine_config &config); void validate_device_types(); // output helpers - void build_output_prefix(std::string &str); - void output_via_delegate(osd_output_channel channel, const char *format, ...) ATTR_PRINTF(3,4); + void build_output_prefix(std::ostream &str) const; + template <typename Format, typename... Params> void output_via_delegate(osd_output_channel channel, Format &&fmt, Params &&...args); void output_indented_errors(std::string &text, const char *header); - // random number generation - s32 random_i32(); - u32 random_u32(); - s64 random_i64(); - u64 random_u64(); - // internal driver list driver_enumerator m_drivlist; @@ -113,13 +103,15 @@ private: int_map m_defstr_map; // current state - const game_driver * m_current_driver; - machine_config * m_current_config; - const device_t * m_current_device; - const char * m_current_ioport; + game_driver const * m_current_driver; + device_t const * m_current_device; + char const * m_current_ioport; int_map m_region_map; - std::unordered_set<std::string> m_already_checked; - bool m_validate_all; + string_set m_ioport_set; + string_set m_already_checked; + string_set m_slotcard_set; + bool m_checking_card; + bool const m_quick; }; #endif // MAME_EMU_VALIDITY_H diff --git a/src/emu/video.cpp b/src/emu/video.cpp index e253afcfc4a..06a16b76cda 100644 --- a/src/emu/video.cpp +++ b/src/emu/video.cpp @@ -9,19 +9,26 @@ ***************************************************************************/ #include "emu.h" -#include "emuopts.h" -#include "debugger.h" -#include "ui/uimain.h" + #include "crsshair.h" -#include "rendersw.hxx" +#include "debugger.h" +#include "emuopts.h" +#include "fileio.h" +#include "main.h" #include "output.h" +#include "screen.h" -#include "aviio.h" +#include "ui/uimain.h" + +#include "corestr.h" +#include "path.h" #include "png.h" #include "xmlfile.h" #include "osdepend.h" +#include "rendersw.hxx" + //************************************************************************** // DEBUGGING @@ -85,14 +92,16 @@ video_manager::video_manager(running_machine &machine) , m_overall_real_ticks(0) , m_overall_emutime(attotime::zero) , m_overall_valid_counter(0) - , m_throttled(machine.options().throttle()) + , m_throttled(true) , m_throttle_rate(1.0f) , m_fastforward(false) , m_seconds_to_run(machine.options().seconds_to_run()) , m_auto_frameskip(machine.options().auto_frameskip()) , m_speed(original_speed_setting()) + , m_low_latency(machine.options().low_latency()) , m_empty_skip_count(0) - , m_frameskip_level(machine.options().frameskip()) + , m_frameskip_max(m_auto_frameskip ? machine.options().frameskip() : 0) + , m_frameskip_level(m_auto_frameskip ? 0 : machine.options().frameskip()) , m_frameskip_counter(0) , m_frameskip_adjust(0) , m_skipping_this_frame(false) @@ -101,11 +110,6 @@ video_manager::video_manager(running_machine &machine) , m_snap_native(true) , m_snap_width(0) , m_snap_height(0) - , m_timecode_enabled(false) - , m_timecode_write(false) - , m_timecode_text("") - , m_timecode_start(attotime::zero) - , m_timecode_total(attotime::zero) { // request a callback upon exiting machine.add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(&video_manager::exit, this)); @@ -114,12 +118,12 @@ video_manager::video_manager(running_machine &machine) // extract initial execution state from global configuration settings update_refresh_speed(); - const unsigned screen_count(screen_device_iterator(machine.root_device()).count()); + const unsigned screen_count(screen_device_enumerator(machine.root_device()).count()); const bool no_screens(!screen_count); // create a render target for snapshots const char *viewname = machine.options().snap_view(); - m_snap_native = !no_screens && (viewname[0] == 0 || strcmp(viewname, "native") == 0); + m_snap_native = !no_screens && !strcmp(viewname, "native"); if (m_snap_native) { @@ -137,7 +141,7 @@ video_manager::video_manager(running_machine &machine) util::xml::data_node *const viewnode(layoutnode->add_child("view", nullptr)); if (!viewnode) throw emu_fatalerror("Couldn't create XML node??"); - viewnode->set_attribute("name", util::xml::normalize_string(util::string_format("s%1$u", i).c_str())); + viewnode->set_attribute("name", util::string_format("s%1$u", i).c_str()); util::xml::data_node *const screennode(viewnode->add_child("screen", nullptr)); if (!screennode) throw emu_fatalerror("Couldn't create XML node??"); @@ -167,21 +171,12 @@ video_manager::video_manager(running_machine &machine) if (sscanf(machine.options().snap_size(), "%dx%d", &m_snap_width, &m_snap_height) != 2) m_snap_width = m_snap_height = 0; - // start recording movie if specified - const char *filename = machine.options().mng_write(); - if (filename[0] != 0) - begin_recording(filename, MF_MNG); - - filename = machine.options().avi_write(); - if (filename[0] != 0) - begin_recording(filename, MF_AVI); - // if no screens, create a periodic timer to drive updates if (no_screens) { m_screenless_frame_timer = machine.scheduler().timer_alloc(timer_expired_delegate(FUNC(video_manager::screenless_update_callback), this)); m_screenless_frame_timer->adjust(screen_device::DEFAULT_FRAME_PERIOD, 0, screen_device::DEFAULT_FRAME_PERIOD); - machine.output().set_notifier(nullptr, video_notifier_callback, this); + machine.output().set_global_notifier(video_notifier_callback, this); } } @@ -193,18 +188,18 @@ video_manager::video_manager(running_machine &machine) void video_manager::set_frameskip(int frameskip) { - // -1 means autoframeskip - if (frameskip == -1) + if (0 > frameskip) { + // -1 means autoframeskip + if (!m_auto_frameskip) + m_frameskip_level = 0; m_auto_frameskip = true; - m_frameskip_level = 0; } - - // any other level is a direct control - else if (frameskip >= 0 && frameskip <= MAX_FRAMESKIP) + else { + // any other level is a direct control m_auto_frameskip = false; - m_frameskip_level = frameskip; + m_frameskip_level = std::min<int>(frameskip, MAX_FRAMESKIP); } } @@ -220,54 +215,64 @@ void video_manager::frame_update(bool from_debugger) // only render sound and video if we're in the running phase machine_phase const phase = machine().phase(); bool skipped_it = m_skipping_this_frame; - if (phase == machine_phase::RUNNING && (!machine().paused() || machine().options().update_in_pause())) - { - bool anything_changed = finish_screen_updates(); + bool const update_screens = (phase == machine_phase::RUNNING) && (!machine().paused() || machine().options().update_in_pause()); + bool anything_changed = update_screens && finish_screen_updates(); - // if none of the screens changed and we haven't skipped too many frames in a row, - // mark this frame as skipped to prevent throttling; this helps for games that - // don't update their screen at the monitor refresh rate - if (!anything_changed && !m_auto_frameskip && m_frameskip_level == 0 && m_empty_skip_count++ < 3) - skipped_it = true; - else - m_empty_skip_count = 0; - } + // update inputs and draw the user interface + machine().osd().input_update(true); + anything_changed = emulator_info::draw_user_interface(machine()) || anything_changed; - // draw the user interface - emulator_info::draw_user_interface(machine()); + // let plugins draw over the UI + anything_changed = emulator_info::frame_hook() || anything_changed; + + // if none of the screens changed and we haven't skipped too many frames in a row, + // mark this frame as skipped to prevent throttling; this helps for games that + // don't update their screen at the monitor refresh rate + if (!anything_changed && !m_auto_frameskip && (m_frameskip_level == 0) && (m_empty_skip_count++ < 3)) + skipped_it = true; + else + m_empty_skip_count = 0; // if we're throttling, synchronize before rendering attotime current_time = machine().time(); - if (!from_debugger && !skipped_it && effective_throttle()) + if (!from_debugger && phase > machine_phase::INIT && !m_low_latency && effective_throttle()) update_throttle(current_time); // ask the OSD to update - g_profiler.start(PROFILER_BLIT); - machine().osd().update(!from_debugger && skipped_it); - g_profiler.stop(); + { + auto profile = g_profiler.start(PROFILER_BLIT); + machine().osd().update(!from_debugger && skipped_it); + } + + // we synchronize after rendering instead of before, if low latency mode is enabled + if (!from_debugger && phase > machine_phase::INIT && m_low_latency && effective_throttle()) + update_throttle(current_time); + machine().osd().input_update(false); emulator_info::periodic_check(); - // perform tasks for this frame if (!from_debugger) + { + // perform tasks for this frame machine().call_notifiers(MACHINE_NOTIFY_FRAME); - // update frameskipping - if (!from_debugger) - update_frameskip(); + // update frameskipping + if (phase > machine_phase::INIT) + update_frameskip(); - // update speed computations - if (!from_debugger && !skipped_it) - recompute_speed(current_time); + // update speed computations + if (!skipped_it && phase > machine_phase::INIT) + recompute_speed(current_time); + } // call the end-of-frame callback if (phase == machine_phase::RUNNING) { // reset partial updates if we're paused or if the debugger is active - screen_device *const screen = screen_device_iterator(machine().root_device()).first(); + screen_device *const screen = screen_device_enumerator(machine().root_device()).first(); bool const debugger_enabled = machine().debug_flags & DEBUG_FLAG_ENABLED; bool const within_instruction_hook = debugger_enabled && machine().debugger().within_instruction_hook(); - if (screen && (machine().paused() || from_debugger || within_instruction_hook)) + if (screen && ((machine().paused() && machine().options().update_in_pause()) || from_debugger || within_instruction_hook)) screen->reset_partial_updates(); } } @@ -293,7 +298,7 @@ std::string video_manager::speed_text() // if we're auto frameskipping, display that plus the level else if (effective_autoframeskip()) - util::stream_format(str, "auto%2d/%d", effective_frameskip(), MAX_FRAMESKIP); + util::stream_format(str, "auto%2d/%d", effective_frameskip(), m_frameskip_max ? m_frameskip_max : MAX_FRAMESKIP); // otherwise, just display the frameskip plus the level else @@ -301,11 +306,11 @@ std::string video_manager::speed_text() // append the speed for all cases except paused if (!paused) - util::stream_format(str, "%4d%%", (int)(100 * m_speed_percent + 0.5)); + util::stream_format(str, " %3d%%", int(100 * m_speed_percent + 0.5)); // display the number of partial updates as well int partials = 0; - for (screen_device &screen : screen_device_iterator(machine().root_device())) + for (screen_device &screen : screen_device_enumerator(machine().root_device())) partials += screen.partial_updates(); if (partials > 1) util::stream_format(str, "\n%d partial updates", partials); @@ -319,7 +324,7 @@ std::string video_manager::speed_text() // file handle //------------------------------------------------- -void video_manager::save_snapshot(screen_device *screen, emu_file &file) +void video_manager::save_snapshot(screen_device *screen, util::core_file &file) { // validate assert(!m_snap_native || screen != nullptr); @@ -330,16 +335,16 @@ void video_manager::save_snapshot(screen_device *screen, emu_file &file) // add two text entries describing the image std::string text1 = std::string(emulator_info::get_appname()).append(" ").append(emulator_info::get_build_version()); std::string text2 = std::string(machine().system().manufacturer).append(" ").append(machine().system().type.fullname()); - png_info pnginfo; - pnginfo.add_text("Software", text1.c_str()); - pnginfo.add_text("System", text2.c_str()); + util::png_info pnginfo; + pnginfo.add_text("Software", text1); + pnginfo.add_text("System", text2); // now do the actual work const rgb_t *palette = (screen != nullptr && screen->has_palette()) ? screen->palette().palette()->entry_list_adjusted() : nullptr; int entries = (screen != nullptr && screen->has_palette()) ? screen->palette().entries() : 0; - png_error error = png_write_bitmap(file, &pnginfo, m_snap_bitmap, entries, palette); - if (error != PNGERR_NONE) - osd_printf_error("Error generating PNG for snapshot: png_error = %d\n", error); + std::error_condition const error = util::png_write_bitmap(file, &pnginfo, m_snap_bitmap, entries, palette); + if (error) + osd_printf_error("Error generating PNG for snapshot (%s:%d %s)\n", error.category().name(), error.value(), error.message()); } @@ -350,290 +355,127 @@ void video_manager::save_snapshot(screen_device *screen, emu_file &file) void video_manager::save_active_screen_snapshots() { - // if we're native, then write one snapshot per visible screen if (m_snap_native) { - // write one snapshot per visible screen - for (screen_device &screen : screen_device_iterator(machine().root_device())) + // if we're native, then write one snapshot per visible screen + for (screen_device &screen : screen_device_enumerator(machine().root_device())) if (machine().render().is_live(screen)) { emu_file file(machine().options().snapshot_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); - osd_file::error filerr = open_next(file, "png"); - if (filerr == osd_file::error::NONE) + std::error_condition const filerr = open_next(file, "png"); + if (!filerr) save_snapshot(&screen, file); } } - - // otherwise, just write a single snapshot else { + // otherwise, just write a single snapshot emu_file file(machine().options().snapshot_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); - osd_file::error filerr = open_next(file, "png"); - if (filerr == osd_file::error::NONE) + std::error_condition const filerr = open_next(file, "png"); + if (!filerr) save_snapshot(nullptr, file); } } //------------------------------------------------- -// save_input_timecode - add a line of current -// timestamp to inp.timecode file +// begin_recording_screen - begin recording a +// movie for a specific screen //------------------------------------------------- -void video_manager::save_input_timecode() +void video_manager::begin_recording_screen(const std::string &filename, uint32_t index, screen_device *screen, movie_recording::format format) { - // if record timecode input is not active, do nothing - if (!m_timecode_enabled) { + // create the emu_file + bool const is_absolute_path = !filename.empty() && osd_is_absolute_path(filename); + std::unique_ptr<emu_file> movie_file = std::make_unique<emu_file>( + is_absolute_path ? "" : machine().options().snapshot_directory(), + OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); + + // and open the actual file + std::error_condition filerr = filename.empty() + ? open_next(*movie_file, movie_recording::format_file_extension(format)) + : movie_file->open(filename); + if (filerr) + { + osd_printf_error("Error creating movie, %s:%d %s\n", filerr.category().name(), filerr.value(), filerr.message()); return; } - m_timecode_write = true; -} -std::string &video_manager::timecode_text(std::string &str) -{ - attotime elapsed_time = machine().time() - m_timecode_start; - str = string_format(" %s%s%02d:%02d %s", - m_timecode_text, - m_timecode_text.empty() ? "" : " ", - (elapsed_time.m_seconds / 60) % 60, - elapsed_time.m_seconds % 60, - machine().paused() ? "[paused] " : ""); - return str; -} + // we have a file; try to create the recording + std::unique_ptr<movie_recording> recording = movie_recording::create(machine(), screen, format, std::move(movie_file), m_snap_bitmap); -std::string &video_manager::timecode_total_text(std::string &str) -{ - attotime elapsed_time = m_timecode_total; - if (machine().ui().show_timecode_counter()) { - elapsed_time += machine().time() - m_timecode_start; - } - str = string_format("TOTAL %02d:%02d ", - (elapsed_time.m_seconds / 60) % 60, - elapsed_time.m_seconds % 60); - return str; + // if successful push it onto the list + if (recording) + m_movie_recordings.push_back(std::move(recording)); } + //------------------------------------------------- -// begin_recording_mng - begin recording a MNG +// begin_recording - begin recording of a movie //------------------------------------------------- -void video_manager::begin_recording_mng(const char *name, uint32_t index, screen_device *screen) +void video_manager::begin_recording(const char *name, movie_recording::format format) { - // stop any existing recording - end_recording_mng(index); - - mng_info_t &info = m_mngs[index]; - - // reset the state - info.m_mng_frame = 0; - info.m_mng_next_frame_time = machine().time(); - - // create a new movie file and start recording - info.m_mng_file = std::make_unique<emu_file>(machine().options().snapshot_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); - osd_file::error filerr; - if (name != nullptr) - { - std::string full_name(name); - - if (index > 0) - { - char name_buf[256] = { 0 }; - snprintf(name_buf, 256, "%s%d", name, index); - full_name = name_buf; - } - - filerr = info.m_mng_file->open(full_name.c_str()); - } - else - { - filerr = open_next(*info.m_mng_file, "mng"); - } - - if (filerr == osd_file::error::NONE) - { - // start the capture - int rate = int(screen->frame_period().as_hz()); - png_error pngerr = mng_capture_start(*info.m_mng_file, m_snap_bitmap, rate); - if (pngerr != PNGERR_NONE) - { - osd_printf_error("Error capturing MNG, png_error=%d\n", pngerr); - return end_recording_mng(index); - } + // create a snapshot bitmap so we know what the target size is + screen_device_enumerator iterator(machine().root_device()); + screen_device_enumerator::iterator iter(iterator.begin()); + uint32_t count = (uint32_t)iterator.count(); + const bool no_screens(!count); - // compute the frame time - info.m_mng_frame_period = attotime::from_hz(rate); - } - else + if (no_screens) { - osd_printf_error("Error creating MNG, osd_file::error=%d\n", int(filerr)); - info.m_mng_file.reset(); + assert(!m_snap_native); + count = 1; } -} -//------------------------------------------------- -// begin_recording_avi - begin recording an AVI -//------------------------------------------------- + // clear out existing recordings + m_movie_recordings.clear(); -void video_manager::begin_recording_avi(const char *name, uint32_t index, screen_device *screen) -{ - // stop any existing recording - end_recording_avi(index); - - avi_info_t &avi_info = m_avis[index]; - - // reset the state - avi_info.m_avi_frame = 0; - avi_info.m_avi_next_frame_time = machine().time(); - - // build up information about this new movie - avi_file::movie_info info; - info.video_format = 0; - info.video_timescale = 1000 * screen->frame_period().as_hz(); - info.video_sampletime = 1000; - info.video_numsamples = 0; - info.video_width = m_snap_bitmap.width(); - info.video_height = m_snap_bitmap.height(); - info.video_depth = 24; - - info.audio_format = 0; - info.audio_timescale = machine().sample_rate(); - info.audio_sampletime = 1; - info.audio_numsamples = 0; - info.audio_channels = 2; - info.audio_samplebits = 16; - info.audio_samplerate = machine().sample_rate(); - - // create a new temporary movie file - osd_file::error filerr; - std::string fullpath; + // check if the supplied name already has the desired extension + std::string_view basename; + std::string extension; + if (name) { - emu_file tempfile(machine().options().snapshot_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); - if (name != nullptr) - { - std::string full_name(name); - - if (index > 0) - { - char name_buf[256] = { 0 }; - snprintf(name_buf, 256, "%s%d", name, index); - full_name = name_buf; - } - - filerr = tempfile.open(full_name.c_str()); - } - else + std::string_view const desired_ext = movie_recording::format_file_extension(format); + basename = name; + extension.reserve(1 + desired_ext.length()); + extension.assign(1, '.').append(desired_ext); + if (core_filename_ends_with(basename, extension)) { - filerr = open_next(tempfile, "avi"); + extension = basename.substr(basename.length() - extension.length()); + basename.remove_suffix(extension.length()); } - - // if we succeeded, make a copy of the name and create the real file over top - if (filerr == osd_file::error::NONE) - fullpath = tempfile.fullpath(); } - if (filerr == osd_file::error::NONE) + if (m_snap_native) { - // compute the frame time - avi_info.m_avi_frame_period = attotime::from_seconds(1000) / info.video_timescale; - - // create the file and free the string - avi_file::error avierr = avi_file::create(fullpath, info, avi_info.m_avi_file); - if (avierr != avi_file::error::NONE) + std::string tempname; + for (uint32_t index = 0; index < count; index++, iter++) { - osd_printf_error("Error creating AVI: %s\n", avi_file::error_string(avierr)); - return end_recording_avi(index); - } - } -} - -//------------------------------------------------- -// begin_recording - begin recording of a movie -//------------------------------------------------- - -void video_manager::begin_recording(const char *name, movie_format format) -{ - // create a snapshot bitmap so we know what the target size is - screen_device_iterator iterator = screen_device_iterator(machine().root_device()); - screen_device_iterator::auto_iterator iter = iterator.begin(); - const uint32_t count = (uint32_t)iterator.count(); - - switch (format) - { - case MF_AVI: - if (m_avis.empty()) - m_avis.resize(count); - if (m_snap_native) - { - for (uint32_t index = 0; index < count; index++, iter++) - { - create_snapshot_bitmap(iter.current()); - begin_recording_avi(name, index, iter.current()); - } - } - else - { - create_snapshot_bitmap(nullptr); - begin_recording_avi(name, 0, iter.current()); - } - break; + create_snapshot_bitmap(iter.current()); // TODO: only do this when starting on-the-fly, and make name match AVI file name - case MF_MNG: - if (m_mngs.empty()) - m_mngs.resize(count); - if (m_snap_native) + if (name) { - for (uint32_t index = 0; index < count; index++, iter++) - { - create_snapshot_bitmap(iter.current()); - begin_recording_mng(name, index, iter.current()); - } + if (1 < count) + tempname = util::string_format("%s%d%s", basename, index, extension); + else + tempname.assign(name).append(extension); } - else - { - create_snapshot_bitmap(nullptr); - begin_recording_mng(name, 0, iter.current()); - } - break; - default: - osd_printf_error("Unknown movie format: %d\n", format); - break; + begin_recording_screen( + tempname, + index, + iter.current(), + format); + } } -} - - -//-------------------------------------------------- -// end_recording_avi - stop recording an AVI movie -//-------------------------------------------------- - -void video_manager::end_recording_avi(uint32_t index) -{ - avi_info_t &info = m_avis[index]; - if (info.m_avi_file) + else { - info.m_avi_file.reset(); - - // reset the state - info.m_avi_frame = 0; + create_snapshot_bitmap(nullptr); + begin_recording_screen(std::string(basename) + extension, 0, iter.current(), format); } } -//-------------------------------------------------- -// end_recording_mng - stop recording a MNG movie -//-------------------------------------------------- - -void video_manager::end_recording_mng(uint32_t index) -{ - mng_info_t &info = m_mngs[index]; - if (info.m_mng_file != nullptr) - { - mng_capture_stop(*info.m_mng_file); - info.m_mng_file.reset(); - - // reset the state - info.m_mng_frame = 0; - } -} //------------------------------------------------- // add_sound_to_recording - add sound to a movie @@ -642,37 +484,10 @@ void video_manager::end_recording_mng(uint32_t index) void video_manager::add_sound_to_recording(const s16 *sound, int numsamples) { - for (uint32_t index = 0; index < m_avis.size(); index++) - { - add_sound_to_avi_recording(sound, numsamples, index); - if (!m_snap_native) - break; - } + for (auto &recording : m_movie_recordings) + recording->add_sound_to_recording(sound, numsamples); } -//------------------------------------------------- -// add_sound_to_avi_recording - add sound to an -// AVI recording for a given screen -//------------------------------------------------- - -void video_manager::add_sound_to_avi_recording(const s16 *sound, int numsamples, uint32_t index) -{ - avi_info_t &info = m_avis[index]; - // only record if we have a file - if (info.m_avi_file != nullptr) - { - g_profiler.start(PROFILER_MOVIE_REC); - - // write the next frame - avi_file::error avierr = info.m_avi_file->append_sound_samples(0, sound + 0, numsamples, 1); - if (avierr == avi_file::error::NONE) - avierr = info.m_avi_file->append_sound_samples(1, sound + 1, numsamples, 1); - if (avierr != avi_file::error::NONE) - end_recording_avi(index); - - g_profiler.stop(); - } -} //------------------------------------------------- // video_exit - close down the video system @@ -681,17 +496,7 @@ void video_manager::add_sound_to_avi_recording(const s16 *sound, int numsamples, void video_manager::exit() { // stop recording any movie - for (uint32_t index = 0; index < (std::max)(m_mngs.size(), m_avis.size()); index++) - { - if (index < m_avis.size()) - end_recording_avi(index); - - if (index < m_mngs.size()) - end_recording_mng(index); - - if (!m_snap_native) - break; - } + m_movie_recordings.clear(); // free the snapshot target machine().render().target_free(m_snap_target); @@ -713,7 +518,7 @@ void video_manager::exit() // when there are no screens to drive it //------------------------------------------------- -void video_manager::screenless_update_callback(void *ptr, int param) +void video_manager::screenless_update_callback(s32 param) { // force an update frame_update(false); @@ -727,45 +532,17 @@ void video_manager::screenless_update_callback(void *ptr, int param) void video_manager::postload() { - for (uint32_t index = 0; index < (std::max)(m_mngs.size(), m_avis.size()); index++) - { - if (index < m_avis.size()) - m_avis[index].m_avi_next_frame_time = machine().time(); - - if (index < m_mngs.size()) - m_mngs[index].m_mng_next_frame_time = machine().time(); + attotime const emutime = machine().time(); + for (const auto &x : m_movie_recordings) + x->set_next_frame_time(emutime); - if (!m_snap_native) - break; - } + // reset speed measurements + m_speed_last_realtime = osd_ticks(); + m_speed_last_emutime = emutime; } //------------------------------------------------- -// is_recording - returns whether or not any -// screen is currently recording -//------------------------------------------------- - -bool video_manager::is_recording() const -{ - for (mng_info_t const &mng : m_mngs) - { - if (mng.m_mng_file) - return true; - else if (!m_snap_native) - break; - } - for (avi_info_t const &avi : m_avis) - { - if (avi.m_avi_file) - return true; - else if (!m_snap_native) - break; - } - return false; -} - -//------------------------------------------------- // effective_autoframeskip - return the effective // autoframeskip value, accounting for fast // forward @@ -839,12 +616,15 @@ inline int video_manager::original_speed_setting() const bool video_manager::finish_screen_updates() { // finish updating the screens - screen_device_iterator iter(machine().root_device()); + screen_device_enumerator iter(machine().root_device()); bool has_live_screen = false; for (screen_device &screen : iter) { + if (screen.partial_scan_hpos() > 0) // previous update ended mid-scanline + screen.update_now(); screen.update_partial(screen.visible_area().max_y); + if (machine().render().is_live(screen)) has_live_screen = true; } @@ -857,9 +637,6 @@ bool video_manager::finish_screen_updates() if (screen.update_quads()) anything_changed = true; - // draw HUD from LUA callback (if any) - anything_changed |= emulator_info::frame_hook(); - // update our movie recording and burn-in state if (!machine().paused()) { @@ -919,17 +696,6 @@ void video_manager::update_throttle(attotime emutime) restoring from a saved state */ - static const u8 popcount[256] = - { - 0,1,1,2,1,2,2,3, 1,2,2,3,2,3,3,4, 1,2,2,3,2,3,3,4, 2,3,3,4,3,4,4,5, - 1,2,2,3,2,3,3,4, 2,3,3,4,3,4,4,5, 2,3,3,4,3,4,4,5, 3,4,4,5,4,5,5,6, - 1,2,2,3,2,3,3,4, 2,3,3,4,3,4,4,5, 2,3,3,4,3,4,4,5, 3,4,4,5,4,5,5,6, - 2,3,3,4,3,4,4,5, 3,4,4,5,4,5,5,6, 3,4,4,5,4,5,5,6, 4,5,5,6,5,6,6,7, - 1,2,2,3,2,3,3,4, 2,3,3,4,3,4,4,5, 2,3,3,4,3,4,4,5, 3,4,4,5,4,5,5,6, - 2,3,3,4,3,4,4,5, 3,4,4,5,4,5,5,6, 3,4,4,5,4,5,5,6, 4,5,5,6,5,6,6,7, - 2,3,3,4,3,4,4,5, 3,4,4,5,4,5,5,6, 3,4,4,5,4,5,5,6, 4,5,5,6,5,6,6,7, - 3,4,4,5,4,5,5,6, 4,5,5,6,5,6,6,7, 4,5,5,6,5,6,6,7, 5,6,6,7,6,7,7,8 - }; // outer scope so we can break out in case of a resync while (1) @@ -1002,7 +768,7 @@ void video_manager::update_throttle(attotime emutime) // if we're more than 1/10th of a second out, or if we are behind at all and emulation // is taking longer than the real frame, we just need to resync if (real_is_ahead_attoseconds < -ATTOSECONDS_PER_SECOND / 10 || - (real_is_ahead_attoseconds < 0 && popcount[m_throttle_history & 0xff] < 6)) + (real_is_ahead_attoseconds < 0 && population_count_32(m_throttle_history & 0xff) < 6)) { if (LOG_THROTTLE) machine().logerror("Resync due to being behind: %s (history=%08X)\n", attotime(0, -real_is_ahead_attoseconds).as_string(18), m_throttle_history); @@ -1041,7 +807,7 @@ osd_ticks_t video_manager::throttle_until_ticks(osd_ticks_t target_ticks) bool const allowed_to_sleep = (machine().options().sleep() && (!effective_autoframeskip() || effective_frameskip() == 0)) || machine().paused(); // loop until we reach our target - g_profiler.start(PROFILER_IDLE); + auto profile = g_profiler.start(PROFILER_IDLE); osd_ticks_t current_ticks = osd_ticks(); while (current_ticks < target_ticks) { @@ -1077,7 +843,6 @@ osd_ticks_t video_manager::throttle_until_ticks(osd_ticks_t target_ticks) } current_ticks = new_ticks; } - g_profiler.stop(); return current_ticks; } @@ -1094,12 +859,12 @@ void video_manager::update_frameskip() if (effective_throttle() && effective_autoframeskip() && m_frameskip_counter == 0) { // calibrate the "adjusted speed" based on the target - double adjusted_speed_percent = m_speed_percent / (double) m_throttle_rate; + double adjusted_speed_percent = m_speed_percent / double(m_throttle_rate); - // if we're too fast, attempt to increase the frameskip double speed = m_speed * 0.001; if (adjusted_speed_percent >= 0.995 * speed) { + // if we're too fast, attempt to decrease the frameskip // but only after 3 consecutive frames where we are too fast if (++m_frameskip_adjust >= 3) { @@ -1108,23 +873,19 @@ void video_manager::update_frameskip() m_frameskip_level--; } } - - // if we're too slow, attempt to increase the frameskip else { - // if below 80% speed, be more aggressive - if (adjusted_speed_percent < 0.80 * speed) + // if we're too slow, attempt to increase the frameskip + if (adjusted_speed_percent < 0.80 * speed) // if below 80% speed, be more aggressive m_frameskip_adjust -= (0.90 * speed - m_speed_percent) / 0.05; - - // if we're close, only force it up to frameskip 8 - else if (m_frameskip_level < 8) + else if (m_frameskip_level < 8) // if we're close, only force it up to frameskip 8 m_frameskip_adjust--; // perform the adjustment while (m_frameskip_adjust <= -2) { m_frameskip_adjust += 2; - if (m_frameskip_level < MAX_FRAMESKIP) + if (m_frameskip_level < (m_frameskip_max ? m_frameskip_max : MAX_FRAMESKIP)) m_frameskip_level++; } } @@ -1152,7 +913,7 @@ void video_manager::update_refresh_speed() // find the screen with the shortest frame period (max refresh rate) // note that we first check the token since this can get called before all screens are created attoseconds_t min_frame_period = ATTOSECONDS_PER_SECOND; - for (screen_device &screen : screen_device_iterator(machine().root_device())) + for (screen_device &screen : screen_device_enumerator(machine().root_device())) { attoseconds_t period = screen.frame_period().attoseconds(); if (period != 0) @@ -1229,10 +990,23 @@ void video_manager::recompute_speed(const attotime &emutime) if (m_seconds_to_run != 0 && emutime.seconds() >= m_seconds_to_run) { // create a final screenshot - emu_file file(machine().options().snapshot_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); - osd_file::error filerr = file.open(machine().basename(), PATH_SEPARATOR "final.png"); - if (filerr == osd_file::error::NONE) - save_snapshot(nullptr, file); + if (m_snap_native) + { + for (screen_device &screen : screen_device_enumerator(machine().root_device())) + { + emu_file file(machine().options().snapshot_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); + std::error_condition const filerr = open_next(file, "png"); + if (!filerr) + save_snapshot(&screen, file); + } + } + else + { + emu_file file(machine().options().snapshot_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); + std::error_condition const filerr = open_next(file, "png"); + if (!filerr) + save_snapshot(nullptr, file); + } //printf("Scheduled exit at %f\n", emutime.as_double()); // schedule our demise @@ -1253,30 +1027,28 @@ typedef software_renderer<u32, 0,0,0, 16,8,0, false, false> snap_renderer; void video_manager::create_snapshot_bitmap(screen_device *screen) { // select the appropriate view in our dummy target - if (m_snap_native && screen != nullptr) + if (m_snap_native && screen) { - screen_device_iterator iter(machine().root_device()); + screen_device_enumerator iter(machine().root_device()); int view_index = iter.indexof(*screen); assert(view_index != -1); m_snap_target->set_view(view_index); } - // get the minimum width/height and set it on the target + // get the minimum width/height and set it on the target and bitmap s32 width, height; compute_snapshot_size(width, height); m_snap_target->set_bounds(width, height); - - // if we don't have a bitmap, or if it's not the right size, allocate a new one - if (!m_snap_bitmap.valid() || width != m_snap_bitmap.width() || height != m_snap_bitmap.height()) - m_snap_bitmap.allocate(width, height); + if (width != m_snap_bitmap.width() || height != m_snap_bitmap.height()) + m_snap_bitmap.resize(width, height); // render the screen there render_primitive_list &primlist = m_snap_target->get_primitives(); primlist.acquire_lock(); if (machine().options().snap_bilinear()) - snap_renderer_bilinear::draw_primitives(primlist, &m_snap_bitmap.pix32(0), width, height, m_snap_bitmap.rowpixels()); + snap_renderer_bilinear::draw_primitives(primlist, &m_snap_bitmap.pix(0), width, height, m_snap_bitmap.rowpixels()); else - snap_renderer::draw_primitives(primlist, &m_snap_bitmap.pix32(0), width, height, m_snap_bitmap.rowpixels()); + snap_renderer::draw_primitives(primlist, &m_snap_bitmap.pix(0), width, height, m_snap_bitmap.rowpixels()); primlist.release_lock(); } @@ -1321,21 +1093,26 @@ void video_manager::pixels(u32 *buffer) // scheme //------------------------------------------------- -osd_file::error video_manager::open_next(emu_file &file, const char *extension, uint32_t added_index) +std::error_condition video_manager::open_next(emu_file &file, const char *extension, uint32_t added_index) { u32 origflags = file.openflags(); // handle defaults const char *snapname = machine().options().snap_name(); - if (snapname == nullptr || snapname[0] == 0) + if (!snapname || !snapname[0]) snapname = "%g/%i"; std::string snapstr(snapname); - // strip any extension in the provided name - int index = snapstr.find_last_of('.'); - if (index != -1) - snapstr = snapstr.substr(0, index); + // strip desired extension if already present + std::string extstr; + extstr.reserve(1 + std::strlen(extension)); + extstr.assign(1, '.').append(extension); + if (core_filename_ends_with(snapstr, extstr)) + { + extstr = snapstr.substr(snapstr.length() - extstr.length()); + snapstr.resize(snapstr.length() - extstr.length()); + } // handle %d in the template (for image devices) std::string snapdev("%d_"); @@ -1344,7 +1121,7 @@ osd_file::error video_manager::open_next(emu_file &file, const char *extension, if (pos != -1) { // if more %d are found, revert to default and ignore them all - if (snapstr.find(snapdev.c_str(), pos + 3) != -1) + if (snapstr.find(snapdev, pos + 3) != -1) snapstr.assign("%g/%i"); // else if there is a single %d, try to create the correct snapname else @@ -1352,29 +1129,17 @@ osd_file::error video_manager::open_next(emu_file &file, const char *extension, int name_found = 0; // find length of the device name - int end1 = snapstr.find("/", pos + 3); - int end2 = snapstr.find("%", pos + 3); - int end; - - if ((end1 != -1) && (end2 != -1)) - end = std::min(end1, end2); - else if (end1 != -1) - end = end1; - else if (end2 != -1) - end = end2; - else + int end = snapstr.find_first_not_of("abcdefghijklmnopqrstuvwxyz1234567890", pos + 3); + if (end == -1) end = snapstr.length(); - if (end - pos < 3) - fatalerror("Something very wrong is going on!!!\n"); - // copy the device name to an std::string std::string snapdevname; snapdevname.assign(snapstr.substr(pos + 3, end - pos - 3)); //printf("check template: %s\n", snapdevname.c_str()); // verify that there is such a device for this system - for (device_image_interface &image : image_interface_iterator(machine().root_device())) + for (device_image_interface &image : image_interface_enumerator(machine().root_device())) { // get the device name std::string tempdevname(image.brief_instance_name()); @@ -1391,7 +1156,7 @@ osd_file::error video_manager::open_next(emu_file &file, const char *extension, filename = filename.substr(0, filename.find_last_of('.')); // setup snapname and remove the %d_ - strreplace(snapstr, snapdevname.c_str(), filename.c_str()); + strreplace(snapstr, snapdevname, filename); snapstr.erase(pos, 3); //printf("check image: %s\n", filename.c_str()); @@ -1406,41 +1171,54 @@ osd_file::error video_manager::open_next(emu_file &file, const char *extension, } } - // add our own extension - snapstr.append(".").append(extension); + // handle %t in the template (for timestamp) + std::string snaptime("%t"); + int pos_time = snapstr.find(snaptime); + + if (pos_time != -1) + { + char t_str[16]; + const std::time_t cur_time = std::time(nullptr); + strftime(t_str, sizeof(t_str), "%Y%m%d_%H%M%S", std::localtime(&cur_time)); + strreplace(snapstr, "%t", t_str); + } + + // append extension + snapstr.append(extstr); // substitute path and gamename up front strreplace(snapstr, "/", PATH_SEPARATOR); strreplace(snapstr, "%g", machine().basename()); - // determine if the template has an index; if not, we always use the same name + // determine if the template has an index std::string fname; if (snapstr.find("%i") == -1) + { + // if not, we always use the same name fname.assign(snapstr); - - // otherwise, we scan for the next available filename + } else { + // otherwise, we scan for the next available filename + // try until we succeed file.set_openflags(OPEN_FLAG_WRITE); for (int seq = 0; ; seq++) { // build up the filename fname.assign(snapstr); - strreplace(fname, "%i", string_format("%04d", seq).c_str()); + strreplace(fname, "%i", string_format("%04d", seq)); // try to open the file; stop when we fail - osd_file::error filerr = file.open(fname.c_str()); - if (filerr == osd_file::error::NOT_FOUND) - { + std::error_condition const filerr = file.open(fname); + if (std::errc::no_such_file_or_directory == filerr) break; - } } } // create the final file file.set_openflags(origflags); - return file.open(fname.c_str()); + return file.open(fname); } @@ -1455,92 +1233,25 @@ void video_manager::record_frame() return; // start the profiler and get the current time - g_profiler.start(PROFILER_MOVIE_REC); + auto profile = g_profiler.start(PROFILER_MOVIE_REC); attotime curtime = machine().time(); - screen_device_iterator device_iterator = screen_device_iterator(machine().root_device()); - screen_device_iterator::auto_iterator iter = device_iterator.begin(); - - for (uint32_t index = 0; index < (std::max)(m_mngs.size(), m_avis.size()); index++, iter++) + bool error = false; + for (auto &recording : m_movie_recordings) { // create the bitmap - create_snapshot_bitmap(iter.current()); - - // handle an AVI recording - if ((index < m_avis.size()) && m_avis[index].m_avi_file) - { - avi_info_t &avi_info = m_avis[index]; - - // loop until we hit the right time - while (avi_info.m_avi_next_frame_time <= curtime) - { - // write the next frame - avi_file::error avierr = avi_info.m_avi_file->append_video_frame(m_snap_bitmap); - if (avierr != avi_file::error::NONE) - { - g_profiler.stop(); // FIXME: double exit if this happens? - end_recording_avi(index); - break; - } - - // advance time - avi_info.m_avi_next_frame_time += avi_info.m_avi_frame_period; - avi_info.m_avi_frame++; - } - } - - // handle a MNG recording - if ((index < m_mngs.size()) && m_mngs[index].m_mng_file) - { - mng_info_t &mng_info = m_mngs[index]; - - // loop until we hit the right time - while (mng_info.m_mng_next_frame_time <= curtime) - { - // set up the text fields in the movie info - png_info pnginfo; - if (mng_info.m_mng_frame == 0) - { - std::string text1 = std::string(emulator_info::get_appname()).append(" ").append(emulator_info::get_build_version()); - std::string text2 = std::string(machine().system().manufacturer).append(" ").append(machine().system().type.fullname()); - pnginfo.add_text("Software", text1.c_str()); - pnginfo.add_text("System", text2.c_str()); - } - - // write the next frame - screen_device *screen = iter.current(); - const rgb_t *palette = (screen != nullptr && screen->has_palette()) ? screen->palette().palette()->entry_list_adjusted() : nullptr; - int entries = (screen != nullptr && screen->has_palette()) ? screen->palette().entries() : 0; - png_error error = mng_capture_frame(*mng_info.m_mng_file, pnginfo, m_snap_bitmap, entries, palette); - if (error != PNGERR_NONE) - { - g_profiler.stop(); // FIXME: double exit if this happens? - end_recording_mng(index); - break; - } + create_snapshot_bitmap(recording->screen()); - // advance time - mng_info.m_mng_next_frame_time += mng_info.m_mng_frame_period; - mng_info.m_mng_frame++; - } - } - - if (!m_snap_native) + // and append the frame + if (!recording->append_video_frame(m_snap_bitmap, curtime)) { + error = true; break; } } - g_profiler.stop(); -} - -//------------------------------------------------- -// toggle_throttle -//------------------------------------------------- - -void video_manager::toggle_throttle() -{ - set_throttled(!throttled()); + if (error) + end_recording(); } @@ -1548,52 +1259,21 @@ void video_manager::toggle_throttle() // toggle_record_movie //------------------------------------------------- -void video_manager::toggle_record_movie(movie_format format) +void video_manager::toggle_record_movie(movie_recording::format format) { if (!is_recording()) { begin_recording(nullptr, format); - machine().popmessage("REC START (%s)", format == MF_MNG ? "MNG" : "AVI"); + machine().popmessage("REC START (%s)", format == movie_recording::format::MNG ? "MNG" : "AVI"); } else { - end_recording(format); - machine().popmessage("REC STOP (%s)", format == MF_MNG ? "MNG" : "AVI"); + end_recording(); + machine().popmessage("REC STOP"); } } -void video_manager::end_recording(movie_format format) +void video_manager::end_recording() { - screen_device_iterator device_iterator = screen_device_iterator(machine().root_device()); - screen_device_iterator::auto_iterator iter = device_iterator.begin(); - const uint32_t count = (uint32_t)device_iterator.count(); - - switch (format) - { - case MF_AVI: - for (uint32_t index = 0; index < count; index++, iter++) - { - end_recording_avi(index); - if (!m_snap_native) - { - break; - } - } - break; - - case MF_MNG: - for (uint32_t index = 0; index < count; index++, iter++) - { - end_recording_mng(index); - if (!m_snap_native) - { - break; - } - } - break; - - default: - osd_printf_error("Unknown movie format: %d\n", format); - break; - } + m_movie_recordings.clear(); } diff --git a/src/emu/video.h b/src/emu/video.h index b8b1c408773..efc819ae793 100644 --- a/src/emu/video.h +++ b/src/emu/video.h @@ -17,7 +17,9 @@ #ifndef MAME_EMU_VIDEO_H #define MAME_EMU_VIDEO_H -#include "aviio.h" +#include "recording.h" + +#include <system_error> //************************************************************************** @@ -40,13 +42,6 @@ class video_manager friend class screen_device; public: - // movie format options - enum movie_format - { - MF_MNG, - MF_AVI - }; - // construction/destruction video_manager(running_machine &machine); @@ -58,21 +53,17 @@ public: bool throttled() const { return m_throttled; } float throttle_rate() const { return m_throttle_rate; } bool fastforward() const { return m_fastforward; } - bool is_recording() const; // setters void set_frameskip(int frameskip); - void set_throttled(bool throttled = true) { m_throttled = throttled; } + void set_throttled(bool throttled) { m_throttled = throttled; } void set_throttle_rate(float throttle_rate) { m_throttle_rate = throttle_rate; } - void set_fastforward(bool ffwd = true) { m_fastforward = ffwd; } + void set_fastforward(bool ffwd) { m_fastforward = ffwd; } void set_output_changed() { m_output_changed = true; } // misc - void toggle_throttle(); - void toggle_record_movie(movie_format format); - void toggle_record_mng() { toggle_record_movie(MF_MNG); } - void toggle_record_avi() { toggle_record_movie(MF_AVI); } - osd_file::error open_next(emu_file &file, const char *extension, uint32_t index = 0); + void toggle_record_movie(movie_recording::format format); + std::error_condition open_next(emu_file &file, const char *extension, uint32_t index = 0); void compute_snapshot_size(s32 &width, s32 &height); void pixels(u32 *buffer); @@ -85,34 +76,21 @@ public: int effective_frameskip() const; // snapshots - void save_snapshot(screen_device *screen, emu_file &file); + bool snap_native() const { return m_snap_native; } + render_target &snapshot_target() { return *m_snap_target; } + void save_snapshot(screen_device *screen, util::core_file &file); void save_active_screen_snapshots(); - void save_input_timecode(); // movies - void begin_recording(const char *name, movie_format format); - void begin_recording_mng(const char *name, uint32_t index, screen_device *screen); - void begin_recording_avi(const char *name, uint32_t index, screen_device *screen); - void end_recording(movie_format format); - void end_recording_mng(uint32_t index); - void end_recording_avi(uint32_t index); + void begin_recording(const char *name, movie_recording::format format); + void end_recording(); void add_sound_to_recording(const s16 *sound, int numsamples); - void add_sound_to_avi_recording(const s16 *sound, int numsamples, uint32_t index); - - void set_timecode_enabled(bool value) { m_timecode_enabled = value; } - bool get_timecode_enabled() { return m_timecode_enabled; } - bool get_timecode_write() { return m_timecode_write; } - void set_timecode_write(bool value) { m_timecode_write = value; } - void set_timecode_text(std::string &str) { m_timecode_text = str; } - void set_timecode_start(attotime time) { m_timecode_start = time; } - void add_to_total_time(attotime time) { m_timecode_total += time; } - std::string &timecode_text(std::string &str); - std::string &timecode_total_text(std::string &str); + bool is_recording() const { return !m_movie_recordings.empty(); } private: // internal helpers void exit(); - void screenless_update_callback(void *ptr, int param); + void screenless_update_callback(s32 param); void postload(); // effective value helpers @@ -132,6 +110,9 @@ private: void create_snapshot_bitmap(screen_device *screen); void record_frame(); + // movies + void begin_recording_screen(const std::string &filename, uint32_t index, screen_device *screen, movie_recording::format format); + // internal state running_machine & m_machine; // reference to our machine @@ -163,9 +144,11 @@ private: u32 m_seconds_to_run; // number of seconds to run before quitting bool m_auto_frameskip; // flag: true if we're automatically frameskipping u32 m_speed; // overall speed (*1000) + bool m_low_latency; // flag: true if we are throttling after blitting // frameskipping u8 m_empty_skip_count; // number of empty frames we have skipped + u8 m_frameskip_max; // maximum frameskip level u8 m_frameskip_level; // current frameskip level u8 m_frameskip_counter; // counter that counts through the frameskip steps s8 m_frameskip_adjust; @@ -179,49 +162,13 @@ private: s32 m_snap_width; // width of snapshots (0 == auto) s32 m_snap_height; // height of snapshots (0 == auto) - // movie recording - MNG - class mng_info_t - { - public: - mng_info_t() - : m_mng_frame_period(attotime::zero) - , m_mng_next_frame_time(attotime::zero) - , m_mng_frame(0) { } - - std::unique_ptr<emu_file> m_mng_file; // handle to the open movie file - attotime m_mng_frame_period; // period of a single movie frame - attotime m_mng_next_frame_time; // time of next frame - u32 m_mng_frame; // current movie frame number - }; - std::vector<mng_info_t> m_mngs; - - // movie recording - AVI - class avi_info_t - { - public: - avi_info_t() - : m_avi_file(nullptr) - , m_avi_frame_period(attotime::zero) - , m_avi_next_frame_time(attotime::zero) - , m_avi_frame(0) { } - - avi_file::ptr m_avi_file; // handle to the open movie file - attotime m_avi_frame_period; // period of a single movie frame - attotime m_avi_next_frame_time; // time of next frame - u32 m_avi_frame; // current movie frame number - }; - std::vector<avi_info_t> m_avis; + // movie recordings + std::vector<movie_recording::ptr> m_movie_recordings; static const bool s_skiptable[FRAMESKIP_LEVELS][FRAMESKIP_LEVELS]; static const attoseconds_t ATTOSECONDS_PER_SPEED_UPDATE = ATTOSECONDS_PER_SECOND / 4; static const int PAUSED_REFRESH_RATE = 30; - - bool m_timecode_enabled; // inp.timecode record enabled - bool m_timecode_write; // Show/hide timer at right (partial time) - std::string m_timecode_text; // Message for that video part (intro, gameplay, extra) - attotime m_timecode_start; // Starting timer for that video part (intro, gameplay, extra) - attotime m_timecode_total; // Show/hide timer at left (total elapsed on resulting video preview) }; #endif // MAME_EMU_VIDEO_H diff --git a/src/emu/video/generic.cpp b/src/emu/video/generic.cpp index b43c9d8fa9d..3e100e16498 100644 --- a/src/emu/video/generic.cpp +++ b/src/emu/video/generic.cpp @@ -2,7 +2,7 @@ // copyright-holders:Nicola Salmoria /********************************************************************* - generic.c + generic.cpp Generic simple video functions. diff --git a/src/emu/video/resnet.cpp b/src/emu/video/resnet.cpp index f746f8448d6..25e9242e4f2 100644 --- a/src/emu/video/resnet.cpp +++ b/src/emu/video/resnet.cpp @@ -2,7 +2,7 @@ // copyright-holders:Couriersud /***************************************************************************** - resnet.c + resnet.cpp Compute weights for resistors networks. @@ -58,7 +58,7 @@ double compute_resistor_weights( int count_2, const int * resistances_2, double * weights_2, int pulldown_2, int pullup_2, int count_3, const int * resistances_3, double * weights_3, int pulldown_3, int pullup_3 ) { - int networks_no; + assert(minval < maxval); int rescount[MAX_NETS]; /* number of resistors in each of the nets */ double r[MAX_NETS][MAX_RES_PER_NET]; /* resistances */ @@ -70,14 +70,10 @@ double compute_resistor_weights( double max_out[MAX_NETS]; double * out[MAX_NETS]; - int i,j,n; - double scale; - double max; - /* parse input parameters */ - networks_no = 0; - for (n = 0; n < MAX_NETS; n++) + int networks_no = 0; + for (int n = 0; n < MAX_NETS; n++) { int count, pd, pu; const int * resistances; @@ -116,7 +112,7 @@ double compute_resistor_weights( if (count > 0) { rescount[networks_no] = count; - for (i=0; i < count; i++) + for (int i=0; i < count; i++) { r[networks_no][i] = 1.0 * resistances[i]; } @@ -130,17 +126,17 @@ double compute_resistor_weights( fatalerror("compute_resistor_weights(): no input data\n"); /* calculate outputs for all given networks */ - for( i = 0; i < networks_no; i++ ) + for (int i = 0; i < networks_no; i++) { double R0, R1, Vout, dst; /* of n resistors */ - for(n = 0; n < rescount[i]; n++) + for (int n = 0; n < rescount[i]; n++) { R0 = ( r_pd[i] == 0 ) ? 1.0/1e12 : 1.0/r_pd[i]; R1 = ( r_pu[i] == 0 ) ? 1.0/1e12 : 1.0/r_pu[i]; - for( j = 0; j < rescount[i]; j++ ) + for (int j = 0; j < rescount[i]; j++) { if( j==n ) /* only one resistance in the network connected to Vcc */ { @@ -158,21 +154,21 @@ double compute_resistor_weights( Vout = (maxval - minval) * R0 / (R1 + R0) + minval; /* and convert it to a destination value */ - dst = (Vout < minval) ? minval : (Vout > maxval) ? maxval : Vout; + dst = std::clamp(Vout, double(minval), double(maxval)); w[i][n] = dst; } } /* calculate maximum outputs for all given networks */ - j = 0; - max = 0.0; - for( i = 0; i < networks_no; i++ ) + int j = 0; + double max = 0.0; + for (int i = 0; i < networks_no; i++) { double sum = 0.0; /* of n resistors */ - for( n = 0; n < rescount[i]; n++ ) + for (int n = 0; n < rescount[i]; n++) sum += w[i][n]; /* maximum output, ie when each resistance is connected to Vcc */ max_out[i] = sum; @@ -183,17 +179,17 @@ double compute_resistor_weights( } } - + double scale; if (scaler < 0.0) /* use autoscale ? */ /* calculate the output scaler according to the network with the greatest output */ - scale = ((double)maxval) / max_out[j]; + scale = double(maxval) / max_out[j]; else /* use scaler provided on entry */ scale = scaler; /* calculate scaled output and fill the output table(s)*/ - for(i = 0; i < networks_no;i++) + for (int i = 0; i < networks_no; i++) { - for (n = 0; n < rescount[i]; n++) + for (int n = 0; n < rescount[i]; n++) { ws[i][n] = w[i][n]*scale; /* scale the result */ (out[i])[n] = ws[i][n]; /* fill the output table */ @@ -206,7 +202,7 @@ if (VERBOSE) osd_printf_info("compute_resistor_weights(): scaler = %15.10f\n",scale); osd_printf_info("min val :%i max val:%i Total number of networks :%i\n", minval, maxval, networks_no ); - for(i = 0; i < networks_no;i++) + for (int i = 0; i < networks_no; i++) { double sum = 0.0; @@ -216,7 +212,7 @@ if (VERBOSE) if (r_pd[i] != 0) osd_printf_info(", pulldown resistor: %i Ohms",r_pd[i]); osd_printf_info("\n maximum output of this network:%10.5f (scaled to %15.10f)\n", max_out[i], max_out[i]*scale ); - for (n = 0; n < rescount[i]; n++) + for (int n = 0; n < rescount[i]; n++) { osd_printf_info(" res %2i:%9.1f Ohms weight=%10.5f (scaled = %15.10f)\n", n, r[i][n], w[i][n], ws[i][n] ); sum += ws[i][n]; @@ -237,7 +233,7 @@ double compute_resistor_net_outputs( int count_2, const int * resistances_2, double * outputs_2, int pulldown_2, int pullup_2, int count_3, const int * resistances_3, double * outputs_3, int pulldown_3, int pullup_3 ) { - int networks_no; + assert(minval < maxval); int rescount[MAX_NETS]; /* number of resistors in each of the nets */ double r[MAX_NETS][MAX_RES_PER_NET]; /* resistances */ @@ -248,18 +244,13 @@ double compute_resistor_net_outputs( double min_out[MAX_NETS]; double * out[MAX_NETS]; - int i,j,n; - double scale; - double min; - double max; - /* parse input parameters */ std::vector<double> o((1<<MAX_RES_PER_NET) * MAX_NETS); std::vector<double> os((1<<MAX_RES_PER_NET) * MAX_NETS); - networks_no = 0; - for (n = 0; n < MAX_NETS; n++) + int networks_no = 0; + for (int n = 0; n < MAX_NETS; n++) { int count, pd, pu; const int * resistances; @@ -297,7 +288,7 @@ double compute_resistor_net_outputs( if (count > 0) { rescount[networks_no] = count; - for (i=0; i < count; i++) + for (int i = 0; i < count; i++) { r[networks_no][i] = 1.0 * resistances[i]; } @@ -312,17 +303,17 @@ double compute_resistor_net_outputs( fatalerror("compute_resistor_net_outputs(): no input data\n"); /* calculate outputs for all given networks */ - for( i = 0; i < networks_no; i++ ) + for (int i = 0; i < networks_no; i++) { double R0, R1, Vout, dst; /* of n resistors, generating 1<<n possible outputs */ - for(n = 0; n < (1<<rescount[i]); n++) + for (int n = 0; n < (1<<rescount[i]); n++) { R0 = ( r_pd[i] == 0 ) ? 1.0/1e12 : 1.0/r_pd[i]; R1 = ( r_pu[i] == 0 ) ? 1.0/1e12 : 1.0/r_pu[i]; - for( j = 0; j < rescount[i]; j++ ) + for (int j = 0; j < rescount[i]; j++) { if( (n & (1<<j)) == 0 )/* only when this resistance in the network connected to GND */ if (r[i][j] != 0.0) @@ -335,22 +326,22 @@ double compute_resistor_net_outputs( Vout = (maxval - minval) * R0 / (R1 + R0) + minval; /* and convert it to a destination value */ - dst = (Vout < minval) ? minval : (Vout > maxval) ? maxval : Vout; + dst = std::clamp(Vout, double(minval), double(maxval)); o[i*(1<<MAX_RES_PER_NET)+n] = dst; } } /* calculate minimum outputs for all given networks */ - min = maxval; - max = minval; - for( i = 0; i < networks_no; i++ ) + double min = maxval; + double max = minval; + for (int i = 0; i < networks_no; i++) { double val; double max_tmp = minval; double min_tmp = maxval; - for (n = 0; n < (1<<rescount[i]); n++) + for (int n = 0; n < (1<<rescount[i]); n++) { if (min_tmp > o[i*(1<<MAX_RES_PER_NET)+n]) min_tmp = o[i*(1<<MAX_RES_PER_NET)+n]; @@ -373,17 +364,17 @@ double compute_resistor_net_outputs( } } - + double scale; if (scaler < 0.0) /* use autoscale ? */ /* calculate the output scaler according to the network with the smallest output */ - scale = ((double)maxval) / (max-min); + scale = double(maxval) / (max-min); else /* use scaler provided on entry */ scale = scaler; /* calculate scaled output and fill the output table(s) */ - for(i = 0; i < networks_no; i++) + for (int i = 0; i < networks_no; i++) { - for (n = 0; n < (1<<rescount[i]); n++) + for (int n = 0; n < (1<<rescount[i]); n++) { os[i*(1<<MAX_RES_PER_NET)+n] = (o[i*(1<<MAX_RES_PER_NET)+n] - min) * scale; /* scale the result */ (out[i])[n] = os[i*(1<<MAX_RES_PER_NET)+n]; /* fill the output table */ @@ -396,7 +387,7 @@ if (VERBOSE) osd_printf_info("compute_resistor_net_outputs(): scaler = %15.10f\n",scale); osd_printf_info("min val :%i max val:%i Total number of networks :%i\n", minval, maxval, networks_no ); - for(i = 0; i < networks_no;i++) + for (int i = 0; i < networks_no; i++) { osd_printf_info(" Network no.%i=> resistances: %i", i, rescount[i] ); if (r_pu[i] != 0) @@ -405,11 +396,11 @@ if (VERBOSE) osd_printf_info(", pulldown resistor: %i Ohms",r_pd[i]); osd_printf_info("\n maximum output of this network:%10.5f", max_out[i] ); osd_printf_info("\n minimum output of this network:%10.5f\n", min_out[i] ); - for (n = 0; n < rescount[i]; n++) + for (int n = 0; n < rescount[i]; n++) { osd_printf_info(" res %2i:%9.1f Ohms\n", n, r[i][n]); } - for (n = 0; n < (1<<rescount[i]); n++) + for (int n = 0; n < (1<<rescount[i]); n++) { osd_printf_info(" combination %2i out=%10.5f (scaled = %15.10f)\n", n, o[i*(1<<MAX_RES_PER_NET)+n], os[i*(1<<MAX_RES_PER_NET)+n] ); } @@ -449,7 +440,6 @@ int compute_res_net(int inputs, int channel, const res_net_info &di) { double rTotal=0.0; double v = 0; - int i; double vBias = di.rgb[channel].vBias; double vOH = di.vOH; @@ -606,7 +596,7 @@ int compute_res_net(int inputs, int channel, const res_net_info &di) /* compute here - pass a / low inputs */ - for (i=0; i<di.rgb[channel].num; i++) + for (int i = 0; i < di.rgb[channel].num; i++) { int level = ((inputs >> i) & 1); if (di.rgb[channel].R[i] != 0.0 && !level) @@ -650,7 +640,7 @@ int compute_res_net(int inputs, int channel, const res_net_info &di) /* Second pass - high inputs */ - for (i=0; i<di.rgb[channel].num; i++) + for (int i = 0; i < di.rgb[channel].num; i++) { int level = ((inputs >> i) & 1); if (di.rgb[channel].R[i] != 0.0 && level) @@ -689,31 +679,27 @@ int compute_res_net(int inputs, int channel, const res_net_info &di) break; } - return (int) (v * 255 / vcc + 0.4); + return int(v * 255 / vcc + 0.4); } void compute_res_net_all(std::vector<rgb_t> &rgb, const u8 *prom, const res_net_decode_info &rdi, const res_net_info &di) { - u8 r,g,b; - int i,j,k; - rgb.resize(rdi.end - rdi.start + 1); - for (i=rdi.start; i<=rdi.end; i++) + for (int i = rdi.start; i <= rdi.end; i++) { u8 t[3] = {0,0,0}; - int s; - for (j=0;j<rdi.numcomp;j++) - for (k=0; k<3; k++) + for (int j = 0; j < rdi.numcomp; j++) + for (int k = 0; k < 3; k++) { - s = rdi.shift[3*j+k]; + int s = rdi.shift[3*j+k]; if (s>0) t[k] = t[k] | ( (prom[i+rdi.offset[3*j+k]]>>s) & rdi.mask[3*j+k]); else t[k] = t[k] | ( (prom[i+rdi.offset[3*j+k]]<<(0-s)) & rdi.mask[3*j+k]); } - r = compute_res_net(t[0], RES_NET_CHAN_RED, di); - g = compute_res_net(t[1], RES_NET_CHAN_GREEN, di); - b = compute_res_net(t[2], RES_NET_CHAN_BLUE, di); + u8 r = compute_res_net(t[0], RES_NET_CHAN_RED, di); + u8 g = compute_res_net(t[1], RES_NET_CHAN_GREEN, di); + u8 b = compute_res_net(t[2], RES_NET_CHAN_BLUE, di); rgb[i-rdi.start] = rgb_t(r,g,b); } } diff --git a/src/emu/video/resnet.h b/src/emu/video/resnet.h index acc62bece79..53ef41ee5dd 100644 --- a/src/emu/video/resnet.h +++ b/src/emu/video/resnet.h @@ -106,7 +106,7 @@ struct res_net_channel_info { // - Least significant bit first double R[8]; // Minimum output voltage - // - Applicable if output is routed through a complimentary + // - Applicable if output is routed through a complementary // - darlington circuit // - typical value ~ 0.9V double minout; @@ -161,7 +161,7 @@ void compute_res_net_all(std::vector<rgb_t> &rgb, const u8 *prom, const res_net_ /* legacy interface */ -namespace emu { namespace detail { +namespace emu::detail { template <std::size_t I, typename T, std::size_t N, typename U> constexpr auto combine_weights(T const (&tab)[N], U w) { return tab[I] * w; } @@ -169,7 +169,7 @@ constexpr auto combine_weights(T const (&tab)[N], U w) { return tab[I] * w; } template <std::size_t I, typename T, std::size_t N, typename U, typename... V> constexpr auto combine_weights(T const (&tab)[N], U w0, V... w) { return (tab[I] * w0) + combine_weights<I + 1>(tab, w...); } -} } // namespace emu::detail +} // namespace emu::detail double compute_resistor_weights( int minval, int maxval, double scaler, diff --git a/src/emu/video/rgbgen.cpp b/src/emu/video/rgbgen.cpp index db4cd95de61..5973b56d54b 100644 --- a/src/emu/video/rgbgen.cpp +++ b/src/emu/video/rgbgen.cpp @@ -2,7 +2,7 @@ // copyright-holders:Vas Crabb, Ryan Holtz /*************************************************************************** - rgbgen.c + rgbgen.cpp General RGB utilities. @@ -10,8 +10,7 @@ #include "emu.h" - -#if !(defined(__ALTIVEC__) || ((!defined(MAME_DEBUG) || defined(__OPTIMIZE__)) && (defined(__SSE2__) || defined(_MSC_VER)) && defined(PTR64))) +#if ((defined(MAME_DEBUG) && !defined(__OPTIMIZE__)) || (!defined(__SSE2__) && (!defined(_M_IX86_FP) || (_M_IX86_FP < 2)))) && !defined(__ALTIVEC__) #include "rgbgen.h" @@ -115,4 +114,4 @@ void rgbaint_t::scale2_add_and_clamp(const rgbaint_t& scale, const rgbaint_t& ot if (u32(m_b) > 255) { m_b = (m_b < 0) ? 0 : 255; } } -#endif // !defined(__ALTIVEC__) +#endif // ((defined(MAME_DEBUG) && !defined(__OPTIMIZE__)) || (!defined(__SSE2__) && (!defined(_M_IX86_FP) || (_M_IX86_FP < 2)))) && !defined(__ALTIVEC__) diff --git a/src/emu/video/rgbgen.h b/src/emu/video/rgbgen.h index e460208d9fd..b20d79c94d8 100644 --- a/src/emu/video/rgbgen.h +++ b/src/emu/video/rgbgen.h @@ -151,16 +151,21 @@ public: inline void shl(const rgbaint_t& shift) { - m_a <<= shift.m_a; - m_r <<= shift.m_r; - m_g <<= shift.m_g; - m_b <<= shift.m_b; + m_a = (u32(shift.m_a) > 31) ? 0 : (m_a << shift.m_a); + m_r = (u32(shift.m_r) > 31) ? 0 : (m_r << shift.m_r); + m_g = (u32(shift.m_g) > 31) ? 0 : (m_g << shift.m_g); + m_b = (u32(shift.m_b) > 31) ? 0 : (m_b << shift.m_b); } inline void shl_imm(const u8 shift) { if (shift == 0) return; + if (shift > 31) + { + zero(); + return; + } m_a <<= shift; m_r <<= shift; @@ -170,16 +175,21 @@ public: inline void shr(const rgbaint_t& shift) { - m_a = s32(u32(m_a) >> shift.m_a); - m_r = s32(u32(m_r) >> shift.m_r); - m_g = s32(u32(m_g) >> shift.m_g); - m_b = s32(u32(m_b) >> shift.m_b); + m_a = (u32(shift.m_a) > 31) ? 0 : s32(u32(m_a) >> shift.m_a); + m_r = (u32(shift.m_r) > 31) ? 0 : s32(u32(m_r) >> shift.m_r); + m_g = (u32(shift.m_g) > 31) ? 0 : s32(u32(m_g) >> shift.m_g); + m_b = (u32(shift.m_b) > 31) ? 0 : s32(u32(m_b) >> shift.m_b); } inline void shr_imm(const u8 shift) { if (shift == 0) return; + if (shift > 31) + { + zero(); + return; + } m_a = s32(u32(m_a) >> shift); m_r = s32(u32(m_r) >> shift); @@ -189,43 +199,19 @@ public: inline void sra(const rgbaint_t& shift) { - m_a >>= shift.m_a; - if (m_a & (1 << (31 - shift.m_a))) - m_a |= ~0 << (32 - shift.m_a); - - m_r >>= shift.m_r; - if (m_r & (1 << (31 - shift.m_r))) - m_r |= ~0 << (32 - shift.m_r); - - m_g >>= shift.m_g; - if (m_g & (1 << (31 - shift.m_g))) - m_g |= ~0 << (32 - shift.m_g); - - m_b >>= shift.m_b; - if (m_b & (1 << (31 - shift.m_b))) - m_b |= ~0 << (32 - shift.m_b); + m_a >>= (u32(shift.m_a) > 31) ? 31 : shift.m_a; + m_r >>= (u32(shift.m_r) > 31) ? 31 : shift.m_r; + m_g >>= (u32(shift.m_g) > 31) ? 31 : shift.m_g; + m_b >>= (u32(shift.m_b) > 31) ? 31 : shift.m_b; } inline void sra_imm(const u8 shift) { - const u32 high_bit = 1 << (31 - shift); - const u32 high_mask = ~0 << (32 - shift); - - m_a >>= shift; - if (m_a & high_bit) - m_a |= high_mask; - - m_r >>= shift; - if (m_r & high_bit) - m_r |= high_mask; - - m_g >>= shift; - if (m_g & high_bit) - m_g |= high_mask; - - m_b >>= shift; - if (m_b & high_bit) - m_b |= high_mask; + const u8 s = std::min<u8>(shift, 31); + m_a >>= s; + m_r >>= s; + m_g >>= s; + m_b >>= s; } void or_reg(const rgbaint_t& color) { or_imm_rgba(color.m_a, color.m_r, color.m_g, color.m_b); } diff --git a/src/emu/video/rgbsse.cpp b/src/emu/video/rgbsse.cpp index 719310ff144..d7cabfa19a2 100644 --- a/src/emu/video/rgbsse.cpp +++ b/src/emu/video/rgbsse.cpp @@ -2,7 +2,7 @@ // copyright-holders:Vas Crabb, Ryan Holtz /*************************************************************************** - rgbsse.c + rgbsse.cpp SSE optimized RGB utilities. @@ -12,11 +12,11 @@ #include "emu.h" -#if (!defined(MAME_DEBUG) || defined(__OPTIMIZE__)) && (defined(__SSE2__) || defined(_MSC_VER)) && defined(PTR64) +#if (!defined(MAME_DEBUG) || defined(__OPTIMIZE__)) && (defined(__SSE2__) || defined(_M_X64) || (defined(_M_IX86_FP) && (_M_IX86_FP >= 2))) -#include <emmintrin.h> #include "rgbsse.h" + /*************************************************************************** TABLES ***************************************************************************/ @@ -185,4 +185,4 @@ void rgbaint_t::scale_and_clamp(const rgbaint_t& scale) clamp_to_uint8(); } -#endif // defined(__SSE2__) || defined(_MSC_VER) +#endif // (!defined(MAME_DEBUG) || defined(__OPTIMIZE__)) && (defined(__SSE2__) || (defined(_M_IX86_FP) && (_M_IX86_FP >= 2))) diff --git a/src/emu/video/rgbsse.h b/src/emu/video/rgbsse.h index a04145a4d82..ea3bc1e208b 100644 --- a/src/emu/video/rgbsse.h +++ b/src/emu/video/rgbsse.h @@ -271,6 +271,9 @@ public: inline void min(const s32 value) { __m128i val = _mm_set1_epi32(value); +#ifdef __SSE4_1__ + m_value = _mm_min_epi32(m_value, val); +#else __m128i is_greater_than = _mm_cmpgt_epi32(m_value, val); __m128i val_to_set = _mm_and_si128(val, is_greater_than); @@ -278,11 +281,15 @@ public: m_value = _mm_and_si128(m_value, keep_mask); m_value = _mm_or_si128(val_to_set, m_value); +#endif } inline void max(const s32 value) { __m128i val = _mm_set1_epi32(value); +#ifdef __SSE4_1__ + m_value = _mm_max_epi32(m_value, val); +#else __m128i is_less_than = _mm_cmplt_epi32(m_value, val); __m128i val_to_set = _mm_and_si128(val, is_less_than); @@ -290,6 +297,7 @@ public: m_value = _mm_and_si128(m_value, keep_mask); m_value = _mm_or_si128(val_to_set, m_value); +#endif } void blend(const rgbaint_t& other, u8 factor); @@ -324,48 +332,38 @@ public: m_value = _mm_unpacklo_epi16(m_value, _mm_setzero_si128()); } - // This function needs absolute value of color and scale to be 11 bits or less + // This function needs absolute value of color and scale to be 15 bits or less inline void scale_add_and_clamp(const rgbaint_t& scale, const rgbaint_t& other) { - // Pack scale into mult a 16 bits - __m128i tmp1 = _mm_packs_epi32(scale.m_value, _mm_setzero_si128()); - // Shift up by 4 - tmp1 = _mm_slli_epi16(tmp1, 4); - // Pack color into mult b 16 bit inputs - m_value = _mm_packs_epi32(m_value, _mm_setzero_si128()); - // Shift up by 4 - m_value = _mm_slli_epi16(m_value, 4); - // Do the 16 bit multiply, bottom 64 bits will contain 16 bit truncated results - m_value = _mm_mulhi_epi16(m_value, tmp1); - // Unpack up to s32, putting the 16 bit value at the top so the sign bit is set by the 16 bit result - m_value = _mm_unpacklo_epi16(_mm_setzero_si128(), m_value); - // Arithmetic shift down the 16 bit value to the lower 16 bits - sra_imm(16); +#ifdef __SSE4_1__ + m_value = _mm_mullo_epi32(m_value, scale.m_value); +#else + // Mask off the top 16 bits of each 32-bit value + m_value = _mm_and_si128(m_value, _mm_set1_epi32(0x0000ffff)); + // Do 16x16 multiplies and sum into 32-bit pairs; the AND above ensures upper pair is always 0 + m_value = _mm_madd_epi16(m_value, scale.m_value); +#endif + // Arithmetic shift down the result by 8 bits + sra_imm(8); add(other); clamp_to_uint8(); } - // This function needs absolute value of color and scale to be 11 bits or less + // This function needs absolute value of color and scale to be 15 bits or less inline void scale2_add_and_clamp(const rgbaint_t& scale, const rgbaint_t& other, const rgbaint_t& scale2) { - // Pack both scale values into mult a 16 bits - __m128i tmp1 = _mm_packs_epi32(scale.m_value, scale2.m_value); - // Shift up by 4 - tmp1 = _mm_slli_epi16(tmp1, 4); - // Pack both color values into mult b 16 bit inputs - m_value = _mm_packs_epi32(m_value, other.m_value); - // Shift up by 4 - m_value = _mm_slli_epi16(m_value, 4); - // Do the 16 bit multiply, top and bottom 64 bits will contain 16 bit truncated results - tmp1 = _mm_mulhi_epi16(m_value, tmp1); - // Unpack up to s32, putting the 16 bit value at the top so the sign bit is set by the 16 bit result - m_value = _mm_unpacklo_epi16(_mm_setzero_si128(), tmp1); - tmp1 = _mm_unpackhi_epi16(_mm_setzero_si128(), tmp1); - // Arithmetic shift down the 16 bit value to the lower 16 bits - sra_imm(16); - tmp1 = _mm_srai_epi32(tmp1, 16); - // Add the results - m_value = _mm_add_epi32(m_value, tmp1); + // Pack 32-bit values to 16-bit values in low half, and scales in top half + __m128i tmp1 = _mm_packs_epi32(m_value, scale.m_value); + // Same for other and scale2 + __m128i tmp2 = _mm_packs_epi32(other.m_value, scale2.m_value); + // Interleave the low halves (m_value, other) + __m128i tmp3 = _mm_unpacklo_epi16(tmp1, tmp2); + // Interleave the top halves (scale, scale2) + __m128i tmp4 = _mm_unpackhi_epi16(tmp1, tmp2); + // Multiply values by scales and add adjacent pairs + m_value = _mm_madd_epi16(tmp3, tmp4); + // Final shift by 8 + sra_imm(8); clamp_to_uint8(); } diff --git a/src/emu/video/rgbutil.h b/src/emu/video/rgbutil.h index 74f425e3417..c6ee325c754 100644 --- a/src/emu/video/rgbutil.h +++ b/src/emu/video/rgbutil.h @@ -13,13 +13,20 @@ #define MAME_EMU_VIDEO_RGBUTIL_H // use SSE on 64-bit implementations, where it can be assumed -#if (!defined(MAME_DEBUG) || defined(__OPTIMIZE__)) && (defined(__SSE2__) || defined(_MSC_VER)) && defined(PTR64) +#if (!defined(MAME_DEBUG) || defined(__OPTIMIZE__)) && (defined(__SSE2__) || defined(_M_X64) || (defined(_M_IX86_FP) && (_M_IX86_FP >= 2))) +#define MAME_RGB_HIGH_PRECISION #include "rgbsse.h" + #elif defined(__ALTIVEC__) + +#define MAME_RGB_HIGH_PRECISION #include "rgbvmx.h" + #else + #include "rgbgen.h" + #endif #endif // MAME_EMU_VIDEO_RGBUTIL_H diff --git a/src/emu/video/rgbvmx.cpp b/src/emu/video/rgbvmx.cpp index 6de183b66c2..62bd25b3578 100644 --- a/src/emu/video/rgbvmx.cpp +++ b/src/emu/video/rgbvmx.cpp @@ -2,7 +2,7 @@ // copyright-holders:Vas Crabb, Ryan Holtz /*************************************************************************** - rgbsse.c + rgbvmx.cpp VMX/Altivec optimised RGB utilities. @@ -190,7 +190,7 @@ void rgbaint_t::scale_and_clamp(const rgbaint_t& scale) clamp_to_uint8(); } -void rgbaint_t::scale_imm_and_clamp(const s32 scale) +void rgbaint_t::scale_imm_and_clamp(s32 scale) { mul_imm(scale); sra_imm(8); diff --git a/src/emu/video/rgbvmx.h b/src/emu/video/rgbvmx.h index 05d26cd9e21..dd1946bfa28 100644 --- a/src/emu/video/rgbvmx.h +++ b/src/emu/video/rgbvmx.h @@ -76,7 +76,7 @@ public: } // This function sets all elements to the same val - void set_all(const s32& val) { set(val, val, val, val); } + void set_all(const s32 &val) { set(val, val, val, val); } // This function zeros all elements void zero() { set_all(0); } // This function zeros only the alpha element @@ -100,31 +100,31 @@ public: return result; } - void set_a16(const s32 value) + void set_a16(s32 value) { const VECS32 temp = { value, value, value, value }; m_value = vec_perm(m_value, temp, alpha_perm); } - void set_a(const s32 value) + void set_a(s32 value) { const VECS32 temp = { value, value, value, value }; m_value = vec_perm(m_value, temp, alpha_perm); } - void set_r(const s32 value) + void set_r(s32 value) { const VECS32 temp = { value, value, value, value }; m_value = vec_perm(m_value, temp, red_perm); } - void set_g(const s32 value) + void set_g(s32 value) { const VECS32 temp = { value, value, value, value }; m_value = vec_perm(m_value, temp, green_perm); } - void set_b(const s32 value) + void set_b(s32 value) { const VECS32 temp = { value, value, value, value }; m_value = vec_perm(m_value, temp, blue_perm); @@ -229,13 +229,13 @@ public: m_value = vec_add(m_value, color2.m_value); } - inline void add_imm(const s32 imm) + inline void add_imm(s32 imm) { const VECS32 temp = { imm, imm, imm, imm }; m_value = vec_add(m_value, temp); } - inline void add_imm_rgba(const s32 a, const s32 r, const s32 g, const s32 b) + inline void add_imm_rgba(s32 a, s32 r, s32 g, s32 b) { #ifdef __LITTLE_ENDIAN__ const VECS32 temp = { b, g, r, a }; @@ -250,13 +250,13 @@ public: m_value = vec_sub(m_value, color2.m_value); } - inline void sub_imm(const s32 imm) + inline void sub_imm(s32 imm) { const VECS32 temp = { imm, imm, imm, imm }; m_value = vec_sub(m_value, temp); } - inline void sub_imm_rgba(const s32 a, const s32 r, const s32 g, const s32 b) + inline void sub_imm_rgba(s32 a, s32 r, s32 g, s32 b) { #ifdef __LITTLE_ENDIAN__ const VECS32 temp = { b, g, r, a }; @@ -271,13 +271,13 @@ public: m_value = vec_sub(color2.m_value, m_value); } - inline void subr_imm(const s32 imm) + inline void subr_imm(s32 imm) { const VECS32 temp = { imm, imm, imm, imm }; m_value = vec_sub(temp, m_value); } - inline void subr_imm_rgba(const s32 a, const s32 r, const s32 g, const s32 b) + inline void subr_imm_rgba(s32 a, s32 r, s32 g, s32 b) { #ifdef __LITTLE_ENDIAN__ const VECS32 temp = { b, g, r, a }; @@ -298,7 +298,7 @@ public: #endif } - inline void mul_imm(const s32 imm) + inline void mul_imm(s32 imm) { const VECU32 value = { u32(imm), u32(imm), u32(imm), u32(imm) }; const VECU32 shift = vec_splat_u32(-16); @@ -310,7 +310,7 @@ public: #endif } - inline void mul_imm_rgba(const s32 a, const s32 r, const s32 g, const s32 b) + inline void mul_imm_rgba(s32 a, s32 r, s32 g, s32 b) { #ifdef __LITTLE_ENDIAN__ const VECU32 value = { u32(b), u32(g), u32(r), u32(a) }; @@ -332,10 +332,17 @@ public: m_value = vec_and(vec_sl(m_value, VECU32(shift.m_value)), vec_cmpgt(limit, VECU32(shift.m_value))); } - inline void shl_imm(const u8 shift) + inline void shl_imm(u8 shift) { - const VECU32 temp = { shift, shift, shift, shift }; - m_value = vec_sl(m_value, temp); + if (32 > shift) + { + const VECU32 temp = { shift, shift, shift, shift }; + m_value = vec_sl(m_value, temp); + } + else + { + m_value = vec_splat_s32(0); + } } inline void shr(const rgbaint_t& shift) @@ -344,10 +351,17 @@ public: m_value = vec_and(vec_sr(m_value, VECU32(shift.m_value)), vec_cmpgt(limit, VECU32(shift.m_value))); } - inline void shr_imm(const u8 shift) + inline void shr_imm(u8 shift) { - const VECU32 temp = { shift, shift, shift, shift }; - m_value = vec_sr(m_value, temp); + if (32 > shift) + { + const VECU32 temp = { shift, shift, shift, shift }; + m_value = vec_sr(m_value, temp); + } + else + { + m_value = vec_splat_s32(0); + } } inline void sra(const rgbaint_t& shift) @@ -356,8 +370,9 @@ public: m_value = vec_sra(m_value, vec_min(VECU32(shift.m_value), limit)); } - inline void sra_imm(const u8 shift) + inline void sra_imm(u8 shift) { + shift = std::min<u8>(shift, 31); const VECU32 temp = { shift, shift, shift, shift }; m_value = vec_sra(m_value, temp); } @@ -367,13 +382,13 @@ public: m_value = vec_or(m_value, color2.m_value); } - inline void or_imm(const s32 value) + inline void or_imm(s32 value) { const VECS32 temp = { value, value, value, value }; m_value = vec_or(m_value, temp); } - inline void or_imm_rgba(const s32 a, const s32 r, const s32 g, const s32 b) + inline void or_imm_rgba(s32 a, s32 r, s32 g, s32 b) { #ifdef __LITTLE_ENDIAN__ const VECS32 temp = { b, g, r, a }; @@ -393,13 +408,13 @@ public: m_value = vec_andc(m_value, color.m_value); } - inline void and_imm(const s32 value) + inline void and_imm(s32 value) { const VECS32 temp = { value, value, value, value }; m_value = vec_and(m_value, temp); } - inline void and_imm_rgba(const s32 a, const s32 r, const s32 g, const s32 b) + inline void and_imm_rgba(s32 a, s32 r, s32 g, s32 b) { #ifdef __LITTLE_ENDIAN__ const VECS32 temp = { b, g, r, a }; @@ -414,13 +429,13 @@ public: m_value = vec_xor(m_value, color2.m_value); } - inline void xor_imm(const s32 value) + inline void xor_imm(s32 value) { const VECS32 temp = { value, value, value, value }; m_value = vec_xor(m_value, temp); } - inline void xor_imm_rgba(const s32 a, const s32 r, const s32 g, const s32 b) + inline void xor_imm_rgba(s32 a, s32 r, s32 g, s32 b) { #ifdef __LITTLE_ENDIAN__ const VECS32 temp = { b, g, r, a }; @@ -430,7 +445,7 @@ public: m_value = vec_xor(m_value, temp); } - inline void clamp_and_clear(const u32 sign) + inline void clamp_and_clear(u32 sign) { const VECS32 vzero = { 0, 0, 0, 0 }; VECS32 vsign = { s32(sign), s32(sign), s32(sign), s32(sign) }; @@ -454,7 +469,7 @@ public: #endif } - inline void sign_extend(const u32 compare, const u32 sign) + inline void sign_extend(u32 compare, u32 sign) { const VECS32 compare_vec = { s32(compare), s32(compare), s32(compare), s32(compare) }; const VECS32 compare_mask = VECS32(vec_cmpeq(vec_and(m_value, compare_vec), compare_vec)); @@ -462,13 +477,13 @@ public: m_value = vec_or(m_value, vec_and(sign_vec, compare_mask)); } - inline void min(const s32 value) + inline void min(s32 value) { const VECS32 temp = { value, value, value, value }; m_value = vec_min(m_value, temp); } - inline void max(const s32 value) + inline void max(s32 value) { const VECS32 temp = { value, value, value, value }; m_value = vec_max(m_value, temp); @@ -477,7 +492,7 @@ public: void blend(const rgbaint_t& other, u8 factor); void scale_and_clamp(const rgbaint_t& scale); - void scale_imm_and_clamp(const s32 scale); + void scale_imm_and_clamp(s32 scale); void scale_add_and_clamp(const rgbaint_t& scale, const rgbaint_t& other) { @@ -503,13 +518,13 @@ public: m_value = VECS32(vec_cmpeq(m_value, value.m_value)); } - inline void cmpeq_imm(const s32 value) + inline void cmpeq_imm(s32 value) { const VECS32 temp = { value, value, value, value }; m_value = VECS32(vec_cmpeq(m_value, temp)); } - inline void cmpeq_imm_rgba(const s32 a, const s32 r, const s32 g, const s32 b) + inline void cmpeq_imm_rgba(s32 a, s32 r, s32 g, s32 b) { #ifdef __LITTLE_ENDIAN__ const VECS32 temp = { b, g, r, a }; @@ -524,13 +539,13 @@ public: m_value = VECS32(vec_cmpgt(m_value, value.m_value)); } - inline void cmpgt_imm(const s32 value) + inline void cmpgt_imm(s32 value) { const VECS32 temp = { value, value, value, value }; m_value = VECS32(vec_cmpgt(m_value, temp)); } - inline void cmpgt_imm_rgba(const s32 a, const s32 r, const s32 g, const s32 b) + inline void cmpgt_imm_rgba(s32 a, s32 r, s32 g, s32 b) { #ifdef __LITTLE_ENDIAN__ const VECS32 temp = { b, g, r, a }; @@ -545,13 +560,13 @@ public: m_value = VECS32(vec_cmplt(m_value, value.m_value)); } - inline void cmplt_imm(const s32 value) + inline void cmplt_imm(s32 value) { const VECS32 temp = { value, value, value, value }; m_value = VECS32(vec_cmplt(m_value, temp)); } - inline void cmplt_imm_rgba(const s32 a, const s32 r, const s32 g, const s32 b) + inline void cmplt_imm_rgba(s32 a, s32 r, s32 g, s32 b) { #ifdef __LITTLE_ENDIAN__ const VECS32 temp = { b, g, r, a }; @@ -567,7 +582,7 @@ public: return *this; } - inline rgbaint_t& operator+=(const s32 other) + inline rgbaint_t& operator+=(s32 other) { const VECS32 temp = { other, other, other, other }; m_value = vec_add(m_value, temp); @@ -592,7 +607,7 @@ public: return *this; } - inline rgbaint_t& operator*=(const s32 other) + inline rgbaint_t& operator*=(s32 other) { const VECS32 value = { other, other, other, other }; const VECU32 shift = vec_splat_u32(-16); @@ -605,19 +620,20 @@ public: return *this; } - inline rgbaint_t& operator>>=(const s32 shift) + inline rgbaint_t& operator>>=(s32 shift) { + shift = s32(std::min<u32>(shift, 31)); const VECU32 temp = { u32(shift), u32(shift), u32(shift), u32(shift) }; m_value = vec_sra(m_value, temp); return *this; } - inline void merge_alpha16(const rgbaint_t& alpha) + inline void merge_alpha16(const rgbaint_t &alpha) { m_value = vec_perm(m_value, alpha.m_value, alpha_perm); } - inline void merge_alpha(const rgbaint_t& alpha) + inline void merge_alpha(const rgbaint_t &alpha) { m_value = vec_perm(m_value, alpha.m_value, alpha_perm); } diff --git a/src/emu/xtal.cpp b/src/emu/xtal.cpp index e0b6c0c2229..62d0370e661 100644 --- a/src/emu/xtal.cpp +++ b/src/emu/xtal.cpp @@ -57,366 +57,474 @@ const double XTAL::known_xtals[] = { /* Frequency Sugarvassed Examples ----------- ---------------------- ---------------------------------------- */ - 32'768, /* 32.768_kHz_XTAL Used to drive RTC chips */ - 38'400, /* 38.4_kHz_XTAL Resonator */ - 384'000, /* 384_kHz_XTAL Resonator - Commonly used for driving OKI MSM5205 */ - 400'000, /* 400_kHz_XTAL Resonator - OKI MSM5205 on Great Swordman h/w */ - 430'000, /* 430_kHz_XTAL Resonator */ - 455'000, /* 455_kHz_XTAL Resonator - OKI MSM5205 on Gladiator h/w */ - 512'000, /* 512_kHz_XTAL Resonator - Toshiba TC8830F */ - 600'000, /* 600_kHz_XTAL - */ - 640'000, /* 640_kHz_XTAL Resonator - NEC UPD7759, Texas Instruments Speech Chips @ 8khz */ - 960'000, /* 960_kHz_XTAL Resonator - Xerox Notetaker Keyboard UART */ - 1'000'000, /* 1_MHz_XTAL Used to drive OKI M6295 chips */ - 1'008'000, /* 1.008_MHz_XTAL Acorn Microcomputer (System 1) */ - 1'056'000, /* 1.056_MHz_XTAL Resonator - OKI M6295 on Trio The Punch h/w */ - 1'294'400, /* 1.2944_MHz_XTAL BBN BitGraph PSG */ - 1'689'600, /* 1.6896_MHz_XTAL Diablo 1355WP Printer */ - 1'750'000, /* 1.75_MHz_XTAL RCA CDP1861 */ - 1'797'100, /* 1.7971_MHz_XTAL SWTPC 6800 (with MIKBUG) */ - 1'843'200, /* 1.8432_MHz_XTAL Bondwell 12/14 */ - 2'000'000, /* 2_MHz_XTAL - */ - 2'012'160, /* 2.01216_MHz_XTAL Cidelsa Draco sound board */ - 2'097'152, /* 2.097152_MHz_XTAL Icatel 1995 - Brazilian public payphone */ - 2'457'600, /* 2.4576_MHz_XTAL Atari ST MFP */ - 2'500'000, /* 2.5_MHz_XTAL Janken Man units */ - 2'950'000, /* 2.95_MHz_XTAL Playmatic MPU-C, MPU-III & Sound-3 */ - 3'000'000, /* 3_MHz_XTAL Probably only used to drive 68705 or similar MCUs on 80's Taito PCBs */ - 3'072'000, /* 3.072_MHz_XTAL INS 8520 input clock rate */ - 3'120'000, /* 3.12_MHz_XTAL SP0250 clock on Gottlieb games */ - 3'276'800, /* 3.2768_MHz_XTAL SP0256 clock in Speech Synthesis for Dragon 32 */ - 3'521'280, /* 3.52128_MHz_XTAL RCA COSMAC VIP */ - 3'570'000, /* 3.57_MHz_XTAL Telmac TMC-600 */ - 3'578'640, /* 3.57864_MHz_XTAL Atari Portfolio PCD3311T */ - 3'579'545, /* 3.579545_MHz_XTAL NTSC color subcarrier, extremely common, used on 100's of PCBs (Keytronic custom part #48-300-010 is equivalent) */ - 3'686'400, /* 3.6864_MHz_XTAL Baud rate clock for MC68681 and similar UARTs */ - 3'840'000, /* 3.84_MHz_XTAL Fairlight CMI Alphanumeric Keyboard */ - 3'900'000, /* 3.9_MHz_XTAL Resonator - Used on some Fidelity boards */ - 3'932'160, /* 3.93216_MHz_XTAL Apple Lisa COP421 (197-0016A) */ - 4'000'000, /* 4_MHz_XTAL - */ - 4'028'000, /* 4.028_MHz_XTAL Sony SMC-777 */ - 4'032'000, /* 4.032_MHz_XTAL GRiD Compass modem board */ - 4'096'000, /* 4.096_MHz_XTAL Used to drive OKI M9810 chips */ - 4'194'304, /* 4.194304_MHz_XTAL Used to drive MC146818 / Nintendo Game Boy */ - 4'224'000, /* 4.224_MHz_XTAL Used to drive OKI M6295 chips, usually with /4 divider */ - 4'410'000, /* 4.41_MHz_XTAL Pioneer PR-8210 ldplayer */ - 4'433'610, /* 4.43361_MHz_XTAL Cidelsa Draco */ - 4'433'619, /* 4.433619_MHz_XTAL PAL color subcarrier (technically 4.43361875mhz)*/ - 4'608'000, /* 4.608_MHz_XTAL Luxor ABC-77 keyboard (Keytronic custom part #48-300-107 is equivalent) */ - 4'915'200, /* 4.9152_MHz_XTAL - */ - 5'000'000, /* 5_MHz_XTAL Mutant Night */ - 5'068'800, /* 5.0688_MHz_XTAL Usually used as MC2661 or COM8116 baud rate clock */ - 5'185'000, /* 5.185_MHz_XTAL Intel INTELLEC® 4 */ - 5'370'000, /* 5.37_MHz_XTAL HP 95LX */ - 5'460'000, /* 5.46_MHz_XTAL ec1840 and ec1841 keyboard */ - 5'529'600, /* 5.5296_MHz_XTAL Kontron PSI98 keyboard */ - 5'626'000, /* 5.626_MHz_XTAL RCA CDP1869 PAL dot clock */ - 5'670'000, /* 5.67_MHz_XTAL RCA CDP1869 NTSC dot clock */ - 5'714'300, /* 5.7143_MHz_XTAL Cidelsa Destroyer, TeleVideo serial keyboards */ - 5'856'000, /* 5.856_MHz_XTAL HP 3478A Multimeter */ - 5'911'000, /* 5.911_MHz_XTAL Philips Videopac Plus G7400 */ - 5'990'400, /* 5.9904_MHz_XTAL Luxor ABC 800 keyboard (Keytronic custom part #48-300-008 is equivalent) */ - 6'000'000, /* 6_MHz_XTAL American Poker II, Taito SJ System */ - 6'144'000, /* 6.144_MHz_XTAL Used on Alpha Denshi early 80's games sound board, Casio FP-200 and Namco Universal System 16 */ - 6'400'000, /* 6.4_MHz_XTAL Textel Compact */ - 6'500'000, /* 6.5_MHz_XTAL Jupiter Ace */ - 6'880'000, /* 6.88_MHz_XTAL Barcrest MPU4 */ - 6'900'000, /* 6.9_MHz_XTAL BBN BitGraph CPU */ - 7'000'000, /* 7_MHz_XTAL Jaleco Mega System PCBs */ - 7'159'090, /* 7.15909_MHz_XTAL Blood Bros (2x NTSC subcarrier) */ - 7'200'000, /* 7.2_MHz_XTAL Image Fight bootleg I80C031 MCU */ - 7'372'800, /* 7.3728_MHz_XTAL - */ - 7'864'300, /* 7.8643_MHz_XTAL Used on InterFlip games as video clock */ - 7'987'000, /* 7.987_MHz_XTAL PC9801-86 YM2608 clock */ - 7'995'500, /* 7.9955_MHz_XTAL Used on Electronic Devices Italy Galaxy Gunners sound board */ - 8'000'000, /* 8_MHz_XTAL Extremely common, used on 100's of PCBs */ - 8'200'000, /* 8.2_MHz_XTAL Universal Mr. Do - Model 8021 PCB */ - 8'388'000, /* 8.388_MHz_XTAL Nintendo Game Boy Color */ - 8'448'000, /* 8.448_MHz_XTAL Banpresto's Note Chance - Used to drive OKI M6295 chips, usually with /8 divider */ - 8'467'200, /* 8.4672_MHz_XTAL Subsino's Ying Hua Lian */ - 8'664'000, /* 8.664_MHz_XTAL Touchmaster */ - 8'700'000, /* 8.7_MHz_XTAL Tandberg TDV 2324 */ - 8'867'236, /* 8.867236_MHz_XTAL RCA CDP1869 PAL color clock (~2x PAL subcarrier) */ - 8'867'238, /* 8.867238_MHz_XTAL ETI-660 (~2x PAL subcarrier) */ - 8'945'000, /* 8.945_MHz_XTAL Hit Me */ - 9'216'000, /* 9.216_MHz_XTAL Conitec PROF-180X */ - 9'600'000, /* 9.6_MHz_XTAL WD37C65 second clock (for 300 KB/sec rate) */ - 9'828'000, /* 9.828_MHz_XTAL Universal PCBs */ - 9'830'400, /* 9.8304_MHz_XTAL Epson PX-8 */ - 9'832'000, /* 9.832_MHz_XTAL Robotron A7150 */ - 9'877'680, /* 9.87768_MHz_XTAL Microterm 420 */ - 9'987'000, /* 9.987_MHz_XTAL Crazy Balloon */ - 10'000'000, /* 10_MHz_XTAL - */ - 10'137'600, /* 10.1376_MHz_XTAL Wyse WY-100 */ - 10'245'000, /* 10.245_MHz_XTAL PES Speech box */ - 10'380'000, /* 10.38_MHz_XTAL Fairlight Q219 Lightpen/Graphics Card */ - 10'480'000, /* 10.48_MHz_XTAL System-80 (50 Hz) */ - 10'500'000, /* 10.5_MHz_XTAL Agat-7 */ - 10'595'000, /* 10.595_MHz_XTAL Mad Alien */ - 10'644'500, /* 10.6445_MHz_XTAL TRS-80 Model I */ - 10'687'500, /* 10.6875_MHz_XTAL BBC Bridge Companion */ - 10'694'250, /* 10.69425_MHz_XTAL Xerox 820 */ - 10'717'200, /* 10.7172_MHz_XTAL Eltec EurocomII */ - 10'730'000, /* 10.73_MHz_XTAL Ruleta RE-900 VDP Clock */ - 10'733'000, /* 10.733_MHz_XTAL The Fairyland Story */ - 10'738'000, /* 10.738_MHz_XTAL Pokerout (poker+breakout) TMS9129 VDP Clock */ - 10'738'635, /* 10.738635_MHz_XTAL TMS9918 family (3x NTSC subcarrier) */ - 10'816'000, /* 10.816_MHz_XTAL Universal 1979-1980 (Cosmic Alien, etc) */ - 10'886'400, /* 10.8864_MHz_XTAL Systel System 100 */ - 10'920'000, /* 10.92_MHz_XTAL ADDS Viewpoint 60, Viewpoint A2 */ - 11'000'000, /* 11_MHz_XTAL Mario I8039 sound */ - 11'004'000, /* 11.004_MHz_XTAL TI 911 VDT */ - 11'059'200, /* 11.0592_MHz_XTAL Used with MCS-51 to generate common baud rates */ - 11'200'000, /* 11.2_MHz_XTAL New York, New York */ - 11'289'000, /* 11.289_MHz_XTAL Vanguard */ - 11'400'000, /* 11.4_MHz_XTAL HP 9845 */ - 11'668'800, /* 11.6688_MHz_XTAL Gameplan pixel clock */ - 11'800'000, /* 11.8_MHz_XTAL IBM PC Music Feature Card */ - 11'980'800, /* 11.9808_MHz_XTAL Luxor ABC 80 */ - 12'000'000, /* 12_MHz_XTAL Extremely common, used on 100's of PCBs */ - 12'057'600, /* 12.0576_MHz_XTAL Poly 1 (38400 * 314) */ - 12'096'000, /* 12.096_MHz_XTAL Some early 80's Atari games */ - 12'288'000, /* 12.288_MHz_XTAL Sega Model 3 digital audio board */ - 12'324'000, /* 12.324_MHz_XTAL Otrona Attache */ - 12'432'000, /* 12.432_MHz_XTAL Kaneko Fly Boy/Fast Freddie Hardware */ - 12'472'500, /* 12.4725_MHz_XTAL Bonanza's Mini Boy 7 */ - 12'480'000, /* 12.48_MHz_XTAL TRS-80 Model II */ - 12'500'000, /* 12.5_MHz_XTAL Red Alert audio board */ - 12'672'000, /* 12.672_MHz_XTAL TRS-80 Model 4 80*24 video */ - 12'800'000, /* 12.8_MHz_XTAL Cave CV1000 */ - 12'854'400, /* 12.8544_MHz_XTAL Alphatronic P3 */ - 12'936'000, /* 12.936_MHz_XTAL CDC 721 */ - 12'979'200, /* 12.9792_MHz_XTAL Exidy 440 */ - 13'300'000, /* 13.3_MHz_XTAL BMC bowling */ - 13'330'560, /* 13.33056_MHz_XTAL Taito L */ - 13'333'000, /* 13.333_MHz_XTAL Ojanko High School */ - 13'400'000, /* 13.4_MHz_XTAL TNK3, Ikari Warriors h/w */ - 13'478'400, /* 13.4784_MHz_XTAL TeleVideo 970 80-column display clock */ - 13'495'200, /* 13.4952_MHz_XTAL Used on Shadow Force pcb and maybe other Technos pcbs? */ - 13'500'000, /* 13.5_MHz_XTAL Microbee */ - 13'516'800, /* 13.5168_MHz_XTAL Kontron KDT6 */ - 13'608'000, /* 13.608_MHz_XTAL TeleVideo 910 & 925 */ - 13'824'000, /* 13.824_MHz_XTAL Robotron PC-1715 display circuit */ - 14'000'000, /* 14_MHz_XTAL - */ - 14'112'000, /* 14.112_MHz_XTAL Timex/Sinclair TS2068 */ - 14'192'640, /* 14.19264_MHz_XTAL Central Data 2650 */ - 14'218'000, /* 14.218_MHz_XTAL Dragon */ - 14'300'000, /* 14.3_MHz_XTAL Agat-7 */ - 14'314'000, /* 14.314_MHz_XTAL Taito TTL Board */ - 14'318'181, /* 14.318181_MHz_XTAL Extremely common, used on 100's of PCBs (4x NTSC subcarrier) */ - 14'580'000, /* 14.58_MHz_XTAL Fortune 32:16 Video Controller */ - 14'705'882, /* 14.705882_MHz_XTAL Aleck64 */ - 14'728'000, /* 14.728_MHz_XTAL ADM 36 */ - 14'742'800, /* 14.7428_MHz_XTAL ADM 23 */ - 14'745'000, /* 14.745_MHz_XTAL Synertek KTM-3 */ - 14'745'600, /* 14.7456_MHz_XTAL Namco System 12 & System Super 22/23 for JVS */ - 14'784'000, /* 14.784_MHz_XTAL Zenith Z-29 */ - 14'916'000, /* 14.916_MHz_XTAL ADDS Viewpoint 122 */ - 14'976'000, /* 14.976_MHz_XTAL CIT-101 80-column display clock */ - 15'000'000, /* 15_MHz_XTAL Sinclair QL, Amusco Poker */ - 15'148'800, /* 15.1488_MHz_XTAL Zentec 9002/9003 */ - 15'206'400, /* 15.2064_MHz_XTAL Falco TS-1 */ - 15'288'000, /* 15.288_MHz_XTAL DEC VT220 80-column display clock */ - 15'300'720, /* 15.30072_MHz_XTAL Microterm 420 */ - 15'360'000, /* 15.36_MHz_XTAL Visual 1050 */ - 15'400'000, /* 15.4_MHz_XTAL DVK KSM */ - 15'468'480, /* 15.46848_MHz_XTAL Bank Panic h/w, Sega G80 */ - 15'582'000, /* 15.582_MHz_XTAL Zentec Zephyr */ - 15'667'200, /* 15.6672_MHz_XTAL Apple Macintosh */ - 15'700'000, /* 15.7_MHz_XTAL Motogonki */ - 15'897'600, /* 15.8976_MHz_XTAL IAI Swyft */ - 15'920'000, /* 15.92_MHz_XTAL HP Integral PC */ - 15'974'400, /* 15.9744_MHz_XTAL Osborne 1 (9600 * 52 * 32) */ - 16'000'000, /* 16_MHz_XTAL Extremely common, used on 100's of PCBs */ - 16'097'280, /* 16.09728_MHz_XTAL DEC VT240 (1024 * 262 * 60) */ - 16'128'000, /* 16.128_MHz_XTAL Fujitsu FM-7 */ - 16'200'000, /* 16.2_MHz_XTAL Debut */ - 16'257'000, /* 16.257_MHz_XTAL IBM PC MDA & EGA */ - 16'364'000, /* 16.364_MHz_XTAL Corvus Concept */ - 16'384'000, /* 16.384_MHz_XTAL - */ - 16'400'000, /* 16.4_MHz_XTAL MS 6102 */ - 16'572'000, /* 16.572_MHz_XTAL Micro-Term ACT-5A */ - 16'588'800, /* 16.5888_MHz_XTAL SM 7238 */ - 16'669'800, /* 16.6698_MHz_XTAL Qume QVT-102 */ - 16'670'000, /* 16.67_MHz_XTAL - */ - 16'777'216, /* 16.777216_MHz_XTAL Nintendo Game Boy Advance */ - 16'934'400, /* 16.9344_MHz_XTAL Usually used to drive 90's Yamaha OPL/FM chips (44100 * 384) */ - 17'010'000, /* 17.01_MHz_XTAL Epic 14E */ - 17'064'000, /* 17.064_MHz_XTAL Memorex 1377 */ - 17'350'000, /* 17.35_MHz_XTAL ITT Courier 1700 */ - 17'360'000, /* 17.36_MHz_XTAL OMTI Series 10 SCSI controller */ - 17'430'000, /* 17.43_MHz_XTAL Videx Videoterm */ - 17'550'000, /* 17.55_MHz_XTAL HP 264x display clock (50 Hz configuration) */ - 17'600'000, /* 17.6_MHz_XTAL LSI Octopus */ - 17'734'470, /* 17.73447_MHz_XTAL (~4x PAL subcarrier) */ - 17'734'472, /* 17.734472_MHz_XTAL actually ~4x PAL subcarrier */ - 17'971'200, /* 17.9712_MHz_XTAL Compucolor II, Hazeltine Esprit III */ - 18'000'000, /* 18_MHz_XTAL S.A.R, Ikari Warriors 3 */ - 18'414'000, /* 18.414_MHz_XTAL Ann Arbor Ambassador */ - 18'432'000, /* 18.432_MHz_XTAL Extremely common, used on 100's of PCBs (48000 * 384) */ - 18'480'000, /* 18.48_MHz_XTAL Wyse WY-100 video */ - 18'575'000, /* 18.575_MHz_XTAL Visual 102, Visual 220 */ - 18'720'000, /* 18.72_MHz_XTAL Nokia MikroMikko 1 */ - 18'867'000, /* 18.867_MHz_XTAL Decision Data IS-482 */ - 18'869'600, /* 18.8696_MHz_XTAL Memorex 2178 */ - 19'339'600, /* 19.3396_MHz_XTAL TeleVideo TVI-955 80-column display clock */ - 19'584'000, /* 19.584_MHz_XTAL ADM-42 */ - 19'600'000, /* 19.6_MHz_XTAL Universal Mr. Do - Model 8021 PCB */ - 19'602'000, /* 19.602_MHz_XTAL Ampex 210+ 80-column display clock */ - 19'660'800, /* 19.6608_MHz_XTAL Euro League (bootleg), labeled as "UKI 19.6608 20PF" */ - 19'661'400, /* 19.6614_MHz_XTAL Wyse WY-30 */ - 19'923'000, /* 19.923_MHz_XTAL Cinematronics vectors */ - 19'968'000, /* 19.968_MHz_XTAL Used mostly by some Taito games */ - 20'000'000, /* 20_MHz_XTAL - */ - 20'160'000, /* 20.16_MHz_XTAL Nintendo 8080 */ - 20'275'200, /* 20.2752_MHz_XTAL TRS-80 Model III */ - 20'375'040, /* 20.37504_MHz_XTAL Apple Lisa dot clock (197-0019A) */ - 20'625'000, /* 20.625_MHz_XTAL SM 7238 */ - 20'790'000, /* 20.79_MHz_XTAL Blockade-hardware Gremlin games */ - 21'000'000, /* 21_MHz_XTAL Lock-On pixel clock */ - 21'052'600, /* 21.0526_MHz_XTAL NEC PC-98xx pixel clock */ - 21'060'000, /* 21.06_MHz_XTAL HP 264x display clock (60 Hz configuration) */ - 21'254'400, /* 21.2544_MHz_XTAL TeleVideo 970 132-column display clock */ - 21'281'370, /* 21.28137_MHz_XTAL Radica Tetris (PAL) */ - 21'300'000, /* 21.3_MHz_XTAL - */ - 21'477'272, /* 21.477272_MHz_XTAL BMC bowling, some Data East 90's games, Vtech Socrates; (6x NTSC subcarrier) */ - 22'000'000, /* 22_MHz_XTAL - */ - 22'032'000, /* 22.032_MHz_XTAL Intellec Series II I/O controller */ - 22'096'000, /* 22.096_MHz_XTAL ADDS Viewpoint 122 */ - 22'118'400, /* 22.1184_MHz_XTAL Amusco Poker */ - 22'248'000, /* 22.248_MHz_XTAL Quantel DPB-7000 */ - 22'321'000, /* 22.321_MHz_XTAL Apple LaserWriter II NT */ - 22'464'000, /* 22.464_MHz_XTAL CIT-101 132-column display clock */ - 22'656'000, /* 22.656_MHz_XTAL Super Pinball Action (~1440x NTSC line rate) */ - 22'896'000, /* 22.896_MHz_XTAL DEC VT220 132-column display clock */ - 23'814'000, /* 23.814_MHz_XTAL TeleVideo TVI-912, 920 & 950 */ - 23'961'600, /* 23.9616_MHz_XTAL Osborne 4 (Vixen) */ - 24'000'000, /* 24_MHz_XTAL Mario, 80's Data East games, 80's Konami games */ - 24'073'400, /* 24.0734_MHz_XTAL DEC Rainbow 100 */ - 24'300'000, /* 24.3_MHz_XTAL ADM 36 132-column display clock */ - 24'576'000, /* 24.576_MHz_XTAL Pole Position h/w, Model 3 CPU board */ - 24'883'200, /* 24.8832_MHz_XTAL DEC VT100 */ - 25'000'000, /* 25_MHz_XTAL Namco System 22, Taito GNET, Dogyuun h/w */ - 25'174'800, /* 25.1748_MHz_XTAL Sega System 16A/16B (1600x NTSC line rate) */ - 25'175'000, /* 25.175_MHz_XTAL IBM MCGA/VGA 320/640-pixel graphics */ - 25'200'000, /* 25.2_MHz_XTAL Tektronix 4404 video clock */ - 25'398'360, /* 25.39836_MHz_XTAL Tandberg TDV 2324 */ - 25'400'000, /* 25.4_MHz_XTAL PC9801-86 PCM base clock */ - 25'447'000, /* 25.447_MHz_XTAL Namco EVA3A (Funcube2) */ - 25'771'500, /* 25.7715_MHz_XTAL HP-2622A */ - 25'920'000, /* 25.92_MHz_XTAL ADDS Viewpoint 60 */ - 26'000'000, /* 26_MHz_XTAL Gaelco PCBs */ - 26'366'000, /* 26.366_MHz_XTAL DEC VT320 */ - 26'580'000, /* 26.58_MHz_XTAL Wyse WY-60 80-column display clock */ - 26'590'906, /* 26.590906_MHz_XTAL Atari Jaguar NTSC */ - 26'593'900, /* 26.5939_MHz_XTAL Atari Jaguar PAL */ - 26'601'712, /* 26.601712_MHz_XTAL Astro Corp.'s Show Hand, PAL Vtech/Yeno Socrates (6x PAL subcarrier) */ - 26'666'000, /* 26.666_MHz_XTAL Imagetek I4100/I4220/I4300 */ - 26'666'666, /* 26.666666_MHz_XTAL Irem M92 but most use 27MHz */ - 26'686'000, /* 26.686_MHz_XTAL Typically used on 90's Taito PCBs to drive the custom chips */ - 26'989'200, /* 26.9892_MHz_XTAL TeleVideo 965 */ - 27'000'000, /* 27_MHz_XTAL Some Banpresto games macrossp, Irem M92 and 90's Toaplan games */ - 27'164'000, /* 27.164_MHz_XTAL Typically used on 90's Taito PCBs to drive the custom chips */ - 27'210'900, /* 27.2109_MHz_XTAL LA Girl */ - 27'562'000, /* 27.562_MHz_XTAL Visual 220 */ - 28'000'000, /* 28_MHz_XTAL - */ - 28'322'000, /* 28.322_MHz_XTAL Saitek RISC 2500, Mephisto Montreux */ - 28'375'160, /* 28.37516_MHz_XTAL Amiga PAL systems */ - 28'475'000, /* 28.475_MHz_XTAL CoCo 3 PAL */ - 28'480'000, /* 28.48_MHz_XTAL Chromatics CGC-7900 */ - 28'636'363, /* 28.636363_MHz_XTAL Later Leland games and Atari GT, Amiga NTSC, Raiden2 h/w (8x NTSC subcarrier)*/ - 28'640'000, /* 28.64_MHz_XTAL Fukki FG-1c AI AM-2 PCB */ - 28'700'000, /* 28.7_MHz_XTAL - */ - 29'376'000, /* 29.376_MHz_XTAL Qume QVT-103 */ - 29'491'200, /* 29.4912_MHz_XTAL Xerox Alto-II system clock (tagged 29.4MHz in the schematics) */ - 30'000'000, /* 30_MHz_XTAL Impera Magic Card */ - 30'209'800, /* 30.2098_MHz_XTAL Philips CD-i NTSC (1920x NTSC line rate) */ - 30'476'180, /* 30.47618_MHz_XTAL Taito F3, JC, Under Fire */ - 30'800'000, /* 30.8_MHz_XTAL 15IE-00-013 */ - 31'279'500, /* 31.2795_MHz_XTAL Wyse WY-30+ */ - 31'684'000, /* 31.684_MHz_XTAL TeleVideo TVI-955 132-column display clock */ - 31'948'800, /* 31.9488_MHz_XTAL NEC PC-88xx, PC-98xx */ - 32'000'000, /* 32_MHz_XTAL - */ - 32'147'000, /* 32.147_MHz_XTAL Ampex 210+ 132-column display clock */ - 32'220'000, /* 32.22_MHz_XTAL Typically used on 90's Data East PCBs (close to 9x NTSC subcarrier which is 32.215905Mhz */ - 32'256'000, /* 32.256_MHz_XTAL Hitachi MB-6890 */ - 32'317'400, /* 32.3174_MHz_XTAL DEC VT330, VT340 */ - 32'530'470, /* 32.53047_MHz_XTAL Seta 2 */ - 33'000'000, /* 33_MHz_XTAL Sega Model 3 video board */ - 33'264'000, /* 33.264_MHz_XTAL Hazeltine 1500 terminal */ - 33'330'000, /* 33.33_MHz_XTAL Sharp X68000 XVI */ - 33'333'000, /* 33.333_MHz_XTAL Sega Model 3 CPU board, Vegas */ - 33'833'000, /* 33.833_MHz_XTAL - */ - 33'868'800, /* 33.8688_MHz_XTAL Usually used to drive 90's Yamaha OPL/FM chips with /2 divider */ - 34'000'000, /* 34_MHz_XTAL Gaelco PCBs */ - 34'291'712, /* 34.291712_MHz_XTAL Fairlight CMI master card */ - 34'846'000, /* 34.846_MHz_XTAL Visual 550 */ - 35'904'000, /* 35.904_MHz_XTAL Used on HP98543 graphics board */ - 36'000'000, /* 36_MHz_XTAL Sega Model 1 video board */ - 36'864'000, /* 36.864_MHz_XTAL Unidesa Cirsa Rock 'n' Roll */ - 37'980'000, /* 37.98_MHz_XTAL Falco 5220 */ - 38'769'220, /* 38.76922_MHz_XTAL Namco System 21 video board */ - 38'863'630, /* 38.86363_MHz_XTAL Sharp X68000 15.98kHz video */ - 39'321'600, /* 39.3216_MHz_XTAL Sun 2/120 */ - 39'710'000, /* 39.71_MHz_XTAL Wyse WY-60 132-column display clock */ - 40'000'000, /* 40_MHz_XTAL - */ - 40'210'000, /* 40.21_MHz_XTAL Fairlight CMI IIx */ - 41'539'000, /* 41.539_MHz_XTAL IBM PS/2 132-column text mode */ - 42'000'000, /* 42_MHz_XTAL BMC A-00211 - Popo Bear */ - 42'105'200, /* 42.1052_MHz_XTAL NEC PC-88xx */ - 42'954'545, /* 42.954545_MHz_XTAL CPS3 (12x NTSC subcarrier)*/ - 43'320'000, /* 43.32_MHz_XTAL DEC VT420 */ - 44'100'000, /* 44.1_MHz_XTAL Subsino's Bishou Jan */ - 44'236'800, /* 44.2368_MHz_XTAL ReCo6502, Fortune 32:16 */ - 44'452'800, /* 44.4528_MHz_XTAL TeleVideo 965 */ - 44'900'000, /* 44.9_MHz_XTAL IBM 8514 1024x768 43.5Hz graphics */ - 45'000'000, /* 45_MHz_XTAL Eolith with Hyperstone CPUs */ - 45'158'400, /* 45.1584_MHz_XTAL Philips CD-i CDIC, Sega Model 2A video, Sega Model 3 CPU */ - 45'619'200, /* 45.6192_MHz_XTAL DEC VK100 */ - 45'830'400, /* 45.8304_MHz_XTAL Microterm 5510 */ - 46'615'120, /* 46.61512_MHz_XTAL Soundblaster 16 PCM base clock */ - 47'736'000, /* 47.736_MHz_XTAL Visual 100 */ - 48'000'000, /* 48_MHz_XTAL Williams/Midway Y/Z-unit system / SSV board */ - 48'384'000, /* 48.384_MHz_XTAL Namco NB-1 */ - 48'556'800, /* 48.5568_MHz_XTAL Wyse WY-85 */ - 48'654'000, /* 48.654_MHz_XTAL Qume QVT-201 */ - 48'660'000, /* 48.66_MHz_XTAL Zaxxon */ - 49'152'000, /* 49.152_MHz_XTAL Used on some Namco PCBs, Baraduke h/w, System 21, Super System 22 */ - 49'423'500, /* 49.4235_MHz_XTAL Wyse WY-185 */ - 50'000'000, /* 50_MHz_XTAL Williams/Midway T/W/V-unit system */ - 50'113'000, /* 50.113_MHz_XTAL Namco NA-1 (14x NTSC subcarrier)*/ - 50'349'000, /* 50.349_MHz_XTAL Sega System 18 (~3200x NTSC line rate) */ - 50'350'000, /* 50.35_MHz_XTAL Sharp X68030 video */ - 51'200'000, /* 51.2_MHz_XTAL Namco Super System 22 video clock */ - 52'000'000, /* 52_MHz_XTAL Cojag */ - 52'832'000, /* 52.832_MHz_XTAL Wang PC TIG video controller */ - 53'203'424, /* 53.203424_MHz_XTAL Master System, Mega Drive PAL (12x PAL subcarrier) */ - 53'693'175, /* 53.693175_MHz_XTAL PSX-based h/w, Sony ZN1-2-based (15x NTSC subcarrier) */ - 54'000'000, /* 54_MHz_XTAL Taito JC */ - 55'000'000, /* 55_MHz_XTAL Eolith Vega */ - 57'272'727, /* 57.272727_MHz_XTAL Psikyo SH2 with /2 divider (16x NTSC subcarrier)*/ - 58'000'000, /* 58_MHz_XTAL Magic Reel (Play System) */ - 59'292'000, /* 59.292_MHz_XTAL Data General D461 */ - 60'000'000, /* 60_MHz_XTAL ARM610 */ - 61'440'000, /* 61.44_MHz_XTAL Donkey Kong */ - 64'000'000, /* 64_MHz_XTAL BattleToads */ - 64'108'800, /* 64.1088_MHz_XTAL HP Topcat high-res */ - 66'666'700, /* 66.6667_MHz_XTAL Later Midway games */ - 67'737'600, /* 67.7376_MHz_XTAL PSX-based h/w, Sony ZN1-2-based */ - 68'850'000, /* 68.85_MHz_XTAL Wyse WY-50 */ - 69'551'990, /* 69.55199_MHz_XTAL Sharp X68000 31.5kHz video */ - 72'000'000, /* 72_MHz_XTAL Aristocrat MKV */ - 72'576'000, /* 72.576_MHz_XTAL Centipede, Millipede, Missile Command, Let's Go Bowling "Multipede" */ - 73'728'000, /* 73.728_MHz_XTAL Ms. Pac-Man/Galaga 20th Anniversary */ - 77'414'400, /* 77.4144_MHz_XTAL NCD17c */ - 80'000'000, /* 80_MHz_XTAL ARM710 */ - 87'183'360, /* 87.18336_MHz_XTAL AT&T 630 MTG */ - 92'940'500, /* 92.9405_MHz_XTAL Sun cgthree */ - 99'522'000, /* 99.522_MHz_XTAL Radius Two Page Display */ - 100'000'000, /* 100_MHz_XTAL PSX-based Namco System 12, Vegas, Sony ZN1-2-based */ - 101'491'200, /* 101.4912_MHz_XTAL PSX-based Namco System 10 */ - 108'108'000, /* 108.108_MHz_XTAL HP 98550 high-res color card */ - 200'000'000 /* 200_MHz_XTAL Base SH4 CPU (Naomi, Hikaru etc.) */ + 32'768, // 32.768_kHz_XTAL Used to drive RTC chips + 38'400, // 38.4_kHz_XTAL Resonator + 384'000, // 384_kHz_XTAL Resonator - Commonly used for driving OKI MSM5205 + 400'000, // 400_kHz_XTAL Resonator - OKI MSM5205 on Great Swordman h/w + 430'000, // 430_kHz_XTAL Resonator + 455'000, // 455_kHz_XTAL Resonator - OKI MSM5205 on Gladiator h/w + 500'000, // 500_kHz_XTAL Resonator - MIDI clock on various synthesizers (31250 * 16) + 512'000, // 512_kHz_XTAL Resonator - Toshiba TC8830F + 600'000, // 600_kHz_XTAL - + 640'000, // 640_kHz_XTAL Resonator - NEC UPD7759, Texas Instruments Speech Chips @ 8khz + 960'000, // 960_kHz_XTAL Resonator - Xerox Notetaker Keyboard UART + 1'000'000, // 1_MHz_XTAL Used to drive OKI M6295 chips + 1'008'000, // 1.008_MHz_XTAL Acorn Microcomputer (System 1) + 1'056'000, // 1.056_MHz_XTAL Resonator - OKI M6295 on Trio The Punch h/w + 1'294'400, // 1.2944_MHz_XTAL BBN BitGraph PSG + 1'600'000, // 1.6_MHz_XTAL Resonator - Roland TR-707 + 1'689'600, // 1.6896_MHz_XTAL Diablo 1355WP Printer + 1'750'000, // 1.75_MHz_XTAL RCA CDP1861 + 1'797'100, // 1.7971_MHz_XTAL SWTPC 6800 (with MIKBUG) + 1'843'200, // 1.8432_MHz_XTAL Bondwell 12/14 + 2'000'000, // 2_MHz_XTAL - + 2'012'160, // 2.01216_MHz_XTAL Cidelsa Draco sound board + 2'097'152, // 2.097152_MHz_XTAL Icatel 1995 - Brazilian public payphone + 2'250'000, // 2.25_MHz_XTAL Resonator - YM2154 on Yamaha PSR-60 & PSR-70 + 2'376'000, // 2.376_MHz_XTAL CIT-101 keyboard + 2'457'600, // 2.4576_MHz_XTAL Atari ST MFP + 2'470'000, // 2.47_MHz_XTAL CSA2.47MG ceramic oscillator - Casio CZ-1 + 2'500'000, // 2.5_MHz_XTAL Janken Man units + 2'600'000, // 2.6_MHz_XTAL Sharp PC-1500 + 2'700'000, // 2.7_MHz_XTAL Resonator - YM2154 on Yamaha RX15 + 2'950'000, // 2.95_MHz_XTAL Playmatic MPU-C, MPU-III & Sound-3 + 3'000'000, // 3_MHz_XTAL Probably only used to drive 68705 or similar MCUs on 80's Taito PCBs + 3'072'000, // 3.072_MHz_XTAL INS 8520 input clock rate + 3'120'000, // 3.12_MHz_XTAL SP0250 clock on Gottlieb games + 3'276'800, // 3.2768_MHz_XTAL SP0256 clock in Speech Synthesis for Dragon 32 + 3'300'000, // 3.3_MHz_XTAL LC 80 (export) + 3'521'280, // 3.52128_MHz_XTAL RCA COSMAC VIP + 3'546'800, // 3.5468_MHz_XTAL Atari 400 PAL + 3'546'894, // 3.546894_MHz_XTAL Atari 2600 PAL + 3'547'000, // 3.547_MHz_XTAL Philips G7200, Philips C7240 + 3'562'500, // 3.5625_MHz_XTAL Jopac JO7400 + 3'570'000, // 3.57_MHz_XTAL Telmac TMC-600 + 3'578'640, // 3.57864_MHz_XTAL Atari Portfolio PCD3311T + 3'579'000, // 3.579_MHz_XTAL BeebOPL + 3'579'545, // 3.579545_MHz_XTAL NTSC color subcarrier, extremely common, used on 100's of PCBs (Keytronic custom part #48-300-010 is equivalent) + 3'579'575, // 3.579575_MHz_XTAL Atari 2600 NTSC + 3'580'000, // 3.58_MHz_XTAL Resonator - Ritam Monty + 3'680'000, // 3.68_MHz_XTAL Resonator - Baud rate clock for the 6551 in the MTU-130 + 3'686'400, // 3.6864_MHz_XTAL Baud rate clock for MC68681 and similar UARTs + 3'840'000, // 3.84_MHz_XTAL Fairlight CMI Alphanumeric Keyboard + 3'900'000, // 3.9_MHz_XTAL Resonator - Used on some Fidelity boards + 3'932'160, // 3.93216_MHz_XTAL Apple Lisa COP421 (197-0016A) + 4'000'000, // 4_MHz_XTAL - + 4'032'000, // 4.032_MHz_XTAL GRiD Compass modem board + 4'096'000, // 4.096_MHz_XTAL Used to drive OKI M9810 chips + 4'194'304, // 4.194304_MHz_XTAL Used to drive MC146818 / Nintendo Game Boy + 4'220'000, // 4.220_MHz_XTAL Used to drive OKI M6295 chips on some Namco boards, usually with /4 divider + 4'224'000, // 4.224_MHz_XTAL Used to drive OKI M6295 chips, usually with /4 divider + 4'410'000, // 4.41_MHz_XTAL Pioneer PR-8210 ldplayer + 4'433'610, // 4.43361_MHz_XTAL Cidelsa Draco + 4'433'619, // 4.433619_MHz_XTAL PAL color subcarrier (technically 4.43361875mhz) + 4'608'000, // 4.608_MHz_XTAL Luxor ABC-77 keyboard (Keytronic custom part #48-300-107 is equivalent) + 4'915'200, // 4.9152_MHz_XTAL - + 4'946'800, // 4.9468_MHz_XTAL Casio CPS-2000 + 4'946'864, // 4.946864_MHz_XTAL Casiotone 8000 + 4'952'000, // 4.952_MHz_XTAL IGS M036 based mahjong games, for TT5665 sound chip + 5'000'000, // 5_MHz_XTAL Mutant Night + 5'068'800, // 5.0688_MHz_XTAL Usually used as MC2661 or COM8116 baud rate clock + 5'185'000, // 5.185_MHz_XTAL Intel INTELLEC® 4 + 5'370'000, // 5.37_MHz_XTAL HP 95LX + 5'460'000, // 5.46_MHz_XTAL ec1840 and ec1841 keyboard + 5'500'000, // 5.5_MHz_XTAL Yamaha PSS-480 + 5'529'600, // 5.5296_MHz_XTAL Kontron PSI98 keyboard + 5'626'000, // 5.626_MHz_XTAL RCA CDP1869 PAL dot clock + 5'659'200, // 5.6592_MHz_XTAL Digilog 320 dot clock + 5'670'000, // 5.67_MHz_XTAL RCA CDP1869 NTSC dot clock + 5'714'300, // 5.7143_MHz_XTAL Cidelsa Destroyer, TeleVideo serial keyboards + 5'856'000, // 5.856_MHz_XTAL HP 3478A Multimeter + 5'911'000, // 5.911_MHz_XTAL Philips Videopac Plus G7400 + 5'990'400, // 5.9904_MHz_XTAL Luxor ABC 800 keyboard (Keytronic custom part #48-300-008 is equivalent) + 6'000'000, // 6_MHz_XTAL American Poker II, Taito SJ System + 6'048'000, // 6.048_MHz_XTAL Hektor II + 6'144'000, // 6.144_MHz_XTAL Used on Alpha Denshi early 80's games sound board, Casio FP-200 and Namco Universal System 16 + 6'400'000, // 6.4_MHz_XTAL Textel Compact + 6'500'000, // 6.5_MHz_XTAL Jupiter Ace, Roland QDD interface + 6'880'000, // 6.88_MHz_XTAL Barcrest MPU4 + 6'900'000, // 6.9_MHz_XTAL BBN BitGraph CPU + 7'000'000, // 7_MHz_XTAL Jaleco Mega System PCBs + 7'056'000, // 7.056_MHz_XTAL Alesis QS FXCHIP (LCM of 44.1 kHz and 48 kHz) + 7'159'090, // 7.15909_MHz_XTAL Blood Bros (2x NTSC subcarrier) + 7'200'000, // 7.2_MHz_XTAL Novag Constellation (later models, with /2 divider), Kawai K1 keyscan IC + 7'372'800, // 7.3728_MHz_XTAL - + 7'680'000, // 7.68_MHz_XTAL Psion Series 3 + 7'864'300, // 7.8643_MHz_XTAL Used on InterFlip games as video clock + 7'987'000, // 7.987_MHz_XTAL PC9801-86 YM2608 clock + 7'995'500, // 7.9955_MHz_XTAL Used on Electronic Devices Italy Galaxy Gunners sound board + 8'000'000, // 8_MHz_XTAL Extremely common, used on 100's of PCBs + 8'053'000, // 8.053_MHz_XTAL Mad Motor + 8'200'000, // 8.2_MHz_XTAL Universal Mr. Do - Model 8021 PCB + 8'388'000, // 8.388_MHz_XTAL Nintendo Game Boy Color + 8'448'000, // 8.448_MHz_XTAL Banpresto's Note Chance - Used to drive OKI M6295 chips, usually with /8 divider + 8'467'200, // 8.4672_MHz_XTAL Subsino's Ying Hua Lian + 8'664'000, // 8.664_MHz_XTAL Touchmaster + 8'700'000, // 8.7_MHz_XTAL Tandberg TDV 2324 + 8'860'000, // 8.86_MHz_XTAL AlphaTantel + 8'867'000, // 8.867_MHz_XTAL Philips G7400 (~2x PAL subcarrier) + 8'867'236, // 8.867236_MHz_XTAL RCA CDP1869 PAL color clock (~2x PAL subcarrier) + 8'867'238, // 8.867238_MHz_XTAL ETI-660 (~2x PAL subcarrier) + 8'945'000, // 8.945_MHz_XTAL Hit Me + 8'960'000, // 8.96_MHz_XTAL Casio CZ-101 (divided by 2 for Music LSI) + 9'000'000, // 9_MHz_XTAL Homedata PCBs + 9'216'000, // 9.216_MHz_XTAL Univac UTS 20 + 9'263'750, // 9.263750_MHz_XTAL Sai Yu Gou Ma Roku bootleg + 9'400'000, // 9.4_MHz_XTAL Yamaha MU-5 and TG-100 + 9'426'500, // 9.4265_MHz_XTAL Yamaha DX7, and DX9 + 9'600'000, // 9.6_MHz_XTAL WD37C65 second clock (for 300 KB/sec rate) + 9'732'000, // 9.732_MHz_XTAL CTA Invader + 9'828'000, // 9.828_MHz_XTAL Universal PCBs + 9'830'400, // 9.8304_MHz_XTAL Epson PX-8 + 9'832'000, // 9.832_MHz_XTAL Robotron A7150 + 9'877'680, // 9.87768_MHz_XTAL Microterm 420 + 9'987'000, // 9.987_MHz_XTAL Crazy Balloon + 10'000'000, // 10_MHz_XTAL - + 10'137'600, // 10.1376_MHz_XTAL Wyse WY-100 + 10'240'000, // 10.240_MHz_XTAL Stella 8085 based fruit machines + 10'245'000, // 10.245_MHz_XTAL PES Speech box + 10'380'000, // 10.38_MHz_XTAL Fairlight Q219 Lightpen/Graphics Card + 10'480'000, // 10.48_MHz_XTAL System-80 (50 Hz) + 10'500'000, // 10.5_MHz_XTAL Agat-7 + 10'595'000, // 10.595_MHz_XTAL Mad Alien + 10'644'000, // 10.644_MHz_XTAL Northwest Digitial Systems GP-19 + 10'644'500, // 10.6445_MHz_XTAL TRS-80 Model I + 10'687'500, // 10.6875_MHz_XTAL BBC Bridge Companion + 10'694'250, // 10.69425_MHz_XTAL Xerox 820 + 10'717'200, // 10.7172_MHz_XTAL Eltec EurocomII + 10'730'000, // 10.73_MHz_XTAL Ruleta RE-900 VDP Clock + 10'733'000, // 10.733_MHz_XTAL The Fairyland Story + 10'738'000, // 10.738_MHz_XTAL Pokerout (poker+breakout) TMS9129 VDP Clock + 10'738'635, // 10.738635_MHz_XTAL TMS9918 family (3x NTSC subcarrier) + 10'816'000, // 10.816_MHz_XTAL Universal 1979-1980 (Cosmic Alien, etc) + 10'886'400, // 10.8864_MHz_XTAL Systel System 100 + 10'920'000, // 10.92_MHz_XTAL ADDS Viewpoint 60, Viewpoint A2 + 11'000'000, // 11_MHz_XTAL Mario I8039 sound + 11'004'000, // 11.004_MHz_XTAL TI 911 VDT + 11'055'000, // 11.055_MHz_XTAL Atari Tank 8 + 11'059'200, // 11.0592_MHz_XTAL Used with MCS-51 to generate common baud rates + 11'200'000, // 11.2_MHz_XTAL New York, New York + 11'289'000, // 11.289_MHz_XTAL Vanguard + 11'289'600, // 11.2896_MHz_XTAL Frantic Fred + 11'400'000, // 11.4_MHz_XTAL HP 9845 + 11'668'800, // 11.6688_MHz_XTAL Gameplan pixel clock + 11'730'000, // 11.73_MHz_XTAL Irem M-11 + 11'800'000, // 11.8_MHz_XTAL IBM PC Music Feature Card + 11'980'800, // 11.9808_MHz_XTAL Luxor ABC 80 + 12'000'000, // 12_MHz_XTAL Extremely common, used on 100's of PCBs + 12'057'600, // 12.0576_MHz_XTAL Poly 1 (38400 * 314) + 12'096'000, // 12.096_MHz_XTAL Some early 80's Atari games + 12'288'000, // 12.288_MHz_XTAL Sega Model 3 digital audio board + 12'292'000, // 12.292_MHz_XTAL Northwest Digitial Systems GP-19 + 12'324'000, // 12.324_MHz_XTAL Otrona Attache + 12'335'600, // 12.3356_MHz_XTAL RasterOps ColorBoard 264 (~784x NTSC line rate) + 12'472'500, // 12.4725_MHz_XTAL Bonanza's Mini Boy 7 + 12'480'000, // 12.48_MHz_XTAL TRS-80 Model II + 12'500'000, // 12.5_MHz_XTAL Red Alert audio board + 12'638'000, // 12.638_MHz_XTAL Exidy Sorcerer + 12'672'000, // 12.672_MHz_XTAL TRS-80 Model 4 80*24 video + 12'800'000, // 12.8_MHz_XTAL Cave CV1000 + 12'854'400, // 12.8544_MHz_XTAL Alphatronic P3 + 12'936'000, // 12.936_MHz_XTAL CDC 721 + 12'979'200, // 12.9792_MHz_XTAL Exidy 440 + 13'000'000, // 13_MHz_XTAL STM Pied Piper dot clock + 13'300'000, // 13.3_MHz_XTAL BMC bowling + 13'330'560, // 13.33056_MHz_XTAL Taito L + 13'333'000, // 13.333_MHz_XTAL Ojanko High School + 13'400'000, // 13.4_MHz_XTAL TNK3, Ikari Warriors h/w + 13'478'400, // 13.4784_MHz_XTAL TeleVideo 970 80-column display clock + 13'495'200, // 13.4952_MHz_XTAL Used on Shadow Force pcb and maybe other Technos pcbs? + 13'500'000, // 13.5_MHz_XTAL Microbee + 13'516'800, // 13.5168_MHz_XTAL Kontron KDT6 + 13'560'000, // 13.560_MHz_XTAL Tong Zi Maque + 13'608'000, // 13.608_MHz_XTAL TeleVideo 910 & 925 + 13'824'000, // 13.824_MHz_XTAL Robotron PC-1715 display circuit + 13'977'600, // 13.9776_MHz_XTAL Kaypro II dot clock + 14'000'000, // 14_MHz_XTAL - + 14'112'000, // 14.112_MHz_XTAL Timex/Sinclair TS2068 + 14'192'640, // 14.19264_MHz_XTAL Central Data 2650 + 14'218'000, // 14.218_MHz_XTAL Dragon + 14'250'450, // 14.25045_MHz_XTAL Apple II Europlus + 14'300'000, // 14.3_MHz_XTAL Agat-7 + 14'314'000, // 14.314_MHz_XTAL Taito TTL Board + 14'318'181, // 14.318181_MHz_XTAL Extremely common, used on 100's of PCBs (4x NTSC subcarrier) + 14'349'600, // 14.3496_MHz_XTAL Roland S-50 VDP + 14'469'000, // 14.469_MHz_XTAL Esprit Systems Executive 10/102 + 14'580'000, // 14.58_MHz_XTAL Fortune 32:16 Video Controller + 14'705'882, // 14.705882_MHz_XTAL Aleck64 + 14'728'000, // 14.728_MHz_XTAL ADM 36 + 14'742'800, // 14.7428_MHz_XTAL ADM 23 + 14'745'000, // 14.745_MHz_XTAL Synertek KTM-3 + 14'745'600, // 14.7456_MHz_XTAL Namco System 12 & System Super 22/23 for JVS + 14'746'000, // 14.746_MHz_XTAL Namco System 10 MGEXIO + 14'784'000, // 14.784_MHz_XTAL Zenith Z-29 + 14'916'000, // 14.916_MHz_XTAL ADDS Viewpoint 122 + 14'976'000, // 14.976_MHz_XTAL CIT-101 80-column display clock + 15'000'000, // 15_MHz_XTAL Sinclair QL, Amusco Poker + 15'148'800, // 15.1488_MHz_XTAL Zentec 9002/9003 + 15'206'400, // 15.2064_MHz_XTAL Falco TS-1 + 15'288'000, // 15.288_MHz_XTAL DEC VT220 80-column display clock + 15'300'720, // 15.30072_MHz_XTAL Microterm 420 + 15'360'000, // 15.36_MHz_XTAL Visual 1050 + 15'400'000, // 15.4_MHz_XTAL DVK KSM + 15'468'480, // 15.46848_MHz_XTAL Bank Panic h/w, Sega G80 + 15'582'000, // 15.582_MHz_XTAL Zentec Zephyr + 15'625'000, // 15.625_MHz_XTAL Zaccaria The Invaders + 15'667'200, // 15.6672_MHz_XTAL Apple Macintosh + 15'700'000, // 15.7_MHz_XTAL Motogonki + 15'741'000, // 15.741_MHz_XTAL DECmate II 80-column display clock + 15'897'600, // 15.8976_MHz_XTAL IAI Swyft + 15'920'000, // 15.92_MHz_XTAL HP Integral PC + 15'930'000, // 15.93_MHz_XTAL ADM 12 + 15'974'400, // 15.9744_MHz_XTAL Osborne 1 (9600 * 52 * 32) + 16'000'000, // 16_MHz_XTAL Extremely common, used on 100's of PCBs + 16'097'280, // 16.09728_MHz_XTAL DEC VT240 (1024 * 262 * 60) + 16'128'000, // 16.128_MHz_XTAL Fujitsu FM-7 + 16'200'000, // 16.2_MHz_XTAL Debut + 16'257'000, // 16.257_MHz_XTAL IBM PC MDA & EGA + 16'313'000, // 16.313_MHz_XTAL Micro-Term ERGO 201 + 16'364'000, // 16.364_MHz_XTAL Corvus Concept + 16'384'000, // 16.384_MHz_XTAL - + 16'400'000, // 16.4_MHz_XTAL MS 6102 + 16'537'000, // 16.537_MHz_XTAL Falco terminals 80-column clock + 16'572'000, // 16.572_MHz_XTAL Micro-Term ACT-5A + 16'588'800, // 16.5888_MHz_XTAL SM 7238 + 16'666'600, // 16.6666_MHz_XTAL Firebeat GCU + 16'667'000, // 16.667_MHz_XTAL Visual XDS-19P + 16'669'800, // 16.6698_MHz_XTAL Qume QVT-102 + 16'670'000, // 16.67_MHz_XTAL - + 16'777'216, // 16.777216_MHz_XTAL Nintendo Game Boy Advance + 16'934'400, // 16.9344_MHz_XTAL Usually used to drive 90's Yamaha OPL/FM chips (44100 * 384) + 16'960'000, // 16.960_MHz_XTAL Esprit Systems Executive 10/102 + 17'010'000, // 17.01_MHz_XTAL Epic 14E + 17'064'000, // 17.064_MHz_XTAL Memorex 1377 + 17'074'800, // 17.0748_MHz_XTAL SWTPC 8212 + 17'320'000, // 17.320_MHz_XTAL Visual 50 + 17'350'000, // 17.35_MHz_XTAL ITT Courier 1700 + 17'360'000, // 17.36_MHz_XTAL OMTI Series 10 SCSI controller + 17'430'000, // 17.43_MHz_XTAL Videx Videoterm + 17'550'000, // 17.55_MHz_XTAL HP 264x display clock (50 Hz configuration) + 17'600'000, // 17.6_MHz_XTAL LSI Octopus + 17'734'470, // 17.73447_MHz_XTAL 4x PAL subcarrier + 17'734'472, // 17.734472_MHz_XTAL 4x PAL subcarrier - All of these exist, exact 4x PAL is actually 17'734'475 + 17'734'475, // 17.734475_MHz_XTAL 4x PAL subcarrier - " + 17'734'476, // 17.734476_MHz_XTAL 4x PAL subcarrier - " + 17'812'000, // 17.812_MHz_XTAL Videopac C52 + 17'971'200, // 17.9712_MHz_XTAL Compucolor II, Hazeltine Esprit III + 18'000'000, // 18_MHz_XTAL S.A.R, Ikari Warriors 3 + 18'414'000, // 18.414_MHz_XTAL Ann Arbor Ambassador + 18'432'000, // 18.432_MHz_XTAL Extremely common, used on 100's of PCBs (48000 * 384) + 18'480'000, // 18.48_MHz_XTAL Wyse WY-100 video + 18'575'000, // 18.575_MHz_XTAL Visual 102, Visual 220 + 18'600'000, // 18.6_MHz_XTAL Teleray Model 10 + 18'720'000, // 18.72_MHz_XTAL Nokia MikroMikko 1 + 18'867'000, // 18.867_MHz_XTAL Decision Data IS-482 + 18'869'600, // 18.8696_MHz_XTAL Memorex 2178 + 19'170'000, // 19.17_MHz_XTAL Ericsson ISA8 Monochrome HR Graphics Board + 19'339'600, // 19.3396_MHz_XTAL TeleVideo TVI-955 80-column display clock + 19'584'000, // 19.584_MHz_XTAL ADM-42 + 19'600'000, // 19.6_MHz_XTAL Universal Mr. Do - Model 8021 PCB + 19'602'000, // 19.602_MHz_XTAL Ampex 210+ 80-column display clock + 19'660'800, // 19.6608_MHz_XTAL Euro League (bootleg), labeled as "UKI 19.6608 20PF" + 19'661'400, // 19.6614_MHz_XTAL Wyse WY-30 + 19'718'400, // 19.7184_MHz_XTAL Informer 207/100 + 19'923'000, // 19.923_MHz_XTAL Cinematronics vectors + 19'968'000, // 19.968_MHz_XTAL Used mostly by some Taito games + 20'000'000, // 20_MHz_XTAL - + 20'160'000, // 20.16_MHz_XTAL Nintendo 8080 + 20'275'200, // 20.2752_MHz_XTAL TRS-80 Model III + 20'282'000, // 20.282_MHz_XTAL Northwest Digitial Systems GP-19 + 20'375'040, // 20.37504_MHz_XTAL Apple Lisa dot clock (197-0019A) + 20'625'000, // 20.625_MHz_XTAL SM 7238 + 20'790'000, // 20.79_MHz_XTAL Blockade-hardware Gremlin games + 21'000'000, // 21_MHz_XTAL Lock-On pixel clock + 21'052'600, // 21.0526_MHz_XTAL NEC PC-98xx pixel clock + 21'060'000, // 21.06_MHz_XTAL HP 264x display clock (60 Hz configuration) + 21'254'400, // 21.2544_MHz_XTAL TeleVideo 970 132-column display clock + 21'281'370, // 21.28137_MHz_XTAL Radica Tetris (PAL) + 21'300'000, // 21.3_MHz_XTAL - + 21'328'100, // 21.3281_MHz_XTAL Philips NMS8245 + 21'477'272, // 21.477272_MHz_XTAL BMC bowling, some Data East 90's games, Vtech Socrates; (6x NTSC subcarrier) + 21'667'500, // 21.6675_MHz_XTAL AT&T 610 80-column display clock + 21'756'600, // 21.7566_MHz_XTAL Tab Products E-22 80-column display clock + 22'000'000, // 22_MHz_XTAL - + 22'032'000, // 22.032_MHz_XTAL Intellec Series II I/O controller + 22'096'000, // 22.096_MHz_XTAL ADDS Viewpoint 122 + 22'118'400, // 22.1184_MHz_XTAL Amusco Poker + 22'168'000, // 22.168_MHz_XTAL Sony HB-10P VDP (5x PAL subcarrier) + 22'248'000, // 22.248_MHz_XTAL Quantel DPB-7000 + 22'321'000, // 22.321_MHz_XTAL Apple LaserWriter II NT + 22'464'000, // 22.464_MHz_XTAL CIT-101 132-column display clock + 22'579'000, // 22.579_MHz_XTAL Sega System H1 SCSP clock + 22'579'200, // 22.5792_MHz_XTAL Enhanced Apple Digital Sound Chip clock (44100 * 512) + 22'656'000, // 22.656_MHz_XTAL Super Pinball Action (~1440x NTSC line rate) + 22'680'000, // 22.680_MHz_XTAL HDS200 80-columns display clock + 22'896'000, // 22.896_MHz_XTAL DEC VT220 132-column display clock + 23'200'000, // 23.2_MHz_XTAL Roland JV-80 & JV-880 PCM clock + 23'814'000, // 23.814_MHz_XTAL TeleVideo TVI-912, 920 & 950 + 23'961'600, // 23.9616_MHz_XTAL Osborne 4 (Vixen) + 24'000'000, // 24_MHz_XTAL Mario, 80's Data East games, 80's Konami games + 24'073'400, // 24.0734_MHz_XTAL DEC Rainbow 100 + 24'167'829, // 24.167829_MHz_XTAL Neo Geo AES rev. 3-3 and later (~1536x NTSC line rate) + 24'270'000, // 24.27_MHz_XTAL CIT-101XL + 24'300'000, // 24.3_MHz_XTAL ADM 36 132-column display clock + 24'576'000, // 24.576_MHz_XTAL Pole Position h/w, Model 3 CPU board + 24'883'200, // 24.8832_MHz_XTAL DEC VT100 + 25'000'000, // 25_MHz_XTAL Namco System 22, Taito GNET, Dogyuun h/w + 25'174'800, // 25.1748_MHz_XTAL Sega System 16A/16B (1600x NTSC line rate) + 25'175'000, // 25.175_MHz_XTAL IBM MCGA/VGA 320/640-pixel graphics + 25'200'000, // 25.2_MHz_XTAL Tektronix 4404 video clock + 25'398'360, // 25.39836_MHz_XTAL Tandberg TDV 2324 + 25'400'000, // 25.4_MHz_XTAL PC9801-86 PCM base clock + 25'447'000, // 25.447_MHz_XTAL Namco EVA3A (Funcube2) + 25'771'500, // 25.7715_MHz_XTAL HP-2622A + 25'920'000, // 25.92_MHz_XTAL ADDS Viewpoint 60 + 26'000'000, // 26_MHz_XTAL Gaelco PCBs + 26'195'000, // 26.195_MHz_XTAL Roland JD-800 + 26'366'000, // 26.366_MHz_XTAL DEC VT320 + 26'580'000, // 26.58_MHz_XTAL Wyse WY-60 80-column display clock + 26'590'906, // 26.590906_MHz_XTAL Atari Jaguar NTSC + 26'593'900, // 26.5939_MHz_XTAL Atari Jaguar PAL + 26'601'712, // 26.601712_MHz_XTAL Astro Corp.'s Show Hand, PAL Vtech/Yeno Socrates (6x PAL subcarrier) + 26'666'000, // 26.666_MHz_XTAL Imagetek I4220/I4300 + 26'666'666, // 26.666666_MHz_XTAL Irem M92 but most use 27MHz + 26'670'000, // 26.670_MHz_XTAL Namco EVA + 26'686'000, // 26.686_MHz_XTAL Typically used on 90's Taito PCBs to drive the custom chips + 26'800'000, // 26.8_MHz_XTAL SAA7110 TV decoder + 26'824'000, // 26.824_MHz_XTAL Astro Corp.'s Zoo + 26'880'000, // 26.88_MHz_XTAL Roland RF5C36/SA-16 clock (30000 * 896) + 26'989'200, // 26.9892_MHz_XTAL TeleVideo 965 + 27'000'000, // 27_MHz_XTAL Some Banpresto games macrossp, Irem M92 and 90's Toaplan games, Pinnacle Zoran based PCI cards + 27'164'000, // 27.164_MHz_XTAL Typically used on 90's Taito PCBs to drive the custom chips + 27'210'900, // 27.2109_MHz_XTAL LA Girl + 27'562'000, // 27.562_MHz_XTAL Visual 220 + 27'720'000, // 27.72_MHz_XTAL AT&T 610 132-column display clock + 27'956'000, // 27.956_MHz_XTAL CIT-101e 132-column display clock + 28'000'000, // 28_MHz_XTAL Sega System H1 SH2 clock, Kyukyoku Tiger / Twin Cobra + 28'224'000, // 28.224_MHz_XTAL Roland JD-800 + 28'322'000, // 28.322_MHz_XTAL Saitek RISC 2500, Mephisto Montreux + 28'375'160, // 28.37516_MHz_XTAL Amiga PAL systems + 28'432'000, // 28.432_MHz_XTAL Fuuki FG-3J MAIN-J PCB + 28'475'000, // 28.475_MHz_XTAL CoCo 3 PAL + 28'480'000, // 28.48_MHz_XTAL Chromatics CGC-7900 + 28'636'000, // 28.636_MHz_XTAL Super Kaneko Nova System + 28'636'363, // 28.636363_MHz_XTAL Later Leland games and Atari GT, Amiga NTSC, Raiden2 h/w (8x NTSC subcarrier), NEC PC-88xx + 28'640'000, // 28.64_MHz_XTAL Fuuki FG-1c AI AM-2 PCB + 28'700'000, // 28.7_MHz_XTAL - + 29'376'000, // 29.376_MHz_XTAL Qume QVT-103 + 29'491'200, // 29.4912_MHz_XTAL Xerox Alto-II system clock (tagged 29.4MHz in the schematics) + 30'000'000, // 30_MHz_XTAL Impera Magic Card + 30'209'800, // 30.2098_MHz_XTAL Philips CD-i NTSC (1920x NTSC line rate) + 30'240'000, // 30.24_MHz_XTAL Macintosh IIci RBV, 12- or 13-inch display + 30'476'180, // 30.47618_MHz_XTAL Taito F3, JC, Under Fire + 30'800'000, // 30.8_MHz_XTAL 15IE-00-013 + 31'279'500, // 31.2795_MHz_XTAL Wyse WY-30+ + 31'334'400, // 31.3344_MHz_XTAL Macintosh II + 31'684'000, // 31.684_MHz_XTAL TeleVideo TVI-955 132-column display clock + 31'948'800, // 31.9488_MHz_XTAL NEC PC-88xx, PC-98xx + 32'000'000, // 32_MHz_XTAL - + 32'147'000, // 32.147_MHz_XTAL Ampex 210+ 132-column display clock + 32'215'900, // 32.2159_MHz_XTAL Sega System 32, Sega Game Gear (close to 9x NTSC subcarrier which is 32.215905Mhz + 32'220'000, // 32.22_MHz_XTAL Typically used on 90's Data East PCBs + 32'223'800, // 32.2238_MHz_XTAL Sony SMC-777 (~2048x NTSC line rate) + 32'256'000, // 32.256_MHz_XTAL Hitachi MB-6890 + 32'317'400, // 32.3174_MHz_XTAL DEC VT330, VT340 + 32'530'470, // 32.53047_MHz_XTAL Seta 2 + 32'640'000, // 32.64_MHz_XTAL Vector 4 + 32'768'000, // 32.768_MHz_XTAL Roland D-50 audio clock + 33'000'000, // 33_MHz_XTAL Sega Model 3 video board + 33'264'000, // 33.264_MHz_XTAL Hazeltine 1500 terminal + 33'330'000, // 33.33_MHz_XTAL Sharp X68000 XVI + 33'333'000, // 33.333_MHz_XTAL Sega Model 3 CPU board, Vegas + 33'333'333, // 33.333333_MHz_XTAL Super Kaneko Nova System Sound clock with /2 divider + 33'833'000, // 33.833_MHz_XTAL - + 33'868'800, // 33.8688_MHz_XTAL Usually used to drive 90's Yamaha OPL/FM chips with /2 divider + 34'000'000, // 34_MHz_XTAL Gaelco PCBs + 34'291'712, // 34.291712_MHz_XTAL Fairlight CMI master card + 34'846'000, // 34.846_MHz_XTAL Visual 550 + 35'452'500, // 35.4525_MHz_XTAL Nokia MikroMikko 2 + 35'469'000, // 35.469_MHz_XTAL ZX Spectrum +2/+3 (~8x PAL subcarrier) + 35'640'000, // 35.640_MHz_XTAL HDS200 132-column display clock + 35'834'400, // 35.8344_MHz_XTAL Tab Products E-22 132-column display clock + 35'840'000, // 35.84_MHz_XTAL Akai MPC 60 voice PCB + 35'904'000, // 35.904_MHz_XTAL Used on HP98543 graphics board + 36'000'000, // 36_MHz_XTAL Sega Model 1 video board + 36'864'000, // 36.864_MHz_XTAL Unidesa Cirsa Rock 'n' Roll + 37'980'000, // 37.98_MHz_XTAL Falco 5220 + 38'769'220, // 38.76922_MHz_XTAL Namco System 21 video board + 38'863'630, // 38.86363_MHz_XTAL Sharp X68000 15.98kHz video + 39'321'600, // 39.3216_MHz_XTAL Sun 2/120 + 39'710'000, // 39.71_MHz_XTAL Wyse WY-60 132-column display clock + 40'000'000, // 40_MHz_XTAL - + 40'210'000, // 40.21_MHz_XTAL Fairlight CMI IIx + 41'539'000, // 41.539_MHz_XTAL IBM PS/2 132-column text mode + 42'000'000, // 42_MHz_XTAL BMC A-00211 - Popo Bear + 42'105'200, // 42.1052_MHz_XTAL NEC PC-88xx + 42'954'545, // 42.954545_MHz_XTAL CPS3 (12x NTSC subcarrier) + 43'320'000, // 43.32_MHz_XTAL DEC VT420 + 44'000'000, // 44_MHz_XTAL VGame slots + 44'100'000, // 44.1_MHz_XTAL Subsino's Bishou Jan + 44'236'800, // 44.2368_MHz_XTAL ReCo6502, Fortune 32:16 + 44'452'800, // 44.4528_MHz_XTAL TeleVideo 965 + 44'900'000, // 44.9_MHz_XTAL IBM 8514 1024x768 43.5Hz graphics + 45'000'000, // 45_MHz_XTAL Eolith with Hyperstone CPUs + 45'158'400, // 45.1584_MHz_XTAL Philips CD-i CDIC, Sega Model 2A video, Sega Model 3 CPU + 45'619'200, // 45.6192_MHz_XTAL DEC VK100 + 45'830'400, // 45.8304_MHz_XTAL Microterm 5510 + 46'615'120, // 46.61512_MHz_XTAL Soundblaster 16 PCM base clock + 47'736'000, // 47.736_MHz_XTAL Visual 100 + 48'000'000, // 48_MHz_XTAL Williams/Midway Y/Z-unit system / SSV board + 48'384'000, // 48.384_MHz_XTAL Namco NB-1 + 48'556'800, // 48.5568_MHz_XTAL Wyse WY-85 + 48'654'000, // 48.654_MHz_XTAL Qume QVT-201 + 48'660'000, // 48.66_MHz_XTAL Zaxxon + 48'940'000, // 48.94_MHz_XTAL Queen Bee New + 49'152'000, // 49.152_MHz_XTAL Used on some Namco PCBs, Baraduke h/w, System 21, Super System 22 + 49'423'500, // 49.4235_MHz_XTAL Wyse WY-185 + 50'000'000, // 50_MHz_XTAL Williams/Midway T/W/V-unit system + 50'113'000, // 50.113_MHz_XTAL Namco NA-1 (14x NTSC subcarrier) + 50'349'000, // 50.349_MHz_XTAL Sega System 18 (~3200x NTSC line rate) + 50'350'000, // 50.35_MHz_XTAL Sharp X68030 video + 51'200'000, // 51.2_MHz_XTAL Namco Super System 22 video clock + 52'000'000, // 52_MHz_XTAL Cojag + 52'832'000, // 52.832_MHz_XTAL Wang PC TIG video controller + 52'867'000, // 52.867_MHz_XTAL Atlus Print Club (Sega C2 PCB) + 53'203'424, // 53.203424_MHz_XTAL Master System, Mega Drive PAL (12x PAL subcarrier) + 53'693'175, // 53.693175_MHz_XTAL PSX-based h/w, Sony ZN1-2-based (15x NTSC subcarrier) + 54'000'000, // 54_MHz_XTAL Taito JC + 55'000'000, // 55_MHz_XTAL Eolith Vega + 56'000'000, // 56_MHz_XTAL ARM7500 based Belatra slot machines + 57'272'727, // 57.272727_MHz_XTAL Psikyo SH2 with /2 divider (16x NTSC subcarrier) + 57'283'200, // 57.2832_MHz_XTAL Macintosh IIci RBV, 15-inch portrait display + 58'000'000, // 58_MHz_XTAL Magic Reel (Play System) + 58'982'400, // 58.9824_MHz_XTAL Wyse WY-65 + 59'292'000, // 59.292_MHz_XTAL Data General D461 + 60'000'000, // 60_MHz_XTAL ARM610 + 61'440'000, // 61.44_MHz_XTAL Donkey Kong + 64'000'000, // 64_MHz_XTAL BattleToads + 64'108'800, // 64.1088_MHz_XTAL HP Topcat high-res + 66'000'000, // 66_MHz_XTAL - + 66'666'700, // 66.6667_MHz_XTAL Later Midway games + 67'737'600, // 67.7376_MHz_XTAL PSX-based h/w, Sony ZN1-2-based + 68'850'000, // 68.85_MHz_XTAL Wyse WY-50 + 69'196'800, // 69.1968_MHz_XTAL DEC VCB0x/VAXstation dot clock + 69'551'990, // 69.55199_MHz_XTAL Sharp X68000 31.5kHz video + 72'000'000, // 72_MHz_XTAL Aristocrat MKV + 72'576'000, // 72.576_MHz_XTAL Centipede, Millipede, Missile Command, Let's Go Bowling "Multipede" + 73'728'000, // 73.728_MHz_XTAL Ms. Pac-Man/Galaga 20th Anniversary + 75'000'000, // 75_MHz_XTAL Sony NEWS NWS-5000X + 77'414'400, // 77.4144_MHz_XTAL NCD17c + 80'000'000, // 80_MHz_XTAL ARM710 + 87'183'360, // 87.18336_MHz_XTAL AT&T 630 MTG + 92'940'500, // 92.9405_MHz_XTAL Sun cgthree + 96'000'000, // 96_MHz_XTAL Acorn A680 + 99'522'000, // 99.522_MHz_XTAL Radius Two Page Display + 100'000'000, // 100_MHz_XTAL PSX-based Namco System 12, Vegas, Sony ZN1-2-based + 101'491'200, // 101.4912_MHz_XTAL PSX-based Namco System 10 + 105'561'000, // 105.561_MHz_XTAL Sun cgsix + 108'108'000, // 108.108_MHz_XTAL HP 98550 high-res color card + 120'000'000, // 120_MHz_XTAL Astro Corp.'s Stone Age + 200'000'000 // 200_MHz_XTAL Base SH4 CPU (Naomi, Hikaru etc.) }; double XTAL::last_correct_value = -1; @@ -489,7 +597,7 @@ void XTAL::validate(const std::string &message) const fail(m_base_clock, message); } -void XTAL::fail(double base_clock, std::string message) +void XTAL::fail(double base_clock, const std::string &message) { std::string full_message = util::string_format("Unknown crystal value %.0f. ", base_clock); if(xtal_error_low && xtal_error_high) @@ -497,371 +605,5 @@ void XTAL::fail(double base_clock, std::string message) else full_message += util::string_format(" Did you mean %.0f?", xtal_error_low ? xtal_error_low : xtal_error_high); full_message += util::string_format(" Context: %s\n", message); - fatalerror("%s\n", full_message.c_str()); + fatalerror("%s\n", full_message); } - -/* - -For further reference: - -A search at http://search.digikey.com/scripts/DkSearch/dksus.dll?Cat=852333;keywords=cry -reveals the following shipping frequencies as of 1/1/2008: - -20kHz -25.600kHz -26.667kHz -28kHz - -30kHz -30.720kHz -30.76kHz -31.2kHz -31.25kHz -31.5kHz -32.000kHz -32.56kHz -32.768kHz -32.919kHz -34kHz -36kHz -38kHz -38.4kHz -39.500kHz - -40kHz -44.100kHz -46.604kHz -46.6084kHz - -50kHz -59.787kHz - -60.000kHz -60.002kHz -60.005kHz -65.535kHz -65.536kHz -69kHz - -70kHz -71kHz -72kHz -73kHz -74kHz -74.3kHz -74.4kHz -75kHz -76kHz -76.79kHz -76.8kHz -76.81kHz -77kHz -77.204kHz -77.287kHz -77.500kHz -77.503kHz -77.504kHz -78kHz -79kHz - -83kHz - -96kHz -96.006kHz - -100kHz -111kHz -117.72kHz -120kHz -120.8475kHz -125kHz -131.072kHz -149.475kHz -153.600kHz - -200kHz - -307.2kHz - -1.000MHz -1.8432MHz - -2.000MHz -2.048MHz -2.097152MHz -2.4576MHz -2.5MHz -2.560MHz -2.949120MHz - -3.000MHz -3.276MHz -3.2768MHz -3.579MHz -3.579545MHz -3.640MHz -3.6864MHz -3.700MHz -3.859MHz -3.93216MHz - -4.000MHz -4.032MHz -4.096MHz -4.09625MHz -4.194MHz -4.194304MHz -4.332MHz -4.433MHz -4.433616MHz -4.433618MHz -4.433619MHz -4.74687MHz -4.800MHz -4.8970MHz -4.90625MHz -4.915MHz -4.9152MHz - -5.000MHz -5.0688MHz -5.120MHz -5.223438MHz -5.5MHz -5.5296MHz -5.9904MHz - -6.000MHz -6.14MHz -6.144MHz -6.1760MHz -6.400 MHz -6.49830MHz -6.5MHz -6.5536MHz -6.612813MHz -6.7458MHz -6.757MHz -6.76438MHz - -7.1505MHz -7.15909 MHz -7.2MHz -7.3728MHz -7.68MHz -7.94888MHz - -8.000MHz -8.000156MHz -8.192MHz -8.388608MHz -8.432MHz -8.5MHz -8.6432MHz - -9.000MHz -9.216MHz -9.509375MHz -9.545MHz -9.6MHz -9.7941MHz -9.830MHz -9.8304MHz -9.84375MHz -9.8438MHz - -10.000MHz -10.240MHz -10.245MHz -10.6244MHz -10.738635MHz -10.73865MHz - -11.000MHz -11.046MHz -11.0592MHz -11.228MHz -11.2896MHz -11.520MHz -11.981350MHz - -12.000MHz -12.000393MHz -12.096MHz -12.1875MHz -12.288MHz -12.352MHz -12.500MHz -12.688MHz -12.800MHz -12.96MHz - -13.000MHz -13.0625MHz -13.225MHz -13.2256MHz -13.500MHz -13.5168MHz -13.56MHz -13.605MHz -13.824MHz -13.94916MHz - -14.00MHz -14.318MHz -14.31818MHz -14.3359MHz -14.3594MHz -14.4MHz -14.5MHz -14.69MHz -14.7456MHz -14.850MHz - -15MHz -15.360MHz - -16.000MHz -16.000312MHz -16.128MHz -16.257MHz -16.3676MHz -16.368MHz -16.384MHz -16.576MHz -16.6660MHz -16.667MHz -16.670MHz -16.800MHz -16.934MHz -16.9344MHz - -17.734475MHz - -18.000MHz -18.432MHz -18.869MHz - -19.200MHz -19.440MHz -19.660MHz -19.6608MHz -19.68MHz -19.800MHz - -20.000MHz -20.35625MHz -20.3563MHz -20.480MHz - -21.47727MHz - -22.000MHz -22.118MHz -22.1184MHz -22.400MHz -22.5MHz -22.5792MHz -22.6278MHz - -23MHz -23.2643MHz -23.5MHz -23.5122MHz -23.592MHz - -24.000MHz -24.00014MHz -24.5MHz -24.545454 MHz -24.5535MHz -24.576MHz -24.704MHz -24.7456MHz - -25.000MHz -25MHz -25.175MHz -25.2235MHz -25.4563MHz -25.5MHz - -26.000MHz -26.45125MHz -26.4513MHz -26.5MHz -26.5971MHz -26.800MHz - -27.000MHz -27.1344MHz -27.3067MHz -27.4688MHz - -28.000MHz -28.224MHz -28.259375MHz -28.2594MHz -28.322MHz -28.375MHz -28.5938MHz -28.636MHz -28.6363MHz -28.63636MHz - -29.4912MHz -29.498928MHz -29.500MHz - -30.000MHz -32.000MHz -32.514MHz -32.768MHz -33.000MHz -33.333MHz -33.3333MHz -33.8688MHz -35.2512MHz -35.3280MHz -36.000MHz -38.000MHz -38.00053MHz -38.400MHz -38.880MHz -39MHz - -40.000MHz -40.320MHz -40.960 MHz -42.000MHz -44.000MHz -44.2368MHz -44.545MHz -44.736MHz -44.800MHz -44.900MHz -45.000MHz -46.000MHz -48.000MHz -49.152MHz -49.86MHz - -50.000MHz -53.125MHz -55.000MHz - -60.000MHz -64.000MHz -66.000MHz -66.666MHz -66.6666MHz - -73.66979MHz -75.957292MHz -76.121875MHz - -80.000MHz - -100.00MHz - -*/ diff --git a/src/emu/xtal.h b/src/emu/xtal.h index f3e9b7d0cbd..b58f00943b2 100644 --- a/src/emu/xtal.h +++ b/src/emu/xtal.h @@ -69,7 +69,7 @@ private: static const double known_xtals[]; static double last_correct_value, xtal_error_low, xtal_error_high; - static void fail(double base_clock, std::string message); + static void fail(double base_clock, const std::string &message); static bool validate(double base_clock); static void check_ordering(); }; |