diff options
Diffstat (limited to 'src/emu/machine.cpp')
-rw-r--r-- | src/emu/machine.cpp | 1424 |
1 files changed, 1424 insertions, 0 deletions
diff --git a/src/emu/machine.cpp b/src/emu/machine.cpp new file mode 100644 index 00000000000..9562d4aa32f --- /dev/null +++ b/src/emu/machine.cpp @@ -0,0 +1,1424 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + machine.c + + Controls execution of the core MAME system. + +**************************************************************************** + + 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 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 "ui/selgame.h" +#include "uiinput.h" +#include "crsshair.h" +#include "validity.h" +#include "unzip.h" +#include "debug/debugcon.h" +#include "debug/debugvw.h" +#include "image.h" +#include "luaengine.h" +#include "network.h" +#include <time.h> + +#if defined(EMSCRIPTEN) +#include <emscripten.h> + +void js_set_main_loop(running_machine * machine); +#endif + + + +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** + +// a giant string buffer for temporary strings +static char giant_string_buffer[65536] = { 0 }; + + + +//************************************************************************** +// RUNNING MACHINE +//************************************************************************** + +osd_interface &running_machine::osd() const +{ + return m_manager.osd(); +} + +//------------------------------------------------- +// running_machine - constructor +//------------------------------------------------- + +running_machine::running_machine(const machine_config &_config, machine_manager &manager) + : firstcpu(NULL), + primary_screen(NULL), + debug_flags(0), + romload_data(NULL), + ui_input_data(NULL), + debugcpu_data(NULL), + generic_machine_data(NULL), + m_config(_config), + m_system(_config.gamedrv()), + m_manager(manager), + m_current_phase(MACHINE_PHASE_PREINIT), + m_paused(false), + m_hard_reset_pending(false), + m_exit_pending(false), + m_soft_reset_timer(NULL), + m_rand_seed(0x9d14abd7), + m_ui_active(_config.options().ui_active()), + m_basename(_config.gamedrv().name), + m_sample_rate(_config.options().sample_rate()), + m_saveload_schedule(SLS_NONE), + m_saveload_schedule_time(attotime::zero), + m_saveload_searchpath(NULL), + + m_save(*this), + m_memory(*this), + m_ioport(*this), + m_parameters(*this), + m_scheduler(*this) +{ + 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) | (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 = dynamic_cast<cpu_device *>(&executing->device()); + if (cpu != NULL) + strprintf(m_context, "'%s' (%s)", cpu->tag(), core_i64_format(cpu->pc(), cpu->space(AS_PROGRAM).logaddrchars(), cpu->is_octal())); + } + else + m_context.assign("(no context)"); + + return m_context.c_str(); +} + +TIMER_CALLBACK_MEMBER(running_machine::autoboot_callback) +{ + if (strlen(options().autoboot_script())!=0) { + manager().lua()->load_script(options().autoboot_script()); + } + else if (strlen(options().autoboot_command())!=0) { + std::string cmd = std::string(options().autoboot_command()); + strreplace(cmd, "'", "\\'"); + std::string val = std::string("emu.keypost('").append(cmd).append("')"); + manager().lua()->load_string(val.c_str()); + } +} + +//------------------------------------------------- +// start - initialize the emulated machine +//------------------------------------------------- + +void running_machine::start() +{ + // initialize basic can't-fail systems here + config_init(*this); + m_input.reset(global_alloc(input_manager(*this))); + output_init(*this); + m_render.reset(global_alloc(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_manager.osd().init(*this); + + // create the video manager + m_video.reset(global_alloc(video_manager(*this))); + m_ui.reset(global_alloc(ui_manager(*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.reset(global_alloc(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(); + + // initialize the watchdog + m_watchdog_counter = 0; + m_watchdog_timer = m_scheduler.timer_alloc(timer_expired_delegate(FUNC(running_machine::watchdog_fired), this)); + if (config().m_watchdog_vblank_count != 0 && primary_screen != NULL) + primary_screen->register_vblank_callback(vblank_state_delegate(FUNC(running_machine::watchdog_vblank), this)); + save().save_item(NAME(m_watchdog_enabled)); + save().save_item(NAME(m_watchdog_counter)); + + // save the random seed or save states might be broken in drivers that use the rand() method + save().save_item(NAME(m_rand_seed)); + + // initialize image devices + image_init(*this); + m_tilemap.reset(global_alloc(tilemap_manager(*this))); + crosshair_init(*this); + network_init(*this); + + // initialize the debugger + if ((debug_flags & DEBUG_FLAG_ENABLED) != 0) + debugger_init(*this); + + m_render->resolve_tags(); + + // 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("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)); + start_all_devices(); + save().register_postload(save_prepost_delegate(FUNC(running_machine::postload_all_devices), this)); + + // 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 & MACHINE_SUPPORTS_SAVE) != 0) + schedule_load("auto"); + + // set up the cheat engine + m_cheat.reset(global_alloc(cheat_manager(*this))); + + // allocate autoboot timer + m_autoboot_timer = scheduler().timer_alloc(timer_expired_delegate(FUNC(running_machine::autoboot_callback), this)); + + manager().update_machine(); +} + + +//------------------------------------------------- +// 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() && &system() != &GAME_NAME(___empty)) + { + m_logfile.reset(global_alloc(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 + config_load_settings(*this); + + // disallow save state registrations starting here. + // Don't do it earlier, config load can create network + // devices with timers. + m_save.allow_registration(false); + + nvram_load(); + sound().ui_mute(false); + + // initialize ui lists + ui().initialize(*this); + + // display the startup screens + ui().display_startup_screens(firstrun, false); + + // perform a soft reset -- this takes us to the running phase + soft_reset(); + +#ifdef MAME_DEBUG + g_tagmap_finds = 0; + if (strcmp(config().m_gamedrv.name, "___empty") != 0) + g_tagmap_counter_enabled = true; +#endif + // handle initial load + if (m_saveload_schedule != SLS_NONE) + handle_saveload(); + + // 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); + +#if defined(EMSCRIPTEN) + //break out to our async javascript loop and halt + js_set_main_loop(this); +#endif + + // 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; + +#ifdef MAME_DEBUG + if (g_tagmap_counter_enabled) + { + g_tagmap_counter_enabled = false; + if (*(options().command()) == 0) + osd_printf_info("%d tagmap lookups\n", g_tagmap_finds); + } +#endif + + // save the NVRAM and configuration + sound().ui_mute(true); + nvram_save(); + config_save_settings(*this); + } + catch (emu_fatalerror &fatal) + { + osd_printf_error("FATALERROR: %s\n", fatal.string()); + error = MAMERR_FATALERROR; + if (fatal.exitcode() != 0) + error = fatal.exitcode(); + } + catch (emu_exception &) + { + osd_printf_error("Caught unhandled emulator exception\n"); + error = MAMERR_FATALERROR; + } + catch (binding_type_exception &btex) + { + osd_printf_error("Error performing a late bind of type %s to %s\n", btex.m_actual_type.name(), btex.m_target_type.name()); + error = MAMERR_FATALERROR; + } + catch (add_exception &aex) + { + osd_printf_error("Tag '%s' already exists in tagged_list\n", aex.tag()); + error = MAMERR_FATALERROR; + } + catch (std::exception &ex) + { + osd_printf_error("Caught unhandled %s exception: %s\n", typeid(ex).name(), ex.what()); + error = MAMERR_FATALERROR; + } + catch (...) + { + osd_printf_error("Caught unhandled exception\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; + +#ifdef MAME_DEBUG + if (g_tagmap_counter_enabled) + { + g_tagmap_counter_enabled = false; + if (*(options().command()) == 0) + osd_printf_info("%d tagmap lookups\n", g_tagmap_finds); + } +#endif + + // call all exit callbacks registered + call_notifiers(MACHINE_NOTIFY_EXIT); + zip_file_cache_clear(); + + // close the logfile + m_logfile.reset(); + return error; +} + + +//------------------------------------------------- +// schedule_exit - schedule a clean exit +//------------------------------------------------- + +void running_machine::schedule_exit() +{ + m_exit_pending = true; + + // if we're executing, abort out immediately + m_scheduler.eat_all_cycles(); + +#ifdef MAME_DEBUG + if (g_tagmap_counter_enabled) + { + g_tagmap_counter_enabled = false; + if (*(options().command()) == 0) + osd_printf_info("%d tagmap lookups\n", g_tagmap_finds); + } +#endif + + // if we're autosaving on exit, schedule a save as well + if (options().autosave() && (m_system.flags & MACHINE_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(); +} + + +//------------------------------------------------- +// get_statename - allow to specify a subfolder of +// the state directory for state loading/saving, +// very useful for MESS and consoles or computers +// where you can have separate folders for diff +// software +//------------------------------------------------- + +std::string running_machine::get_statename(const char *option) +{ + std::string statename_str(""); + if (option == NULL || option[0] == 0) + statename_str.assign("%g"); + else + statename_str.assign(option); + + // strip any extension in the provided statename + int index = statename_str.find_last_of('.'); + if (index != -1) + statename_str = statename_str.substr(0, index); + + // handle %d in the template (for image devices) + std::string statename_dev("%d_"); + int pos = statename_str.find(statename_dev); + + if (pos != -1) + { + // if more %d are found, revert to default and ignore them all + if (statename_str.find(statename_dev.c_str(), pos + 3) != -1) + statename_str.assign("%g"); + // else if there is a single %d, try to create the correct snapname + else + { + int name_found = 0; + + // find length of the device name + int end1 = statename_str.find("/", pos + 3); + int end2 = statename_str.find("%", pos + 3); + int end = -1; + + if ((end1 != -1) && (end2 != -1)) + end = MIN(end1, end2); + else if (end1 != -1) + end = end1; + else if (end2 != -1) + end = end2; + else + end = statename_str.length(); + + if (end - pos < 3) + fatalerror("Something very wrong is going on!!!\n"); + + // copy the device name to an std::string + std::string devname_str; + devname_str.assign(statename_str.substr(pos + 3, end - pos - 3)); + //printf("check template: %s\n", devname_str.c_str()); + + // verify that there is such a device for this system + image_interface_iterator iter(root_device()); + for (device_image_interface *image = iter.first(); image != NULL; image = iter.next()) + { + // get the device name + std::string tempdevname(image->brief_instance_name()); + //printf("check device: %s\n", tempdevname.c_str()); + + if (devname_str.compare(tempdevname) == 0) + { + // verify that such a device has an image mounted + if (image->basename_noext() != NULL) + { + std::string filename(image->basename_noext()); + + // setup snapname and remove the %d_ + strreplace(statename_str, devname_str.c_str(), filename.c_str()); + statename_str.erase(pos, 3); + //printf("check image: %s\n", filename.c_str()); + + name_found = 1; + } + } + } + + // or fallback to default + if (name_found == 0) + statename_str.assign("%g"); + } + } + + // substitute path and gamename up front + strreplace(statename_str, "/", PATH_SEPARATOR); + strreplace(statename_str, "%g", basename()); + + return statename_str; +} + +//------------------------------------------------- +// 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.assign(filename); + } + else + { + m_saveload_searchpath = options().state_directory(); + // take into account the statename option + const char *stateopt = options().state_name(); + std::string statename = get_statename(stateopt); + m_saveload_pending_file.assign(statename.c_str()).append(PATH_SEPARATOR).append(filename).append(".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(); +} + + +//------------------------------------------------- +// immediate_save - save state. +//------------------------------------------------- + +void running_machine::immediate_save(const char *filename) +{ + // specify the filename to save or load + set_saveload_filename(filename); + + // set up some parameters for handle_saveload() + m_saveload_schedule = SLS_SAVE; + m_saveload_schedule_time = this->time(); + + // jump right into the save, anonymous timers can't hurt us! + handle_saveload(); +} + + +//------------------------------------------------- +// 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(); +} + + +//------------------------------------------------- +// immediate_load - load state. +//------------------------------------------------- + +void running_machine::immediate_load(const char *filename) +{ + // specify the filename to save or load + set_saveload_filename(filename); + + // set up some parameters for handle_saveload() + m_saveload_schedule = SLS_LOAD; + m_saveload_schedule_time = this->time(); + + // jump right into the load, anonymous timers can't hurt us + handle_saveload(); +} + + +//------------------------------------------------- +// 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); +} + + +//------------------------------------------------- +// toggle_pause - toggles the pause state +//------------------------------------------------- + +void running_machine::toggle_pause() +{ + if (paused()) + resume(); + else + pause(); +} + + +//------------------------------------------------- +// 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(*global_alloc(logerror_callback_item(callback))); +} + +/*------------------------------------------------- + popmessage - pop up a user-visible message +-------------------------------------------------*/ + +void running_machine::popmessage(const char *format, ...) const +{ + // if the format is NULL, it is a signal to clear the popmessage + if (format == NULL) + ui().popup_time(0, " "); + + // otherwise, generate the buffer and call the UI to display the message + else + { + std::string temp; + va_list arg; + + // dump to the buffer + va_start(arg, format); + strvprintf(temp,format, arg); + va_end(arg); + + // pop it in the UI + ui().popup_time(temp.length() / 40 + 2, "%s", temp.c_str()); + } +} + + +/*------------------------------------------------- + logerror - log to the debugger and any other + OSD-defined output streams +-------------------------------------------------*/ + +void running_machine::logerror(const char *format, ...) const +{ + va_list arg; + va_start(arg, format); + vlogerror(format, arg); + va_end(arg); +} + + +//------------------------------------------------- +// vlogerror - vprintf-style error logging +//------------------------------------------------- + +void running_machine::vlogerror(const char *format, va_list args) const +{ + // 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.empty()) + 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.c_str()); + 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 & MACHINE_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.clear(); + 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); + + // setup autoboot if needed + m_autoboot_timer->adjust(attotime(options().autoboot_delay(),0),0); + + // 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) + m_watchdog_counter = config().m_watchdog_vblank_count; + + // 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(const 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 + osd_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 + osd_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(); +} + + +//------------------------------------------------- +// 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(); +} + + +/*************************************************************************** + NVRAM MANAGEMENT +***************************************************************************/ + +const char *running_machine::image_parent_basename(device_t *device) +{ + device_t *dev = device; + while(dev != &root_device()) + { + device_image_interface *intf = NULL; + if (dev!=NULL && dev->interface(intf)) + { + return intf->basename_noext(); + } + dev = dev->owner(); + } + return NULL; +} + +/*------------------------------------------------- + nvram_filename - returns filename of system's + NVRAM depending of selected BIOS +-------------------------------------------------*/ + +std::string &running_machine::nvram_filename(std::string &result, device_t &device) +{ + // start with either basename or basename_biosnum + result.assign(basename()); + if (root_device().system_bios() != 0 && root_device().default_bios() != root_device().system_bios()) + strcatprintf(result, "_%d", root_device().system_bios() - 1); + + // device-based NVRAM gets its own name in a subdirectory + if (&device != &root_device()) + { + // add per software nvrams into one folder + const char *software = image_parent_basename(&device); + if (software!=NULL && strlen(software)>0) + { + result.append(PATH_SEPARATOR).append(software); + } + std::string tag(device.tag()); + tag.erase(0, 1); + strreplacechr(tag,':', '_'); + result.append(PATH_SEPARATOR).append(tag); + } + return result; +} + +/*------------------------------------------------- + nvram_load - load a system's NVRAM +-------------------------------------------------*/ + +void running_machine::nvram_load() +{ + nvram_interface_iterator iter(root_device()); + for (device_nvram_interface *nvram = iter.first(); nvram != NULL; nvram = iter.next()) + { + std::string filename; + emu_file file(options().nvram_directory(), OPEN_FLAG_READ); + if (file.open(nvram_filename(filename, nvram->device()).c_str()) == FILERR_NONE) + { + nvram->nvram_load(file); + file.close(); + } + else + nvram->nvram_reset(); + } +} + + +/*------------------------------------------------- + nvram_save - save a system's NVRAM +-------------------------------------------------*/ + +void running_machine::nvram_save() +{ + nvram_interface_iterator iter(root_device()); + for (device_nvram_interface *nvram = iter.first(); nvram != NULL; nvram = iter.next()) + { + std::string filename; + emu_file file(options().nvram_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); + if (file.open(nvram_filename(filename, nvram->device()).c_str()) == FILERR_NONE) + { + nvram->nvram_save(file); + file.close(); + } + } +} +//************************************************************************** +// 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; +} + + + +//************************************************************************** +// JAVASCRIPT PORT-SPECIFIC +//************************************************************************** + +#if defined(EMSCRIPTEN) + +static running_machine * jsmess_machine; + +void js_main_loop() +{ + device_scheduler * scheduler; + scheduler = &(jsmess_machine->scheduler()); + attotime stoptime(scheduler->time() + attotime(0,HZ_TO_ATTOSECONDS(60))); + while (scheduler->time() < stoptime) { + scheduler->timeslice(); + } +} + +void js_set_main_loop(running_machine * machine) { + jsmess_machine = machine; + EM_ASM ( + JSMESS.running = true; + ); + emscripten_set_main_loop(&js_main_loop, 0, 1); +} + +running_machine * js_get_machine() { + return jsmess_machine; +} + +ui_manager * js_get_ui() { + return &(jsmess_machine->ui()); +} + +sound_manager * js_get_sound() { + return &(jsmess_machine->sound()); +} + +#endif /* defined(EMSCRIPTEN) */ |