summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu')
-rw-r--r--src/emu/addrmap.cpp342
-rw-r--r--src/emu/addrmap.h273
-rw-r--r--src/emu/attotime.cpp43
-rw-r--r--src/emu/attotime.h78
-rw-r--r--src/emu/audio_effects/aeffect.cpp46
-rw-r--r--src/emu/audio_effects/aeffect.h43
-rw-r--r--src/emu/audio_effects/compressor.cpp28
-rw-r--r--src/emu/audio_effects/compressor.h24
-rw-r--r--src/emu/audio_effects/eq.cpp331
-rw-r--r--src/emu/audio_effects/eq.h84
-rw-r--r--src/emu/audio_effects/filter.cpp259
-rw-r--r--src/emu/audio_effects/filter.h78
-rw-r--r--src/emu/audio_effects/reverb.cpp28
-rw-r--r--src/emu/audio_effects/reverb.h24
-rw-r--r--src/emu/bookkeeping.cpp107
-rw-r--r--src/emu/bookkeeping.h13
-rw-r--r--src/emu/config.cpp345
-rw-r--r--src/emu/config.h81
-rw-r--r--src/emu/crsshair.cpp148
-rw-r--r--src/emu/crsshair.h4
-rw-r--r--src/emu/debug/debugbuf.cpp364
-rw-r--r--src/emu/debug/debugcmd.cpp3571
-rw-r--r--src/emu/debug/debugcmd.h220
-rw-r--r--src/emu/debug/debugcon.cpp1043
-rw-r--r--src/emu/debug/debugcon.h179
-rw-r--r--src/emu/debug/debugcpu.cpp2317
-rw-r--r--src/emu/debug/debugcpu.h333
-rw-r--r--src/emu/debug/debughlp.cpp1567
-rw-r--r--src/emu/debug/debughlp.h6
-rw-r--r--src/emu/debug/debugvw.cpp69
-rw-r--r--src/emu/debug/debugvw.h36
-rw-r--r--src/emu/debug/dvbpoints.cpp66
-rw-r--r--src/emu/debug/dvbpoints.h14
-rw-r--r--src/emu/debug/dvdisasm.cpp120
-rw-r--r--src/emu/debug/dvdisasm.h14
-rw-r--r--src/emu/debug/dvepoints.cpp314
-rw-r--r--src/emu/debug/dvepoints.h49
-rw-r--r--src/emu/debug/dvmemory.cpp655
-rw-r--r--src/emu/debug/dvmemory.h71
-rw-r--r--src/emu/debug/dvrpoints.cpp298
-rw-r--r--src/emu/debug/dvrpoints.h52
-rw-r--r--src/emu/debug/dvstate.cpp48
-rw-r--r--src/emu/debug/dvstate.h4
-rw-r--r--src/emu/debug/dvtext.cpp41
-rw-r--r--src/emu/debug/dvtext.h13
-rw-r--r--src/emu/debug/dvwpoints.cpp65
-rw-r--r--src/emu/debug/dvwpoints.h8
-rw-r--r--src/emu/debug/express.cpp1165
-rw-r--r--src/emu/debug/express.h137
-rw-r--r--src/emu/debug/points.cpp525
-rw-r--r--src/emu/debug/points.h186
-rw-r--r--src/emu/debug/textbuf.cpp332
-rw-r--r--src/emu/debug/textbuf.h83
-rw-r--r--src/emu/debugger.cpp7
-rw-r--r--src/emu/debugger.h1
-rw-r--r--src/emu/devcb.cpp117
-rw-r--r--src/emu/devcb.h1014
-rw-r--r--src/emu/devcpu.cpp77
-rw-r--r--src/emu/devcpu.h24
-rw-r--r--src/emu/devdelegate.cpp33
-rw-r--r--src/emu/devdelegate.h441
-rw-r--r--src/emu/devfind.cpp411
-rw-r--r--src/emu/devfind.h731
-rw-r--r--src/emu/device.cpp312
-rw-r--r--src/emu/device.h888
-rw-r--r--src/emu/device.ipp42
-rw-r--r--src/emu/didisasm.cpp6
-rw-r--r--src/emu/didisasm.h11
-rw-r--r--src/emu/diexec.cpp188
-rw-r--r--src/emu/diexec.h117
-rw-r--r--src/emu/digfx.cpp40
-rw-r--r--src/emu/digfx.h16
-rw-r--r--src/emu/diimage.cpp1118
-rw-r--r--src/emu/diimage.h363
-rw-r--r--src/emu/dimemory.cpp54
-rw-r--r--src/emu/dimemory.h60
-rw-r--r--src/emu/dinetwork.cpp102
-rw-r--r--src/emu/dinetwork.h45
-rw-r--r--src/emu/dinvram.cpp5
-rw-r--r--src/emu/dinvram.h21
-rw-r--r--src/emu/dipalette.cpp2
-rw-r--r--src/emu/dipalette.h18
-rw-r--r--src/emu/dipty.cpp11
-rw-r--r--src/emu/dipty.h12
-rw-r--r--src/emu/dirom.cpp285
-rw-r--r--src/emu/dirom.h50
-rw-r--r--src/emu/dirom.ipp150
-rw-r--r--src/emu/dirtc.cpp8
-rw-r--r--src/emu/dirtc.h7
-rw-r--r--src/emu/diserial.cpp134
-rw-r--r--src/emu/diserial.h24
-rw-r--r--src/emu/dislot.cpp107
-rw-r--r--src/emu/dislot.h162
-rw-r--r--src/emu/disound.cpp421
-rw-r--r--src/emu/disound.h73
-rw-r--r--src/emu/distate.cpp239
-rw-r--r--src/emu/distate.h186
-rw-r--r--src/emu/divideo.cpp6
-rw-r--r--src/emu/divideo.h2
-rw-r--r--src/emu/divtlb.cpp41
-rw-r--r--src/emu/divtlb.h57
-rw-r--r--src/emu/drawgfx.cpp113
-rw-r--r--src/emu/drawgfx.h7
-rw-r--r--src/emu/drawgfxt.ipp49
-rw-r--r--src/emu/drivenum.cpp13
-rw-r--r--src/emu/drivenum.h2
-rw-r--r--src/emu/driver.cpp103
-rw-r--r--src/emu/driver.h82
-rw-r--r--src/emu/drivers/empty.cpp10
-rw-r--r--src/emu/drivers/testcpu.cpp104
-rw-r--r--src/emu/emu.h21
-rw-r--r--src/emu/emualloc.cpp207
-rw-r--r--src/emu/emualloc.h146
-rw-r--r--src/emu/emucore.cpp79
-rw-r--r--src/emu/emucore.h211
-rw-r--r--src/emu/emufwd.h66
-rw-r--r--src/emu/emumem.cpp2535
-rw-r--r--src/emu/emumem.h2645
-rw-r--r--src/emu/emumem_aspace.cpp1283
-rw-r--r--src/emu/emumem_hea.h18
-rw-r--r--src/emu/emumem_hedp.cpp692
-rw-r--r--src/emu/emumem_hedp.h80
-rw-r--r--src/emu/emumem_hedr.h100
-rw-r--r--src/emu/emumem_hedr.ipp591
-rw-r--r--src/emu/emumem_hedr0.cpp276
-rw-r--r--src/emu/emumem_hedr1.cpp297
-rw-r--r--src/emu/emumem_hedr2.cpp297
-rw-r--r--src/emu/emumem_hedr3.cpp297
-rw-r--r--src/emu/emumem_hedw.h100
-rw-r--r--src/emu/emumem_hedw.ipp579
-rw-r--r--src/emu/emumem_hedw0.cpp278
-rw-r--r--src/emu/emumem_hedw1.cpp297
-rw-r--r--src/emu/emumem_hedw2.cpp297
-rw-r--r--src/emu/emumem_hedw3.cpp297
-rw-r--r--src/emu/emumem_hem.cpp294
-rw-r--r--src/emu/emumem_hem.h58
-rw-r--r--src/emu/emumem_hep.cpp86
-rw-r--r--src/emu/emumem_hep.h32
-rw-r--r--src/emu/emumem_het.cpp161
-rw-r--r--src/emu/emumem_het.h38
-rw-r--r--src/emu/emumem_heu.cpp320
-rw-r--r--src/emu/emumem_heu.h43
-rw-r--r--src/emu/emumem_heun.cpp294
-rw-r--r--src/emu/emumem_heun.h54
-rw-r--r--src/emu/emumem_hws.cpp311
-rw-r--r--src/emu/emumem_hws.h162
-rw-r--r--src/emu/emumem_mud.cpp42
-rw-r--r--src/emu/emumem_mud.h10
-rw-r--r--src/emu/emumem_mview.cpp1112
-rw-r--r--src/emu/emuopts.cpp455
-rw-r--r--src/emu/emuopts.h30
-rw-r--r--src/emu/emupal.cpp65
-rw-r--r--src/emu/emupal.h75
-rw-r--r--src/emu/fileio.cpp390
-rw-r--r--src/emu/fileio.h148
-rw-r--r--src/emu/gamedrv.h414
-rw-r--r--src/emu/hashfile.cpp14
-rw-r--r--src/emu/http.cpp69
-rw-r--r--src/emu/http.h20
-rw-r--r--src/emu/image.cpp206
-rw-r--r--src/emu/image.h7
-rw-r--r--src/emu/inpttype.h1303
-rw-r--r--src/emu/inpttype.ipp1152
-rw-r--r--src/emu/input.cpp1085
-rw-r--r--src/emu/input.h920
-rw-r--r--src/emu/inputdev.cpp361
-rw-r--r--src/emu/inputdev.h91
-rw-r--r--src/emu/ioport.cpp2201
-rw-r--r--src/emu/ioport.h973
-rw-r--r--src/emu/layout/LICENSE116
-rw-r--r--src/emu/layout/cyberlead.lay14
-rw-r--r--src/emu/layout/dualhovu.lay3
-rw-r--r--src/emu/layout/dualhsxs.lay3
-rw-r--r--src/emu/layout/dualhuov.lay3
-rw-r--r--src/emu/layout/ex800.lay15
-rw-r--r--src/emu/layout/exorterm155.lay77
-rw-r--r--src/emu/layout/generic.h38
-rw-r--r--src/emu/layout/hp9122c.lay8
-rw-r--r--src/emu/layout/ie15.lay88
-rw-r--r--src/emu/layout/lx800.lay41
-rw-r--r--src/emu/layout/monitors.lay41
-rw-r--r--src/emu/layout/nes_rob.lay64
-rw-r--r--src/emu/layout/noscreens.lay11
-rw-r--r--src/emu/layout/quadhsxs.lay3
-rw-r--r--src/emu/layout/qx10ascii.lay98
-rw-r--r--src/emu/layout/qx10hasci.lay86
-rw-r--r--src/emu/layout/smartboard.lay434
-rw-r--r--src/emu/layout/teletex800.lay167
-rw-r--r--src/emu/layout/triphsxs.lay3
-rw-r--r--src/emu/machine.cpp451
-rw-r--r--src/emu/machine.h91
-rw-r--r--src/emu/main.cpp23
-rw-r--r--src/emu/main.h73
-rw-r--r--src/emu/mconfig.cpp179
-rw-r--r--src/emu/mconfig.h102
-rw-r--r--src/emu/memarray.cpp72
-rw-r--r--src/emu/memarray.h8
-rw-r--r--src/emu/natkeyboard.cpp482
-rw-r--r--src/emu/natkeyboard.h58
-rw-r--r--src/emu/network.cpp39
-rw-r--r--src/emu/network.h2
-rw-r--r--src/emu/output.cpp157
-rw-r--r--src/emu/output.h75
-rw-r--r--src/emu/parameters.cpp2
-rw-r--r--src/emu/profiler.cpp14
-rw-r--r--src/emu/profiler.h114
-rw-r--r--src/emu/recording.cpp309
-rw-r--r--src/emu/recording.h89
-rw-r--r--src/emu/render.cpp1680
-rw-r--r--src/emu/render.h585
-rw-r--r--src/emu/rendersw.hxx1460
-rw-r--r--src/emu/rendertypes.h163
-rw-r--r--src/emu/rendfont.cpp377
-rw-r--r--src/emu/rendfont.h17
-rw-r--r--src/emu/rendlay.cpp3862
-rw-r--r--src/emu/rendlay.h672
-rw-r--r--src/emu/rendutil.cpp673
-rw-r--r--src/emu/rendutil.h109
-rw-r--r--src/emu/resampler.cpp439
-rw-r--r--src/emu/resampler.h66
-rw-r--r--src/emu/romentry.h23
-rw-r--r--src/emu/romload.cpp1464
-rw-r--r--src/emu/romload.h129
-rw-r--r--src/emu/save.cpp448
-rw-r--r--src/emu/save.h345
-rw-r--r--src/emu/schedule.cpp605
-rw-r--r--src/emu/schedule.h111
-rw-r--r--src/emu/screen.cpp641
-rw-r--r--src/emu/screen.h317
-rw-r--r--src/emu/softlist.cpp361
-rw-r--r--src/emu/softlist.h187
-rw-r--r--src/emu/softlist_dev.cpp298
-rw-r--r--src/emu/softlist_dev.h44
-rw-r--r--src/emu/sound.cpp3048
-rw-r--r--src/emu/sound.h715
-rw-r--r--src/emu/speaker.cpp154
-rw-r--r--src/emu/speaker.h121
-rw-r--r--src/emu/tilemap.cpp232
-rw-r--r--src/emu/tilemap.h76
-rw-r--r--src/emu/ui/cmddata.h333
-rw-r--r--src/emu/ui/uimain.h13
-rw-r--r--src/emu/uiinput.cpp288
-rw-r--r--src/emu/uiinput.h109
-rw-r--r--src/emu/validity.cpp1691
-rw-r--r--src/emu/validity.h58
-rw-r--r--src/emu/video.cpp810
-rw-r--r--src/emu/video.h95
-rw-r--r--src/emu/video/generic.cpp2
-rw-r--r--src/emu/video/resnet.cpp110
-rw-r--r--src/emu/video/resnet.h6
-rw-r--r--src/emu/video/rgbgen.cpp7
-rw-r--r--src/emu/video/rgbgen.h68
-rw-r--r--src/emu/video/rgbsse.cpp8
-rw-r--r--src/emu/video/rgbsse.h66
-rw-r--r--src/emu/video/rgbutil.h9
-rw-r--r--src/emu/video/rgbvmx.cpp4
-rw-r--r--src/emu/video/rgbvmx.h102
-rw-r--r--src/emu/xtal.cpp1198
-rw-r--r--src/emu/xtal.h2
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 &region : 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 &param, 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 &param, 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 &param, 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> &params)
+void debugger_commands::execute_help(const std::vector<std::string_view> &params)
{
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> &params)
+void debugger_commands::execute_print(const std::vector<std::string_view> &params)
{
/* 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> &params)
{
- 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> &params)
+template <typename T>
+void debugger_commands::execute_index_command(std::vector<std::string_view> const &params, 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 &param : index)
+ m_console.printf(unused_message, param);
+}
+
+
+/*-------------------------------------------------
+ execute_printf - execute the printf command
+-------------------------------------------------*/
+
+void debugger_commands::execute_printf(const std::vector<std::string_view> &params)
+{
/* 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> &params)
+void debugger_commands::execute_logerror(const std::vector<std::string_view> &params)
{
- /* 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> &params)
+void debugger_commands::execute_tracelog(const std::vector<std::string_view> &params)
{
- /* 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> &params)
+void debugger_commands::execute_tracesym(const std::vector<std::string_view> &params)
{
// 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> &params)
+{
+ 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> &params)
+void debugger_commands::execute_quit(const std::vector<std::string_view> &params)
{
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> &params)
+void debugger_commands::execute_do(const std::vector<std::string_view> &params)
{
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> &para
execute_step - execute the step command
-------------------------------------------------*/
-void debugger_commands::execute_step(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_step(const std::vector<std::string_view> &params)
{
/* 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> &params)
+void debugger_commands::execute_over(const std::vector<std::string_view> &params)
{
/* 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> &params)
+void debugger_commands::execute_out(const std::vector<std::string_view> &params)
{
- 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> &params)
+void debugger_commands::execute_go(const std::vector<std::string_view> &params)
{
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> &para
command
-------------------------------------------------*/
-void debugger_commands::execute_go_vblank(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_go_vblank(const std::vector<std::string_view> &params)
{
- 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> &params)
+void debugger_commands::execute_go_interrupt(const std::vector<std::string_view> &params)
{
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> &params)
+void debugger_commands::execute_go_exception(const std::vector<std::string_view> &params)
{
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> &params)
+void debugger_commands::execute_go_time(const std::vector<std::string_view> &params)
{
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> &params)
+void debugger_commands::execute_go_privilege(const std::vector<std::string_view> &params)
+{
+ 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> &params)
+{
+ 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> &params)
{
- 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> &params)
+void debugger_commands::execute_next(const std::vector<std::string_view> &params)
{
- 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> &params)
+void debugger_commands::execute_focus(const std::vector<std::string_view> &params)
{
- /* 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> &params)
+void debugger_commands::execute_ignore(const std::vector<std::string_view> &params)
{
- /* 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> &params)
+void debugger_commands::execute_observe(const std::vector<std::string_view> &params)
{
- /* 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> &params)
+void debugger_commands::execute_suspend(const std::vector<std::string_view> &params)
{
- /* 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> &params)
+void debugger_commands::execute_resume(const std::vector<std::string_view> &params)
{
- /* 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> &params)
+{
+ 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> &params)
+{
+ 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> &params)
+void debugger_commands::execute_comment_add(const std::vector<std::string_view> &params)
{
- 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> &params)
+void debugger_commands::execute_comment_del(const std::vector<std::string_view> &params)
{
- 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> &params)
* @brief Print current list of comments in debugger
*
*
*/
-void debugger_commands::execute_comment_list(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_comment_list(const std::vector<std::string_view> &params)
{
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> &params)
* @brief Add and Save current list of comments in debugger
*
*/
-void debugger_commands::execute_comment_commit(int ref, const std::vector<std::string> &params)
+void debugger_commands::execute_comment_commit(const std::vector<std::string_view> &params)
{
- 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> &params)
+void debugger_commands::execute_comment_save(const std::vector<std::string_view> &params)
{
- 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> &params)
* @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> &params)
+void debugger_commands::execute_bpset(const std::vector<std::string_view> &params)
{
- 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> &params)
+void debugger_commands::execute_bpclear(const std::vector<std::string_view> &params)
{
- 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> &params)
+void debugger_commands::execute_bpdisenable(bool enable, const std::vector<std::string_view> &params)
{
- 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> &params)
+void debugger_commands::execute_bplist(const std::vector<std::string_view> &params)
{
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> &params)
+void debugger_commands::execute_wpset(int spacenum, const std::vector<std::string_view> &params)
{
- 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> &params)
+void debugger_commands::execute_wpclear(const std::vector<std::string_view> &params)
{
- 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> &params)
+void debugger_commands::execute_wpdisenable(bool enable, const std::vector<std::string_view> &params)
{
- 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> &params)
+void debugger_commands::execute_wplist(const std::vector<std::string_view> &params)
{
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> &params)
+void debugger_commands::execute_rpset(const std::vector<std::string_view> &params)
{
+ // 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> &params)
+void debugger_commands::execute_rpclear(const std::vector<std::string_view> &params)
{
- 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> &params)
+void debugger_commands::execute_rpdisenable(bool enable, const std::vector<std::string_view> &params)
{
- 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> &params)
+{
+ // 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> &params)
+{
+ 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> &params)
+void debugger_commands::execute_epdisenable(bool enable, const std::vector<std::string_view> &params)
{
- 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> &params)
+{
+ 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> &params)
+void debugger_commands::execute_rplist(const std::vector<std::string_view> &params)
{
- /* 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> &params)
+void debugger_commands::execute_statesave(const std::vector<std::string_view> &params)
{
- 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> &params)
+void debugger_commands::execute_stateload(const std::vector<std::string_view> &params)
{
- 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> &params)
+void debugger_commands::execute_rewind(const std::vector<std::string_view> &params)
{
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> &params)
+void debugger_commands::execute_save(int spacenum, const std::vector<std::string_view> &params)
{
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> &params)
+{
+ 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> &params)
+void debugger_commands::execute_load(int spacenum, const std::vector<std::string_view> &params)
{
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> &params)
+{
+ 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> &params)
+void debugger_commands::execute_dump(int spacenum, const std::vector<std::string_view> &params)
{
- /* 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> &params)
+void debugger_commands::execute_strdump(int spacenum, const std::vector<std::string_view> &params)
{
- 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> &params)
+{
+ 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> &params)
+void debugger_commands::execute_cheatnext(bool initial, const std::vector<std::string_view> &params)
{
- 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> &params)
+void debugger_commands::execute_cheatlist(const std::vector<std::string_view> &params)
{
- 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> &params)
+void debugger_commands::execute_cheatundo(const std::vector<std::string_view> &params)
{
- 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> &params)
+void debugger_commands::execute_find(int spacenum, const std::vector<std::string_view> &params)
{
- 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> &params)
+{
+ 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> &params)
+void debugger_commands::execute_dasm(const std::vector<std::string_view> &params)
{
- 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> &params, bool trace_over)
+void debugger_commands::execute_trace(const std::vector<std::string_view> &params, 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> &params)
-{
- 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> &params)
-{
- 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> &params)
+void debugger_commands::execute_traceflush(const std::vector<std::string_view> &params)
{
- 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> &params)
+void debugger_commands::execute_history(const std::vector<std::string_view> &params)
{
- /* 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> &params)
+void debugger_commands::execute_trackpc(const std::vector<std::string_view> &params)
{
// 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> &params)
+void debugger_commands::execute_trackmem(const std::vector<std::string_view> &params)
{
// 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> &params)
+void debugger_commands::execute_pcatmem(int spacenum, const std::vector<std::string_view> &params)
{
- // 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> &params)
+void debugger_commands::execute_snap(const std::vector<std::string_view> &params)
{
/* 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> &params)
+void debugger_commands::execute_source(const std::vector<std::string_view> &params)
{
- 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> &params)
+void debugger_commands::execute_map(int spacenum, const std::vector<std::string_view> &params)
{
- 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> &params)
+void debugger_commands::execute_memdump(const std::vector<std::string_view> &params)
{
- 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> &params)
+void debugger_commands::execute_symlist(const std::vector<std::string_view> &params)
{
- 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> &params)
+void debugger_commands::execute_softreset(const std::vector<std::string_view> &params)
{
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> &params)
+void debugger_commands::execute_hardreset(const std::vector<std::string_view> &params)
{
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> &params)
+void debugger_commands::execute_images(const std::vector<std::string_view> &params)
{
- 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> &params)
+void debugger_commands::execute_mount(const std::vector<std::string_view> &params)
{
- 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> &params)
+void debugger_commands::execute_unmount(const std::vector<std::string_view> &params)
{
- 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> &params)
+void debugger_commands::execute_input(const std::vector<std::string_view> &params)
{
- 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> &params)
+void debugger_commands::execute_dumpkbd(const std::vector<std::string_view> &params)
{
// 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 &param, bool &result);
-
- /* validates a parameter as a numeric value */
- bool validate_number_parameter(const std::string &param, 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 &param, 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> &params, bool trace_over);
-
- void execute_help(int ref, const std::vector<std::string> &params);
- void execute_print(int ref, const std::vector<std::string> &params);
- void execute_printf(int ref, const std::vector<std::string> &params);
- void execute_logerror(int ref, const std::vector<std::string> &params);
- void execute_tracelog(int ref, const std::vector<std::string> &params);
- void execute_tracesym(int ref, const std::vector<std::string> &params);
- void execute_quit(int ref, const std::vector<std::string> &params);
- void execute_do(int ref, const std::vector<std::string> &params);
- void execute_step(int ref, const std::vector<std::string> &params);
- void execute_over(int ref, const std::vector<std::string> &params);
- void execute_out(int ref, const std::vector<std::string> &params);
- void execute_go(int ref, const std::vector<std::string> &params);
- void execute_go_vblank(int ref, const std::vector<std::string> &params);
- void execute_go_interrupt(int ref, const std::vector<std::string> &params);
- void execute_go_exception(int ref, const std::vector<std::string> &params);
- void execute_go_time(int ref, const std::vector<std::string> &params);
- void execute_go_privilege(int ref, const std::vector<std::string> &params);
- void execute_focus(int ref, const std::vector<std::string> &params);
- void execute_ignore(int ref, const std::vector<std::string> &params);
- void execute_observe(int ref, const std::vector<std::string> &params);
- void execute_suspend(int ref, const std::vector<std::string> &params);
- void execute_resume(int ref, const std::vector<std::string> &params);
- void execute_next(int ref, const std::vector<std::string> &params);
- void execute_comment_add(int ref, const std::vector<std::string> &params);
- void execute_comment_del(int ref, const std::vector<std::string> &params);
- void execute_comment_save(int ref, const std::vector<std::string> &params);
- void execute_comment_list(int ref, const std::vector<std::string> &params);
- void execute_comment_commit(int ref, const std::vector<std::string> &params);
- void execute_bpset(int ref, const std::vector<std::string> &params);
- void execute_bpclear(int ref, const std::vector<std::string> &params);
- void execute_bpdisenable(int ref, const std::vector<std::string> &params);
- void execute_bplist(int ref, const std::vector<std::string> &params);
- void execute_wpset(int ref, const std::vector<std::string> &params);
- void execute_wpclear(int ref, const std::vector<std::string> &params);
- void execute_wpdisenable(int ref, const std::vector<std::string> &params);
- void execute_wplist(int ref, const std::vector<std::string> &params);
- void execute_rpset(int ref, const std::vector<std::string> &params);
- void execute_rpclear(int ref, const std::vector<std::string> &params);
- void execute_rpdisenable(int ref, const std::vector<std::string> &params);
- void execute_rplist(int ref, const std::vector<std::string> &params);
- void execute_hotspot(int ref, const std::vector<std::string> &params);
- void execute_statesave(int ref, const std::vector<std::string> &params);
- void execute_stateload(int ref, const std::vector<std::string> &params);
- void execute_rewind(int ref, const std::vector<std::string> &params);
- void execute_save(int ref, const std::vector<std::string> &params);
- void execute_load(int ref, const std::vector<std::string> &params);
- void execute_dump(int ref, const std::vector<std::string> &params);
- void execute_cheatinit(int ref, const std::vector<std::string> &params);
- void execute_cheatnext(int ref, const std::vector<std::string> &params);
- void execute_cheatlist(int ref, const std::vector<std::string> &params);
- void execute_cheatundo(int ref, const std::vector<std::string> &params);
- void execute_dasm(int ref, const std::vector<std::string> &params);
- void execute_find(int ref, const std::vector<std::string> &params);
- void execute_trace(int ref, const std::vector<std::string> &params);
- void execute_traceover(int ref, const std::vector<std::string> &params);
- void execute_traceflush(int ref, const std::vector<std::string> &params);
- void execute_history(int ref, const std::vector<std::string> &params);
- void execute_trackpc(int ref, const std::vector<std::string> &params);
- void execute_trackmem(int ref, const std::vector<std::string> &params);
- void execute_pcatmem(int ref, const std::vector<std::string> &params);
- void execute_snap(int ref, const std::vector<std::string> &params);
- void execute_source(int ref, const std::vector<std::string> &params);
- void execute_map(int ref, const std::vector<std::string> &params);
- void execute_memdump(int ref, const std::vector<std::string> &params);
- void execute_symlist(int ref, const std::vector<std::string> &params);
- void execute_softreset(int ref, const std::vector<std::string> &params);
- void execute_hardreset(int ref, const std::vector<std::string> &params);
- void execute_images(int ref, const std::vector<std::string> &params);
- void execute_mount(int ref, const std::vector<std::string> &params);
- void execute_unmount(int ref, const std::vector<std::string> &params);
- void execute_input(int ref, const std::vector<std::string> &params);
- void execute_dumpkbd(int ref, const std::vector<std::string> &params);
+
+ 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> &params);
+ template <typename T>
+ void execute_index_command(std::vector<std::string_view> const &params, T &&apply, char const *unused_message);
+
+ void execute_help(const std::vector<std::string_view> &params);
+ void execute_print(const std::vector<std::string_view> &params);
+ void execute_printf(const std::vector<std::string_view> &params);
+ void execute_logerror(const std::vector<std::string_view> &params);
+ void execute_tracelog(const std::vector<std::string_view> &params);
+ void execute_tracesym(const std::vector<std::string_view> &params);
+ void execute_cls(const std::vector<std::string_view> &params);
+ void execute_quit(const std::vector<std::string_view> &params);
+ void execute_do(const std::vector<std::string_view> &params);
+ void execute_step(const std::vector<std::string_view> &params);
+ void execute_over(const std::vector<std::string_view> &params);
+ void execute_out(const std::vector<std::string_view> &params);
+ void execute_go(const std::vector<std::string_view> &params);
+ void execute_go_vblank(const std::vector<std::string_view> &params);
+ void execute_go_interrupt(const std::vector<std::string_view> &params);
+ void execute_go_exception(const std::vector<std::string_view> &params);
+ void execute_go_time(const std::vector<std::string_view> &params);
+ void execute_go_privilege(const std::vector<std::string_view> &params);
+ void execute_go_branch(bool sense, const std::vector<std::string_view> &params);
+ void execute_go_next_instruction(const std::vector<std::string_view> &params);
+ void execute_focus(const std::vector<std::string_view> &params);
+ void execute_ignore(const std::vector<std::string_view> &params);
+ void execute_observe(const std::vector<std::string_view> &params);
+ void execute_suspend(const std::vector<std::string_view> &params);
+ void execute_resume(const std::vector<std::string_view> &params);
+ void execute_next(const std::vector<std::string_view> &params);
+ void execute_cpulist(const std::vector<std::string_view> &params);
+ void execute_time(const std::vector<std::string_view> &params);
+ void execute_comment_add(const std::vector<std::string_view> &params);
+ void execute_comment_del(const std::vector<std::string_view> &params);
+ void execute_comment_save(const std::vector<std::string_view> &params);
+ void execute_comment_list(const std::vector<std::string_view> &params);
+ void execute_comment_commit(const std::vector<std::string_view> &params);
+ void execute_bpset(const std::vector<std::string_view> &params);
+ void execute_bpclear(const std::vector<std::string_view> &params);
+ void execute_bpdisenable(bool enable, const std::vector<std::string_view> &params);
+ void execute_bplist(const std::vector<std::string_view> &params);
+ void execute_wpset(int spacenum, const std::vector<std::string_view> &params);
+ void execute_wpclear(const std::vector<std::string_view> &params);
+ void execute_wpdisenable(bool enable, const std::vector<std::string_view> &params);
+ void execute_wplist(const std::vector<std::string_view> &params);
+ void execute_rpset(const std::vector<std::string_view> &params);
+ void execute_rpclear(const std::vector<std::string_view> &params);
+ void execute_rpdisenable(bool enable, const std::vector<std::string_view> &params);
+ void execute_rplist(const std::vector<std::string_view> &params);
+ void execute_epset(const std::vector<std::string_view> &params);
+ void execute_epclear(const std::vector<std::string_view> &params);
+ void execute_epdisenable(bool enable, const std::vector<std::string_view> &params);
+ void execute_eplist(const std::vector<std::string_view> &params);
+ void execute_statesave(const std::vector<std::string_view> &params);
+ void execute_stateload(const std::vector<std::string_view> &params);
+ void execute_rewind(const std::vector<std::string_view> &params);
+ void execute_save(int spacenum, const std::vector<std::string_view> &params);
+ void execute_saveregion(const std::vector<std::string_view> &params);
+ void execute_load(int spacenum, const std::vector<std::string_view> &params);
+ void execute_loadregion(const std::vector<std::string_view> &params);
+ void execute_dump(int spacenum, const std::vector<std::string_view> &params);
+ void execute_strdump(int spacenum, const std::vector<std::string_view> &params);
+ void execute_cheatrange(bool init, const std::vector<std::string_view> &params);
+ void execute_cheatnext(bool initial, const std::vector<std::string_view> &params);
+ void execute_cheatlist(const std::vector<std::string_view> &params);
+ void execute_cheatundo(const std::vector<std::string_view> &params);
+ void execute_dasm(const std::vector<std::string_view> &params);
+ void execute_find(int spacenum, const std::vector<std::string_view> &params);
+ void execute_fill(int spacenum, const std::vector<std::string_view> &params);
+ void execute_trace(const std::vector<std::string_view> &params, bool trace_over);
+ void execute_traceflush(const std::vector<std::string_view> &params);
+ void execute_history(const std::vector<std::string_view> &params);
+ void execute_trackpc(const std::vector<std::string_view> &params);
+ void execute_trackmem(const std::vector<std::string_view> &params);
+ void execute_pcatmem(int spacenum, const std::vector<std::string_view> &params);
+ void execute_snap(const std::vector<std::string_view> &params);
+ void execute_source(const std::vector<std::string_view> &params);
+ void execute_map(int spacenum, const std::vector<std::string_view> &params);
+ void execute_memdump(const std::vector<std::string_view> &params);
+ void execute_symlist(const std::vector<std::string_view> &params);
+ void execute_softreset(const std::vector<std::string_view> &params);
+ void execute_hardreset(const std::vector<std::string_view> &params);
+ void execute_images(const std::vector<std::string_view> &params);
+ void execute_mount(const std::vector<std::string_view> &params);
+ void execute_unmount(const std::vector<std::string_view> &params);
+ void execute_input(const std::vector<std::string_view> &params);
+ void execute_dumpkbd(const std::vector<std::string_view> &params);
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> &params)
+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> &params)
+{
+ 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> &params)
{
- 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 &param : 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(&param[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, &params[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 &regions = 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 &param) 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> &params);
+ void execute_help_custom(const std::vector<std::string_view> &params);
+ 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> &params);
+ 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 &param) 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> &registerpoint_list() const { return m_rplist; }
- int registerpoint_set(const char *condition, const char *action = nullptr);
+ const std::forward_list<debug_registerpoint> &registerpoint_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 &region)
- : 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 &region)
+ : 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 &region : 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 &region);
- 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 &region);
+ 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 &region : 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 &region : 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>> &regions() 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 &&region, 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 &&region) { 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=" &lt;&lt;"><color red="0.01" green="0.01" blue="0.01" /></text></element>
+ <element name="text_uiu2b"><text string=" &lt; "><color red="0.01" green="0.01" blue="0.01" /></text></element>
+ <element name="text_uiu2c"><text string=" &gt;"><color red="0.01" green="0.01" blue="0.01" /></text></element>
+ <element name="text_uiu2d"><text string=" &gt;&gt;"><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 &&notifier) 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 = &current_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 (&current_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 (&current_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 &current_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 &&notint, 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 &region,
+ 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 &region, 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 &region, 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 &region,
+ 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 &current_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(),
+ [&regiontag] (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 &&region) : 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 &region, const rom_entry *parent_region, const rom_entry *romp);
+ void fill_rom_data(memory_region &region, const rom_entry *romp);
+ void copy_rom_data(device_t &device, memory_region &region, const rom_entry *romp);
+ void process_rom_entries(
+ device_t &device,
+ const std::vector<std::string> &searchpath,
+ u8 bios,
+ memory_region &region,
+ 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 &timeslice_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 &timeslice_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 &ni;
+ }
+ // 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, &current_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, &current_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, &current_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, &current_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();
};