mirror of
https://github.com/holub/mame
synced 2025-05-29 00:53:09 +03:00
1890 lines
54 KiB
C
1890 lines
54 KiB
C
/***************************************************************************
|
|
|
|
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);
|
|
}
|