netlist: Add validation support to netlist device.

mame -validate now also checks all netlist devices. It does this
by constructing a temporary netlist.
This commit also fixes some memory leaks and a bad bug which
surfaced in validation.
This commit is contained in:
couriersud 2019-04-15 21:08:13 +02:00
parent 7e623cc886
commit 86f0b315b6
8 changed files with 224 additions and 63 deletions

View File

@ -53,7 +53,7 @@ class netlist_mame_device::netlist_mame_callbacks_t : public netlist::callbacks_
{
public:
netlist_mame_callbacks_t(netlist_mame_device &parent)
netlist_mame_callbacks_t(const netlist_mame_device &parent)
: netlist::callbacks_t()
, m_parent(parent)
{
@ -80,13 +80,47 @@ protected:
m_parent.logerror("netlist ERROR: %s\n", ls.c_str());
break;
case plib::plog_level::FATAL:
emu_fatalerror error("netlist ERROR: %s\n", ls.c_str());
throw error;
throw emu_fatalerror(1, "netlist ERROR: %s\n", ls.c_str());
}
}
private:
const netlist_mame_device &m_parent;
};
class netlist_validate_callbacks_t : public netlist::callbacks_t
{
public:
netlist_validate_callbacks_t()
: netlist::callbacks_t()
{
}
protected:
void vlog(const plib::plog_level &l, const pstring &ls) const override
{
switch (l)
{
case plib::plog_level::DEBUG:
break;
case plib::plog_level::INFO:
break;
case plib::plog_level::VERBOSE:
break;
case plib::plog_level::WARNING:
osd_printf_verbose("netlist WARNING: %s\n", ls.c_str());
break;
case plib::plog_level::ERROR:
osd_printf_error("netlist ERROR: %s\n", ls.c_str());
break;
case plib::plog_level::FATAL:
//throw emu_fatalerror(1, "netlist ERROR: %s\n", ls.c_str());
throw emu_fatalerror("netlist ERROR: %s\n", ls.c_str());
}
}
private:
netlist_mame_device &m_parent;
};
@ -100,8 +134,14 @@ public:
{
}
netlist_mame_t(netlist_mame_device &parent, const pstring &aname, plib::unique_ptr<netlist::callbacks_t> cbs)
: netlist::netlist_t(aname, std::move(cbs))
, m_parent(parent)
{
}
running_machine &machine() { return m_parent.machine(); }
netlist_mame_device &parent() { return m_parent; }
netlist_mame_device &parent() const { return m_parent; }
private:
netlist_mame_device &m_parent;
@ -211,9 +251,33 @@ private:
// Extensions to interface netlist with MAME code ....
// ----------------------------------------------------------------------------------------
/*! Specific exception if memregion is not available.
* The exception is thrown if the memregions are not available.
* This may be the case in device_validity_check and needs
* to be ignored.
*/
class memregion_not_set : public netlist::nl_exception
{
public:
/*! Constructor.
* Allows a descriptive text to be assed to the exception
*/
explicit memregion_not_set(const pstring &text //!< text to be passed
)
: netlist::nl_exception(text) { }
template<typename... Args>
explicit memregion_not_set(const pstring &fmt //!< format to be used
, Args&&... args //!< arguments to be passed
)
: netlist::nl_exception(plib::pfmt(fmt)(std::forward<Args>(args)...)) { }
};
class netlist_source_memregion_t : public netlist::source_t
{
public:
netlist_source_memregion_t(device_t &dev, pstring name)
: netlist::source_t(), m_dev(dev), m_name(name)
{
@ -228,12 +292,12 @@ private:
class netlist_data_memregions_t : public netlist::source_t
{
public:
netlist_data_memregions_t(device_t &dev);
netlist_data_memregions_t(const device_t &dev);
virtual plib::unique_ptr<plib::pistream> stream(const pstring &name) override;
private:
device_t &m_dev;
const device_t &m_dev;
};
@ -244,11 +308,17 @@ private:
plib::unique_ptr<plib::pistream> netlist_source_memregion_t::stream(const pstring &name)
{
//memory_region *mem = static_cast<netlist_mame_device::netlist_mame_t &>(setup().setup().exec()).machine().root_device().memregion(m_name.c_str());
memory_region *mem = m_dev.machine().root_device().memregion(m_name.c_str());
return plib::make_unique<plib::pimemstream>(mem->base(), mem->bytes());
if (m_dev.has_running_machine())
{
memory_region *mem = m_dev.machine().root_device().memregion(m_name.c_str());
return plib::make_unique<plib::pimemstream>(mem->base(), mem->bytes());
}
else
throw memregion_not_set("memregion unavailable for {1} in source {2}", name, m_name);
//return plib::unique_ptr<plib::pimemstream>(nullptr);
}
netlist_data_memregions_t::netlist_data_memregions_t(device_t &dev)
netlist_data_memregions_t::netlist_data_memregions_t(const device_t &dev)
: netlist::source_t(netlist::source_t::DATA), m_dev(dev)
{
}
@ -602,10 +672,14 @@ void netlist_mame_analog_output_device::custom_netlist_additions(netlist::netlis
const pstring pin(m_in);
pstring dname = pstring("OUT_") + pin;
pstring dfqn = nlstate.setup().build_fqn(dname);
m_delegate.bind_relative_to(owner()->machine().root_device());
/* ignore if no running machine -> called within device_validity_check context */
if (owner()->has_running_machine())
m_delegate.bind_relative_to(owner()->machine().root_device());
auto dev = netlist::pool().make_poolptr<NETLIB_NAME(analog_callback)>(nlstate, dfqn);
static_cast<NETLIB_NAME(analog_callback) *>(dev.get())->register_callback(std::move(m_delegate));
//static_cast<NETLIB_NAME(analog_callback) *>(dev.get())->register_callback(std::move(m_delegate));
dev->register_callback(std::move(m_delegate));
nlstate.add_dev(dfqn, std::move(dev));
nlstate.setup().register_link(dname + ".IN", pin);
}
@ -639,10 +713,12 @@ void netlist_mame_logic_output_device::custom_netlist_additions(netlist::netlist
pstring dname = "OUT_" + pin;
pstring dfqn = nlstate.setup().build_fqn(dname);
m_delegate.bind_relative_to(owner()->machine().root_device());
/* ignore if no running machine -> called within device_validity_check context */
if (owner()->has_running_machine())
m_delegate.bind_relative_to(owner()->machine().root_device());
auto dev = netlist::pool().make_poolptr<NETLIB_NAME(logic_callback)>(nlstate, dfqn);
static_cast<NETLIB_NAME(logic_callback) *>(dev.get())->register_callback(std::move(m_delegate));
dev->register_callback(std::move(m_delegate));
nlstate.add_dev(dfqn, std::move(dev));
nlstate.setup().register_link(dname + ".IN", pin);
}
@ -858,25 +934,16 @@ netlist_mame_device::~netlist_mame_device()
void netlist_mame_device::device_config_complete()
{
LOGDEVCALLS("device_config_complete %s\n", this->mconfig().gamedrv().name);
}
void netlist_mame_device::device_validity_check(validity_checker &valid) const
void netlist_mame_device::common_dev_start(netlist::netlist_t *lnetlist) const
{
LOGDEVCALLS("device_validity_check %s\n", this->mconfig().gamedrv().name);
}
void netlist_mame_device::device_start()
{
LOGDEVCALLS("device_start entry\n");
//printf("clock is %d\n", clock());
m_netlist = netlist::pool().make_poolptr<netlist_mame_t>(*this, "netlist");
auto &lsetup = lnetlist->nlstate().setup();
// register additional devices
nl_register_devices();
nl_register_devices(lsetup);
/* let sub-devices add sources and do stuff prior to parsing */
for (device_t &d : subdevices())
@ -885,15 +952,17 @@ void netlist_mame_device::device_start()
if( sdev != nullptr )
{
LOGDEVCALLS("Preparse subdevice %s/%s\n", d.name(), d.shortname());
sdev->pre_parse_action(m_netlist->nlstate());
sdev->pre_parse_action(lnetlist->nlstate());
}
}
/* add default data provider for roms */
setup().register_source(plib::make_unique<netlist_data_memregions_t>(*this));
/* add default data provider for roms - if not in validity check*/
if (has_running_machine())
lsetup.register_source(plib::make_unique<netlist_data_memregions_t>(*this));
m_setup_func(setup());
m_setup_func(lsetup);
#if 1
/* let sub-devices tweak the netlist */
for (device_t &d : subdevices())
{
@ -901,15 +970,52 @@ void netlist_mame_device::device_start()
if( sdev != nullptr )
{
LOGDEVCALLS("Found subdevice %s/%s\n", d.name(), d.shortname());
sdev->custom_netlist_additions(m_netlist->nlstate());
sdev->custom_netlist_additions(lnetlist->nlstate());
}
}
lsetup.prepare_to_run();
#endif
}
setup().prepare_to_run();
netlist().nlstate().save(*this, m_rem, this->name(), "m_rem");
netlist().nlstate().save(*this, m_div, this->name(), "m_div");
netlist().nlstate().save(*this, m_old, this->name(), "m_old");
void netlist_mame_device::device_validity_check(validity_checker &valid) const
{
#if 1
LOGDEVCALLS("device_validity_check %s\n", this->mconfig().gamedrv().name);
try
{
//netlist_mame_t lnetlist(*this, "netlist", plib::make_unique<netlist_validate_callbacks_t>());
netlist::netlist_t lnetlist("netlist", plib::make_unique<netlist_validate_callbacks_t>());
common_dev_start(&lnetlist);
}
catch (memregion_not_set &err)
{
osd_printf_verbose("%s\n", err.what());
}
catch (emu_fatalerror &err)
{
osd_printf_error("%s\n", err.string());
}
catch (std::exception &err)
{
osd_printf_error("%s\n", err.what());
}
#endif
}
void netlist_mame_device::device_start()
{
LOGDEVCALLS("device_start entry\n");
m_netlist = netlist::pool().make_poolptr<netlist_mame_t>(*this, "netlist");
common_dev_start(m_netlist.get());
m_netlist->nlstate().save(*this, m_rem, this->name(), "m_rem");
m_netlist->nlstate().save(*this, m_div, this->name(), "m_div");
m_netlist->nlstate().save(*this, m_old, this->name(), "m_old");
save_state();
@ -1055,9 +1161,9 @@ void netlist_mame_cpu_device::device_start()
}
void netlist_mame_cpu_device::nl_register_devices()
void netlist_mame_cpu_device::nl_register_devices(netlist::setup_t &lsetup) const
{
setup().factory().register_device<nld_analog_callback>( "NETDEV_CALLBACK", "nld_analog_callback", "-");
lsetup.factory().register_device<nld_analog_callback>( "NETDEV_CALLBACK", "nld_analog_callback", "-");
}
ATTR_COLD uint64_t netlist_mame_cpu_device::execute_clocks_to_cycles(uint64_t clocks) const
@ -1182,10 +1288,10 @@ void netlist_mame_sound_device::device_start()
}
void netlist_mame_sound_device::nl_register_devices()
void netlist_mame_sound_device::nl_register_devices(netlist::setup_t &lsetup) const
{
setup().factory().register_device<nld_sound_out>("NETDEV_SOUND_OUT", "nld_sound_out", "+CHAN");
setup().factory().register_device<nld_sound_in>("NETDEV_SOUND_IN", "nld_sound_in", "-");
lsetup.factory().register_device<nld_sound_out>("NETDEV_SOUND_OUT", "nld_sound_out", "+CHAN");
lsetup.factory().register_device<nld_sound_in>("NETDEV_SOUND_IN", "nld_sound_in", "-");
}

View File

@ -20,6 +20,7 @@ class nld_sound_in;
namespace netlist {
class setup_t;
class netlist_t;
class netlist_state_t;
class nlparse_t;
template <typename T>
@ -131,7 +132,7 @@ protected:
netlist_mame_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
// Custom to netlist ...
virtual void nl_register_devices() { }
virtual void nl_register_devices(netlist::setup_t &lsetup) const { }
// device_t overrides
virtual void device_config_complete() override;
@ -148,6 +149,8 @@ protected:
private:
void save_state();
void common_dev_start(netlist::netlist_t *lnetlist) const;
/* timing support here - so sound can hijack it ... */
netlist::netlist_time m_rem;
netlist::netlist_time m_old;
@ -186,6 +189,11 @@ public:
// construction/destruction
netlist_mame_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
~netlist_mame_cpu_device()
{
}
offs_t genPC() const { return m_genPC; }
netlist_mame_cpu_device & set_source(void (*setup_func)(netlist::nlparse_t &))
@ -203,7 +211,7 @@ public:
protected:
// netlist_mame_device
virtual void nl_register_devices() override;
virtual void nl_register_devices(netlist::setup_t &lsetup) const override;
// device_t overrides
virtual void device_start() override;
@ -262,7 +270,7 @@ public:
protected:
// netlist_mame_device
virtual void nl_register_devices() override;
virtual void nl_register_devices(netlist::setup_t &lsetup) const override;
// device_t overrides
virtual void device_start() override;
@ -288,6 +296,10 @@ public:
{
}
virtual ~netlist_mame_sub_interface()
{
}
virtual void custom_netlist_additions(netlist::netlist_state_t &nlstate) { }
virtual void pre_parse_action(netlist::netlist_state_t &nlstate) { }

View File

@ -207,7 +207,6 @@ namespace netlist
if (idx != plib::container::npos)
connect(m_Q[i], m_I[idx]);
}
m_ign = 0;
}
@ -237,6 +236,9 @@ namespace netlist
if (m_family_name != "")
m_family_desc = anetlist.setup().family_from_model(m_family_name);
if (m_family_desc == nullptr)
throw nl_exception("family description not found for {1}", m_family_name);
return pool().make_poolptr<tt_type>(anetlist, name, m_family_desc, m_ttbl, m_desc);
}
private:

View File

@ -433,7 +433,11 @@ namespace netlist
void operator delete (void * mem) = delete;
#endif
protected:
~object_t() noexcept = default; // only childs should be destructible
// only childs should be destructible
~object_t() noexcept
{
name_hash().erase(name_hash().find(this));
}
private:
//pstring m_name;
@ -1369,7 +1373,10 @@ namespace netlist
{
for (auto & d : m_devices)
if (d.first == name)
log().fatal(MF_1_DUPLICATE_NAME_DEVICE_LIST, d.first);
{
dev.release();
log().fatal(MF_1_DUPLICATE_NAME_DEVICE_LIST, name);
}
//m_devices.push_back(std::move(dev));
m_devices.insert(m_devices.end(), { name, std::move(dev) });
}

View File

@ -272,8 +272,10 @@ pstring setup_t::get_initial_param_val(const pstring &name, const pstring &def)
void setup_t::register_term(detail::core_terminal_t &term)
{
if (!m_terminals.insert({term.name(), &term}).second)
{
log().fatal(MF_2_ADDING_1_2_TO_TERMINAL_LIST, termtype_as_str(term),
term.name());
}
log().debug("{1} {2}\n", termtype_as_str(term), term.name());
}
@ -1144,7 +1146,11 @@ bool source_t::parse(nlparse_t &setup, const pstring &name)
return false;
else
{
return setup.parse_stream(stream(name), name);
auto strm(stream(name));
if (strm)
return setup.parse_stream(std::move(strm), name);
else
return false;
}
}

View File

@ -34,13 +34,18 @@ public:
using iterator = C *;
using const_iterator = const C *;
uninitialised_array_t() noexcept = default;
//uninitialised_array_t() noexcept = default;
uninitialised_array_t() noexcept
: m_initialized(0)
{
}
COPYASSIGNMOVE(uninitialised_array_t, delete)
~uninitialised_array_t() noexcept
{
for (std::size_t i=0; i<N; i++)
(*this)[i].~C();
if (m_initialized>=N)
for (std::size_t i=0; i<N; i++)
(*this)[i].~C();
}
size_t size() const { return N; }
@ -58,6 +63,7 @@ public:
template<typename... Args>
void emplace(const std::size_t index, Args&&... args)
{
m_initialized++;
// allocate on buffer
new (&m_buf[index]) C(std::forward<Args>(args)...);
}
@ -78,6 +84,7 @@ private:
/* ensure proper alignment */
PALIGNAS_VECTOROPT()
std::array<typename std::aligned_storage<sizeof(C), alignof(C)>::type, N> m_buf;
unsigned m_initialized;
};
// ----------------------------------------------------------------------------------------

View File

@ -40,12 +40,18 @@ namespace plib {
{
min_bytes = std::max(mp->m_min_alloc, min_bytes);
m_free = min_bytes;
std::size_t alloc_bytes = (min_bytes + mp->m_min_align - 1) & ~(mp->m_min_align - 1);
m_data_allocated = static_cast<char *>(::operator new(alloc_bytes));
std::size_t alloc_bytes = (min_bytes + mp->m_min_align); // - 1); // & ~(mp->m_min_align - 1);
//m_data_allocated = ::operator new(alloc_bytes);
m_data_allocated = new char[alloc_bytes];
void *r = m_data_allocated;
std::align(mp->m_min_align, min_bytes, r, alloc_bytes);
m_data = reinterpret_cast<char *>(r);
}
~block()
{
//::operator delete(m_data_allocated);
delete [] m_data_allocated;
}
std::size_t m_num_alloc;
std::size_t m_free;
std::size_t m_cur;
@ -106,7 +112,8 @@ namespace plib {
plib::perrlogger("Found {} info blocks\n", sinfo().size());
plib::perrlogger("Found block with {} dangling allocations\n", b->m_num_alloc);
}
::operator delete(b->m_data);
plib::pdelete(b);
//::operator delete(b->m_data);
}
}
@ -125,8 +132,6 @@ namespace plib {
auto *ret = reinterpret_cast<void *>(b->m_data + b->m_cur);
auto capacity(rs);
ret = std::align(align, size, ret, capacity);
// FIXME: if (ret == nullptr)
// printf("Oh no\n");
sinfo().insert({ ret, info(b, b->m_cur)});
rs -= (capacity - size);
b->m_cur += rs;
@ -141,8 +146,6 @@ namespace plib {
auto *ret = reinterpret_cast<void *>(b->m_data + b->m_cur);
auto capacity(rs);
ret = std::align(align, size, ret, capacity);
// FIXME: if (ret == nullptr)
// printf("Oh no\n");
sinfo().insert({ ret, info(b, b->m_cur)});
rs -= (capacity - size);
b->m_cur += rs;
@ -162,12 +165,20 @@ namespace plib {
plib::terminate("mempool::free - double free was called\n");
else
{
//b->m_free = m_min_alloc;
//b->cur_ptr = b->data;
b->m_num_alloc--;
//printf("Freeing in block %p %lu\n", b, b->m_num_alloc);
sinfo().erase(it);
if (b->m_num_alloc == 0)
{
mempool *mp = b->m_mempool;
auto itb = std::find(mp->m_blocks.begin(), mp->m_blocks.end(), b);
if (itb == mp->m_blocks.end())
plib::terminate("mempool::free - block not found\n");
mp->m_blocks.erase(itb);
plib::pdelete(b);
}
}
b->m_num_alloc--;
//printf("Freeing in block %p %lu\n", b, b->m_num_alloc);
sinfo().erase(it);
}
template <typename T>

View File

@ -369,6 +369,7 @@ NETLIST_START(gtrak10)
NET_C(RT, R66.1)
NET_C(SLICK_Q, R65.1)
#if 0
CAP(C44, CAP_U(10))
NET_C(C44.1, R63.2)
NET_C(C44.1, R73.2)
@ -377,5 +378,14 @@ NETLIST_START(gtrak10)
NET_C(C44.1, R66.2)
NET_C(C44.1, R65.2)
ALIAS(VIDEO_OUT, C44.2)
#else
// A capacitor just kills performance completely
RES(RVID, RES_K(10))
NET_C(GND, RVID.2)
NET_C(RVID.1, R63.2, R73.2, R64.2, R74.2, R66.2, R65.2)
ALIAS(VIDEO_OUT, RVID.1)
#endif
NETLIST_END()