summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/mame.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/mame.c')
-rw-r--r--src/emu/mame.c1889
1 files changed, 1889 insertions, 0 deletions
diff --git a/src/emu/mame.c b/src/emu/mame.c
new file mode 100644
index 00000000000..bed4dd9ba96
--- /dev/null
+++ b/src/emu/mame.c
@@ -0,0 +1,1889 @@
+/***************************************************************************
+
+ mame.c
+
+ Controls execution of the core MAME system.
+
+ Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+ Visit http://mamedev.org for licensing and usage restrictions.
+
+****************************************************************************
+
+ 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 February, 2006:
+
+ main()
+ - does platform-specific init
+ - calls run_game() [mame.c]
+
+ run_game() [mame.c]
+ - calls mame_validitychecks() [validity.c] to perform validity checks on all compiled drivers
+ - calls setjmp to prepare for deep error handling
+ - begins resource tracking (level 1)
+ - calls create_machine [mame.c] to initialize the Machine structure
+ - calls init_machine() [mame.c]
+
+ init_machine() [mame.c]
+ - calls cpuintrf_init() [cpuintrf.c] to determine which CPUs are available
+ - calls sndintrf_init() [sndintrf.c] to determine which sound chips are available
+ - calls fileio_init() [fileio.c] to initialize file I/O info
+ - calls config_init() [config.c] to initialize configuration 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 drawgfx_init() [drawgfx.c] to initialize rendering globals
+ - 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 generic_video_init() [video/generic.c] to initialize generic video structures
+ - calls generic_sound_init() [audio/generic.c] to initialize generic sound structures
+ - calls timer_init() [timer.c] to reset the timer system
+ - calls osd_init() [osdepend.h] to do platform-specific initialization
+ - calls code_init() [input.c] to initialize the input system
+ - 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 cpuexec_init() [cpuexec.c] to initialize the CPUs
+ - calls cpuint_init() [cpuint.c] to initialize the CPU interrupts
+ - calls the driver's DRIVER_INIT callback
+ - calls video_init() [video.c] to start the video system
+ - calls sound_init() [sound.c] to start the audio system
+ - calls the driver's MACHINE_START, SOUND_START, and VIDEO_START callbacks
+ - disposes of regions marked as disposable
+ - calls saveload_init() [mame.c] to set up for save/load
+ - calls cheat_init() [cheat.c] to initialize the cheat system
+ - calls mame_debug_init() [debugcpu.c] to set up the debugger
+
+ - calls config_load_settings() [config.c] to load the configuration file
+ - calls nvram_load [machine/generic.c] to load NVRAM
+ - calls ui_init() [ui.c] to initialize the user interface
+ - begins resource tracking (level 2)
+ - calls soft_reset() [mame.c] to reset all systems
+
+ -------------------( at this point, we're up and running )----------------------
+
+ - calls cpuexec_timeslice() [cpuexec.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 "osdepend.h"
+#include "driver.h"
+#include "config.h"
+#include "cheat.h"
+#include "debugger.h"
+#include "profiler.h"
+#include "render.h"
+#include "ui.h"
+#include "uimenu.h"
+
+#ifdef MAME_DEBUG
+#include "debug/debugcon.h"
+#endif
+
+#include <stdarg.h>
+#include <setjmp.h>
+#include <time.h>
+
+
+
+/***************************************************************************
+ CONSTANTS
+***************************************************************************/
+
+#define MAX_MEMORY_REGIONS 32
+
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+typedef struct _region_info region_info;
+struct _region_info
+{
+ UINT8 * base;
+ UINT32 length;
+ UINT32 type;
+ UINT32 flags;
+};
+
+
+typedef struct _callback_item callback_item;
+struct _callback_item
+{
+ callback_item * next;
+ union
+ {
+ void (*exit)(running_machine *);
+ void (*reset)(running_machine *);
+ void (*frame)(running_machine *);
+ void (*pause)(running_machine *, int);
+ void (*log)(running_machine *, const char *);
+ } func;
+};
+
+
+/* typedef struct _mame_private mame_private; */
+struct _mame_private
+{
+ /* system state */
+ int current_phase;
+ UINT8 paused;
+ UINT8 hard_reset_pending;
+ UINT8 exit_pending;
+ const game_driver *new_driver_pending;
+ astring * saveload_pending_file;
+ emu_timer * soft_reset_timer;
+ mame_file * logfile;
+
+ /* callbacks */
+ callback_item * frame_callback_list;
+ callback_item * reset_callback_list;
+ callback_item * pause_callback_list;
+ callback_item * exit_callback_list;
+ callback_item * logerror_callback_list;
+
+ /* load/save */
+ void (*saveload_schedule_callback)(running_machine *);
+ attotime saveload_schedule_time;
+
+ /* array of memory regions */
+ region_info mem_region[MAX_MEMORY_REGIONS];
+
+ /* error recovery and exiting */
+ jmp_buf fatal_error_jmpbuf;
+ int fatal_error_jmpbuf_valid;
+
+ /* random number seed */
+ UINT32 rand_seed;
+
+ /* base time */
+ time_t base_time;
+};
+
+
+
+/***************************************************************************
+ GLOBAL VARIABLES
+***************************************************************************/
+
+/* the active machine */
+running_machine *Machine;
+
+/* the current options */
+static core_options *mame_opts;
+
+/* started empty? */
+static UINT8 started_empty;
+
+/* output channels */
+static output_callback output_cb[OUTPUT_CHANNEL_COUNT];
+static void *output_cb_param[OUTPUT_CHANNEL_COUNT];
+
+/* the "disclaimer" that should be printed when run with no parameters */
+const char *mame_disclaimer =
+ "MAME is an emulator: it reproduces, more or less faithfully, the behaviour of\n"
+ "several arcade machines. But hardware is useless without software, so an image\n"
+ "of the ROMs which run on that hardware is required. Such ROMs, like any other\n"
+ "commercial software, are copyrighted material and it is therefore illegal to\n"
+ "use them if you don't own the original arcade machine. Needless to say, ROMs\n"
+ "are not distributed together with MAME. Distribution of MAME together with ROM\n"
+ "images is a violation of copyright law and should be promptly reported to the\n"
+ "authors so that appropriate legal action can be taken.\n";
+
+const char *memory_region_names[REGION_MAX] =
+{
+ "REGION_INVALID",
+ "REGION_CPU1",
+ "REGION_CPU2",
+ "REGION_CPU3",
+ "REGION_CPU4",
+ "REGION_CPU5",
+ "REGION_CPU6",
+ "REGION_CPU7",
+ "REGION_CPU8",
+ "REGION_GFX1",
+ "REGION_GFX2",
+ "REGION_GFX3",
+ "REGION_GFX4",
+ "REGION_GFX5",
+ "REGION_GFX6",
+ "REGION_GFX7",
+ "REGION_GFX8",
+ "REGION_PROMS",
+ "REGION_SOUND1",
+ "REGION_SOUND2",
+ "REGION_SOUND3",
+ "REGION_SOUND4",
+ "REGION_SOUND5",
+ "REGION_SOUND6",
+ "REGION_SOUND7",
+ "REGION_SOUND8",
+ "REGION_USER1",
+ "REGION_USER2",
+ "REGION_USER3",
+ "REGION_USER4",
+ "REGION_USER5",
+ "REGION_USER6",
+ "REGION_USER7",
+ "REGION_USER8",
+ "REGION_USER9",
+ "REGION_USER10",
+ "REGION_USER11",
+ "REGION_USER12",
+ "REGION_USER13",
+ "REGION_USER14",
+ "REGION_USER15",
+ "REGION_USER16",
+ "REGION_USER17",
+ "REGION_USER18",
+ "REGION_USER19",
+ "REGION_USER20",
+ "REGION_DISKS",
+ "REGION_PLDS"
+};
+
+
+
+/***************************************************************************
+ FUNCTION PROTOTYPES
+***************************************************************************/
+
+extern int mame_validitychecks(const game_driver *driver);
+
+static int parse_ini_file(core_options *options, const char *name);
+
+static running_machine *create_machine(const game_driver *driver);
+static void reset_machine(running_machine *machine);
+static void destroy_machine(running_machine *machine);
+static void init_machine(running_machine *machine);
+static TIMER_CALLBACK( soft_reset );
+static void free_callback_list(callback_item **cb);
+
+static void saveload_init(running_machine *machine);
+static void handle_save(running_machine *machine);
+static void handle_load(running_machine *machine);
+
+static void logfile_callback(running_machine *machine, const char *buffer);
+
+
+
+/***************************************************************************
+ CORE IMPLEMENTATION
+***************************************************************************/
+
+/*-------------------------------------------------
+ mame_execute - run the core emulation
+-------------------------------------------------*/
+
+int mame_execute(core_options *options)
+{
+ int exit_pending = FALSE;
+ int error = MAMERR_NONE;
+ int firstgame = TRUE;
+ int firstrun = TRUE;
+
+ /* loop across multiple hard resets */
+ while (error == MAMERR_NONE && !exit_pending)
+ {
+ const game_driver *driver;
+ running_machine *machine;
+ mame_private *mame;
+ callback_item *cb;
+ astring *gamename;
+
+ /* specify the mame_options */
+ mame_opts = options;
+
+ /* convert the specified gamename to a driver */
+ gamename = core_filename_extract_base(astring_alloc(), options_get_string(mame_options(), OPTION_GAMENAME), TRUE);
+ driver = driver_get_name(astring_c(gamename));
+ astring_free(gamename);
+
+ /* if no driver, use the internal empty driver */
+ if (driver == NULL)
+ {
+ driver = &driver_empty;
+ if (firstgame)
+ started_empty = TRUE;
+ }
+
+ /* otherwise, perform validity checks before anything else */
+ else if (mame_validitychecks(driver) != 0)
+ return MAMERR_FAILED_VALIDITY;
+ firstgame = FALSE;
+
+ /* parse any INI files as the first thing */
+ options_revert(mame_options(), OPTION_PRIORITY_INI);
+ mame_parse_ini_files(mame_options(), driver);
+
+ /* create the machine structure and driver */
+ machine = create_machine(driver);
+ reset_machine(machine);
+ mame = machine->mame_data;
+
+ /* start in the "pre-init phase" */
+ mame->current_phase = MAME_PHASE_PREINIT;
+
+ /* looooong term: remove this */
+ Machine = machine;
+
+ init_resource_tracking();
+
+ /* use setjmp/longjmp for deep error recovery */
+ mame->fatal_error_jmpbuf_valid = TRUE;
+ error = setjmp(mame->fatal_error_jmpbuf);
+ if (error == 0)
+ {
+ int settingsloaded;
+
+ /* move to the init phase */
+ mame->current_phase = MAME_PHASE_INIT;
+
+ /* start tracking resources for real */
+ begin_resource_tracking();
+
+ /* if we have a logfile, set up the callback */
+ mame->logerror_callback_list = NULL;
+ if (options_get_bool(mame_options(), OPTION_LOG))
+ {
+ file_error filerr = mame_fopen(SEARCHPATH_DEBUGLOG, "error.log", OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS, &mame->logfile);
+ assert_always(filerr == FILERR_NONE, "unable to open log file");
+ add_logerror_callback(machine, logfile_callback);
+ }
+
+ /* then finish setting up our local machine */
+ init_machine(machine);
+
+ /* load the configuration settings and NVRAM */
+ settingsloaded = config_load_settings();
+ nvram_load();
+
+ /* display the startup screens */
+ ui_display_startup_screens(firstrun, !settingsloaded);
+ firstrun = FALSE;
+
+ /* start resource tracking; note that soft_reset assumes it can */
+ /* call end_resource_tracking followed by begin_resource_tracking */
+ /* to clear out resources allocated between resets */
+ begin_resource_tracking();
+
+ /* perform a soft reset -- this takes us to the running phase */
+ soft_reset(machine, 0);
+
+ /* run the CPUs until a reset or exit */
+ mame->hard_reset_pending = FALSE;
+ while ((!mame->hard_reset_pending && !mame->exit_pending) || mame->saveload_pending_file != NULL)
+ {
+ profiler_mark(PROFILER_EXTRA);
+
+ /* execute CPUs if not paused */
+ if (!mame->paused)
+ cpuexec_timeslice();
+
+ /* otherwise, just pump video updates through */
+ else
+ video_frame_update();
+
+ /* handle save/load */
+ if (mame->saveload_schedule_callback)
+ (*mame->saveload_schedule_callback)(machine);
+
+ profiler_mark(PROFILER_END);
+ }
+
+ /* and out via the exit phase */
+ mame->current_phase = MAME_PHASE_EXIT;
+
+ /* stop tracking resources at this level */
+ end_resource_tracking();
+
+ /* save the NVRAM and configuration */
+ nvram_save();
+ config_save_settings();
+ }
+ mame->fatal_error_jmpbuf_valid = FALSE;
+
+ /* call all exit callbacks registered */
+ for (cb = mame->exit_callback_list; cb; cb = cb->next)
+ (*cb->func.exit)(machine);
+
+ /* close all inner resource tracking */
+ exit_resource_tracking();
+
+ /* close the logfile */
+ if (mame->logfile != NULL)
+ mame_fclose(mame->logfile);
+
+ /* free our callback lists */
+ free_callback_list(&mame->exit_callback_list);
+ free_callback_list(&mame->pause_callback_list);
+ free_callback_list(&mame->reset_callback_list);
+ free_callback_list(&mame->frame_callback_list);
+
+ /* grab data from the MAME structure before it goes away */
+ if (mame->new_driver_pending != NULL)
+ {
+ options_set_string(mame_options(), OPTION_GAMENAME, mame->new_driver_pending->name, OPTION_PRIORITY_CMDLINE);
+ firstrun = TRUE;
+ }
+ exit_pending = mame->exit_pending;
+
+ /* destroy the machine */
+ destroy_machine(machine);
+
+ /* reset the options */
+ mame_opts = NULL;
+ }
+
+ /* return an error */
+ return error;
+}
+
+
+/*-------------------------------------------------
+ mame_options - accesses the options for the
+ currently running emulation
+-------------------------------------------------*/
+
+core_options *mame_options(void)
+{
+ assert(mame_opts != NULL);
+ return mame_opts;
+}
+
+
+
+/*-------------------------------------------------
+ mame_get_phase - return the current program
+ phase
+-------------------------------------------------*/
+
+int mame_get_phase(running_machine *machine)
+{
+ mame_private *mame = machine->mame_data;
+ return mame->current_phase;
+}
+
+
+/*-------------------------------------------------
+ add_frame_callback - request a callback on
+ frame update
+-------------------------------------------------*/
+
+void add_frame_callback(running_machine *machine, void (*callback)(running_machine *))
+{
+ mame_private *mame = machine->mame_data;
+ callback_item *cb, **cur;
+
+ assert_always(mame_get_phase(machine) == MAME_PHASE_INIT, "Can only call add_frame_callback at init time!");
+
+ /* allocate memory */
+ cb = malloc_or_die(sizeof(*cb));
+
+ /* add us to the end of the list */
+ cb->func.frame = callback;
+ cb->next = NULL;
+ for (cur = &mame->frame_callback_list; *cur; cur = &(*cur)->next) ;
+ *cur = cb;
+}
+
+
+/*-------------------------------------------------
+ add_reset_callback - request a callback on
+ reset
+-------------------------------------------------*/
+
+void add_reset_callback(running_machine *machine, void (*callback)(running_machine *))
+{
+ mame_private *mame = machine->mame_data;
+ callback_item *cb, **cur;
+
+ assert_always(mame_get_phase(machine) == MAME_PHASE_INIT, "Can only call add_reset_callback at init time!");
+
+ /* allocate memory */
+ cb = malloc_or_die(sizeof(*cb));
+
+ /* add us to the end of the list */
+ cb->func.reset = callback;
+ cb->next = NULL;
+ for (cur = &mame->reset_callback_list; *cur; cur = &(*cur)->next) ;
+ *cur = cb;
+}
+
+
+/*-------------------------------------------------
+ add_pause_callback - request a callback on
+ pause
+-------------------------------------------------*/
+
+void add_pause_callback(running_machine *machine, void (*callback)(running_machine *, int))
+{
+ mame_private *mame = machine->mame_data;
+ callback_item *cb, **cur;
+
+ assert_always(mame_get_phase(machine) == MAME_PHASE_INIT, "Can only call add_pause_callback at init time!");
+
+ /* allocate memory */
+ cb = malloc_or_die(sizeof(*cb));
+
+ /* add us to the end of the list */
+ cb->func.pause = callback;
+ cb->next = NULL;
+ for (cur = &mame->pause_callback_list; *cur; cur = &(*cur)->next) ;
+ *cur = cb;
+}
+
+
+/*-------------------------------------------------
+ add_exit_callback - request a callback on
+ termination
+-------------------------------------------------*/
+
+void add_exit_callback(running_machine *machine, void (*callback)(running_machine *))
+{
+ mame_private *mame = machine->mame_data;
+ callback_item *cb;
+
+ assert_always(mame_get_phase(machine) == MAME_PHASE_INIT, "Can only call add_exit_callback at init time!");
+
+ /* allocate memory */
+ cb = malloc_or_die(sizeof(*cb));
+
+ /* add us to the head of the list */
+ cb->func.exit = callback;
+ cb->next = mame->exit_callback_list;
+ mame->exit_callback_list = cb;
+}
+
+
+/*-------------------------------------------------
+ mame_frame_update - handle update tasks for a
+ frame boundary
+-------------------------------------------------*/
+
+void mame_frame_update(running_machine *machine)
+{
+ callback_item *cb;
+
+ /* call all registered reset callbacks */
+ for (cb = machine->mame_data->frame_callback_list; cb; cb = cb->next)
+ (*cb->func.frame)(machine);
+}
+
+
+
+/***************************************************************************
+ GLOBAL SYSTEM STATES
+***************************************************************************/
+
+/*-------------------------------------------------
+ mame_schedule_exit - schedule a clean exit
+-------------------------------------------------*/
+
+void mame_schedule_exit(running_machine *machine)
+{
+ mame_private *mame = machine->mame_data;
+
+ /* if we are in-game but we started with the select game menu, return to that instead */
+ if (started_empty && options_get_string(mame_options(), OPTION_GAMENAME)[0] != 0)
+ {
+ options_set_string(mame_options(), OPTION_GAMENAME, "", OPTION_PRIORITY_CMDLINE);
+ ui_menu_force_game_select();
+ }
+
+ /* otherwise, exit for real */
+ else
+ mame->exit_pending = TRUE;
+
+ /* if we're executing, abort out immediately */
+ if (cpu_getactivecpu() >= 0)
+ activecpu_adjust_icount(-activecpu_get_icount() - 1);
+
+ /* if we're autosaving on exit, schedule a save as well */
+ if (options_get_bool(mame_options(), OPTION_AUTOSAVE) && (machine->gamedrv->flags & GAME_SUPPORTS_SAVE))
+ mame_schedule_save(machine, "auto");
+}
+
+
+/*-------------------------------------------------
+ mame_schedule_hard_reset - schedule a hard-
+ reset of the system
+-------------------------------------------------*/
+
+void mame_schedule_hard_reset(running_machine *machine)
+{
+ mame_private *mame = machine->mame_data;
+ mame->hard_reset_pending = TRUE;
+
+ /* if we're executing, abort out immediately */
+ if (cpu_getactivecpu() >= 0)
+ activecpu_adjust_icount(-activecpu_get_icount() - 1);
+}
+
+
+/*-------------------------------------------------
+ mame_schedule_soft_reset - schedule a soft-
+ reset of the system
+-------------------------------------------------*/
+
+void mame_schedule_soft_reset(running_machine *machine)
+{
+ mame_private *mame = machine->mame_data;
+
+ timer_adjust(mame->soft_reset_timer, attotime_zero, 0, attotime_zero);
+
+ /* we can't be paused since the timer needs to fire */
+ mame_pause(machine, FALSE);
+
+ /* if we're executing, abort out immediately */
+ if (cpu_getactivecpu() >= 0)
+ activecpu_adjust_icount(-activecpu_get_icount() - 1);
+}
+
+
+/*-------------------------------------------------
+ mame_schedule_new_driver - schedule a new game
+ to be loaded
+-------------------------------------------------*/
+
+void mame_schedule_new_driver(running_machine *machine, const game_driver *driver)
+{
+ mame_private *mame = machine->mame_data;
+ mame->hard_reset_pending = TRUE;
+ mame->new_driver_pending = driver;
+
+ /* if we're executing, abort out immediately */
+ if (cpu_getactivecpu() >= 0)
+ activecpu_adjust_icount(-activecpu_get_icount() - 1);
+}
+
+
+/*-------------------------------------------------
+ mame_schedule_save - schedule a save to
+ occur as soon as possible
+-------------------------------------------------*/
+
+void mame_schedule_save(running_machine *machine, const char *filename)
+{
+ mame_private *mame = machine->mame_data;
+
+ /* free any existing request and allocate a copy of the requested name */
+ if (mame->saveload_pending_file != NULL)
+ astring_free(mame->saveload_pending_file);
+ mame->saveload_pending_file = astring_assemble_4(astring_alloc(), machine->basename, PATH_SEPARATOR, filename, ".sta");
+
+ /* note the start time and set a timer for the next timeslice to actually schedule it */
+ mame->saveload_schedule_callback = handle_save;
+ mame->saveload_schedule_time = timer_get_time();
+
+ /* we can't be paused since we need to clear out anonymous timers */
+ mame_pause(machine, FALSE);
+}
+
+
+/*-------------------------------------------------
+ mame_schedule_load - schedule a load to
+ occur as soon as possible
+-------------------------------------------------*/
+
+void mame_schedule_load(running_machine *machine, const char *filename)
+{
+ mame_private *mame = machine->mame_data;
+
+ /* free any existing request and allocate a copy of the requested name */
+ if (mame->saveload_pending_file != NULL)
+ astring_free(mame->saveload_pending_file);
+ mame->saveload_pending_file = astring_assemble_4(astring_alloc(), machine->basename, PATH_SEPARATOR, filename, ".sta");
+
+ /* note the start time and set a timer for the next timeslice to actually schedule it */
+ mame->saveload_schedule_callback = handle_load;
+ mame->saveload_schedule_time = timer_get_time();
+
+ /* we can't be paused since we need to clear out anonymous timers */
+ mame_pause(machine, FALSE);
+}
+
+
+/*-------------------------------------------------
+ mame_is_scheduled_event_pending - is a
+ scheduled event pending?
+-------------------------------------------------*/
+
+int mame_is_scheduled_event_pending(running_machine *machine)
+{
+ /* we can't check for saveload_pending_file here because it will bypass */
+ /* required UI screens if a state is queued from the command line */
+ mame_private *mame = machine->mame_data;
+ return mame->exit_pending || mame->hard_reset_pending;
+}
+
+
+/*-------------------------------------------------
+ mame_pause - pause or resume the system
+-------------------------------------------------*/
+
+void mame_pause(running_machine *machine, int pause)
+{
+ mame_private *mame = machine->mame_data;
+ callback_item *cb;
+
+ /* ignore if nothing has changed */
+ if (mame->paused == pause)
+ return;
+ mame->paused = pause;
+
+ /* call all registered pause callbacks */
+ for (cb = mame->pause_callback_list; cb; cb = cb->next)
+ (*cb->func.pause)(machine, mame->paused);
+}
+
+
+/*-------------------------------------------------
+ mame_is_paused - the system paused?
+-------------------------------------------------*/
+
+int mame_is_paused(running_machine *machine)
+{
+ mame_private *mame = machine->mame_data;
+ return (mame->current_phase != MAME_PHASE_RUNNING) || mame->paused;
+}
+
+
+
+/***************************************************************************
+ MEMORY REGIONS
+***************************************************************************/
+
+/*-------------------------------------------------
+ memory_region_to_index - returns an index
+ given either an index or a REGION_* identifier
+-------------------------------------------------*/
+
+static int memory_region_to_index(mame_private *mame, int num)
+{
+ int i;
+
+ /* if we're already an index, stop there */
+ if (num < MAX_MEMORY_REGIONS)
+ return num;
+
+ /* scan for a match */
+ for (i = 0; i < MAX_MEMORY_REGIONS; i++)
+ if (mame->mem_region[i].type == num)
+ return i;
+
+ return -1;
+}
+
+
+/*-------------------------------------------------
+ new_memory_region - allocates memory for a
+ region
+-------------------------------------------------*/
+
+UINT8 *new_memory_region(running_machine *machine, int type, UINT32 length, UINT32 flags)
+{
+ mame_private *mame = machine->mame_data;
+ int num;
+
+ assert(type >= MAX_MEMORY_REGIONS);
+
+ /* find a free slot */
+ for (num = 0; num < MAX_MEMORY_REGIONS; num++)
+ if (mame->mem_region[num].base == NULL)
+ break;
+ if (num < 0)
+ fatalerror("Out of memory regions!");
+
+ /* allocate the region */
+ mame->mem_region[num].length = length;
+ mame->mem_region[num].type = type;
+ mame->mem_region[num].flags = flags;
+ mame->mem_region[num].base = malloc_or_die(length);
+ return mame->mem_region[num].base;
+}
+
+
+/*-------------------------------------------------
+ free_memory_region - releases memory for a
+ region
+-------------------------------------------------*/
+
+void free_memory_region(running_machine *machine, int num)
+{
+ mame_private *mame = machine->mame_data;
+
+ /* convert to an index and bail if invalid */
+ num = memory_region_to_index(mame, num);
+ if (num < 0)
+ return;
+
+ /* free the region in question */
+ free(mame->mem_region[num].base);
+ memset(&mame->mem_region[num], 0, sizeof(mame->mem_region[num]));
+}
+
+
+/*-------------------------------------------------
+ memory_region - returns pointer to a memory
+ region
+-------------------------------------------------*/
+
+UINT8 *memory_region(int num)
+{
+ running_machine *machine = Machine;
+ mame_private *mame = machine->mame_data;
+
+ /* convert to an index and return the result */
+ num = memory_region_to_index(mame, num);
+ return (num >= 0) ? mame->mem_region[num].base : NULL;
+}
+
+
+/*-------------------------------------------------
+ memory_region_length - returns length of a
+ memory region
+-------------------------------------------------*/
+
+UINT32 memory_region_length(int num)
+{
+ running_machine *machine = Machine;
+ mame_private *mame = machine->mame_data;
+
+ /* convert to an index and return the result */
+ num = memory_region_to_index(mame, num);
+ return (num >= 0) ? mame->mem_region[num].length : 0;
+}
+
+
+/*-------------------------------------------------
+ memory_region_type - returns the type of a
+ memory region
+-------------------------------------------------*/
+
+UINT32 memory_region_type(running_machine *machine, int num)
+{
+ mame_private *mame = machine->mame_data;
+
+ /* convert to an index and return the result */
+ num = memory_region_to_index(mame, num);
+ return (num >= 0) ? mame->mem_region[num].type : 0;
+}
+
+
+/*-------------------------------------------------
+ memory_region_flags - returns flags for a
+ memory region
+-------------------------------------------------*/
+
+UINT32 memory_region_flags(running_machine *machine, int num)
+{
+ mame_private *mame = machine->mame_data;
+
+ /* convert to an index and return the result */
+ num = memory_region_to_index(mame, num);
+ return (num >= 0) ? mame->mem_region[num].flags : 0;
+}
+
+
+
+/***************************************************************************
+ OUTPUT MANAGEMENT
+***************************************************************************/
+
+/*-------------------------------------------------
+ mame_set_output_channel - configure an output
+ channel
+-------------------------------------------------*/
+
+void mame_set_output_channel(output_channel channel, output_callback callback, void *param, output_callback *prevcb, void **prevparam)
+{
+ assert(channel < OUTPUT_CHANNEL_COUNT);
+ assert(callback != NULL);
+
+ /* return the originals if requested */
+ if (prevcb != NULL)
+ *prevcb = output_cb[channel];
+ if (prevparam != NULL)
+ *prevparam = output_cb_param[channel];
+
+ /* set the new ones */
+ output_cb[channel] = callback;
+ output_cb_param[channel] = param;
+}
+
+
+/*-------------------------------------------------
+ mame_file_output_callback - default callback
+ for file output
+-------------------------------------------------*/
+
+void mame_file_output_callback(void *param, const char *format, va_list argptr)
+{
+ vfprintf((FILE *)param, format, argptr);
+}
+
+
+/*-------------------------------------------------
+ mame_null_output_callback - default callback
+ for no output
+-------------------------------------------------*/
+
+void mame_null_output_callback(void *param, const char *format, va_list argptr)
+{
+}
+
+
+/*-------------------------------------------------
+ mame_printf_error - output an error to the
+ appropriate callback
+-------------------------------------------------*/
+
+void mame_printf_error(const char *format, ...)
+{
+ va_list argptr;
+
+ /* by default, we go to stderr */
+ if (output_cb[OUTPUT_CHANNEL_ERROR] == NULL)
+ {
+ output_cb[OUTPUT_CHANNEL_ERROR] = mame_file_output_callback;
+ output_cb_param[OUTPUT_CHANNEL_ERROR] = stderr;
+ }
+
+ /* do the output */
+ va_start(argptr, format);
+ (*output_cb[OUTPUT_CHANNEL_ERROR])(output_cb_param[OUTPUT_CHANNEL_ERROR], format, argptr);
+ va_end(argptr);
+}
+
+
+/*-------------------------------------------------
+ mame_printf_warning - output a warning to the
+ appropriate callback
+-------------------------------------------------*/
+
+void mame_printf_warning(const char *format, ...)
+{
+ va_list argptr;
+
+ /* by default, we go to stderr */
+ if (output_cb[OUTPUT_CHANNEL_WARNING] == NULL)
+ {
+ output_cb[OUTPUT_CHANNEL_WARNING] = mame_file_output_callback;
+ output_cb_param[OUTPUT_CHANNEL_WARNING] = stderr;
+ }
+
+ /* do the output */
+ va_start(argptr, format);
+ (*output_cb[OUTPUT_CHANNEL_WARNING])(output_cb_param[OUTPUT_CHANNEL_WARNING], format, argptr);
+ va_end(argptr);
+}
+
+
+/*-------------------------------------------------
+ mame_printf_info - output info text to the
+ appropriate callback
+-------------------------------------------------*/
+
+void mame_printf_info(const char *format, ...)
+{
+ va_list argptr;
+
+ /* by default, we go to stdout */
+ if (output_cb[OUTPUT_CHANNEL_INFO] == NULL)
+ {
+ output_cb[OUTPUT_CHANNEL_INFO] = mame_file_output_callback;
+ output_cb_param[OUTPUT_CHANNEL_INFO] = stdout;
+ }
+
+ /* do the output */
+ va_start(argptr, format);
+ (*output_cb[OUTPUT_CHANNEL_INFO])(output_cb_param[OUTPUT_CHANNEL_INFO], format, argptr);
+ va_end(argptr);
+}
+
+
+/*-------------------------------------------------
+ mame_printf_verbose - output verbose text to
+ the appropriate callback
+-------------------------------------------------*/
+
+void mame_printf_verbose(const char *format, ...)
+{
+ va_list argptr;
+
+ /* if we're not verbose, skip it */
+ if (mame_opts == NULL || !options_get_bool(mame_options(), OPTION_VERBOSE))
+ return;
+
+ /* by default, we go to stdout */
+ if (output_cb[OUTPUT_CHANNEL_VERBOSE] == NULL)
+ {
+ output_cb[OUTPUT_CHANNEL_VERBOSE] = mame_file_output_callback;
+ output_cb_param[OUTPUT_CHANNEL_VERBOSE] = stdout;
+ }
+
+ /* do the output */
+ va_start(argptr, format);
+ (*output_cb[OUTPUT_CHANNEL_VERBOSE])(output_cb_param[OUTPUT_CHANNEL_VERBOSE], format, argptr);
+ va_end(argptr);
+}
+
+
+/*-------------------------------------------------
+ mame_printf_debug - output debug text to the
+ appropriate callback
+-------------------------------------------------*/
+
+void mame_printf_debug(const char *format, ...)
+{
+ va_list argptr;
+
+ /* by default, we go to stderr */
+ if (output_cb[OUTPUT_CHANNEL_DEBUG] == NULL)
+ {
+#ifdef MAME_DEBUG
+ output_cb[OUTPUT_CHANNEL_DEBUG] = mame_file_output_callback;
+ output_cb_param[OUTPUT_CHANNEL_DEBUG] = stdout;
+#else
+ output_cb[OUTPUT_CHANNEL_DEBUG] = mame_null_output_callback;
+ output_cb_param[OUTPUT_CHANNEL_DEBUG] = NULL;
+#endif
+ }
+
+ /* do the output */
+ va_start(argptr, format);
+ (*output_cb[OUTPUT_CHANNEL_DEBUG])(output_cb_param[OUTPUT_CHANNEL_DEBUG], format, argptr);
+ va_end(argptr);
+}
+
+
+/*-------------------------------------------------
+ mame_printf_log - output log text to the
+ appropriate callback
+-------------------------------------------------*/
+
+void mame_printf_log(const char *format, ...)
+{
+ va_list argptr;
+
+ /* by default, we go to stderr */
+ if (output_cb[OUTPUT_CHANNEL_LOG] == NULL)
+ {
+ output_cb[OUTPUT_CHANNEL_LOG] = mame_file_output_callback;
+ output_cb_param[OUTPUT_CHANNEL_LOG] = stderr;
+ }
+
+ /* do the output */
+ va_start(argptr, format);
+ (*output_cb[OUTPUT_CHANNEL_LOG])(output_cb_param[OUTPUT_CHANNEL_LOG], format, argptr);
+ va_end(argptr);
+}
+
+
+
+/***************************************************************************
+ MISCELLANEOUS
+***************************************************************************/
+
+/*-------------------------------------------------
+ fatalerror - print a message and escape back
+ to the OSD layer
+-------------------------------------------------*/
+
+DECL_NORETURN static void fatalerror_common(running_machine *machine, int exitcode, const char *buffer) ATTR_NORETURN;
+
+static void fatalerror_common(running_machine *machine, int exitcode, const char *buffer)
+{
+ /* output and return */
+ mame_printf_error("%s\n", giant_string_buffer);
+
+ /* break into the debugger if attached */
+ osd_break_into_debugger(giant_string_buffer);
+
+ /* longjmp back if we can; otherwise, exit */
+ if (machine != NULL && machine->mame_data != NULL && machine->mame_data->fatal_error_jmpbuf_valid)
+ longjmp(machine->mame_data->fatal_error_jmpbuf, exitcode);
+ else
+ exit(exitcode);
+}
+
+
+void CLIB_DECL fatalerror(const char *text, ...)
+{
+ running_machine *machine = Machine;
+ va_list arg;
+
+ /* dump to the buffer; assume no one writes >2k lines this way */
+ va_start(arg, text);
+ vsnprintf(giant_string_buffer, GIANT_STRING_BUFFER_SIZE, text, arg);
+ va_end(arg);
+
+ fatalerror_common(machine, MAMERR_FATALERROR, giant_string_buffer);
+}
+
+
+void CLIB_DECL fatalerror_exitcode(int exitcode, const char *text, ...)
+{
+ running_machine *machine = Machine;
+ va_list arg;
+
+ /* dump to the buffer; assume no one writes >2k lines this way */
+ va_start(arg, text);
+ vsnprintf(giant_string_buffer, GIANT_STRING_BUFFER_SIZE, text, arg);
+ va_end(arg);
+
+ fatalerror_common(machine, exitcode, giant_string_buffer);
+}
+
+
+/*-------------------------------------------------
+ popmessage - pop up a user-visible message
+-------------------------------------------------*/
+
+void CLIB_DECL popmessage(const char *text, ...)
+{
+ extern void CLIB_DECL ui_popup(const char *text, ...) ATTR_PRINTF(1,2);
+ va_list arg;
+
+ /* dump to the buffer */
+ va_start(arg, text);
+ vsnprintf(giant_string_buffer, GIANT_STRING_BUFFER_SIZE, text, arg);
+ va_end(arg);
+
+ /* pop it in the UI */
+ ui_popup("%s", giant_string_buffer);
+}
+
+
+/*-------------------------------------------------
+ logerror - log to the debugger and any other
+ OSD-defined output streams
+-------------------------------------------------*/
+
+void CLIB_DECL logerror(const char *text, ...)
+{
+ running_machine *machine = Machine;
+
+ /* currently, we need a machine to do this */
+ if (machine != NULL)
+ {
+ mame_private *mame = machine->mame_data;
+ callback_item *cb;
+
+ /* process only if there is a target */
+ if (mame->logerror_callback_list != NULL)
+ {
+ va_list arg;
+
+ profiler_mark(PROFILER_LOGERROR);
+
+ /* dump to the buffer */
+ va_start(arg, text);
+ vsnprintf(giant_string_buffer, GIANT_STRING_BUFFER_SIZE, text, arg);
+ va_end(arg);
+
+ /* log to all callbacks */
+ for (cb = mame->logerror_callback_list; cb; cb = cb->next)
+ (*cb->func.log)(machine, giant_string_buffer);
+
+ profiler_mark(PROFILER_END);
+ }
+ }
+}
+
+
+/*-------------------------------------------------
+ add_logerror_callback - adds a callback to be
+ called on logerror()
+-------------------------------------------------*/
+
+void add_logerror_callback(running_machine *machine, void (*callback)(running_machine *, const char *))
+{
+ mame_private *mame = machine->mame_data;
+ callback_item *cb, **cur;
+
+ assert_always(mame_get_phase(machine) == MAME_PHASE_INIT, "Can only call add_logerror_callback at init time!");
+
+ cb = auto_malloc(sizeof(*cb));
+ cb->func.log = callback;
+ cb->next = NULL;
+
+ for (cur = &mame->logerror_callback_list; *cur; cur = &(*cur)->next) ;
+ *cur = cb;
+}
+
+
+/*-------------------------------------------------
+ logfile_callback - callback for logging to
+ logfile
+-------------------------------------------------*/
+
+static void logfile_callback(running_machine *machine, const char *buffer)
+{
+ mame_private *mame = machine->mame_data;
+ if (mame->logfile != NULL)
+ mame_fputs(mame->logfile, buffer);
+}
+
+
+/*-------------------------------------------------
+ mame_find_cpu_index - return the index of the
+ given CPU, or -1 if not found
+-------------------------------------------------*/
+
+int mame_find_cpu_index(running_machine *machine, const char *tag)
+{
+ int cpunum;
+
+ for (cpunum = 0; cpunum < MAX_CPU; cpunum++)
+ if (machine->drv->cpu[cpunum].tag && strcmp(machine->drv->cpu[cpunum].tag, tag) == 0)
+ return cpunum;
+
+ return -1;
+}
+
+
+/*-------------------------------------------------
+ mame_rand - standardized random numbers
+-------------------------------------------------*/
+
+UINT32 mame_rand(running_machine *machine)
+{
+ mame_private *mame = machine->mame_data;
+ mame->rand_seed = 1664525 * mame->rand_seed + 1013904223;
+
+ /* return rotated by 16 bits; the low bits have a short period
+ and are frequently used */
+ return (mame->rand_seed >> 16) | (mame->rand_seed << 16);
+}
+
+
+
+/***************************************************************************
+ INTERNAL INITIALIZATION LOGIC
+***************************************************************************/
+
+/*-------------------------------------------------
+ mame_parse_ini_files - parse the relevant INI
+ files and apply their options
+-------------------------------------------------*/
+
+void mame_parse_ini_files(core_options *options, const game_driver *driver)
+{
+ /* parse the INI file defined by the platform (e.g., "mame.ini") */
+ /* we do this twice so that the first file can change the INI path */
+ parse_ini_file(options, CONFIGNAME);
+ parse_ini_file(options, CONFIGNAME);
+
+ /* debug builds: parse "debug.ini" as well */
+#ifdef MAME_DEBUG
+ parse_ini_file(options, "debug");
+#endif
+
+ /* if we have a valid game driver, parse game-specific INI files */
+ if (driver != NULL)
+ {
+ const game_driver *parent = driver_get_clone(driver);
+ const game_driver *gparent = (parent != NULL) ? driver_get_clone(parent) : NULL;
+ astring *sourcename;
+ machine_config drv;
+
+ /* expand the machine driver to look at the info */
+ expand_machine_driver(driver->drv, &drv);
+
+ /* parse "vector.ini" for vector games */
+ if (drv.video_attributes & VIDEO_TYPE_VECTOR)
+ parse_ini_file(options, "vector");
+
+ /* next parse "source/<sourcefile>.ini"; if that doesn't exist, try <sourcefile>.ini */
+ sourcename = core_filename_extract_base(astring_alloc(), driver->source_file, TRUE);
+ astring_insc(sourcename, 0, "source" PATH_SEPARATOR);
+ if (!parse_ini_file(options, astring_c(sourcename)))
+ {
+ core_filename_extract_base(sourcename, driver->source_file, TRUE);
+ parse_ini_file(options, astring_c(sourcename));
+ }
+ astring_free(sourcename);
+
+ /* then parent the grandparent, parent, and game-specific INIs */
+ if (gparent != NULL)
+ parse_ini_file(options, gparent->name);
+ if (parent != NULL)
+ parse_ini_file(options, parent->name);
+ parse_ini_file(options, driver->name);
+ }
+}
+
+
+/*-------------------------------------------------
+ parse_ini_file - parse a single INI file
+-------------------------------------------------*/
+
+static int parse_ini_file(core_options *options, const char *name)
+{
+ file_error filerr;
+ mame_file *file;
+ astring *fname;
+
+ /* don't parse if it has been disabled */
+ if (!options_get_bool(options, OPTION_READCONFIG))
+ return FALSE;
+
+ /* open the file; if we fail, that's ok */
+ fname = astring_assemble_2(astring_alloc(), name, ".ini");
+ filerr = mame_fopen_options(options, SEARCHPATH_INI, astring_c(fname), OPEN_FLAG_READ, &file);
+ astring_free(fname);
+ if (filerr != FILERR_NONE)
+ return FALSE;
+
+ /* parse the file and close it */
+ mame_printf_verbose("Parsing %s.ini\n", name);
+ options_parse_ini_file(options, mame_core_file(file), OPTION_PRIORITY_INI);
+ mame_fclose(file);
+ return TRUE;
+}
+
+
+/*-------------------------------------------------
+ create_machine - create the running machine
+ object and initialize it based on options
+-------------------------------------------------*/
+
+static running_machine *create_machine(const game_driver *driver)
+{
+ running_machine *machine;
+
+ /* allocate memory for the machine */
+ machine = malloc(sizeof(*machine));
+ if (machine == NULL)
+ goto error;
+ memset(machine, 0, sizeof(*machine));
+
+ /* allocate memory for the internal mame_data */
+ machine->mame_data = malloc(sizeof(*machine->mame_data));
+ if (machine->mame_data == NULL)
+ goto error;
+ memset(machine->mame_data, 0, sizeof(*machine->mame_data));
+
+ /* initialize the driver-related variables in the machine */
+ machine->gamedrv = driver;
+ machine->drv = malloc(sizeof(*machine->drv));
+ if (machine->drv == NULL)
+ goto error;
+ machine->basename = mame_strdup(machine->gamedrv->name);
+ expand_machine_driver(machine->gamedrv->drv, (machine_config *)machine->drv);
+
+ /* allocate the driver data */
+ if (machine->drv->driver_data_size != 0)
+ {
+ machine->driver_data = malloc(machine->drv->driver_data_size);
+ if (machine->driver_data == NULL)
+ goto error;
+ }
+ return machine;
+
+error:
+ if (machine->driver_data != NULL)
+ free(machine->driver_data);
+ if (machine->drv != NULL)
+ free((machine_config *)machine->drv);
+ if (machine->mame_data != NULL)
+ free(machine->mame_data);
+ if (machine != NULL)
+ free(machine);
+ return NULL;
+}
+
+
+/*-------------------------------------------------
+ reset_machine - reset the state of the
+ machine object
+-------------------------------------------------*/
+
+static void reset_machine(running_machine *machine)
+{
+ int scrnum;
+
+ /* reset most portions of the machine */
+
+ /* video-related information */
+ memset(machine->gfx, 0, sizeof(machine->gfx));
+ for (scrnum = 0; scrnum < MAX_SCREENS; scrnum++)
+ machine->screen[scrnum] = machine->drv->screen[scrnum].defstate;
+
+ /* palette-related information */
+ machine->pens = NULL;
+ machine->game_colortable = NULL;
+ machine->remapped_colortable = NULL;
+ machine->shadow_table = NULL;
+
+ /* audio-related information */
+ machine->sample_rate = options_get_int(mame_options(), OPTION_SAMPLERATE);
+
+ /* input-related information */
+ machine->input_ports = NULL;
+ machine->record_file = NULL;
+ machine->playback_file = NULL;
+
+ /* debugger-related information */
+#ifdef MAME_DEBUG
+ machine->debug_mode = options_get_bool(mame_options(), OPTION_DEBUG);
+#else
+ machine->debug_mode = 0;
+#endif
+
+ /* reset the global MAME data and clear the other privates */
+ memset(machine->mame_data, 0, sizeof(*machine->mame_data));
+ machine->video_data = NULL;
+ machine->palette_data = NULL;
+ machine->streams_data = NULL;
+
+ /* reset the driver data */
+ if (machine->drv->driver_data_size != 0)
+ memset(machine->driver_data, 0, machine->drv->driver_data_size);
+}
+
+
+
+/*-------------------------------------------------
+ destroy_machine - free the machine data
+-------------------------------------------------*/
+
+static void destroy_machine(running_machine *machine)
+{
+ assert(machine == Machine);
+ if (machine->driver_data != NULL)
+ free(machine->driver_data);
+ if (machine->drv != NULL)
+ free((machine_config *)machine->drv);
+ if (machine->mame_data != NULL)
+ free(machine->mame_data);
+ if (machine->basename != NULL)
+ free((void *)machine->basename);
+ free(machine);
+ Machine = NULL;
+}
+
+
+/*-------------------------------------------------
+ init_machine - initialize the emulated machine
+-------------------------------------------------*/
+
+static void init_machine(running_machine *machine)
+{
+ mame_private *mame = machine->mame_data;
+ int num;
+
+ /* initialize basic can't-fail systems here */
+ cpuintrf_init(machine);
+ sndintrf_init(machine);
+ fileio_init(machine);
+ config_init(machine);
+ input_init(machine);
+ output_init(machine);
+ state_init(machine);
+ state_save_allow_registration(TRUE);
+ drawgfx_init(machine);
+ palette_init(machine);
+ render_init(machine);
+ ui_init(machine);
+ generic_machine_init(machine);
+ generic_video_init(machine);
+ generic_sound_init();
+ mame->rand_seed = 0x9d14abd7;
+
+ /* initialize the timers and allocate a soft_reset timer */
+ /* this must be done before cpu_init so that CPU's can allocate timers */
+ timer_init(machine);
+ mame->soft_reset_timer = timer_alloc(soft_reset);
+
+ /* init the osd layer */
+ osd_init(machine);
+
+ /* 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 */
+ input_port_init(machine, machine->gamedrv->ipt);
+
+ /* initialize the base time (if not doing record/playback) */
+ if (machine->record_file == NULL && machine->playback_file == NULL)
+ time(&mame->base_time);
+ else
+ mame->base_time = 0;
+
+ /* first load ROMs, then populate memory, and finally initialize CPUs */
+ /* these operations must proceed in this order */
+ rom_init(machine, machine->gamedrv->rom);
+ memory_init(machine);
+ cpuexec_init(machine);
+ cpuint_init(machine);
+
+#ifdef MESS
+ /* initialize the devices */
+ devices_init(machine);
+#endif
+
+#ifdef MAME_DEBUG
+ /* initialize the debugger */
+ if (machine->debug_mode)
+ mame_debug_init(machine);
+#endif
+
+ /* 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);
+ if (machine->gamedrv->driver_init != NULL)
+ (*machine->gamedrv->driver_init)(machine);
+
+ /* start the video and audio hardware */
+ video_init(machine);
+ sound_init(machine);
+
+ /* call the driver's _START callbacks */
+ if (machine->drv->machine_start != NULL) (*machine->drv->machine_start)(machine);
+ if (machine->drv->sound_start != NULL) (*machine->drv->sound_start)(machine);
+ if (machine->drv->video_start != NULL) (*machine->drv->video_start)(machine);
+
+ /* free memory regions allocated with REGIONFLAG_DISPOSE (typically gfx roms) */
+ for (num = 0; num < MAX_MEMORY_REGIONS; num++)
+ if (mame->mem_region[num].flags & ROMREGION_DISPOSE)
+ free_memory_region(machine, num);
+
+ /* initialize miscellaneous systems */
+ saveload_init(machine);
+ if (options_get_bool(mame_options(), OPTION_CHEAT))
+ cheat_init(machine);
+}
+
+
+/*-------------------------------------------------
+ soft_reset - actually perform a soft-reset
+ of the system
+-------------------------------------------------*/
+
+static TIMER_CALLBACK( soft_reset )
+{
+ mame_private *mame = machine->mame_data;
+ callback_item *cb;
+
+ logerror("Soft reset\n");
+
+ /* temporarily in the reset phase */
+ mame->current_phase = MAME_PHASE_RESET;
+
+ /* a bit gross -- back off of the resource tracking, and put it back at the end */
+ assert(get_resource_tag() == 2);
+ end_resource_tracking();
+ begin_resource_tracking();
+
+ /* allow save state registrations during the reset */
+ state_save_allow_registration(TRUE);
+
+ /* unfortunately, we can't rely on callbacks to reset the interrupt */
+ /* structures, as these need to happen before we call the reset */
+ /* functions registered by the drivers */
+ cpuint_reset();
+
+ /* run the driver's reset callbacks */
+ if (machine->drv->machine_reset != NULL)
+ (*machine->drv->machine_reset)(machine);
+ if (machine->drv->sound_reset != NULL)
+ (*machine->drv->sound_reset)(machine);
+ if (machine->drv->video_reset != NULL)
+ (*machine->drv->video_reset)(machine);
+
+ /* call all registered reset callbacks */
+ for (cb = machine->mame_data->reset_callback_list; cb; cb = cb->next)
+ (*cb->func.reset)(machine);
+
+ /* disallow save state registrations starting here */
+ state_save_allow_registration(FALSE);
+
+ /* now we're running */
+ mame->current_phase = MAME_PHASE_RUNNING;
+
+ /* set the global time to the current time */
+ /* this allows 0-time queued callbacks to run before any CPUs execute */
+ timer_set_global_time(timer_get_time());
+}
+
+
+/*-------------------------------------------------
+ free_callback_list - free a list of callbacks
+-------------------------------------------------*/
+
+static void free_callback_list(callback_item **cb)
+{
+ while (*cb)
+ {
+ callback_item *temp = *cb;
+ *cb = (*cb)->next;
+ free(temp);
+ }
+}
+
+
+
+/***************************************************************************
+ SAVE/RESTORE
+***************************************************************************/
+
+/*-------------------------------------------------
+ saveload_init - initialize the save/load logic
+-------------------------------------------------*/
+
+static void saveload_init(running_machine *machine)
+{
+ const char *savegame = options_get_string(mame_options(), OPTION_STATE);
+
+ /* if we're coming in with a savegame request, process it now */
+ if (savegame[0] != 0)
+ mame_schedule_load(machine, savegame);
+
+ /* if we're in autosave mode, schedule a load */
+ else if (options_get_bool(mame_options(), OPTION_AUTOSAVE) && (machine->gamedrv->flags & GAME_SUPPORTS_SAVE))
+ mame_schedule_load(machine, "auto");
+}
+
+
+/*-------------------------------------------------
+ handle_save - attempt to perform a save
+-------------------------------------------------*/
+
+static void handle_save(running_machine *machine)
+{
+ mame_private *mame = machine->mame_data;
+ file_error filerr;
+ mame_file *file;
+
+ /* if no name, bail */
+ if (mame->saveload_pending_file == NULL)
+ {
+ mame->saveload_schedule_callback = NULL;
+ return;
+ }
+
+ /* if there are anonymous timers, we can't save just yet */
+ if (timer_count_anonymous() > 0)
+ {
+ /* if more than a second has passed, we're probably screwed */
+ if (attotime_sub(timer_get_time(), mame->saveload_schedule_time).seconds > 0)
+ {
+ popmessage("Unable to save due to pending anonymous timers. See error.log for details.");
+ goto cancel;
+ }
+ return;
+ }
+
+ /* open the file */
+ filerr = mame_fopen(SEARCHPATH_STATE, astring_c(mame->saveload_pending_file), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS, &file);
+ if (filerr == FILERR_NONE)
+ {
+ int cpunum;
+
+ /* write the save state */
+ if (state_save_save_begin(file) != 0)
+ {
+ popmessage("Error: Unable to save state due to illegal registrations. See error.log for details.");
+ mame_fclose(file);
+ goto cancel;
+ }
+
+ /* write the default tag */
+ state_save_push_tag(0);
+ state_save_save_continue();
+ state_save_pop_tag();
+
+ /* loop over CPUs */
+ for (cpunum = 0; cpunum < cpu_gettotalcpu(); cpunum++)
+ {
+ cpuintrf_push_context(cpunum);
+
+ /* make sure banking is set */
+ activecpu_reset_banking();
+
+ /* save the CPU data */
+ state_save_push_tag(cpunum + 1);
+ state_save_save_continue();
+ state_save_pop_tag();
+
+ cpuintrf_pop_context();
+ }
+
+ /* finish and close */
+ state_save_save_finish();
+ mame_fclose(file);
+
+ /* pop a warning if the game doesn't support saves */
+ if (!(machine->gamedrv->flags & GAME_SUPPORTS_SAVE))
+ popmessage("State successfully saved.\nWarning: Save states are not officially supported for this game.");
+ else
+ popmessage("State successfully saved.");
+ }
+ else
+ popmessage("Error: Failed to save state");
+
+cancel:
+ /* unschedule the save */
+ astring_free(mame->saveload_pending_file);
+ mame->saveload_pending_file = NULL;
+ mame->saveload_schedule_callback = NULL;
+}
+
+
+/*-------------------------------------------------
+ handle_load - attempt to perform a load
+-------------------------------------------------*/
+
+static void handle_load(running_machine *machine)
+{
+ mame_private *mame = machine->mame_data;
+ file_error filerr;
+ mame_file *file;
+
+ /* if no name, bail */
+ if (mame->saveload_pending_file == NULL)
+ {
+ mame->saveload_schedule_callback = NULL;
+ return;
+ }
+
+ /* if there are anonymous timers, we can't load just yet because the timers might */
+ /* overwrite data we have loaded */
+ if (timer_count_anonymous() > 0)
+ {
+ /* if more than a second has passed, we're probably screwed */
+ if (attotime_sub(timer_get_time(), mame->saveload_schedule_time).seconds > 0)
+ {
+ popmessage("Unable to load due to pending anonymous timers. See error.log for details.");
+ goto cancel;
+ }
+ return;
+ }
+
+ /* open the file */
+ filerr = mame_fopen(SEARCHPATH_STATE, astring_c(mame->saveload_pending_file), OPEN_FLAG_READ, &file);
+ if (filerr == FILERR_NONE)
+ {
+ /* start loading */
+ if (state_save_load_begin(file) == 0)
+ {
+ int cpunum;
+
+ /* read tag 0 */
+ state_save_push_tag(0);
+ state_save_load_continue();
+ state_save_pop_tag();
+
+ /* loop over CPUs */
+ for (cpunum = 0; cpunum < cpu_gettotalcpu(); cpunum++)
+ {
+ cpuintrf_push_context(cpunum);
+
+ /* make sure banking is set */
+ activecpu_reset_banking();
+
+ /* load the CPU data */
+ state_save_push_tag(cpunum + 1);
+ state_save_load_continue();
+ state_save_pop_tag();
+
+ /* make sure banking is set */
+ activecpu_reset_banking();
+
+ cpuintrf_pop_context();
+ }
+
+ /* finish and close */
+ state_save_load_finish();
+ popmessage("State successfully loaded.");
+ }
+ else
+ popmessage("Error: Failed to load state");
+ mame_fclose(file);
+ }
+ else
+ popmessage("Error: Failed to load state");
+
+cancel:
+ /* unschedule the load */
+ astring_free(mame->saveload_pending_file);
+ mame->saveload_pending_file = NULL;
+ mame->saveload_schedule_callback = NULL;
+}
+
+
+
+/***************************************************************************
+ SYSTEM TIME
+***************************************************************************/
+
+/*-------------------------------------------------
+ get_tm_time - converts a MAME
+-------------------------------------------------*/
+
+static void get_tm_time(struct tm *t, mame_system_tm *systm)
+{
+ systm->second = t->tm_sec;
+ systm->minute = t->tm_min;
+ systm->hour = t->tm_hour;
+ systm->mday = t->tm_mday;
+ systm->month = t->tm_mon;
+ systm->year = t->tm_year + 1900;
+ systm->weekday = t->tm_wday;
+ systm->day = t->tm_yday;
+ systm->is_dst = t->tm_isdst;
+}
+
+
+
+/*-------------------------------------------------
+ fill_systime - fills out a mame_system_time
+ structure
+-------------------------------------------------*/
+
+static void fill_systime(mame_system_time *systime, time_t t)
+{
+ systime->time = t;
+ get_tm_time(localtime(&t), &systime->local_time);
+ get_tm_time(gmtime(&t), &systime->utc_time);
+}
+
+
+
+/*-------------------------------------------------
+ mame_get_base_datetime - retrieve the time of
+ the host system; useful for RTC implementations
+-------------------------------------------------*/
+
+void mame_get_base_datetime(running_machine *machine, mame_system_time *systime)
+{
+ mame_private *mame = machine->mame_data;
+ fill_systime(systime, mame->base_time);
+}
+
+
+
+/*-------------------------------------------------
+ mame_get_current_datetime - retrieve the current
+ time (offsetted by the baes); useful for RTC
+ implementations
+-------------------------------------------------*/
+
+void mame_get_current_datetime(running_machine *machine, mame_system_time *systime)
+{
+ mame_private *mame = machine->mame_data;
+ fill_systime(systime, mame->base_time + timer_get_time().seconds);
+}