From 6f4c1f6e5b2525ac8b700b3a0754f5e99c4223e1 Mon Sep 17 00:00:00 2001 From: Vas Crabb Date: Fri, 1 Nov 2019 00:47:41 +1100 Subject: Spring cleaning: * Changed emu_fatalerror to use util::string_format semantics * Fixed some incorrectly marked up stuff in build scripts * Make internal layout compression type a scoped enum (only zlib is supported still, but at least the values aren't magic numbers now) * Fixed memory leaks in Xbox USB * There can only be one "perfect quantum" device - enforce that only the root machine can set it, as allowing subdevices to will cause weird issues with slot cards overiding it * Allow multiple devices to set maximum quantum and use the most restrictive one (it's maximum quantum, it would be minimum interleave) * Got rid of device_slot_card_interface as it wasn't providing value * Added a helper template to reduce certain kinds of boilerplate in slots/buses * Cleaned up some particularly bad slot code (plenty more of that to do), and made some slots more idiomatic --- src/emu/dislot.cpp | 59 +++++---------------- src/emu/dislot.h | 142 +++++++++++++++++++++++++++++++++++++++++++-------- src/emu/emucore.cpp | 74 +++------------------------ src/emu/emucore.h | 46 +++++++++++------ src/emu/emufwd.h | 2 +- src/emu/image.cpp | 16 +++--- src/emu/machine.cpp | 2 +- src/emu/main.h | 24 ++++----- src/emu/mconfig.cpp | 134 +++++++++++++++++++++++++++++++++++++++--------- src/emu/mconfig.h | 96 ++++++++++++++++++++++++++++------ src/emu/render.cpp | 9 ++-- src/emu/romload.cpp | 28 +++++----- src/emu/schedule.cpp | 26 ++-------- src/emu/validity.cpp | 4 +- 14 files changed, 407 insertions(+), 255 deletions(-) (limited to 'src/emu') diff --git a/src/emu/dislot.cpp b/src/emu/dislot.cpp index e5bb4cff0d3..9e8a5128996 100644 --- a/src/emu/dislot.cpp +++ b/src/emu/dislot.cpp @@ -11,10 +11,6 @@ #include "zippath.h" -// ------------------------------------------------- -// ctor -// ------------------------------------------------- - device_slot_interface::device_slot_interface(const machine_config &mconfig, device_t &device) : device_interface(device, "slot"), m_default_clock(DERIVED_CLOCK(1, 1)), @@ -24,20 +20,11 @@ device_slot_interface::device_slot_interface(const machine_config &mconfig, devi { } - -// ------------------------------------------------- -// dtor -// ------------------------------------------------- - device_slot_interface::~device_slot_interface() { } -// ------------------------------------------------- -// device_slot_option ctor -// ------------------------------------------------- - device_slot_interface::slot_option::slot_option(const char *name, const device_type &devtype, bool selectable) : m_name(name), m_devtype(devtype), @@ -50,44 +37,39 @@ device_slot_interface::slot_option::slot_option(const char *name, const device_t } -// ------------------------------------------------- -// option_add -// ------------------------------------------------- +void device_slot_interface::interface_validity_check(validity_checker &valid) const +{ + if (m_default_option && (m_options.find(m_default_option) == m_options.end())) + osd_printf_error("Default option '%s' does not correspond to any configured option\n", m_default_option); +} + device_slot_interface::slot_option &device_slot_interface::option_add(const char *name, const device_type &devtype) { + if (!name || !*name) + throw emu_fatalerror("slot '%s' attempt to add option without name\n", device().tag()); + const slot_option *const existing = option(name); if (existing) throw emu_fatalerror("slot '%s' duplicate option '%s'\n", device().tag(), name); - if (m_options.count(name)) - throw tag_add_exception(name); - return m_options.emplace(name, std::make_unique(name, devtype, true)).first->second->clock(m_default_clock); } -// ------------------------------------------------- -// option_add_internal -// ------------------------------------------------- - device_slot_interface::slot_option &device_slot_interface::option_add_internal(const char *name, const device_type &devtype) { + if (!name || !*name) + throw emu_fatalerror("slot '%s' attempt to add option without name\n", device().tag()); + const slot_option *const existing = option(name); if (existing) throw emu_fatalerror("slot '%s' duplicate option '%s'\n", device().tag(), name); - if (m_options.count(name)) - throw tag_add_exception(name); - return m_options.emplace(name, std::make_unique(name, devtype, false)).first->second->clock(m_default_clock); } -// ------------------------------------------------- -// option -// ------------------------------------------------- - device_slot_interface::slot_option *device_slot_interface::config_option(const char *name) { auto const search = m_options.find(name); @@ -98,10 +80,6 @@ device_slot_interface::slot_option *device_slot_interface::config_option(const c } -// ------------------------------------------------- -// has_selectable_options -// ------------------------------------------------- - bool device_slot_interface::has_selectable_options() const { if (!fixed()) @@ -114,10 +92,6 @@ bool device_slot_interface::has_selectable_options() const } -// ------------------------------------------------- -// option -// ------------------------------------------------- - const device_slot_interface::slot_option *device_slot_interface::option(const char *name) const { if (name) @@ -130,15 +104,6 @@ const device_slot_interface::slot_option *device_slot_interface::option(const ch } -device_slot_card_interface::device_slot_card_interface(const machine_config &mconfig, device_t &device) - : device_interface(device, "slot") -{ -} - -device_slot_card_interface::~device_slot_card_interface() -{ -} - get_default_card_software_hook::get_default_card_software_hook(const std::string &path, std::function &&get_hashfile_extrainfo) : m_get_hashfile_extrainfo(std::move(get_hashfile_extrainfo)) , m_called_get_hashfile_extrainfo(false) diff --git a/src/emu/dislot.h b/src/emu/dislot.h index da6a1b0aa3d..a88102f84e5 100644 --- a/src/emu/dislot.h +++ b/src/emu/dislot.h @@ -9,13 +9,10 @@ #ifndef MAME_EMU_DISLOT_H #define MAME_EMU_DISLOT_H - //************************************************************************** // TYPE DEFINITIONS //************************************************************************** -// ======================> get_default_card_software_hook - class get_default_card_software_hook { // goofy "hook" to pass to device_slot_interface::get_default_card_software @@ -42,8 +39,7 @@ private: }; -// ======================> device_slot_interface - +/// \brief Base class for configurable slot devices class device_slot_interface : public device_interface { public: @@ -77,20 +73,88 @@ public: u32 m_clock; }; - // construction/destruction - device_slot_interface(const machine_config &mconfig, device_t &device); virtual ~device_slot_interface(); + /// \brief Set whether slot is fixed + /// + /// Allows a slot to be configured as fixed. Fixed slots can only + /// be configured programmatically. Options for fixed slots are not + /// user-selectable. By default, slots are not fixed. + /// \param [in] fixed True to mark the slot as fixed, or false to + /// mark it user-configurable. + /// \sa fixed void set_fixed(bool fixed) { m_fixed = fixed; } + + /// \brief Set the default option + /// + /// Set the default option the slot. The corresponding card device + /// will be loaded if no other option is selected by the user or + /// programmatically. + /// \param [in] option The name of the default option. This must be + /// correspond to an option added using #option_add or + /// #option_add_internal, or be nullptr to not load any card + /// device by default. The string is not copied and must remain + /// valid for the lifetime of the device (or until another default + /// option is configured). + /// \sa default_option void set_default_option(const char *option) { m_default_option = option; } + + /// \brief Clear options + /// + /// This removes all previously added options. void option_reset() { m_options.clear(); } - slot_option &option_add(const char *option, const device_type &devtype); + + /// \brief Add a user-selectable option + /// + /// Adds an option that may be selected by the user via the command + /// line or other configureation methods. + /// \param [in] option The name used to select the option. This + /// will also be used as the tag when instantiating the card. It + /// must be a valid device tag, and should be terse but + /// descriptive. The string is copied when the option is added. + /// \param [in] devtype Device type of the option. The device type + /// description is used in the user interface. + /// \return A reference to the added option for additional + /// configuration. + slot_option &option_add(char const *option, const device_type &devtype); + + /// \brief Add an internal option + /// + /// Adds an option that may only be selected programmatically. This + /// is most often used for options used for loading software. + /// \param [in] option The name used to select the option. This + /// will also be used as the tag when instantiating the card. It + /// must be a valid device tag. The string is copied when the + /// option is added. + /// \param [in] devtype Device type of the option. The device type + /// description is used in the user interface. + /// \return A reference to the added option for additional + /// configuration. slot_option &option_add_internal(const char *option, const device_type &devtype); + void set_option_default_bios(const char *option, const char *default_bios) { config_option(option)->default_bios(default_bios); } template void set_option_machine_config(const char *option, T &&machine_config) { config_option(option)->machine_config(std::forward(machine_config)); } void set_option_device_input_defaults(const char *option, const input_device_default *default_input) { config_option(option)->input_device_defaults(default_input); } + + /// \brief Returns whether the slot is fixed + /// + /// Fixed slots can only be configured programmatically. Slots are + /// not fixed by default. + /// \return True if the slot is fixed, or false if it is + /// user-configurable. + /// \sa set_fixed bool fixed() const { return m_fixed; } + + /// \brief Returns true if the slot has user-selectable options + /// + /// Returns true if the slot is not marked as fixed and has at least + /// one user-selectable option (added using #option_add rather than + /// #option_add_internal). Returns false if the slot is marked as + /// fixed, all options are internal, or there are no options. + /// \return True if the slot has user-selectable options, or false + /// otherwise. bool has_selectable_options() const; + const char *default_option() const { return m_default_option; } const std::unordered_map> &option_list() const { return m_options; } const slot_option *option(const char *name) const; @@ -101,30 +165,68 @@ public: slot_option &option_set(const char *tag, const device_type &devtype) { m_default_option = tag; m_fixed = true; return option_add_internal(tag, devtype); } protected: + device_slot_interface(machine_config const &mconfig, device_t &device); + virtual void interface_validity_check(validity_checker &valid) const override ATTR_COLD; void set_default_clock(u32 clock) { m_default_clock = clock; } private: // internal state - std::unordered_map> m_options; + std::unordered_map> m_options; u32 m_default_clock; - const char *m_default_option; + char const *m_default_option; bool m_fixed; device_t *m_card_device; - slot_option *config_option(const char *option); + slot_option *config_option(char const *option); }; -// iterator -typedef device_interface_iterator slot_interface_iterator; - -// ======================> device_slot_card_interface -class device_slot_card_interface : public device_interface +/// \brief Base class for slots that accept a single card interface +/// +/// Performs basic validity checks to ensure the configured card (if +/// any) implements the required interface. +template +class device_single_card_slot_interface : public device_slot_interface { public: - // construction/destruction - device_slot_card_interface(const machine_config &mconfig, device_t &device); - virtual ~device_slot_card_interface(); + Card *get_card_device() const { return dynamic_cast(device_slot_interface::get_card_device()); } + +protected: + device_single_card_slot_interface(machine_config const &mconfig, device_t &device) : + device_slot_interface(mconfig, device) + { + } + + virtual void interface_validity_check(validity_checker &valid) const override ATTR_COLD + { + device_slot_interface::interface_validity_check(valid); + device_t *const card(device_slot_interface::get_card_device()); + if (card && !dynamic_cast(card)) + { + osd_printf_error( + "Card device %s (%s) does not implement %s\n", + card->tag(), + card->name(), + typeid(Card).name()); + } + } + + virtual void interface_pre_start() override ATTR_COLD + { + device_slot_interface::interface_pre_start(); + device_t *const card(device_slot_interface::get_card_device()); + if (card && !dynamic_cast(card)) + { + throw emu_fatalerror( + "slot '%s' card device %s (%s) does not implement %s\n", + device().tag(), + card->tag(), + card->name(), + typeid(Card).name()); + } + } }; -#endif /* MAME_EMU_DISLOT_H */ +typedef device_interface_iterator slot_interface_iterator; + +#endif // MAME_EMU_DISLOT_H diff --git a/src/emu/emucore.cpp b/src/emu/emucore.cpp index 35256b4f364..689d5fa1c37 100644 --- a/src/emu/emucore.cpp +++ b/src/emu/emucore.cpp @@ -14,60 +14,16 @@ const char *const endianness_names[2] = { "little", "big" }; -emu_fatalerror::emu_fatalerror(const char *format, ...) : code(0) +emu_fatalerror::emu_fatalerror(util::format_argument_pack const &args) + : emu_fatalerror(0, args) { - if (format == nullptr) - { - text[0] = '\0'; - } - else - { - va_list ap; - va_start(ap, format); - vsnprintf(text, sizeof(text), format, ap); - va_end(ap); - } - osd_break_into_debugger(text); + osd_break_into_debugger(m_text.c_str()); } -emu_fatalerror::emu_fatalerror(const char *format, va_list ap) : code(0) +emu_fatalerror::emu_fatalerror(int _exitcode, util::format_argument_pack const &args) + : m_text(util::string_format(args)) + , m_code(_exitcode) { - if (format == nullptr) - { - text[0] = '\0'; - } - else - { - vsnprintf(text, sizeof(text), format, ap); - } - osd_break_into_debugger(text); -} - -emu_fatalerror::emu_fatalerror(int _exitcode, const char *format, ...) : code(_exitcode) -{ - if (format == nullptr) - { - text[0] = '\0'; - } - else - { - va_list ap; - va_start(ap, format); - vsnprintf(text, sizeof(text), format, ap); - va_end(ap); - } -} - -emu_fatalerror::emu_fatalerror(int _exitcode, const char *format, va_list ap) : code(_exitcode) -{ - if (format == nullptr) - { - text[0] = '\0'; - } - else - { - vsnprintf(text, sizeof(text), format, ap); - } } @@ -82,21 +38,3 @@ void report_bad_device_cast(const device_t *dev, const std::type_info &src_type, throw emu_fatalerror("Error: bad downcast<> or device<>. Tried to convert the device %s (%s) of type %s to a %s, which are incompatible.\n", dev->tag(), dev->name(), src_type.name(), dst_type.name()); } - -void fatalerror(const char *format, ...) -{ - va_list ap; - va_start(ap, format); - emu_fatalerror error(format, ap); - va_end(ap); - throw error; -} - -void fatalerror_exitcode(running_machine &machine, int exitcode, const char *format, ...) -{ - va_list ap; - va_start(ap, format); - emu_fatalerror error(exitcode, format, ap); - va_end(ap); - throw error; -} diff --git a/src/emu/emucore.h b/src/emu/emucore.h index 92135e7ae84..ef6713c8d8f 100644 --- a/src/emu/emucore.h +++ b/src/emu/emucore.h @@ -29,6 +29,7 @@ // standard C++ includes #include #include +#include #include #include @@ -37,6 +38,7 @@ #include "emualloc.h" #include "corestr.h" #include "bitmap.h" +#include "strformat.h" #include "emufwd.h" @@ -247,17 +249,26 @@ class emu_exception : public std::exception { }; class emu_fatalerror : public emu_exception { public: - emu_fatalerror(const char *format, ...) ATTR_PRINTF(2,3); - emu_fatalerror(const char *format, va_list ap); - emu_fatalerror(int _exitcode, const char *format, ...) ATTR_PRINTF(3,4); - emu_fatalerror(int _exitcode, const char *format, va_list ap); - - const char *string() const { return text; } - int exitcode() const { return code; } + emu_fatalerror(util::format_argument_pack const &args); + emu_fatalerror(int _exitcode, util::format_argument_pack const &args); + + template + emu_fatalerror(Format const &fmt, Params &&... args) + : emu_fatalerror(static_cast const &>(util::make_format_argument_pack(fmt, std::forward(args)...))) + { + } + template + emu_fatalerror(int _exitcode, Format const &fmt, Params &&... args) + : emu_fatalerror(_exitcode, static_cast const &>(util::make_format_argument_pack(fmt, std::forward(args)...))) + { + } + + virtual char const *what() const noexcept override { return m_text.c_str(); } + int exitcode() const noexcept { return m_code; } private: - char text[1024]; - int code; + std::string m_text; + int m_code; }; class tag_add_exception @@ -269,12 +280,13 @@ private: std::string m_tag; }; + //************************************************************************** // CASTING TEMPLATES //************************************************************************** -void report_bad_cast(const std::type_info &src_type, const std::type_info &dst_type); -void report_bad_device_cast(const device_t *dev, const std::type_info &src_type, const std::type_info &dst_type); +[[noreturn]] void report_bad_cast(const std::type_info &src_type, const std::type_info &dst_type); +[[noreturn]] void report_bad_device_cast(const device_t *dev, const std::type_info &src_type, const std::type_info &dst_type); template inline std::enable_if_t::value> report_bad_cast(Source *const src) @@ -316,15 +328,15 @@ inline Dest downcast(Source &src) //************************************************************************** -// FUNCTION PROTOTYPES +// INLINE FUNCTIONS //************************************************************************** -[[noreturn]] void fatalerror(const char *format, ...) ATTR_PRINTF(1,2); -[[noreturn]] void fatalerror_exitcode(running_machine &machine, int exitcode, const char *format, ...) ATTR_PRINTF(3,4); +template +[[noreturn]] inline void fatalerror(T &&... args) +{ + throw emu_fatalerror(std::forward(args)...); +} -//************************************************************************** -// INLINE FUNCTIONS -//************************************************************************** // convert a series of 32 bits into a float inline float u2f(u32 v) diff --git a/src/emu/emufwd.h b/src/emu/emufwd.h index cb0af700c64..97955067865 100644 --- a/src/emu/emufwd.h +++ b/src/emu/emufwd.h @@ -178,7 +178,7 @@ struct ioport_port_live; class running_machine; // declared in mconfig.h -namespace emu { namespace detail { struct machine_config_replace; } } +namespace emu { namespace detail { class machine_config_replace; } } class machine_config; // declared in natkeyboard.h diff --git a/src/emu/image.cpp b/src/emu/image.cpp index 3013d03f668..e2cf6d91c83 100644 --- a/src/emu/image.cpp +++ b/src/emu/image.cpp @@ -73,11 +73,11 @@ image_manager::image_manager(running_machine &machine) if (machine.options().write_config()) write_config(machine.options(), nullptr, &machine.system()); - fatalerror_exitcode(machine, EMU_ERR_DEVICE, "Device %s load (-%s %s) failed: %s", - image.device().name(), - image.instance_name().c_str(), - startup_image_name.c_str(), - image_err.c_str()); + throw emu_fatalerror(EMU_ERR_DEVICE, "Device %s load (-%s %s) failed: %s", + image.device().name(), + image.instance_name(), + startup_image_name, + image_err); } } } @@ -244,9 +244,9 @@ void image_manager::postdevice_init() /* unload all images */ unload_all(); - fatalerror_exitcode(machine(), EMU_ERR_DEVICE, "Device %s load failed: %s", - image.device().name(), - image_err.c_str()); + throw emu_fatalerror(EMU_ERR_DEVICE, "Device %s load failed: %s", + image.device().name(), + image_err); } } /* add a callback for when we shut down */ diff --git a/src/emu/machine.cpp b/src/emu/machine.cpp index 1b00ddfcfd9..331d5a7905a 100644 --- a/src/emu/machine.cpp +++ b/src/emu/machine.cpp @@ -388,7 +388,7 @@ int running_machine::run(bool quiet) } catch (emu_fatalerror &fatal) { - osd_printf_error("Fatal error: %s\n", fatal.string()); + osd_printf_error("Fatal error: %s\n", fatal.what()); error = EMU_ERR_FATALERROR; if (fatal.exitcode() != 0) error = fatal.exitcode(); diff --git a/src/emu/main.h b/src/emu/main.h index a62457a1c75..2a088a3995d 100644 --- a/src/emu/main.h +++ b/src/emu/main.h @@ -23,19 +23,17 @@ // CONSTANTS //************************************************************************** -enum -{ - EMU_ERR_NONE = 0, /* no error */ - EMU_ERR_FAILED_VALIDITY = 1, /* failed validity checks */ - EMU_ERR_MISSING_FILES = 2, /* missing files */ - EMU_ERR_FATALERROR = 3, /* some other fatal error */ - EMU_ERR_DEVICE = 4, /* device initialization error (MESS-specific) */ - EMU_ERR_NO_SUCH_GAME = 5, /* game was specified but doesn't exist */ - EMU_ERR_INVALID_CONFIG = 6, /* some sort of error in configuration */ - EMU_ERR_IDENT_NONROMS = 7, /* identified all non-ROM files */ - EMU_ERR_IDENT_PARTIAL = 8, /* identified some files but not all */ - EMU_ERR_IDENT_NONE = 9 /* identified no files */ -}; +constexpr int EMU_ERR_NONE = 0; // no error +constexpr int EMU_ERR_FAILED_VALIDITY = 1; // failed validity checks +constexpr int EMU_ERR_MISSING_FILES = 2; // missing files +constexpr int EMU_ERR_FATALERROR = 3; // some other fatal error +constexpr int EMU_ERR_DEVICE = 4; // device initialization error +constexpr int EMU_ERR_NO_SUCH_SYSTEM = 5; // system was specified but doesn't exist +constexpr int EMU_ERR_INVALID_CONFIG = 6; // some sort of error in configuration +constexpr int EMU_ERR_IDENT_NONROMS = 7; // identified all non-ROM files +constexpr int EMU_ERR_IDENT_PARTIAL = 8; // identified some files but not all +constexpr int EMU_ERR_IDENT_NONE = 9; // identified no files + //************************************************************************** // TYPE DEFINITIONS diff --git a/src/emu/mconfig.cpp b/src/emu/mconfig.cpp index e9533aff7d8..d6385c6d628 100644 --- a/src/emu/mconfig.cpp +++ b/src/emu/mconfig.cpp @@ -13,7 +13,9 @@ #include "screen.h" #include + #include +#include //************************************************************************** @@ -37,12 +39,13 @@ private: //------------------------------------------------- machine_config::machine_config(const game_driver &gamedrv, emu_options &options) - : m_minimum_quantum(attotime::zero) - , m_gamedrv(gamedrv) + : m_gamedrv(gamedrv) , m_options(options) , m_root_device() , m_default_layouts([] (char const *a, char const *b) { return 0 > std::strcmp(a, b); }) , m_current_device(nullptr) + , m_maximum_quantums([] (char const *a, char const *b) { return 0 > std::strcmp(a, b); }) + , m_perfect_quantum_device(nullptr, "") { // add the root device device_add("root", gamedrv.type, 0); @@ -116,6 +119,51 @@ machine_config::~machine_config() } +//------------------------------------------------- +// maximum_quantum - get smallest configured +// maximum quantum +//------------------------------------------------- + +attotime machine_config::maximum_quantum(attotime const &default_quantum) const +{ + return std::accumulate( + m_maximum_quantums.begin(), + m_maximum_quantums.end(), + default_quantum, + [] (attotime const &lhs, maximum_quantum_map::value_type const &rhs) { return (std::min)(lhs, rhs.second); }); +} + +//------------------------------------------------- +// perfect_quantum_device - get device configured +// for perfect quantum if any +//------------------------------------------------- + +device_execute_interface *machine_config::perfect_quantum_device() const +{ + if (!m_perfect_quantum_device.first) + return nullptr; + + device_t *const found(m_perfect_quantum_device.first->subdevice(m_perfect_quantum_device.second.c_str())); + if (!found) + { + throw emu_fatalerror( + "Device %s relative to %s specified for perfect interleave is not present!\n", + m_perfect_quantum_device.second, + m_perfect_quantum_device.first->tag()); + } + + device_execute_interface *result; + if (!found->interface(result)) + { + throw emu_fatalerror("Device %s (%s) specified for perfect interleave does not implement device_execute_interface!\n", + found->tag(), + found->shortname()); + } + + return result; +} + + //------------------------------------------------- // set_default_layout - set layout for current // device @@ -129,6 +177,19 @@ void machine_config::set_default_layout(internal_layout const &layout) } +//------------------------------------------------- +// set_maximum_quantum - set maximum scheduling +// quantum for current device device +//------------------------------------------------- + +void machine_config::set_maximum_quantum(attotime const &quantum) +{ + std::pair const ins(m_maximum_quantums.emplace(current_device().tag(), quantum)); + if (!ins.second) + ins.first->second = quantum; +} + + //------------------------------------------------- // device_add - configuration helper to add a // new device @@ -287,24 +348,6 @@ device_t &machine_config::replace_device(std::unique_ptr &&device, dev } -//------------------------------------------------- -// device_find - configuration helper to -// locate a device -//------------------------------------------------- - -device_t *machine_config::device_find(device_t *owner, const char *tag) -{ - // find the original device by relative tag (must exist) - assert(owner != nullptr); - device_t *device = owner->subdevice(tag); - if (device == nullptr) - throw emu_fatalerror("Unable to find device '%s'\n", tag); - - // return the device - return device; -} - - //------------------------------------------------- // remove_references - globally remove references // to a device about to be removed from the tree @@ -312,19 +355,64 @@ device_t *machine_config::device_find(device_t *owner, const char *tag) void machine_config::remove_references(device_t &device) { - // remove default layouts for subdevices + // sanity check + if (m_perfect_quantum_device.first == &device) + { + throw emu_fatalerror( + "Removing %s device %s would make the perfect quantum device target invalid\n", + device.shortname(), + device.tag()); + } + + // remove default layouts and maximum quantum settings for subdevices char const *const tag(device.tag()); std::size_t const taglen(std::strlen(tag)); - default_layout_map::iterator it(m_default_layouts.lower_bound(tag)); - while ((m_default_layouts.end() != it) && !std::strncmp(tag, it->first, taglen)) + for (auto it = m_default_layouts.lower_bound(tag); (m_default_layouts.end() != it) && !std::strncmp(tag, it->first, taglen); ) { if (!it->first[taglen] || (':' == it->first[taglen])) it = m_default_layouts.erase(it); else ++it; } + for (auto it = m_maximum_quantums.lower_bound(tag); (m_maximum_quantums.end() != it) && !std::strncmp(tag, it->first, taglen); ) + { + if (!it->first[taglen] || (':' == it->first[taglen])) + it = m_maximum_quantums.erase(it); + else + ++it; + } // iterate over all devices and remove any references for (device_t &scan : device_iterator(root_device())) scan.subdevices().m_tagmap.clear(); } + + +//------------------------------------------------- +// set_perfect_quantum - set device to base +// scheduling interval on +//------------------------------------------------- + +void machine_config::set_perfect_quantum(device_t &device, std::string tag) +{ + if (!m_current_device) + { + throw emu_fatalerror( + "Perfect quantum device can only be set during configuration (set to %s relative to %s)\n", + tag, + device.tag()); + } + + if (m_current_device != m_root_device.get()) + { + throw emu_fatalerror( + "Perfect quantum device can only be set by the root device (set to %s relative to %s while configuring %s device %s)\n", + tag, + device.tag(), + m_current_device->shortname(), + m_current_device->tag()); + } + + m_perfect_quantum_device.first = &device; + m_perfect_quantum_device.second = std::move(tag); +} diff --git a/src/emu/mconfig.h b/src/emu/mconfig.h index 84d42d94996..af332e7db9c 100644 --- a/src/emu/mconfig.h +++ b/src/emu/mconfig.h @@ -33,17 +33,32 @@ namespace emu { namespace detail { -struct machine_config_replace { machine_config &config; }; +class machine_config_replace +{ +public: + machine_config_replace(machine_config_replace const &) = default; + machine_config &config; +private: + machine_config_replace(machine_config &c) : config(c) { } + friend class ::machine_config; +}; } } // namesapce emu::detail +/// \brief Internal layout description +/// +/// Holds the compressed and decompressed data size, compression method, +/// and a reference to the compressed layout data. Note that copying +/// the structure will not copy the referenced data. struct internal_layout { + enum class compression { NONE, ZLIB }; + size_t decompressed_size; size_t compressed_size; - u8 compression_type; - const u8* data; + compression compression_type; + u8 const *data; }; @@ -96,19 +111,68 @@ public: emu_options &options() const { return m_options; } device_t *device(const char *tag) const { return root_device().subdevice(tag); } template DeviceClass *device(const char *tag) const { return downcast(device(tag)); } + attotime maximum_quantum(attotime const &default_quantum) const; + device_execute_interface *perfect_quantum_device() const; + + /// \brief Apply visitor to internal layouts + /// + /// Calls the supplied visitor for each device with an internal + /// layout. The order of devices is implementation-dependent. + /// \param [in] op The visitor. It must provide a function call + // operator that can be invoked with two arguments: a reference + // to a #device_t and a const reference to an #internal_layout. template void apply_default_layouts(T &&op) const { for (std::pair const &lay : m_default_layouts) op(*device(lay.first), *lay.second); } - // public state - attotime m_minimum_quantum; // minimum scheduling quantum - std::string m_perfect_cpu_quantum; // tag of CPU to use for "perfect" scheduling - - // configuration methods + /// \brief Get a device replacement helper + /// + /// Pass the result in place of the machine configuration itself to + /// replace an existing device. + /// \return A device replacement helper to pass to a device type + /// when replacing an existing device. + emu::detail::machine_config_replace replace() { return emu::detail::machine_config_replace(*this); }; + + /// \brief Set internal layout for current device + /// + /// Set internal layout for current device. Each device in the + /// system can have its own internal layout. Tags in the layout + /// will be resolved relative to the device. Replaces previously + /// set layout if any. + /// \param [in] layout Reference to the internal layout description + /// structure. Neither the description structure nor the + /// compressed data is copied. It is the caller's responsibility + /// to ensure both remain valid until layouts and views are + /// instantiated. void set_default_layout(internal_layout const &layout); + /// \brief Set maximum scheduling quantum + /// + /// Set the maximum scheduling quantum required for the current + /// device. The smallest maximum quantum requested by a device in + /// the system will be used. + /// \param [in] quantum Maximum scheduling quantum in attoseconds. + void set_maximum_quantum(attotime const &quantum); + + template + void set_perfect_quantum(T &&tag) + { + set_perfect_quantum(current_device(), std::forward(tag)); + } + template + void set_perfect_quantum(device_finder const &finder) + { + std::pair const target(finder.finder_target()); + set_perfect_quantum(target.first, target.second); + } + template + void set_perfect_quantum(device_finder &finder) + { + set_perfect_quantum(const_cast const &>(finder)); + } + // helpers during configuration; not for general use token begin_configuration(device_t &device) { @@ -116,7 +180,6 @@ public: m_current_device = &device; return token(*this, device); } - emu::detail::machine_config_replace replace() { return emu::detail::machine_config_replace{ *this }; }; device_t *device_add(const char *tag, device_type type, u32 clock); template device_t *device_add(const char *tag, Creator &&type, u32 clock) @@ -162,11 +225,11 @@ public: return device_replace(tag, std::forward(type), clock.value(), std::forward(args)...); } device_t *device_remove(const char *tag); - device_t *device_find(device_t *owner, const char *tag); private: class current_device_stack; typedef std::map default_layout_map; + typedef std::map maximum_quantum_map; // internal helpers std::pair resolve_owner(const char *tag) const; @@ -174,13 +237,16 @@ private: device_t &add_device(std::unique_ptr &&device, device_t *owner); device_t &replace_device(std::unique_ptr &&device, device_t &owner, device_t *existing); void remove_references(device_t &device); + void set_perfect_quantum(device_t &device, std::string tag); // internal state - game_driver const & m_gamedrv; - emu_options & m_options; - std::unique_ptr m_root_device; - default_layout_map m_default_layouts; - device_t * m_current_device; + game_driver const & m_gamedrv; + emu_options & m_options; + std::unique_ptr m_root_device; + default_layout_map m_default_layouts; + device_t * m_current_device; + maximum_quantum_map m_maximum_quantums; + std::pair m_perfect_quantum_device; }; #endif // MAME_EMU_MCONFIG_H diff --git a/src/emu/render.cpp b/src/emu/render.cpp index 0c598345ea1..55340803345 100644 --- a/src/emu/render.cpp +++ b/src/emu/render.cpp @@ -1995,12 +1995,11 @@ bool render_target::load_layout_file(const char *dirname, const internal_layout z_stream stream; int zerr; - /* initialize the stream */ + // initialize the stream memset(&stream, 0, sizeof(stream)); stream.next_out = tempout.get(); stream.avail_out = layout_data.decompressed_size; - zerr = inflateInit(&stream); if (zerr != Z_OK) { @@ -2008,12 +2007,12 @@ bool render_target::load_layout_file(const char *dirname, const internal_layout return false; } - /* decompress this chunk */ + // decompress this chunk stream.next_in = (unsigned char *)layout_data.data; stream.avail_in = layout_data.compressed_size; zerr = inflate(&stream, Z_NO_FLUSH); - /* stop at the end of the stream */ + // stop at the end of the stream if (zerr == Z_STREAM_END) { // OK @@ -2024,7 +2023,7 @@ bool render_target::load_layout_file(const char *dirname, const internal_layout return false; } - /* clean up */ + // clean up zerr = inflateEnd(&stream); if (zerr != Z_OK) { diff --git a/src/emu/romload.cpp b/src/emu/romload.cpp index d04d0e308a3..a6610ec6c0c 100644 --- a/src/emu/romload.cpp +++ b/src/emu/romload.cpp @@ -471,7 +471,7 @@ void rom_load_manager::display_rom_load_results(bool from_list) { /* create the error message and exit fatally */ osd_printf_error("%s", m_errorstring); - fatalerror_exitcode(machine(), EMU_ERR_MISSING_FILES, "Required files are missing, the machine cannot be run."); + throw emu_fatalerror(EMU_ERR_MISSING_FILES, "Required files are missing, the machine cannot be run."); } /* if we had warnings, output them, but continue */ @@ -595,7 +595,7 @@ int rom_load_manager::open_rom_file(const char *regiontag, const rom_entry *romp } if (tag5.find_first_of('%') != -1) - fatalerror("We do not support clones of clones!\n"); + throw emu_fatalerror("We do not support clones of clones!\n"); // try to load from the available location(s): // - if we are not using lists, we have regiontag only; @@ -688,11 +688,11 @@ int rom_load_manager::read_rom_data(const rom_entry *parent_region, const rom_en /* make sure we only fill within the region space */ if (ROM_GETOFFSET(romp) + numgroups * groupsize + (numgroups - 1) * skip > m_region->bytes()) - fatalerror("Error in RomModule definition: %s out of memory region space\n", ROM_GETNAME(romp)); + throw emu_fatalerror("Error in RomModule definition: %s out of memory region space\n", ROM_GETNAME(romp)); /* make sure the length was valid */ if (numbytes == 0) - fatalerror("Error in RomModule definition: %s has an invalid length\n", ROM_GETNAME(romp)); + throw emu_fatalerror("Error in RomModule definition: %s has an invalid length\n", ROM_GETNAME(romp)); /* special case for simple loads */ if (datamask == 0xff && (groupsize == 1 || !reversed) && skip == 0) @@ -790,11 +790,11 @@ void rom_load_manager::fill_rom_data(const rom_entry *romp) // make sure we fill within the region space if (ROM_GETOFFSET(romp) + numbytes > m_region->bytes()) - fatalerror("Error in RomModule definition: FILL out of memory region space\n"); + throw emu_fatalerror("Error in RomModule definition: FILL out of memory region space\n"); // make sure the length was valid if (numbytes == 0) - fatalerror("Error in RomModule definition: FILL has an invalid length\n"); + throw emu_fatalerror("Error in RomModule definition: FILL has an invalid length\n"); // for fill bytes, the byte that gets filled is the first byte of the hashdata string u8 fill_byte = u8(strtol(ROM_GETHASHDATA(romp), nullptr, 0)); @@ -823,20 +823,20 @@ void rom_load_manager::copy_rom_data(const rom_entry *romp) /* make sure we copy within the region space */ if (ROM_GETOFFSET(romp) + numbytes > m_region->bytes()) - fatalerror("Error in RomModule definition: COPY out of target memory region space\n"); + throw emu_fatalerror("Error in RomModule definition: COPY out of target memory region space\n"); /* make sure the length was valid */ if (numbytes == 0) - fatalerror("Error in RomModule definition: COPY has an invalid length\n"); + throw emu_fatalerror("Error in RomModule definition: COPY has an invalid length\n"); /* make sure the source was valid */ memory_region *region = machine().root_device().memregion(srcrgntag); if (region == nullptr) - fatalerror("Error in RomModule definition: COPY from an invalid region\n"); + throw emu_fatalerror("Error in RomModule definition: COPY from an invalid region\n"); /* make sure we find within the region space */ if (srcoffs + numbytes > region->bytes()) - fatalerror("Error in RomModule definition: COPY out of source memory region space\n"); + throw emu_fatalerror("Error in RomModule definition: COPY out of source memory region space\n"); /* fill the data */ memcpy(base, region->base() + srcoffs, numbytes); @@ -856,13 +856,13 @@ void rom_load_manager::process_rom_entries(const char *regiontag, const rom_entr while (!ROMENTRY_ISREGIONEND(romp)) { if (ROMENTRY_ISCONTINUE(romp)) - fatalerror("Error in RomModule definition: ROM_CONTINUE not preceded by ROM_LOAD\n"); + throw emu_fatalerror("Error in RomModule definition: ROM_CONTINUE not preceded by ROM_LOAD\n"); if (ROMENTRY_ISIGNORE(romp)) - fatalerror("Error in RomModule definition: ROM_IGNORE not preceded by ROM_LOAD\n"); + throw emu_fatalerror("Error in RomModule definition: ROM_IGNORE not preceded by ROM_LOAD\n"); if (ROMENTRY_ISRELOAD(romp)) - fatalerror("Error in RomModule definition: ROM_RELOAD not preceded by ROM_LOAD\n"); + throw emu_fatalerror("Error in RomModule definition: ROM_RELOAD not preceded by ROM_LOAD\n"); if (ROMENTRY_ISFILL(romp)) { @@ -1005,7 +1005,7 @@ int open_disk_image(emu_options &options, const game_driver *gamedrv, const rom_ } if (tag5.find_first_of('%') != -1) - fatalerror("We do not support clones of clones!\n"); + throw emu_fatalerror("We do not support clones of clones!\n"); // try to load from the available location(s): // - if we are not using lists, we have locationtag only; diff --git a/src/emu/schedule.cpp b/src/emu/schedule.cpp index e2b1497995f..fbe9f667255 100644 --- a/src/emu/schedule.cpp +++ b/src/emu/schedule.cpp @@ -756,29 +756,13 @@ void device_scheduler::rebuild_execute_list() // if we haven't yet set a scheduling quantum, do it now if (m_quantum_list.empty()) { - // set the core scheduling quantum - attotime min_quantum = machine().config().m_minimum_quantum; - - // if none specified default to 60Hz - if (min_quantum.is_zero()) - min_quantum = attotime::from_hz(60); + // set the core scheduling quantum, ensuring it's no longer than 60Hz + attotime min_quantum = machine().config().maximum_quantum(attotime::from_hz(60)); // if the configuration specifies a device to make perfect, pick that as the minimum - if (!machine().config().m_perfect_cpu_quantum.empty()) - { - device_t *device = machine().root_device().subdevice(machine().config().m_perfect_cpu_quantum.c_str()); - if (device == nullptr) - fatalerror("Device '%s' specified for perfect interleave is not present!\n", machine().config().m_perfect_cpu_quantum.c_str()); - - device_execute_interface *exec; - if (!device->interface(exec)) - fatalerror("Device '%s' specified for perfect interleave is not an executing device!\n", machine().config().m_perfect_cpu_quantum.c_str()); - - min_quantum = std::min(attotime(0, exec->minimum_quantum()), min_quantum); - } - - // make sure it's no higher than 60Hz - min_quantum = std::min(min_quantum, attotime::from_hz(60)); + device_execute_interface *const exec(machine().config().perfect_quantum_device()); + if (exec) + min_quantum = (std::min)(attotime(0, exec->minimum_quantum()), min_quantum); // inform the timer system of our decision add_scheduling_quantum(min_quantum, attotime::never); diff --git a/src/emu/validity.cpp b/src/emu/validity.cpp index 882da29c1ff..a46639d882e 100644 --- a/src/emu/validity.cpp +++ b/src/emu/validity.cpp @@ -238,7 +238,7 @@ bool validity_checker::check_all_matching(const char *string) // if we failed to match anything, it if (string && !validated_any) - throw emu_fatalerror(EMU_ERR_NO_SUCH_GAME, "No matching systems found for '%s'", string); + throw emu_fatalerror(EMU_ERR_NO_SUCH_SYSTEM, "No matching systems found for '%s'", string); return !(m_errors > 0 || m_warnings > 0); } @@ -318,7 +318,7 @@ void validity_checker::validate_one(const game_driver &driver) } catch (emu_fatalerror &err) { - osd_printf_error("Fatal error %s", err.string()); + osd_printf_error("Fatal error %s", err.what()); } // if we had warnings or errors, output -- cgit v1.2.3