summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/emu/machine.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/emu/machine.c')
-rw-r--r--trunk/src/emu/machine.c1101
1 files changed, 1101 insertions, 0 deletions
diff --git a/trunk/src/emu/machine.c b/trunk/src/emu/machine.c
new file mode 100644
index 00000000000..86b5aff6a23
--- /dev/null
+++ b/trunk/src/emu/machine.c
@@ -0,0 +1,1101 @@
+/***************************************************************************
+
+ machine.c
+
+ Controls execution of the core MAME system.
+
+****************************************************************************
+
+ Copyright Aaron Giles
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name 'MAME' nor the names of its contributors may be
+ used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+****************************************************************************
+
+ Since there has been confusion in the past over the order of
+ initialization and other such things, here it is, all spelled out
+ as of January, 2008:
+
+ main()
+ - does platform-specific init
+ - calls mame_execute() [mame.c]
+
+ mame_execute() [mame.c]
+ - calls mame_validitychecks() [validity.c] to perform validity checks on all compiled drivers
+ - begins resource tracking (level 1)
+ - calls create_machine [mame.c] to initialize the running_machine structure
+ - calls init_machine() [mame.c]
+
+ init_machine() [mame.c]
+ - calls fileio_init() [fileio.c] to initialize file I/O info
+ - calls config_init() [config.c] to initialize configuration system
+ - calls input_init() [input.c] to initialize the input system
+ - calls output_init() [output.c] to initialize the output system
+ - calls state_init() [state.c] to initialize save state system
+ - calls state_save_allow_registration() [state.c] to allow registrations
+ - calls palette_init() [palette.c] to initialize palette system
+ - calls render_init() [render.c] to initialize the rendering system
+ - calls ui_init() [ui.c] to initialize the user interface
+ - calls generic_machine_init() [machine/generic.c] to initialize generic machine structures
+ - calls timer_init() [timer.c] to reset the timer system
+ - calls osd_init() [osdepend.h] to do platform-specific initialization
+ - calls input_port_init() [inptport.c] to set up the input ports
+ - calls rom_init() [romload.c] to load the game's ROMs
+ - calls memory_init() [memory.c] to process the game's memory maps
+ - calls the driver's DRIVER_INIT callback
+ - calls device_list_start() [devintrf.c] to start any devices
+ - calls video_init() [video.c] to start the video system
+ - calls tilemap_init() [tilemap.c] to start the tilemap system
+ - calls crosshair_init() [crsshair.c] to configure the crosshairs
+ - calls sound_init() [sound.c] to start the audio system
+ - calls debugger_init() [debugger.c] to set up the debugger
+ - calls the driver's MACHINE_START, SOUND_START, and VIDEO_START callbacks
+ - calls cheat_init() [cheat.c] to initialize the cheat system
+ - calls image_init() [image.c] to initialize the image system
+
+ - calls config_load_settings() [config.c] to load the configuration file
+ - calls nvram_load [machine/generic.c] to load NVRAM
+ - calls ui_display_startup_screens() [ui.c] to display the the startup screens
+ - begins resource tracking (level 2)
+ - calls soft_reset() [mame.c] to reset all systems
+
+ -------------------( at this point, we're up and running )----------------------
+
+ - calls scheduler->timeslice() [schedule.c] over and over until we exit
+ - ends resource tracking (level 2), freeing all auto_mallocs and timers
+ - calls the nvram_save() [machine/generic.c] to save NVRAM
+ - calls config_save_settings() [config.c] to save the game's configuration
+ - calls all registered exit routines [mame.c]
+ - ends resource tracking (level 1), freeing all auto_mallocs and timers
+
+ - exits the program
+
+***************************************************************************/
+
+#include "emu.h"
+#include "emuopts.h"
+#include "osdepend.h"
+#include "config.h"
+#include "debugger.h"
+#include "render.h"
+#include "cheat.h"
+#include "uimain.h"
+#include "uiinput.h"
+#include "crsshair.h"
+#include "validity.h"
+#include "unzip.h"
+#include "debug/debugcon.h"
+
+#include <time.h>
+
+
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+// a giant string buffer for temporary strings
+static char giant_string_buffer[65536] = { 0 };
+
+
+
+//**************************************************************************
+// RUNNING MACHINE
+//**************************************************************************
+
+//-------------------------------------------------
+// running_machine - constructor
+//-------------------------------------------------
+
+running_machine::running_machine(const machine_config &_config, osd_interface &osd, bool exit_to_game_select)
+ : firstcpu(NULL),
+ primary_screen(NULL),
+ palette(NULL),
+ pens(NULL),
+ colortable(NULL),
+ shadow_table(NULL),
+ debug_flags(0),
+ palette_data(NULL),
+ romload_data(NULL),
+ ui_input_data(NULL),
+ debugcpu_data(NULL),
+ generic_machine_data(NULL),
+
+ m_config(_config),
+ m_system(_config.gamedrv()),
+ m_osd(osd),
+ m_cheat(NULL),
+ m_render(NULL),
+ m_input(NULL),
+ m_sound(NULL),
+ m_video(NULL),
+ m_tilemap(NULL),
+ m_debug_view(NULL),
+ m_current_phase(MACHINE_PHASE_PREINIT),
+ m_paused(false),
+ m_hard_reset_pending(false),
+ m_exit_pending(false),
+ m_exit_to_game_select(exit_to_game_select),
+ m_new_driver_pending(NULL),
+ m_soft_reset_timer(NULL),
+ m_rand_seed(0x9d14abd7),
+ m_ui_active(false),
+ m_basename(_config.gamedrv().name),
+ m_sample_rate(_config.options().sample_rate()),
+ m_logfile(NULL),
+ m_saveload_schedule(SLS_NONE),
+ m_saveload_schedule_time(attotime::zero),
+ m_saveload_searchpath(NULL),
+ m_logerror_list(m_respool),
+
+ m_save(*this),
+ m_memory(*this),
+ m_ioport(*this),
+ m_scheduler(*this)
+{
+ memset(gfx, 0, sizeof(gfx));
+ memset(&m_base_time, 0, sizeof(m_base_time));
+
+ // set the machine on all devices
+ device_iterator iter(root_device());
+ for (device_t *device = iter.first(); device != NULL; device = iter.next())
+ device->set_machine(*this);
+
+ // find devices
+ for (device_t *device = iter.first(); device != NULL; device = iter.next())
+ if (dynamic_cast<cpu_device *>(device) != NULL)
+ {
+ firstcpu = downcast<cpu_device *>(device);
+ break;
+ }
+ screen_device_iterator screeniter(root_device());
+ primary_screen = screeniter.first();
+
+ // fetch core options
+ if (options().debug())
+ debug_flags = (DEBUG_FLAG_ENABLED | DEBUG_FLAG_CALL_HOOK) | (options().debug_internal() ? 0 : DEBUG_FLAG_OSD_ENABLED);
+}
+
+
+//-------------------------------------------------
+// ~running_machine - destructor
+//-------------------------------------------------
+
+running_machine::~running_machine()
+{
+}
+
+
+//-------------------------------------------------
+// describe_context - return a string describing
+// which device is currently executing and its
+// PC
+//-------------------------------------------------
+
+const char *running_machine::describe_context()
+{
+ device_execute_interface *executing = m_scheduler.currently_executing();
+ if (executing != NULL)
+ {
+ cpu_device *cpu = downcast<cpu_device *>(&executing->device());
+ if (cpu != NULL)
+ m_context.printf("'%s' (%s)", cpu->tag(), core_i64_format(cpu->pc(), cpu->space(AS_PROGRAM)->logaddrchars(), cpu->is_octal()));
+ }
+ else
+ m_context.cpy("(no context)");
+
+ return m_context;
+}
+
+
+//-------------------------------------------------
+// start - initialize the emulated machine
+//-------------------------------------------------
+
+void running_machine::start()
+{
+ // initialize basic can't-fail systems here
+ config_init(*this);
+ m_input = auto_alloc(*this, input_manager(*this));
+ output_init(*this);
+ palette_init(*this);
+ m_render = auto_alloc(*this, render_manager(*this));
+ generic_machine_init(*this);
+
+ // allocate a soft_reset timer
+ m_soft_reset_timer = m_scheduler.timer_alloc(timer_expired_delegate(FUNC(running_machine::soft_reset), this));
+
+ // init the osd layer
+ m_osd.init(*this);
+
+ // create the video manager
+ m_video = auto_alloc(*this, video_manager(*this));
+ ui_init(*this);
+
+ // initialize the base time (needed for doing record/playback)
+ ::time(&m_base_time);
+
+ // initialize the input system and input ports for the game
+ // this must be done before memory_init in order to allow specifying
+ // callbacks based on input port tags
+ time_t newbase = m_ioport.initialize();
+ if (newbase != 0)
+ m_base_time = newbase;
+
+ // intialize UI input
+ ui_input_init(*this);
+
+ // initialize the streams engine before the sound devices start
+ m_sound = auto_alloc(*this, sound_manager(*this));
+
+ // first load ROMs, then populate memory, and finally initialize CPUs
+ // these operations must proceed in this order
+ rom_init(*this);
+ m_memory.initialize();
+ m_watchdog_timer = m_scheduler.timer_alloc(timer_expired_delegate(FUNC(running_machine::watchdog_fired), this));
+ save().save_item(NAME(m_watchdog_enabled));
+ save().save_item(NAME(m_watchdog_counter));
+
+ // allocate the gfx elements prior to device initialization
+ gfx_init(*this);
+
+ // initialize image devices
+ image_init(*this);
+ m_tilemap = auto_alloc(*this, tilemap_manager(*this));
+ crosshair_init(*this);
+ network_init(*this);
+
+ // initialize the debugger
+ if ((debug_flags & DEBUG_FLAG_ENABLED) != 0)
+ debugger_init(*this);
+
+ // call the game driver's init function
+ // this is where decryption is done and memory maps are altered
+ // so this location in the init order is important
+ ui_set_startup_text(*this, "Initializing...", true);
+
+ // register callbacks for the devices, then start them
+ add_notifier(MACHINE_NOTIFY_RESET, machine_notify_delegate(FUNC(running_machine::reset_all_devices), this));
+ add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(FUNC(running_machine::stop_all_devices), this));
+ save().register_presave(save_prepost_delegate(FUNC(running_machine::presave_all_devices), this));
+ save().register_postload(save_prepost_delegate(FUNC(running_machine::postload_all_devices), this));
+ start_all_devices();
+
+ // if we're coming in with a savegame request, process it now
+ const char *savegame = options().state();
+ if (savegame[0] != 0)
+ schedule_load(savegame);
+
+ // if we're in autosave mode, schedule a load
+ else if (options().autosave() && (m_system.flags & GAME_SUPPORTS_SAVE) != 0)
+ schedule_load("auto");
+
+ // set up the cheat engine
+ m_cheat = auto_alloc(*this, cheat_manager(*this));
+
+ // disallow save state registrations starting here
+ m_save.allow_registration(false);
+}
+
+
+//-------------------------------------------------
+// add_dynamic_device - dynamically add a device
+//-------------------------------------------------
+
+device_t &running_machine::add_dynamic_device(device_t &owner, device_type type, const char *tag, UINT32 clock)
+{
+ // add the device in a standard manner
+ device_t *device = const_cast<machine_config &>(m_config).device_add(&owner, tag, type, clock);
+
+ // notify this device and all its subdevices that they are now configured
+ device_iterator iter(root_device());
+ for (device_t *device = iter.first(); device != NULL; device = iter.next())
+ if (!device->configured())
+ device->config_complete();
+ return *device;
+}
+
+
+//-------------------------------------------------
+// run - execute the machine
+//-------------------------------------------------
+
+int running_machine::run(bool firstrun)
+{
+ int error = MAMERR_NONE;
+
+ // use try/catch for deep error recovery
+ try
+ {
+ // move to the init phase
+ m_current_phase = MACHINE_PHASE_INIT;
+
+ // if we have a logfile, set up the callback
+ if (options().log())
+ {
+ m_logfile = auto_alloc(*this, emu_file(OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS));
+ file_error filerr = m_logfile->open("error.log");
+ assert_always(filerr == FILERR_NONE, "unable to open log file");
+ add_logerror_callback(logfile_callback);
+ }
+
+ // then finish setting up our local machine
+ start();
+
+ // load the configuration settings and NVRAM
+ bool settingsloaded = config_load_settings(*this);
+ nvram_load(*this);
+ sound().ui_mute(false);
+
+ // display the startup screens
+ ui_display_startup_screens(*this, firstrun, !settingsloaded);
+
+ // perform a soft reset -- this takes us to the running phase
+ soft_reset();
+
+ // run the CPUs until a reset or exit
+ m_hard_reset_pending = false;
+ while ((!m_hard_reset_pending && !m_exit_pending) || m_saveload_schedule != SLS_NONE)
+ {
+ g_profiler.start(PROFILER_EXTRA);
+
+ // execute CPUs if not paused
+ if (!m_paused)
+ m_scheduler.timeslice();
+
+ // otherwise, just pump video updates through
+ else
+ m_video->frame_update();
+
+ // handle save/load
+ if (m_saveload_schedule != SLS_NONE)
+ handle_saveload();
+
+ g_profiler.stop();
+ }
+
+ // and out via the exit phase
+ m_current_phase = MACHINE_PHASE_EXIT;
+
+ // save the NVRAM and configuration
+ sound().ui_mute(true);
+ nvram_save(*this);
+ config_save_settings(*this);
+ }
+ catch (emu_fatalerror &fatal)
+ {
+ mame_printf_error("%s\n", fatal.string());
+ error = MAMERR_FATALERROR;
+ if (fatal.exitcode() != 0)
+ error = fatal.exitcode();
+ }
+ catch (emu_exception &)
+ {
+ mame_printf_error("Caught unhandled emulator exception\n");
+ error = MAMERR_FATALERROR;
+ }
+ catch (std::bad_alloc &)
+ {
+ mame_printf_error("Out of memory!\n");
+ error = MAMERR_FATALERROR;
+ }
+
+ // make sure our phase is set properly before cleaning up,
+ // in case we got here via exception
+ m_current_phase = MACHINE_PHASE_EXIT;
+
+ // call all exit callbacks registered
+ call_notifiers(MACHINE_NOTIFY_EXIT);
+ zip_file_cache_clear();
+
+ // close the logfile
+ auto_free(*this, m_logfile);
+ return error;
+}
+
+
+//-------------------------------------------------
+// schedule_exit - schedule a clean exit
+//-------------------------------------------------
+
+void running_machine::schedule_exit()
+{
+ // if we are in-game but we started with the select game menu, return to that instead
+ if (m_exit_to_game_select && options().system_name()[0] != 0)
+ {
+ options().set_system_name("");
+ ui_menu_force_game_select(*this, &render().ui_container());
+ }
+
+ // otherwise, exit for real
+ else
+ m_exit_pending = true;
+
+ // if we're executing, abort out immediately
+ m_scheduler.eat_all_cycles();
+
+ // if we're autosaving on exit, schedule a save as well
+ if (options().autosave() && (m_system.flags & GAME_SUPPORTS_SAVE) && this->time() > attotime::zero)
+ schedule_save("auto");
+}
+
+
+//-------------------------------------------------
+// schedule_hard_reset - schedule a hard-reset of
+// the machine
+//-------------------------------------------------
+
+void running_machine::schedule_hard_reset()
+{
+ m_hard_reset_pending = true;
+
+ // if we're executing, abort out immediately
+ m_scheduler.eat_all_cycles();
+}
+
+
+//-------------------------------------------------
+// schedule_soft_reset - schedule a soft-reset of
+// the system
+//-------------------------------------------------
+
+void running_machine::schedule_soft_reset()
+{
+ m_soft_reset_timer->adjust(attotime::zero);
+
+ // we can't be paused since the timer needs to fire
+ resume();
+
+ // if we're executing, abort out immediately
+ m_scheduler.eat_all_cycles();
+}
+
+
+//-------------------------------------------------
+// schedule_new_driver - schedule a new game to
+// be loaded
+//-------------------------------------------------
+
+void running_machine::schedule_new_driver(const game_driver &driver)
+{
+ m_hard_reset_pending = true;
+ m_new_driver_pending = &driver;
+
+ // if we're executing, abort out immediately
+ m_scheduler.eat_all_cycles();
+}
+
+
+//-------------------------------------------------
+// set_saveload_filename - specifies the filename
+// for state loading/saving
+//-------------------------------------------------
+
+void running_machine::set_saveload_filename(const char *filename)
+{
+ // free any existing request and allocate a copy of the requested name
+ if (osd_is_absolute_path(filename))
+ {
+ m_saveload_searchpath = NULL;
+ m_saveload_pending_file.cpy(filename);
+ }
+ else
+ {
+ m_saveload_searchpath = options().state_directory();
+ m_saveload_pending_file.cpy(basename()).cat(PATH_SEPARATOR).cat(filename).cat(".sta");
+ }
+}
+
+
+//-------------------------------------------------
+// schedule_save - schedule a save to occur as
+// soon as possible
+//-------------------------------------------------
+
+void running_machine::schedule_save(const char *filename)
+{
+ // specify the filename to save or load
+ set_saveload_filename(filename);
+
+ // note the start time and set a timer for the next timeslice to actually schedule it
+ m_saveload_schedule = SLS_SAVE;
+ m_saveload_schedule_time = this->time();
+
+ // we can't be paused since we need to clear out anonymous timers
+ resume();
+}
+
+
+//-------------------------------------------------
+// schedule_load - schedule a load to occur as
+// soon as possible
+//-------------------------------------------------
+
+void running_machine::schedule_load(const char *filename)
+{
+ // specify the filename to save or load
+ set_saveload_filename(filename);
+
+ // note the start time and set a timer for the next timeslice to actually schedule it
+ m_saveload_schedule = SLS_LOAD;
+ m_saveload_schedule_time = this->time();
+
+ // we can't be paused since we need to clear out anonymous timers
+ resume();
+}
+
+
+//-------------------------------------------------
+// pause - pause the system
+//-------------------------------------------------
+
+void running_machine::pause()
+{
+ // ignore if nothing has changed
+ if (m_paused)
+ return;
+ m_paused = true;
+
+ // call the callbacks
+ call_notifiers(MACHINE_NOTIFY_PAUSE);
+}
+
+
+//-------------------------------------------------
+// resume - resume the system
+//-------------------------------------------------
+
+void running_machine::resume()
+{
+ // ignore if nothing has changed
+ if (!m_paused)
+ return;
+ m_paused = false;
+
+ // call the callbacks
+ call_notifiers(MACHINE_NOTIFY_RESUME);
+}
+
+
+//-------------------------------------------------
+// add_notifier - add a notifier of the
+// given type
+//-------------------------------------------------
+
+void running_machine::add_notifier(machine_notification event, machine_notify_delegate callback)
+{
+ assert_always(m_current_phase == MACHINE_PHASE_INIT, "Can only call add_notifier at init time!");
+
+ // exit notifiers are added to the head, and executed in reverse order
+ if (event == MACHINE_NOTIFY_EXIT)
+ m_notifier_list[event].prepend(*global_alloc(notifier_callback_item(callback)));
+
+ // all other notifiers are added to the tail, and executed in the order registered
+ else
+ m_notifier_list[event].append(*global_alloc(notifier_callback_item(callback)));
+}
+
+
+//-------------------------------------------------
+// add_logerror_callback - adds a callback to be
+// called on logerror()
+//-------------------------------------------------
+
+void running_machine::add_logerror_callback(logerror_callback callback)
+{
+ assert_always(m_current_phase == MACHINE_PHASE_INIT, "Can only call add_logerror_callback at init time!");
+ m_logerror_list.append(*auto_alloc(*this, logerror_callback_item(callback)));
+}
+
+
+//-------------------------------------------------
+// logerror - printf-style error logging
+//-------------------------------------------------
+
+void CLIB_DECL running_machine::logerror(const char *format, ...)
+{
+ // process only if there is a target
+ if (m_logerror_list.first() != NULL)
+ {
+ va_list arg;
+ va_start(arg, format);
+ vlogerror(format, arg);
+ va_end(arg);
+ }
+}
+
+
+//-------------------------------------------------
+// vlogerror - vprintf-style error logging
+//-------------------------------------------------
+
+void CLIB_DECL running_machine::vlogerror(const char *format, va_list args)
+{
+ // process only if there is a target
+ if (m_logerror_list.first() != NULL)
+ {
+ g_profiler.start(PROFILER_LOGERROR);
+
+ // dump to the buffer
+ vsnprintf(giant_string_buffer, ARRAY_LENGTH(giant_string_buffer), format, args);
+
+ // log to all callbacks
+ for (logerror_callback_item *cb = m_logerror_list.first(); cb != NULL; cb = cb->next())
+ (*cb->m_func)(*this, giant_string_buffer);
+
+ g_profiler.stop();
+ }
+}
+
+
+//-------------------------------------------------
+// base_datetime - retrieve the time of the host
+// system; useful for RTC implementations
+//-------------------------------------------------
+
+void running_machine::base_datetime(system_time &systime)
+{
+ systime.set(m_base_time);
+}
+
+
+//-------------------------------------------------
+// current_datetime - retrieve the current time
+// (offset by the base); useful for RTC
+// implementations
+//-------------------------------------------------
+
+void running_machine::current_datetime(system_time &systime)
+{
+ systime.set(m_base_time + this->time().seconds);
+}
+
+
+//-------------------------------------------------
+// rand - standardized random numbers
+//-------------------------------------------------
+
+UINT32 running_machine::rand()
+{
+ m_rand_seed = 1664525 * m_rand_seed + 1013904223;
+
+ // return rotated by 16 bits; the low bits have a short period
+ // and are frequently used
+ return (m_rand_seed >> 16) | (m_rand_seed << 16);
+}
+
+
+//-------------------------------------------------
+// call_notifiers - call notifiers of the given
+// type
+//-------------------------------------------------
+
+void running_machine::call_notifiers(machine_notification which)
+{
+ for (notifier_callback_item *cb = m_notifier_list[which].first(); cb != NULL; cb = cb->next())
+ cb->m_func();
+}
+
+
+//-------------------------------------------------
+// handle_saveload - attempt to perform a save
+// or load
+//-------------------------------------------------
+
+void running_machine::handle_saveload()
+{
+ UINT32 openflags = (m_saveload_schedule == SLS_LOAD) ? OPEN_FLAG_READ : (OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS);
+ const char *opnamed = (m_saveload_schedule == SLS_LOAD) ? "loaded" : "saved";
+ const char *opname = (m_saveload_schedule == SLS_LOAD) ? "load" : "save";
+ file_error filerr = FILERR_NONE;
+
+ // if no name, bail
+ emu_file file(m_saveload_searchpath, openflags);
+ if (!m_saveload_pending_file)
+ goto cancel;
+
+ // if there are anonymous timers, we can't save just yet, and we can't load yet either
+ // because the timers might overwrite data we have loaded
+ if (!m_scheduler.can_save())
+ {
+ // if more than a second has passed, we're probably screwed
+ if ((this->time() - m_saveload_schedule_time) > attotime::from_seconds(1))
+ {
+ popmessage("Unable to %s due to pending anonymous timers. See error.log for details.", opname);
+ goto cancel;
+ }
+ return;
+ }
+
+ // open the file
+ filerr = file.open(m_saveload_pending_file);
+ if (filerr == FILERR_NONE)
+ {
+ // read/write the save state
+ save_error saverr = (m_saveload_schedule == SLS_LOAD) ? m_save.read_file(file) : m_save.write_file(file);
+
+ // handle the result
+ switch (saverr)
+ {
+ case STATERR_ILLEGAL_REGISTRATIONS:
+ popmessage("Error: Unable to %s state due to illegal registrations. See error.log for details.", opname);
+ break;
+
+ case STATERR_INVALID_HEADER:
+ popmessage("Error: Unable to %s state due to an invalid header. Make sure the save state is correct for this game.", opname);
+ break;
+
+ case STATERR_READ_ERROR:
+ popmessage("Error: Unable to %s state due to a read error (file is likely corrupt).", opname);
+ break;
+
+ case STATERR_WRITE_ERROR:
+ popmessage("Error: Unable to %s state due to a write error. Verify there is enough disk space.", opname);
+ break;
+
+ case STATERR_NONE:
+ if (!(m_system.flags & GAME_SUPPORTS_SAVE))
+ popmessage("State successfully %s.\nWarning: Save states are not officially supported for this game.", opnamed);
+ else
+ popmessage("State successfully %s.", opnamed);
+ break;
+
+ default:
+ popmessage("Error: Unknown error during state %s.", opnamed);
+ break;
+ }
+
+ // close and perhaps delete the file
+ if (saverr != STATERR_NONE && m_saveload_schedule == SLS_SAVE)
+ file.remove_on_close();
+ }
+ else
+ popmessage("Error: Failed to open file for %s operation.", opname);
+
+ // unschedule the operation
+cancel:
+ m_saveload_pending_file.reset();
+ m_saveload_searchpath = NULL;
+ m_saveload_schedule = SLS_NONE;
+}
+
+
+//-------------------------------------------------
+// soft_reset - actually perform a soft-reset
+// of the system
+//-------------------------------------------------
+
+void running_machine::soft_reset(void *ptr, INT32 param)
+{
+ logerror("Soft reset\n");
+
+ // temporarily in the reset phase
+ m_current_phase = MACHINE_PHASE_RESET;
+
+ // set up the watchdog timer; only start off enabled if explicitly configured
+ m_watchdog_enabled = (config().m_watchdog_vblank_count != 0 || config().m_watchdog_time != attotime::zero);
+ watchdog_reset();
+ m_watchdog_enabled = true;
+
+ // call all registered reset callbacks
+ call_notifiers(MACHINE_NOTIFY_RESET);
+
+ // now we're running
+ m_current_phase = MACHINE_PHASE_RUNNING;
+}
+
+
+//-------------------------------------------------
+// watchdog_reset - reset the watchdog timer
+//-------------------------------------------------
+
+void running_machine::watchdog_reset()
+{
+ // if we're not enabled, skip it
+ if (!m_watchdog_enabled)
+ m_watchdog_timer->adjust(attotime::never);
+
+ // VBLANK-based watchdog?
+ else if (config().m_watchdog_vblank_count != 0)
+ {
+ // register a VBLANK callback for the primary screen
+ m_watchdog_counter = config().m_watchdog_vblank_count;
+ if (primary_screen != NULL)
+ primary_screen->register_vblank_callback(vblank_state_delegate(FUNC(running_machine::watchdog_vblank), this));
+ }
+
+ // timer-based watchdog?
+ else if (config().m_watchdog_time != attotime::zero)
+ m_watchdog_timer->adjust(config().m_watchdog_time);
+
+ // default to an obscene amount of time (3 seconds)
+ else
+ m_watchdog_timer->adjust(attotime::from_seconds(3));
+}
+
+
+//-------------------------------------------------
+// watchdog_enable - reset the watchdog timer
+//-------------------------------------------------
+
+void running_machine::watchdog_enable(bool enable)
+{
+ // when re-enabled, we reset our state
+ if (m_watchdog_enabled != enable)
+ {
+ m_watchdog_enabled = enable;
+ watchdog_reset();
+ }
+}
+
+
+//-------------------------------------------------
+// watchdog_fired - watchdog timer callback
+//-------------------------------------------------
+
+void running_machine::watchdog_fired(void *ptr, INT32 param)
+{
+ logerror("Reset caused by the watchdog!!!\n");
+
+ bool verbose = options().verbose();
+#ifdef MAME_DEBUG
+ verbose = true;
+#endif
+ if (verbose)
+ popmessage("Reset caused by the watchdog!!!\n");
+
+ schedule_soft_reset();
+}
+
+
+//-------------------------------------------------
+// watchdog_vblank - VBLANK state callback for
+// watchdog timers
+//-------------------------------------------------
+
+void running_machine::watchdog_vblank(screen_device &screen, bool vblank_state)
+{
+ // VBLANK starting
+ if (vblank_state && m_watchdog_enabled)
+ {
+ // check the watchdog
+ if (config().m_watchdog_vblank_count != 0)
+ if (--m_watchdog_counter == 0)
+ watchdog_fired();
+ }
+}
+
+
+//-------------------------------------------------
+// logfile_callback - callback for logging to
+// logfile
+//-------------------------------------------------
+
+void running_machine::logfile_callback(running_machine &machine, const char *buffer)
+{
+ if (machine.m_logfile != NULL)
+ machine.m_logfile->puts(buffer);
+}
+
+
+//-------------------------------------------------
+// start_all_devices - start any unstarted devices
+//-------------------------------------------------
+
+void running_machine::start_all_devices()
+{
+ // iterate through the devices
+ int last_failed_starts = -1;
+ while (last_failed_starts != 0)
+ {
+ // iterate over all devices
+ int failed_starts = 0;
+ device_iterator iter(root_device());
+ for (device_t *device = iter.first(); device != NULL; device = iter.next())
+ if (!device->started())
+ {
+ // attempt to start the device, catching any expected exceptions
+ try
+ {
+ // if the device doesn't have a machine yet, set it first
+ if (device->m_machine == NULL)
+ device->set_machine(*this);
+
+ // now start the device
+ mame_printf_verbose("Starting %s '%s'\n", device->name(), device->tag());
+ device->start();
+ }
+
+ // handle missing dependencies by moving the device to the end
+ catch (device_missing_dependencies &)
+ {
+ // if we're the end, fail
+ mame_printf_verbose(" (missing dependencies; rescheduling)\n");
+ failed_starts++;
+ }
+ }
+
+ // each iteration should reduce the number of failed starts; error if
+ // this doesn't happen
+ if (failed_starts == last_failed_starts)
+ throw emu_fatalerror("Circular dependency in device startup!");
+ last_failed_starts = failed_starts;
+ }
+}
+
+
+//-------------------------------------------------
+// reset_all_devices - reset all devices in the
+// hierarchy
+//-------------------------------------------------
+
+void running_machine::reset_all_devices()
+{
+ // reset the root and it will reset children
+ root_device().reset();
+}
+
+
+//-------------------------------------------------
+// stop_all_devices - stop all the devices in the
+// hierarchy
+//-------------------------------------------------
+
+void running_machine::stop_all_devices()
+{
+ // first let the debugger save comments
+ if ((debug_flags & DEBUG_FLAG_ENABLED) != 0)
+ debug_comment_save(*this);
+
+ // iterate over devices and stop them
+ device_iterator iter(root_device());
+ for (device_t *device = iter.first(); device != NULL; device = iter.next())
+ device->stop();
+
+ // then nuke the device tree
+// global_free(m_root_device);
+}
+
+
+//-------------------------------------------------
+// presave_all_devices - tell all the devices we
+// are about to save
+//-------------------------------------------------
+
+void running_machine::presave_all_devices()
+{
+ device_iterator iter(root_device());
+ for (device_t *device = iter.first(); device != NULL; device = iter.next())
+ device->pre_save();
+}
+
+
+//-------------------------------------------------
+// postload_all_devices - tell all the devices we
+// just completed a load
+//-------------------------------------------------
+
+void running_machine::postload_all_devices()
+{
+ device_iterator iter(root_device());
+ for (device_t *device = iter.first(); device != NULL; device = iter.next())
+ device->post_load();
+}
+
+
+
+//**************************************************************************
+// CALLBACK ITEMS
+//**************************************************************************
+
+//-------------------------------------------------
+// notifier_callback_item - constructor
+//-------------------------------------------------
+
+running_machine::notifier_callback_item::notifier_callback_item(machine_notify_delegate func)
+ : m_next(NULL),
+ m_func(func)
+{
+}
+
+
+//-------------------------------------------------
+// logerror_callback_item - constructor
+//-------------------------------------------------
+
+running_machine::logerror_callback_item::logerror_callback_item(logerror_callback func)
+ : m_next(NULL),
+ m_func(func)
+{
+}
+
+
+
+//**************************************************************************
+// SYSTEM TIME
+//**************************************************************************
+
+//-------------------------------------------------
+// system_time - constructor
+//-------------------------------------------------
+
+system_time::system_time()
+{
+ set(0);
+}
+
+
+//-------------------------------------------------
+// set - fills out a system_time structure
+//-------------------------------------------------
+
+void system_time::set(time_t t)
+{
+ time = t;
+ local_time.set(*localtime(&t));
+ utc_time.set(*gmtime(&t));
+}
+
+
+//-------------------------------------------------
+// get_tm_time - converts a tm struction to a
+// MAME mame_system_tm structure
+//-------------------------------------------------
+
+void system_time::full_time::set(struct tm &t)
+{
+ second = t.tm_sec;
+ minute = t.tm_min;
+ hour = t.tm_hour;
+ mday = t.tm_mday;
+ month = t.tm_mon;
+ year = t.tm_year + 1900;
+ weekday = t.tm_wday;
+ day = t.tm_yday;
+ is_dst = t.tm_isdst;
+}