// license:BSD-3-Clause // copyright-holders:Aaron Giles /*************************************************************************** device.c Device interface functions. ***************************************************************************/ #include "emu.h" #include "string.h" #include "debug/debugcpu.h" //************************************************************************** // LIVE DEVICE MANAGEMENT //************************************************************************** //------------------------------------------------- // device_t - constructor for a new // running device; initial state is derived // from the provided config //------------------------------------------------- device_t::device_t(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) : m_type(type), m_name(name), m_shortname(shortname), m_searchpath(shortname), m_source(source), m_owner(owner), m_next(nullptr), m_configured_clock(clock), m_unscaled_clock(clock), m_clock(clock), m_clock_scale(1.0), m_attoseconds_per_clock((clock == 0) ? 0 : HZ_TO_ATTOSECONDS(clock)), m_machine_config(mconfig), m_input_defaults(nullptr), m_default_bios_tag(""), m_machine(nullptr), m_save(nullptr), m_basetag(tag), m_config_complete(false), m_started(false), m_auto_finder_list(nullptr) { if (owner != nullptr) m_tag.assign((owner->owner() == nullptr) ? "" : owner->tag()).append(":").append(tag); else m_tag.assign(":"); static_set_clock(*this, clock); } //------------------------------------------------- // ~device_t - destructor for a device_t //------------------------------------------------- device_t::~device_t() { } //------------------------------------------------- // memregion - return a pointer to the region // info for a given region //------------------------------------------------- memory_region *device_t::memregion(const char *_tag) const { // build a fully-qualified name and look it up if (_tag) { auto search = machine().memory().regions().find(subtag(_tag).c_str()); if (search != machine().memory().regions().end()) return search->second.get(); else return nullptr; } else return nullptr; } //------------------------------------------------- // memshare - return a pointer to the memory share // info for a given share //------------------------------------------------- memory_share *device_t::memshare(const char *_tag) const { // build a fully-qualified name and look it up if (_tag) { auto search = machine().memory().shares().find(subtag(_tag).c_str()); if (search != machine().memory().shares().end()) return search->second.get(); else return nullptr; } else return nullptr; } //------------------------------------------------- // membank - return a pointer to the memory // bank info for a given bank //------------------------------------------------- memory_bank *device_t::membank(const char *_tag) const { if (_tag) { auto search = machine().memory().banks().find(subtag(_tag).c_str()); if (search != machine().memory().banks().end()) return search->second.get(); else return nullptr; } else return nullptr; } //------------------------------------------------- // ioport - return a pointer to the I/O port // object for a given port name //------------------------------------------------- ioport_port *device_t::ioport(const char *tag) const { // build a fully-qualified name and look it up return machine().ioport().port(subtag(tag).c_str()); } //------------------------------------------------- // ioport - return a pointer to the I/O port // object for a given port name //------------------------------------------------- std::string device_t::parameter(const char *tag) const { // build a fully-qualified name and look it up return machine().parameters().lookup(subtag(tag)); } //------------------------------------------------- // static_set_clock - set/change the clock on // a device //------------------------------------------------- void device_t::static_set_clock(device_t &device, UINT32 clock) { // derive the clock from our owner if requested if ((clock & 0xff000000) == 0xff000000) { assert(device.m_owner != nullptr); clock = device.m_owner->m_configured_clock * ((clock >> 12) & 0xfff) / ((clock >> 0) & 0xfff); } device.m_clock = device.m_unscaled_clock = device.m_configured_clock = clock; device.m_attoseconds_per_clock = (clock == 0) ? 0 : HZ_TO_ATTOSECONDS(clock); } //------------------------------------------------- // config_complete - called when the // configuration of a device is complete //------------------------------------------------- void device_t::config_complete() { // first notify the interfaces for (device_interface &intf : interfaces()) intf.interface_config_complete(); // then notify the device itself device_config_complete(); // then mark ourselves complete m_config_complete = true; } //------------------------------------------------- // validity_check - validate a device after the // configuration has been constructed //------------------------------------------------- void device_t::validity_check(validity_checker &valid) const { // validate via the interfaces for (device_interface &intf : interfaces()) intf.interface_validity_check(valid); // let the device itself validate device_validity_check(valid); } //------------------------------------------------- // reset - reset a device //------------------------------------------------- void device_t::reset() { // let the interfaces do their pre-work for (device_interface &intf : interfaces()) intf.interface_pre_reset(); // reset the device device_reset(); // reset all child devices for (device_t &child : subdevices()) child.reset(); // now allow for some post-child reset action device_reset_after_children(); // let the interfaces do their post-work for (device_interface &intf : interfaces()) intf.interface_post_reset(); } //------------------------------------------------- // set_unscaled_clock - sets the given device's // unscaled clock //------------------------------------------------- void device_t::set_unscaled_clock(UINT32 clock) { m_unscaled_clock = clock; m_clock = m_unscaled_clock * m_clock_scale; m_attoseconds_per_clock = (m_clock == 0) ? 0 : HZ_TO_ATTOSECONDS(m_clock); notify_clock_changed(); } //------------------------------------------------- // set_clock_scale - sets a scale factor for the // device's clock //------------------------------------------------- void device_t::set_clock_scale(double clockscale) { m_clock_scale = clockscale; m_clock = m_unscaled_clock * m_clock_scale; m_attoseconds_per_clock = (m_clock == 0) ? 0 : HZ_TO_ATTOSECONDS(m_clock); notify_clock_changed(); } //------------------------------------------------- // clocks_to_attotime - converts a number of // clock ticks to an attotime //------------------------------------------------- attotime device_t::clocks_to_attotime(UINT64 numclocks) const { if (numclocks < m_clock) return attotime(0, numclocks * m_attoseconds_per_clock); else { UINT32 remainder; UINT32 quotient = divu_64x32_rem(numclocks, m_clock, &remainder); return attotime(quotient, (UINT64)remainder * (UINT64)m_attoseconds_per_clock); } } //------------------------------------------------- // attotime_to_clocks - converts a duration as // attotime to CPU clock ticks //------------------------------------------------- UINT64 device_t::attotime_to_clocks(const attotime &duration) const { return mulu_32x32(duration.seconds(), m_clock) + (UINT64)duration.attoseconds() / (UINT64)m_attoseconds_per_clock; } //------------------------------------------------- // timer_alloc - allocate a timer for our device // callback //------------------------------------------------- emu_timer *device_t::timer_alloc(device_timer_id id, void *ptr) { return machine().scheduler().timer_alloc(*this, id, ptr); } //------------------------------------------------- // timer_set - set a temporary timer that will // call our device callback //------------------------------------------------- void device_t::timer_set(const attotime &duration, device_timer_id id, int param, void *ptr) { machine().scheduler().timer_set(duration, *this, id, param, ptr); } //------------------------------------------------- // set_machine - notify that the machine now // exists //------------------------------------------------- void device_t::set_machine(running_machine &machine) { m_machine = &machine; m_save = &machine.save(); } //------------------------------------------------- // findit - seach for all objects in auto finder // list and return status //------------------------------------------------- bool device_t::findit(bool isvalidation) const { bool allfound = true; for (finder_base *autodev = m_auto_finder_list; autodev != nullptr; autodev = autodev->m_next) { if (isvalidation) { // sanity checking const char *tag = autodev->finder_tag(); if (tag == nullptr) { osd_printf_error("Finder tag is null!\n"); allfound = false; continue; } if (tag[0] == '^' && tag[1] == ':') { osd_printf_error("Malformed finder tag: %s\n", tag); allfound = false; continue; } } allfound &= autodev->findit(isvalidation); } return allfound; } //------------------------------------------------- // start - start a device //------------------------------------------------- void device_t::start() { // prepare the logerror buffer if (m_machine->allow_logging()) m_string_buffer.reserve(1024); // find all the registered devices if (!findit(false)) throw emu_fatalerror("Missing some required objects, unable to proceed"); // let the interfaces do their pre-work for (device_interface &intf : interfaces()) intf.interface_pre_start(); // remember the number of state registrations int state_registrations = machine().save().registration_count(); // start the device device_start(); // complain if nothing was registered by the device state_registrations = machine().save().registration_count() - state_registrations; device_execute_interface *exec; device_sound_interface *sound; if (state_registrations == 0 && (interface(exec) || interface(sound)) && type() != SPEAKER) { logerror("Device did not register any state to save!\n"); if ((machine().system().flags & MACHINE_SUPPORTS_SAVE) != 0) fatalerror("Device '%s' did not register any state to save!\n", tag()); } // let the interfaces do their post-work for (device_interface &intf : interfaces()) intf.interface_post_start(); // force an update of the clock notify_clock_changed(); // if we're debugging, create a device_debug object if ((machine().debug_flags & DEBUG_FLAG_ENABLED) != 0) { m_debug = std::make_unique(*this); debug_setup(); } // register our save states save_item(NAME(m_clock)); save_item(NAME(m_unscaled_clock)); save_item(NAME(m_clock_scale)); // we're now officially started m_started = true; } //------------------------------------------------- // stop - stop a device //------------------------------------------------- void device_t::stop() { // let the interfaces do their pre-work for (device_interface &intf : interfaces()) intf.interface_pre_stop(); // stop the device device_stop(); // let the interfaces do their post-work for (device_interface &intf : interfaces()) intf.interface_post_stop(); // free any debugging info m_debug.reset(); // we're now officially stopped, and the machine is off-limits m_started = false; m_machine = nullptr; } //------------------------------------------------- // debug_setup - set up for debugging //------------------------------------------------- void device_t::debug_setup() { // notify the interface for (device_interface &intf : interfaces()) intf.interface_debug_setup(); // notify the device device_debug_setup(); } //------------------------------------------------- // pre_save - tell the device and its interfaces // that we are about to save //------------------------------------------------- void device_t::pre_save() { // notify the interface for (device_interface &intf : interfaces()) intf.interface_pre_save(); // notify the device device_pre_save(); } //------------------------------------------------- // post_load - tell the device and its interfaces // that we just completed a load //------------------------------------------------- void device_t::post_load() { // notify the interface for (device_interface &intf : interfaces()) intf.interface_post_load(); // notify the device device_post_load(); } //------------------------------------------------- // notify_clock_changed - notify all interfaces // that the clock has changed //------------------------------------------------- void device_t::notify_clock_changed() { // first notify interfaces for (device_interface &intf : interfaces()) intf.interface_clock_changed(); // then notify the device device_clock_changed(); } //------------------------------------------------- // device_config_complete - perform any // operations now that the configuration is // complete //------------------------------------------------- void device_t::device_config_complete() { // do nothing by default } //------------------------------------------------- // device_validity_check - validate a device after // the configuration has been constructed //------------------------------------------------- void device_t::device_validity_check(validity_checker &valid) const { // do nothing by default } //------------------------------------------------- // rom_region - return a pointer to the implicit // rom region description for this device //------------------------------------------------- const rom_entry *device_t::device_rom_region() const { // none by default return nullptr; } //------------------------------------------------- // machine_config - return a pointer to a machine // config constructor describing sub-devices for // this device //------------------------------------------------- machine_config_constructor device_t::device_mconfig_additions() const { // none by default return nullptr; } //------------------------------------------------- // input_ports - return a pointer to the implicit // input ports description for this device //------------------------------------------------- ioport_constructor device_t::device_input_ports() const { // none by default return nullptr; } //------------------------------------------------- // device_reset - actually handle resetting of // a device; designed to be overriden by the // actual device implementation //------------------------------------------------- void device_t::device_reset() { // do nothing by default } //------------------------------------------------- // device_reset_after_children - hook to do // reset logic that must happen after the children // are reset; designed to be overriden by the // actual device implementation //------------------------------------------------- void device_t::device_reset_after_children() { // do nothing by default } //------------------------------------------------- // device_stop - clean up anything that needs to // happen before the running_machine goes away //------------------------------------------------- void device_t::device_stop() { // do nothing by default } //------------------------------------------------- // device_pre_save - called prior to saving the // state, so that registered variables can be // properly normalized //------------------------------------------------- void device_t::device_pre_save() { // do nothing by default } //------------------------------------------------- // device_post_load - called after the loading a // saved state, so that registered variables can // be expanded as necessary //------------------------------------------------- void device_t::device_post_load() { // do nothing by default } //------------------------------------------------- // device_clock_changed - called when the // device clock is altered in any way; designed // to be overridden by the actual device // implementation //------------------------------------------------- void device_t::device_clock_changed() { // do nothing by default } //------------------------------------------------- // device_debug_setup - called when the debugger // is active to allow for device-specific setup //------------------------------------------------- void device_t::device_debug_setup() { // do nothing by default } //------------------------------------------------- // device_timer - called whenever a device timer // fires //------------------------------------------------- void device_t::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) { // do nothing by default } //------------------------------------------------- // subdevice_slow - perform a slow name lookup, // caching the results //------------------------------------------------- device_t *device_t::subdevice_slow(const char *tag) const { // resolve the full path std::string fulltag = subtag(tag); // we presume the result is a rooted path; also doubled colons mess up our // tree walk, so catch them early assert(fulltag[0] == ':'); assert(fulltag.find("::") == -1); // walk the device list to the final path device_t *curdevice = &mconfig().root_device(); if (fulltag.length() > 1) for (int start = 1, end = fulltag.find_first_of(':', start); start != 0 && curdevice != nullptr; start = end + 1, end = fulltag.find_first_of(':', start)) { std::string part(fulltag, start, (end == -1) ? -1 : end - start); curdevice = curdevice->subdevices().find(part); } // if we got a match, add to the fast map if (curdevice != nullptr) m_subdevices.m_tagmap.insert(std::make_pair(tag, curdevice)); return curdevice; } //------------------------------------------------- // subtag - create a fully resolved path relative // to our device based on the provided tag //------------------------------------------------- std::string device_t::subtag(const char *tag) const { std::string result; // if the tag begins with a colon, ignore our path and start from the root if (*tag == ':') { tag++; result.assign(":"); } // otherwise, start with our path else { result.assign(m_tag); if (result != ":") result.append(":"); } // iterate over the tag, look for special path characters to resolve const char *caret; while ((caret = strchr(tag, '^')) != nullptr) { // copy everything up to there result.append(tag, caret - tag); tag = caret + 1; // strip trailing colons int len = result.length(); while (result[--len] == ':') result = result.substr(0, len); // remove the last path part, leaving the last colon if (result != ":") { int lastcolon = result.find_last_of(':'); if (lastcolon != -1) result = result.substr(0, lastcolon + 1); } } // copy everything else result.append(tag); // strip trailing colons up to the root int len = result.length(); while (len > 1 && result[--len] == ':') result = result.substr(0, len); return result; } //------------------------------------------------- // register_auto_finder - add a new item to the // list of stuff to find after we go live //------------------------------------------------- finder_base *device_t::register_auto_finder(finder_base &autodev) { // add to this list finder_base *old = m_auto_finder_list; m_auto_finder_list = &autodev; return old; } //************************************************************************** // LIVE DEVICE INTERFACES //************************************************************************** //------------------------------------------------- // device_interface - constructor //------------------------------------------------- device_interface::device_interface(device_t &device, const char *type) : m_interface_next(nullptr), m_device(device), m_type(type) { device_interface **tailptr; for (tailptr = &device.interfaces().m_head; *tailptr != nullptr; tailptr = &(*tailptr)->m_interface_next) { } *tailptr = this; } //------------------------------------------------- // ~device_interface - destructor //------------------------------------------------- device_interface::~device_interface() { } //------------------------------------------------- // interface_config_complete - perform any // operations now that the configuration is // complete //------------------------------------------------- void device_interface::interface_config_complete() { // do nothing by default } //------------------------------------------------- // interface_validity_check - default validation // for a device after the configuration has been // constructed //------------------------------------------------- void device_interface::interface_validity_check(validity_checker &valid) const { } //------------------------------------------------- // interface_pre_start - called before the // device's own start function //------------------------------------------------- void device_interface::interface_pre_start() { // do nothing by default } //------------------------------------------------- // interface_post_start - called after the // device's own start function //------------------------------------------------- void device_interface::interface_post_start() { // do nothing by default } //------------------------------------------------- // interface_pre_reset - called before the // device's own reset function //------------------------------------------------- void device_interface::interface_pre_reset() { // do nothing by default } //------------------------------------------------- // interface_post_reset - called after the // device's own reset function //------------------------------------------------- void device_interface::interface_post_reset() { // do nothing by default } //------------------------------------------------- // interface_pre_stop - called before the // device's own stop function //------------------------------------------------- void device_interface::interface_pre_stop() { // do nothing by default } //------------------------------------------------- // interface_post_stop - called after the // device's own stop function //------------------------------------------------- void device_interface::interface_post_stop() { // do nothing by default } //------------------------------------------------- // interface_pre_save - called prior to saving the // state, so that registered variables can be // properly normalized //------------------------------------------------- void device_interface::interface_pre_save() { // do nothing by default } //------------------------------------------------- // interface_post_load - called after the loading a // saved state, so that registered variables can // be expaneded as necessary //------------------------------------------------- void device_interface::interface_post_load() { // do nothing by default } //------------------------------------------------- // interface_clock_changed - called when the // device clock is altered in any way; designed // to be overridden by the actual device // implementation //------------------------------------------------- void device_interface::interface_clock_changed() { // do nothing by default } //------------------------------------------------- // interface_debug_setup - called to allow // interfaces to set up any debugging for this // device //------------------------------------------------- void device_interface::interface_debug_setup() { // do nothing by default }