summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu')
-rw-r--r--src/emu/addrmap.h24
-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.cpp45
-rw-r--r--src/emu/bookkeeping.h11
-rw-r--r--src/emu/debug/debugcmd.cpp188
-rw-r--r--src/emu/debug/debugcmd.h5
-rw-r--r--src/emu/debug/debugcpu.cpp217
-rw-r--r--src/emu/debug/debugcpu.h7
-rw-r--r--src/emu/debug/debughlp.cpp82
-rw-r--r--src/emu/debug/debugvw.cpp4
-rw-r--r--src/emu/debug/debugvw.h3
-rw-r--r--src/emu/debug/dvbpoints.cpp3
-rw-r--r--src/emu/debug/dvbpoints.h1
-rw-r--r--src/emu/debug/dvdisasm.cpp47
-rw-r--r--src/emu/debug/dvdisasm.h8
-rw-r--r--src/emu/debug/dvepoints.cpp314
-rw-r--r--src/emu/debug/dvepoints.h49
-rw-r--r--src/emu/debug/dvmemory.cpp1
-rw-r--r--src/emu/debug/dvrpoints.cpp2
-rw-r--r--src/emu/debug/dvrpoints.h1
-rw-r--r--src/emu/debug/dvwpoints.cpp1
-rw-r--r--src/emu/debug/dvwpoints.h1
-rw-r--r--src/emu/debug/express.cpp2
-rw-r--r--src/emu/debug/points.cpp2
-rw-r--r--src/emu/debug/points.h1
-rw-r--r--src/emu/devcb.h12
-rw-r--r--src/emu/devcpu.cpp31
-rw-r--r--src/emu/devcpu.h4
-rw-r--r--src/emu/device.cpp11
-rw-r--r--src/emu/device.h68
-rw-r--r--src/emu/diexec.cpp72
-rw-r--r--src/emu/diexec.h15
-rw-r--r--src/emu/digfx.cpp38
-rw-r--r--src/emu/digfx.h14
-rw-r--r--src/emu/diimage.h10
-rw-r--r--src/emu/dinetwork.cpp77
-rw-r--r--src/emu/dinetwork.h32
-rw-r--r--src/emu/dirom.h2
-rw-r--r--src/emu/dirom.ipp6
-rw-r--r--src/emu/dirtc.h2
-rw-r--r--src/emu/diserial.cpp88
-rw-r--r--src/emu/diserial.h8
-rw-r--r--src/emu/disound.cpp458
-rw-r--r--src/emu/disound.h72
-rw-r--r--src/emu/drawgfx.cpp96
-rw-r--r--src/emu/drawgfx.h6
-rw-r--r--src/emu/drivenum.cpp6
-rw-r--r--src/emu/driver.h8
-rw-r--r--src/emu/drivers/testcpu.cpp96
-rw-r--r--src/emu/emucore.h2
-rw-r--r--src/emu/emufwd.h3
-rw-r--r--src/emu/emumem.h149
-rw-r--r--src/emu/emumem_aspace.cpp64
-rw-r--r--src/emu/emumem_hedr.h4
-rw-r--r--src/emu/emumem_hedr.ipp86
-rw-r--r--src/emu/emumem_hedw.h4
-rw-r--r--src/emu/emumem_hedw.ipp85
-rw-r--r--src/emu/emumem_mview.cpp123
-rw-r--r--src/emu/emupal.cpp22
-rw-r--r--src/emu/emupal.h8
-rw-r--r--src/emu/fileio.cpp8
-rw-r--r--src/emu/gamedrv.h87
-rw-r--r--src/emu/http.cpp26
-rw-r--r--src/emu/image.cpp24
-rw-r--r--src/emu/inpttype.h5
-rw-r--r--src/emu/inpttype.ipp23
-rw-r--r--src/emu/input.cpp102
-rw-r--r--src/emu/ioport.cpp418
-rw-r--r--src/emu/ioport.h200
-rw-r--r--src/emu/layout/cyberlead.lay14
-rw-r--r--src/emu/layout/nes_rob.lay1
-rw-r--r--src/emu/layout/smartboard.lay1
-rw-r--r--src/emu/layout/teletex800.lay167
-rw-r--r--src/emu/machine.cpp80
-rw-r--r--src/emu/main.h3
-rw-r--r--src/emu/mconfig.cpp2
-rw-r--r--src/emu/memarray.cpp4
-rw-r--r--src/emu/natkeyboard.cpp155
-rw-r--r--src/emu/natkeyboard.h5
-rw-r--r--src/emu/network.cpp9
-rw-r--r--src/emu/recording.cpp13
-rw-r--r--src/emu/recording.h3
-rw-r--r--src/emu/render.cpp467
-rw-r--r--src/emu/render.h55
-rw-r--r--src/emu/rendersw.hxx349
-rw-r--r--src/emu/rendfont.cpp79
-rw-r--r--src/emu/rendlay.cpp130
-rw-r--r--src/emu/rendlay.h50
-rw-r--r--src/emu/rendutil.cpp3
-rw-r--r--src/emu/resampler.cpp439
-rw-r--r--src/emu/resampler.h66
-rw-r--r--src/emu/romload.cpp11
-rw-r--r--src/emu/romload.h3
-rw-r--r--src/emu/save.cpp60
-rw-r--r--src/emu/save.h4
-rw-r--r--src/emu/schedule.cpp14
-rw-r--r--src/emu/schedule.h3
-rw-r--r--src/emu/screen.cpp35
-rw-r--r--src/emu/screen.h15
-rw-r--r--src/emu/softlist.cpp3
-rw-r--r--src/emu/softlist_dev.h2
-rw-r--r--src/emu/sound.cpp3352
-rw-r--r--src/emu/sound.h1083
-rw-r--r--src/emu/speaker.cpp235
-rw-r--r--src/emu/speaker.h131
-rw-r--r--src/emu/tilemap.cpp4
-rw-r--r--src/emu/tilemap.h2
-rw-r--r--src/emu/ui/uimain.h4
-rw-r--r--src/emu/uiinput.cpp262
-rw-r--r--src/emu/uiinput.h88
-rw-r--r--src/emu/validity.cpp64
-rw-r--r--src/emu/xtal.cpp1278
121 files changed, 8057 insertions, 5330 deletions
diff --git a/src/emu/addrmap.h b/src/emu/addrmap.h
index 7d350c14afd..25ff7d8cec5 100644
--- a/src/emu/addrmap.h
+++ b/src/emu/addrmap.h
@@ -157,6 +157,30 @@ public:
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; }
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 9f3e414e03f..47e4b2a195a 100644
--- a/src/emu/bookkeeping.cpp
+++ b/src/emu/bookkeeping.cpp
@@ -22,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;
@@ -53,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
-------------------------------------------------*/
@@ -64,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;
}
@@ -156,8 +166,8 @@ void bookkeeping_manager::coin_counter_w(int num, int on)
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;
}
@@ -177,10 +187,23 @@ int bookkeeping_manager::coin_counter_get_count(int 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 >= std::size(m_coinlockedout))
return;
diff --git a/src/emu/bookkeeping.h b/src/emu/bookkeeping.h
index 0d09c497b36..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,6 +62,7 @@ public:
// getters
running_machine &machine() const { return m_machine; }
+
private:
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);
diff --git a/src/emu/debug/debugcmd.cpp b/src/emu/debug/debugcmd.cpp
index 730565494a4..0b7efa44a93 100644
--- a/src/emu/debug/debugcmd.cpp
+++ b/src/emu/debug/debugcmd.cpp
@@ -469,10 +469,14 @@ void debugger_commands::execute_print(const std::vector<std::string_view> &param
mini_printf - safe printf to a buffer
-------------------------------------------------*/
-bool debugger_commands::mini_printf(std::ostream &stream, std::string_view format, int params, u64 *param)
+bool debugger_commands::mini_printf(std::ostream &stream, const std::vector<std::string_view> &params)
{
+ std::string_view const format(params[0]);
auto f = format.begin();
+ int param = 1;
+ u64 number;
+
// parse the string looking for % signs
while (f != format.end())
{
@@ -495,21 +499,48 @@ bool debugger_commands::mini_printf(std::ostream &stream, std::string_view forma
// formatting
else if (c == '%')
{
+ bool left_justify = false;
+ bool zero_fill = false;
int width = 0;
- int zerofill = 0;
+ int precision = 0;
- // parse out the width
- while (f != format.end() && *f >= '0' && *f <= '9')
+ // parse optional left justification flag
+ if (f != format.end() && *f == '-')
{
- c = *f++;
- if (c == '0' && width == 0)
- zerofill = 1;
- width = width * 10 + (c - '0');
+ left_justify = true;
+ f++;
+ }
+
+ // parse optional zero fill flag
+ if (f != format.end() && *f == '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 (f == format.end()) break;
- // get the format
- c = *f++;
switch (c)
{
case '%':
@@ -517,70 +548,89 @@ bool debugger_commands::mini_printf(std::ostream &stream, std::string_view forma
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 false;
}
- if (u32(*param >> 32) != 0)
- util::stream_format(stream, zerofill ? "%0*X" : "%*X", (width <= 8) ? 1 : width - 8, u32(*param >> 32));
- else if (width > 8)
- util::stream_format(stream, zerofill ? "%0*X" : "%*X", width - 8, 0);
- util::stream_format(stream, zerofill ? "%0*X" : "%*X", (width < 8) ? width : 8, u32(*param));
- param++;
- params--;
break;
case 'O':
case 'o':
- if (params == 0)
+ 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 >> 60) != 0)
- {
- util::stream_format(stream, zerofill ? "%0*o" : "%*o", (width <= 20) ? 1 : width - 20, u32(*param >> 60));
- util::stream_format(stream, "%0*o", 10, u32(BIT(*param, 30, 30)));
- }
- else
- {
- if (width > 20)
- util::stream_format(stream, zerofill ? "%0*o" : "%*o", width - 20, 0);
- if (u32(BIT(*param, 30, 30)) != 0)
- util::stream_format(stream, zerofill ? "%0*o" : "%*o", (width <= 10) ? 1 : width - 10, u32(BIT(*param, 30, 30)));
- else if (width > 10)
- util::stream_format(stream, zerofill ? "%0*o" : "%*o", width - 10, 0);
- }
- util::stream_format(stream, zerofill ? "%0*o" : "%*o", (width < 10) ? width : 10, u32(BIT(*param, 0, 30)));
- 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 false;
}
- util::stream_format(stream, 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 false;
}
- stream << 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;
}
}
@@ -630,15 +680,9 @@ void debugger_commands::execute_index_command(std::vector<std::string_view> cons
void debugger_commands::execute_printf(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 (!m_console.validate_number_parameter(params[i], values[i]))
- return;
-
/* then do a printf */
std::ostringstream buffer;
- if (mini_printf(buffer, params[0], params.size() - 1, &values[1]))
+ if (mini_printf(buffer, params))
m_console.printf("%s\n", std::move(buffer).str());
}
@@ -649,15 +693,9 @@ void debugger_commands::execute_printf(const std::vector<std::string_view> &para
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 (!m_console.validate_number_parameter(params[i], values[i]))
- return;
-
/* then do a printf */
std::ostringstream buffer;
- if (mini_printf(buffer, params[0], params.size() - 1, &values[1]))
+ if (mini_printf(buffer, params))
m_machine.logerror("%s", std::move(buffer).str());
}
@@ -668,15 +706,9 @@ void debugger_commands::execute_logerror(const std::vector<std::string_view> &pa
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 (!m_console.validate_number_parameter(params[i], values[i]))
- return;
-
/* then do a printf */
std::ostringstream buffer;
- if (mini_printf(buffer, params[0], params.size() - 1, &values[1]))
+ if (mini_printf(buffer, params))
m_console.get_visible_cpu()->debug()->trace_printf("%s", std::move(buffer).str());
}
@@ -688,12 +720,11 @@ void debugger_commands::execute_tracelog(const std::vector<std::string_view> &pa
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_console.visible_symtable().find(strmakelower(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]);
@@ -704,15 +735,18 @@ void debugger_commands::execute_tracesym(const std::vector<std::string_view> &pa
util::stream_format(format, "%s=%s ",
params[i],
sym->format().empty() ? "%16X" : sym->format());
-
- // validate the parameter
- if (!m_console.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
std::ostringstream buffer;
- if (mini_printf(buffer, format.str(), params.size(), values))
+ if (mini_printf(buffer, printf_params))
m_console.get_visible_cpu()->debug()->trace_printf("%s", std::move(buffer).str());
}
@@ -3483,16 +3517,16 @@ void debugger_commands::execute_trace(const std::vector<std::string_view> &param
using namespace std::literals;
if (!util::streqlower(filename, "off"sv))
{
- std::ios_base::openmode mode = std::ios_base::out;
+ std::ios_base::openmode mode;
// opening for append?
if ((filename[0] == '>') && (filename[1] == '>'))
{
- mode |= std::ios_base::ate;
+ mode = std::ios_base::in | std::ios_base::out | std::ios_base::ate;
filename = filename.substr(2);
}
else
- mode |= std::ios_base::trunc;
+ mode = std::ios_base::out | std::ios_base::trunc;
f = std::make_unique<std::ofstream>(filename.c_str(), mode);
if (f->fail())
diff --git a/src/emu/debug/debugcmd.h b/src/emu/debug/debugcmd.h
index 97d1a628ab5..cf38c6d903a 100644
--- a/src/emu/debug/debugcmd.h
+++ b/src/emu/debug/debugcmd.h
@@ -13,9 +13,6 @@
#pragma once
-#include "debugcpu.h"
-#include "debugcon.h"
-
#include <string_view>
@@ -73,7 +70,7 @@ private:
u64 global_get(global_entry *global);
void global_set(global_entry *global, u64 value);
- bool mini_printf(std::ostream &stream, std::string_view format, int params, u64 *param);
+ 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);
diff --git a/src/emu/debug/debugcpu.cpp b/src/emu/debug/debugcpu.cpp
index 5c5a186e2aa..968966b1b74 100644
--- a/src/emu/debug/debugcpu.cpp
+++ b/src/emu/debug/debugcpu.cpp
@@ -25,7 +25,6 @@
#include "uiinput.h"
#include "corestr.h"
-#include "coreutil.h"
#include "osdepend.h"
#include "xmlfile.h"
@@ -410,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
//**************************************************************************
@@ -436,6 +499,7 @@ 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()
@@ -639,6 +703,13 @@ void device_debug::stop_hook()
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))
{
@@ -773,6 +844,7 @@ void device_debug::privilege_hook()
}
}
+
//-------------------------------------------------
// instruction_hook - called by the CPU cores
// before executing each instruction
@@ -781,7 +853,7 @@ 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);
@@ -795,6 +867,11 @@ void device_debug::instruction_hook(offs_t curpc)
// 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)
@@ -866,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)
@@ -886,68 +963,68 @@ void device_debug::instruction_hook(offs_t curpc)
}
// 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();
+ debugcpu.wait_for_debugger(m_device);
- // reset any transient state
- debugcpu.reset_transient_flags();
- debugcpu.set_break_cpu(nullptr);
-
- // remember the last visible CPU in the debugger
- machine.debugger().console().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.debug_view().update_all(DVT_STATE);
- 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();
}
- machine.sound().debugger_mute(false);
-
- // remember the last visible CPU in the debugger
- machine.debugger().console().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 | 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());
+ // 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);
@@ -1727,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());
}
@@ -1754,7 +1831,7 @@ void device_debug::trace(std::unique_ptr<std::ostream> &&file, bool trace_over,
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;
@@ -1770,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_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
@@ -1780,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;
}
@@ -1860,7 +1941,7 @@ 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;
+ m_flags &= ~(DEBUG_FLAG_LIVE_BP | DEBUG_FLAG_LIVE_RP);
for (auto &bpp : m_bplist)
if (bpp.second->m_enabled)
{
@@ -1868,16 +1949,13 @@ void device_debug::breakpoint_update_flags()
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 (debug_registerpoint &rp : m_rplist)
+ if (rp.m_enabled)
{
- if (rp.m_enabled)
- {
- m_flags |= DEBUG_FLAG_LIVE_BP;
- break;
- }
+ m_flags |= DEBUG_FLAG_LIVE_RP;
+ break;
}
}
@@ -1894,8 +1972,6 @@ 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
auto bpitp = m_bplist.equal_range(pc);
for (auto bpit = bpitp.first; bpit != bpitp.second; ++bpit)
@@ -1903,6 +1979,8 @@ void device_debug::breakpoint_check(offs_t pc)
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();
@@ -1919,12 +1997,23 @@ 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 (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();
diff --git a/src/emu/debug/debugcpu.h b/src/emu/debug/debugcpu.h
index 9a9f6e69f70..631d4c788d9 100644
--- a/src/emu/debug/debugcpu.h
+++ b/src/emu/debug/debugcpu.h
@@ -55,6 +55,7 @@ public:
void exception_hook(int exception);
void privilege_hook();
void instruction_hook(offs_t curpc);
+ void wait_hook();
// debugger focus
void ignore(bool ignore = true);
@@ -174,6 +175,7 @@ private:
// breakpoint and watchpoint helpers
void breakpoint_update_flags();
void breakpoint_check(offs_t pc);
+ 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);
@@ -204,6 +206,7 @@ private:
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
@@ -328,7 +331,8 @@ 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
@@ -409,6 +413,7 @@ public:
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;
diff --git a/src/emu/debug/debughlp.cpp b/src/emu/debug/debughlp.cpp
index ee455bd84f8..8e5fa1b9c64 100644
--- a/src/emu/debug/debughlp.cpp
+++ b/src/emu/debug/debughlp.cpp
@@ -46,7 +46,7 @@ const help_item f_static_help_list[] =
" Breakpoints\n"
" Watchpoints\n"
" Registerpoints\n"
- " Exception Points\n"
+ " Exceptionpoints\n"
" Expressions\n"
" Comments\n"
" Cheats\n"
@@ -195,14 +195,14 @@ const help_item f_static_help_list[] =
{
"exceptionpoints",
"\n"
- "Exception Point Commands\n"
+ "Exceptionpoint Commands\n"
"Type help <command> for further details on each command\n"
"\n"
- " ep[set] <type>[,<condition>[,<action>]] -- sets exception point on <type>\n"
- " epclear [<epnum>] -- clears a given exception point or all if no <epnum> specified\n"
- " epdisable [<epnum>] -- disabled a given exception point or all if no <epnum> specified\n"
- " epenable [<epnum>] -- enables a given exception point or all if no <epnum> specified\n"
- " eplist -- lists all the exception points\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",
@@ -359,16 +359,20 @@ const help_item f_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"
@@ -379,18 +383,12 @@ const help_item f_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"
@@ -1594,12 +1592,12 @@ const help_item f_static_help_list[] =
"\n"
" ep[set] <type>[,<condition>[,<action>]]\n"
"\n"
- "Sets a new exception point for exceptions of type <type> on the currently visible CPU. "
+ "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 exception point is hit. If the result of the expression is true (non-zero), "
- "the exception point will actually halt execution at the start of the exception handler; "
+ "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 exception point is hit and the "
+ "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"
@@ -1608,8 +1606,8 @@ const help_item f_static_help_list[] =
"internally or externally vectored interrupts, errors occurring within instructions and "
"system calls.\n"
"\n"
- "Each exception point that is set is assigned an index which can be used in other "
- "exception point commands to reference this exception point.\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"
@@ -1622,57 +1620,57 @@ const help_item f_static_help_list[] =
"\n"
" epclear [<epnum>[,...]]\n"
"\n"
- "The epclear command clears exception points. If <epnum> is specified, only the requested "
- "exception points are cleared, otherwise all exception points are cleared.\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 exception point index 3.\n"
+ " Clear exceptionpoint index 3.\n"
"\n"
"epclear\n"
- " Clear all exception points.\n"
+ " Clear all exceptionpoints.\n"
},
{
"epdisable",
"\n"
" epdisable [<epnum>[,...]]\n"
"\n"
- "The epdisable command disables exception points. If <epnum> is specified, only the requested "
- "exception points are disabled, otherwise all exception points are disabled. Note that "
- "disabling an exception point does not delete it, it just temporarily marks the exception "
- "point as inactive.\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 exception point index 3.\n"
+ " Disable exceptionpoint index 3.\n"
"\n"
"epdisable\n"
- " Disable all exception points.\n"
+ " Disable all exceptionpoints.\n"
},
{
"epenable",
"\n"
" epenable [<epnum>[,...]]\n"
"\n"
- "The epenable command enables exception points. If <epnum> is specified, only the "
- "requested exception points are enabled, otherwise all exception points are enabled.\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 exception point index 3.\n"
+ " Enable exceptionpoint index 3.\n"
"\n"
"epenable\n"
- " Enable all exception points.\n"
+ " Enable all exceptionpoints.\n"
},
{
"eplist",
"\n"
" eplist\n"
"\n"
- "The eplist command lists all the current exception points, along with their index and "
+ "The eplist command lists all the current exceptionpoints, along with their index and "
"any conditions or actions attached to them.\n"
},
{
diff --git a/src/emu/debug/debugvw.cpp b/src/emu/debug/debugvw.cpp
index 54fe57ecf5a..7412ddd2f60 100644
--- a/src/emu/debug/debugvw.cpp
+++ b/src/emu/debug/debugvw.cpp
@@ -14,6 +14,7 @@
#include "debugcpu.h"
#include "dvbpoints.h"
#include "dvdisasm.h"
+#include "dvepoints.h"
#include "dvmemory.h"
#include "dvrpoints.h"
#include "dvstate.h"
@@ -369,6 +370,9 @@ debug_view *debug_view_manager::alloc_view(debug_view_type type, debug_view_osd_
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);
}
diff --git a/src/emu/debug/debugvw.h b/src/emu/debug/debugvw.h
index 73f93d14f32..64bd5dac10a 100644
--- a/src/emu/debug/debugvw.h
+++ b/src/emu/debug/debugvw.h
@@ -35,7 +35,8 @@ enum debug_view_type
DVT_LOG,
DVT_BREAK_POINTS,
DVT_WATCH_POINTS,
- DVT_REGISTER_POINTS
+ DVT_REGISTER_POINTS,
+ DVT_EXCEPTION_POINTS
};
diff --git a/src/emu/debug/dvbpoints.cpp b/src/emu/debug/dvbpoints.cpp
index d7be62c1eaa..0290f8719dc 100644
--- a/src/emu/debug/dvbpoints.cpp
+++ b/src/emu/debug/dvbpoints.cpp
@@ -9,8 +9,9 @@
***************************************************************************/
#include "emu.h"
-#include "debugger.h"
#include "dvbpoints.h"
+
+#include "debugcpu.h"
#include "points.h"
#include <algorithm>
diff --git a/src/emu/debug/dvbpoints.h b/src/emu/debug/dvbpoints.h
index 1bf365f521e..be291fa66d8 100644
--- a/src/emu/debug/dvbpoints.h
+++ b/src/emu/debug/dvbpoints.h
@@ -12,7 +12,6 @@
#pragma once
-#include "debugcpu.h"
#include "debugvw.h"
#include <vector>
diff --git a/src/emu/debug/dvdisasm.cpp b/src/emu/debug/dvdisasm.cpp
index 0dcc0b98982..17cc766ef94 100644
--- a/src/emu/debug/dvdisasm.cpp
+++ b/src/emu/debug/dvdisasm.cpp
@@ -9,10 +9,11 @@
***************************************************************************/
#include "emu.h"
-#include "debugvw.h"
#include "dvdisasm.h"
+
+#include "debugbuf.h"
#include "debugcpu.h"
-#include "debugger.h"
+
//**************************************************************************
// DEBUG VIEW DISASM SOURCE
@@ -394,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 };
@@ -427,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++)
@@ -460,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);
}
}
}
diff --git a/src/emu/debug/dvdisasm.h b/src/emu/debug/dvdisasm.h
index 0a16f95d5fe..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,6 +35,9 @@ 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
{
@@ -119,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 da792305c0d..7c10407822b 100644
--- a/src/emu/debug/dvmemory.cpp
+++ b/src/emu/debug/dvmemory.cpp
@@ -12,7 +12,6 @@
#include "dvmemory.h"
#include "debugcpu.h"
-#include "debugger.h"
#include <algorithm>
#include <cctype>
diff --git a/src/emu/debug/dvrpoints.cpp b/src/emu/debug/dvrpoints.cpp
index 7295a1b2170..a36f42bd1d0 100644
--- a/src/emu/debug/dvrpoints.cpp
+++ b/src/emu/debug/dvrpoints.cpp
@@ -11,7 +11,7 @@
#include "emu.h"
#include "dvrpoints.h"
-#include "debugger.h"
+#include "debugcpu.h"
#include "points.h"
#include <algorithm>
diff --git a/src/emu/debug/dvrpoints.h b/src/emu/debug/dvrpoints.h
index 307ccbc212e..31b821d7fae 100644
--- a/src/emu/debug/dvrpoints.h
+++ b/src/emu/debug/dvrpoints.h
@@ -12,7 +12,6 @@
#pragma once
-#include "debugcpu.h"
#include "debugvw.h"
#include <utility>
diff --git a/src/emu/debug/dvwpoints.cpp b/src/emu/debug/dvwpoints.cpp
index 21d3f30c5ca..116904232a1 100644
--- a/src/emu/debug/dvwpoints.cpp
+++ b/src/emu/debug/dvwpoints.cpp
@@ -11,6 +11,7 @@
#include "emu.h"
#include "dvwpoints.h"
+#include "debugcpu.h"
#include "points.h"
#include <algorithm>
diff --git a/src/emu/debug/dvwpoints.h b/src/emu/debug/dvwpoints.h
index 650a0aefad5..df1145742b6 100644
--- a/src/emu/debug/dvwpoints.h
+++ b/src/emu/debug/dvwpoints.h
@@ -12,7 +12,6 @@
#pragma once
-#include "debugcpu.h"
#include "debugvw.h"
diff --git a/src/emu/debug/express.cpp b/src/emu/debug/express.cpp
index 0d711d676d2..9ab75b7b655 100644
--- a/src/emu/debug/express.cpp
+++ b/src/emu/debug/express.cpp
@@ -1740,7 +1740,7 @@ void parsed_expression::infix_to_postfix()
else if (token->is_operator())
{
// normalize the operator based on neighbors
- normalize_operator(*token, prev, next != m_tokenlist.end() ? &*next : nullptr, stack, was_rparen);
+ 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.
diff --git a/src/emu/debug/points.cpp b/src/emu/debug/points.cpp
index 961488eebb2..12e4b614843 100644
--- a/src/emu/debug/points.cpp
+++ b/src/emu/debug/points.cpp
@@ -10,8 +10,10 @@
#include "emu.h"
#include "points.h"
+
#include "debugger.h"
#include "debugcon.h"
+#include "debugcpu.h"
//**************************************************************************
diff --git a/src/emu/debug/points.h b/src/emu/debug/points.h
index 42abd46398f..cf9e5bfc44d 100644
--- a/src/emu/debug/points.h
+++ b/src/emu/debug/points.h
@@ -13,7 +13,6 @@
#pragma once
-#include "debugcpu.h"
#include "express.h"
diff --git a/src/emu/devcb.h b/src/emu/devcb.h
index be62d8b6365..d2ee406aa60 100644
--- a/src/emu/devcb.h
+++ b/src/emu/devcb.h
@@ -756,6 +756,12 @@ private:
auto set_constant(Result val) { return set([val] () { return val; }); }
auto append_constant(Result val) { return append([val] () { return val; }); }
+ void remove()
+ {
+ set_used();
+ m_target.m_creators.clear();
+ }
+
private:
void set_used() { assert(!m_used); m_used = true; }
@@ -2152,6 +2158,12 @@ private:
m_target.m_creators.emplace_back(std::make_unique<nop_creator>());
}
+ void remove()
+ {
+ set_used();
+ m_target.m_creators.clear();
+ }
+
private:
void set_used() { assert(!m_used); m_used = true; }
diff --git a/src/emu/devcpu.cpp b/src/emu/devcpu.cpp
index efd12d682a9..616a501f438 100644
--- a/src/emu/devcpu.cpp
+++ b/src/emu/devcpu.cpp
@@ -21,15 +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),
- m_access_to_be_redone(false),
- m_access_before_delay_tag(nullptr)
+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)
{
}
@@ -63,9 +63,7 @@ bool cpu_device::access_before_time(u64 access_time, u64 current_time) noexcept
{
s32 delta = access_time - current_time;
if(*m_icountptr <= delta) {
- if(*m_icountptr > 0)
- *m_icountptr = 0;
- m_access_to_be_redone = true;
+ defer_access();
return true;
}
@@ -100,6 +98,13 @@ void cpu_device::access_after_delay(u32 cycles) noexcept
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;
- *m_icountptr = 0;
}
diff --git a/src/emu/devcpu.h b/src/emu/devcpu.h
index 70f7b1667a5..e4a37575858 100644
--- a/src/emu/devcpu.h
+++ b/src/emu/devcpu.h
@@ -50,7 +50,9 @@ public:
// 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
@@ -58,7 +60,7 @@ protected:
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
diff --git a/src/emu/device.cpp b/src/emu/device.cpp
index 1940c619455..272df837cd4 100644
--- a/src/emu/device.cpp
+++ b/src/emu/device.cpp
@@ -368,7 +368,7 @@ void device_t::set_unscaled_clock(u32 clock, bool sync_on_new_clock_domain)
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
@@ -393,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
@@ -544,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());
}
@@ -646,7 +645,7 @@ 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;
+ u32 const scaled_clock = m_unscaled_clock * m_clock_scale + 0.5;
if (m_clock != scaled_clock)
{
m_clock = scaled_clock;
diff --git a/src/emu/device.h b/src/emu/device.h
index 87736144292..4c2e1d8a180 100644
--- a/src/emu/device.h
+++ b/src/emu/device.h
@@ -63,6 +63,18 @@ template <typename T>
using is_device_interface = std::bool_constant<std::is_base_of_v<device_interface, T> && !is_device_implementation<T>::value>;
+struct device_flags
+{
+ enum type : u16
+ {
+ NOT_WORKING = u16(1) << 0,
+ SAVE_UNSUPPORTED = u16(1) << 1,
+
+ NONE = u16(0),
+ ALL = (u16(1) << 2) - 1U
+ };
+};
+
struct device_feature
{
enum type : u32
@@ -107,6 +119,7 @@ struct device_feature
};
};
+DECLARE_ENUM_BITWISE_OPERATORS(device_flags::type);
DECLARE_ENUM_BITWISE_OPERATORS(device_feature::type);
@@ -165,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,6 +213,7 @@ 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;
@@ -220,6 +229,7 @@ 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)
@@ -228,12 +238,13 @@ public:
}
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())
@@ -241,13 +252,14 @@ public:
{
}
- 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())
@@ -259,6 +271,7 @@ 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; }
@@ -306,22 +319,6 @@ 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
/// \{
@@ -404,7 +401,7 @@ constexpr auto driver_device_creator = &emu::detail::driver_tag_func<DriverClass
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, (Type##_device_traits::shortname), (Type##_device_traits::fullname), (Type##_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>;
@@ -440,7 +437,7 @@ constexpr auto driver_device_creator = &emu::detail::driver_tag_func<DriverClass
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, (Type##_device_traits::shortname), (Type##_device_traits::fullname), (Type##_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)>{ };
/// \}
@@ -565,9 +562,22 @@ 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
@@ -577,7 +587,7 @@ public:
/// displayed on starting a system.
/// \return Bitwise or of the feature constants for unemulated
/// features of the device.
- /// \sa imperfect_features
+ /// \sa emulation_flags imperfect_features
static constexpr feature_type unemulated_features() { return feature::NONE; }
/// \brief Report imperfectly emulated features
@@ -594,7 +604,7 @@ public:
/// 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 unemulated_features
+ /// \sa emulation_flags unemulated_features
static constexpr feature_type imperfect_features() { return feature::NONE; }
/// \brief Get parent device type for ROM search
diff --git a/src/emu/diexec.cpp b/src/emu/diexec.cpp
index 4b06edc36d5..1cd5266c518 100644
--- a/src/emu/diexec.cpp
+++ b/src/emu/diexec.cpp
@@ -70,8 +70,6 @@ device_execute_interface::device_execute_interface(const machine_config &mconfig
, 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;
@@ -100,12 +98,11 @@ void device_execute_interface::abort_timeslice() noexcept
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;
}
}
@@ -130,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;
@@ -145,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();
@@ -285,17 +284,6 @@ u32 device_execute_interface::execute_max_cycles() const noexcept
//-------------------------------------------------
-// execute_input_lines - return the total number
-// of input lines for the device
-//-------------------------------------------------
-
-u32 device_execute_interface::execute_input_lines() const noexcept
-{
- return 0;
-}
-
-
-//-------------------------------------------------
// execute_default_irq_vector - return the default
// IRQ vector when an acknowledge is processed
//-------------------------------------------------
@@ -318,18 +306,6 @@ bool device_execute_interface::execute_input_edge_triggered(int linenum) const n
//-------------------------------------------------
-// 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
//-------------------------------------------------
@@ -462,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));
@@ -537,7 +513,7 @@ int device_execute_interface::standard_irq_callback(int irqline, offs_t pc)
vector = m_driver_irq(device(), irqline);
// notify the debugger
- if (device().machine().debug_flags & DEBUG_FLAG_ENABLED)
+ if (debugger_enabled())
device().debug()->interrupt_hook(irqline, pc);
return vector;
@@ -608,21 +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
+ 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(irqline, ASSERT_LINE);
- set_input_line(irqline, CLEAR_LINE);
+ if (expiry.is_never() || (expiry <= m_scheduler->time()))
+ {
+ set_input_line(irqline, ASSERT_LINE);
+ set_input_line(irqline, CLEAR_LINE);
+ }
}
else
{
- set_input_line(irqline, ASSERT_LINE);
+ 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_pulse_end_timers[irqline]->adjust(target_time - m_scheduler->time(), irqline);
+ m_pulse_end_timers[irqline]->adjust(target_time - m_scheduler->time(), irqline);
+ }
}
}
@@ -681,7 +664,7 @@ 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
@@ -714,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++)
{
@@ -723,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 51973ad19cc..1e8303287e8 100644
--- a/src/emu/diexec.h
+++ b/src/emu/diexec.h
@@ -109,7 +109,6 @@ 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); }
@@ -164,7 +163,7 @@ public:
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_state(int linenum) const { assert(device().started()); return m_input[linenum].m_curstate; }
+ 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);
// suspend/resume
@@ -189,8 +188,7 @@ public:
// 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:
@@ -201,13 +199,11 @@ protected:
virtual u32 execute_max_cycles() const noexcept;
// input line information getters
- virtual u32 execute_input_lines() const noexcept;
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
@@ -219,7 +215,6 @@ protected:
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; }
int standard_irq_callback(int irqline, offs_t pc);
@@ -242,6 +237,12 @@ 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
diff --git a/src/emu/digfx.cpp b/src/emu/digfx.cpp
index c0b25c69b22..31f87216775 100644
--- a/src/emu/digfx.cpp
+++ b/src/emu/digfx.cpp
@@ -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);
+ 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 6ba323baafc..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,17 +186,18 @@ 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
diff --git a/src/emu/diimage.h b/src/emu/diimage.h
index 900a4410a05..aa9c991c955 100644
--- a/src/emu/diimage.h
+++ b/src/emu/diimage.h
@@ -148,15 +148,13 @@ public:
u32 fread(void *buffer, u32 length)
{
check_for_file();
- size_t actual;
- m_file->read(buffer, length, actual);
+ auto const [err, actual] = read(*m_file, buffer, length);
return actual;
}
u32 fwrite(const void *buffer, u32 length)
{
check_for_file();
- size_t actual;
- m_file->write(buffer, length, actual);
+ auto const [err, actual] = write(*m_file, buffer, length);
return actual;
}
std::error_condition fseek(s64 offset, int whence)
@@ -172,10 +170,6 @@ public:
return result;
}
- // allocate and read into buffers
- u32 fread(std::unique_ptr<u8 []> &ptr, u32 length) { ptr = std::make_unique<u8 []>(length); return fread(ptr.get(), length); }
- u32 fread(std::unique_ptr<u8 []> &ptr, u32 length, offs_t offset) { ptr = std::make_unique<u8 []>(length); return fread(ptr.get() + offset, length - offset); }
-
// 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; }
diff --git a/src/emu/dinetwork.cpp b/src/emu/dinetwork.cpp
index 7333fd25de8..30e10361c97 100644
--- a/src/emu/dinetwork.cpp
+++ b/src/emu/dinetwork.cpp
@@ -3,16 +3,21 @@
#include "emu.h"
#include "dinetwork.h"
-#include "osdnet.h"
+
+#include "osdepend.h"
+
+#include <algorithm>
+
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;
// Convert to Mibps to Bps
m_bandwidth = bandwidth << (20 - 3);
m_mtu = mtu;
- memset(m_mac, 0, 6);
m_intf = -1;
m_loopback_control = false;
}
@@ -21,15 +26,23 @@ 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 fcs)
@@ -65,6 +78,25 @@ int device_network_interface::send(u8 *buf, int len, int fcs)
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);
@@ -85,7 +117,7 @@ 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), result);
@@ -98,28 +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 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();
- // Set device polling time to transfer time for one mtu
- m_dev.reset(open_netdev(id, this, (int)(m_bandwidth / m_mtu)));
- 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;
}
@@ -136,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 6dd062c16aa..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, 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_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 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,19 +32,25 @@ 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];
// bandwidth in bytes per second
u32 m_bandwidth;
// maximum transmission unit, used for device polling time
u32 m_mtu;
- std::unique_ptr<osd_netdev> m_dev;
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;
};
diff --git a/src/emu/dirom.h b/src/emu/dirom.h
index 1173a1b5d35..72d844f8b0e 100644
--- a/src/emu/dirom.h
+++ b/src/emu/dirom.h
@@ -22,6 +22,7 @@ public:
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); }
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); }
@@ -40,6 +41,7 @@ protected:
private:
optional_memory_region m_rom_region;
+ optional_address_space m_rom_space;
address_space_config m_rom_config;
typename memory_access<AddrWidth, DataWidth, AddrShift, Endian>::cache m_rom_cache;
diff --git a/src/emu/dirom.ipp b/src/emu/dirom.ipp
index 28951ae50aa..d41da70a618 100644
--- a/src/emu/dirom.ipp
+++ b/src/emu/dirom.ipp
@@ -9,6 +9,7 @@ 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)
@@ -109,6 +110,11 @@ void device_rom_interface<AddrWidth, DataWidth, AddrShift, Endian>::interface_pr
{
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;
diff --git a/src/emu/dirtc.h b/src/emu/dirtc.h
index e741cfc2628..e76a8d3dba3 100644
--- a/src/emu/dirtc.h
+++ b/src/emu/dirtc.h
@@ -51,7 +51,7 @@ public:
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);
- virtual void set_current_time(const system_time &systime);
+ void set_current_time(const system_time &systime);
bool has_battery() const { return rtc_battery_backed(); }
diff --git a/src/emu/diserial.cpp b/src/emu/diserial.cpp
index 5f7c00b7e59..2d900f27459 100644
--- a/src/emu/diserial.cpp
+++ b/src/emu/diserial.cpp
@@ -25,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),
@@ -44,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()
@@ -81,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));
@@ -174,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)
{
@@ -226,8 +218,10 @@ void device_serial_interface::rx_w(int state)
{
LOGMASKED(LOG_RX, "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);
+ {
+ // 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--;
}
@@ -272,6 +266,10 @@ 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)
@@ -279,7 +277,7 @@ void device_serial_interface::receive_register_update_bit(int bit)
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;
@@ -304,13 +302,13 @@ 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)
@@ -323,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;
@@ -356,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++;
}
@@ -366,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;
@@ -377,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;
@@ -422,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);
}
diff --git a/src/emu/diserial.h b/src/emu/diserial.h
index aab03940206..eac3599e68a 100644
--- a/src/emu/diserial.h
+++ b/src/emu/diserial.h
@@ -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 */
diff --git a/src/emu/disound.cpp b/src/emu/disound.cpp
index 83d2c2bf084..57fd6a9c030 100644
--- a/src/emu/disound.cpp
+++ b/src/emu/disound.cpp
@@ -23,9 +23,11 @@
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),
- m_specified_inputs_mask(0)
+ 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)
{
}
@@ -43,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;
}
@@ -71,45 +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)
-{
- return device().machine().sound().stream_alloc(*this, inputs, outputs, sample_rate, stream_update_delegate(&device_sound_interface::sound_stream_update, this), STREAM_DEFAULT_FLAGS);
-}
-
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, stream_update_delegate(&device_sound_interface::sound_stream_update, this), flags);
+ 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 : device().machine().sound().streams())
- if (&stream->device() == &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 : device().machine().sound().streams())
- if (&stream->device() == &device())
- outputs += stream->output_count();
+ for(auto *stream : m_sound_streams)
+ outputs += stream->output_count();
return outputs;
}
@@ -120,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 : device().machine().sound().streams())
- if (&stream->device() == &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());
}
@@ -147,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 : 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());
}
@@ -175,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(stream_inputnum).gain() : 0.0f;
+ auto [stream, input] = input_to_stream_input(inputnum);
+ return stream->input_gain(input);
}
@@ -188,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(stream_outputnum).gain() : 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);
}
@@ -201,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->input(stream_inputnum).set_gain(gain);
+ auto [stream, input] = input_to_stream_input(inputnum);
+ stream->set_input_gain(input, gain);
}
@@ -216,45 +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 : device().machine().sound().streams())
- if (&stream->device() == &device())
- for (int num = 0; num < stream->output_count(); num++)
- stream->output(num).set_gain(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->output(stream_outputnum).set_gain(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 : device().machine().sound().streams())
- if (&stream->device() == &device())
- for (int inputnum = 0; inputnum < stream->input_count(); inputnum++, overall++)
- {
- auto &input = stream->input(inputnum);
- if (input.valid() && &input.source().stream().device() == &source_device && input.source().index() == 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
@@ -264,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);
- if (!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());
}
}
@@ -284,240 +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_enumerator iter(device().machine().root_device());
- for (device_sound_interface const &sound : iter)
- {
- // scan each route on the device
- for (sound_route const &route : sound.routes())
- {
- device_t *const target_device = route.m_base.get().subdevice(route.m_target);
- if (target_device == &device())
- {
- // see if we are the target of this route; if we are, make sure the source device is started
- if (!sound.device().started())
- throw device_missing_dependencies();
- if (route.m_input != AUTO_ALLOC_INPUT)
- m_specified_inputs_mask |= 1 << route.m_input;
- }
- }
- }
-
- // 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);
- if (target_device == &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_enumerator(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);
- if (target_device == &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, 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()
-{
- // update all streams on this device prior to reset
- for (auto &stream : device().machine().sound().streams())
- if (&stream->device() == &device())
- stream->update();
-}
-
-
-//-------------------------------------------------
-// sound_stream_update - default implementation
-// that should be overridden
-//-------------------------------------------------
-
-void device_sound_interface::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
+void device_sound_interface::sound_request_input(u32 input)
{
- throw emu_fatalerror("sound_stream_update called but not overridden by owning class");
+ 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_enumerator(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);
- 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;
- }
- }
- }
-
- // keep a small buffer handy for tracking cleared buffers
- m_output_clear.resize(m_outputs);
-
- // allocate the mixer stream
- m_mixer_stream = stream_alloc(m_auto_allocated_inputs, m_outputs, device().machine().sample_rate(), STREAM_DEFAULT_FLAGS);
-}
-
-
-//-------------------------------------------------
-// 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()
-{
- // mixer stream could be null if no inputs were specified
- if (m_mixer_stream != nullptr)
- 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);
}
-
-//-------------------------------------------------
-// sound_stream_update - mix all inputs to one
-// output
-//-------------------------------------------------
-
-void device_mixer_interface::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
+void device_mixer_interface::sound_stream_update(sound_stream &stream)
{
- // special case: single input, single output, same rate
- if (inputs.size() == 1 && outputs.size() == 1 && inputs[0].sample_rate() == outputs[0].sample_rate())
- {
- outputs[0] = inputs[0];
- return;
- }
-
- // reset the clear flags
- std::fill(std::begin(m_output_clear), std::end(m_output_clear), false);
-
- // loop over inputs
- for (int inputnum = 0; inputnum < m_auto_allocated_inputs; inputnum++)
- {
- // skip if the gain is 0
- auto &input = inputs[inputnum];
- if (input.gain() == 0)
- continue;
-
- // either store or accumulate
- int outputnum = m_outputmap[inputnum];
- auto &output = outputs[outputnum];
- if (!m_output_clear[outputnum])
- output.copy(input);
- else
- output.add(input);
-
- m_output_clear[outputnum] = true;
- }
-
- // clear anything unused
- for (int outputnum = 0; outputnum < m_outputs; outputnum++)
- if (!m_output_clear[outputnum])
- outputs[outputnum].fill(0);
+ stream.copy(0, 0);
}
diff --git a/src/emu/disound.h b/src/emu/disound.h
index e48ba290bc6..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,8 +33,6 @@ constexpr int AUTO_ALLOC_INPUT = 65535;
// TYPE DEFINITIONS
//**************************************************************************
-class read_stream_view;
-class write_stream_view;
enum sound_stream_flags : u32;
@@ -43,73 +40,89 @@ enum sound_stream_flags : u32;
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, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs);
+ 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 *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
- u32 m_specified_inputs_mask; // mask of inputs explicitly specified (not counting auto-allocated)
+ 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
@@ -123,22 +136,15 @@ 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, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
-
- // internal state
- u8 m_outputs; // number of outputs
- std::vector<u8> m_outputmap; // map of inputs to outputs
- std::vector<bool> m_output_clear; // flag for tracking cleared buffers
- sound_stream *m_mixer_stream; // mixing stream
+ virtual void sound_stream_update(sound_stream &stream) override;
};
// iterator
diff --git a/src/emu/drawgfx.cpp b/src/emu/drawgfx.cpp
index 8a63d71467c..d7648483f21 100644
--- a/src/emu/drawgfx.cpp
+++ b/src/emu/drawgfx.cpp
@@ -66,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);
diff --git a/src/emu/drawgfx.h b/src/emu/drawgfx.h
index 4b5be8a4fa3..d28024bb442 100644
--- a/src/emu/drawgfx.h
+++ b/src/emu/drawgfx.h
@@ -538,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);
@@ -547,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/drivenum.cpp b/src/emu/drivenum.cpp
index 3db8a3334d5..84158749189 100644
--- a/src/emu/drivenum.cpp
+++ b/src/emu/drivenum.cpp
@@ -260,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;
@@ -303,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)
{
diff --git a/src/emu/driver.h b/src/emu/driver.h
index 75e035d46c9..ed7172d25ef 100644
--- a/src/emu/driver.h
+++ b/src/emu/driver.h
@@ -156,10 +156,10 @@ 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
diff --git a/src/emu/drivers/testcpu.cpp b/src/emu/drivers/testcpu.cpp
index 95e3d5a1ec2..c6df4ff74af 100644
--- a/src/emu/drivers/testcpu.cpp
+++ b/src/emu/drivers/testcpu.cpp
@@ -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
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,49 +124,43 @@ public:
m_cpu->ppcdrc_set_options(PPCDRC_COMPATIBLE_OPTIONS);
// set a timer to go off right away
- timer_alloc(FUNC(timer_tick), this)->adjust(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");
}
}
@@ -152,20 +169,18 @@ public:
{
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
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/emucore.h b/src/emu/emucore.h
index ca54f5208cd..f6552461131 100644
--- a/src/emu/emucore.h
+++ b/src/emu/emucore.h
@@ -233,7 +233,7 @@ public:
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>
+ 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)...)))
{
diff --git a/src/emu/emufwd.h b/src/emu/emufwd.h
index fe6ae26cb2c..cf14dcb8163 100644
--- a/src/emu/emufwd.h
+++ b/src/emu/emufwd.h
@@ -38,7 +38,6 @@ class output_module;
// declared in osdepend.h
class osd_font;
class osd_interface;
-class osd_midi_device;
@@ -236,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.h b/src/emu/emumem.h
index 1b9b6363622..39470a45632 100644
--- a/src/emu/emumem.h
+++ b/src/emu/emumem.h
@@ -71,26 +71,6 @@ 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;
@@ -1641,85 +1621,85 @@ template<int Width, int AddrShift, endianness_t Endian, int TargetWidth, bool Al
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)
{
- static constexpr u32 LowBits = emu::detail::handler_entry_dispatch_level_to_lowbits(Level, Width, AddrShift);
- return dispatch[(offset & mask) >> LowBits]->read(offset, mem_mask);
+ 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)
{
- static constexpr u32 LowBits = emu::detail::handler_entry_dispatch_level_to_lowbits(Level, Width, AddrShift);
- return dispatch[(offset & mask) >> LowBits]->write(offset, data, mem_mask);
+ 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)
{
- static constexpr u32 LowBits = emu::detail::handler_entry_dispatch_level_to_lowbits(Level, Width, AddrShift);
- return dispatch[(offset & mask) >> LowBits]->read_flags(offset, mem_mask);
+ 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)
{
- static constexpr u32 LowBits = emu::detail::handler_entry_dispatch_level_to_lowbits(Level, Width, AddrShift);
- return dispatch[(offset & mask) >> LowBits]->write_flags(offset, data, mem_mask);
+ 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)
{
- static constexpr u32 LowBits = emu::detail::handler_entry_dispatch_level_to_lowbits(Level, Width, AddrShift);
- return dispatch[(offset & mask) >> LowBits]->lookup_flags(offset, mem_mask);
+ 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)
{
- static constexpr u32 LowBits = emu::detail::handler_entry_dispatch_level_to_lowbits(Level, Width, AddrShift);
- return dispatch[(offset & mask) >> LowBits]->lookup_flags(offset, mem_mask);
+ 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)
{
- static constexpr u32 LowBits = emu::detail::handler_entry_dispatch_level_to_lowbits(Level, Width, AddrShift);
- return dispatch[(offset & mask) >> LowBits]->read_interruptible(offset, mem_mask);
+ 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)
{
- static constexpr u32 LowBits = emu::detail::handler_entry_dispatch_level_to_lowbits(Level, Width, AddrShift);
- return dispatch[(offset & mask) >> LowBits]->write_interruptible(offset, data, mem_mask);
+ 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
-namespace emu::detail {
-
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;
+ 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)
+ memory_access_specific() :
+ m_space(nullptr),
+ m_addrmask(0),
+ m_dispatch_read(nullptr),
+ m_dispatch_write(nullptr)
{
}
- inline address_space &space() const {
+ address_space &space() const {
return *m_space;
}
@@ -1731,6 +1711,7 @@ public:
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); }
@@ -1745,6 +1726,7 @@ public:
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); }
@@ -1760,6 +1742,7 @@ public:
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); }
@@ -1774,6 +1757,7 @@ public:
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); }
@@ -1788,6 +1772,7 @@ public:
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); }
@@ -1802,6 +1787,7 @@ public:
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); }
@@ -1816,11 +1802,11 @@ public:
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(-1), address & m_addrmask, mask, m_dispatch_read);
+ 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(-1), address & m_addrmask, data, mask, m_dispatch_write);
+ dispatch_write_interruptible<Level, Width, AddrShift>(~offs_t(0), address & m_addrmask, data, mask, m_dispatch_write);
}
private:
@@ -1832,27 +1818,27 @@ private:
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(-1), address & m_addrmask, mask, m_dispatch_read);
+ 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(-1), address & m_addrmask, data, mask, m_dispatch_write);
+ 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(-1), address & m_addrmask, mask, m_dispatch_read);
+ 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(-1), address & m_addrmask, data, mask, m_dispatch_write);
+ 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(-1), address & m_addrmask, mask, m_dispatch_read);
+ 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(-1), address & m_addrmask, mask, m_dispatch_write);
+ 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);
@@ -1891,13 +1877,13 @@ public:
// 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);
}
@@ -1922,6 +1908,7 @@ public:
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); }
@@ -1936,6 +1923,7 @@ public:
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); }
@@ -1951,6 +1939,7 @@ public:
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); }
@@ -1965,6 +1954,7 @@ public:
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); }
@@ -1980,6 +1970,7 @@ public:
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); }
@@ -1994,6 +1985,7 @@ public:
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); }
@@ -2313,13 +2305,13 @@ protected:
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)
+ 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
@@ -2343,6 +2335,24 @@ protected:
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
@@ -2406,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;
@@ -2427,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;
@@ -2442,7 +2454,7 @@ public:
// setup
void prepare_map();
- void prepare_device_map(address_map &map);
+ void prepare_device_map(address_map &map) ATTR_COLD;
void populate_from_map(address_map *map = nullptr);
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); }
@@ -2458,7 +2470,7 @@ protected:
virtual std::pair<void *, void *> get_cache_info() = 0;
virtual std::pair<const void *, const void *> get_specific_info() = 0;
- void prepare_map_generic(address_map &map, bool allow_alloc);
+ void prepare_map_generic(address_map &map, bool allow_alloc) ATTR_COLD;
// private state
device_t & m_device; // reference to the owning device
@@ -2630,8 +2642,8 @@ public:
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);
- void prepare_device_map(address_map &map);
+ 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);
@@ -2645,13 +2657,13 @@ public:
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;
@@ -2671,6 +2683,7 @@ private:
void make_subdispatch(std::string context);
int id_to_slot(int id) const;
void register_state();
+ void refresh_id();
};
diff --git a/src/emu/emumem_aspace.cpp b/src/emu/emumem_aspace.cpp
index b0d00d6fca8..f93472b110b 100644
--- a/src/emu/emumem_aspace.cpp
+++ b/src/emu/emumem_aspace.cpp
@@ -10,10 +10,6 @@
***************************************************************************/
#include "emu.h"
-#include <list>
-#include <map>
-#include "emuopts.h"
-#include "debug/debugcpu.h"
#include "emumem_mud.h"
#include "emumem_hea.h"
@@ -27,6 +23,14 @@
#include "emumem_het.h"
#include "emumem_hws.h"
+#include "emuopts.h"
+#include "debug/debugcpu.h"
+
+#include "mfpresolve.h"
+
+#include <list>
+#include <map>
+
//**************************************************************************
// DEBUGGING
@@ -310,23 +314,24 @@ public:
m_root_write->detach(handlers);
}
- // generate accessor table
- virtual void accessors(data_accessors &accessors) const override
+ // generate specific accessor info
+ virtual specific_access_info specific_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);
+ 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
@@ -370,6 +375,7 @@ public:
// 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); }
@@ -384,6 +390,7 @@ public:
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); }
@@ -397,23 +404,6 @@ public:
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); }
-
- // static access to these functions
- static u8 read_byte_static(this_type &space, offs_t address) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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> *m_root_read;
handler_entry_write<Width, AddrShift> *m_root_write;
diff --git a/src/emu/emumem_hedr.h b/src/emu/emumem_hedr.h
index e1e9aa98743..e48737f31c9 100644
--- a/src/emu/emumem_hedr.h
+++ b/src/emu/emumem_hedr.h
@@ -17,7 +17,7 @@ public:
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> *handler);
- handler_entry_read_dispatch(address_space *space, memory_view &view);
+ 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();
@@ -92,6 +92,8 @@ private:
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);
diff --git a/src/emu/emumem_hedr.ipp b/src/emu/emumem_hedr.ipp
index 9994f251897..c79691e5e89 100644
--- a/src/emu/emumem_hedr.ipp
+++ b/src/emu/emumem_hedr.ipp
@@ -25,6 +25,7 @@ template<int HighBits, int Width, int AddrShift> handler_entry_read_dispatch<Hig
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>();
@@ -35,7 +36,7 @@ template<int HighBits, int Width, int AddrShift> handler_entry_read_dispatch<Hig
}
}
-template<int HighBits, int Width, int AddrShift> handler_entry_read_dispatch<HighBits, Width, AddrShift>::handler_entry_read_dispatch(address_space *space, memory_view &view) : 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)
+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)
{
m_ranges_array.resize(1);
m_dispatch_array.resize(1);
@@ -43,12 +44,15 @@ template<int HighBits, int Width, int AddrShift> handler_entry_read_dispatch<Hig
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].set(0, 0);
+ m_u_ranges[i].start = addrstart;
+ m_u_ranges[i].end = addrend;
}
}
@@ -60,6 +64,7 @@ template<int HighBits, int Width, int AddrShift> handler_entry_read_dispatch<Hig
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();
@@ -92,10 +97,12 @@ template<int HighBits, int Width, int AddrShift> offs_t handler_entry_read_dispa
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 = map.empty() ? m_view->m_addrstart & HIGHMASK : map.back().end + 1;
- offs_t end = m_view->m_addrend + 1;
+ 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())
@@ -116,6 +123,7 @@ template<int HighBits, int Width, int AddrShift> void handler_entry_read_dispatc
} 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())
@@ -123,7 +131,7 @@ template<int HighBits, int Width, int AddrShift> void handler_entry_read_dispatc
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 && !((cur ^ base) & UPMASK));
+ } while(cur != end && !((cur ^ base) & UPMASK));
}
}
@@ -343,7 +351,7 @@ template<int HighBits, int Width, int AddrShift> void handler_entry_read_dispatc
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_u_dispatch[entry];
- if(cur->is_dispatch())
+ 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>(this->m_space, m_u_ranges[entry], cur);
@@ -654,57 +662,29 @@ template<int HighBits, int Width, int AddrShift> void handler_entry_read_dispatc
u32 dt = lowbits - LowBits;
u32 ne = 1 << dt;
u32 ee = end_entry - start_entry;
- if(m_view) {
- auto filter = [s = m_view->m_addrstart, e = m_view->m_addrend] (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 {
- 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] = ranges[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 {
- if(m_view) {
- auto filter = [s = m_view->m_addrstart, e = m_view->m_addrend] (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();
- }
- } else {
- 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] = ranges[entry];
- dispatch[entry]->ref();
- }
+ 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();
}
}
}
diff --git a/src/emu/emumem_hedw.h b/src/emu/emumem_hedw.h
index 130293a73a4..4bd3098a333 100644
--- a/src/emu/emumem_hedw.h
+++ b/src/emu/emumem_hedw.h
@@ -17,7 +17,7 @@ public:
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> *handler);
- handler_entry_write_dispatch(address_space *space, memory_view &view);
+ 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();
@@ -92,6 +92,8 @@ private:
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);
diff --git a/src/emu/emumem_hedw.ipp b/src/emu/emumem_hedw.ipp
index 37c09ccfe11..dd6001d4bb0 100644
--- a/src/emu/emumem_hedw.ipp
+++ b/src/emu/emumem_hedw.ipp
@@ -25,6 +25,7 @@ template<int HighBits, int Width, int AddrShift> handler_entry_write_dispatch<Hi
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>();
@@ -35,7 +36,7 @@ template<int HighBits, int Width, int AddrShift> handler_entry_write_dispatch<Hi
}
}
-template<int HighBits, int Width, int AddrShift> handler_entry_write_dispatch<HighBits, Width, AddrShift>::handler_entry_write_dispatch(address_space *space, memory_view &view) : 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)
+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)
{
m_ranges_array.resize(1);
m_dispatch_array.resize(1);
@@ -43,11 +44,16 @@ template<int HighBits, int Width, int AddrShift> handler_entry_write_dispatch<Hi
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++)
+ 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> 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)
@@ -58,6 +64,7 @@ template<int HighBits, int Width, int AddrShift> handler_entry_write_dispatch<Hi
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();
@@ -91,10 +98,11 @@ template<int HighBits, int Width, int AddrShift> offs_t handler_entry_write_disp
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 = map.empty() ? m_view->m_addrstart & HIGHMASK : map.back().end + 1;
- offs_t end = m_view->m_addrend + 1;
+ 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())
@@ -115,6 +123,7 @@ template<int HighBits, int Width, int AddrShift> void handler_entry_write_dispat
} 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())
@@ -122,7 +131,7 @@ template<int HighBits, int Width, int AddrShift> void handler_entry_write_dispat
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 && !((cur ^ base) & UPMASK));
+ } while(cur != end && !((cur ^ base) & UPMASK));
}
}
@@ -655,57 +664,29 @@ template<int HighBits, int Width, int AddrShift> void handler_entry_write_dispat
u32 dt = lowbits - LowBits;
u32 ne = 1 << dt;
u32 ee = end_entry - start_entry;
- if(m_view) {
- auto filter = [s = m_view->m_addrstart, e = m_view->m_addrend] (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 {
- 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] = ranges[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 {
- if(m_view) {
- auto filter = [s = m_view->m_addrstart, e = m_view->m_addrend] (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();
- }
- } else {
- 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] = ranges[entry];
- dispatch[entry]->ref();
- }
+ 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();
}
}
}
diff --git a/src/emu/emumem_mview.cpp b/src/emu/emumem_mview.cpp
index 2456cadc7f9..8d16f8f439a 100644
--- a/src/emu/emumem_mview.cpp
+++ b/src/emu/emumem_mview.cpp
@@ -505,18 +505,25 @@ 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(NAME([this]() { m_handler_read->select_a(m_cur_id); m_handler_write->select_a(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;
- m_handler_read->select_a(-1);
- m_handler_write->select_a(-1);
-
- if(m_space)
- m_space->invalidate_caches(read_or_write::READWRITE);
+ refresh_id();
}
void memory_view::select(int slot)
@@ -527,11 +534,7 @@ void memory_view::select(int slot)
m_cur_slot = slot;
m_cur_id = i->second;
- 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);
+ refresh_id();
}
int memory_view::id_to_slot(int id) const
@@ -553,60 +556,60 @@ void memory_view::initialize_from_address_map(offs_t addrstart, offs_t addrend,
}
namespace {
- template<int Width, int AddrShift> void h_make_1(int HighBits, address_space &space, memory_view &view, handler_entry *&r, handler_entry *&w) {
+ 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); w = new handler_entry_write_dispatch<std::max(0, Width), Width, AddrShift>(&space, view); break;
- case 1: r = new handler_entry_read_dispatch<std::max(1, Width), Width, AddrShift>(&space, view); w = new handler_entry_write_dispatch<std::max(1, Width), Width, AddrShift>(&space, view); break;
- case 2: r = new handler_entry_read_dispatch<std::max(2, Width), Width, AddrShift>(&space, view); w = new handler_entry_write_dispatch<std::max(2, Width), Width, AddrShift>(&space, view); break;
- case 3: r = new handler_entry_read_dispatch<std::max(3, Width), Width, AddrShift>(&space, view); w = new handler_entry_write_dispatch<std::max(3, Width), Width, AddrShift>(&space, view); break;
- case 4: r = new handler_entry_read_dispatch< 4, Width, AddrShift>(&space, view); w = new handler_entry_write_dispatch< 4, Width, AddrShift>(&space, view); break;
- case 5: r = new handler_entry_read_dispatch< 5, Width, AddrShift>(&space, view); w = new handler_entry_write_dispatch< 5, Width, AddrShift>(&space, view); break;
- case 6: r = new handler_entry_read_dispatch< 6, Width, AddrShift>(&space, view); w = new handler_entry_write_dispatch< 6, Width, AddrShift>(&space, view); break;
- case 7: r = new handler_entry_read_dispatch< 7, Width, AddrShift>(&space, view); w = new handler_entry_write_dispatch< 7, Width, AddrShift>(&space, view); break;
- case 8: r = new handler_entry_read_dispatch< 8, Width, AddrShift>(&space, view); w = new handler_entry_write_dispatch< 8, Width, AddrShift>(&space, view); break;
- case 9: r = new handler_entry_read_dispatch< 9, Width, AddrShift>(&space, view); w = new handler_entry_write_dispatch< 9, Width, AddrShift>(&space, view); break;
- case 10: r = new handler_entry_read_dispatch<10, Width, AddrShift>(&space, view); w = new handler_entry_write_dispatch<10, Width, AddrShift>(&space, view); break;
- case 11: r = new handler_entry_read_dispatch<11, Width, AddrShift>(&space, view); w = new handler_entry_write_dispatch<11, Width, AddrShift>(&space, view); break;
- case 12: r = new handler_entry_read_dispatch<12, Width, AddrShift>(&space, view); w = new handler_entry_write_dispatch<12, Width, AddrShift>(&space, view); break;
- case 13: r = new handler_entry_read_dispatch<13, Width, AddrShift>(&space, view); w = new handler_entry_write_dispatch<13, Width, AddrShift>(&space, view); break;
- case 14: r = new handler_entry_read_dispatch<14, Width, AddrShift>(&space, view); w = new handler_entry_write_dispatch<14, Width, AddrShift>(&space, view); break;
- case 15: r = new handler_entry_read_dispatch<15, Width, AddrShift>(&space, view); w = new handler_entry_write_dispatch<15, Width, AddrShift>(&space, view); break;
- case 16: r = new handler_entry_read_dispatch<16, Width, AddrShift>(&space, view); w = new handler_entry_write_dispatch<16, Width, AddrShift>(&space, view); break;
- case 17: r = new handler_entry_read_dispatch<17, Width, AddrShift>(&space, view); w = new handler_entry_write_dispatch<17, Width, AddrShift>(&space, view); break;
- case 18: r = new handler_entry_read_dispatch<18, Width, AddrShift>(&space, view); w = new handler_entry_write_dispatch<18, Width, AddrShift>(&space, view); break;
- case 19: r = new handler_entry_read_dispatch<19, Width, AddrShift>(&space, view); w = new handler_entry_write_dispatch<19, Width, AddrShift>(&space, view); break;
- case 20: r = new handler_entry_read_dispatch<20, Width, AddrShift>(&space, view); w = new handler_entry_write_dispatch<20, Width, AddrShift>(&space, view); break;
- case 21: r = new handler_entry_read_dispatch<21, Width, AddrShift>(&space, view); w = new handler_entry_write_dispatch<21, Width, AddrShift>(&space, view); break;
- case 22: r = new handler_entry_read_dispatch<22, Width, AddrShift>(&space, view); w = new handler_entry_write_dispatch<22, Width, AddrShift>(&space, view); break;
- case 23: r = new handler_entry_read_dispatch<23, Width, AddrShift>(&space, view); w = new handler_entry_write_dispatch<23, Width, AddrShift>(&space, view); break;
- case 24: r = new handler_entry_read_dispatch<24, Width, AddrShift>(&space, view); w = new handler_entry_write_dispatch<24, Width, AddrShift>(&space, view); break;
- case 25: r = new handler_entry_read_dispatch<25, Width, AddrShift>(&space, view); w = new handler_entry_write_dispatch<25, Width, AddrShift>(&space, view); break;
- case 26: r = new handler_entry_read_dispatch<26, Width, AddrShift>(&space, view); w = new handler_entry_write_dispatch<26, Width, AddrShift>(&space, view); break;
- case 27: r = new handler_entry_read_dispatch<27, Width, AddrShift>(&space, view); w = new handler_entry_write_dispatch<27, Width, AddrShift>(&space, view); break;
- case 28: r = new handler_entry_read_dispatch<28, Width, AddrShift>(&space, view); w = new handler_entry_write_dispatch<28, Width, AddrShift>(&space, view); break;
- case 29: r = new handler_entry_read_dispatch<29, Width, AddrShift>(&space, view); w = new handler_entry_write_dispatch<29, Width, AddrShift>(&space, view); break;
- case 30: r = new handler_entry_read_dispatch<30, Width, AddrShift>(&space, view); w = new handler_entry_write_dispatch<30, Width, AddrShift>(&space, view); break;
- case 31: r = new handler_entry_read_dispatch<31, Width, AddrShift>(&space, view); w = new handler_entry_write_dispatch<31, Width, AddrShift>(&space, view); break;
- case 32: r = new handler_entry_read_dispatch<32, Width, AddrShift>(&space, view); w = new handler_entry_write_dispatch<32, Width, AddrShift>(&space, view); break;
+ 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, handler_entry *&r, handler_entry *&w) {
+ 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, r, w); break;
- case 8|(4-0): h_make_1<0, 0>(HighBits, space, view, r, w); break;
- case 16|(4+3): h_make_1<1, 3>(HighBits, space, view, r, w); break;
- case 16|(4-0): h_make_1<1, 0>(HighBits, space, view, r, w); break;
- case 16|(4-1): h_make_1<1, -1>(HighBits, space, view, r, w); break;
- case 32|(4+3): h_make_1<2, 3>(HighBits, space, view, r, w); break;
- case 32|(4-0): h_make_1<2, 0>(HighBits, space, view, r, w); break;
- case 32|(4-1): h_make_1<2, -1>(HighBits, space, view, r, w); break;
- case 32|(4-2): h_make_1<2, -2>(HighBits, space, view, r, w); break;
- case 64|(4-0): h_make_1<3, 0>(HighBits, space, view, r, w); break;
- case 64|(4-1): h_make_1<3, -1>(HighBits, space, view, r, w); break;
- case 64|(4-2): h_make_1<3, -2>(HighBits, space, view, r, w); break;
- case 64|(4-3): h_make_1<3, -3>(HighBits, space, view, r, w); break;
+ 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();
}
}
@@ -632,7 +635,7 @@ std::pair<handler_entry *, handler_entry *> memory_view::make_handlers(address_s
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, m_handler_read, m_handler_write);
+ 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();
}
diff --git a/src/emu/emupal.cpp b/src/emu/emupal.cpp
index 9e1370a24ed..d8eb5a3e774 100644
--- a/src/emu/emupal.cpp
+++ b/src/emu/emupal.cpp
@@ -168,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);
@@ -198,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);
@@ -447,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);
@@ -457,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
diff --git a/src/emu/emupal.h b/src/emu/emupal.h
index cb4884a72fb..17deb2b0903 100644
--- a/src/emu/emupal.h
+++ b/src/emu/emupal.h
@@ -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 };
@@ -296,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);
@@ -363,14 +367,16 @@ 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 noexcept override { return m_entries; }
diff --git a/src/emu/fileio.cpp b/src/emu/fileio.cpp
index 23426cb61fa..c0341b93368 100644
--- a/src/emu/fileio.cpp
+++ b/src/emu/fileio.cpp
@@ -14,6 +14,8 @@
#include "util/path.h"
#include "util/unzip.h"
+#include <tuple>
+
//#define VERBOSE 1
#define LOG_OUTPUT_FUNC osd_printf_verbose
#include "logmacro.h"
@@ -531,9 +533,10 @@ u32 emu_file::read(void *buffer, u32 length)
return 0;
// read the data if we can
+ std::error_condition err;
size_t actual = 0;
if (m_file)
- m_file->read(buffer, length, actual);
+ std::tie(err, actual) = util::read(*m_file, buffer, length);
return actual;
}
@@ -601,9 +604,10 @@ 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)
- m_file->write(buffer, length, actual);
+ std::tie(err, actual) = util::write(*m_file, buffer, length);
return actual;
}
diff --git a/src/emu/gamedrv.h b/src/emu/gamedrv.h
index 4f0050e2a19..b3a1087a66a 100644
--- a/src/emu/gamedrv.h
+++ b/src/emu/gamedrv.h
@@ -40,16 +40,13 @@ struct machine_flags
ROT180 = FLIP_X | FLIP_Y,
ROT270 = FLIP_Y | SWAP_XY,
- NOT_WORKING = 0x0000'0040,
- SUPPORTS_SAVE = 0x0000'0080, // system supports save states
- NO_COCKTAIL = 0x0000'0100, // screen flip support is missing
- IS_BIOS_ROOT = 0x0000'0200, // this driver entry is a BIOS root
- REQUIRES_ARTWORK = 0x0000'0400, // requires external artwork for key game elements
- CLICKABLE_ARTWORK = 0x0000'0800, // artwork is clickable and requires mouse cursor
- UNOFFICIAL = 0x0000'1000, // unofficial hardware modification
- NO_SOUND_HW = 0x0000'2000, // system has no sound output
- MECHANICAL = 0x0000'4000, // contains mechanical parts (pinball, redemption games, ...)
- IS_INCOMPLETE = 0x0000'8000 // 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
};
};
@@ -66,33 +63,28 @@ DECLARE_ENUM_BITWISE_OPERATORS(machine_flags::type);
/// \{
// flags for machine drivers
-constexpr u64 MACHINE_NOT_WORKING = machine_flags::NOT_WORKING; ///< Imperfect emulation prevents using the system as intended
-constexpr u64 MACHINE_SUPPORTS_SAVE = machine_flags::SUPPORTS_SAVE; ///< 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_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_CLICKABLE_ARTWORK = machine_flags::CLICKABLE_ARTWORK; ///< Enables pointer display for the system to facilitate using clickable artwork
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 feature flags
-constexpr u64 MACHINE_UNEMULATED_PROTECTION = 0x00000001'00000000; ///< Some form of protection is imperfectly emulated (e.g. copy protection or anti-tampering)
-constexpr u64 MACHINE_WRONG_COLORS = 0x00000002'00000000; ///< Colours are completely wrong
-constexpr u64 MACHINE_IMPERFECT_COLORS = 0x00000004'00000000; ///< Colours are close but not completely accurate
-constexpr u64 MACHINE_IMPERFECT_GRAPHICS = 0x00000008'00000000; ///< Graphics are emulated incorrectly for the system
-constexpr u64 MACHINE_NO_SOUND = 0x00000010'00000000; ///< The system has sound output, but it is not emulated
-constexpr u64 MACHINE_IMPERFECT_SOUND = 0x00000020'00000000; ///< Sound is known to be imperfectly emulated for the system
-constexpr u64 MACHINE_IMPERFECT_CONTROLS = 0x00000040'00000000; ///< Controls or inputs are emulated imperfectly for the system
-constexpr u64 MACHINE_NODEVICE_MICROPHONE = 0x00000080'00000000; ///< The system has unemulated audio capture functionality
-constexpr u64 MACHINE_NODEVICE_PRINTER = 0x00000100'00000000; ///< The system has unemulated printer functionality
-constexpr u64 MACHINE_NODEVICE_LAN = 0x00000200'00000000; ///< The system has unemulated local area networking
-constexpr u64 MACHINE_IMPERFECT_TIMING = 0x00000400'00000000; ///< Timing is known to be imperfectly emulated for the system
-
-// useful combinations of flags
-constexpr u64 MACHINE_IS_SKELETON = MACHINE_NO_SOUND | MACHINE_NOT_WORKING; ///< Useful combination of flags for preliminary systems
-constexpr u64 MACHINE_IS_SKELETON_MECHANICAL = MACHINE_IS_SKELETON | MACHINE_MECHANICAL | MACHINE_REQUIRES_ARTWORK; // flag combination for skeleton mechanical machines
+// 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
/// \}
/// \}
@@ -113,6 +105,20 @@ 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
@@ -175,18 +181,19 @@ 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)>
+ 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
diff --git a/src/emu/http.cpp b/src/emu/http.cpp
index 8c95e731552..2a30a13dba3 100644
--- a/src/emu/http.cpp
+++ b/src/emu/http.cpp
@@ -144,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;
@@ -175,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++) {
@@ -185,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 "";
}
@@ -204,23 +204,23 @@ struct http_response_impl : public http_manager::http_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;
}
@@ -258,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;
@@ -267,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 */);
}
diff --git a/src/emu/image.cpp b/src/emu/image.cpp
index 3839a57549b..52e553953a7 100644
--- a/src/emu/image.cpp
+++ b/src/emu/image.cpp
@@ -181,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);
std::error_condition const filerr = file.open(filename);
- if (!filerr)
- {
- std::string inistring = options.output_ini();
- file.puts(inistring);
- retval = 0;
- }
- return retval;
+ if (filerr)
+ return 1;
+
+ file.puts(options.output_ini());
+ return 0;
}
/*-------------------------------------------------
diff --git a/src/emu/inpttype.h b/src/emu/inpttype.h
index de5b42d0a77..92d4cbd2be1 100644
--- a/src/emu/inpttype.h
+++ b/src/emu/inpttype.h
@@ -206,6 +206,7 @@ enum ioport_type : osd::u32
IPT_SLOT_STOP2,
IPT_SLOT_STOP3,
IPT_SLOT_STOP4,
+ IPT_SLOT_STOP5,
IPT_SLOT_STOP_ALL,
IPT_GAMBLING_LAST,
@@ -268,7 +269,9 @@ enum ioport_type : osd::u32
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,
@@ -297,6 +300,8 @@ enum ioport_type : osd::u32
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,
diff --git a/src/emu/inpttype.ipp b/src/emu/inpttype.ipp
index e5cb81c45fc..c51afa8ce31 100644
--- a/src/emu/inpttype.ipp
+++ b/src/emu/inpttype.ipp
@@ -132,6 +132,7 @@ namespace {
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()
@@ -887,18 +888,20 @@ namespace {
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_seq::not_code, KEYCODE_LSHIFT, input_seq::not_code, KEYCODE_RSHIFT) ) \
+ 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_seq::not_code, KEYCODE_LSHIFT, input_seq::not_code, KEYCODE_RSHIFT) ) \
- INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_PAUSE, N_p("input-name", "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, N_p("input-name", "Pause - Single Step"), input_seq(KEYCODE_P, KEYCODE_LSHIFT, input_seq::or_code, KEYCODE_P, KEYCODE_RSHIFT) ) \
- INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_REWIND_SINGLE, N_p("input-name", "Rewind - Single Step"), input_seq(KEYCODE_TILDE, KEYCODE_LSHIFT) ) \
- INPUT_PORT_DIGITAL_TYPE( 0, UI, UI_SAVE_STATE, N_p("input-name", "Save State"), input_seq(KEYCODE_F7, KEYCODE_LSHIFT, input_seq::or_code, KEYCODE_F7, 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_PORT_DIGITAL_TYPE( 0, UI, UI_FRAMESKIP_DEC, N_p("input-name", "Frameskip Dec"), input_seq(KEYCODE_F8) ) \
+ 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) ) \
@@ -906,7 +909,7 @@ namespace {
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_F6) ) \
+ 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) ) \
@@ -923,6 +926,8 @@ namespace {
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 \
diff --git a/src/emu/input.cpp b/src/emu/input.cpp
index 9e82f290bf7..5eeee806f9d 100644
--- a/src/emu/input.cpp
+++ b/src/emu/input.cpp
@@ -35,17 +35,19 @@ namespace {
// simple class to match codes to strings
struct code_string_table
{
+ 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++)
+ 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;
@@ -64,56 +66,56 @@ struct code_string_table
// token strings for device classes
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
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
const code_string_table modifier_token_table[] =
{
- { 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" },
- { ~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
const code_string_table modifier_string_table[] =
{
- { 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" },
- { ~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
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
@@ -351,7 +353,7 @@ const code_string_table itemid_token_table[] =
{ ITEM_ID_ADD_RELATIVE15,"ADDREL15" },
{ ITEM_ID_ADD_RELATIVE16,"ADDREL16" },
- { ~0U, nullptr }
+ { code_string_table::SENTINEL, nullptr }
};
@@ -696,7 +698,7 @@ std::string input_manager::code_to_token(input_code code) const
// determine the devclass part
const char *devclass = (*devclass_token_table)[code.device_class()];
- if (devclass == nullptr)
+ if (!devclass)
return "INVALID";
// determine the devindex part; keyboard 0 doesn't show an index
@@ -754,8 +756,8 @@ input_code input_manager::code_from_token(std::string_view _token)
// first token should be the devclass
int curtok = 0;
- input_device_class devclass = input_device_class((*devclass_token_table)[token[curtok++]]);
- 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
@@ -770,26 +772,28 @@ input_code input_manager::code_from_token(std::string_view _token)
// next token is the item ID
input_item_id itemid = input_item_id((*itemid_token_table)[token[curtok]]);
- bool standard = (itemid != ~input_item_id(0));
+ 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();
@@ -808,7 +812,7 @@ input_code input_manager::code_from_token(std::string_view _token)
if (curtok < numtokens)
{
modifier = input_item_modifier((*modifier_token_table)[token[curtok]]);
- if (modifier != ~input_item_modifier(0))
+ if (modifier != input_item_modifier(code_string_table::SENTINEL))
curtok++;
else
modifier = ITEM_MODIFIER_NONE;
@@ -817,8 +821,8 @@ input_code input_manager::code_from_token(std::string_view _token)
// if we have another token, it is the item class
if (curtok < numtokens)
{
- u32 temp = (*itemclass_token_table)[token[curtok]];
- if (temp != ~0)
+ u32 const temp = (*itemclass_token_table)[token[curtok]];
+ if (temp != code_string_table::SENTINEL)
{
curtok++;
itemclass = input_item_class(temp);
@@ -841,7 +845,7 @@ input_code input_manager::code_from_token(std::string_view _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;
}
@@ -1209,8 +1213,8 @@ bool input_manager::map_device_to_controller(const devicemap_table &table)
return false;
// first token should be the devclass
- input_device_class devclass = input_device_class((*devclass_token_table)[strmakeupper(token[0])]);
- 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
diff --git a/src/emu/ioport.cpp b/src/emu/ioport.cpp
index 77e42c100c1..712df85db29 100644
--- a/src/emu/ioport.cpp
+++ b/src/emu/ioport.cpp
@@ -32,6 +32,7 @@
#include <algorithm>
#include <cctype>
#include <ctime>
+#include <sstream>
namespace {
@@ -237,6 +238,19 @@ const struct
{ INPUT_STRING_None, "None" },
};
+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)
{
@@ -407,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);
@@ -435,22 +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) noexcept
- : 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) noexcept
- : 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;
@@ -520,12 +531,12 @@ void input_type_entry::restore_default_seq() noexcept
// digital_joystick - constructor
//-------------------------------------------------
-digital_joystick::digital_joystick(int player, int number)
- : 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)
{
}
@@ -608,6 +619,8 @@ void digital_joystick::frame_update()
m_current4way &= ~(UP_BIT | DOWN_BIT);
}
}
+ else
+ m_current4way &= m_current;
}
@@ -662,10 +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_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)
{
}
@@ -679,10 +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_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)
{
}
@@ -696,41 +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_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)
+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 < std::size(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)
@@ -898,11 +911,8 @@ std::vector<char32_t> ioport_field::keyboard_codes(int which) const
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 < std::size(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));
}
@@ -1346,7 +1356,7 @@ float ioport_field::crosshair_read() const
// 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)
@@ -1355,71 +1365,76 @@ void ioport_field::expand_diplocation(const char *location, std::string &errorbu
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(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(':');
- if (colon != nullptr)
+ std::string_view name;
+ if (colon != std::string_view::npos)
{
// allocate and copy the name if it is present
- lastname = name.assign(number, colon - number).c_str();
+ 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;
}
else
{
// otherwise, just copy the last name
- if (lastname == nullptr)
+ 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.emplace_back(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);
}
@@ -1453,15 +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),
- 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)
@@ -1509,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)
{
}
@@ -1626,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;
@@ -1655,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();
}
@@ -1733,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())
@@ -1769,15 +1784,15 @@ 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_playback_accumulated_speed(0)
- , m_playback_accumulated_frames(0)
- , m_deselected_card_config()
- , m_applied_device_defaults(false)
+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)
{
for (auto &entries : m_type_to_entry)
std::fill(std::begin(entries), std::end(entries), nullptr);
@@ -1800,12 +1815,17 @@ time_t ioport_manager::initialize()
// if we have a token list, proceed
device_enumerator iter(machine().root_device());
- for (device_t &device : iter)
{
- std::string errors;
- m_portlist.append(device, errors);
- if (!errors.empty())
- osd_printf_error("Input port errors:\n%s", errors);
+ 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
@@ -2946,9 +2966,13 @@ Type ioport_manager::playback_read(Type &result)
return result = Type(0);
// read the value; if we fail, end playback
- size_t read;
- m_playback_stream->read(&result, sizeof(result), read);
- if (sizeof(result) != read)
+ 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");
return result = Type(0);
@@ -3132,9 +3156,8 @@ void ioport_manager::record_write(Type value)
value = little_endianize_int16(value);
// write the value; if we fail, end recording
- size_t written;
- if (m_record_stream->write(&value, sizeof(value), written) || (sizeof(value) != written))
- record_end("Out of space");
+ if (write(*m_record_stream, &value, sizeof(value)).first)
+ record_end("Write error");
}
template<>
@@ -3259,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
@@ -3359,6 +3394,7 @@ ioport_configurer& ioport_configurer::field_alloc(ioport_type type, ioport_value
// reset the current setting
m_cursetting = nullptr;
+ m_curshift = 0;
return *this;
}
@@ -3369,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 < std::size(m_curfield->m_chars); index++)
- if (m_curfield->m_chars[index][0] == 0)
- {
- const size_t char_count = std::size(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;
@@ -3462,10 +3499,10 @@ ioport_configurer& ioport_configurer::onoff_alloc(const char *name, ioport_value
// dynamic_field - constructor
//-------------------------------------------------
-dynamic_field::dynamic_field(ioport_field &field)
- : 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)
@@ -3519,36 +3556,36 @@ void dynamic_field::write(ioport_value newval)
// analog_field - constructor
//-------------------------------------------------
-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)
+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())
@@ -3760,10 +3797,11 @@ s32 analog_field::apply_settings(s32 value) const
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
diff --git a/src/emu/ioport.h b/src/emu/ioport.h
index bbff70db21d..8e8009ffce6 100644
--- a/src/emu/ioport.h
+++ b/src/emu/ioport.h
@@ -25,9 +25,12 @@
#include <cstdint>
#include <cstring>
#include <ctime>
+#include <functional>
+#include <iosfwd>
#include <initializer_list>
#include <list>
#include <memory>
+#include <string_view>
#include <vector>
@@ -43,12 +46,13 @@ constexpr ioport_value IP_ACTIVE_LOW = 0xffffffff;
constexpr int MAX_PLAYERS = 10;
// unicode constants
+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_PRIVATE + 2;
+constexpr char32_t UCHAR_MAMEKEY_BEGIN = UCHAR_SHIFT_END + 1;
// crosshair types
@@ -325,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;
@@ -470,7 +474,7 @@ 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;
@@ -485,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
@@ -529,7 +533,7 @@ class 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
const char *name() const { return m_name.c_str(); }
@@ -664,7 +668,7 @@ public:
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
@@ -744,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);
};
@@ -779,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
@@ -1033,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);
@@ -1070,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);
@@ -1082,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;
};
@@ -1097,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)
@@ -1126,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
@@ -1135,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) \
@@ -1205,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) \
@@ -1222,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(owner, DEVICE_SELF, _callback, #_callback));
-
-#define PORT_CROSSHAIR_MAPPER_MEMBER(_device, _class, _member) \
- configurer.field_set_crossmapper(ioport_field_crossmap_delegate(owner, _device, &_class::_member, #_class "::" #_member));
+#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) \
@@ -1252,48 +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(owner, DEVICE_SELF, &_class::_member, #_class "::" #_member));
-#define PORT_CUSTOM_DEVICE_MEMBER(_device, _class, _member) \
- configurer.field_set_dynamic_read(ioport_field_read_delegate(owner, _device, &_class::_member, #_class "::" #_member));
+#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(owner, _device, &_class::_member, #_class "::" #_member), (_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( \
- owner, \
- DEVICE_SELF, \
- static_cast<ioport_value (*)(_class &)>([] (_class &device) -> ioport_value { return (device._member() & 1) ? ~ioport_value(0) : 0; }), \
- #_class "::" #_member));
-#define PORT_READ_LINE_DEVICE_MEMBER(_device, _class, _member) \
+#define PORT_READ_LINE_MEMBER_IMPL(_device, _funcptr, _name) \
configurer.field_set_dynamic_read( \
ioport_field_read_delegate( \
owner, \
_device, \
- static_cast<ioport_value (*)(_class &)>([] (_class &device) -> ioport_value { return (device._member() & 1) ? ~ioport_value(0) : 0; }), \
- #_class "::" #_member));
+ 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( \
- owner, \
- DEVICE_SELF, \
- static_cast<void (*)(_class &, ioport_field &, u32, ioport_value, ioport_value)>([] (_class &device, ioport_field &field, u32 param, ioport_value oldval, ioport_value newval) { device._member(newval); }), \
- #_class "::" #_member));
-#define PORT_WRITE_LINE_DEVICE_MEMBER(_device, _class, _member) \
+#define PORT_WRITE_LINE_MEMBER_IMPL(_device, _funcptr, _name) \
configurer.field_set_dynamic_write( \
ioport_field_write_delegate( \
owner, \
_device, \
- static_cast<void (*)(_class &, ioport_field &, u32, ioport_value, ioport_value)>([] (_class &device, ioport_field &field, u32 param, ioport_value oldval, ioport_value newval) { device._member(newval); }), \
- #_class "::" #_member));
+ 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
+// DIP switch definition
#define PORT_DIPNAME(_mask, _default, _name) \
configurer.field_alloc(IPT_DIPSWITCH, (_default), (_mask), (_name));
#define PORT_DIPSETTING(_default, _name) \
@@ -1302,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
@@ -1319,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
@@ -1362,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/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/nes_rob.lay b/src/emu/layout/nes_rob.lay
index 285a6d6fe86..c5b10747151 100644
--- a/src/emu/layout/nes_rob.lay
+++ b/src/emu/layout/nes_rob.lay
@@ -1,6 +1,7 @@
<?xml version="1.0"?>
<!--
license:CC0-1.0
+authors:hap
-->
<mamelayout version="2">
diff --git a/src/emu/layout/smartboard.lay b/src/emu/layout/smartboard.lay
index eea851def47..1b028bc23fe 100644
--- a/src/emu/layout/smartboard.lay
+++ b/src/emu/layout/smartboard.lay
@@ -1,6 +1,7 @@
<?xml version="1.0"?>
<!--
license:CC0-1.0
+authors:Sandro Ronco, hap
-->
<mamelayout version="2">
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/machine.cpp b/src/emu/machine.cpp
index 669f5fa27a5..a77140dba16 100644
--- a/src/emu/machine.cpp
+++ b/src/emu/machine.cpp
@@ -212,7 +212,9 @@ void running_machine::start()
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
@@ -232,14 +234,28 @@ void running_machine::start()
if (filename[0] != 0 && !m_video->is_recording())
m_video->begin_recording(filename, movie_recording::format::AVI);
- // if we're coming in with a savegame request, process it now
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();
}
@@ -284,15 +300,13 @@ int running_machine::run(bool quiet)
// 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();
@@ -331,9 +345,12 @@ int running_machine::run(bool quiet)
// 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)
@@ -408,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");
}
@@ -863,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
@@ -870,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
}
@@ -883,39 +901,36 @@ void running_machine::handle_saveload()
auto const filerr = file.open(m_saveload_pending_file);
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;
}
@@ -926,11 +941,11 @@ void running_machine::handle_saveload()
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);
}
}
}
@@ -1160,8 +1175,17 @@ void running_machine::nvram_save()
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();
}
}
diff --git a/src/emu/main.h b/src/emu/main.h
index 78553ca5236..a62ccb46211 100644
--- a/src/emu/main.h
+++ b/src/emu/main.h
@@ -13,6 +13,7 @@
#include "emufwd.h"
+#include <map>
#include <memory>
#include <string>
#include <vector>
@@ -57,7 +58,7 @@ public:
static bool draw_user_interface(running_machine& machine);
static void periodic_check();
static bool frame_hook();
- static void sound_hook();
+ 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();
};
diff --git a/src/emu/mconfig.cpp b/src/emu/mconfig.cpp
index 2c8c1aa011b..275a4af46ae 100644
--- a/src/emu/mconfig.cpp
+++ b/src/emu/mconfig.cpp
@@ -404,6 +404,6 @@ void machine_config::set_perfect_quantum(device_t &device, std::string tag)
m_current_device->tag());
}
- m_perfect_quantum_device.first = &device;
+ m_perfect_quantum_device.first = tag.empty() ? nullptr : &device;
m_perfect_quantum_device.second = std::move(tag);
}
diff --git a/src/emu/memarray.cpp b/src/emu/memarray.cpp
index 660e07ad9a1..eb03974ff1c 100644
--- a/src/emu/memarray.cpp
+++ b/src/emu/memarray.cpp
@@ -134,8 +134,8 @@ 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; }
diff --git a/src/emu/natkeyboard.cpp b/src/emu/natkeyboard.cpp
index 681f64cd263..1abe9a140ff 100644
--- a/src/emu/natkeyboard.cpp
+++ b/src/emu/natkeyboard.cpp
@@ -34,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 = '?';
@@ -47,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);
};
@@ -344,7 +345,7 @@ natural_keyboard::natural_keyboard(running_machine &machine)
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));
}
@@ -455,23 +456,14 @@ void natural_keyboard::post_char(char32_t ch, bool normalize_crlf)
// 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_char(*text++, true);
- length--;
- }
+ // iterate over characters
+ for (char32_t ch : text)
+ post_char(ch, true);
}
@@ -479,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;
@@ -502,83 +490,74 @@ void natural_keyboard::post_utf8(const char *text, size_t length, const attotime
// append to the buffer
post_char(uc, true);
- text += count;
- length -= count;
+ text.remove_prefix(count);
}
}
-void natural_keyboard::post_utf8(std::string_view text, const attotime &rate)
-{
- if (!text.empty())
- post_utf8(text.data(), 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;
@@ -588,18 +567,11 @@ void natural_keyboard::post_coded(const char *text, size_t length, const attotim
// if we got a code, post it
if (ch != 0)
post_char(ch);
- curpos += increment;
+ text.remove_prefix(increment);
}
}
-void natural_keyboard::post_coded(std::string_view text, const attotime &rate)
-{
- if (!text.empty())
- post_coded(text.data(), text.size(), rate);
-}
-
-
//-------------------------------------------------
// paste - does a paste from the keyboard
//-------------------------------------------------
@@ -704,11 +676,11 @@ void natural_keyboard::build_codes()
{
if (!(curshift & ~mask))
{
- // fetch the code, ignoring 0 and shifters
+ // 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))
{
m_have_charkeys = true;
keycode_map::iterator const found(devinfo.codemap.find(code));
@@ -855,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;
}
diff --git a/src/emu/natkeyboard.h b/src/emu/natkeyboard.h
index c14de66fa66..57b58ffd173 100644
--- a/src/emu/natkeyboard.h
+++ b/src/emu/natkeyboard.h
@@ -45,7 +45,6 @@ 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_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; }
@@ -62,10 +61,8 @@ public:
// posting
void post_char(char32_t ch, bool normalize_crlf = false);
- 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(std::u32string_view text, const attotime &rate = attotime::zero);
void post_utf8(std::string_view text, const attotime &rate = attotime::zero);
- void post_coded(const char *text, size_t length = 0, const attotime &rate = attotime::zero);
void post_coded(std::string_view text, const attotime &rate = attotime::zero);
void paste();
diff --git a/src/emu/network.cpp b/src/emu/network.cpp
index 4566298a994..5953031133e 100644
--- a/src/emu/network.cpp
+++ b/src/emu/network.cpp
@@ -87,10 +87,11 @@ void network_manager::config_save(config_type cfg_type, util::xml::data_node *pa
{
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/recording.cpp b/src/emu/recording.cpp
index d9c25c57767..91bc0e58bf7 100644
--- a/src/emu/recording.cpp
+++ b/src/emu/recording.cpp
@@ -146,7 +146,10 @@ movie_recording::ptr movie_recording::create(running_machine &machine, screen_de
// 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;
}
@@ -190,10 +193,12 @@ bool avi_movie_recording::initialize(running_machine &machine, std::unique_ptr<e
info.audio_timescale = machine.sample_rate();
info.audio_sampletime = 1;
info.audio_numsamples = 0;
- info.audio_channels = 2;
+ 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));
@@ -225,9 +230,9 @@ bool avi_movie_recording::add_sound_to_recording(const s16 *sound, int numsample
auto profile = g_profiler.start(PROFILER_MOVIE_REC);
// write the next frame
- avi_file::error avierr = m_avi_file->append_sound_samples(0, sound + 0, numsamples, 1);
- if (avierr == avi_file::error::NONE)
- avierr = m_avi_file->append_sound_samples(1, sound + 1, numsamples, 1);
+ 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;
}
diff --git a/src/emu/recording.h b/src/emu/recording.h
index 0a26c5c7db8..98798063a73 100644
--- a/src/emu/recording.h
+++ b/src/emu/recording.h
@@ -50,6 +50,7 @@ public:
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
@@ -63,6 +64,8 @@ public:
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;
diff --git a/src/emu/render.cpp b/src/emu/render.cpp
index 8251eabb2ec..545827262bb 100644
--- a/src/emu/render.cpp
+++ b/src/emu/render.cpp
@@ -57,6 +57,7 @@
#include "util/xmlfile.h"
#include <algorithm>
+#include <limits>
@@ -99,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
@@ -301,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;
}
@@ -883,19 +919,21 @@ 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_ui_container(ui)
, m_curview(0U)
, m_flags(flags)
, m_listindex(0)
@@ -1030,9 +1068,12 @@ void render_target::set_view(unsigned viewindex)
{
if (m_views.size() > viewindex)
{
+ 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());
}
}
@@ -1050,6 +1091,229 @@ void render_target::set_max_texture_size(int maxwidth, int maxheight)
//-------------------------------------------------
+// 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
+//-------------------------------------------------
+
+void render_target::pointer_left(
+ osd::ui_event_handler::pointer type,
+ u16 ptrid,
+ u16 device,
+ s32 x,
+ s32 y,
+ u32 released,
+ s16 clicks)
+{
+ auto const target_f(map_point_layout(x, y));
+ current_view().pointer_left(type, ptrid, device, target_f.first, target_f.second, released, clicks);
+
+ // store the updated state if relevant
+ if (m_pointers.size() > ptrid)
+ {
+ m_pointers[ptrid].newpos = std::make_pair(std::numeric_limits<float>::min(), std::numeric_limits<float>::min());
+ m_pointers[ptrid].newbuttons = 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)
+ {
+ 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)
+ {
+ 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();
+ }
+}
+
+
+//-------------------------------------------------
+// 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()))
+ {
+ --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;
+ }
+ }
+
+ // check for moving across vertical edges
+ if (y < m_pointers[ptr].oldpos.second)
+ {
+ while (edges.second && (y < y_edges[edges.second - 1].position()))
+ {
+ --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;
+ }
+
+ // 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;
+ }
+}
+
+
+//-------------------------------------------------
// set_visibility_toggle - show or hide selected
// parts of a view
//-------------------------------------------------
@@ -1061,7 +1325,7 @@ void render_target::set_visibility_toggle(unsigned index, bool enable)
m_views[m_curview].second |= u32(1) << index;
else
m_views[m_curview].second &= ~(u32(1) << index);
- current_view().recompute(visibility_mask(), m_layerconfig.zoom_to_screen());
+ update_layer_config();
current_view().preload();
}
@@ -1454,8 +1718,8 @@ render_primitive_list &render_target::get_primitives()
}
}
- // 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;
@@ -1468,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
@@ -1489,16 +1753,12 @@ bool render_target::map_point_container(s32 target_x, s32 target_y, render_conta
std::pair<float, float> target_f(map_point_internal(target_x, target_y));
// explicitly check for the UI container
- if (&container == &m_manager.ui_container())
+ if (&container == m_ui_container)
{
// this hit test went against the UI container
- if ((target_f.first >= 0.0f) && (target_f.first < 1.0f) && (target_f.second >= 0.0f) && (target_f.second < 1.0f))
- {
- // this point was successfully mapped
- container_x = float(target_x) / m_width;
- container_y = float(target_y) / m_height;
- return true;
- }
+ 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
{
@@ -1517,15 +1777,12 @@ bool render_target::map_point_container(s32 target_x, s32 target_y, render_conta
[&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());
- if (bounds.includes(target_f.first, target_f.second))
- {
- // point successfully mapped
- container_x = (target_f.first - bounds.x0) / bounds.width();
- container_y = (target_f.second - bounds.y0) / bounds.height();
- return true;
- }
+ 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);
}
}
@@ -1536,74 +1793,6 @@ bool render_target::map_point_container(s32 target_x, s32 target_y, render_conta
//-------------------------------------------------
-// map_point_input - attempts to map a point on
-// the specified render_target to an input port
-// field, 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)
-{
- std::pair<float, float> target_f(map_point_internal(target_x, target_y));
- 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);
-
- auto const &items(current_view().interactive_items());
- m_hit_test.resize(items.size() * 2);
- std::fill(m_hit_test.begin(), m_hit_test.end(), false);
-
- for (auto const &edge : current_view().interactive_edges_x())
- {
- if ((edge.position() > target_f.first) || ((edge.position() == target_f.first) && edge.trailing()))
- break;
- else
- m_hit_test[edge.index()] = !edge.trailing();
- }
-
- for (auto const &edge : current_view().interactive_edges_y())
- {
- if ((edge.position() > target_f.second) || ((edge.position() == target_f.second) && edge.trailing()))
- break;
- else
- m_hit_test[items.size() + edge.index()] = !edge.trailing();
- }
-
- for (unsigned i = 0; items.size() > i; ++i)
- {
- if (m_hit_test[i] && m_hit_test[items.size() + i])
- {
- layout_view_item &item(items[i]);
- render_bounds const bounds(item.bounds());
- if (bounds.includes(target_f.first, target_f.second))
- {
- if (item.has_input())
- {
- // point successfully mapped
- std::tie(input_port, input_mask) = item.input_tag_and_mask();
- input_x = (target_f.first - bounds.x0) / bounds.width();
- input_y = (target_f.second - bounds.y0) / bounds.height();
- return true;
- }
- else
- {
- break;
- }
- }
- }
- }
-
- // default to point not mapped
- input_port = nullptr;
- input_mask = 0;
- input_x = input_y = -1.0f;
- return false;
-}
-
-
-//-------------------------------------------------
// invalidate_all - if any of our primitive lists
// contain a reference to the given pointer,
// clear them
@@ -1632,7 +1821,7 @@ void render_target::resolve_tags()
for (layout_file &file : m_filelist)
file.resolve_tags();
- current_view().recompute(visibility_mask(), m_layerconfig.zoom_to_screen());
+ update_layer_config();
current_view().preload();
}
@@ -1644,7 +1833,10 @@ void render_target::resolve_tags()
void render_target::update_layer_config()
{
+ 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());
}
@@ -2119,11 +2311,10 @@ bool render_target::load_layout_file(const char *dirname, const internal_layout
size_t decompressed = 0;
do
{
- size_t actual;
- std::error_condition const err = inflater->read(
+ auto const [err, actual] = read(
+ *inflater,
&tempout[decompressed],
- layout_data.decompressed_size - decompressed,
- actual);
+ layout_data.decompressed_size - decompressed);
decompressed += actual;
if (err)
{
@@ -2621,6 +2812,25 @@ std::pair<float, float> render_target::map_point_internal(s32 target_x, s32 targ
//-------------------------------------------------
+// map_point_layout - map point from screen
+// coordinates to layout coordinates
+//-------------------------------------------------
+
+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;
+}
+
+
+//-------------------------------------------------
// view_name - return the name of the indexed
// view, or nullptr if it doesn't exist
//-------------------------------------------------
@@ -2699,12 +2909,11 @@ void render_target::config_load(util::xml::data_node const *targetnode)
set_orientation(orientation_add(rotate, orientation()));
// apply the opposite orientation to the UI
- if (is_ui_target())
+ if (m_ui_container)
{
- render_container &ui_container = m_manager.ui_container();
- render_container::user_settings settings = ui_container.get_user_settings();
+ render_container::user_settings settings = m_ui_container->get_user_settings();
settings.m_orientation = orientation_add(orientation_reverse(rotate), settings.m_orientation);
- ui_container.set_user_settings(settings);
+ m_ui_container->set_user_settings(settings);
}
}
@@ -2744,6 +2953,8 @@ void render_target::config_load(util::xml::data_node const *targetnode)
{
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());
}
}
}
@@ -3105,7 +3316,6 @@ render_manager::render_manager(running_machine &machine)
, m_ui_target(nullptr)
, m_live_textures(0)
, m_texture_id(0)
- , m_ui_container(std::make_unique<render_container>(*this))
{
// register callbacks
machine.configuration().config_register(
@@ -3126,7 +3336,7 @@ render_manager::render_manager(running_machine &machine)
render_manager::~render_manager()
{
// free all the containers since they may own textures
- m_ui_container.reset();
+ m_ui_containers.clear();
m_screen_container_list.clear();
// better not be any outstanding textures when we die
@@ -3182,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(*new 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(*new 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));
}
@@ -3224,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.get() || 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
diff --git a/src/emu/render.h b/src/emu/render.h
index 2faa5f46394..c4c0f504edd 100644
--- a/src/emu/render.h
+++ b/src/emu/render.h
@@ -48,6 +48,8 @@
#include "rendertypes.h"
+#include "interface/uievents.h"
+
#include <cmath>
#include <list>
#include <memory>
@@ -492,14 +494,15 @@ class 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; }
@@ -525,6 +528,13 @@ public:
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(); }
@@ -550,7 +560,6 @@ 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);
@@ -559,15 +568,24 @@ public:
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);
@@ -578,6 +596,7 @@ private:
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_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);
@@ -593,13 +612,10 @@ 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
+ 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
@@ -618,7 +634,8 @@ private:
float m_max_refresh; // maximum refresh rate, 0 or if none
int m_orientation; // orientation
render_layer_config m_layerconfig; // layer configuration
- std::vector<bool> m_hit_test; // used when mapping points to inputs
+ 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
@@ -665,7 +682,7 @@ 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);
@@ -686,20 +703,20 @@ private:
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
- std::unique_ptr<render_container> m_ui_container; // UI container
- std::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 5561928e7df..d4fde37566e 100644
--- a/src/emu/rendersw.hxx
+++ b/src/emu/rendersw.hxx
@@ -14,6 +14,7 @@
#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>
class software_renderer
@@ -29,10 +30,15 @@ private:
};
// internal helpers
+ 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 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); }
+ 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 constexpr PixelType dest_assemble_rgb(u32 r, u32 g, u32 b) { return (r << DstShiftR) | (g << DstShiftG) | (b << DstShiftB); }
@@ -426,8 +432,8 @@ private:
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);
@@ -440,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;
@@ -473,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);
@@ -509,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);
@@ -576,7 +577,6 @@ private:
}
-
//**************************************************************************
// RECT RASTERIZERS
//**************************************************************************
@@ -663,19 +663,17 @@ 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(render_primitive const &prim, PixelType *dstdata, u32 pitch, quad_setup_data const &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)
{
- // ensure all parameters are valid
- assert(prim.texture.palette != nullptr);
-
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
@@ -690,7 +688,7 @@ private:
// loop over cols
for (s32 x = setup.startx; x < setup.endx; x++)
{
- u32 const pix = get_texel_palette16(prim.texture, curu, curv);
+ u32 const pix = gettexel(prim, curu, curv);
*dest++ = source32_to_dest(pix);
curu += setup.dudx;
curv += setup.dvdx;
@@ -716,7 +714,7 @@ private:
// loop over cols
for (s32 x = setup.startx; x < setup.endx; x++)
{
- u32 const pix = get_texel_palette16(prim.texture, curu, curv);
+ 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;
@@ -747,7 +745,7 @@ private:
// loop over cols
for (s32 x = setup.startx; x < setup.endx; x++)
{
- u32 const pix = get_texel_palette16(prim.texture, curu, curv);
+ 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;
@@ -763,11 +761,12 @@ 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(render_primitive const &prim, PixelType *dstdata, u32 pitch, quad_setup_data const &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)
{
// ensure all parameters are valid
assert(prim.texture.palette != nullptr);
@@ -786,18 +785,8 @@ private:
// loop over cols
for (s32 x = setup.startx; x < setup.endx; x++)
{
- const u32 pix = get_texel_palette16(prim.texture, curu, curv);
- if ((pix & 0xffffff) != 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);
- }
+ u32 const pix = gettexel(prim, curu, curv);
+ notint(*dest, pix);
dest++;
curu += setup.dudx;
curv += setup.dvdx;
@@ -823,193 +812,9 @@ private:
// loop over cols
for (s32 x = setup.startx; x < setup.endx; x++)
{
- u32 const pix = get_texel_palette16(prim.texture, curu, curv);
- if ((pix & 0xffffff) != 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);
- curu += setup.dudx;
- curv += setup.dvdx;
- }
- }
- }
- }
- }
-
-
-
- //**************************************************************************
- // 16-BIT YUY RASTERIZERS
- //**************************************************************************
-
- //-------------------------------------------------
- // draw_quad_yuy16_none - perform
- // rasterization of a 16bpp YUY image
- //-------------------------------------------------
-
- static void draw_quad_yuy16_none(render_primitive const &prim, PixelType *dstdata, u32 pitch, quad_setup_data const &setup)
- {
- 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;
- 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 < setup.endx; x++)
- {
- u32 const pix = ycc_to_rgb(get_texel_yuy16(prim.texture, curu, curv));
- *dest++ = source32_to_dest(pix);
- curu += setup.dudx;
- curv += setup.dvdx;
- }
- }
- }
- else if (is_opaque(prim.color.a))
- {
- // coloring-only case
-
- // clamp R,G,B to 0-256 range
- 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;
- 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 < setup.endx; x++)
- {
- u32 const pix = ycc_to_rgb(get_texel_yuy16(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 += setup.dudx;
- curv += setup.dvdx;
- }
- }
- }
- else if (!is_transparent(prim.color.a))
- {
- // alpha and/or coloring case
-
- // clamp R,G,B and inverse A to 0-256 range
- 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;
- 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 < setup.endx; x++)
- {
- u32 const pix = ycc_to_rgb(get_texel_yuy16(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 += setup.dudx;
- curv += setup.dvdx;
- }
- }
- }
- }
-
-
- //-------------------------------------------------
- // draw_quad_yuy16_add - perform
- // rasterization by using RGB add after YUY
- // conversion
- //-------------------------------------------------
-
- static void draw_quad_yuy16_add(render_primitive const &prim, PixelType *dstdata, u32 pitch, quad_setup_data const &setup)
- {
- // simply can't do this without reading from the dest
- if constexpr (NoDestRead)
- return;
-
- 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;
- 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 < setup.endx; x++)
- {
- u32 const pix = ycc_to_rgb(get_texel_yuy16(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);
- *dest++ = dest_assemble_rgb(r, g, b);
- curu += setup.dudx;
- curv += setup.dvdx;
- }
- }
- }
- else
- {
- // alpha and/or coloring case
-
- // clamp R,G,B and inverse A to 0-256 range
- 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;
- 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 < setup.endx; x++)
- {
- const u32 pix = ycc_to_rgb(get_texel_yuy16(prim.texture, curu, curv));
- const 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);
+ u32 const pix = gettexel(prim, curu, curv);
+ tinted(*dest, pix, sr, sg, sb);
+ dest++;
curu += setup.dudx;
curv += setup.dvdx;
}
@@ -1224,6 +1029,7 @@ private:
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 += setup.dudx;
curv += setup.dvdx;
@@ -1244,6 +1050,7 @@ private:
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 += setup.dudx;
curv += setup.dvdx;
@@ -1283,6 +1090,7 @@ private:
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 += setup.dudx;
curv += setup.dvdx;
@@ -1303,6 +1111,7 @@ private:
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 += setup.dudx;
curv += setup.dvdx;
@@ -1783,25 +1592,101 @@ private:
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):
diff --git a/src/emu/rendfont.cpp b/src/emu/rendfont.cpp
index 171a8f6d7f5..259d7731fe5 100644
--- a/src/emu/rendfont.cpp
+++ b/src/emu/rendfont.cpp
@@ -16,7 +16,6 @@
#include "render.h"
#include "corestr.h"
-#include "coreutil.h"
#include "multibyte.h"
#include "path.h"
@@ -129,13 +128,12 @@ public:
bool read(util::read_stream &f)
{
- std::size_t actual(0);
- return !f.read(m_data, sizeof(m_data), actual) && actual == sizeof(m_data);
+ auto const [err, actual] = util::read(f, m_data, sizeof(m_data));
+ return !err && (actual == sizeof(m_data));
}
bool write(util::write_stream &f)
{
- std::size_t actual(0);
- return !f.write(m_data, sizeof(m_data), actual) && actual == sizeof(m_data);
+ return !util::write(f, m_data, sizeof(m_data)).first;
}
bool check_magic() const
@@ -760,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
@@ -884,7 +883,7 @@ bool render_font::load_cached_bdf(std::string_view 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, 0, filename.length() - ((4U < filename.length()) && core_filename_ends_with(filename, ".bdf") ? 4 : 0));
@@ -1346,6 +1345,11 @@ bool render_font::load_cached(util::random_read &file, u64 length, u32 hash)
// now read the rest of the data
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));
@@ -1353,18 +1357,14 @@ bool render_font::load_cached(util::random_read &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));
- std::size_t bytes(0);
- if (file.read(&m_rawdata[bytes_read], chunk, bytes) || bytes != 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
@@ -1446,11 +1446,11 @@ bool render_font::save_cached(util::random_write &file, 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 table_offs;
if (file.tell(table_offs))
- throw emu_fatalerror("Error writing cached file");
+ 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);
@@ -1459,9 +1459,8 @@ bool render_font::save_cached(util::random_write &file, u64 length, u32 hash)
std::vector<u8> tempbuffer(65536);
// write the empty table to the beginning of the file
- std::size_t bytes_written(0);
- if (file.write(&chartable[0], chartable.size(), bytes_written) || 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]);
@@ -1503,8 +1502,8 @@ bool render_font::save_cached(util::random_write &file, u64 length, u32 hash)
*dest++ = accum;
// write the data
- if (file.write(&tempbuffer[0], dest - &tempbuffer[0], bytes_written) || 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);
@@ -1529,15 +1528,8 @@ bool render_font::save_cached(util::random_write &file, u64 length, u32 hash)
LOG("render_font::save_cached: writing character table\n");
if (file.seek(table_offs, SEEK_SET))
return false;
- u8 const *bytes(&chartable[0]);
- for (u64 remaining = chartable.size(); remaining; )
- {
- u32 const chunk((std::min<u64>)(std::numeric_limits<u32>::max(), remaining));
- if (file.write(bytes, chunk, bytes_written) || chunk != bytes_written)
- throw emu_fatalerror("Error writing cached file");
- bytes += chunk;
- remaining -= chunk;
- }
+ if (write(file, &chartable[0], chartable.size()).first)
+ throw emu_fatalerror("Error writing cached font file");
}
// no trouble?
@@ -1588,6 +1580,11 @@ void render_font::render_font_command_glyph()
// now read the rest of the data
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));
@@ -1595,18 +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));
- std::size_t bytes(0);
- if (file->read(&m_rawdata_cmd[bytes_read], chunk, bytes) || bytes != 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/rendlay.cpp b/src/emu/rendlay.cpp
index a447fffec64..489f232a1ad 100644
--- a/src/emu/rendlay.cpp
+++ b/src/emu/rendlay.cpp
@@ -1790,10 +1790,10 @@ private:
{
u8 const *const src(reinterpret_cast<u8 const *>(dst));
rgb_t const d(
- u8((float(src[3]) * c.a) + 0.5),
- u8((float(src[0]) * c.r) + 0.5),
- u8((float(src[1]) * c.g) + 0.5),
- u8((float(src[2]) * c.b) + 0.5));
+ 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);
}
@@ -2069,17 +2069,12 @@ private:
return;
}
svgbuf[len] = '\0';
- for (char *ptr = svgbuf.get(); len; )
+ size_t actual;
+ std::tie(filerr, actual) = read(file, svgbuf.get(), len);
+ if (filerr || (actual < len))
{
- size_t read;
- filerr = file.read(ptr, size_t(len), read);
- if (filerr || !read)
- {
- osd_printf_warning("Error reading component image '%s'\n", m_imagefile);
- return;
- }
- ptr += read;
- len -= read;
+ osd_printf_warning("Error reading component image '%s'\n", m_imagefile);
+ return;
}
parse_svg(svgbuf.get());
}
@@ -2152,7 +2147,7 @@ protected:
for (u32 y = bounds.top(); y <= bounds.bottom(); ++y)
std::fill_n(&dest.pix(y, bounds.left()), width, f);
}
- else if (c.a)
+ else
{
// compute premultiplied color
u32 const a(c.a * 255.0F);
@@ -2161,6 +2156,9 @@ protected:
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)
{
@@ -2194,6 +2192,7 @@ public:
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;
@@ -2303,17 +2302,13 @@ public:
if ((x >= minfill) && (x <= maxfill))
{
if (255 <= a)
- {
dst = std::fill_n(dst, maxfill - x + 1, f);
- x = maxfill;
- }
else
- {
while (x++ <= maxfill)
alpha_blend(*dst++, a, r, g, b, inva);
- --x;
- }
+
--dst;
+ x = maxfill;
}
else
{
@@ -2415,17 +2410,13 @@ public:
if ((x >= minfill) && (x <= maxfill))
{
if (255 <= a)
- {
dst = std::fill_n(dst, maxfill - x + 1, f);
- x = maxfill;
- }
else
- {
while (x++ <= maxfill)
alpha_blend(*dst++, a, r, g, b, inva);
- --x;
- }
+
--dst;
+ x = maxfill;
}
else
{
@@ -4013,7 +4004,17 @@ layout_view::layout_view(
, 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;
view_environment local(env, m_name.c_str());
@@ -4191,6 +4192,7 @@ void layout_view::recompute(u32 visibility_mask, bool zoom_to_screen)
// loop over items and filter by visibility mask
bool first = true;
bool scrfirst = true;
+ bool haveinput = false;
for (item &curitem : m_items)
{
if ((visibility_mask & curitem.visibility_mask()) == curitem.visibility_mask())
@@ -4222,9 +4224,15 @@ void layout_view::recompute(u32 visibility_mask, bool zoom_to_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;
@@ -4264,6 +4272,7 @@ void layout_view::recompute(u32 visibility_mask, bool zoom_to_screen)
// 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)
@@ -4295,6 +4304,29 @@ void layout_view::recompute(u32 visibility_mask, bool zoom_to_screen)
//-------------------------------------------------
+// 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
//-------------------------------------------------
@@ -4328,6 +4360,50 @@ void layout_view::set_recomputed_callback(recomputed_delegate &&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
//-------------------------------------------------
diff --git a/src/emu/rendlay.h b/src/emu/rendlay.h
index 0391bc6e961..23431e7fd92 100644
--- a/src/emu/rendlay.h
+++ b/src/emu/rendlay.h
@@ -16,6 +16,8 @@
#include "rendertypes.h"
#include "screen.h"
+#include "interface/uievents.h"
+
#include <array>
#include <functional>
#include <map>
@@ -436,6 +438,9 @@ public:
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>;
@@ -522,11 +527,21 @@ public:
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);
- void set_preload_callback(preload_delegate &&handler);
- void set_recomputed_callback(recomputed_delegate &&handler);
+ 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();
@@ -536,6 +551,28 @@ public:
// 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;
@@ -576,6 +613,10 @@ private:
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
@@ -586,6 +627,9 @@ private:
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
};
diff --git a/src/emu/rendutil.cpp b/src/emu/rendutil.cpp
index db7220351b8..5d09061ab70 100644
--- a/src/emu/rendutil.cpp
+++ b/src/emu/rendutil.cpp
@@ -41,8 +41,7 @@ private:
{
jpeg_corefile_source &src = *static_cast<jpeg_corefile_source *>(cinfo->src);
- size_t nbytes;
- src.infile->read(src.buffer, INPUT_BUF_SIZE, nbytes); // TODO: check error return
+ auto [err, nbytes] = read(*src.infile, src.buffer, INPUT_BUF_SIZE); // TODO: check error return
if (0 >= nbytes)
{
diff --git a/src/emu/resampler.cpp b/src/emu/resampler.cpp
new file mode 100644
index 00000000000..18a5d9f4a21
--- /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 + m_skip + 1;
+}
+
+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 + m_fs / m_ft + 1;
+}
+
+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/romload.cpp b/src/emu/romload.cpp
index a429153be3f..33541aedce4 100644
--- a/src/emu/romload.cpp
+++ b/src/emu/romload.cpp
@@ -960,10 +960,10 @@ void rom_load_manager::fill_rom_data(memory_region &region, const rom_entry *rom
copy_rom_data - copy a region of ROM space
-------------------------------------------------*/
-void rom_load_manager::copy_rom_data(memory_region &region, const rom_entry *romp)
+void rom_load_manager::copy_rom_data(device_t &device, memory_region &region, const rom_entry *romp)
{
u8 *base = region.base() + ROM_GETOFFSET(romp);
- const std::string &srcrgntag = romp->name();
+ const std::string srcrgntag = device.subtag(romp->name());
u32 numbytes = ROM_GETLENGTH(romp);
u32 srcoffs = u32(strtol(romp->hashdata().c_str(), nullptr, 0)); /* srcoffset in place of hashdata */
@@ -995,6 +995,7 @@ void rom_load_manager::copy_rom_data(memory_region &region, const rom_entry *rom
-------------------------------------------------*/
void rom_load_manager::process_rom_entries(
+ device_t &device,
const std::vector<std::string> &searchpath,
u8 bios,
memory_region &region,
@@ -1028,7 +1029,7 @@ void rom_load_manager::process_rom_entries(
}
else if (ROMENTRY_ISCOPY(romp))
{
- copy_rom_data(region, romp++);
+ copy_rom_data(device, region, romp++);
}
else if (ROMENTRY_ISFILE(romp))
{
@@ -1437,7 +1438,7 @@ void rom_load_manager::load_software_part_region(device_t &device, software_list
// now process the entries in the region
if (ROMREGION_ISROMDATA(region))
{
- process_rom_entries(swsearch, 0U, *memregion, region, region + 1, true);
+ process_rom_entries(device, swsearch, 0U, *memregion, region, region + 1, true);
}
else if (ROMREGION_ISDISKDATA(region))
{
@@ -1510,7 +1511,7 @@ void rom_load_manager::process_region_list()
if (searchpath.empty())
searchpath = device.searchpath();
assert(!searchpath.empty());
- process_rom_entries(searchpath, device.system_bios(), *memregion, region, region + 1, false);
+ process_rom_entries(device, searchpath, device.system_bios(), *memregion, region, region + 1, false);
}
else if (ROMREGION_ISDISKDATA(region))
{
diff --git a/src/emu/romload.h b/src/emu/romload.h
index 4ad0303a9f6..f655b81bf78 100644
--- a/src/emu/romload.h
+++ b/src/emu/romload.h
@@ -461,8 +461,9 @@ private:
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(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,
diff --git a/src/emu/save.cpp b/src/emu/save.cpp
index ab1fc5a854d..35599c2e679 100644
--- a/src/emu/save.cpp
+++ b/src/emu/save.cpp
@@ -27,7 +27,6 @@
#include "main.h"
-#include "util/coreutil.h"
#include "util/ioprocs.h"
#include "util/ioprocsfilter.h"
@@ -68,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);
}
@@ -102,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
@@ -187,9 +196,7 @@ void save_manager::save_memory(device_t *device, const char *module, const char
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;
}
@@ -219,8 +226,8 @@ save_error save_manager::check_file(running_machine &machine, util::core_file &f
// seek to the beginning and read the header
file.seek(0, SEEK_SET);
u8 header[HEADER_SIZE];
- size_t actual(0);
- if (file.read(header, sizeof(header), actual) || actual != 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());
@@ -267,9 +274,8 @@ save_error save_manager::write_file(util::core_file &file)
[] (size_t total_size) { return true; },
[&writer] (const void *data, size_t size)
{
- size_t written;
- std::error_condition filerr = writer->write(data, size, written);
- return !filerr && (size == written);
+ auto const [filerr, written] = write(*writer, data, size);
+ return !filerr;
},
[&file, &writer] ()
{
@@ -300,9 +306,8 @@ save_error save_manager::read_file(util::core_file &file)
[] (size_t total_size) { return true; },
[&reader] (void *data, size_t size)
{
- std::size_t read;
- std::error_condition filerr = reader->read(data, size, read);
- return !filerr && (read == size);
+ auto const [filerr, actual] = read(*reader, data, size);
+ return !filerr && (actual == size);
},
[&file, &reader] ()
{
@@ -408,10 +413,6 @@ save_error save_manager::read_buffer(const void *buf, size_t size)
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)
@@ -455,10 +456,6 @@ inline save_error save_manager::do_write(T check_space, U write_block, V start_h
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)
{
- // 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)
@@ -508,21 +505,21 @@ inline save_error save_manager::do_read(T check_length, U read_block, V start_he
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[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] = little_endianize_int32(entry->m_stride);
- crc = core_crc32(crc, (u8 *)&temp[0], sizeof(temp));
+ temp[3] = 0;
+ crc.append(&temp[0], sizeof(temp));
}
- return crc;
+ return crc.finish();
}
@@ -663,10 +660,6 @@ 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;
-
// get the save manager to load state
return m_save.read_stream(m_data);
}
@@ -916,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);
@@ -957,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";
diff --git a/src/emu/save.h b/src/emu/save.h
index 677f127853a..066f20f3330 100644
--- a/src/emu/save.h
+++ b/src/emu/save.h
@@ -34,7 +34,6 @@ enum save_error
{
STATERR_NONE,
STATERR_NOT_FOUND,
- STATERR_ILLEGAL_REGISTRATIONS,
STATERR_INVALID_HEADER,
STATERR_READ_ERROR,
STATERR_WRITE_ERROR,
@@ -160,6 +159,7 @@ 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);
@@ -334,7 +334,7 @@ private:
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
diff --git a/src/emu/schedule.cpp b/src/emu/schedule.cpp
index acc596d6798..a475fd9b515 100644
--- a/src/emu/schedule.cpp
+++ b/src/emu/schedule.cpp
@@ -230,6 +230,7 @@ void emu_timer::register_save(save_manager &manager)
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));
}
@@ -473,7 +474,7 @@ 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
{
@@ -672,6 +673,9 @@ void device_scheduler::presave()
{
// report the timer state after a log
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
@@ -724,7 +728,7 @@ void device_scheduler::postload()
{
emu_timer &timer = *private_list;
private_list = timer.m_next;
- timer_list_insert(timer);
+ timer_list_insert<true>(timer);
}
m_suspend_changes_pending = true;
@@ -839,6 +843,7 @@ void device_scheduler::rebuild_execute_list()
// the list at the appropriate location
//-------------------------------------------------
+template <bool CheckIndex>
inline emu_timer &device_scheduler::timer_list_insert(emu_timer &timer)
{
// disabled timers never expire
@@ -849,7 +854,10 @@ inline emu_timer &device_scheduler::timer_list_insert(emu_timer &timer)
for (emu_timer *curtimer = m_timer_list; curtimer; prevtimer = curtimer, curtimer = curtimer->m_next)
{
// if the current list entry expires after us, we should be inserted before it
- if (curtimer->m_expire > timer.m_expire)
+ 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;
diff --git a/src/emu/schedule.h b/src/emu/schedule.h
index e13fc18d573..b8515c0a00d 100644
--- a/src/emu/schedule.h
+++ b/src/emu/schedule.h
@@ -85,6 +85,7 @@ private:
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
+ 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>;
@@ -143,7 +144,7 @@ private:
void apply_suspend_changes();
// 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();
diff --git a/src/emu/screen.cpp b/src/emu/screen.cpp
index cf311fb21eb..0d8e35d6e4c 100644
--- a/src/emu/screen.cpp
+++ b/src/emu/screen.cpp
@@ -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))
@@ -877,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));
@@ -1039,19 +1041,19 @@ 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 (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(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));
}
@@ -1168,6 +1170,14 @@ 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));
@@ -1278,6 +1288,14 @@ void screen_device::update_now()
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
@@ -1393,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;
@@ -1914,7 +1933,7 @@ void screen_device::finalize_burnin()
//-------------------------------------------------
-// finalize_burnin - finalize the burnin bitmap
+// load_effect_overlay -
//-------------------------------------------------
void screen_device::load_effect_overlay(const char *filename)
diff --git a/src/emu/screen.h b/src/emu/screen.h
index a2d9e7f7f2e..9100ae5bacc 100644
--- a/src/emu/screen.h
+++ b/src/emu/screen.h
@@ -427,10 +427,10 @@ private:
// 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;
// internal helpers
@@ -461,7 +461,7 @@ 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
optional_memory_region m_svg_region; // the region in which the svg data is in
@@ -485,6 +485,7 @@ private:
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
@@ -504,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;
@@ -517,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
diff --git a/src/emu/softlist.cpp b/src/emu/softlist.cpp
index 4892a5ca5d8..c6bb7161be3 100644
--- a/src/emu/softlist.cpp
+++ b/src/emu/softlist.cpp
@@ -286,8 +286,7 @@ softlist_parser::softlist_parser(
char buffer[1024];
for (bool done = false; !done; )
{
- size_t length;
- file.read(buffer, sizeof(buffer), length); // TODO: better error handling
+ 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)
diff --git a/src/emu/softlist_dev.h b/src/emu/softlist_dev.h
index 772bb970c99..41c92b4f3c0 100644
--- a/src/emu/softlist_dev.h
+++ b/src/emu/softlist_dev.h
@@ -134,7 +134,7 @@ public:
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:
diff --git a/src/emu/sound.cpp b/src/emu/sound.cpp
index c9626855197..a08ede3f471 100644
--- a/src/emu/sound.cpp
+++ b/src/emu/sound.cpp
@@ -1,5 +1,5 @@
// license:BSD-3-Clause
-// copyright-holders:Aaron Giles
+// copyright-holders:O. Galibert, Aaron Giles
/***************************************************************************
sound.cpp
@@ -10,6 +10,9 @@
#include "emu.h"
+#include "audio_effects/aeffect.h"
+#include "resampler.h"
+
#include "config.h"
#include "emuopts.h"
#include "main.h"
@@ -20,1632 +23,2531 @@
#include "osdepend.h"
+#include "util/language.h"
+
+#include <algorithm>
//**************************************************************************
// DEBUGGING
//**************************************************************************
-//#define VERBOSE 1
-#define LOG_OUTPUT_FUNC osd_printf_debug
+#define LOG_OUTPUT_FUNC m_machine.logerror
-#include "logmacro.h"
+#define LOG_OSD_INFO (1U << 1)
+#define LOG_MAPPING (1U << 2)
+#define LOG_OSD_STREAMS (1U << 3)
+#define LOG_ORDER (1U << 4)
-#define LOG_OUTPUT_WAV (0)
+#define VERBOSE 0
+
+#include "logmacro.h"
-//**************************************************************************
-// GLOBAL VARIABLES
-//**************************************************************************
const attotime sound_manager::STREAMS_UPDATE_ATTOTIME = attotime::from_hz(STREAMS_UPDATE_FREQUENCY);
-//**************************************************************************
-// STREAM BUFFER
-//**************************************************************************
+//**// Output buffer management
-//-------------------------------------------------
-// stream_buffer - constructor
-//-------------------------------------------------
+// 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.
-stream_buffer::stream_buffer(u32 sample_rate) :
- m_end_second(0),
- m_end_sample(0),
- m_sample_rate(sample_rate),
- m_sample_attos((sample_rate == 0) ? ATTOSECONDS_PER_SECOND : ((ATTOSECONDS_PER_SECOND + sample_rate - 1) / sample_rate)),
- m_buffer(sample_rate)
+
+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)
{
}
-
-//-------------------------------------------------
-// stream_buffer - destructor
-//-------------------------------------------------
-
-stream_buffer::~stream_buffer()
+template<typename S> void emu::detail::output_buffer_interleaved<S>::set_buffer_size(u32 buffer_size)
{
-#if (SOUND_DEBUG)
- if (m_wav_file)
- flush_wav();
-#endif
+ m_buffer.resize(m_channels*buffer_size, 0);
}
-
-//-------------------------------------------------
-// set_sample_rate - set a new sample rate for
-// this buffer
-//-------------------------------------------------
-
-void stream_buffer::set_sample_rate(u32 rate, bool resample)
+template<typename S> void emu::detail::output_buffer_interleaved<S>::prepare_space(u32 samples)
{
- // skip if nothing is actually changing
- if (rate == m_sample_rate)
+ if(!m_channels)
return;
- // force resampling off if coming to or from an invalid rate, or if we're at time 0 (startup)
- sound_assert(rate >= SAMPLE_RATE_MINIMUM - 1);
- if (rate < SAMPLE_RATE_MINIMUM || m_sample_rate < SAMPLE_RATE_MINIMUM || (m_end_second == 0 && m_end_sample == 0))
- resample = false;
-
- // note the time and period of the current buffer (end_time is AFTER the final sample)
- attotime prevperiod = sample_period();
- attotime prevend = end_time();
-
- // compute the time and period of the new buffer
- attotime newperiod = attotime(0, (ATTOSECONDS_PER_SECOND + rate - 1) / rate);
- attotime newend = attotime(prevend.seconds(), (prevend.attoseconds() / newperiod.attoseconds()) * newperiod.attoseconds());
-
- // buffer a short runway of previous samples; in order to support smooth
- // sample rate changes (needed by, e.g., Q*Bert's Votrax), we buffer a few
- // samples at the previous rate, and then reconstitute them resampled
- // (via simple point sampling) at the new rate. The litmus test is the
- // voice when jumping off the edge in Q*Bert; without this extra effort
- // it is crackly and/or glitchy at times
- sample_t buffer[64];
- int buffered_samples = std::min(m_sample_rate, std::min(rate, u32(std::size(buffer))));
-
- // if the new rate is lower, downsample into our holding buffer;
- // otherwise just copy into our holding buffer for later upsampling
- bool new_rate_higher = (rate > m_sample_rate);
- if (resample)
- {
- if (!new_rate_higher)
- backfill_downsample(&buffer[0], buffered_samples, newend, newperiod);
- else
- {
- u32 end = m_end_sample;
- for (int index = 0; index < buffered_samples; index++)
- {
- end = prev_index(end);
-#if (SOUND_DEBUG)
- // multiple resamples can occur before clearing out old NaNs so
- // neuter them for this specific case
- if (std::isnan(m_buffer[end]))
- buffer[index] = 0;
- else
-#endif
- buffer[index] = get(end);
- }
- }
+ // 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;
}
- // ensure our buffer is large enough to hold a full second at the new rate
- if (m_buffer.size() < rate)
- m_buffer.resize(rate);
-
- // set the new rate
- m_sample_rate = rate;
- m_sample_attos = newperiod.attoseconds();
-
- // compute the new end sample index based on the buffer time
- m_end_sample = time_to_buffer_index(prevend, false, true);
-
- // if the new rate is higher, upsample from our temporary buffer;
- // otherwise just copy our previously-downsampled data
- if (resample)
- {
-#if (SOUND_DEBUG)
- // for aggressive debugging, fill the buffer with NANs to catch anyone
- // reading beyond what we resample below
- fill(NAN);
-#endif
-
- if (new_rate_higher)
- backfill_upsample(&buffer[0], buffered_samples, prevend, prevperiod);
- else
- {
- u32 end = m_end_sample;
- for (int index = 0; index < buffered_samples; index++)
- {
- end = prev_index(end);
- put(end, buffer[index]);
- }
- }
- }
-
- // if not resampling, clear the buffer
- else
- fill(0);
+ // 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);
}
-
-//-------------------------------------------------
-// open_wav - open a WAV file for logging purposes
-//-------------------------------------------------
-
-#if (SOUND_DEBUG)
-void stream_buffer::open_wav(char const *filename)
+template<typename S> void emu::detail::output_buffer_interleaved<S>::commit(u32 samples)
{
- // always open at 48k so that sound programs can handle it
- // re-sample as needed
- m_wav_file = util::wav_open(filename, 48000, 1);
+ m_write_position += samples;
}
-#endif
-
-
-//-------------------------------------------------
-// flush_wav - flush data to the WAV file
-//-------------------------------------------------
-#if (SOUND_DEBUG)
-void stream_buffer::flush_wav()
+template<typename S> void emu::detail::output_buffer_interleaved<S>::sync()
{
- // skip if no file
- if (!m_wav_file)
- return;
+ m_sync_sample += m_write_position - m_sync_position;
+ m_sync_position = m_write_position;
+}
- // grab a view of the data from the last-written point
- read_stream_view view(this, m_last_written, m_end_sample, 1.0f);
- m_last_written = m_end_sample;
+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);
+}
- // iterate over chunks for conversion
- s16 buffer[1024];
- for (int samplebase = 0; samplebase < view.samples(); samplebase += std::size(buffer))
- {
- // clamp to the buffer size
- int cursamples = view.samples() - samplebase;
- if (cursamples > std::size(buffer))
- cursamples = std::size(buffer);
+template<typename S> void emu::detail::output_buffer_flat<S>::register_save_state(device_t &device, const char *id1, const char *id2)
+{
+ auto &save = device.machine().save();
- // convert and fill
- for (int sampindex = 0; sampindex < cursamples; sampindex++)
- buffer[sampindex] = s16(view.get(samplebase + sampindex) * 32768.0);
+ for(unsigned int i=0; i != m_buffer.size(); i++)
+ save.save_item(&device, id1, id2, i, NAME(m_buffer[i]));
- // write to the WAV
- util::wav_add_data_16(*m_wav_file, buffer, cursamples);
- }
+ 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));
}
-#endif
-
-
-//-------------------------------------------------
-// index_time - return the attotime of a given
-// index within the buffer
-//-------------------------------------------------
-attotime stream_buffer::index_time(s32 index) const
+template<typename S> void emu::detail::output_buffer_flat<S>::set_buffer_size(u32 buffer_size)
{
- index = clamp_index(index);
- return attotime(m_end_second - ((index > m_end_sample) ? 1 : 0), index * m_sample_attos);
+ for(auto &b : m_buffer)
+ b.resize(buffer_size, 0);
}
-
-//-------------------------------------------------
-// time_to_buffer_index - given an attotime,
-// return the buffer index corresponding to it
-//-------------------------------------------------
-
-u32 stream_buffer::time_to_buffer_index(attotime time, bool round_up, bool allow_expansion)
+template<typename S> void emu::detail::output_buffer_flat<S>::prepare_space(u32 samples)
{
- // compute the sample index within the second
- int sample = (time.attoseconds() + (round_up ? (m_sample_attos - 1) : 0)) / m_sample_attos;
- sound_assert(sample >= 0 && sample <= size());
-
- // if the time is past the current end, make it the end
- if (time.seconds() > m_end_second || (time.seconds() == m_end_second && sample > m_end_sample))
- {
- sound_assert(allow_expansion);
-
- m_end_sample = sample;
- m_end_second = time.m_seconds;
+ if(!m_channels)
+ return;
- // due to round_up, we could tweak over the line into the next second
- if (sample >= size())
- {
- m_end_sample -= size();
- m_end_second++;
- }
+ // 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;
}
- // if the time is before the start, fail
- if (time.seconds() + 1 < m_end_second || (time.seconds() + 1 == m_end_second && sample < m_end_sample))
- throw emu_fatalerror("Attempt to create an out-of-bounds view");
-
- return clamp_index(sample);
+ // 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)
+{
+ m_write_position += samples;
+}
-//-------------------------------------------------
-// backfill_downsample - this is called BEFORE
-// the sample rate change to downsample from the
-// end of the current buffer into a temporary
-// holding location
-//-------------------------------------------------
-
-void stream_buffer::backfill_downsample(sample_t *dest, int samples, attotime newend, attotime newperiod)
+template<typename S> void emu::detail::output_buffer_flat<S>::sync()
{
- // compute the time of the first sample to be backfilled; start one period before
- attotime time = newend - newperiod;
+ m_sync_sample += m_write_position - m_sync_position;
+ m_sync_position = m_write_position;
+}
- // loop until we run out of buffered data
- int dstindex;
- for (dstindex = 0; dstindex < samples && time.seconds() >= 0; dstindex++)
- {
- u32 srcindex = time_to_buffer_index(time, false);
-#if (SOUND_DEBUG)
- // multiple resamples can occur before clearing out old NaNs so
- // neuter them for this specific case
- if (std::isnan(m_buffer[srcindex]))
- dest[dstindex] = 0;
+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
-#endif
- dest[dstindex] = get(srcindex);
- time -= newperiod;
+ for(u32 channel = 0; channel != m_channels; channel++)
+ std::fill(m_buffer[channel].begin(), m_buffer[channel].begin() + m_history, 0.0);
+
+ m_write_position += delta;
+ m_sync_position = m_history;
}
- for ( ; dstindex < samples; dstindex++)
- dest[dstindex] = 0;
}
+template<typename S> void emu::detail::output_buffer_flat<S>::resample(u32 previous_rate, u32 next_rate, attotime sync_time, attotime now)
+{
+ if(!m_write_position)
+ return;
-//-------------------------------------------------
-// backfill_upsample - this is called AFTER the
-// sample rate change to take a copied buffer
-// of samples at the old rate and upsample them
-// to the new (current) rate
-//-------------------------------------------------
+ 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);
+ }
-void stream_buffer::backfill_upsample(sample_t const *src, int samples, attotime prevend, attotime prevperiod)
-{
- // compute the time of the first sample to be backfilled; start one period before
- attotime time = end_time() - sample_period();
+ 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);
- // also adjust the buffered sample end time to point to the sample time of the
- // final sample captured
- prevend -= prevperiod;
+ double step = double(previous_rate) / double(next_rate);
+ u32 pindex = ppos - pbase;
+ u32 nend = nwrite - nbase;
- // loop until we run out of buffered data
- u32 end = m_end_sample;
- int srcindex = 0;
- while (1)
- {
- // if our backfill time is before the current buffered sample time,
- // back up until we have a sample that covers this time
- while (time < prevend && srcindex < samples)
- {
- prevend -= prevperiod;
- srcindex++;
- }
+ // 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
- // stop when we run out of source
- if (srcindex >= samples)
- break;
+ 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
- // write this sample at the pevious position
- end = prev_index(end);
- put(end, src[srcindex]);
+ 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;
- // back up to the next sample time
- time -= sample_period();
+ 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
+}
-//**************************************************************************
-// SOUND STREAM OUTPUT
-//**************************************************************************
+template class emu::detail::output_buffer_flat<sound_stream::sample_t>;
+template class emu::detail::output_buffer_interleaved<s16>;
-//-------------------------------------------------
-// sound_stream_output - constructor
-//-------------------------------------------------
-sound_stream_output::sound_stream_output() :
- m_stream(nullptr),
- m_index(0),
- m_gain(1.0)
+// 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)
{
}
-//-------------------------------------------------
-// init - initialization
-//-------------------------------------------------
+//**// Streams and routes
-void sound_stream_output::init(sound_stream &stream, u32 index, char const *tag)
+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))
{
- // set the passed-in data
- m_stream = &stream;
- m_index = index;
+ sound_assert(outputs > 0 || inputs > 0);
- // save our state
- auto &save = stream.device().machine().save();
- save.save_item(&stream.device(), "stream.output", tag, index, NAME(m_gain));
+ // create a name
+ m_name = m_device.name();
+ m_name += " '";
+ m_name += m_device.tag();
+ m_name += "'";
-#if (LOG_OUTPUT_WAV)
- std::string filename = stream.device().machine().basename();
- filename += stream.device().tag();
- for (int index = 0; index < filename.size(); index++)
- if (filename[index] == ':')
- filename[index] = '_';
- if (dynamic_cast<default_resampler_stream *>(&stream) != nullptr)
- filename += "_resampler";
- filename += "_OUT_";
- char buf[10];
- sprintf(buf, "%d", index);
- filename += buf;
- filename += ".wav";
- m_buffer.open_wav(filename.c_str());
-#endif
+ // 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()
+{
+}
-//-------------------------------------------------
-// name - return the friendly name of this output
-//-------------------------------------------------
+void sound_stream::add_bw_route(sound_stream *source, int output, int input, float gain)
+{
+ m_bw_routes.emplace_back(route_bw(source, output, input, gain));
+}
-std::string sound_stream_output::name() const
+void sound_stream::add_fw_route(sound_stream *target, int input, int output)
{
- // start with our owning stream's name
- std::ostringstream str;
- util::stream_format(str, "%s Ch.%d", m_stream->name(), m_stream->output_base() + m_index);
- return str.str();
+ m_fw_routes.emplace_back(route_fw(target, input, output));
}
+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 false;
+}
-//-------------------------------------------------
-// optimize_resampler - optimize resamplers by
-// either returning the native rate or another
-// input's resampler if they can be reused
-//-------------------------------------------------
+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;
+}
-sound_stream_output &sound_stream_output::optimize_resampler(sound_stream_output *input_resampler)
+std::vector<sound_stream *> sound_stream::targets() const
{
- // if no resampler, or if the resampler rate matches our rate, return ourself
- if (input_resampler == nullptr || buffer_sample_rate() == input_resampler->buffer_sample_rate())
- return *this;
+ 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;
+}
- // scan our list of resamplers to see if there's another match
- for (auto &resampler : m_resampler_list)
- if (resampler->buffer_sample_rate() == input_resampler->buffer_sample_rate())
- return *resampler;
+void sound_stream::register_state()
+{
+ // create a unique tag for saving
+ m_state_tag = string_format("%d", m_device.machine().sound().unique_id());
+ auto &save = m_device.machine().save();
- // add the input to our list and return the one we were given back
- m_resampler_list.push_back(input_resampler);
- return *input_resampler;
-}
+ 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");
+}
-//**************************************************************************
-// SOUND STREAM INPUT
-//**************************************************************************
-//-------------------------------------------------
-// sound_stream_input - constructor
-//-------------------------------------------------
+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);
+}
-sound_stream_input::sound_stream_input() :
- m_owner(nullptr),
- m_native_source(nullptr),
- m_resampler_source(nullptr),
- m_index(0),
- m_gain(1.0),
- m_user_gain(1.0)
+void sound_stream::add_dependants(std::vector<sound_stream *> &deps)
{
+ 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);
}
-//-------------------------------------------------
-// init - initialization
-//-------------------------------------------------
+//**// Stream sample rate
-void sound_stream_input::init(sound_stream &stream, u32 index, char const *tag, sound_stream_output *resampler)
+void sound_stream::set_sample_rate(u32 new_rate)
{
- // set the passed-in values
- m_owner = &stream;
- m_index = index;
- m_resampler_source = resampler;
-
- // save our state
- auto &save = stream.device().machine().save();
- save.save_item(&stream.device(), "stream.input", tag, index, NAME(m_gain));
- save.save_item(&stream.device(), "stream.input", tag, index, NAME(m_user_gain));
+ m_input_adaptive = m_output_adaptive = false;
+ internal_set_sample_rate(new_rate);
}
-
-//-------------------------------------------------
-// name - return the friendly name of this input
-//-------------------------------------------------
-
-std::string sound_stream_input::name() const
+void sound_stream::internal_set_sample_rate(u32 new_rate)
{
- // start with our owning stream's name
- std::ostringstream str;
- util::stream_format(str, "%s", m_owner->name());
+ 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();
- // if we have a source, indicate where the sound comes from by device name and tag
- if (valid())
- util::stream_format(str, " <- %s", m_native_source->name());
- return str.str();
+ } else
+ m_sample_rate = new_rate;
}
+bool sound_stream::try_solving_frequency()
+{
+ if(frequency_is_solved())
+ return false;
-//-------------------------------------------------
-// set_source - wire up the output source for
-// our consumption
-//-------------------------------------------------
+ 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;
-void sound_stream_input::set_source(sound_stream_output *source)
-{
- m_native_source = source;
- if (m_resampler_source != nullptr)
- m_resampler_source->stream().set_input(0, &source->stream(), source->index());
+ m_sample_rate = freqfw > freqbw ? freqfw : freqbw;
+ return true;
+ }
}
-//-------------------------------------------------
-// update - update our source's stream to the
-// current end time and return a view to its
-// contents
-//-------------------------------------------------
+//**// Stream flow and updates
-read_stream_view sound_stream_input::update(attotime start, attotime end)
+void sound_stream::init()
{
- // shouldn't get here unless valid
- sound_assert(valid());
+ // 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);
- // pick an optimized resampler
- sound_stream_output &source = m_native_source->optimize_resampler(m_resampler_source);
+ m_output_buffer.set_buffer_size(bsize);
- // if not using our own resampler, keep it up to date in case we need to invoke it later
- if (m_resampler_source != nullptr && &source != m_resampler_source)
- m_resampler_source->set_end_time(end);
+ m_samples_to_update = 0;
+ m_started = true;
+ if(synchronous())
+ reprime_sync_timer();
+}
- // update the source, returning a view of the needed output over the start and end times
- return source.stream().update_view(start, end, source.index()).apply_gain(m_gain * m_user_gain * source.gain());
+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(!is_active() || m_in_update || m_device.machine().phase() <= machine_phase::RESET)
+ return;
-//-------------------------------------------------
-// apply_sample_rate_changes - tell our sources
-// to apply any sample rate changes, informing
-// them of our current rate
-//-------------------------------------------------
+ // 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() + 1; // We want to include the current sample, hence the +1
-void sound_stream_input::apply_sample_rate_changes(u32 updatenum, u32 downstream_rate)
-{
- // shouldn't get here unless valid
- sound_assert(valid());
+ if(m_samples_to_update > 0) {
+ m_in_update = true;
- // if we have a resampler, tell it (and it will tell the native source)
- if (m_resampler_source != nullptr)
- m_resampler_source->stream().apply_sample_rate_changes(updatenum, downstream_rate);
+ // If there's anything to do, well, do it, starting with the dependencies
+ for(auto &stream : m_dependant_streams)
+ stream->update_nodeps();
- // otherwise, just tell the native source directly
- else
- m_native_source->stream().apply_sample_rate_changes(updatenum, downstream_rate);
+ do_update();
+ m_in_update = false;
+ }
+ m_samples_to_update = 0;
}
+void sound_stream::update_nodeps()
+{
+ if(!is_active() || m_in_update || m_device.machine().phase() <= machine_phase::RESET)
+ return;
+ // 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() + 1; // We want to include the current sample, hence the +1
-//**************************************************************************
-// SOUND STREAM
-//**************************************************************************
+ if(m_samples_to_update > 0) {
+ m_in_update = true;
-//-------------------------------------------------
-// sound_stream - private common constructor
-//-------------------------------------------------
+ // If there's anything to do, well, do it
+ do_update();
+ m_in_update = false;
+ }
+ m_samples_to_update = 0;
+}
-sound_stream::sound_stream(device_t &device, u32 inputs, u32 outputs, u32 output_base, u32 sample_rate, sound_stream_flags flags) :
- m_device(device),
- m_next(nullptr),
- m_sample_rate((sample_rate < SAMPLE_RATE_MINIMUM) ? (SAMPLE_RATE_MINIMUM - 1) : (sample_rate < SAMPLE_RATE_OUTPUT_ADAPTIVE) ? sample_rate : 48000),
- m_pending_sample_rate(SAMPLE_RATE_INVALID),
- m_last_sample_rate_update(0),
- m_input_adaptive(sample_rate == SAMPLE_RATE_INPUT_ADAPTIVE),
- m_output_adaptive(sample_rate == SAMPLE_RATE_OUTPUT_ADAPTIVE),
- m_synchronous((flags & STREAM_SYNCHRONOUS) != 0),
- m_resampling_disabled((flags & STREAM_DISABLE_INPUT_RESAMPLING) != 0),
- m_sync_timer(nullptr),
- m_last_update_end_time(attotime::zero),
- m_input(inputs),
- m_input_view(inputs),
- m_empty_buffer(100),
- m_output_base(output_base),
- m_output(outputs),
- m_output_view(outputs)
+void sound_stream::create_resamplers()
{
- sound_assert(outputs > 0);
+ if(!is_active()) {
+ for(auto &r : m_bw_routes)
+ r.m_resampler = nullptr;
+ return;
+ }
- // create a name
- m_name = m_device.name();
- m_name += " '";
- m_name += m_device.tag();
- m_name += "'";
+ 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;
+}
- // create a unique tag for saving
- std::string 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", state_tag.c_str(), 0, NAME(m_sample_rate));
- save.save_item(&m_device, "stream.sound_stream", state_tag.c_str(), 0, NAME(m_last_update_end_time));
- save.register_postload(save_prepost_delegate(FUNC(sound_stream::postload), this));
- save.register_presave(save_prepost_delegate(FUNC(sound_stream::presave), this));
+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;
+ }
- // initialize all inputs
- for (unsigned int inputnum = 0; inputnum < m_input.size(); inputnum++)
- {
- // allocate a resampler stream if needed, and get a pointer to its output
- sound_stream_output *resampler = nullptr;
- if (!m_resampling_disabled)
- {
- m_resampler_list.push_back(std::make_unique<default_resampler_stream>(m_device));
- resampler = &m_resampler_list.back()->m_output[0];
+ m_output_buffer.set_history(history);
+}
+
+u32 sound_stream::get_history_for_bw_route(const sound_stream *source, u32 channel) const
+{
+ 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;
+}
- // add the new input
- m_input[inputnum].init(*this, inputnum, state_tag.c_str(), resampler);
+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;
+ }
+ }
}
- // initialize all outputs
- for (unsigned int outputnum = 0; outputnum < m_output.size(); outputnum++)
- m_output[outputnum].init(*this, outputnum, state_tag.c_str());
+ // Prepare the output space (if any)
+ m_output_buffer.prepare_space(m_samples_to_update);
- // create an update timer for synchronous streams
- if (synchronous())
- m_sync_timer = m_device.timer_alloc(FUNC(sound_stream::sync_update), this);
+ // Call the callback
+ m_callback(*this);
- // force an update to the sample rates
- sample_rate_changed();
+ // Update the indexes
+ m_output_buffer.commit(m_samples_to_update);
}
-
-//-------------------------------------------------
-// sound_stream - constructor
-//-------------------------------------------------
-
-sound_stream::sound_stream(device_t &device, u32 inputs, u32 outputs, u32 output_base, u32 sample_rate, stream_update_delegate callback, sound_stream_flags flags) :
- sound_stream(device, inputs, outputs, output_base, sample_rate, flags)
+void sound_stream::sync(attotime now)
{
- m_callback_ex = std::move(callback);
+ m_sync_time = now;
+ m_output_buffer.sync();
}
-//-------------------------------------------------
-// ~sound_stream - destructor
-//-------------------------------------------------
-sound_stream::~sound_stream()
+
+attotime sound_stream::sample_to_time(u64 index) const
{
+ 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_sample_rate - set the sample rate on a
-// given stream
-//-------------------------------------------------
+//**// Synchronous stream updating
-void sound_stream::set_sample_rate(u32 new_rate)
+void sound_stream::reprime_sync_timer()
{
- // we will update this on the next global update
- if (new_rate != sample_rate())
- m_pending_sample_rate = new_rate;
+ 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();
+ reprime_sync_timer();
+}
-//-------------------------------------------------
-// set_input - configure a stream's input
-//-------------------------------------------------
-void sound_stream::set_input(int index, sound_stream *input_stream, int output_index, float gain)
+//**// 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)
{
- LOG("stream_set_input(%p, '%s', %d, %p, %d, %f)\n", (void *)this, m_device.tag(),
- index, (void *)input_stream, output_index, gain);
+ // 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));
- // 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()));
+ // register global states
+ machine.save().save_item(NAME(m_last_sync_time));
- // 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()));
+ // 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);
- // wire it up
- m_input[index].set_source((input_stream != nullptr) ? &input_stream->m_output[output_index] : nullptr);
- m_input[index].set_gain(gain);
+ // mark the generation as "just starting"
+ m_osd_info.m_generation = 0xffffffff;
+}
- // update sample rates now that we know the input
- sample_rate_changed();
+sound_manager::~sound_manager()
+{
+ 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 - force a stream to update to
-// the current emulated time
-//-------------------------------------------------
+//**// Sound system initialization
-void sound_stream::update()
+void sound_manager::before_devices_init()
{
- // ignore any update requests if we're already up to date
- attotime start = m_output[0].end_time();
- attotime end = m_device.machine().time();
- if (start >= end)
- return;
+ // 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();
- // regular update then
- update_view(start, end);
+ m_machine.save().register_postload(save_prepost_delegate(FUNC(sound_manager::postload), this));
}
-
-//-------------------------------------------------
-// update_view - force a stream to update to
-// the current emulated time and return a view
-// to the generated samples from the given
-// output number
-//-------------------------------------------------
-
-read_stream_view sound_stream::update_view(attotime start, attotime end, u32 outputnum)
+void sound_manager::postload()
{
- sound_assert(start <= end);
- sound_assert(outputnum < m_output.size());
+ 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;
+ }
+}
- // clean up parameters for when the asserts go away
- if (outputnum >= m_output.size())
- outputnum = 0;
- if (start > end)
- start = end;
+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;
+ }
- auto profile = g_profiler.start(PROFILER_SOUND);
+ 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);
+ }
- // reposition our start to coincide with the current buffer end
- attotime update_start = m_output[outputnum].end_time();
- if (update_start <= end)
- {
- // create views for all the outputs
- for (unsigned int outindex = 0; outindex < m_output.size(); outindex++)
- m_output_view[outindex] = m_output[outindex].view(update_start, end);
-
- // skip if nothing to do
- u32 samples = m_output_view[0].samples();
- sound_assert(samples >= 0);
- if (samples != 0 && m_sample_rate >= SAMPLE_RATE_MINIMUM)
- {
- sound_assert(!synchronous() || samples == 1);
-
- // ensure all input streams are up to date, and create views for them as well
- for (unsigned int inputnum = 0; inputnum < m_input.size(); inputnum++)
- {
- if (m_input[inputnum].valid())
- m_input_view[inputnum] = m_input[inputnum].update(update_start, end);
- else
- m_input_view[inputnum] = empty_view(update_start, end);
- sound_assert(m_input_view[inputnum].samples() > 0);
- sound_assert(m_resampling_disabled || m_input_view[inputnum].sample_rate() == m_sample_rate);
- }
+ // 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 (SOUND_DEBUG)
- // clear each output view to NANs before we call the callback
- for (unsigned int outindex = 0; outindex < m_output.size(); outindex++)
- m_output_view[outindex].fill(NAN);
-#endif
+ // 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 we have an extended callback, that's all we need
- m_callback_ex(*this, m_input_view, m_output_view);
+ 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());
+ }
-#if (SOUND_DEBUG)
- // make sure everything was overwritten
- for (unsigned int outindex = 0; outindex < m_output.size(); outindex++)
- for (int sampindex = 0; sampindex < m_output_view[outindex].samples(); sampindex++)
- m_output_view[outindex].get(sampindex);
+ // 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();
+ }
- for (unsigned int outindex = 0; outindex < m_output.size(); outindex++)
- m_output[outindex].m_buffer.flush_wav();
-#endif
- }
+ for(microphone_device &dev : microphone_device_enumerator(machine().root_device())) {
+ dev.set_id(m_microphones.size());
+ m_microphones.emplace_back(microphone_info(dev));
}
- // return the requested view
- return read_stream_view(m_output_view[outputnum], start);
-}
+ // Allocate the buffer to pass for recording
+ m_record_buffer.resize(m_outputs_count * machine().sample_rate(), 0);
+ m_record_samples = 0;
+ // Create resamplers and setup history
+ rebuild_all_resamplers();
-//-------------------------------------------------
-// apply_sample_rate_changes - if there is a
-// pending sample rate change, apply it now
-//-------------------------------------------------
+ m_effects_done = false;
-void sound_stream::apply_sample_rate_changes(u32 updatenum, u32 downstream_rate)
-{
- // grab the new rate and invalidate
- u32 new_rate = (m_pending_sample_rate != SAMPLE_RATE_INVALID) ? m_pending_sample_rate : m_sample_rate;
- m_pending_sample_rate = SAMPLE_RATE_INVALID;
+ m_effects_thread = std::make_unique<std::thread>(
+ [this]{ run_effects(); });
+}
- // clamp to the minimum - 1 (anything below minimum means "off" and
- // will not call the sound callback at all)
- if (new_rate < SAMPLE_RATE_MINIMUM)
- new_rate = SAMPLE_RATE_MINIMUM - 1;
- // if we're input adaptive, override with the rate of our input
- if (input_adaptive() && m_input.size() > 0 && m_input[0].valid())
- new_rate = m_input[0].source().stream().sample_rate();
+//**// Effects, input and output management
- // if we're output adaptive, override with the rate of our output
- if (output_adaptive())
- {
- if (m_last_sample_rate_update == updatenum)
- sound_assert(new_rate == m_sample_rate);
- else
- m_last_sample_rate_update = updatenum;
- new_rate = downstream_rate;
+void sound_manager::input_get(int id, sound_stream &stream)
+{
+ u32 samples = stream.samples();
+ u64 end_pos = stream.end_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;
+ }
+ }
}
+}
- // if something is different, process the change
- if (new_rate != SAMPLE_RATE_INVALID && new_rate != m_sample_rate)
- {
- // update to the new rate and notify everyone
-#if (SOUND_DEBUG)
- printf("stream %s changing rates %d -> %d\n", name().c_str(), m_sample_rate, new_rate);
-#endif
- m_sample_rate = new_rate;
- sample_rate_changed();
+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;
+ }
+ outb++;
}
-
- // now call through our inputs and apply the rate change there
- for (auto &input : m_input)
- if (input.valid())
- input.apply_sample_rate_changes(updatenum, m_sample_rate);
}
+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();
+ }
-//-------------------------------------------------
-// print_graph_recursive - helper for debugging;
-// prints info on this stream and then recursively
-// prints info on all inputs
-//-------------------------------------------------
+ // 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);
-#if (SOUND_DEBUG)
-void sound_stream::print_graph_recursive(int indent, int index)
-{
- osd_printf_info("%*s%s Ch.%d @ %d\n", indent, "", name(), index + m_output_base, sample_rate());
- for (int index = 0; index < m_input.size(); index++)
- if (m_input[index].valid())
- {
- if (m_input[index].m_resampler_source != nullptr)
- m_input[index].m_resampler_source->stream().print_graph_recursive(indent + 2, m_input[index].m_resampler_source->index());
- else
- m_input[index].m_native_source->stream().print_graph_recursive(indent + 2, m_input[index].m_native_source->index());
- }
-}
-#endif
+ 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;
+ switch(step.m_mode) {
+ case mixing_step::CLEAR:
+ for(u32 sample = 0; sample != samples; sample++) {
+ *dest = 0;
+ dest += skip;
+ }
+ break;
-//-------------------------------------------------
-// sample_rate_changed - recompute sample
-// rate data, and all streams that are affected
-// by this stream
-//-------------------------------------------------
+ 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;
+ }
-void sound_stream::sample_rate_changed()
-{
- // if invalid, just punt
- if (m_sample_rate == SAMPLE_RATE_INVALID)
- return;
+ 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;
+ }
+ }
+ }
- // update all output buffers
- for (auto &output : m_output)
- output.sample_rate_changed(m_sample_rate);
+ for(auto &si : m_speakers)
+ si.m_effects.back().m_buffer.sync();
- // if synchronous, prime the timer
- if (synchronous())
- reprime_sync_timer();
+ // 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();
+}
-//-------------------------------------------------
-// postload - save/restore callback
-//-------------------------------------------------
+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::postload()
+std::vector<audio_effect *> sound_manager::default_effect_chain() const
{
- // set the end time of all of our streams to the value saved in m_last_update_end_time
- for (auto &output : m_output)
- output.set_end_time(m_last_update_end_time);
+ std::vector<audio_effect *> res;
+ for(const auto &e : m_default_effects)
+ res.push_back(e.get());
+ return res;
+}
- // recompute the sample rate information
- sample_rate_changed();
+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();
}
+
+
+
//-------------------------------------------------
-// presave - save/restore callback
+// start_recording - begin audio recording
//-------------------------------------------------
-void sound_stream::presave()
+bool sound_manager::start_recording(std::string_view filename)
{
- // save the stream end time
- m_last_update_end_time = m_output[0].end_time();
+ 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;
}
//-------------------------------------------------
-// reprime_sync_timer - set up the next sync
-// timer to go off just a hair after the end of
-// the current sample period
+// stop_recording - end audio recording
//-------------------------------------------------
-void sound_stream::reprime_sync_timer()
+void sound_manager::stop_recording()
{
- attotime curtime = m_device.machine().time();
- attotime target = m_output[0].end_time() + attotime(0, 1);
- m_sync_timer->adjust(target - curtime);
+ // close any open WAV file
+ m_wavfile.reset();
}
//-------------------------------------------------
-// sync_update - timer callback to handle a
-// synchronous stream
+// mute - mute sound output
//-------------------------------------------------
-void sound_stream::sync_update(s32)
+void sound_manager::mute(bool mute, u8 reason)
{
- update();
- reprime_sync_timer();
+ if(mute)
+ m_muted |= reason;
+ else
+ m_muted &= ~reason;
}
//-------------------------------------------------
-// empty_view - return an empty view covering the
-// given time period as a substitute for invalid
-// inputs
+// reset - reset all sound chips
//-------------------------------------------------
-read_stream_view sound_stream::empty_view(attotime start, attotime end)
+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())
{
- // if our dummy buffer doesn't match our sample rate, update and clear it
- if (m_empty_buffer.sample_rate() != m_sample_rate)
- m_empty_buffer.set_sample_rate(m_sample_rate, false);
-
- // allocate a write view so that it can expand, and convert back to a read view
- // on the return
- return write_stream_view(m_empty_buffer, start, end);
+ 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()));
}
+sound_manager::microphone_info::microphone_info(microphone_device &dev) : m_dev(dev)
+{
+ m_channels = dev.outputs();
+}
+void sound_manager::reset()
+{
+ LOG_OUTPUT_FUNC("Sound reset\n");
+}
-//**************************************************************************
-// RESAMPLER STREAM
-//**************************************************************************
//-------------------------------------------------
-// default_resampler_stream - derived sound_stream
-// class that handles resampling
+// pause - pause sound output
//-------------------------------------------------
-default_resampler_stream::default_resampler_stream(device_t &device) :
- sound_stream(device, 1, 1, 0, SAMPLE_RATE_OUTPUT_ADAPTIVE, stream_update_delegate(&default_resampler_stream::resampler_sound_update, this), STREAM_DISABLE_INPUT_RESAMPLING),
- m_max_latency(0)
+void sound_manager::pause()
{
- // create a name
- m_name = "Default Resampler '";
- m_name += device.tag();
- m_name += "'";
+ mute(true, MUTE_REASON_PAUSE);
}
//-------------------------------------------------
-// resampler_sound_update - stream callback
-// handler for resampling an input stream to the
-// target sample rate of the output
+// resume - resume sound output
//-------------------------------------------------
-void default_resampler_stream::resampler_sound_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
+void sound_manager::resume()
{
- sound_assert(inputs.size() == 1);
- sound_assert(outputs.size() == 1);
+ mute(false, MUTE_REASON_PAUSE);
+}
- auto &input = inputs[0];
- auto &output = outputs[0];
- // if the input has an invalid rate, just fill with zeros
- if (input.sample_rate() <= 1)
- {
- output.fill(0);
+//**// Configuration management
+
+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;
- }
- // optimize_resampler ensures we should not have equal sample rates
- sound_assert(input.sample_rate() != output.sample_rate());
+ switch(cfg_type) {
+ case config_type::INIT:
+ break;
- // compute the stepping value and the inverse
- stream_buffer::sample_t step = stream_buffer::sample_t(input.sample_rate()) / stream_buffer::sample_t(output.sample_rate());
- stream_buffer::sample_t stepinv = 1.0 / step;
+ case config_type::CONTROLLER:
+ break;
- // determine the latency we need to introduce, in input samples:
- // 1 input sample for undersampled inputs
- // 1 + step input samples for oversampled inputs
- s64 latency_samples = 1 + ((step < 1.0) ? 0 : s32(step));
- if (latency_samples <= m_max_latency)
- latency_samples = m_max_latency;
- else
- m_max_latency = latency_samples;
- attotime latency = latency_samples * input.sample_period();
-
- // clamp the latency to the start (only relevant at the beginning)
- s32 dstindex = 0;
- attotime output_start = output.start_time();
- auto numsamples = output.samples();
- while (latency > output_start && dstindex < numsamples)
- {
- output.put(dstindex++, 0);
- output_start += output.sample_period();
- }
- if (dstindex >= numsamples)
- return;
+ case config_type::DEFAULT: {
+ // In the global config, get the default effect chain configuration
- // create a rebased input buffer around the adjusted start time
- read_stream_view rebased(input, output_start - latency);
- sound_assert(rebased.start_time() + latency <= output_start);
+ 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);
+ }
+ }
- // compute the fractional input start position
- attotime delta = output_start - (rebased.start_time() + latency);
- sound_assert(delta.seconds() == 0);
- stream_buffer::sample_t srcpos = stream_buffer::sample_t(double(delta.attoseconds()) / double(rebased.sample_period_attoseconds()));
- sound_assert(srcpos <= 1.0f);
+ // and the resampler configuration
+ util::xml::data_node const *rs_node = parentnode->get_child("resampler");
+ if(rs_node) {
+ 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);
- // input is undersampled: point sample except where our sample period covers a boundary
- s32 srcindex = 0;
- if (step < 1.0)
- {
- stream_buffer::sample_t cursample = rebased.get(srcindex++);
- for ( ; dstindex < numsamples; dstindex++)
- {
- // if still within the current sample, just replicate
- srcpos += step;
- if (srcpos <= 1.0)
- output.put(dstindex, cursample);
-
- // if crossing a sample boundary, blend with the neighbor
- else
- {
- srcpos -= 1.0;
- sound_assert(srcpos <= step + 1e-5);
- stream_buffer::sample_t prevsample = cursample;
- cursample = rebased.get(srcindex++);
- output.put(dstindex, stepinv * (prevsample * (step - srcpos) + srcpos * cursample));
- }
+ // this also applies the hq settings if resampler is hq
+ set_resampler_type(rs_node->get_attribute_int("type", RESAMPLER_LOFI));
}
- sound_assert(srcindex <= rebased.samples());
+ break;
}
- // input is oversampled: sum the energy
- else
- {
- float cursample = rebased.get(srcindex++);
- for ( ; dstindex < numsamples; dstindex++)
- {
- // compute the partial first sample and advance
- stream_buffer::sample_t scale = 1.0 - srcpos;
- stream_buffer::sample_t sample = cursample * scale;
-
- // add in complete samples until we only have a fraction left
- stream_buffer::sample_t remaining = step - scale;
- while (remaining >= 1.0)
- {
- sample += rebased.get(srcindex++);
- remaining -= 1.0;
- }
+ 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;
+ }
+ }
- // add in the final partial sample
- cursample = rebased.get(srcindex++);
- sample += cursample * remaining;
- output.put(dstindex, sample * stepinv);
+ // 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);
- // our position is now the remainder
- srcpos = remaining;
- sound_assert(srcindex <= rebased.samples());
+ 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));
}
- }
-}
+ 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));
+ }
-//**************************************************************************
-// SOUND MANAGER
-//**************************************************************************
+ // 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;
+ }
+
+ case config_type::FINAL:
+ break;
+ }
+}
+
//-------------------------------------------------
-// sound_manager - constructor
+// config_save - save data to the configuration
+// file
//-------------------------------------------------
-sound_manager::sound_manager(running_machine &machine) :
- m_machine(machine),
- m_update_timer(nullptr),
- m_update_number(0),
- m_last_update(attotime::zero),
- m_finalmix_leftover(0),
- m_samples_this_update(0),
- m_finalmix(machine.sample_rate()),
- m_leftmix(machine.sample_rate()),
- m_rightmix(machine.sample_rate()),
- m_compressor_scale(1.0),
- m_compressor_counter(0),
- m_compressor_enabled(machine.options().compressor()),
- m_muted(0),
- m_nosound_mode(machine.osd().no_sound()),
- m_attenuation(0),
- m_unique_id(0),
- m_wavfile(),
- m_first_reset(true)
+void sound_manager::config_save(config_type cfg_type, util::xml::data_node *parentnode)
{
- // count the mixers
-#if VERBOSE
- mixer_interface_enumerator iter(machine.root_device());
- LOG("total mixers = %d\n", iter.count());
-#endif
+ 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);
+ }
- // 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));
+ 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;
+ }
- // register global states
- machine.save().save_item(NAME(m_last_update));
+ 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);
+ }
+ }
- // set the starting attenuation
- set_attenuation(machine.options().volume());
+ // 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));
+ }
+ }
- // 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);
+ // 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 - destructor
-//-------------------------------------------------
-sound_manager::~sound_manager()
+//**// Mapping between speakers/microphones and OSD endpoints
+
+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();
}
-
-//-------------------------------------------------
-// stream_alloc - allocate a new stream with the
-// new-style callback and flags
-//-------------------------------------------------
-
-sound_stream *sound_manager::stream_alloc(device_t &device, u32 inputs, u32 outputs, u32 sample_rate, stream_update_delegate callback, sound_stream_flags flags)
+void sound_manager::config_add_sound_io_connection_node(sound_io_device *dev, std::string name, float db)
{
- // determine output base
- u32 output_base = 0;
- for (auto &stream : m_stream_list)
- if (&stream->device() == &device)
- output_base += stream->output_count();
+ internal_config_add_sound_io_connection_node(dev, name, db);
+ m_osd_info.m_generation --;
+}
- m_stream_list.push_back(std::make_unique<sound_stream>(device, inputs, outputs, output_base, sample_rate, callback, flags));
- return m_stream_list.back().get();
+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));
}
+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 --;
+}
-//-------------------------------------------------
-// start_recording - begin audio recording
-//-------------------------------------------------
+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));
+}
-bool sound_manager::start_recording(std::string_view filename)
+void sound_manager::config_remove_sound_io_connection_node(sound_io_device *dev, std::string name)
{
- if (m_wavfile)
- return false;
- m_wavfile = util::wav_open(filename, machine().sample_rate(), 2);
- return bool(m_wavfile);
+ internal_config_remove_sound_io_connection_node(dev, name);
+ m_osd_info.m_generation --;
}
-bool sound_manager::start_recording()
+void sound_manager::internal_config_remove_sound_io_connection_node(sound_io_device *dev, std::string name)
{
- // open the output WAV file if specified
- char const *const filename = machine().options().wav_write();
- return *filename ? start_recording(filename) : false;
+ 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 --;
+}
-//-------------------------------------------------
-// stop_recording - end 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::stop_recording()
+void sound_manager::config_set_volume_sound_io_connection_node(sound_io_device *dev, std::string name, float db)
{
- // close any open WAV file
- m_wavfile.reset();
+ 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;
+ }
+}
-//-------------------------------------------------
-// set_attenuation - set the global volume
-//-------------------------------------------------
+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::set_attenuation(float attenuation)
+void sound_manager::internal_config_set_volume_sound_io_connection_default(sound_io_device *dev, float db)
{
- // currently OSD only supports integral attenuation
- m_attenuation = int(attenuation);
- machine().osd().set_mastervolume(m_muted ? -32 : m_attenuation);
+ auto &config = config_get_sound_io(dev);
+ for(auto &nmap : config.m_node_mappings)
+ if(nmap.first == "") {
+ nmap.second = db;
+ return;
+ }
}
-//-------------------------------------------------
-// indexed_mixer_input - return the mixer
-// device and input index of the global mixer
-// input
-//-------------------------------------------------
+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 --;
+}
-bool sound_manager::indexed_mixer_input(int index, mixer_input &info) const
+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)
{
- // scan through the mixers until we find the indexed input
- for (device_mixer_interface &mixer : mixer_interface_enumerator(machine().root_device()))
- {
- if (index < mixer.inputs())
- {
- info.mixer = &mixer;
- info.stream = mixer.input_to_stream_input(index, info.inputnum);
- sound_assert(info.stream != nullptr);
- return true;
- }
- index -= mixer.inputs();
- }
+ 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));
+}
- // didn't locate
- info.mixer = nullptr;
- return false;
+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 --;
}
+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));
+}
-//-------------------------------------------------
-// samples - fills the specified buffer with
-// 16-bit stereo audio samples generated during
-// the current frame
-//-------------------------------------------------
+void sound_manager::config_remove_sound_io_channel_connection_node(sound_io_device *dev, u32 guest_channel, std::string name, u32 node_channel)
+{
+ internal_config_remove_sound_io_channel_connection_node(dev, guest_channel, name, node_channel);
+ m_osd_info.m_generation --;
+}
-void sound_manager::samples(s16 *buffer)
+void sound_manager::internal_config_remove_sound_io_channel_connection_node(sound_io_device *dev, u32 guest_channel, std::string name, u32 node_channel)
{
- for (int sample = 0; sample < m_samples_this_update * 2; sample++)
- *buffer++ = m_finalmix[sample];
+ 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;
+ }
}
+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 --;
+}
-//-------------------------------------------------
-// mute - mute sound output
-//-------------------------------------------------
+void sound_manager::internal_config_remove_sound_io_channel_connection_default(sound_io_device *dev, u32 guest_channel, 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) == "" && std::get<2>(*i) == node_channel) {
+ config.m_channel_mappings.erase(i);
+ return;
+ }
+}
-void sound_manager::mute(bool mute, u8 reason)
+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)
{
- bool old_muted = m_muted;
- if (mute)
- m_muted |= reason;
- else
- m_muted &= ~reason;
+ internal_config_set_volume_sound_io_channel_connection_node(dev, guest_channel, name, node_channel, db);
+ m_osd_info.m_generation --;
+}
- if(old_muted != (m_muted != 0))
- set_attenuation(m_attenuation);
+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;
+ }
}
+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 --;
+}
-//-------------------------------------------------
-// recursive_remove_stream_from_orphan_list -
-// remove the given stream from the orphan list
-// and recursively remove all our inputs
-//-------------------------------------------------
+void sound_manager::internal_config_set_volume_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) {
+ std::get<3>(cmap) = db;
+ return;
+ }
+}
-void sound_manager::recursive_remove_stream_from_orphan_list(sound_stream *which)
+void sound_manager::startup_cleanups()
{
- m_orphan_stream_list.erase(which);
- for (int inputnum = 0; inputnum < which->input_count(); inputnum++)
- {
- auto &input = which->input(inputnum);
- if (input.valid())
- recursive_remove_stream_from_orphan_list(&input.source().stream());
+ 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;
+ }
+
+ 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;
+ }
}
-}
-//-------------------------------------------------
-// apply_sample_rate_changes - recursively
-// update sample rates throughout the system
-//-------------------------------------------------
+ // 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;
+ }
-void sound_manager::apply_sample_rate_changes()
-{
- // update sample rates if they have changed
- for (speaker_device &speaker : speaker_device_enumerator(machine().root_device()))
- {
- int stream_out;
- sound_stream *stream = speaker.output_to_stream_output(0, stream_out);
-
- // due to device removal, some speakers may end up with no outputs; just skip those
- if (stream != nullptr)
- {
- sound_assert(speaker.outputs() == 1);
- stream->apply_sample_rate_changes(m_update_number, machine().sample_rate());
- }
+ 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;
+ }
-//-------------------------------------------------
-// reset - reset all sound chips
-//-------------------------------------------------
+ // 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::reset()
+void sound_manager::osd_information_update()
{
- // reset all the sound chips
- for (device_sound_interface &sound : sound_interface_enumerator(machine().root_device()))
- sound.device().reset();
-
- // apply any sample rate changes now
- apply_sample_rate_changes();
+ // 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);
+ }
+}
- // on first reset, identify any orphaned streams
- if (m_first_reset)
- {
- m_first_reset = false;
+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);
+ }
+ }
- // put all the streams on the orphan list to start
- for (auto &stream : m_stream_list)
- m_orphan_stream_list[stream.get()] = 0;
+ 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)));
+ }
+ }
- // then walk the graph like we do on update and remove any we touch
- for (speaker_device &speaker : speaker_device_enumerator(machine().root_device()))
- {
- int dummy;
- sound_stream *const output = speaker.output_to_stream_output(0, dummy);
- if (output)
- recursive_remove_stream_from_orphan_list(output);
+ 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));
+ }
- m_speakers.emplace_back(speaker);
+ 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);
+ }
}
-#if (SOUND_DEBUG)
- // dump the sound graph when we start up
- for (speaker_device &speaker : speaker_device_enumerator(machine().root_device()))
- {
- int index;
- sound_stream *output = speaker.output_to_stream_output(0, index);
- if (output != nullptr)
- output->print_graph_recursive(0, index);
+ 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)));
+ }
}
- // dump the orphan list as well
- if (m_orphan_stream_list.size() != 0)
- {
- osd_printf_info("\nOrphaned streams:\n");
- for (auto &stream : m_orphan_stream_list)
- osd_printf_info(" %s\n", stream.first->name());
- }
-#endif
+ 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
-//-------------------------------------------------
-// pause - pause sound output
-//-------------------------------------------------
-
-void sound_manager::pause()
+std::vector<u32> sound_manager::find_channel_mapping(const std::array<double, 3> &position, const osd::audio_info::node_info *node)
{
- mute(true, MUTE_REASON_PAUSE);
+ 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;
}
-//-------------------------------------------------
-// resume - resume sound output
-//-------------------------------------------------
-
-void sound_manager::resume()
+void sound_manager::update_osd_streams()
{
- mute(false, MUTE_REASON_PAUSE);
-}
+ 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;
+ }
+ // 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;
+ }
-//-------------------------------------------------
-// config_load - read and apply data from the
-// configuration file
-//-------------------------------------------------
+ // 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;
+ }
-void sound_manager::config_load(config_type cfg_type, config_level cfg_level, util::xml::data_node const *parentnode)
-{
- // we only care system-specific configuration
- if ((cfg_type != config_type::SYSTEM) || !parentnode)
- return;
+ // Otherwise use the default method
+ return get_input_stream_for_node_and_device(node, dev, is_system_default, true);
+ };
- // master volume attenuation
- if (util::xml::data_node const *node = parentnode->get_child("attenuation"))
- {
- // treat source INI files or more specific as higher priority than CFG
- // FIXME: leaky abstraction - this depends on a front-end implementation detail
- if ((OPTION_PRIORITY_NORMAL + 5) > machine().options().get_entry(OPTION_VOLUME)->priority())
- set_attenuation(std::clamp(int(node->get_attribute_int("value", 0)), -32, 0));
- }
- // iterate over channel nodes
- for (util::xml::data_node const *node = parentnode->get_child("channel"); node != nullptr; node = node->get_next_sibling("channel"))
- {
- mixer_input info;
- if (indexed_mixer_input(node->get_attribute_int("index", -1), info))
- {
- // note that this doesn't disallow out-of-range values
- float value = node->get_attribute_float("value", std::nanf(""));
-
- if (!std::isnan(value))
- info.stream->input(info.inputnum).set_user_gain(value);
- }
- }
+ 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;
+ }
- // iterate over speaker panning nodes
- for (util::xml::data_node const *node = parentnode->get_child("panning"); node != nullptr; node = node->get_next_sibling("panning"))
- {
- char const *const tag = node->get_attribute_string("tag", nullptr);
- if (tag != nullptr)
- {
- for (speaker_device &speaker : speaker_device_enumerator(machine().root_device()))
- {
- if (!strcmp(tag, speaker.tag()))
- {
- float value = node->get_attribute_float("value", speaker.defpan());
- speaker.set_pan(value);
- break;
+ // 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;
+ }
-//-------------------------------------------------
-// config_save - save data to the configuration
-// file
-//-------------------------------------------------
+ // 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;
+ }
-void sound_manager::config_save(config_type cfg_type, util::xml::data_node *parentnode)
-{
- // we only save system-specific configuration
- if (cfg_type != config_type::SYSTEM)
- return;
+ // 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;
+ }
- // master volume attenuation
- if (m_attenuation != machine().options().volume())
- {
- if (util::xml::data_node *const node = parentnode->add_child("attenuation", nullptr))
- node->set_attribute_int("value", m_attenuation);
- }
+ 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);
+ }
- // iterate over mixer channels for per-channel volume
- for (int mixernum = 0; ; mixernum++)
- {
- mixer_input info;
- if (!indexed_mixer_input(mixernum, info))
- break;
+ } 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);
+ }
+ }
+ }
- float const value = info.stream->input(info.inputnum).user_gain();
- if (value != 1.0f)
- {
- util::xml::data_node *const node = parentnode->add_child("channel", nullptr);
- if (node)
- {
- node->set_attribute_int("index", mixernum);
- node->set_attribute_float("value", value);
+ 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);
+ }
}
}
}
- // iterate over speakers for panning
- for (speaker_device &speaker : speaker_device_enumerator(machine().root_device()))
- {
- float const value = speaker.pan();
- if (value != speaker.defpan())
- {
- util::xml::data_node *const node = parentnode->add_child("panning", nullptr);
- if (node)
- {
- node->set_attribute("tag", speaker.tag());
- node->set_attribute_float("value", value);
- }
+ // 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);
}
-}
+ // 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);
+ }
-//-------------------------------------------------
-// adjust_toward_compressor_scale - adjust the
-// current scale factor toward the current goal,
-// in small increments
-//-------------------------------------------------
+ // 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);
+}
-stream_buffer::sample_t sound_manager::adjust_toward_compressor_scale(stream_buffer::sample_t curscale, stream_buffer::sample_t prevsample, stream_buffer::sample_t rawsample)
+void sound_manager::mapping_update()
{
- stream_buffer::sample_t proposed_scale = curscale;
+ 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");
+ }
+ }
- // if we want to get larger, increment by 0.01
- if (curscale < m_compressor_scale)
- {
- proposed_scale += 0.01f;
- if (proposed_scale > m_compressor_scale)
- proposed_scale = m_compressor_scale;
- }
+ generate_mapping();
- // otherwise, decrement by 0.01
- else
- {
- proposed_scale -= 0.01f;
- if (proposed_scale < m_compressor_scale)
- proposed_scale = m_compressor_scale;
- }
+ 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);
+ }
+ }
- // compute the sample at the current scale and at the proposed scale
- stream_buffer::sample_t cursample = rawsample * curscale;
- stream_buffer::sample_t proposed_sample = rawsample * proposed_scale;
+ 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);
+ }
+ }
+ }
+}
- // if they trend in the same direction, it's ok to take the step
- if ((cursample < prevsample && proposed_sample < prevsample) || (cursample > prevsample && proposed_sample > prevsample))
- curscale = proposed_scale;
- // return the current scale
- return curscale;
-}
-//-------------------------------------------------
-// update - mix everything down to its final form
-// and send it to the OSD layer
-//-------------------------------------------------
+//**// Global sound system update
-void sound_manager::update(s32 param)
+u64 sound_manager::rate_and_time_to_index(attotime time, u32 sample_rate) const
{
- LOG("sound_update\n");
+ return time.m_seconds * sample_rate + ((time.m_attoseconds / 100'000'000) * sample_rate) / 10'000'000'000LL; //'
+}
+void sound_manager::update(s32)
+{
auto profile = g_profiler.start(PROFILER_SOUND);
- // determine the duration of this update
- attotime update_period = machine().time() - m_last_update;
- sound_assert(update_period.seconds() == 0);
+ if(m_osd_info.m_generation == 0xffffffff)
+ startup_cleanups();
- // use that to compute the number of samples we need from the speakers
- attoseconds_t sample_rate_attos = HZ_TO_ATTOSECONDS(machine().sample_rate());
- m_samples_this_update = update_period.attoseconds() / sample_rate_attos;
+ mapping_update();
+ streams_update();
- // recompute the end time to an even sample boundary
- attotime endtime = m_last_update + attotime(0, m_samples_this_update * sample_rate_attos);
+ m_last_sync_time = machine().time();
+}
- // clear out the mix bufers
- std::fill_n(&m_leftmix[0], m_samples_this_update, 0);
- std::fill_n(&m_rightmix[0], m_samples_this_update, 0);
+void sound_manager::streams_update()
+{
+ attotime now = machine().time();
+ {
+ 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;
+ }
- // force all the speaker streams to generate the proper number of samples
- for (speaker_device &speaker : m_speakers)
- speaker.mix(&m_leftmix[0], &m_rightmix[0], m_last_update, endtime, m_samples_this_update, (m_muted & MUTE_REASON_SYSTEM));
+ for(sound_stream *stream : m_ordered_streams)
+ stream->update_nodeps();
+ }
- // determine the maximum in this section
- stream_buffer::sample_t curmax = 0;
- for (int sampindex = 0; sampindex < m_samples_this_update; sampindex++)
+ // Send the hooked samples to lua
{
- auto sample = m_leftmix[sampindex];
- if (sample < 0)
- sample = -sample;
- if (sample > curmax)
- curmax = sample;
-
- sample = m_rightmix[sampindex];
- if (sample < 0)
- sample = -sample;
- if (sample > curmax)
- curmax = sample;
+ 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);
}
- // pull in current compressor scale factor before modifying
- stream_buffer::sample_t lscale = m_compressor_scale;
- stream_buffer::sample_t rscale = m_compressor_scale;
+ for(sound_stream *stream : m_ordered_streams)
+ if(stream->device().type() != SPEAKER)
+ stream->sync(now);
- // if we're above what the compressor will handle, adjust the compression
- if (curmax * m_compressor_scale > 1.0)
- {
- m_compressor_scale = 1.0 / curmax;
- m_compressor_counter = STREAMS_UPDATE_FREQUENCY / 5;
- }
+ for(osd_input_stream &stream : m_osd_input_streams)
+ stream.m_buffer.sync();
- // if we're currently scaled, wait a bit to see if we can trend back toward 1.0
- else if (m_compressor_counter != 0)
- m_compressor_counter--;
+ 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 * m_outputs_count);
- // try to migrate toward 0 unless we're going to introduce clipping
- else if (m_compressor_scale < 1.0 && curmax * 1.01 * m_compressor_scale < 1.0)
- {
- m_compressor_scale *= 1.01f;
- if (m_compressor_scale > 1.0)
- m_compressor_scale = 1.0;
- }
+ m_effects_condition.notify_all();
+}
-#if (SOUND_DEBUG)
- if (lscale != m_compressor_scale)
- printf("scale=%.5f\n", m_compressor_scale);
-#endif
+//**// 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;
+}
- // track whether there are pending scale changes in left/right
- stream_buffer::sample_t lprev = 0, rprev = 0;
+void sound_manager::rebuild_all_resamplers()
+{
+ m_resamplers.clear();
- // 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;
-
- // ensure that changing the compression won't reverse direction to reduce "pops"
- stream_buffer::sample_t lsamp = m_leftmix[sampindex];
- if (lscale != m_compressor_scale && sample != m_finalmix_leftover)
- lscale = adjust_toward_compressor_scale(lscale, lprev, lsamp);
-
- lprev = lsamp * lscale;
- if (m_compressor_enabled)
- lsamp = lprev;
-
- // clamp the left side
- if (lsamp > 1.0)
- lsamp = 1.0;
- else if (lsamp < -1.0)
- lsamp = -1.0;
- finalmix[finalmix_offset++] = s16(lsamp * 32767.0);
-
- // ensure that changing the compression won't reverse direction to reduce "pops"
- stream_buffer::sample_t rsamp = m_rightmix[sampindex];
- if (rscale != m_compressor_scale && sample != m_finalmix_leftover)
- rscale = adjust_toward_compressor_scale(rscale, rprev, rsamp);
-
- rprev = rsamp * rscale;
- if (m_compressor_enabled)
- rsamp = rprev;
-
- // clamp the right side
- if (rsamp > 1.0)
- rsamp = 1.0;
- else if (rsamp < -1.0)
- rsamp = -1.0;
- finalmix[finalmix_offset++] = s16(rsamp * 32767.0);
+ for(auto &stream : m_stream_list)
+ stream->create_resamplers();
+
+ for(auto &stream : m_stream_list)
+ stream->lookup_history_sizes();
+}
+
+void sound_manager::set_resampler_type(u32 type)
+{
+ if(type != m_resampler_type) {
+ m_resampler_type = type;
+ rebuild_all_resamplers();
}
- m_finalmix_leftover = sample - m_samples_this_update * 1000;
+}
- // play the result
- if (finalmix_offset > 0)
- {
- 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)
- util::wav_add_data_16(*m_wavfile, finalmix, finalmix_offset);
+void sound_manager::set_resampler_hq_latency(double latency)
+{
+ if(latency != m_resampler_hq_latency) {
+ m_resampler_hq_latency = latency;
+ rebuild_all_resamplers();
}
+}
- // update any orphaned streams so they don't get too far behind
- for (auto &stream : m_orphan_stream_list)
- stream.first->update();
+void sound_manager::set_resampler_hq_length(u32 length)
+{
+ if(length != m_resampler_hq_length) {
+ m_resampler_hq_length = length;
+ rebuild_all_resamplers();
+ }
+}
- // remember the update time
- m_last_update = endtime;
- m_update_number++;
+void sound_manager::set_resampler_hq_phases(u32 phases)
+{
+ if(phases != m_resampler_hq_phases) {
+ m_resampler_hq_phases = phases;
+ rebuild_all_resamplers();
+ }
+}
- // apply sample rate changes
- apply_sample_rate_changes();
+const char *sound_manager::resampler_type_names(u32 type) const
+{
+ using util::lang_translate;
- // notify that new samples have been generated
- emulator_info::sound_hook();
+ if(type == RESAMPLER_HQ)
+ return _("HQ");
+ else
+ return _("LoFi");
}
diff --git a/src/emu/sound.h b/src/emu/sound.h
index 15f6a5743a2..57c96683f0a 100644
--- a/src/emu/sound.h
+++ b/src/emu/sound.h
@@ -1,5 +1,5 @@
// license:BSD-3-Clause
-// copyright-holders:Aaron Giles
+// copyright-holders:O. Galibert, Aaron Giles
/***************************************************************************
sound.h
@@ -35,7 +35,7 @@
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,
+ current end time is (via stream.end_time()), it will say t=1.0,
which is in the future, because it knows it will hold that last
sample until 1.0s.
@@ -62,21 +62,19 @@
#define MAME_EMU_SOUND_H
#include "wavwrite.h"
-
+#include "interface/audio.h"
+#include <mutex>
+#include <thread>
+#include <condition_variable>
//**************************************************************************
// CONSTANTS
//**************************************************************************
// special sample-rate values
-constexpr u32 SAMPLE_RATE_INVALID = 0xffffffff;
-constexpr u32 SAMPLE_RATE_INPUT_ADAPTIVE = 0xfffffffe;
-constexpr u32 SAMPLE_RATE_OUTPUT_ADAPTIVE = 0xfffffffd;
-
-// anything below this sample rate is effectively treated as "off"
-constexpr u32 SAMPLE_RATE_MINIMUM = 50;
-
-
+constexpr u32 SAMPLE_RATE_INPUT_ADAPTIVE = 0xffffffff;
+constexpr u32 SAMPLE_RATE_OUTPUT_ADAPTIVE = 0xfffffffe;
+constexpr u32 SAMPLE_RATE_ADAPTIVE = 0xfffffffd;
//**************************************************************************
// DEBUGGING
@@ -86,7 +84,7 @@ constexpr u32 SAMPLE_RATE_MINIMUM = 50;
#ifdef MAME_DEBUG
#define SOUND_DEBUG (1)
#else
-#define SOUND_DEBUG (0)
+#define SOUND_DEBUG (1)
#endif
// if SOUND_DEBUG is on, make assertions fire regardless of MAME_DEBUG
@@ -96,491 +94,9 @@ constexpr u32 SAMPLE_RATE_MINIMUM = 50;
#define sound_assert assert
#endif
-
-
-//**************************************************************************
-// TYPE DEFINITIONS
-//**************************************************************************
-
-// ======================> stream_buffer
-
-class stream_buffer
-{
- // stream_buffer is an internal class, not directly accessed
- // outside of the classes below
- friend class read_stream_view;
- friend class write_stream_view;
- friend class sound_stream;
- friend class sound_stream_output;
-
-public:
- // the one public bit is the sample type
- using sample_t = float;
-
-private:
- // constructor/destructor
- stream_buffer(u32 sample_rate = 48000);
- ~stream_buffer();
-
- // disable copying of stream_buffers directly
- stream_buffer(stream_buffer const &src) = delete;
- stream_buffer &operator=(stream_buffer const &rhs) = delete;
-
- // return the current sample rate
- u32 sample_rate() const { return m_sample_rate; }
-
- // set a new sample rate
- void set_sample_rate(u32 rate, bool resample);
-
- // return the current sample period in attoseconds
- attoseconds_t sample_period_attoseconds() const { return m_sample_attos; }
- attotime sample_period() const { return attotime(0, m_sample_attos); }
-
- // return the attotime of the current end of buffer
- attotime end_time() const { return index_time(m_end_sample); }
-
- // set the ending time (for forced resyncs; generally not used)
- void set_end_time(attotime time)
- {
- m_end_second = time.seconds();
- m_end_sample = u32(time.attoseconds() / m_sample_attos);
- }
-
- // return the effective buffer size; currently it is a full second of audio
- // at the current sample rate, but this maybe change in the future
- u32 size() const { return m_sample_rate; }
-
- // read the sample at the given index (clamped); should be valid in all cases
- sample_t get(s32 index) const
- {
- sound_assert(u32(index) < size());
- sample_t value = m_buffer[index];
-#if (SOUND_DEBUG)
- sound_assert(!std::isnan(value));
-#endif
- return value;
- }
-
- // write the sample at the given index (clamped)
- void put(s32 index, sample_t data)
- {
- sound_assert(u32(index) < size());
- m_buffer[index] = data;
- }
-
- // simple helpers to step indexes
- u32 next_index(u32 index) { index++; return (index == size()) ? 0 : index; }
- u32 prev_index(u32 index) { return (index == 0) ? (size() - 1) : (index - 1); }
-
- // clamp an index to the size of the buffer; allows for indexing +/- one
- // buffers' worth of range
- u32 clamp_index(s32 index) const
- {
- if (index < 0)
- index += size();
- else if (index >= size())
- index -= size();
- sound_assert(index >= 0 && index < size());
- return index;
- }
-
- // fill the buffer with the given value
- void fill(sample_t value) { std::fill_n(&m_buffer[0], m_buffer.size(), value); }
-
- // return the attotime of a given index within the buffer
- attotime index_time(s32 index) const;
-
- // given an attotime, return the buffer index corresponding to it
- u32 time_to_buffer_index(attotime time, bool round_up, bool allow_expansion = false);
-
- // downsample from our buffer into a temporary buffer
- void backfill_downsample(sample_t *dest, int samples, attotime newend, attotime newperiod);
-
- // upsample from a temporary buffer into our buffer
- void backfill_upsample(sample_t const *src, int samples, attotime prevend, attotime prevperiod);
-
- // internal state
- u32 m_end_second; // current full second of the buffer end
- u32 m_end_sample; // current sample number within the final second
- u32 m_sample_rate; // sample rate of the data in the buffer
- attoseconds_t m_sample_attos; // pre-computed attoseconds per sample
- std::vector<sample_t> m_buffer; // vector of actual buffer data
-
-#if (SOUND_DEBUG)
-public:
- // for debugging, provide an interface to write a WAV stream
- void open_wav(char const *filename);
- void flush_wav();
-
-private:
- // internal debugging state
- util::wav_file_ptr m_wav_file; // pointer to the current WAV file
- u32 m_last_written = 0; // last written sample index
-#endif
-};
-
-
-// ======================> read_stream_view
-
-class read_stream_view
-{
-public:
- using sample_t = stream_buffer::sample_t;
-
-protected:
- // private constructor used by write_stream_view that allows for expansion
- read_stream_view(stream_buffer &buffer, attotime start, attotime end) :
- read_stream_view(&buffer, 0, buffer.time_to_buffer_index(end, true, true), 1.0)
- {
- // start has to be set after end, since end can expand the buffer and
- // potentially invalidate start
- m_start = buffer.time_to_buffer_index(start, false);
- normalize_start_end();
- }
-
-public:
- // base constructor to simplify some of the code
- read_stream_view(stream_buffer *buffer, s32 start, s32 end, sample_t gain) :
- m_buffer(buffer),
- m_end(end),
- m_start(start),
- m_gain(gain)
- {
- normalize_start_end();
- }
-
- // empty constructor so we can live in an array or vector
- read_stream_view() :
- read_stream_view(nullptr, 0, 0, 1.0)
- {
- }
-
- // constructor that covers the given time period
- read_stream_view(stream_buffer &buffer, attotime start, attotime end, sample_t gain) :
- read_stream_view(&buffer, buffer.time_to_buffer_index(start, false), buffer.time_to_buffer_index(end, true), gain)
- {
- }
-
- // copy constructor
- read_stream_view(read_stream_view const &src) :
- read_stream_view(src.m_buffer, src.m_start, src.m_end, src.m_gain)
- {
- }
-
- // copy constructor that sets a different start time
- read_stream_view(read_stream_view const &src, attotime start) :
- read_stream_view(src.m_buffer, src.m_buffer->time_to_buffer_index(start, false), src.m_end, src.m_gain)
- {
- }
-
- // copy assignment
- read_stream_view &operator=(read_stream_view const &rhs)
- {
- m_buffer = rhs.m_buffer;
- m_start = rhs.m_start;
- m_end = rhs.m_end;
- m_gain = rhs.m_gain;
- normalize_start_end();
- return *this;
- }
-
- // return the local gain
- sample_t gain() const { return m_gain; }
-
- // return the sample rate of the data
- u32 sample_rate() const { return m_buffer->sample_rate(); }
-
- // return the sample period (in attoseconds) of the data
- attoseconds_t sample_period_attoseconds() const { return m_buffer->sample_period_attoseconds(); }
- attotime sample_period() const { return m_buffer->sample_period(); }
-
- // return the number of samples represented by the buffer
- u32 samples() const { return m_end - m_start; }
-
- // return the starting or ending time of the buffer
- attotime start_time() const { return m_buffer->index_time(m_start); }
- attotime end_time() const { return m_buffer->index_time(m_end); }
-
- // set the gain
- read_stream_view &set_gain(float gain) { m_gain = gain; return *this; }
-
- // apply an additional gain factor
- read_stream_view &apply_gain(float gain) { m_gain *= gain; return *this; }
-
- // safely fetch a gain-scaled sample from the buffer
- sample_t get(s32 index) const
- {
- sound_assert(u32(index) < samples());
- index += m_start;
- if (index >= m_buffer->size())
- index -= m_buffer->size();
- return m_buffer->get(index) * m_gain;
- }
-
- // safely fetch a raw sample from the buffer; if you use this, you need to
- // apply the gain yourself for correctness
- sample_t getraw(s32 index) const
- {
- sound_assert(u32(index) < samples());
- index += m_start;
- if (index >= m_buffer->size())
- index -= m_buffer->size();
- return m_buffer->get(index);
- }
-
-protected:
- // normalize start/end
- void normalize_start_end()
- {
- // ensure that end is always greater than start; we'll
- // wrap to the buffer length as needed
- if (m_end < m_start && m_buffer != nullptr)
- m_end += m_buffer->size();
- sound_assert(m_end >= m_start);
- }
-
- // internal state
- stream_buffer *m_buffer; // pointer to the stream buffer we're viewing
- s32 m_end; // ending sample index (always >= start)
- s32 m_start; // starting sample index
- sample_t m_gain; // overall gain factor
-};
-
-
-// ======================> write_stream_view
-
-class write_stream_view : public read_stream_view
-{
-
-public:
- // empty constructor so we can live in an array or vector
- write_stream_view()
- {
- }
-
- // constructor that covers the given time period
- write_stream_view(stream_buffer &buffer, attotime start, attotime end) :
- read_stream_view(buffer, start, end)
- {
- }
-
- // constructor that converts from a read_stream_view
- write_stream_view(read_stream_view const &src) :
- read_stream_view(src)
- {
- }
-
- // safely write a sample to the buffer
- void put(s32 start, sample_t sample)
- {
- sound_assert(u32(start) < samples());
- m_buffer->put(index_to_buffer_index(start), sample);
- }
-
- // write a sample to the buffer, clamping to +/- the clamp value
- void put_clamp(s32 index, sample_t sample, sample_t clamp = 1.0)
- {
- assert(clamp >= sample_t(0));
- put(index, std::clamp(sample, -clamp, clamp));
- }
-
- // write a sample to the buffer, converting from an integer with the given maximum
- void put_int(s32 index, s32 sample, s32 max)
- {
- put(index, sample_t(sample) * (1.0f / sample_t(max)));
- }
-
- // write a sample to the buffer, converting from an integer with the given maximum
- void put_int_clamp(s32 index, s32 sample, s32 maxclamp)
- {
- assert(maxclamp >= 0);
- put_int(index, std::clamp(sample, -maxclamp, maxclamp), maxclamp);
- }
-
- // safely add a sample to the buffer
- void add(s32 start, sample_t sample)
- {
- sound_assert(u32(start) < samples());
- u32 index = index_to_buffer_index(start);
- m_buffer->put(index, m_buffer->get(index) + sample);
- }
-
- // add a sample to the buffer, converting from an integer with the given maximum
- void add_int(s32 index, s32 sample, s32 max)
- {
- add(index, sample_t(sample) * (1.0f / sample_t(max)));
- }
-
- // fill part of the view with the given value
- void fill(sample_t value, s32 start, s32 count)
- {
- if (start + count > samples())
- count = samples() - start;
- u32 index = index_to_buffer_index(start);
- for (s32 sampindex = 0; sampindex < count; sampindex++)
- {
- m_buffer->put(index, value);
- index = m_buffer->next_index(index);
- }
- }
- void fill(sample_t value, s32 start) { fill(value, start, samples() - start); }
- void fill(sample_t value) { fill(value, 0, samples()); }
-
- // copy data from another view
- void copy(read_stream_view const &src, s32 start, s32 count)
- {
- if (start + count > samples())
- count = samples() - start;
- u32 index = index_to_buffer_index(start);
- for (s32 sampindex = 0; sampindex < count; sampindex++)
- {
- m_buffer->put(index, src.get(start + sampindex));
- index = m_buffer->next_index(index);
- }
- }
- void copy(read_stream_view const &src, s32 start) { copy(src, start, samples() - start); }
- void copy(read_stream_view const &src) { copy(src, 0, samples()); }
-
- // add data from another view to our current values
- void add(read_stream_view const &src, s32 start, s32 count)
- {
- if (start + count > samples())
- count = samples() - start;
- u32 index = index_to_buffer_index(start);
- for (s32 sampindex = 0; sampindex < count; sampindex++)
- {
- m_buffer->put(index, m_buffer->get(index) + src.get(start + sampindex));
- index = m_buffer->next_index(index);
- }
- }
- void add(read_stream_view const &src, s32 start) { add(src, start, samples() - start); }
- void add(read_stream_view const &src) { add(src, 0, samples()); }
-
-private:
- // given a stream starting offset, return the buffer index
- u32 index_to_buffer_index(s32 start) const
- {
- u32 index = start + m_start;
- if (index >= m_buffer->size())
- index -= m_buffer->size();
- return index;
- }
-};
-
-
-// ======================> sound_stream_output
-
-class sound_stream_output
-{
-#if (SOUND_DEBUG)
- friend class sound_stream;
-#endif
-
-public:
- // construction/destruction
- sound_stream_output();
-
- // initialization
- void init(sound_stream &stream, u32 index, char const *tag_base);
-
- // no copying allowed
- sound_stream_output(sound_stream_output const &src) = delete;
- sound_stream_output &operator=(sound_stream_output const &rhs) = delete;
-
- // simple getters
- sound_stream &stream() const { sound_assert(m_stream != nullptr); return *m_stream; }
- attotime end_time() const { return m_buffer.end_time(); }
- u32 index() const { return m_index; }
- stream_buffer::sample_t gain() const { return m_gain; }
- u32 buffer_sample_rate() const { return m_buffer.sample_rate(); }
-
- // simple setters
- void set_gain(float gain) { m_gain = gain; }
-
- // return a friendly name
- std::string name() const;
-
- // handle a changing sample rate
- void sample_rate_changed(u32 rate) { m_buffer.set_sample_rate(rate, true); }
-
- // return an output view covering a time period
- write_stream_view view(attotime start, attotime end) { return write_stream_view(m_buffer, start, end); }
-
- // resync the buffer to the given end time
- void set_end_time(attotime end) { m_buffer.set_end_time(end); }
-
- // attempt to optimize resamplers by reusing them where possible
- sound_stream_output &optimize_resampler(sound_stream_output *input_resampler);
-
-private:
- // internal state
- sound_stream *m_stream; // owning stream
- stream_buffer m_buffer; // output buffer
- u32 m_index; // output index within the stream
- stream_buffer::sample_t m_gain; // gain to apply to the output
- std::vector<sound_stream_output *> m_resampler_list; // list of resamplers we're connected to
-};
-
-
-// ======================> sound_stream_input
-
-class sound_stream_input
-{
-#if (SOUND_DEBUG)
- friend class sound_stream;
-#endif
-
-public:
- // construction/destruction
- sound_stream_input();
-
- // initialization
- void init(sound_stream &stream, u32 index, char const *tag_base, sound_stream_output *resampler);
-
- // no copying allowed
- sound_stream_input(sound_stream_input const &src) = delete;
- sound_stream_input &operator=(sound_stream_input const &rhs) = delete;
-
- // simple getters
- bool valid() const { return (m_native_source != nullptr); }
- sound_stream &owner() const { sound_assert(valid()); return *m_owner; }
- sound_stream_output &source() const { sound_assert(valid()); return *m_native_source; }
- u32 index() const { return m_index; }
- stream_buffer::sample_t gain() const { return m_gain; }
- stream_buffer::sample_t user_gain() const { return m_user_gain; }
-
- // simple setters
- void set_gain(float gain) { m_gain = gain; }
- void set_user_gain(float gain) { m_user_gain = gain; }
-
- // return a friendly name
- std::string name() const;
-
- // connect the source
- void set_source(sound_stream_output *source);
-
- // update and return an reading view
- read_stream_view update(attotime start, attotime end);
-
- // tell inputs to apply sample rate changes
- void apply_sample_rate_changes(u32 updatenum, u32 downstream_rate);
-
-private:
- // internal state
- sound_stream *m_owner; // pointer to the owning stream
- sound_stream_output *m_native_source; // pointer to the native sound_stream_output
- sound_stream_output *m_resampler_source; // pointer to the resampler output
- u32 m_index; // input index within the stream
- stream_buffer::sample_t m_gain; // gain to apply to this input
- stream_buffer::sample_t m_user_gain; // user-controlled gain to apply to this input
-};
-
-
-// ======================> stream_update_delegate
-
-// new-style callback
-using stream_update_delegate = delegate<void (sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)>;
-
+using stream_update_delegate = delegate<void (sound_stream &stream)>;
+class audio_effect;
+class audio_resampler;
// ======================> sound_stream_flags
@@ -592,86 +108,200 @@ enum sound_stream_flags : u32
// 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,
-
- // specify that input streams should not be resampled; stream update handler
- // must be able to accommodate multiple strams of differing input rates
- STREAM_DISABLE_INPUT_RESAMPLING = 0x02
+ STREAM_SYNCHRONOUS = 0x01
};
+namespace emu::detail {
+ template<typename S> class output_buffer_interleaved {
+ public:
+ 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;
+ };
+
+ template<typename S> class output_buffer_flat {
+ public:
+ 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;
+ };
+}
// ======================> sound_stream
class sound_stream
{
+public:
friend class sound_manager;
+ using sample_t = float;
- // private common constructopr
- sound_stream(device_t &device, u32 inputs, u32 outputs, u32 output_base, u32 sample_rate, sound_stream_flags flags);
-
-public:
// construction/destruction
- sound_stream(device_t &device, u32 inputs, u32 outputs, u32 output_base, u32 sample_rate, stream_update_delegate callback, sound_stream_flags flags = STREAM_DEFAULT_FLAGS);
+ 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();
// simple getters
- sound_stream *next() const { return m_next; }
device_t &device() const { return m_device; }
std::string name() const { return m_name; }
- bool input_adaptive() const { return m_input_adaptive || m_synchronous; }
+ bool input_adaptive() const { return m_input_adaptive; }
bool output_adaptive() const { return m_output_adaptive; }
bool synchronous() const { return m_synchronous; }
- bool resampling_disabled() const { return m_resampling_disabled; }
+ bool is_active() const { return m_sample_rate != 0; }
// input and output getters
- u32 input_count() const { return m_input.size(); }
- u32 output_count() const { return m_output.size(); }
- u32 output_base() const { return m_output_base; }
- sound_stream_input &input(int index) { sound_assert(index >= 0 && index < m_input.size()); return m_input[index]; }
- sound_stream_output &output(int index) { sound_assert(index >= 0 && index < m_output.size()); return m_output[index]; }
+ 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_pending_sample_rate != SAMPLE_RATE_INVALID) ? m_pending_sample_rate : m_sample_rate; }
- attotime sample_time() const { return m_output[0].end_time(); }
- attotime sample_period() const { return attotime(0, sample_period_attoseconds()); }
- attoseconds_t sample_period_attoseconds() const { return (m_sample_rate != SAMPLE_RATE_INVALID) ? HZ_TO_ATTOSECONDS(m_sample_rate) : ATTOSECONDS_PER_SECOND; }
-
- // set the sample rate of the stream; will kick in at the next global update
+ 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(); }
+ attotime start_time() const { return sample_to_time(start_index()); }
+ attotime end_time() const { return sample_to_time(end_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);
- // connect the output 'outputnum' of given input_stream to this stream's input 'inputnum'
- void set_input(int inputnum, sound_stream *input_stream, int outputnum = 0, float gain = 1.0f);
-
// force an update to the current time
void update();
- // force an update to the current time, returning a view covering the given time period
- read_stream_view update_view(attotime start, attotime end, u32 outputnum = 0);
+ // number of samples to handle
+ s32 samples() const { return m_samples_to_update; }
- // apply any pending sample rate changes; should only be called by the sound manager
- void apply_sample_rate_changes(u32 updatenum, u32 downstream_rate);
+ // write a sample to the buffer
+ void put(s32 output, s32 index, sample_t sample) { *m_output_buffer.ptrw(output, index) = sample; }
-#if (SOUND_DEBUG)
- // print one level of the sound graph and recursively tell our inputs to do the same
- void print_graph_recursive(int indent, int index);
-#endif
+ // 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)); }
-protected:
- // protected state
- std::string m_name; // name of this stream
+ // 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:
- // perform most of the initialization here
- void init_common(u32 inputs, u32 outputs, u32 sample_rate, sound_stream_flags flags);
+ struct route_bw {
+ sound_stream *m_source;
+ int m_output;
+ int m_input;
+ float m_gain;
+ const audio_resampler *m_resampler;
- // if the sample rate has changed, this gets called to update internals
- void sample_rate_changed();
+ 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) {}
+ };
+
+ 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) {}
+ };
- // handle updates after a save state load
- void postload();
- // handle updates before a save state load
- void presave();
+ // perform most of the initialization here
+ void init();
// re-print the synchronization timer
void reprime_sync_timer();
@@ -679,68 +309,58 @@ private:
// timer callback for synchronous streams
void sync_update(s32);
- // return a view of 0 data covering the given time period
- read_stream_view empty_view(attotime start, attotime end);
+ void update_nodeps();
+ void sync(attotime now);
+ u64 get_current_sample_index() const;
+ void do_update();
+
+ 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
- sound_stream *m_next; // next stream in the chain
+ 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 live sample rate
- u32 m_pending_sample_rate; // pending sample rate for dynamic changes
- u32 m_last_sample_rate_update; // update number of last sample rate change
+ 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_resampling_disabled; // is resampling of input streams disabled?
+ bool m_started;
+ bool m_in_update;
emu_timer *m_sync_timer; // update timer for synchronous streams
- attotime m_last_update_end_time; // last end_time() in update
-
- // input information
- std::vector<sound_stream_input> m_input; // list of streams we directly depend upon
- std::vector<read_stream_view> m_input_view; // array of output views for passing to the callback
- std::vector<std::unique_ptr<sound_stream>> m_resampler_list; // internal list of resamplers
- stream_buffer m_empty_buffer; // empty buffer for invalid inputs
-
- // output information
- u32 m_output_base; // base index of our outputs, relative to our device
- std::vector<sound_stream_output> m_output; // list of streams which directly depend upon us
- std::vector<write_stream_view> m_output_view; // array of output views for passing to the callback
-
// callback information
- stream_update_delegate m_callback_ex; // extended callback function
-};
-
-
-// ======================> default_resampler_stream
-
-class default_resampler_stream : public sound_stream
-{
-public:
- // construction/destruction
- default_resampler_stream(device_t &device);
-
- // update handler
- void resampler_sound_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs);
-
-private:
- // internal state
- u32 m_max_latency;
+ stream_update_delegate m_callback; // update callback function
};
-// ======================> sound_manager
-
-// 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
-};
-
class sound_manager
{
friend class sound_stream;
@@ -755,6 +375,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
@@ -763,14 +409,13 @@ 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; }
- int sample_count() const { return m_samples_this_update; }
int unique_id() { return m_unique_id++; }
- stream_buffer::sample_t compressor_scale() const { return m_compressor_scale; }
- // allocate a new stream with a new-style callback
+ const typename osd::audio_info &get_osd_info() const { return m_osd_info; }
+ const std::vector<mapping> &get_mappings() const { return m_mappings; }
+
+ // 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
@@ -778,9 +423,21 @@ public:
bool start_recording();
bool start_recording(std::string_view filename);
void stop_recording();
-
- // set the global OSD attenuation level
- void set_attenuation(float attenuation);
+ 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); }
@@ -791,22 +448,132 @@ public:
void debugger_mute(bool turn_off) { mute(turn_off, MUTE_REASON_DEBUGGER); }
void system_mute(bool turn_off) { mute(turn_off, MUTE_REASON_SYSTEM); }
- // return information about the given mixer input, by index
- bool indexed_mixer_input(int index, mixer_input &info) const;
+ // 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();
- // fill the given buffer with 16-bit stereo audio samples
- void samples(s16 *buffer);
+ 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;
+
+ 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:
+ 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);
- // helper to remove items from the orphan list
- void recursive_remove_stream_from_orphan_list(sound_stream *stream);
-
- // apply pending sample rate changes
- void apply_sample_rate_changes();
-
// reset all sound chips
void reset();
@@ -818,39 +585,83 @@ private:
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);
- // helper to adjust scale factor toward a goal
- stream_buffer::sample_t adjust_toward_compressor_scale(stream_buffer::sample_t curscale, stream_buffer::sample_t prevsample, stream_buffer::sample_t rawsample);
-
// periodic sound update, called STREAMS_UPDATE_FREQUENCY per second
- void update(s32 param = 0);
+ 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);
+
// internal state
- running_machine &m_machine; // reference to the running machine
- emu_timer *m_update_timer; // timer that runs the update function
- std::vector<std::reference_wrapper<speaker_device> > m_speakers;
-
- u32 m_update_number; // current update index; used for sample rate updates
- attotime m_last_update; // time of the last update
- u32 m_finalmix_leftover; // leftover samples in the final mix
- u32 m_samples_this_update; // number of samples this update
- std::vector<s16> m_finalmix; // final mix, in 16-bit signed format
- std::vector<stream_buffer::sample_t> m_leftmix; // left speaker mix, in native format
- std::vector<stream_buffer::sample_t> m_rightmix; // right speaker mix, in native format
-
- stream_buffer::sample_t m_compressor_scale; // current compressor scale factor
- int m_compressor_counter; // compressor update counter for backoff
- bool m_compressor_enabled; // enable compressor (it will still be calculated for detecting overdrive)
-
- u8 m_muted; // bitmask of muting reasons
- bool m_nosound_mode; // true if we're in "nosound" mode
- int m_attenuation; // current attentuation level (at the OSD)
- int m_unique_id; // unique ID used for stream identification
- util::wav_file_ptr m_wavfile; // WAV file for streaming
+ 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
- std::map<sound_stream *, u8> m_orphan_stream_list; // list of orphaned streams
- bool m_first_reset; // is this our first reset?
+ 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 33b1e8d329a..d57f9119c24 100644
--- a/src/emu/speaker.cpp
+++ b/src/emu/speaker.cpp
@@ -5,6 +5,7 @@
speaker.cpp
Speaker output sound device.
+ Microphone input sound device.
***************************************************************************/
@@ -14,210 +15,80 @@
-//**************************************************************************
-// GLOBAL VARIABLES
-//**************************************************************************
-
-// device type definition
DEFINE_DEVICE_TYPE(SPEAKER, speaker_device, "speaker", "Speaker")
-
-
-
-//**************************************************************************
-// 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)
- , m_pan(0.0)
- , m_defpan(0.0)
- , m_current_max(0)
- , m_samples_this_bucket(0)
+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);
}
-
-//-------------------------------------------------
-// ~speaker_device - destructor
-//-------------------------------------------------
-
-speaker_device::~speaker_device()
+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;
}
-
-//-------------------------------------------------
-// set_position - set speaker position
-//-------------------------------------------------
-
-speaker_device &speaker_device::set_position(double x, double y, double z)
+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)
{
- // as mentioned in the header file, y and z params currently have no effect
- m_x = x;
- m_y = y;
- m_z = z;
-
- // hard pan to left
- if (m_x < 0.0)
- set_pan(-1.0f);
-
- // hard pan to right
- else if (m_x > 0.0)
- set_pan(1.0f);
-
- // center (mono)
- else
- set_pan(0.0f);
-
- m_defpan = m_pan;
- return *this;
}
-//-------------------------------------------------
-// mix - mix in samples from the speaker's stream
-//-------------------------------------------------
+sound_io_device::~sound_io_device()
+{
+}
-void speaker_device::mix(stream_buffer::sample_t *leftmix, stream_buffer::sample_t *rightmix, attotime start, attotime end, int expected_samples, bool suppress)
+speaker_device::~speaker_device()
{
- // skip if no stream
- if (m_mixer_stream == nullptr)
- return;
-
- // skip if invalid range
- if (start > end)
- return;
-
- // get a view on the desired range
- read_stream_view view = m_mixer_stream->update_view(start, end);
- sound_assert(view.samples() >= expected_samples);
-
- // track maximum sample value for each 0.1s bucket
- if (machine().options().speaker_report() != 0)
- {
- u32 samples_per_bucket = m_mixer_stream->sample_rate() / BUCKETS_PER_SECOND;
- for (int sample = 0; sample < expected_samples; sample++)
- {
- m_current_max = std::max(m_current_max, fabsf(view.get(sample)));
- if (++m_samples_this_bucket >= samples_per_bucket)
- {
- m_max_sample.push_back(m_current_max);
- m_current_max = 0.0f;
- m_samples_this_bucket = 0;
- }
- }
- }
-
- // mix if sound is enabled
- if (!suppress)
- {
- // if the speaker is hard panned to the left, send only to the left
- if (m_pan == -1.0f)
- for (int sample = 0; sample < expected_samples; sample++)
- leftmix[sample] += view.get(sample);
-
- // if the speaker is hard panned to the right, send only to the right
- else if (m_pan == 1.0f)
- for (int sample = 0; sample < expected_samples; sample++)
- rightmix[sample] += view.get(sample);
-
- // otherwise, send to both
- else
- {
- const float leftpan = (m_pan <= 0.0f) ? 1.0f : 1.0f - m_pan;
- const float rightpan = (m_pan >= 0.0f) ? 1.0f : 1.0f + m_pan;
-
- for (int sample = 0; sample < expected_samples; sample++)
- {
- stream_buffer::sample_t cursample = view.get(sample);
- leftmix[sample] += cursample * leftpan;
- rightmix[sample] += cursample * rightpan;
- }
- }
- }
}
+microphone_device::~microphone_device()
+{
+}
-//-------------------------------------------------
-// device_start - handle device startup
-//-------------------------------------------------
void speaker_device::device_start()
{
+ 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_stop - cleanup and report
-//-------------------------------------------------
+void speaker_device::sound_stream_update(sound_stream &stream)
+{
+ machine().sound().output_push(m_id, stream);
+}
-void speaker_device::device_stop()
+void microphone_device::sound_stream_update(sound_stream &stream)
{
- // level 1: just report if there was any clipping
- // level 2: report the overall maximum, even if no clipping
- // level 3: print a detailed list of all the times there was clipping
- // level 4: print a detailed list of every bucket
- int report = machine().options().speaker_report();
- if (report != 0)
- {
- m_max_sample.push_back(m_current_max);
-
- // determine overall maximum and number of clipped buckets
- stream_buffer::sample_t overallmax = 0;
- u32 clipped = 0;
- for (auto &curmax : m_max_sample)
- {
- overallmax = std::max(overallmax, curmax);
- if (curmax > stream_buffer::sample_t(1.0))
- clipped++;
- }
-
- // levels 1 and 2 just get a summary
- if (clipped != 0 || report == 2 || report == 4)
- osd_printf_info("Speaker \"%s\" - max = %.5f (gain *= %.3f) - clipped in %d/%d (%d%%) buckets\n", tag(), overallmax, 1 / (overallmax ? overallmax : 1), clipped, m_max_sample.size(), clipped * 100 / m_max_sample.size());
-
- // levels 3 and 4 get a full dump
- if (report >= 3)
- {
- static char const * const s_stars = "************************************************************";
- static char const * const s_spaces = " ";
- int totalstars = strlen(s_stars);
- double t = 0;
- if (overallmax < 1.0)
- overallmax = 1.0;
- int leftstars = totalstars / overallmax;
- for (auto &curmax : m_max_sample)
- {
- if (curmax > stream_buffer::sample_t(1.0) || report == 4)
- {
- osd_printf_info("%6.1f: %9.5f |", t, curmax);
- if (curmax == 0)
- osd_printf_info("%.*s|\n", leftstars, s_spaces);
- else if (curmax <= 1.0)
- {
- int stars = std::max(1, std::min(leftstars, int(curmax * totalstars / overallmax)));
- osd_printf_info("%.*s", stars, s_stars);
- int spaces = leftstars - stars;
- if (spaces != 0)
- osd_printf_info("%.*s", spaces, s_spaces);
- osd_printf_info("|\n");
- }
- else
- {
- int rightstars = std::max(1, std::min(totalstars, int(curmax * totalstars / overallmax)) - leftstars);
- osd_printf_info("%.*s|%.*s\n", leftstars, s_stars, rightstars, s_stars);
- }
- }
- t += 1.0 / double(BUCKETS_PER_SECOND);
- }
- }
- }
+ machine().sound().input_get(m_id, stream);
}
diff --git a/src/emu/speaker.h b/src/emu/speaker.h
index 3cb0794d998..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,65 +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[];
-class speaker_device : public device_t, public device_mixer_interface
+ // configuration state
+ std::vector<std::array<double, 3>> m_positions;
+ sound_stream *m_stream;
+ int m_id;
+
+ 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);
+ : 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) {}
+
virtual ~speaker_device();
- // configuration helpers
- speaker_device &set_position(double x, double y, double z);
- 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_floor() { set_position( 0.0, -0.5, 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 &headrest_center() { set_position( 0.0, 0.0, -0.1); return *this; }
- speaker_device &headrest_left() { set_position(-0.1, 0.0, -0.1); return *this; }
- speaker_device &headrest_right() { set_position( 0.1, 0.0, -0.1); return *this; }
- speaker_device &seat() { set_position( 0.0, -0.5, 0.0); return *this; }
- speaker_device &backrest() { set_position( 0.0, -0.2, 0.1); return *this; }
-
- // internally for use by the sound system
- void mix(stream_buffer::sample_t *leftmix, stream_buffer::sample_t *rightmix, attotime start, attotime end, int expected_samples, bool suppress);
-
- // user panning configuration
- void set_pan(float pan) { m_pan = std::clamp(pan, -1.0f, 1.0f); }
- float pan() { return m_pan; }
- float defpan() { return m_defpan; }
+ virtual bool is_output() const override { return true; }
protected:
+
// device-level overrides
virtual void device_start() override ATTR_COLD;
- virtual void device_stop() override ATTR_COLD;
- // configuration state
- double m_x;
- double m_y;
- double m_z;
- float m_pan;
- float m_defpan;
-
- // internal state
- static constexpr int BUCKETS_PER_SECOND = 10;
- std::vector<stream_buffer::sample_t> m_max_sample;
- stream_buffer::sample_t m_current_max;
- u32 m_samples_this_bucket;
+ 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
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 1aa18eef168..8c4e20e158f 100644
--- a/src/emu/tilemap.cpp
+++ b/src/emu/tilemap.cpp
@@ -1556,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
diff --git a/src/emu/tilemap.h b/src/emu/tilemap.h
index b6b2c566bfa..3fc1a353315 100644
--- a/src/emu/tilemap.h
+++ b/src/emu/tilemap.h
@@ -754,7 +754,7 @@ public:
protected:
// device-level overrides
- virtual void device_start() override;
+ virtual void device_start() override ATTR_COLD;
private:
// devices
diff --git a/src/emu/ui/uimain.h b/src/emu/ui/uimain.h
index bf10957d743..e45c71f9468 100644
--- a/src/emu/ui/uimain.h
+++ b/src/emu/ui/uimain.h
@@ -13,6 +13,8 @@
#pragma once
+#include <functional>
+
/***************************************************************************
TYPE DEFINITIONS
@@ -35,6 +37,8 @@ public:
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:
diff --git a/src/emu/uiinput.cpp b/src/emu/uiinput.cpp
index 138bf67055d..12ba8a08bb0 100644
--- a/src/emu/uiinput.cpp
+++ b/src/emu/uiinput.cpp
@@ -36,19 +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_x(-1)
- , m_current_mouse_y(-1)
- , m_current_mouse_down(false)
- , m_current_mouse_field(nullptr)
, m_events_start(0)
, m_events_end(0)
{
std::fill(std::begin(m_next_repeat), std::end(m_next_repeat), 0);
std::fill(std::begin(m_seqpressed), std::end(m_seqpressed), 0);
-
- // add a frame callback to poll inputs
- machine.add_notifier(MACHINE_NOTIFY_FRAME, machine_notify_delegate(&ui_input_manager::frame_update, this));
}
@@ -58,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)
@@ -80,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;
- }
}
@@ -106,37 +82,6 @@ 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::type::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::type::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::type::MOUSE_DOWN:
- m_current_mouse_down = true;
- break;
-
- case ui_event::type::MOUSE_UP:
- m_current_mouse_down = false;
- break;
-
- default:
- /* do nothing */
- break;
- }
-
// is the queue filled up?
if ((m_events_end + 1) % std::size(m_events) == m_events_start)
return false;
@@ -186,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
@@ -272,7 +165,7 @@ bool ui_input_manager::pressed_repeat(int code, int speed)
/* 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);
@@ -319,105 +212,84 @@ void ui_input_manager::push_window_defocus_event(render_target *target)
}
/*-------------------------------------------------
- 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::type::NONE };
- event.event_type = ui_event::type::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::type::NONE };
- event.event_type = ui_event::type::MOUSE_LEAVE;
- event.target = target;
- push_event(event);
-}
-
-/*-------------------------------------------------
- push_mouse_down_event - pushes a mouse
- down event to the specified render_target
--------------------------------------------------*/
-
-void ui_input_manager::push_mouse_down_event(render_target *target, s32 x, s32 y)
-{
- ui_event event = { ui_event::type::NONE };
- event.event_type = ui_event::type::MOUSE_DOWN;
- 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
--------------------------------------------------*/
-
-void ui_input_manager::push_mouse_up_event(render_target *target, s32 x, s32 y)
-{
- ui_event event = { ui_event::type::NONE };
- event.event_type = ui_event::type::MOUSE_UP;
- 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::type::NONE };
- event.event_type = ui_event::type::MOUSE_RDOWN;
+ 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::type::NONE };
- event.event_type = ui_event::type::MOUSE_RUP;
+ 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::type::NONE };
- event.event_type = ui_event::type::MOUSE_DOUBLE_CLICK;
+ 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);
}
@@ -439,7 +311,7 @@ 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::type::NONE };
event.event_type = ui_event::type::MOUSE_WHEEL;
@@ -447,7 +319,7 @@ void ui_input_manager::push_mouse_wheel_event(render_target *target, s32 x, s32
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 3fc89d7a4ff..54170bad7ec 100644
--- a/src/emu/uiinput.h
+++ b/src/emu/uiinput.h
@@ -13,6 +13,8 @@
#pragma once
+#include "interface/uievents.h"
+
/***************************************************************************
TYPE DEFINITIONS
@@ -25,17 +27,15 @@ struct ui_event
NONE,
WINDOW_FOCUS,
WINDOW_DEFOCUS,
- MOUSE_MOVE,
- MOUSE_LEAVE,
- MOUSE_DOWN,
- MOUSE_UP,
- MOUSE_RDOWN,
- MOUSE_RUP,
- MOUSE_DOUBLE_CLICK,
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;
@@ -44,17 +44,27 @@ 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
bool push_event(ui_event event);
@@ -68,17 +78,13 @@ public:
// 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
+ 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);
@@ -86,44 +92,32 @@ public:
running_machine &machine() const { return m_machine; }
// queueing events
- void push_window_focus_event(render_target *target);
- void push_window_defocus_event(render_target *target);
- 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);
+ 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 = 128;
+ 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 4cb6202b4d9..92b141f269e 100644
--- a/src/emu/validity.cpp
+++ b/src/emu/validity.cpp
@@ -22,6 +22,7 @@
#include "unicode.h"
#include <cctype>
+#include <sstream>
#include <type_traits>
#include <typeinfo>
@@ -174,13 +175,6 @@ void validate_integer_semantics()
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;
@@ -2150,6 +2144,49 @@ void validity_checker::validate_driver(device_t &root)
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());
+ }
+ }
}
@@ -2495,12 +2532,13 @@ void validity_checker::validate_inputs(device_t &root)
// 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);
+ {
+ // 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)
diff --git a/src/emu/xtal.cpp b/src/emu/xtal.cpp
index f5dc48a1f49..62d0370e661 100644
--- a/src/emu/xtal.cpp
+++ b/src/emu/xtal.cpp
@@ -57,450 +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 */
- 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'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'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'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'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'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'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'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'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'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'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'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'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'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'074'800, /* 17.0748_MHz_XTAL SWTPC 8212 */
- 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'686'000, /* 26.686_MHz_XTAL Typically used on 90's Taito PCBs to drive the custom chips */
- 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 */
- 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'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'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 */
- 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'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'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)*/
- 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'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.) */
+ 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;
@@ -583,369 +607,3 @@ void XTAL::fail(double base_clock, const std::string &message)
full_message += util::string_format(" Context: %s\n", message);
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
-
-*/