summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/validity.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/validity.cpp')
-rw-r--r--src/emu/validity.cpp1373
1 files changed, 1005 insertions, 368 deletions
diff --git a/src/emu/validity.cpp b/src/emu/validity.cpp
index c8b5b52892c..4cb6202b4d9 100644
--- a/src/emu/validity.cpp
+++ b/src/emu/validity.cpp
@@ -12,345 +12,140 @@
#include "validity.h"
#include "emuopts.h"
+#include "main.h"
#include "romload.h"
+#include "speaker.h"
#include "video/rgbutil.h"
+#include "corestr.h"
+#include "path.h"
+#include "unicode.h"
+
#include <cctype>
#include <type_traits>
#include <typeinfo>
-//**************************************************************************
-// TYPE DEFINITIONS
-//**************************************************************************
-
-//**************************************************************************
-// INLINE FUNCTIONS
-//**************************************************************************
+namespace {
//-------------------------------------------------
-// ioport_string_from_index - return an indexed
-// string from the I/O port system
+// diamond_inheritance - forward declaration of a
+// class to force MSVC to use unknown inheritance
+// form of pointers to member functions
//-------------------------------------------------
-inline const char *validity_checker::ioport_string_from_index(u32 index)
-{
- return ioport_configurer::string_from_token((const char *)(uintptr_t)index);
-}
+class diamond_inheritance;
//-------------------------------------------------
-// get_defstr_index - return the index of the
-// string assuming it is one of the default
-// strings
+// test_delegate - a delegate that can return a
+// result in a register
//-------------------------------------------------
-inline int validity_checker::get_defstr_index(const char *string, bool suppress_error)
-{
- // check for strings that should be DEF_STR
- auto strindex = m_defstr_map.find(string);
- if (!suppress_error && strindex != m_defstr_map.end() && string != ioport_string_from_index(strindex->second))
- osd_printf_error("Must use DEF_STR( %s )\n", string);
- return (strindex != m_defstr_map.end()) ? strindex->second : 0;
-}
+using test_delegate = delegate<char (void const *&)>;
//-------------------------------------------------
-// random_u64
-// random_s64
-// random_u32
-// random_s32
-//-------------------------------------------------
-#undef rand
-inline s32 validity_checker::random_i32() { return s32(random_u32()); }
-inline u32 validity_checker::random_u32() { return rand() ^ (rand() << 15); }
-inline s64 validity_checker::random_i64() { return s64(random_u64()); }
-inline u64 validity_checker::random_u64() { return u64(random_u32()) ^ (u64(random_u32()) << 30); }
-
-
-
-//-------------------------------------------------
-// validate_tag - ensure that the given tag
-// meets the general requirements
+// make_diamond_class_delegate - make a delegate
+// bound to an instance of an incomplete class
+// type
//-------------------------------------------------
-void validity_checker::validate_tag(const char *tag)
+test_delegate make_diamond_class_delegate(char (diamond_inheritance::*func)(void const *&), diamond_inheritance *obj)
{
- // some common names that are now deprecated
- if (strcmp(tag, "main") == 0 || strcmp(tag, "audio") == 0 || strcmp(tag, "sound") == 0 || strcmp(tag, "left") == 0 || strcmp(tag, "right") == 0)
- osd_printf_error("Invalid generic tag '%s' used\n", tag);
-
- // scan for invalid characters
- static char const *const validchars = "abcdefghijklmnopqrstuvwxyz0123456789_.:^$";
- for (char const *p = tag; *p; ++p)
- {
- // only lower-case permitted
- if (*p != tolower(u8(*p)))
- {
- osd_printf_error("Tag '%s' contains upper-case characters\n", tag);
- break;
- }
- if (*p == ' ')
- {
- osd_printf_error("Tag '%s' contains spaces\n", tag);
- break;
- }
- if (!strchr(validchars, *p))
- {
- osd_printf_error("Tag '%s' contains invalid character '%c'\n", tag, *p);
- break;
- }
- }
-
- // find the start of the final tag
- const char *begin = strrchr(tag, ':');
- if (begin == nullptr)
- begin = tag;
- else
- begin += 1;
-
- // 0-length = bad
- if (*begin == 0)
- osd_printf_error("Found 0-length tag\n");
-
- // too short/too long = bad
- if (strlen(begin) < MIN_TAG_LENGTH)
- osd_printf_error("Tag '%s' is too short (must be at least %d characters)\n", tag, MIN_TAG_LENGTH);
+ return test_delegate(func, obj);
}
-
-//**************************************************************************
-// VALIDATION FUNCTIONS
-//**************************************************************************
-
//-------------------------------------------------
-// validity_checker - constructor
+// virtual_base - simple class that will be used
+// as the top vertex of the diamond
//-------------------------------------------------
-validity_checker::validity_checker(emu_options &options, bool quick)
- : m_drivlist(options)
- , m_errors(0)
- , m_warnings(0)
- , m_print_verbose(options.verbose())
- , m_current_driver(nullptr)
- , m_current_device(nullptr)
- , m_current_ioport(nullptr)
- , m_checking_card(false)
- , m_quick(quick)
+struct virtual_base
{
- // pre-populate the defstr map with all the default strings
- for (int strnum = 1; strnum < INPUT_STRING_COUNT; strnum++)
- {
- const char *string = ioport_string_from_index(strnum);
- if (string != nullptr)
- m_defstr_map.insert(std::make_pair(string, strnum));
- }
-}
+ char get_base(void const *&p) { p = this; return 'x'; }
+ int x;
+};
-//-------------------------------------------------
-// validity_checker - destructor
-//-------------------------------------------------
-
-validity_checker::~validity_checker()
-{
- validate_end();
-}
//-------------------------------------------------
-// check_driver - check a single driver
+// virtual_derived_a - first class derived from
+// virtual base
//-------------------------------------------------
-void validity_checker::check_driver(const game_driver &driver)
+struct virtual_derived_a : virtual virtual_base
{
- // simply validate the one driver
- validate_begin();
- validate_one(driver);
- validate_end();
-}
+ char get_derived_a(void const *&p) { p = this; return 'a'; }
+ int a;
+};
//-------------------------------------------------
-// check_shared_source - check all drivers that
-// share the same source file as the given driver
+// virtual_derived_b - second class derived from
+// virtual base
//-------------------------------------------------
-void validity_checker::check_shared_source(const game_driver &driver)
+struct virtual_derived_b : virtual virtual_base
{
- // initialize
- validate_begin();
-
- // then iterate over all drivers and check the ones that share the same source file
- m_drivlist.reset();
- while (m_drivlist.next())
- if (strcmp(driver.type.source(), m_drivlist.driver().type.source()) == 0)
- validate_one(m_drivlist.driver());
-
- // cleanup
- validate_end();
-}
+ char get_derived_b(void const *&p) { p = this; return 'b'; }
+ int b;
+};
//-------------------------------------------------
-// check_all_matching - check all drivers whose
-// names match the given string
+// diamond_inheritance - actual definition of
+// class with diamond inheritance
//-------------------------------------------------
-bool validity_checker::check_all_matching(const char *string)
+class diamond_inheritance : public virtual_derived_a, public virtual_derived_b
{
- // start by checking core stuff
- validate_begin();
- validate_core();
- validate_inlines();
- validate_rgb();
-
- // if we had warnings or errors, output
- if (m_errors > 0 || m_warnings > 0 || !m_verbose_text.empty())
- {
- output_via_delegate(OSD_OUTPUT_CHANNEL_ERROR, "Core: %d errors, %d warnings\n", m_errors, m_warnings);
- if (m_errors > 0)
- output_indented_errors(m_error_text, "Errors");
- if (m_warnings > 0)
- output_indented_errors(m_warning_text, "Warnings");
- if (!m_verbose_text.empty())
- output_indented_errors(m_verbose_text, "Messages");
- output_via_delegate(OSD_OUTPUT_CHANNEL_ERROR, "\n");
- }
-
- // then iterate over all drivers and check them
- m_drivlist.reset();
- bool validated_any = false;
- while (m_drivlist.next())
- {
- if (driver_list::matches(string, m_drivlist.driver().name))
- {
- validate_one(m_drivlist.driver());
- validated_any = true;
- }
- }
-
- // validate devices
- if (!string)
- validate_device_types();
-
- // cleanup
- validate_end();
-
- // if we failed to match anything, it
- if (string && !validated_any)
- throw emu_fatalerror(EMU_ERR_NO_SUCH_SYSTEM, "No matching systems found for '%s'", string);
-
- return !(m_errors > 0 || m_warnings > 0);
-}
+};
//-------------------------------------------------
-// validate_begin - prepare for validation by
-// taking over the output callbacks and resetting
-// our internal state
+// pure_virtual_base - abstract class with a
+// vtable
//-------------------------------------------------
-void validity_checker::validate_begin()
+struct pure_virtual_base
{
- // take over error and warning outputs
- osd_output::push(this);
-
- // reset all our maps
- m_names_map.clear();
- m_descriptions_map.clear();
- m_roms_map.clear();
- m_defstr_map.clear();
- m_region_map.clear();
- m_ioport_set.clear();
-
- // reset internal state
- m_errors = 0;
- m_warnings = 0;
- m_already_checked.clear();
-}
+ virtual ~pure_virtual_base() = default;
+ virtual char operator()(void const *&p) const = 0;
+};
//-------------------------------------------------
-// validate_end - restore output callbacks and
-// clean up
+// ioport_string_from_index - return an indexed
+// string from the I/O port system
//-------------------------------------------------
-void validity_checker::validate_end()
+inline char const *ioport_string_from_index(u32 index)
{
- // restore the original output callbacks
- osd_output::pop(this);
+ return ioport_configurer::string_from_token(reinterpret_cast<char const *>(uintptr_t(index)));
}
//-------------------------------------------------
-// validate_drivers - master validity checker
+// random_u64
+// random_s64
+// random_u32
+// random_s32
//-------------------------------------------------
-
-void validity_checker::validate_one(const game_driver &driver)
-{
- // help verbose validation detect configuration-related crashes
- if (m_print_verbose)
- output_via_delegate(OSD_OUTPUT_CHANNEL_ERROR, "Validating driver %s (%s)...\n", driver.name, core_filename_extract_base(driver.type.source()).c_str());
-
- // set the current driver
- m_current_driver = &driver;
- m_current_device = nullptr;
- m_current_ioport = nullptr;
- m_region_map.clear();
- m_ioport_set.clear();
- m_checking_card = false;
-
- // reset error/warning state
- int start_errors = m_errors;
- int start_warnings = m_warnings;
- m_error_text.clear();
- m_warning_text.clear();
- m_verbose_text.clear();
-
- // wrap in try/catch to catch fatalerrors
- try
- {
- machine_config config(driver, m_blank_options);
- validate_driver(config.root_device());
- validate_roms(config.root_device());
- validate_inputs(config.root_device());
- validate_devices(config);
- }
- catch (emu_fatalerror const &err)
- {
- osd_printf_error("Fatal error %s", err.what());
- }
-
- // if we had warnings or errors, output
- if (m_errors > start_errors || m_warnings > start_warnings || !m_verbose_text.empty())
- {
- if (!m_print_verbose)
- output_via_delegate(OSD_OUTPUT_CHANNEL_ERROR, "Driver %s (file %s): ", driver.name, core_filename_extract_base(driver.type.source()).c_str());
- output_via_delegate(OSD_OUTPUT_CHANNEL_ERROR, "%d errors, %d warnings\n", m_errors - start_errors, m_warnings - start_warnings);
- if (m_errors > start_errors)
- output_indented_errors(m_error_text, "Errors");
- if (m_warnings > start_warnings)
- output_indented_errors(m_warning_text, "Warnings");
- if (!m_verbose_text.empty())
- output_indented_errors(m_verbose_text, "Messages");
- output_via_delegate(OSD_OUTPUT_CHANNEL_ERROR, "\n");
- }
-
- // reset the driver/device
- m_current_driver = nullptr;
- m_current_device = nullptr;
- m_current_ioport = nullptr;
- m_region_map.clear();
- m_ioport_set.clear();
- m_checking_card = false;
-}
+#undef rand
+inline u32 random_u32() { return rand() ^ (rand() << 15); }
+inline s32 random_i32() { return s32(random_u32()); }
+inline u64 random_u64() { return u64(random_u32()) ^ (u64(random_u32()) << 30); }
+inline s64 random_i64() { return s64(random_u64()); }
//-------------------------------------------------
-// validate_core - validate core internal systems
+// validate_integer_semantics - validate that
+// integers behave as expected, particularly
+// with regards to overflow and shifting
//-------------------------------------------------
-void validity_checker::validate_core()
+void validate_integer_semantics()
{
// basic system checks
if (~0 != -1) osd_printf_error("Machine must be two's complement\n");
@@ -403,7 +198,7 @@ void validity_checker::validate_core()
// behaviors
//-------------------------------------------------
-void validity_checker::validate_inlines()
+void validate_inlines()
{
volatile u64 testu64a = random_u64();
volatile s64 testi64a = random_i64();
@@ -474,13 +269,13 @@ void validity_checker::validate_inlines()
if (resultu32 != expectedu32)
osd_printf_error("Error testing divu_64x32 (%16X / %08X) = %08X (expected %08X)\n", u64(testu64a), u32(testu32a), resultu32, expectedu32);
- resulti32 = div_64x32_rem(testi64a, testi32a, &remainder);
+ resulti32 = div_64x32_rem(testi64a, testi32a, remainder);
expectedi32 = testi64a / s64(testi32a);
expremainder = testi64a % s64(testi32a);
if (resulti32 != expectedi32 || remainder != expremainder)
osd_printf_error("Error testing div_64x32_rem (%16X / %08X) = %08X,%08X (expected %08X,%08X)\n", s64(testi64a), s32(testi32a), resulti32, remainder, expectedi32, expremainder);
- resultu32 = divu_64x32_rem(testu64a, testu32a, &uremainder);
+ resultu32 = divu_64x32_rem(testu64a, testu32a, uremainder);
expectedu32 = testu64a / u64(testu32a);
expuremainder = testu64a % u64(testu32a);
if (resultu32 != expectedu32 || uremainder != expuremainder)
@@ -517,14 +312,42 @@ void validity_checker::validate_inlines()
for (int i = 0; i <= 32; i++)
{
u32 t = i < 32 ? (1 << (31 - i) | testu32a >> i) : 0;
- u8 resultu8 = count_leading_zeros(t);
+ u8 resultu8 = count_leading_zeros_32(t);
if (resultu8 != i)
- osd_printf_error("Error testing count_leading_zeros %08x=%02x (expected %02x)\n", t, resultu8, i);
+ osd_printf_error("Error testing count_leading_zeros_32 %08x=%02x (expected %02x)\n", t, resultu8, i);
t ^= 0xffffffff;
- resultu8 = count_leading_ones(t);
+ resultu8 = count_leading_ones_32(t);
if (resultu8 != i)
- osd_printf_error("Error testing count_leading_ones %08x=%02x (expected %02x)\n", t, resultu8, i);
+ osd_printf_error("Error testing count_leading_ones_32 %08x=%02x (expected %02x)\n", t, resultu8, i);
+ }
+
+ u32 expected32 = testu32a << 1 | testu32a >> 31;
+ for (int i = -33; i <= 33; i++)
+ {
+ u32 resultu32r = rotr_32(testu32a, i);
+ u32 resultu32l = rotl_32(testu32a, -i);
+
+ if (resultu32r != expected32)
+ osd_printf_error("Error testing rotr_32 %08x, %d=%08x (expected %08x)\n", u32(testu32a), i, resultu32r, expected32);
+ if (resultu32l != expected32)
+ osd_printf_error("Error testing rotl_32 %08x, %d=%08x (expected %08x)\n", u32(testu32a), -i, resultu32l, expected32);
+
+ expected32 = expected32 >> 1 | expected32 << 31;
+ }
+
+ u64 expected64 = testu64a << 1 | testu64a >> 63;
+ for (int i = -65; i <= 65; i++)
+ {
+ u64 resultu64r = rotr_64(testu64a, i);
+ u64 resultu64l = rotl_64(testu64a, -i);
+
+ if (resultu64r != expected64)
+ osd_printf_error("Error testing rotr_64 %016x, %d=%016x (expected %016x)\n", u64(testu64a), i, resultu64r, expected64);
+ if (resultu64l != expected64)
+ osd_printf_error("Error testing rotl_64 %016x, %d=%016x (expected %016x)\n", u64(testu64a), -i, resultu64l, expected64);
+
+ expected64 = expected64 >> 1 | expected64 << 63;
}
}
@@ -534,7 +357,7 @@ void validity_checker::validate_inlines()
// class
//-------------------------------------------------
-void validity_checker::validate_rgb()
+void validate_rgb()
{
/*
This performs cursory tests of most of the vector-optimised RGB
@@ -564,29 +387,31 @@ void validity_checker::validate_rgb()
scale_imm_add_and_clamp(const s32, const rgbaint_t&);
*/
- auto random_i32_nolimit = [this]
- {
- s32 result;
- do { result = random_i32(); } while ((result == std::numeric_limits<s32>::min()) || (result == std::numeric_limits<s32>::max()));
- return result;
- };
+ auto random_i32_nolimit =
+ [] ()
+ {
+ s32 result;
+ do { result = random_i32(); } while ((result == std::numeric_limits<s32>::min()) || (result == std::numeric_limits<s32>::max()));
+ return result;
+ };
volatile s32 expected_a, expected_r, expected_g, expected_b;
volatile s32 actual_a, actual_r, actual_g, actual_b;
volatile s32 imm;
rgbaint_t rgb, other;
rgb_t packed;
- auto check_expected = [&] (const char *desc)
- {
- const volatile s32 a = rgb.get_a32();
- const volatile s32 r = rgb.get_r32();
- const volatile s32 g = rgb.get_g32();
- const volatile s32 b = rgb.get_b32();
- if (a != expected_a) osd_printf_error("Error testing %s get_a32() = %d (expected %d)\n", desc, s32(a), s32(expected_a));
- if (r != expected_r) osd_printf_error("Error testing %s get_r32() = %d (expected %d)\n", desc, s32(r), s32(expected_r));
- if (g != expected_g) osd_printf_error("Error testing %s get_g32() = %d (expected %d)\n", desc, s32(g), s32(expected_g));
- if (b != expected_b) osd_printf_error("Error testing %s get_b32() = %d (expected %d)\n", desc, s32(b), s32(expected_b));
- };
+ auto check_expected =
+ [&] (const char *desc)
+ {
+ const volatile s32 a = rgb.get_a32();
+ const volatile s32 r = rgb.get_r32();
+ const volatile s32 g = rgb.get_g32();
+ const volatile s32 b = rgb.get_b32();
+ if (a != expected_a) osd_printf_error("Error testing %s get_a32() = %d (expected %d)\n", desc, s32(a), s32(expected_a));
+ if (r != expected_r) osd_printf_error("Error testing %s get_r32() = %d (expected %d)\n", desc, s32(r), s32(expected_r));
+ if (g != expected_g) osd_printf_error("Error testing %s get_g32() = %d (expected %d)\n", desc, s32(g), s32(expected_g));
+ if (b != expected_b) osd_printf_error("Error testing %s get_b32() = %d (expected %d)\n", desc, s32(b), s32(expected_b));
+ };
// check set/get
expected_a = random_i32();
@@ -1009,6 +834,15 @@ void validity_checker::validate_rgb()
rgb.shl(rgbaint_t(19, 3, 21, 6));
check_expected("rgbaint_t::shl");
+ // test shift left out of range
+ expected_a = (actual_a = random_i32()) & 0;
+ expected_r = (actual_r = random_i32()) & 0;
+ expected_g = (actual_g = random_i32()) & 0;
+ expected_b = (actual_b = random_i32()) & 0;
+ rgb.set(actual_a, actual_r, actual_g, actual_b);
+ rgb.shl(rgbaint_t(-19, 32, -21, 38));
+ check_expected("rgbaint_t::shl");
+
// test shift left immediate
expected_a = (actual_a = random_i32()) << 7;
expected_r = (actual_r = random_i32()) << 7;
@@ -1018,6 +852,15 @@ void validity_checker::validate_rgb()
rgb.shl_imm(7);
check_expected("rgbaint_t::shl_imm");
+ // test shift left immediate out of range
+ expected_a = (actual_a = random_i32()) & 0;
+ expected_r = (actual_r = random_i32()) & 0;
+ expected_g = (actual_g = random_i32()) & 0;
+ expected_b = (actual_b = random_i32()) & 0;
+ rgb.set(actual_a, actual_r, actual_g, actual_b);
+ rgb.shl_imm(32);
+ check_expected("rgbaint_t::shl_imm");
+
// test logical shift right
expected_a = s32(u32(actual_a = random_i32()) >> 8);
expected_r = s32(u32(actual_r = random_i32()) >> 18);
@@ -1036,6 +879,15 @@ void validity_checker::validate_rgb()
rgb.shr(rgbaint_t(21, 13, 11, 17));
check_expected("rgbaint_t::shr");
+ // test logical shift right out of range
+ expected_a = (actual_a = random_i32()) & 0;
+ expected_r = (actual_r = random_i32()) & 0;
+ expected_g = (actual_g = random_i32()) & 0;
+ expected_b = (actual_b = random_i32()) & 0;
+ rgb.set(actual_a, actual_r, actual_g, actual_b);
+ rgb.shr(rgbaint_t(40, -18, -26, 32));
+ check_expected("rgbaint_t::shr");
+
// test logical shift right immediate
expected_a = s32(u32(actual_a = random_i32()) >> 5);
expected_r = s32(u32(actual_r = random_i32()) >> 5);
@@ -1054,6 +906,15 @@ void validity_checker::validate_rgb()
rgb.shr_imm(15);
check_expected("rgbaint_t::shr_imm");
+ // test logical shift right immediate out of range
+ expected_a = (actual_a = random_i32()) & 0;
+ expected_r = (actual_r = random_i32()) & 0;
+ expected_g = (actual_g = random_i32()) & 0;
+ expected_b = (actual_b = random_i32()) & 0;
+ rgb.set(actual_a, actual_r, actual_g, actual_b);
+ rgb.shr_imm(35);
+ check_expected("rgbaint_t::shr_imm");
+
// test arithmetic shift right
expected_a = (actual_a = random_i32()) >> 16;
expected_r = (actual_r = random_i32()) >> 20;
@@ -1072,6 +933,15 @@ void validity_checker::validate_rgb()
rgb.sra(rgbaint_t(1, 29, 10, 22));
check_expected("rgbaint_t::sra");
+ // test arithmetic shift right out of range
+ expected_a = (actual_a = random_i32()) >> 31;
+ expected_r = (actual_r = random_i32()) >> 31;
+ expected_g = (actual_g = random_i32()) >> 31;
+ expected_b = (actual_b = random_i32()) >> 31;
+ rgb.set(actual_a, actual_r, actual_g, actual_b);
+ rgb.sra(rgbaint_t(-16, -20, 46, 32));
+ check_expected("rgbaint_t::sra");
+
// test arithmetic shift right immediate (method)
expected_a = (actual_a = random_i32()) >> 12;
expected_r = (actual_r = random_i32()) >> 12;
@@ -1090,6 +960,15 @@ void validity_checker::validate_rgb()
rgb.sra_imm(9);
check_expected("rgbaint_t::sra_imm");
+ // test arithmetic shift right immediate out of range (method)
+ expected_a = (actual_a = random_i32()) >> 31;
+ expected_r = (actual_r = random_i32()) >> 31;
+ expected_g = (actual_g = random_i32()) >> 31;
+ expected_b = (actual_b = random_i32()) >> 31;
+ rgb.set(actual_a, actual_r, actual_g, actual_b);
+ rgb.sra_imm(38);
+ check_expected("rgbaint_t::sra_imm");
+
// test arithmetic shift right immediate (operator)
expected_a = (actual_a = random_i32()) >> 7;
expected_r = (actual_r = random_i32()) >> 7;
@@ -1108,6 +987,15 @@ void validity_checker::validate_rgb()
rgb >>= 11;
check_expected("rgbaint_t::operator>>=");
+ // test arithmetic shift right immediate out of range (operator)
+ expected_a = (actual_a = random_i32()) >> 31;
+ expected_r = (actual_r = random_i32()) >> 31;
+ expected_g = (actual_g = random_i32()) >> 31;
+ expected_b = (actual_b = random_i32()) >> 31;
+ rgb.set(actual_a, actual_r, actual_g, actual_b);
+ rgb >>= 41;
+ check_expected("rgbaint_t::operator>>=");
+
// test RGB equality comparison
actual_a = random_i32_nolimit();
actual_r = random_i32_nolimit();
@@ -1452,6 +1340,711 @@ void validity_checker::validate_rgb()
//-------------------------------------------------
+// validate_delegates_mfp - test delegate member
+// function functionality
+//-------------------------------------------------
+
+void validate_delegates_mfp()
+{
+ struct base_a
+ {
+ virtual ~base_a() = default;
+ char get_a(void const *&p) { p = this; return 'a'; }
+ virtual char get_a_v(void const *&p) { p = this; return 'A'; }
+ int a;
+ };
+
+ struct base_b
+ {
+ virtual ~base_b() = default;
+ char get_b(void const *&p) { p = this; return 'b'; }
+ virtual char get_b_v(void const *&p) { p = this; return 'B'; }
+ int b;
+ };
+
+ struct multiple_inheritance : base_a, base_b
+ {
+ };
+
+ struct overridden : base_a, base_b
+ {
+ virtual char get_a_v(void const *&p) override { p = this; return 'x'; }
+ virtual char get_b_v(void const *&p) override { p = this; return 'y'; }
+ };
+
+ multiple_inheritance mi;
+ overridden o;
+ diamond_inheritance d;
+ char ch;
+ void const *addr;
+
+ // test non-virtual member functions and "this" pointer adjustment
+ test_delegate cb1(&multiple_inheritance::get_a, &mi);
+ test_delegate cb2(&multiple_inheritance::get_b, &mi);
+
+ addr = nullptr;
+ ch = cb1(addr);
+ if ('a' != ch)
+ osd_printf_error("Error testing delegate non-virtual member function dispatch\n");
+ if (static_cast<base_a *>(&mi) != addr)
+ osd_printf_error("Error testing delegate this pointer adjustment %p -> %p (expected %p)\n", static_cast<void const *>(&mi), addr, static_cast<void const *>(static_cast<base_a *>(&mi)));
+
+ addr = nullptr;
+ ch = cb2(addr);
+ if ('b' != ch)
+ osd_printf_error("Error testing delegate non-virtual member function dispatch\n");
+ if (static_cast<base_b *>(&mi) != addr)
+ osd_printf_error("Error testing delegate this pointer adjustment %p -> %p (expected %p)\n", static_cast<void const *>(&mi), addr, static_cast<void const *>(static_cast<base_b *>(&mi)));
+
+ // test that "this" pointer adjustment survives copy construction
+ test_delegate cb3(cb1);
+ test_delegate cb4(cb2);
+
+ addr = nullptr;
+ ch = cb3(addr);
+ if ('a' != ch)
+ osd_printf_error("Error testing copy constructed delegate non-virtual member function dispatch\n");
+ if (static_cast<base_a *>(&mi) != addr)
+ osd_printf_error("Error testing copy constructed delegate this pointer adjustment %p -> %p (expected %p)\n", static_cast<void const *>(&mi), addr, static_cast<void const *>(static_cast<base_a *>(&mi)));
+
+ addr = nullptr;
+ ch = cb4(addr);
+ if ('b' != ch)
+ osd_printf_error("Error testing copy constructed delegate non-virtual member function dispatch\n");
+ if (static_cast<base_b *>(&mi) != addr)
+ osd_printf_error("Error testing copy constructed delegate this pointer adjustment %p -> %p (expected %p)\n", static_cast<void const *>(&mi), addr, static_cast<void const *>(static_cast<base_b *>(&mi)));
+
+ // test that "this" pointer adjustment survives assignment and doesn't suffer generational loss
+ cb1 = cb4;
+ cb2 = cb3;
+
+ addr = nullptr;
+ ch = cb1(addr);
+ if ('b' != ch)
+ osd_printf_error("Error testing assigned delegate non-virtual member function dispatch\n");
+ if (static_cast<base_b *>(&mi) != addr)
+ osd_printf_error("Error testing assigned delegate this pointer adjustment %p -> %p (expected %p)\n", static_cast<void const *>(&mi), addr, static_cast<void const *>(static_cast<base_b *>(&mi)));
+
+ addr = nullptr;
+ ch = cb2(addr);
+ if ('a' != ch)
+ osd_printf_error("Error testing assigned delegate non-virtual member function dispatch\n");
+ if (static_cast<base_a *>(&mi) != addr)
+ osd_printf_error("Error testing assigned delegate this pointer adjustment %p -> %p (expected %p)\n", static_cast<void const *>(&mi), addr, static_cast<void const *>(static_cast<base_a *>(&mi)));
+
+ // test virtual member functions and "this" pointer adjustment
+ cb1 = test_delegate(&multiple_inheritance::get_a_v, &mi);
+ cb2 = test_delegate(&multiple_inheritance::get_b_v, &mi);
+
+ addr = nullptr;
+ ch = cb1(addr);
+ if ('A' != ch)
+ osd_printf_error("Error testing delegate virtual member function dispatch\n");
+ if (static_cast<base_a *>(&mi) != addr)
+ osd_printf_error("Error testing delegate this pointer adjustment for virtual member function %p -> %p (expected %p)\n", static_cast<void const *>(&mi), addr, static_cast<void const *>(static_cast<base_a *>(&mi)));
+
+ addr = nullptr;
+ ch = cb2(addr);
+ if ('B' != ch)
+ osd_printf_error("Error testing delegate virtual member function dispatch\n");
+ if (static_cast<base_b *>(&mi) != addr)
+ osd_printf_error("Error testing delegate this pointer adjustment for virtual member function %p -> %p (expected %p)\n", static_cast<void const *>(&mi), addr, static_cast<void const *>(static_cast<base_b *>(&mi)));
+
+ // test that virtual member functions survive copy construction
+ test_delegate cb5(cb1);
+ test_delegate cb6(cb2);
+
+ addr = nullptr;
+ ch = cb5(addr);
+ if ('A' != ch)
+ osd_printf_error("Error testing copy constructed delegate virtual member function dispatch\n");
+ if (static_cast<base_a *>(&mi) != addr)
+ osd_printf_error("Error testing copy constructed delegate this pointer adjustment for virtual member function %p -> %p (expected %p)\n", static_cast<void const *>(&mi), addr, static_cast<void const *>(static_cast<base_a *>(&mi)));
+
+ addr = nullptr;
+ ch = cb6(addr);
+ if ('B' != ch)
+ osd_printf_error("Error testing copy constructed delegate virtual member function dispatch\n");
+ if (static_cast<base_b *>(&mi) != addr)
+ osd_printf_error("Error testing copy constructed delegate this pointer adjustment for virtual member function %p -> %p (expected %p)\n", static_cast<void const *>(&mi), addr, static_cast<void const *>(static_cast<base_b *>(&mi)));
+
+ // test virtual member function dispatch through base pointer
+ cb1 = test_delegate(&base_a::get_a_v, static_cast<base_a *>(&o));
+ cb2 = test_delegate(&base_b::get_b_v, static_cast<base_b *>(&o));
+
+ addr = nullptr;
+ ch = cb1(addr);
+ if ('x' != ch)
+ osd_printf_error("Error testing delegate virtual member function dispatch through base class pointer\n");
+ if (&o != addr)
+ osd_printf_error("Error testing delegate this pointer adjustment for virtual member function through base class pointer %p -> %p (expected %p)\n", static_cast<void const *>(static_cast<base_a *>(&o)), addr, static_cast<void const *>(&o));
+
+ addr = nullptr;
+ ch = cb2(addr);
+ if ('y' != ch)
+ osd_printf_error("Error testing delegate virtual member function dispatch through base class pointer\n");
+ if (&o != addr)
+ osd_printf_error("Error testing delegate this pointer adjustment for virtual member function through base class pointer %p -> %p (expected %p)\n", static_cast<void const *>(static_cast<base_b *>(&o)), addr, static_cast<void const *>(&o));
+
+ // test creating delegates for a forward-declared class
+ cb1 = make_diamond_class_delegate(&diamond_inheritance::get_derived_a, &d);
+ cb2 = make_diamond_class_delegate(&diamond_inheritance::get_derived_b, &d);
+
+ addr = nullptr;
+ ch = cb1(addr);
+ if ('a' != ch)
+ osd_printf_error("Error testing delegate non-virtual member function dispatch for incomplete class\n");
+ if (static_cast<virtual_derived_a *>(&d) != addr)
+ osd_printf_error("Error testing delegate this pointer adjustment for incomplete class %p -> %p (expected %p)\n", static_cast<void const *>(&d), addr, static_cast<void const *>(static_cast<virtual_derived_b *>(&d)));
+
+ addr = nullptr;
+ ch = cb2(addr);
+ if ('b' != ch)
+ osd_printf_error("Error testing delegate non-virtual member function dispatch for incomplete class\n");
+ if (static_cast<virtual_derived_b *>(&d) != addr)
+ osd_printf_error("Error testing delegate this pointer adjustment for incomplete class %p -> %p (expected %p)\n", static_cast<void const *>(&d), addr, static_cast<void const *>(static_cast<virtual_derived_b *>(&d)));
+
+#if defined(_MSC_VER) && !defined(__clang__)
+ // test MSVC extension allowing casting member pointer types across virtual inheritance relationships
+ cb1 = make_diamond_class_delegate(&diamond_inheritance::get_base, &d);
+
+ addr = nullptr;
+ ch = cb1(addr);
+ if ('x' != ch)
+ osd_printf_error("Error testing delegate non-virtual member function dispatch for incomplete class\n");
+ if (static_cast<virtual_base *>(&d) != addr)
+ osd_printf_error("Error testing delegate this pointer adjustment for incomplete class %p -> %p (expected %p)\n", static_cast<void const *>(&d), addr, static_cast<void const *>(static_cast<virtual_base *>(&d)));
+#endif // defined(_MSC_VER) && !defined(__clang__)
+}
+
+
+//-------------------------------------------------
+// validate_delegates_latebind - test binding a
+// delegate to an object after the function is
+// set
+//-------------------------------------------------
+
+void validate_delegates_latebind()
+{
+ struct derived_a : pure_virtual_base, delegate_late_bind
+ {
+ virtual char operator()(void const *&p) const override { p = this; return 'a'; }
+ };
+
+ struct derived_b : pure_virtual_base, delegate_late_bind
+ {
+ virtual char operator()(void const *&p) const override { p = this; return 'b'; }
+ };
+
+ struct unrelated : delegate_late_bind
+ {
+ };
+
+ char ch;
+ void const *addr;
+ derived_a a;
+ derived_b b;
+ unrelated u;
+
+ // delegate with no target object
+ test_delegate cb1(&pure_virtual_base::operator(), static_cast<pure_virtual_base *>(nullptr));
+
+ // test late bind on construction
+ test_delegate cb2(cb1, a);
+ addr = nullptr;
+ ch = cb2(addr);
+ if (('a' != ch) || (&a != addr))
+ osd_printf_error("Error testing delegate late bind on construction\n");
+
+ // test explicit late bind
+ cb1.late_bind(b);
+ ch = cb1(addr);
+ if (('b' != ch) || (&b != addr))
+ osd_printf_error("Error testing delegate explicit late bind\n");
+
+ // test late bind when object is set
+ cb1.late_bind(a);
+ ch = cb1(addr);
+ if (('a' != ch) || (&a != addr))
+ osd_printf_error("Error testing delegate explicit late bind when object is set\n");
+
+ // test late bind on copy of delegate with target set
+ test_delegate cb3(cb1, b);
+ addr = nullptr;
+ ch = cb3(addr);
+ if (('b' != ch) || (&b != addr))
+ osd_printf_error("Error testing delegate late bind on construction using template with object set\n");
+
+ // test late bind exception
+ ch = '-';
+ try
+ {
+ cb1.late_bind(u);
+ }
+ catch (binding_type_exception const &e)
+ {
+ if ((e.target_type() != typeid(pure_virtual_base)) || (e.actual_type() != typeid(unrelated)))
+ {
+ osd_printf_error(
+ "Error testing delegate late bind type error %s -> %s (expected %s -> %s)\n",
+ e.actual_type().name(),
+ e.target_type().name(),
+ typeid(unrelated).name(),
+ typeid(pure_virtual_base).name());
+ }
+ ch = '+';
+ }
+ if ('+' != ch)
+ osd_printf_error("Error testing delegate late bind type error\n");
+
+ // test syntax for creating delegate with alternate late bind base
+ delegate<char (void const *&), pure_virtual_base> cb4(
+ [] (auto &o, void const *&p) { p = &o; return 'l'; },
+ static_cast<unrelated *>(nullptr));
+ try { cb1.late_bind(a); }
+ catch (binding_type_exception const &) { }
+}
+
+
+//-------------------------------------------------
+// validate_delegates_functoid - test delegate
+// functoid functionality
+//-------------------------------------------------
+
+void validate_delegates_functoid()
+{
+ using void_delegate = delegate<void (void const *&)>;
+ struct const_only
+ {
+ char operator()(void const *&p) const { return 'C'; }
+ };
+
+ struct const_or_not
+ {
+ char operator()(void const *&p) { return 'n'; }
+ char operator()(void const *&p) const { return 'c'; }
+ };
+
+ struct noncopyable
+ {
+ noncopyable() = default;
+ noncopyable(noncopyable const &) = delete;
+ noncopyable &operator=(noncopyable const &) = delete;
+
+ char operator()(void const *&p) { p = this; return '*'; }
+ };
+
+ noncopyable n;
+ char ch;
+ void const *addr = nullptr;
+
+ // test that const call operators are supported
+ test_delegate cb1{ const_only() };
+ if ('C' != cb1(addr))
+ osd_printf_error("Error testing delegate functoid dispatch\n");
+
+ // test that non-const call operators are preferred
+ cb1 = test_delegate{ const_or_not() };
+ if ('n' != cb1(addr))
+ osd_printf_error("Error testing delegate functoid dispatch\n");
+
+ // test that functoids are implicitly mutable
+ cb1 = test_delegate{ [a = &addr, c = '0'] (void const *&p) mutable { p = a; return c++; } };
+
+ addr = nullptr;
+ ch = cb1(addr);
+ if (('0' != ch) || (&addr != addr))
+ osd_printf_error("Error testing delegate functoid %c (expected 0)\n", ch);
+
+ addr = nullptr;
+ ch = cb1(addr);
+ if (('1' != ch) || (&addr != addr))
+ osd_printf_error("Error testing delegate functoid %c (expected 1)\n", ch);
+
+ // test that functoids survive copy construction
+ test_delegate cb2(cb1);
+
+ addr = nullptr;
+ ch = cb2(addr);
+ if (('2' != ch) || (&addr != addr))
+ osd_printf_error("Error testing delegate functoid %c (expected 2)\n", ch);
+
+ addr = nullptr;
+ ch = cb2(addr);
+ if (('3' != ch) || (&addr != addr))
+ osd_printf_error("Error testing delegate functoid %c (expected 3)\n", ch);
+
+ addr = nullptr;
+ ch = cb1(addr);
+ if (('2' != ch) || (&addr != addr))
+ osd_printf_error("Error testing delegate functoid %c (expected 2)\n", ch);
+
+ // test that functoids survive assignment
+ cb1 = cb2;
+
+ addr = nullptr;
+ ch = cb1(addr);
+ if (('4' != ch) || (&addr != addr))
+ osd_printf_error("Error testing delegate functoid %c (expected 4)\n", ch);
+
+ addr = nullptr;
+ ch = cb1(addr);
+ if (('5' != ch) || (&addr != addr))
+ osd_printf_error("Error testing delegate functoid %c (expected 5)\n", ch);
+
+ addr = nullptr;
+ ch = cb2(addr);
+ if (('4' != ch) || (&addr != addr))
+ osd_printf_error("Error testing delegate functoid %c (expected 4)\n", ch);
+
+ // test that std::ref can be used with non-copyable functoids
+ test_delegate cb3(std::ref(n));
+
+ addr = nullptr;
+ ch = cb3(addr);
+ if (('*' != ch) || (&n != addr))
+ osd_printf_error("Error testing delegate with functoid reference wrapper %p (expected %p)\n", addr, static_cast<void const *>(&n));
+
+ // test that std::ref survives copy construction and assignment
+ cb2 = cb3;
+ test_delegate cb4(cb3);
+
+ addr = nullptr;
+ ch = cb2(addr);
+ if (('*' != ch) || (&n != addr))
+ osd_printf_error("Error testing delegate with functoid reference wrapper %p (expected %p)\n", addr, static_cast<void const *>(&n));
+
+ addr = nullptr;
+ ch = cb4(addr);
+ if (('*' != ch) || (&n != addr))
+ osd_printf_error("Error testing delegate with functoid reference wrapper %p (expected %p)\n", addr, static_cast<void const *>(&n));
+
+ // test discarding return value for delegates returning void
+ void_delegate void_cb1{ [&cb1] (void const *&p) { p = &cb1; return 123; } };
+ void_delegate void_cb2{ std::ref(n) };
+
+ addr = nullptr;
+ void_cb1(addr);
+ if (&cb1 != addr)
+ osd_printf_error("Error testing delegate with functoid requiring adapter %p (expected %p)\n", addr, static_cast<void const *>(&cb1));
+
+ addr = nullptr;
+ void_cb2(addr);
+ if (&n != addr)
+ osd_printf_error("Error testing delegate with functoid requiring adapter %p (expected %p)\n", addr, static_cast<void const *>(&n));
+
+ // test that adaptor is generated after assignment
+ void_cb2 = void_cb1;
+
+ addr = nullptr;
+ void_cb2(addr);
+ if (&cb1 != addr)
+ osd_printf_error("Error testing delegate with functoid requiring adapter %p (expected %p)\n", addr, static_cast<void const *>(&cb1));
+}
+
+} // anonymous namespace
+
+
+
+//-------------------------------------------------
+// get_defstr_index - return the index of the
+// string assuming it is one of the default
+// strings
+//-------------------------------------------------
+
+inline int validity_checker::get_defstr_index(const char *string, bool suppress_error)
+{
+ // check for strings that should be DEF_STR
+ auto strindex = m_defstr_map.find(string);
+ if (!suppress_error && strindex != m_defstr_map.end() && string != ioport_string_from_index(strindex->second))
+ osd_printf_error("Must use DEF_STR( %s )\n", string);
+ return (strindex != m_defstr_map.end()) ? strindex->second : 0;
+}
+
+
+
+//-------------------------------------------------
+// validate_tag - ensure that the given tag
+// meets the general requirements
+//-------------------------------------------------
+
+void validity_checker::validate_tag(const char *tag)
+{
+ // some common names that are now deprecated
+ if (strcmp(tag, "main") == 0 || strcmp(tag, "audio") == 0 || strcmp(tag, "sound") == 0 || strcmp(tag, "left") == 0 || strcmp(tag, "right") == 0)
+ osd_printf_error("Invalid generic tag '%s' used\n", tag);
+
+ // scan for invalid characters
+ static char const *const validchars = "abcdefghijklmnopqrstuvwxyz0123456789_.:^$";
+ for (char const *p = tag; *p; ++p)
+ {
+ // only lower-case permitted
+ if (*p != tolower(u8(*p)))
+ {
+ osd_printf_error("Tag '%s' contains upper-case characters\n", tag);
+ break;
+ }
+ if (*p == ' ')
+ {
+ osd_printf_error("Tag '%s' contains spaces\n", tag);
+ break;
+ }
+ if (!strchr(validchars, *p))
+ {
+ osd_printf_error("Tag '%s' contains invalid character '%c'\n", tag, *p);
+ break;
+ }
+ }
+
+ // find the start of the final tag
+ const char *begin = strrchr(tag, ':');
+ if (begin == nullptr)
+ begin = tag;
+ else
+ begin += 1;
+
+ // 0-length = bad
+ if (*begin == 0)
+ osd_printf_error("Found 0-length tag\n");
+
+ // too short/too long = bad
+ if (strlen(begin) < MIN_TAG_LENGTH)
+ osd_printf_error("Tag '%s' is too short (must be at least %d characters)\n", tag, MIN_TAG_LENGTH);
+}
+
+
+
+//**************************************************************************
+// VALIDATION FUNCTIONS
+//**************************************************************************
+
+//-------------------------------------------------
+// validity_checker - constructor
+//-------------------------------------------------
+
+validity_checker::validity_checker(emu_options &options, bool quick)
+ : m_drivlist(options)
+ , m_errors(0)
+ , m_warnings(0)
+ , m_print_verbose(options.verbose())
+ , m_current_driver(nullptr)
+ , m_current_device(nullptr)
+ , m_current_ioport(nullptr)
+ , m_checking_card(false)
+ , m_quick(quick)
+{
+ // pre-populate the defstr map with all the default strings
+ for (int strnum = 1; strnum < INPUT_STRING_COUNT; strnum++)
+ {
+ const char *string = ioport_string_from_index(strnum);
+ if (string != nullptr)
+ m_defstr_map.insert(std::make_pair(string, strnum));
+ }
+}
+
+//-------------------------------------------------
+// validity_checker - destructor
+//-------------------------------------------------
+
+validity_checker::~validity_checker()
+{
+ validate_end();
+}
+
+//-------------------------------------------------
+// check_driver - check a single driver
+//-------------------------------------------------
+
+void validity_checker::check_driver(const game_driver &driver)
+{
+ // simply validate the one driver
+ validate_begin();
+ validate_one(driver);
+ validate_end();
+}
+
+
+//-------------------------------------------------
+// check_shared_source - check all drivers that
+// share the same source file as the given driver
+//-------------------------------------------------
+
+void validity_checker::check_shared_source(const game_driver &driver)
+{
+ // initialize
+ validate_begin();
+
+ // then iterate over all drivers and check the ones that share the same source file
+ m_drivlist.reset();
+ while (m_drivlist.next())
+ if (strcmp(driver.type.source(), m_drivlist.driver().type.source()) == 0)
+ validate_one(m_drivlist.driver());
+
+ // cleanup
+ validate_end();
+}
+
+
+//-------------------------------------------------
+// check_all_matching - check all drivers whose
+// names match the given string
+//-------------------------------------------------
+
+bool validity_checker::check_all_matching(const char *string)
+{
+ // start by checking core stuff
+ validate_begin();
+ validate_integer_semantics();
+ validate_inlines();
+ validate_rgb();
+ validate_delegates_mfp();
+ validate_delegates_latebind();
+ validate_delegates_functoid();
+
+ // if we had warnings or errors, output
+ if (m_errors > 0 || m_warnings > 0 || !m_verbose_text.empty())
+ {
+ output_via_delegate(OSD_OUTPUT_CHANNEL_ERROR, "Core: %d errors, %d warnings\n", m_errors, m_warnings);
+ if (m_errors > 0)
+ output_indented_errors(m_error_text, "Errors");
+ if (m_warnings > 0)
+ output_indented_errors(m_warning_text, "Warnings");
+ if (!m_verbose_text.empty())
+ output_indented_errors(m_verbose_text, "Messages");
+ output_via_delegate(OSD_OUTPUT_CHANNEL_ERROR, "\n");
+ }
+
+ // then iterate over all drivers and check them
+ m_drivlist.reset();
+ bool validated_any = false;
+ while (m_drivlist.next())
+ {
+ if (driver_list::matches(string, m_drivlist.driver().name))
+ {
+ validate_one(m_drivlist.driver());
+ validated_any = true;
+ }
+ }
+
+ // validate devices
+ if (!string)
+ validate_device_types();
+
+ // cleanup
+ validate_end();
+
+ // if we failed to match anything, it
+ if (string && !validated_any)
+ throw emu_fatalerror(EMU_ERR_NO_SUCH_SYSTEM, "No matching systems found for '%s'", string);
+
+ return !(m_errors > 0 || m_warnings > 0);
+}
+
+
+//-------------------------------------------------
+// validate_begin - prepare for validation by
+// taking over the output callbacks and resetting
+// our internal state
+//-------------------------------------------------
+
+void validity_checker::validate_begin()
+{
+ // take over error and warning outputs
+ osd_output::push(this);
+
+ // reset all our maps
+ m_names_map.clear();
+ m_descriptions_map.clear();
+ m_roms_map.clear();
+ m_defstr_map.clear();
+ m_region_map.clear();
+ m_ioport_set.clear();
+ m_slotcard_set.clear();
+
+ // reset internal state
+ m_errors = 0;
+ m_warnings = 0;
+ m_already_checked.clear();
+}
+
+
+//-------------------------------------------------
+// validate_end - restore output callbacks and
+// clean up
+//-------------------------------------------------
+
+void validity_checker::validate_end()
+{
+ // restore the original output callbacks
+ osd_output::pop(this);
+}
+
+
+//-------------------------------------------------
+// validate_drivers - master validity checker
+//-------------------------------------------------
+
+void validity_checker::validate_one(const game_driver &driver)
+{
+ // help verbose validation detect configuration-related crashes
+ if (m_print_verbose)
+ output_via_delegate(OSD_OUTPUT_CHANNEL_ERROR, "Validating driver %s (%s)...\n", driver.name, core_filename_extract_base(driver.type.source()));
+
+ // set the current driver
+ m_current_driver = &driver;
+ m_current_device = nullptr;
+ m_current_ioport = nullptr;
+ m_region_map.clear();
+ m_ioport_set.clear();
+ m_checking_card = false;
+
+ // reset error/warning state
+ int start_errors = m_errors;
+ int start_warnings = m_warnings;
+ m_error_text.clear();
+ m_warning_text.clear();
+ m_verbose_text.clear();
+
+ // wrap in try/catch to catch fatalerrors
+ try
+ {
+ machine_config config(driver, m_blank_options);
+ validate_driver(config.root_device());
+ validate_roms(config.root_device());
+ validate_inputs(config.root_device());
+ validate_devices(config);
+ }
+ catch (emu_fatalerror const &err)
+ {
+ osd_printf_error("Fatal error %s", err.what());
+ }
+
+ // if we had warnings or errors, output
+ if (m_errors > start_errors || m_warnings > start_warnings || !m_verbose_text.empty())
+ {
+ if (!m_print_verbose)
+ output_via_delegate(OSD_OUTPUT_CHANNEL_ERROR, "Driver %s (file %s): ", driver.name, core_filename_extract_base(driver.type.source()));
+ output_via_delegate(OSD_OUTPUT_CHANNEL_ERROR, "%d errors, %d warnings\n", m_errors - start_errors, m_warnings - start_warnings);
+ if (m_errors > start_errors)
+ output_indented_errors(m_error_text, "Errors");
+ if (m_warnings > start_warnings)
+ output_indented_errors(m_warning_text, "Warnings");
+ if (!m_verbose_text.empty())
+ output_indented_errors(m_verbose_text, "Messages");
+ output_via_delegate(OSD_OUTPUT_CHANNEL_ERROR, "\n");
+ }
+
+ // reset the driver/device
+ m_current_driver = nullptr;
+ m_current_device = nullptr;
+ m_current_ioport = nullptr;
+ m_region_map.clear();
+ m_ioport_set.clear();
+ m_checking_card = false;
+}
+
+
+//-------------------------------------------------
// validate_driver - validate basic driver
// information
//-------------------------------------------------
@@ -1491,6 +2084,10 @@ void validity_checker::validate_driver(device_t &root)
if (clone_of != -1 && (clone_of = driver_list::non_bios_clone(clone_of)) != -1)
osd_printf_error("Driver is a clone of a clone\n");
+ // look for drivers specifying a parent ROM device type
+ if (root.type().parent_rom_device_type())
+ osd_printf_error("Driver has parent ROM device type '%s'\n", root.type().parent_rom_device_type()->shortname());
+
// make sure the driver name is not too long
if (!is_clone && strlen(m_current_driver->name) > 16)
osd_printf_error("Parent driver name must be 16 characters or less\n");
@@ -1539,18 +2136,18 @@ void validity_checker::validate_driver(device_t &root)
device_t::feature_type const imperfect(m_current_driver->type.imperfect_features());
if (!(m_current_driver->flags & (machine_flags::IS_BIOS_ROOT | machine_flags::NO_SOUND_HW)) && !(unemulated & device_t::feature::SOUND))
{
- sound_interface_iterator iter(root);
+ speaker_device_enumerator iter(root);
if (!iter.first())
osd_printf_error("Driver is missing MACHINE_NO_SOUND or MACHINE_NO_SOUND_HW flag\n");
}
// catch invalid flag combinations
if (unemulated & ~device_t::feature::ALL)
- osd_printf_error("Driver has invalid unemulated feature flags (0x%08lX)\n", static_cast<unsigned long>(unemulated & ~device_t::feature::ALL));
+ osd_printf_error("Driver has invalid unemulated feature flags (0x%08X)\n", util::underlying_value(unemulated & ~device_t::feature::ALL));
if (imperfect & ~device_t::feature::ALL)
- osd_printf_error("Driver has invalid imperfect feature flags (0x%08lX)\n", static_cast<unsigned long>(imperfect & ~device_t::feature::ALL));
+ osd_printf_error("Driver has invalid imperfect feature flags (0x%08X)\n", util::underlying_value(imperfect & ~device_t::feature::ALL));
if (unemulated & imperfect)
- osd_printf_error("Driver cannot have features that are both unemulated and imperfect (0x%08lX)\n", static_cast<unsigned long>(unemulated & imperfect));
+ osd_printf_error("Driver cannot have features that are both unemulated and imperfect (0x%08X)\n", util::underlying_value(unemulated & imperfect));
if ((m_current_driver->flags & machine_flags::NO_SOUND_HW) && ((unemulated | imperfect) & device_t::feature::SOUND))
osd_printf_error("Machine without sound hardware cannot have unemulated/imperfect sound\n");
}
@@ -1563,7 +2160,7 @@ void validity_checker::validate_driver(device_t &root)
void validity_checker::validate_roms(device_t &root)
{
// iterate, starting with the driver's ROMs and continuing with device ROMs
- for (device_t &device : device_iterator(root))
+ for (device_t &device : device_enumerator(root))
{
// track the current device
m_current_device = &device;
@@ -1574,7 +2171,6 @@ void validity_checker::validate_roms(device_t &root)
u32 current_length = 0;
int items_since_region = 1;
int last_bios = 0, max_bios = 0;
- int total_files = 0;
std::unordered_map<std::string, int> bios_names;
std::unordered_map<std::string, std::string> bios_descs;
char const *defbios = nullptr;
@@ -1608,34 +2204,46 @@ void validity_checker::validate_roms(device_t &root)
current_length = ROMREGION_GETLENGTH(romp);
if (!m_region_map.emplace(fulltag, current_length).second)
osd_printf_error("Multiple ROM_REGIONs with the same tag '%s' defined\n", fulltag);
+ if (current_length == 0)
+ osd_printf_error("ROM region '%s' has zero length\n", fulltag);
}
else if (ROMENTRY_ISSYSTEM_BIOS(romp)) // If this is a system bios, make sure it is using the next available bios number
{
int const bios_flags = ROM_GETBIOSFLAGS(romp);
char const *const biosname = romp->name;
if (bios_flags != last_bios + 1)
- osd_printf_error("Non-sequential BIOS %s (specified as %d, expected to be %d)\n", biosname, bios_flags - 1, last_bios);
+ osd_printf_error("Non-sequential BIOS %s (specified as %d, expected to be %d)\n", biosname ? biosname : "UNNAMED", bios_flags - 1, last_bios);
last_bios = bios_flags;
// validate the name
- if (strlen(biosname) > 16)
- osd_printf_error("BIOS name %s exceeds maximum 16 characters\n", biosname);
- for (char const *s = biosname; *s; ++s)
+ if (!biosname || biosname[0] == 0)
+ osd_printf_error("BIOS %d is missing a name\n", bios_flags - 1);
+ else
{
- if (((*s < '0') || (*s > '9')) && ((*s < 'a') || (*s > 'z')) && (*s != '.') && (*s != '_') && (*s != '-'))
+ if (strlen(biosname) > 16)
+ osd_printf_error("BIOS name %s exceeds maximum 16 characters\n", biosname);
+ for (char const *s = biosname; *s; ++s)
{
- osd_printf_error("BIOS name %s contains invalid characters\n", biosname);
- break;
+ if (((*s < '0') || (*s > '9')) && ((*s < 'a') || (*s > 'z')) && (*s != '.') && (*s != '_') && (*s != '-'))
+ {
+ osd_printf_error("BIOS name %s contains invalid characters\n", biosname);
+ break;
+ }
}
- }
- // check for duplicate names/descriptions
- auto const nameins = bios_names.emplace(biosname, bios_flags);
- if (!nameins.second)
- osd_printf_error("Duplicate BIOS name %s specified (%d and %d)\n", biosname, nameins.first->second, bios_flags - 1);
- auto const descins = bios_descs.emplace(romp->hashdata, biosname);
- if (!descins.second)
- osd_printf_error("BIOS %s has duplicate description '%s' (was %s)\n", biosname, romp->hashdata, descins.first->second);
+ // check for duplicate names/descriptions
+ auto const nameins = bios_names.emplace(biosname, bios_flags);
+ if (!nameins.second)
+ osd_printf_error("Duplicate BIOS name %s specified (%d and %d)\n", biosname, nameins.first->second, bios_flags - 1);
+ if (!romp->hashdata || romp->hashdata[0] == 0)
+ osd_printf_error("BIOS %s has empty description\n", biosname);
+ else
+ {
+ auto const descins = bios_descs.emplace(romp->hashdata, biosname);
+ if (!descins.second)
+ osd_printf_error("BIOS %s has duplicate description '%s' (was %s)\n", biosname, romp->hashdata, descins.first->second);
+ }
+ }
}
else if (ROMENTRY_ISDEFAULT_BIOS(romp)) // if this is a default BIOS setting, remember it so it to check at the end
{
@@ -1645,7 +2253,6 @@ void validity_checker::validate_roms(device_t &root)
{
// track the last filename we found
last_name = romp->name;
- total_files++;
max_bios = std::max<int>(max_bios, ROM_GETBIOSFLAGS(romp));
// validate the name
@@ -1704,7 +2311,7 @@ void validity_checker::validate_roms(device_t &root)
// analog input field
//-------------------------------------------------
-void validity_checker::validate_analog_input_field(ioport_field &field)
+void validity_checker::validate_analog_input_field(const ioport_field &field)
{
// analog ports must have a valid sensitivity
if (field.sensitivity() == 0)
@@ -1785,54 +2392,56 @@ void validity_checker::validate_analog_input_field(ioport_field &field)
// setting
//-------------------------------------------------
-void validity_checker::validate_dip_settings(ioport_field &field)
+void validity_checker::validate_dip_settings(const ioport_field &field)
{
- const char *demo_sounds = ioport_string_from_index(INPUT_STRING_Demo_Sounds);
- const char *flipscreen = ioport_string_from_index(INPUT_STRING_Flip_Screen);
+ char const *const demo_sounds = ioport_string_from_index(INPUT_STRING_Demo_Sounds);
+ char const *const flipscreen = ioport_string_from_index(INPUT_STRING_Flip_Screen);
+ char const *const name = field.specific_name() ? field.specific_name() : "UNNAMED";
u8 coin_list[__input_string_coinage_end + 1 - __input_string_coinage_start] = { 0 };
bool coin_error = false;
// iterate through the settings
- for (ioport_setting &setting : field.settings())
+ for (auto setting = field.settings().begin(); field.settings().end() != setting; ++setting)
{
// note any coinage strings
- int strindex = get_defstr_index(setting.name());
+ int strindex = get_defstr_index(setting->name());
if (strindex >= __input_string_coinage_start && strindex <= __input_string_coinage_end)
coin_list[strindex - __input_string_coinage_start] = 1;
// make sure demo sounds default to on
- if (field.name() == demo_sounds && strindex == INPUT_STRING_On && field.defvalue() != setting.value())
+ if (name == demo_sounds && strindex == INPUT_STRING_On && field.defvalue() != setting->value())
osd_printf_error("Demo Sounds must default to On\n");
// check for bad demo sounds options
- if (field.name() == demo_sounds && (strindex == INPUT_STRING_Yes || strindex == INPUT_STRING_No))
- osd_printf_error("Demo Sounds option must be Off/On, not %s\n", setting.name());
+ if (name == demo_sounds && (strindex == INPUT_STRING_Yes || strindex == INPUT_STRING_No))
+ osd_printf_error("Demo Sounds option must be Off/On, not %s\n", setting->name());
// check for bad flip screen options
- if (field.name() == flipscreen && (strindex == INPUT_STRING_Yes || strindex == INPUT_STRING_No))
- osd_printf_error("Flip Screen option must be Off/On, not %s\n", setting.name());
+ if (name == flipscreen && (strindex == INPUT_STRING_Yes || strindex == INPUT_STRING_No))
+ osd_printf_error("Flip Screen option must be Off/On, not %s\n", setting->name());
// if we have a neighbor, compare ourselves to him
- if (setting.next() != nullptr)
+ auto const nextsetting = std::next(setting);
+ if (field.settings().end() != nextsetting)
{
- // check for inverted off/on dispswitch order
- int next_strindex = get_defstr_index(setting.next()->name(), true);
+ // check for inverted off/on DIP switch order
+ int next_strindex = get_defstr_index(nextsetting->name(), true);
if (strindex == INPUT_STRING_On && next_strindex == INPUT_STRING_Off)
- osd_printf_error("%s option must have Off/On options in the order: Off, On\n", field.name());
+ osd_printf_error("%s option must have Off/On options in the order: Off, On\n", name);
- // check for inverted yes/no dispswitch order
+ // check for inverted yes/no DIP switch order
else if (strindex == INPUT_STRING_Yes && next_strindex == INPUT_STRING_No)
- osd_printf_error("%s option must have Yes/No options in the order: No, Yes\n", field.name());
+ osd_printf_error("%s option must have Yes/No options in the order: No, Yes\n", name);
- // check for inverted upright/cocktail dispswitch order
+ // check for inverted upright/cocktail DIP switch order
else if (strindex == INPUT_STRING_Cocktail && next_strindex == INPUT_STRING_Upright)
- osd_printf_error("%s option must have Upright/Cocktail options in the order: Upright, Cocktail\n", field.name());
+ osd_printf_error("%s option must have Upright/Cocktail options in the order: Upright, Cocktail\n", name);
// check for proper coin ordering
else if (strindex >= __input_string_coinage_start && strindex <= __input_string_coinage_end && next_strindex >= __input_string_coinage_start && next_strindex <= __input_string_coinage_end &&
- strindex >= next_strindex && setting.condition() == setting.next()->condition())
+ strindex >= next_strindex && setting->condition() == nextsetting->condition())
{
- osd_printf_error("%s option has unsorted coinage %s > %s\n", field.name(), setting.name(), setting.next()->name());
+ osd_printf_error("%s option has unsorted coinage %s > %s\n", name, setting->name(), nextsetting->name());
coin_error = true;
}
}
@@ -1842,7 +2451,7 @@ void validity_checker::validate_dip_settings(ioport_field &field)
if (coin_error)
{
output_via_delegate(OSD_OUTPUT_CHANNEL_ERROR, " Note proper coin sort order should be:\n");
- for (int entry = 0; entry < ARRAY_LENGTH(coin_list); entry++)
+ for (int entry = 0; entry < std::size(coin_list); entry++)
if (coin_list[entry])
output_via_delegate(OSD_OUTPUT_CHANNEL_ERROR, " %s\n", ioport_string_from_index(__input_string_coinage_start + entry));
}
@@ -1854,8 +2463,14 @@ void validity_checker::validate_dip_settings(ioport_field &field)
// stored within an ioport field or setting
//-------------------------------------------------
-void validity_checker::validate_condition(ioport_condition &condition, device_t &device)
+void validity_checker::validate_condition(const ioport_condition &condition, device_t &device)
{
+ if (condition.tag() == nullptr)
+ {
+ osd_printf_error("Condition referencing null ioport tag\n");
+ return;
+ }
+
// resolve the tag, then find a matching port
if (m_ioport_set.find(device.subtag(condition.tag())) == m_ioport_set.end())
osd_printf_error("Condition referencing non-existent ioport tag '%s'\n", condition.tag());
@@ -1869,7 +2484,7 @@ void validity_checker::validate_condition(ioport_condition &condition, device_t
void validity_checker::validate_inputs(device_t &root)
{
// iterate over devices
- for (device_t &device : device_iterator(root))
+ for (device_t &device : device_enumerator(root))
{
// see if this device has ports; if not continue
if (device.input_ports() == nullptr)
@@ -1914,40 +2529,49 @@ void validity_checker::validate_inputs(device_t &root)
}
// iterate through the fields on this port
- for (ioport_field &field : port.second->fields())
+ for (ioport_field const &field : port.second->fields())
{
// verify analog inputs
if (field.is_analog())
validate_analog_input_field(field);
- // look for invalid (0) types which should be mapped to IPT_OTHER
- if (field.type() == IPT_INVALID)
+ // checks based on field type
+ if ((field.type() > IPT_UI_FIRST) && (field.type() < IPT_UI_LAST))
+ {
+ osd_printf_error("Field has invalid UI control type\n");
+ }
+ else if (field.type() == IPT_INVALID)
+ {
osd_printf_error("Field has an invalid type (0); use IPT_OTHER instead\n");
-
- if (field.type() == IPT_SPECIAL)
+ }
+ else if (field.type() == IPT_SPECIAL)
+ {
osd_printf_error("Field has an invalid type IPT_SPECIAL\n");
-
- // verify dip switches
- if (field.type() == IPT_DIPSWITCH)
+ }
+ else if (field.type() == IPT_DIPSWITCH)
{
- // dip switch fields must have a specific name
+ // DIP switch fields must have a specific name
if (field.specific_name() == nullptr)
osd_printf_error("DIP switch has no specific name\n");
// verify the settings list
validate_dip_settings(field);
}
-
- // verify config settings
- if (field.type() == IPT_CONFIG)
+ else if (field.type() == IPT_CONFIG)
{
// config fields must have a specific name
if (field.specific_name() == nullptr)
osd_printf_error("Config switch has no specific name\n");
}
+ else if (field.type() == IPT_ADJUSTER)
+ {
+ // adjuster fields must have a specific name
+ if (field.specific_name() == nullptr)
+ osd_printf_error("Adjuster has no specific name\n");
+ }
// verify names
- const char *name = field.specific_name();
+ char const *const name = field.specific_name();
if (name != nullptr)
{
// check for empty string
@@ -1961,9 +2585,6 @@ void validity_checker::validate_inputs(device_t &root)
// check for invalid UTF-8
if (!utf8_is_valid_string(name))
osd_printf_error("Field '%s' has invalid characters\n", name);
-
- // look up the string and print an error if default strings are not used
- /*strindex =get_defstr_index(defstr_map, name, driver, &error);*/
}
// verify conditions on the field
@@ -1971,7 +2592,7 @@ void validity_checker::validate_inputs(device_t &root)
validate_condition(field.condition(), device);
// verify conditions on the settings
- for (ioport_setting &setting : field.settings())
+ for (ioport_setting const &setting : field.settings())
if (!setting.condition().none())
validate_condition(setting.condition(), device);
@@ -2011,7 +2632,7 @@ void validity_checker::validate_devices(machine_config &config)
{
std::unordered_set<std::string> device_map;
- for (device_t &device : device_iterator(config.root_device()))
+ for (device_t &device : device_enumerator(config.root_device()))
{
// track the current device
m_current_device = &device;
@@ -2043,6 +2664,10 @@ void validity_checker::validate_devices(machine_config &config)
if (slot->default_option() != nullptr && option.first == slot->default_option())
continue;
+ // if we need to save time, instantiate and validate each slot card type at most once
+ if (m_quick && !m_slotcard_set.insert(option.second->devtype().shortname()).second)
+ continue;
+
m_checking_card = true;
device_t *card;
{
@@ -2057,7 +2682,7 @@ void validity_checker::validate_devices(machine_config &config)
additions(card);
}
- for (device_slot_interface &subslot : slot_interface_iterator(*card))
+ for (device_slot_interface &subslot : slot_interface_enumerator(*card))
{
if (subslot.fixed())
{
@@ -2077,14 +2702,15 @@ void validity_checker::validate_devices(machine_config &config)
}
}
- for (device_t &card_dev : device_iterator(*card))
+ for (device_t &card_dev : device_enumerator(*card))
card_dev.config_complete();
validate_roms(*card);
- for (device_t &card_dev : device_iterator(*card))
+ for (device_t &card_dev : device_enumerator(*card))
{
m_current_device = &card_dev;
card_dev.findit(this);
+ validate_tag(card_dev.basetag());
card_dev.validity_check(*this);
m_current_device = nullptr;
}
@@ -2120,10 +2746,10 @@ void validity_checker::validate_device_types()
device_t *const dev = config.device_add(type.shortname(), type, 0);
char const *name((dev->shortname() && *dev->shortname()) ? dev->shortname() : type.type().name());
- std::string const description((dev->source() && *dev->source()) ? util::string_format("%s(%s)", core_filename_extract_base(dev->source()).c_str(), name) : name);
+ std::string const description((dev->source() && *dev->source()) ? util::string_format("%s(%s)", core_filename_extract_base(dev->source()), name) : name);
if (m_print_verbose)
- output_via_delegate(OSD_OUTPUT_CHANNEL_ERROR, "Validating device %s...\n", description.c_str());
+ output_via_delegate(OSD_OUTPUT_CHANNEL_ERROR, "Validating device %s...\n", description);
// ensure shortname exists
if (!dev->shortname() || !*dev->shortname())
@@ -2199,11 +2825,22 @@ void validity_checker::validate_device_types()
device_t::feature_type const unemulated(dev->type().unemulated_features());
device_t::feature_type const imperfect(dev->type().imperfect_features());
if (unemulated & ~device_t::feature::ALL)
- osd_printf_error("Device has invalid unemulated feature flags (0x%08lX)\n", static_cast<unsigned long>(unemulated & ~device_t::feature::ALL));
+ osd_printf_error("Device has invalid unemulated feature flags (0x%08X)\n", util::underlying_value(unemulated & ~device_t::feature::ALL));
if (imperfect & ~device_t::feature::ALL)
- osd_printf_error("Device has invalid imperfect feature flags (0x%08lX)\n", static_cast<unsigned long>(imperfect & ~device_t::feature::ALL));
+ osd_printf_error("Device has invalid imperfect feature flags (0x%08X)\n", util::underlying_value(imperfect & ~device_t::feature::ALL));
if (unemulated & imperfect)
- osd_printf_error("Device cannot have features that are both unemulated and imperfect (0x%08lX)\n", static_cast<unsigned long>(unemulated & imperfect));
+ osd_printf_error("Device cannot have features that are both unemulated and imperfect (0x%08X)\n", util::underlying_value(unemulated & imperfect));
+
+ // check that parents are only ever one generation deep
+ auto const parent(dev->type().parent_rom_device_type());
+ if (parent)
+ {
+ auto const grandparent(parent->parent_rom_device_type());
+ if ((dev->type() == *parent) || !strcmp(parent->shortname(), name))
+ osd_printf_error("Device has parent ROM set that identical to its type\n");
+ if (grandparent)
+ osd_printf_error("Device has parent ROM set '%s' which has parent ROM set '%s'\n", parent->shortname(), grandparent->shortname());
+ }
// give devices some of the same scrutiny that drivers get - necessary for cards not default for any slots
validate_roms(*dev);
@@ -2256,7 +2893,7 @@ void validity_checker::build_output_prefix(std::ostream &str) const
// error_output - error message output override
//-------------------------------------------------
-void validity_checker::output_callback(osd_output_channel channel, const util::format_argument_pack<std::ostream> &args)
+void validity_checker::output_callback(osd_output_channel channel, const util::format_argument_pack<char> &args)
{
std::ostringstream output;
switch (channel)
@@ -2326,5 +2963,5 @@ void validity_checker::output_indented_errors(std::string &text, const char *hea
if (text[text.size()-1] == '\n')
text.erase(text.size()-1, 1);
strreplace(text, "\n", "\n ");
- output_via_delegate(OSD_OUTPUT_CHANNEL_ERROR, "%s:\n %s\n", header, text.c_str());
+ output_via_delegate(OSD_OUTPUT_CHANNEL_ERROR, "%s:\n %s\n", header, text);
}