mame/src/emu/cpuexec.c
Aaron Giles 8eeb0f1d20 Fixed up some error logging messages to output the correct number of characters
for addresses and data, using the new core_i64_hex_format function.
2009-09-10 08:24:15 +00:00

1973 lines
60 KiB
C

/***************************************************************************
cpuexec.c
Core multi-CPU execution engine.
Copyright Nicola Salmoria and the MAME Team.
Visit http://mamedev.org for licensing and usage restrictions.
***************************************************************************/
#include "driver.h"
#include "profiler.h"
#include "eminline.h"
#include "debugger.h"
/***************************************************************************
DEBUGGING
***************************************************************************/
#define VERBOSE 0
#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
/***************************************************************************
CONSTANTS
***************************************************************************/
/* internal trigger IDs */
enum
{
TRIGGER_INT = -2000,
TRIGGER_YIELDTIME = -3000,
TRIGGER_SUSPENDTIME = -4000
};
/***************************************************************************
TYPE DEFINITIONS
***************************************************************************/
/* internal information about the state of inputs */
typedef struct _cpu_input_data cpu_input_data;
struct _cpu_input_data
{
INT32 vector; /* most recently written vector */
INT32 curvector; /* most recently processed vector */
UINT8 curstate; /* most recently processed state */
INT32 queue[MAX_INPUT_EVENTS]; /* queue of pending events */
int qindex; /* index within the queue */
};
/* internal data hanging off of the classtoken */
typedef struct _cpu_class_data cpu_class_data;
struct _cpu_class_data
{
/* execution lists */
const device_config *device; /* pointer back to our device */
cpu_class_data *next; /* pointer to the next CPU to execute, in order */
cpu_execute_func execute; /* execute function pointer */
/* cycle counting and executing */
int profiler; /* profiler tag */
int * icount; /* pointer to the icount */
int cycles_running; /* number of cycles we are executing */
int cycles_stolen; /* number of cycles we artificially stole */
/* input states and IRQ callbacks */
cpu_irq_callback driver_irq; /* driver-specific IRQ callback */
cpu_input_data input[MAX_INPUT_LINES]; /* data about inputs */
/* suspend states */
UINT8 suspend; /* suspend reason mask (0 = not suspended) */
UINT8 nextsuspend; /* pending suspend reason mask */
UINT8 eatcycles; /* true if we eat cycles while suspended */
UINT8 nexteatcycles; /* pending value */
INT32 trigger; /* pending trigger to release a trigger suspension */
INT32 inttrigger; /* interrupt trigger index */
/* clock and timing information */
UINT64 totalcycles; /* total CPU cycles executed */
attotime localtime; /* local time, relative to the timer system's global time */
INT32 clock; /* current active clock */
double clockscale; /* current active clock scale factor */
INT32 divisor; /* 32-bit attoseconds_per_cycle divisor */
UINT8 divshift; /* right shift amount to fit the divisor into 32 bits */
emu_timer * timedint_timer; /* reference to this CPU's periodic interrupt timer */
UINT32 cycles_per_second; /* cycles per second, adjusted for multipliers */
attoseconds_t attoseconds_per_cycle; /* attoseconds per adjusted clock cycle */
/* internal state reflection */
const cpu_state_table *state; /* pointer to the base table */
const cpu_state_entry *regstate[MAX_REGS];/* pointer to the state entry for each register */
/* these below are hacks to support multiple interrupts per frame */
INT32 iloops; /* number of interrupts remaining this frame */
emu_timer * partial_frame_timer; /* the timer that triggers partial frame interrupts */
attotime partial_frame_period; /* the length of one partial frame for interrupt purposes */
};
/* global data stored in the machine */
/* In mame.h: typedef struct _cpuexec_private cpuexec_private; */
struct _cpuexec_private
{
const device_config *executingcpu; /* pointer to the currently executing CPU */
cpu_class_data *executelist; /* execution list; suspended CPUs are at the back */
char statebuf[256]; /* string buffer containing state description */
};
/***************************************************************************
FUNCTION PROTOTYPES
***************************************************************************/
static void update_clock_information(const device_config *device);
static void compute_perfect_interleave(running_machine *machine);
static void on_vblank(const device_config *device, void *param, int vblank_state);
static TIMER_CALLBACK( trigger_partial_frame_interrupt );
static TIMER_CALLBACK( trigger_periodic_interrupt );
static TIMER_CALLBACK( triggertime_callback );
static TIMER_CALLBACK( empty_event_queue );
static IRQ_CALLBACK( standard_irq_callback );
static void register_save_states(const device_config *device);
static void rebuild_execute_list(running_machine *machine);
static UINT64 get_register_value(const device_config *device, void *baseptr, const cpu_state_entry *entry);
static void set_register_value(const device_config *device, void *baseptr, const cpu_state_entry *entry, UINT64 value);
static void get_register_string_value(const device_config *device, void *baseptr, const cpu_state_entry *entry, char *dest);
#ifdef UNUSED_FUNCTION
static int get_register_string_max_width(const device_config *device, void *baseptr, const cpu_state_entry *entry);
#endif
/***************************************************************************
MACROS
***************************************************************************/
/* these are macros to ensure inlining in cpuexec_timeslice */
#define ATTOTIME_LT(a,b) ((a).seconds < (b).seconds || ((a).seconds == (b).seconds && (a).attoseconds < (b).attoseconds))
#define ATTOTIME_NORMALIZE(a) do { if ((a).attoseconds >= ATTOSECONDS_PER_SECOND) { (a).seconds++; (a).attoseconds -= ATTOSECONDS_PER_SECOND; } } while (0)
/***************************************************************************
INLINE FUNCTIONS
***************************************************************************/
/*-------------------------------------------------
get_class_data - return a pointer to the
class data
-------------------------------------------------*/
INLINE cpu_class_data *get_class_data(const device_config *device)
{
assert(device != NULL);
assert(device->devclass == DEVICE_CLASS_CPU_CHIP);
assert(device->token != NULL);
return (cpu_class_data *)cpu_get_class_header(device) - 1;
}
/*-------------------------------------------------
get_minimum_quantum - return the minimum
quantum required for a given CPU device
-------------------------------------------------*/
INLINE attoseconds_t get_minimum_quantum(const device_config *device)
{
attoseconds_t basetick = 0;
/* fetch the base clock from the classdata if present */
if (device->token != NULL)
basetick = get_class_data(device)->attoseconds_per_cycle;
/* otherwise compute it from the raw data */
if (basetick == 0)
{
UINT32 baseclock = (UINT64)device->clock * cpu_get_clock_multiplier(device) / cpu_get_clock_divider(device);
basetick = HZ_TO_ATTOSECONDS(baseclock);
}
/* apply the minimum cycle count */
return basetick * cpu_get_min_cycles(device);
}
/*-------------------------------------------------
suspend_until_trigger - suspend execution
until the given trigger fires
-------------------------------------------------*/
INLINE void suspend_until_trigger(const device_config *device, int trigger, int eatcycles)
{
cpu_class_data *classdata = get_class_data(device);
/* suspend the CPU immediately if it's not already */
cpu_suspend(device, SUSPEND_REASON_TRIGGER, eatcycles);
/* set the trigger */
classdata->trigger = trigger;
}
/***************************************************************************
CORE CPU EXECUTION
***************************************************************************/
/*-------------------------------------------------
cpuexec_init - initialize internal states of
all CPUs
-------------------------------------------------*/
void cpuexec_init(running_machine *machine)
{
attotime min_quantum;
/* allocate global state */
machine->cpuexec_data = auto_alloc_clear(machine, cpuexec_private);
/* set the core scheduling quantum */
min_quantum = machine->config->minimum_quantum;
if (attotime_compare(min_quantum, attotime_zero) == 0)
min_quantum = ATTOTIME_IN_HZ(60);
if (machine->config->perfect_cpu_quantum != NULL)
{
const device_config *cpu = cputag_get_cpu(machine, machine->config->perfect_cpu_quantum);
attotime cpu_quantum;
if (cpu == NULL)
fatalerror("CPU '%s' specified for perfect interleave is not present!", machine->config->perfect_cpu_quantum);
cpu_quantum = attotime_make(0, get_minimum_quantum(cpu));
min_quantum = attotime_min(cpu_quantum, min_quantum);
}
assert(min_quantum.seconds == 0);
timer_add_scheduling_quantum(machine, min_quantum.attoseconds, attotime_never);
}
/*-------------------------------------------------
cpuexec_timeslice - execute all CPUs for a
single timeslice
-------------------------------------------------*/
void cpuexec_timeslice(running_machine *machine)
{
int call_debugger = ((machine->debug_flags & DEBUG_FLAG_ENABLED) != 0);
timer_execution_state *timerexec = timer_get_execution_state(machine);
cpuexec_private *global = machine->cpuexec_data;
int ran;
/* build the execution list if we don't have one yet */
if (global->executelist == NULL)
rebuild_execute_list(machine);
/* loop until we hit the next timer */
while (ATTOTIME_LT(timerexec->basetime, timerexec->nextfire))
{
cpu_class_data *classdata;
UINT32 suspendchanged;
attotime target;
/* by default, assume our target is the end of the next quantum */
target.seconds = timerexec->basetime.seconds;
target.attoseconds = timerexec->basetime.attoseconds + timerexec->curquantum;
ATTOTIME_NORMALIZE(target);
/* however, if the next timer is going to fire before then, override */
assert(attotime_sub(timerexec->nextfire, target).seconds <= 0);
if (ATTOTIME_LT(timerexec->nextfire, target))
target = timerexec->nextfire;
LOG(("------------------\n"));
LOG(("cpu_timeslice: target = %s\n", attotime_string(target, 9)));
/* apply pending suspension changes */
suspendchanged = 0;
for (classdata = global->executelist; classdata != NULL; classdata = classdata->next)
{
suspendchanged |= (classdata->suspend ^ classdata->nextsuspend);
classdata->suspend = classdata->nextsuspend;
classdata->nextsuspend &= ~SUSPEND_REASON_TIMESLICE;
classdata->eatcycles = classdata->nexteatcycles;
}
/* recompute the execute list if any CPUs changed their suspension state */
if (suspendchanged != 0)
rebuild_execute_list(machine);
/* loop over non-suspended CPUs */
for (classdata = global->executelist; classdata != NULL; classdata = classdata->next)
{
/* only process if our target is later than the CPU's current time (coarse check) */
if (target.seconds >= classdata->localtime.seconds)
{
attoseconds_t delta, actualdelta;
/* compute how many attoseconds to execute this CPU */
delta = target.attoseconds - classdata->localtime.attoseconds;
if (delta < 0 && target.seconds > classdata->localtime.seconds)
delta += ATTOSECONDS_PER_SECOND;
assert(delta == attotime_to_attoseconds(attotime_sub(target, classdata->localtime)));
/* if we have enough for at least 1 cycle, do the math */
if (delta >= classdata->attoseconds_per_cycle)
{
/* compute how many cycles we want to execute */
ran = classdata->cycles_running = divu_64x32((UINT64)delta >> classdata->divshift, classdata->divisor);
LOG((" cpu '%s': %d cycles\n", classdata->device->tag, classdata->cycles_running));
/* if we're not suspended, actually execute */
if (classdata->suspend == 0)
{
profiler_mark_start(classdata->profiler);
/* note that this global variable cycles_stolen can be modified */
/* via the call to cpu_execute */
classdata->cycles_stolen = 0;
global->executingcpu = classdata->device;
*classdata->icount = classdata->cycles_running;
if (!call_debugger)
ran = (*classdata->execute)(classdata->device, classdata->cycles_running);
else
{
debugger_start_cpu_hook(classdata->device, target);
ran = (*classdata->execute)(classdata->device, classdata->cycles_running);
debugger_stop_cpu_hook(classdata->device);
}
/* adjust for any cycles we took back */
assert(ran >= classdata->cycles_stolen);
ran -= classdata->cycles_stolen;
profiler_mark_end();
}
/* account for these cycles */
classdata->totalcycles += ran;
/* update the local time for this CPU */
actualdelta = classdata->attoseconds_per_cycle * ran;
classdata->localtime.attoseconds += actualdelta;
ATTOTIME_NORMALIZE(classdata->localtime);
LOG((" %d ran, %d total, time = %s\n", ran, (INT32)classdata->totalcycles, attotime_string(classdata->localtime, 9)));
/* if the new local CPU time is less than our target, move the target up */
if (ATTOTIME_LT(classdata->localtime, target))
{
assert(attotime_compare(classdata->localtime, target) < 0);
target = classdata->localtime;
/* however, if this puts us before the base, clamp to the base as a minimum */
if (ATTOTIME_LT(target, timerexec->basetime))
{
assert(attotime_compare(target, timerexec->basetime) < 0);
target = timerexec->basetime;
}
LOG((" (new target)\n"));
}
}
}
}
global->executingcpu = NULL;
/* update the base time */
timerexec->basetime = target;
}
/* execute timers */
timer_execute_timers(machine);
}
/*-------------------------------------------------
cpuexec_boost_interleave - temporarily boosts
the interleave factor
-------------------------------------------------*/
void cpuexec_boost_interleave(running_machine *machine, attotime timeslice_time, attotime boost_duration)
{
/* ignore timeslices > 1 second */
if (timeslice_time.seconds > 0)
return;
timer_add_scheduling_quantum(machine, timeslice_time.attoseconds, boost_duration);
}
/***************************************************************************
GLOBAL HELPERS
***************************************************************************/
/*-------------------------------------------------
cpuexec_abort_timeslice - abort execution
for the current timeslice
-------------------------------------------------*/
void cpuexec_abort_timeslice(running_machine *machine)
{
const device_config *executingcpu = machine->cpuexec_data->executingcpu;
if (executingcpu != NULL)
cpu_abort_timeslice(executingcpu);
}
/*-------------------------------------------------
cpuexec_describe_context - return a string
describing which CPUs are currently executing
and their PC
-------------------------------------------------*/
const char *cpuexec_describe_context(running_machine *machine)
{
cpuexec_private *global = machine->cpuexec_data;
const device_config *executingcpu = global->executingcpu;
/* if we have an executing CPU, output data */
if (executingcpu != NULL)
{
const address_space *space = cpu_get_address_space(executingcpu, ADDRESS_SPACE_PROGRAM);
sprintf(global->statebuf, "'%s' (%s)", executingcpu->tag, core_i64_hex_format(cpu_get_pc(executingcpu), space->logaddrchars));
}
else
strcpy(global->statebuf, "(no context)");
return global->statebuf;
}
/***************************************************************************
CPU DEVICE INTERFACE
***************************************************************************/
/*-------------------------------------------------
device_start_cpu - device start callback
-------------------------------------------------*/
static DEVICE_START( cpu )
{
int index = cpu_get_index(device);
cpu_class_header *header;
cpu_class_data *classdata;
const cpu_config *config;
cpu_init_func init;
int num_regs;
int line;
/* validate some basic stuff */
assert(device != NULL);
assert(device->inline_config != NULL);
assert(device->machine != NULL);
assert(device->machine->config != NULL);
/* get pointers to our data */
config = (const cpu_config *)device->inline_config;
header = cpu_get_class_header(device);
classdata = get_class_data(device);
/* build the header */
header->debug = NULL;
header->set_info = (cpu_set_info_func)device_get_info_fct(device, CPUINFO_FCT_SET_INFO);
/* fill in the input states and IRQ callback information */
for (line = 0; line < ARRAY_LENGTH(classdata->input); line++)
{
cpu_input_data *inputline = &classdata->input[line];
/* vector and curvector are initialized later */
inputline->curstate = CLEAR_LINE;
inputline->qindex = 0;
}
/* fill in the suspend states */
classdata->device = device;
classdata->execute = (cpu_execute_func)device_get_info_fct(device, CPUINFO_FCT_EXECUTE);
classdata->profiler = index + PROFILER_CPU_FIRST;
classdata->suspend = SUSPEND_REASON_RESET;
classdata->inttrigger = index + TRIGGER_INT;
/* fill in the clock and timing information */
classdata->clock = (UINT64)device->clock * cpu_get_clock_multiplier(device) / cpu_get_clock_divider(device);
classdata->clockscale = 1.0;
/* allocate timers if we need them */
if (config->vblank_interrupts_per_frame > 1)
classdata->partial_frame_timer = timer_alloc(device->machine, trigger_partial_frame_interrupt, (void *)device);
if (config->timed_interrupt_period != 0)
classdata->timedint_timer = timer_alloc(device->machine, trigger_periodic_interrupt, (void *)device);
/* initialize this CPU */
num_regs = state_save_get_reg_count(device->machine);
init = (cpu_init_func)device_get_info_fct(device, CPUINFO_FCT_INIT);
(*init)(device, standard_irq_callback);
num_regs = state_save_get_reg_count(device->machine) - num_regs;
/* fetch post-initialization data */
classdata->icount = cpu_get_icount_ptr(device);
for (line = 0; line < ARRAY_LENGTH(classdata->input); line++)
{
cpu_input_data *inputline = &classdata->input[line];
inputline->vector = cpu_get_default_irq_vector(device);
inputline->curvector = inputline->vector;
}
update_clock_information(device);
/* fetch information about the CPU states */
classdata->state = cpu_get_state_table(device);
if (classdata->state != NULL)
{
int stateindex;
/* loop over all states specified, and work with any that apply */
for (stateindex = 0; stateindex < classdata->state->entrycount; stateindex++)
{
const cpu_state_entry *entry = &classdata->state->entrylist[stateindex];
if (entry->validmask == 0 || (entry->validmask & classdata->state->subtypemask) != 0)
{
assert(entry->index < MAX_REGS);
assert(classdata->regstate[entry->index] == NULL);
classdata->regstate[entry->index] = entry;
}
}
}
/* if no state registered for saving, we can't save */
if (num_regs == 0)
{
logerror("CPU '%s' did not register any state to save!\n", device->tag);
if (device->machine->gamedrv->flags & GAME_SUPPORTS_SAVE)
fatalerror("CPU '%s' did not register any state to save!", device->tag);
}
/* register some internal states as well */
register_save_states(device);
}
/*-------------------------------------------------
device_reset_cpu - device reset callback
-------------------------------------------------*/
static DEVICE_RESET( cpu )
{
cpu_class_data *classdata = get_class_data(device);
const cpu_config *config = (const cpu_config *)device->inline_config;
cpu_reset_func reset;
int line;
/* enable all CPUs (except for disabled CPUs) */
if (!(config->flags & CPU_DISABLE))
cpu_resume(device, SUSPEND_ANY_REASON);
else
cpu_suspend(device, SUSPEND_REASON_DISABLE, 1);
/* reset the total number of cycles */
classdata->totalcycles = 0;
/* then reset the CPU directly */
reset = (cpu_reset_func)device_get_info_fct(device, CPUINFO_FCT_RESET);
if (reset != NULL)
(*reset)(device);
/* reset the interrupt vectors and queues */
for (line = 0; line < ARRAY_LENGTH(classdata->input); line++)
{
cpu_input_data *inputline = &classdata->input[line];
inputline->vector = cpu_get_default_irq_vector(device);
inputline->qindex = 0;
}
/* reconfingure VBLANK interrupts */
if (config->vblank_interrupts_per_frame > 0 || config->vblank_interrupt_screen != NULL)
{
const device_config *screen;
/* get the screen that will trigger the VBLANK */
/* new style - use screen tag directly */
if (config->vblank_interrupt_screen != NULL)
screen = devtag_get_device(device->machine, config->vblank_interrupt_screen);
/* old style 'hack' setup - use screen #0 */
else
screen = video_screen_first(device->machine->config);
assert(screen != NULL);
video_screen_register_vblank_callback(screen, on_vblank, NULL);
}
/* reconfigure periodic interrupts */
if (config->timed_interrupt_period != 0)
{
attotime timedint_period = UINT64_ATTOTIME_TO_ATTOTIME(config->timed_interrupt_period);
assert(classdata->timedint_timer != NULL);
timer_adjust_periodic(classdata->timedint_timer, timedint_period, 0, timedint_period);
}
}
/*-------------------------------------------------
device_stop_cpu - device stop callback
-------------------------------------------------*/
static DEVICE_STOP( cpu )
{
cpu_exit_func exit;
/* call the CPU's exit function if present */
exit = (cpu_exit_func)device_get_info_fct(device, CPUINFO_FCT_EXIT);
if (exit != NULL)
(*exit)(device);
}
/*-------------------------------------------------
cpu_set_info - device set info callback
-------------------------------------------------*/
void cpu_set_info(const device_config *device, UINT32 state, UINT64 value)
{
cpu_class_header *header = cpu_get_class_header(device);
cpu_set_info_func set_info;
cpuinfo cinfo;
/* if we are live and have a header, save ourself a call */
if (header != NULL)
set_info = header->set_info;
else
set_info = (cpu_set_info_func)device_get_info_fct(device, CPUINFO_FCT_SET_INFO);
switch (state)
{
/* no parameters to set */
default:
/* if we have a state pointer, we can handle some stuff for free */
if (device->token != NULL)
{
const cpu_class_data *classdata = get_class_data(device);
if (classdata->state != NULL)
{
if (state >= CPUINFO_INT_REGISTER && state <= CPUINFO_INT_REGISTER_LAST)
{
set_register_value(device, classdata->state->baseptr, classdata->regstate[state - CPUINFO_INT_REGISTER], value);
return;
}
}
}
/* integer data */
assert(state >= DEVINFO_INT_FIRST && state <= DEVINFO_INT_LAST);
if (state >= DEVINFO_INT_FIRST && state <= DEVINFO_INT_LAST)
{
cinfo.i = value;
(*set_info)(device, state, &cinfo);
}
break;
}
}
/*-------------------------------------------------
cpu_get_info - device get info
callback
-------------------------------------------------*/
DEVICE_GET_INFO( cpu )
{
const cpu_config *config = (device != NULL) ? (const cpu_config *)device->inline_config : NULL;
cpuinfo cinfo = { 0 };
switch (state)
{
/* --- the following bits of info are returned as 64-bit signed integers --- */
case DEVINFO_INT_TOKEN_BYTES:
cinfo.i = 0;
(*config->type)(device, CPUINFO_INT_CONTEXT_SIZE, &cinfo);
info->i = cinfo.i + sizeof(cpu_class_data) + sizeof(cpu_class_header);
break;
case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = sizeof(cpu_config); break;
case DEVINFO_INT_CLASS: info->i = DEVICE_CLASS_CPU_CHIP; break;
/* --- the following bits of info are returned as pointers to data or functions --- */
case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(cpu); break;
case DEVINFO_FCT_STOP: info->stop = DEVICE_STOP_NAME(cpu); break;
case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME(cpu); break;
default:
/* if we don't have a device pointer, ignore everything else */
if (device == NULL)
break;
/* if we have a state pointer, we can handle some stuff for free */
if (device->token != NULL)
{
const cpu_class_data *classdata = get_class_data(device);
if (classdata->state != NULL)
{
if (state >= CPUINFO_INT_REGISTER && state <= CPUINFO_INT_REGISTER_LAST)
{
info->i = get_register_value(device, classdata->state->baseptr, classdata->regstate[state - CPUINFO_INT_REGISTER]);
return;
}
else if (state >= CPUINFO_STR_REGISTER && state <= CPUINFO_STR_REGISTER_LAST)
{
cinfo.s = info->s;
get_register_string_value(device, classdata->state->baseptr, classdata->regstate[state - CPUINFO_STR_REGISTER], cinfo.s);
info->s = cinfo.s;
return;
}
}
}
/* integer data */
if (state >= DEVINFO_INT_FIRST && state <= DEVINFO_INT_LAST)
{
cinfo.i = info->i;
(*config->type)(device, state, &cinfo);
info->i = cinfo.i;
}
/* pointer data */
else if ((state >= DEVINFO_PTR_FIRST && state <= DEVINFO_PTR_LAST) ||
(state >= DEVINFO_FCT_FIRST && state <= DEVINFO_FCT_LAST) ||
(state >= DEVINFO_STR_FIRST && state <= DEVINFO_STR_LAST))
{
cinfo.p = info->p;
(*config->type)(device, state, &cinfo);
info->p = cinfo.p;
}
break;
}
}
/***************************************************************************
CPU SCHEDULING
***************************************************************************/
/*-------------------------------------------------
cpu_suspend - set a suspend reason for the
given CPU
-------------------------------------------------*/
void cpu_suspend(const device_config *device, int reason, int eatcycles)
{
cpu_class_data *classdata = get_class_data(device);
/* set the suspend reason and eat cycles flag */
classdata->nextsuspend |= reason;
classdata->nexteatcycles = eatcycles;
/* if we're active, synchronize */
cpu_abort_timeslice(device);
}
/*-------------------------------------------------
cpu_resume - clear a suspend reason for the
given CPU
-------------------------------------------------*/
void cpu_resume(const device_config *device, int reason)
{
cpu_class_data *classdata = get_class_data(device);
/* clear the suspend reason and eat cycles flag */
classdata->nextsuspend &= ~reason;
/* if we're active, synchronize */
cpu_abort_timeslice(device);
}
/*-------------------------------------------------
cpu_is_executing - return TRUE if the given
CPU is within its execute function
-------------------------------------------------*/
int cpu_is_executing(const device_config *device)
{
return (device == device->machine->cpuexec_data->executingcpu);
}
/*-------------------------------------------------
cpu_is_suspended - returns TRUE if the
given CPU is suspended for any of the given
reasons
-------------------------------------------------*/
int cpu_is_suspended(const device_config *device, int reason)
{
cpu_class_data *classdata = get_class_data(device);
/* return true if the given reason is indicated */
return ((classdata->nextsuspend & reason) != 0);
}
/***************************************************************************
CPU CLOCK MANAGEMENT
***************************************************************************/
/*-------------------------------------------------
cpu_get_clock - gets the given CPU's
clock speed
-------------------------------------------------*/
int cpu_get_clock(const device_config *device)
{
cpu_class_data *classdata;
/* if we haven't been started yet, compute it manually */
if (device->token == NULL)
return (UINT64)device->clock * cpu_get_clock_multiplier(device) / cpu_get_clock_divider(device);
/* return the current clock value */
classdata = get_class_data(device);
return classdata->clock;
}
/*-------------------------------------------------
cpu_set_clock - sets the given CPU's
clock speed
-------------------------------------------------*/
void cpu_set_clock(const device_config *device, int clock)
{
cpu_class_data *classdata = get_class_data(device);
/* set the clock and update the information */
classdata->clock = clock;
update_clock_information(device);
}
/*-------------------------------------------------
cpu_get_clockscale - returns the current
scaling factor for a CPU's clock speed
-------------------------------------------------*/
double cpu_get_clockscale(const device_config *device)
{
cpu_class_data *classdata = get_class_data(device);
/* return the current clock scale factor */
return classdata->clockscale;
}
/*-------------------------------------------------
cpu_set_clockscale - sets the current
scaling factor for a CPU's clock speed
-------------------------------------------------*/
void cpu_set_clockscale(const device_config *device, double clockscale)
{
cpu_class_data *classdata = get_class_data(device);
/* set the scale factor and update the information */
classdata->clockscale = clockscale;
update_clock_information(device);
}
/*-------------------------------------------------
cpu_clocks_to_attotime - converts a number of
clock ticks to an attotime
-------------------------------------------------*/
attotime cpu_clocks_to_attotime(const device_config *device, UINT64 clocks)
{
cpu_class_data *classdata = get_class_data(device);
if (clocks < classdata->cycles_per_second)
return attotime_make(0, clocks * classdata->attoseconds_per_cycle);
else
{
UINT32 remainder;
UINT32 quotient = divu_64x32_rem(clocks, classdata->cycles_per_second, &remainder);
return attotime_make(quotient, (UINT64)remainder * (UINT64)classdata->attoseconds_per_cycle);
}
}
/*-------------------------------------------------
cpu_attotime_to_clocks - converts a duration
as attotime to CPU clock ticks
-------------------------------------------------*/
UINT64 cpu_attotime_to_clocks(const device_config *device, attotime duration)
{
cpu_class_data *classdata = get_class_data(device);
return mulu_32x32(duration.seconds, classdata->cycles_per_second) + (UINT64)duration.attoseconds / (UINT64)classdata->attoseconds_per_cycle;
}
/***************************************************************************
CPU TIMING
***************************************************************************/
/*-------------------------------------------------
cpu_get_local_time - returns the current
local time for a CPU
-------------------------------------------------*/
attotime cpu_get_local_time(const device_config *device)
{
cpu_class_data *classdata = get_class_data(device);
attotime result;
/* if we're active, add in the time from the current slice */
result = classdata->localtime;
if (device == device->machine->cpuexec_data->executingcpu)
{
int cycles = classdata->cycles_running - *classdata->icount;
result = attotime_add(result, cpu_clocks_to_attotime(device, cycles));
}
return result;
}
/*-------------------------------------------------
cpuexec_override_local_time - overrides the
given time with the executing CPU's local
time, if present (this function is private
to timer.c)
-------------------------------------------------*/
attotime cpuexec_override_local_time(running_machine *machine, attotime default_time)
{
if (machine->cpuexec_data != NULL && machine->cpuexec_data->executingcpu != NULL)
return cpu_get_local_time(machine->cpuexec_data->executingcpu);
return default_time;
}
/*-------------------------------------------------
cpu_get_total_cycles - return the total
number of CPU cycles executed on the active
CPU
-------------------------------------------------*/
UINT64 cpu_get_total_cycles(const device_config *device)
{
cpu_class_data *classdata = get_class_data(device);
if (device == device->machine->cpuexec_data->executingcpu)
return classdata->totalcycles + classdata->cycles_running - *classdata->icount;
else
return classdata->totalcycles;
}
/*-------------------------------------------------
cpu_eat_cycles - safely eats cycles so
we don't cross a timeslice boundary
-------------------------------------------------*/
void cpu_eat_cycles(const device_config *device, int cycles)
{
cpu_class_data *classdata = get_class_data(device);
/* ignore if not the executing CPU */
if (device != device->machine->cpuexec_data->executingcpu)
return;
if (cycles > *classdata->icount)
cycles = *classdata->icount;
*classdata->icount -= cycles;
}
/*-------------------------------------------------
cpu_adjust_icount - apply a +/- to the current
icount
-------------------------------------------------*/
void cpu_adjust_icount(const device_config *device, int delta)
{
cpu_class_data *classdata = get_class_data(device);
/* ignore if not the executing CPU */
if (device != device->machine->cpuexec_data->executingcpu)
return;
*classdata->icount += delta;
}
/*-------------------------------------------------
cpu_abort_timeslice - abort execution
for the current timeslice, allowing other
CPUs to run before we run again
-------------------------------------------------*/
void cpu_abort_timeslice(const device_config *device)
{
cpu_class_data *classdata = get_class_data(device);
int delta;
/* ignore if not the executing CPU */
if (device != device->machine->cpuexec_data->executingcpu)
return;
/* swallow the remaining cycles */
if (classdata->icount != NULL)
{
delta = *classdata->icount;
classdata->cycles_stolen += delta;
classdata->cycles_running -= delta;
*classdata->icount -= delta;
}
}
/***************************************************************************
SYNCHRONIZATION HELPERS
***************************************************************************/
/*-------------------------------------------------
cpu_yield - yield the given CPU until the end
of the current timeslice
-------------------------------------------------*/
void cpu_yield(const device_config *device)
{
/* suspend against the timeslice */
cpu_suspend(device, SUSPEND_REASON_TIMESLICE, FALSE);
}
/*-------------------------------------------------
cpu_spin - burn CPU cycles until our timeslice
is up
-------------------------------------------------*/
void cpu_spin(const device_config *device)
{
/* suspend against the timeslice */
cpu_suspend(device, SUSPEND_REASON_TIMESLICE, TRUE);
}
/*-------------------------------------------------
cpu_spinuntil_trigger - burn specified CPU
cycles until a timer trigger
-------------------------------------------------*/
void cpu_spinuntil_trigger(const device_config *device, int trigger)
{
/* suspend until the given trigger fires */
suspend_until_trigger(device, trigger, TRUE);
}
/*-------------------------------------------------
cpu_spinuntil_int - burn CPU cycles until the
next interrupt
-------------------------------------------------*/
void cpu_spinuntil_int(const device_config *device)
{
cpu_class_data *classdata = get_class_data(device);
/* suspend until the given trigger fires */
suspend_until_trigger(device, classdata->inttrigger, TRUE);
}
/*-------------------------------------------------
cpu_spinuntil_time - burn CPU cycles for a
specific period of time
-------------------------------------------------*/
void cpu_spinuntil_time(const device_config *device, attotime duration)
{
static int timetrig = 0;
/* suspend until the given trigger fires */
suspend_until_trigger(device, TRIGGER_SUSPENDTIME + timetrig, TRUE);
/* then set a timer for it */
cpuexec_triggertime(device->machine, TRIGGER_SUSPENDTIME + timetrig, duration);
timetrig = (timetrig + 1) % 256;
}
/***************************************************************************
TRIGGERS
***************************************************************************/
/*-------------------------------------------------
cpuexec_trigger - generate a trigger now
-------------------------------------------------*/
void cpuexec_trigger(running_machine *machine, int trigger)
{
const device_config *cpu;
/* look for suspended CPUs waiting for this trigger and unsuspend them */
for (cpu = machine->firstcpu; cpu != NULL; cpu = cpu_next(cpu))
{
cpu_class_data *classdata = get_class_data(cpu);
/* if we're executing, for an immediate abort */
cpu_abort_timeslice(cpu);
/* see if this is a matching trigger */
if ((classdata->nextsuspend & SUSPEND_REASON_TRIGGER) != 0 && classdata->trigger == trigger)
{
cpu_resume(cpu, SUSPEND_REASON_TRIGGER);
classdata->trigger = 0;
}
}
}
/*-------------------------------------------------
cpuexec_triggertime - generate a trigger after a
specific period of time
-------------------------------------------------*/
void cpuexec_triggertime(running_machine *machine, int trigger, attotime duration)
{
timer_set(machine, duration, NULL, trigger, triggertime_callback);
}
/*-------------------------------------------------
cpu_triggerint - generate a trigger
corresponding to an interrupt on the given CPU
-------------------------------------------------*/
void cpu_triggerint(const device_config *device)
{
cpu_class_data *classdata = get_class_data(device);
/* signal this CPU's interrupt trigger */
cpuexec_trigger(device->machine, classdata->inttrigger);
}
/***************************************************************************
INTERRUPTS
***************************************************************************/
/*-------------------------------------------------
cpu_set_input_line - set the logical state
(ASSERT_LINE/CLEAR_LINE) of an input line
on a CPU
-------------------------------------------------*/
void cpu_set_input_line(const device_config *device, int line, int state)
{
cpu_class_data *classdata = get_class_data(device);
int vector = (line >= 0 && line < MAX_INPUT_LINES) ? classdata->input[line].vector : 0xff;
cpu_set_input_line_and_vector(device, line, state, vector);
}
/*-------------------------------------------------
cpu_set_input_line_vector - set the vector to
be returned during a CPU's interrupt
acknowledge cycle
-------------------------------------------------*/
void cpu_set_input_line_vector(const device_config *device, int line, int vector)
{
cpu_class_data *classdata = get_class_data(device);
if (line >= 0 && line < MAX_INPUT_LINES)
{
classdata->input[line].vector = vector;
return;
}
LOG(("cpu_set_input_line_vector CPU '%s' line %d > max input lines\n", device->tag, line));
}
/*-------------------------------------------------
cpu_set_input_line_and_vector - set the logical
state (ASSERT_LINE/CLEAR_LINE) of an input
line on a CPU and its associated vector
-------------------------------------------------*/
void cpu_set_input_line_and_vector(const device_config *device, int line, int state, int vector)
{
cpu_class_data *classdata = get_class_data(device);
/* catch errors where people use PULSE_LINE for CPUs that don't support it */
if (state == PULSE_LINE && line != INPUT_LINE_NMI && line != INPUT_LINE_RESET)
fatalerror("CPU %s: PULSE_LINE can only be used for NMI and RESET lines\n", device->tag);
if (line >= 0 && line < MAX_INPUT_LINES)
{
cpu_input_data *inputline = &classdata->input[line];
INT32 input_event = (state & 0xff) | (vector << 8);
int event_index = inputline->qindex++;
LOG(("cpu_set_input_line_and_vector('%s',%d,%d,%02x)\n", device->tag, line, state, vector));
/* if we're full of events, flush the queue and log a message */
if (event_index >= ARRAY_LENGTH(inputline->queue))
{
inputline->qindex--;
empty_event_queue(device->machine, (void *)device, line);
event_index = inputline->qindex++;
logerror("Exceeded pending input line event queue on CPU '%s'!\n", device->tag);
}
/* enqueue the event */
if (event_index < ARRAY_LENGTH(inputline->queue))
{
inputline->queue[event_index] = input_event;
/* if this is the first one, set the timer */
if (event_index == 0)
timer_call_after_resynch(device->machine, (void *)device, line, empty_event_queue);
}
}
}
/*-------------------------------------------------
cpu_set_irq_callback - install a driver-
specific callback for IRQ acknowledge
-------------------------------------------------*/
void cpu_set_irq_callback(const device_config *device, cpu_irq_callback callback)
{
cpu_class_data *classdata = get_class_data(device);
classdata->driver_irq = callback;
}
/***************************************************************************
CHEESY FAKE VIDEO TIMING (OBSOLETE)
***************************************************************************/
/*-------------------------------------------------
cpu_getiloops - return the cheesy VBLANK
interrupt counter (deprecated)
-------------------------------------------------*/
int cpu_getiloops(const device_config *device)
{
cpu_class_data *classdata = get_class_data(device);
return classdata->iloops;
}
/***************************************************************************
INTERNAL FUNCTIONS
***************************************************************************/
/*-------------------------------------------------
update_clock_information - recomputes clock
information for the specified CPU
-------------------------------------------------*/
static void update_clock_information(const device_config *device)
{
cpu_class_data *classdata = get_class_data(device);
INT64 attos;
/* recompute cps and spc */
classdata->cycles_per_second = (double)classdata->clock * classdata->clockscale;
classdata->attoseconds_per_cycle = HZ_TO_ATTOSECONDS((double)classdata->clock * classdata->clockscale);
/* update the CPU's divisor */
attos = classdata->attoseconds_per_cycle;
classdata->divshift = 0;
while (attos >= (1UL << 31))
{
classdata->divshift++;
attos >>= 1;
}
classdata->divisor = attos;
/* re-compute the perfect interleave factor */
compute_perfect_interleave(device->machine);
}
/*-------------------------------------------------
compute_perfect_interleave - compute the
"perfect" interleave interval
-------------------------------------------------*/
static void compute_perfect_interleave(running_machine *machine)
{
const device_config *firstcpu = machine->firstcpu;
if (firstcpu != NULL)
{
attoseconds_t smallest = get_minimum_quantum(firstcpu);
attoseconds_t perfect = ATTOSECONDS_PER_SECOND - 1;
const device_config *cpu;
/* start with a huge time factor and find the 2nd smallest cycle time */
for (cpu = cpu_next(firstcpu); cpu != NULL; cpu = cpu_next(cpu))
{
attoseconds_t curquantum = get_minimum_quantum(cpu);
/* find the 2nd smallest cycle interval */
if (curquantum < smallest)
{
perfect = smallest;
smallest = curquantum;
}
else if (curquantum < perfect)
perfect = curquantum;
}
/* adjust the final value */
timer_set_minimum_quantum(machine, perfect);
LOG(("Perfect interleave = %.9f, smallest = %.9f\n", ATTOSECONDS_TO_DOUBLE(perfect), ATTOSECONDS_TO_DOUBLE(smallest)));
}
}
/*-------------------------------------------------
on_vblank - calls any external callbacks
for this screen
-------------------------------------------------*/
static void on_vblank(const device_config *device, void *param, int vblank_state)
{
/* VBLANK starting */
if (vblank_state)
{
const device_config *cpu;
/* find any CPUs that have this screen as their VBLANK interrupt source */
for (cpu = device->machine->firstcpu; cpu != NULL; cpu = cpu_next(cpu))
{
const cpu_config *config = (const cpu_config *)cpu->inline_config;
cpu_class_data *classdata = get_class_data(cpu);
int cpu_interested;
/* start the interrupt counter */
if (!(classdata->suspend & SUSPEND_REASON_DISABLE))
classdata->iloops = 0;
else
classdata->iloops = -1;
/* the hack style VBLANK decleration always uses the first screen */
if (config->vblank_interrupts_per_frame > 1)
cpu_interested = TRUE;
/* for new style declaration, we need to compare the tags */
else if (config->vblank_interrupt_screen != NULL)
cpu_interested = (strcmp(config->vblank_interrupt_screen, device->tag) == 0);
/* no VBLANK interrupt, not interested */
else
cpu_interested = FALSE;
/* if interested, call the interrupt handler */
if (cpu_interested)
{
if (!(classdata->suspend & (SUSPEND_REASON_HALT | SUSPEND_REASON_RESET | SUSPEND_REASON_DISABLE)))
(*config->vblank_interrupt)(cpu);
/* if we have more than one interrupt per frame, start the timer now to trigger the rest of them */
if (config->vblank_interrupts_per_frame > 1 && !(classdata->suspend & SUSPEND_REASON_DISABLE))
{
classdata->partial_frame_period = attotime_div(video_screen_get_frame_period(device->machine->primary_screen), config->vblank_interrupts_per_frame);
timer_adjust_oneshot(classdata->partial_frame_timer, classdata->partial_frame_period, 0);
}
}
}
}
}
/*-------------------------------------------------
trigger_partial_frame_interrupt - called to
trigger a partial frame interrupt
-------------------------------------------------*/
static TIMER_CALLBACK( trigger_partial_frame_interrupt )
{
const device_config *device = (const device_config *)ptr;
const cpu_config *config = (const cpu_config *)device->inline_config;
cpu_class_data *classdata = get_class_data(device);
if (classdata->iloops == 0)
classdata->iloops = config->vblank_interrupts_per_frame;
classdata->iloops--;
/* call the interrupt handler */
if (!cpu_is_suspended(device, SUSPEND_REASON_HALT | SUSPEND_REASON_RESET | SUSPEND_REASON_DISABLE))
(*config->vblank_interrupt)(device);
/* more? */
if (classdata->iloops > 1)
timer_adjust_oneshot(classdata->partial_frame_timer, classdata->partial_frame_period, 0);
}
/*-------------------------------------------------
trigger_periodic_interrupt - timer callback for
timed interrupts
-------------------------------------------------*/
static TIMER_CALLBACK( trigger_periodic_interrupt )
{
const device_config *device = (const device_config *)ptr;
const cpu_config *config = (const cpu_config *)device->inline_config;
/* bail if there is no routine */
if (config->timed_interrupt != NULL && !cpu_is_suspended(device, SUSPEND_REASON_HALT | SUSPEND_REASON_RESET | SUSPEND_REASON_DISABLE))
(*config->timed_interrupt)(device);
}
/*-------------------------------------------------
triggertime_callback - signal a global trigger
-------------------------------------------------*/
static TIMER_CALLBACK( triggertime_callback )
{
cpuexec_trigger(machine, param);
}
/*-------------------------------------------------
empty_event_queue - empty a CPU's event queue
for a specific input line
-------------------------------------------------*/
static TIMER_CALLBACK( empty_event_queue )
{
const device_config *device = (const device_config *)ptr;
cpu_class_data *classdata = get_class_data(device);
cpu_input_data *inputline = &classdata->input[param];
int curevent;
/* loop over all events */
for (curevent = 0; curevent < inputline->qindex; curevent++)
{
INT32 input_event = inputline->queue[curevent];
int state = input_event & 0xff;
int vector = input_event >> 8;
/* set the input line state and vector */
inputline->curstate = state;
inputline->curvector = vector;
/* special case: RESET */
if (param == INPUT_LINE_RESET)
{
/* if we're asserting the line, just halt the CPU */
if (state == ASSERT_LINE)
cpu_suspend(device, SUSPEND_REASON_RESET, 1);
else
{
/* if we're clearing the line that was previously asserted, or if we're just */
/* pulsing the line, reset the CPU */
if ((state == CLEAR_LINE && cpu_is_suspended(device, SUSPEND_REASON_RESET)) || state == PULSE_LINE)
device_reset(device);
/* if we're clearing the line, make sure the CPU is not halted */
cpu_resume(device, SUSPEND_REASON_RESET);
}
}
/* special case: HALT */
else if (param == INPUT_LINE_HALT)
{
/* if asserting, halt the CPU */
if (state == ASSERT_LINE)
cpu_suspend(device, SUSPEND_REASON_HALT, 1);
/* if clearing, unhalt the CPU */
else if (state == CLEAR_LINE)
cpu_resume(device, SUSPEND_REASON_HALT);
}
/* all other cases */
else
{
/* switch off the requested state */
switch (state)
{
case PULSE_LINE:
cpu_set_info(device, CPUINFO_INT_INPUT_STATE + param, ASSERT_LINE);
cpu_set_info(device, CPUINFO_INT_INPUT_STATE + param, CLEAR_LINE);
break;
case HOLD_LINE:
case ASSERT_LINE:
cpu_set_info(device, CPUINFO_INT_INPUT_STATE + param, ASSERT_LINE);
break;
case CLEAR_LINE:
cpu_set_info(device, CPUINFO_INT_INPUT_STATE + param, CLEAR_LINE);
break;
default:
logerror("empty_event_queue cpu '%s', line %d, unknown state %d\n", device->tag, param, state);
break;
}
/* generate a trigger to unsuspend any CPUs waiting on the interrupt */
if (state != CLEAR_LINE)
cpu_triggerint(device);
}
}
/* reset counter */
inputline->qindex = 0;
}
/*-------------------------------------------------
standard_irq_callback - IRQ acknowledge
callback; handles HOLD_LINE case and signals
to the debugger
-------------------------------------------------*/
static IRQ_CALLBACK( standard_irq_callback )
{
cpu_class_data *classdata = get_class_data(device);
cpu_input_data *inputline = &classdata->input[irqline];
int vector = inputline->curvector;
LOG(("standard_irq_callback('%s', %d) $%04x\n", device->tag, irqline, vector));
/* if the IRQ state is HOLD_LINE, clear it */
if (inputline->curstate == HOLD_LINE)
{
LOG(("->set_irq_line('%s',%d,%d)\n", device->tag, irqline, CLEAR_LINE));
cpu_set_info(device, CPUINFO_INT_INPUT_STATE + irqline, CLEAR_LINE);
inputline->curstate = CLEAR_LINE;
}
/* if there's a driver callback, run it */
if (classdata->driver_irq != NULL)
vector = (*classdata->driver_irq)(device, irqline);
/* notify the debugger */
debugger_interrupt_hook(device, irqline);
/* otherwise, just return the current vector */
return vector;
}
/*-------------------------------------------------
register_save_states - register for CPU-
specific save states
-------------------------------------------------*/
static void register_save_states(const device_config *device)
{
cpu_class_data *classdata = get_class_data(device);
int line;
state_save_register_device_item(device, 0, classdata->suspend);
state_save_register_device_item(device, 0, classdata->nextsuspend);
state_save_register_device_item(device, 0, classdata->eatcycles);
state_save_register_device_item(device, 0, classdata->nexteatcycles);
state_save_register_device_item(device, 0, classdata->trigger);
state_save_register_device_item(device, 0, classdata->iloops);
state_save_register_device_item(device, 0, classdata->totalcycles);
state_save_register_device_item(device, 0, classdata->localtime.seconds);
state_save_register_device_item(device, 0, classdata->localtime.attoseconds);
state_save_register_device_item(device, 0, classdata->clock);
state_save_register_device_item(device, 0, classdata->clockscale);
for (line = 0; line < ARRAY_LENGTH(classdata->input); line++)
{
cpu_input_data *inputline = &classdata->input[line];
state_save_register_device_item(device, line, inputline->vector);
state_save_register_device_item(device, line, inputline->curvector);
state_save_register_device_item(device, line, inputline->curstate);
}
}
/*-------------------------------------------------
rebuild_execute_list - rebuild the list of
executing CPUs, moving suspended CPUs to the
end
-------------------------------------------------*/
static void rebuild_execute_list(running_machine *machine)
{
cpuexec_private *global = machine->cpuexec_data;
const device_config *curcpu;
cpu_class_data **tailptr;
/* start with an empty list */
tailptr = &global->executelist;
*tailptr = NULL;
/* first iterate over non-suspended CPUs */
for (curcpu = machine->firstcpu; curcpu != NULL; curcpu = cpu_next(curcpu))
{
cpu_class_data *classdata = get_class_data(curcpu);
if (classdata->suspend == 0)
{
*tailptr = classdata;
tailptr = &classdata->next;
classdata->next = NULL;
}
}
/* then add the suspended CPUs */
for (curcpu = machine->firstcpu; curcpu != NULL; curcpu = cpu_next(curcpu))
{
cpu_class_data *classdata = get_class_data(curcpu);
if (classdata->suspend != 0)
{
*tailptr = classdata;
tailptr = &classdata->next;
classdata->next = NULL;
}
}
}
/*-------------------------------------------------
get_register_value - return a register value
of a CPU using the state table
-------------------------------------------------*/
static UINT64 get_register_value(const device_config *device, void *baseptr, const cpu_state_entry *entry)
{
void *dataptr;
UINT64 result;
/* NULL entry returns 0 */
if (entry == NULL || baseptr == NULL)
return 0;
/* if we have an exporter, call it now */
if ((entry->flags & CPUSTATE_EXPORT) != 0)
{
cpu_state_io_func exportcb = (cpu_state_io_func)device_get_info_fct(device, CPUINFO_FCT_EXPORT_STATE);
assert(exportcb != NULL);
(*exportcb)(device, baseptr, entry);
}
/* pick up the value */
dataptr = (UINT8 *)baseptr + entry->dataoffs;
switch (entry->datasize)
{
default:
case 1: result = *(UINT8 *)dataptr; break;
case 2: result = *(UINT16 *)dataptr; break;
case 4: result = *(UINT32 *)dataptr; break;
case 8: result = *(UINT64 *)dataptr; break;
}
return result & entry->mask;
}
/*-------------------------------------------------
set_register_value - set the value of a
CPU register using the state table
-------------------------------------------------*/
static void set_register_value(const device_config *device, void *baseptr, const cpu_state_entry *entry, UINT64 value)
{
void *dataptr;
/* NULL entry is a no-op */
if (entry == NULL || baseptr == NULL)
return;
/* apply the mask */
value &= entry->mask;
/* sign-extend if necessary */
if ((entry->flags & CPUSTATE_IMPORT_SEXT) != 0 && value > (entry->mask >> 1))
value |= ~entry->mask;
/* store the value */
dataptr = (UINT8 *)baseptr + entry->dataoffs;
switch (entry->datasize)
{
default:
case 1: *(UINT8 *)dataptr = value; break;
case 2: *(UINT16 *)dataptr = value; break;
case 4: *(UINT32 *)dataptr = value; break;
case 8: *(UINT64 *)dataptr = value; break;
}
/* if we have an importer, call it now */
if ((entry->flags & CPUSTATE_IMPORT) != 0)
{
cpu_state_io_func importcb = (cpu_state_io_func)device_get_info_fct(device, CPUINFO_FCT_IMPORT_STATE);
assert(importcb != NULL);
(*importcb)(device, baseptr, entry);
}
}
/*-------------------------------------------------
get_register_string_value - return a string
representation of a CPU register using the
state table
-------------------------------------------------*/
static void get_register_string_value(const device_config *device, void *baseptr, const cpu_state_entry *entry, char *dest)
{
static const UINT64 decdivisor[] = {
1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000,
U64(10000000000), U64(100000000000), U64(1000000000000),
U64(10000000000000), U64(100000000000000), U64(1000000000000000),
U64(10000000000000000), U64(100000000000000000), U64(1000000000000000000),
U64(10000000000000000000)
};
static const char hexchars[] = "0123456789ABCDEF";
int leadzero = 0, width = 0, percent = 0, explicitsign = 0, hitnonzero = 0, reset;
const char *fptr;
UINT64 result;
/* NULL entry does nothing */
if (entry == NULL || entry->symbol == NULL || entry->format == NULL)
return;
/* fetch the data */
result = get_register_value(device, baseptr, entry);
/* start with the basics */
dest += sprintf(dest, "%s%s:", (entry->flags & CPUSTATE_NOSHOW) ? "~" : "", entry->symbol);
/* parse the format */
reset = TRUE;
for (fptr = entry->format; *fptr != 0; fptr++)
{
int digitnum;
/* reset any accumulated state */
if (reset)
leadzero = width = percent = explicitsign = reset = 0;
/* if we're not within a format, then anything other than a % outputs directly */
if (!percent && *fptr != '%')
{
*dest++ = *fptr;
continue;
}
/* handle each character in turn */
switch (*fptr)
{
/* % starts a format; %% outputs a single % */
case '%':
if (!percent)
percent = TRUE;
else
{
*dest++ = *fptr;
percent = FALSE;
}
break;
/* 0 means insert leading 0s, unless it follows another width digit */
case '0':
if (width == 0)
leadzero = TRUE;
else
width *= 10;
break;
/* 1-9 accumulate into the width */
case '1': case '2': case '3': case '4': case '5':
case '6': case '7': case '8': case '9':
width = width * 10 + (*fptr - '0');
break;
/* + means explicit sign */
case '+':
explicitsign = TRUE;
break;
/* X outputs as hexadecimal */
case 'X':
if (width == 0)
fatalerror("Width required for %%X formats\n");
hitnonzero = FALSE;
while (leadzero && width > 16)
{
*dest++ = ' ';
width--;
}
for (digitnum = 15; digitnum >= 0; digitnum--)
{
int digit = (result >> (4 * digitnum)) & 0x0f;
if (digit != 0)
*dest++ = hexchars[digit];
else if (hitnonzero || (leadzero && digitnum < width) || digitnum == 0)
*dest++ = '0';
hitnonzero |= digit;
}
reset = TRUE;
break;
/* d outputs as signed decimal */
case 'd':
if (width == 0)
fatalerror("Width required for %%d formats\n");
if ((result & entry->mask) > (entry->mask >> 1))
{
result = -result & entry->mask;
*dest++ = '-';
width--;
}
else if (explicitsign)
{
*dest++ = '+';
width--;
}
/* fall through to unsigned case */
/* u outputs as unsigned decimal */
case 'u':
if (width == 0)
fatalerror("Width required for %%u formats\n");
hitnonzero = FALSE;
while (leadzero && width > ARRAY_LENGTH(decdivisor))
{
*dest++ = ' ';
width--;
}
for (digitnum = ARRAY_LENGTH(decdivisor); digitnum >= 0; digitnum--)
{
int digit = (result >= decdivisor[digitnum]) ? (result / decdivisor[digitnum]) % 10 : 0;
if (digit != 0)
*dest++ = '0' + digit;
else if (hitnonzero || (leadzero && digitnum < width) || digitnum == 0)
*dest++ = '0';
hitnonzero |= digit;
}
reset = TRUE;
break;
/* s is a custom format */
case 's':
{
cpu_string_io_func exportstring = (cpu_string_io_func)device_get_info_fct(device, CPUINFO_FCT_EXPORT_STRING);
assert(exportstring != NULL);
(*exportstring)(device, baseptr, entry, dest);
dest += strlen(dest);
break;
}
/* other formats unknown */
default:
fatalerror("Unknown format character '%c'\n", *fptr);
break;
}
}
*dest = 0;
}
/*-------------------------------------------------
get_register_string_max_width - return the
maximum width of a string described by a
format
-------------------------------------------------*/
#ifdef UNUSED_FUNCTION
static int get_register_string_max_width(const device_config *device, void *baseptr, const cpu_state_entry *entry)
{
int leadzero = 0, width = 0, percent = 0, explicitsign = 0, reset;
int totalwidth = 0;
const char *fptr;
/* NULL entry does nothing */
if (entry == NULL || entry->symbol == NULL || entry->format == NULL)
return 0;
/* parse the format */
reset = TRUE;
for (fptr = entry->format; *fptr != 0; fptr++)
{
/* reset any accumulated state */
if (reset)
leadzero = width = percent = explicitsign = reset = 0;
/* if we're not within a format, then anything other than a % outputs directly */
if (!percent && *fptr != '%')
{
totalwidth++;
continue;
}
/* handle each character in turn */
switch (*fptr)
{
/* % starts a format; %% outputs a single % */
case '%':
if (!percent)
percent = TRUE;
else
{
totalwidth++;
percent = FALSE;
}
break;
/* 0 means insert leading 0s, unless it follows another width digit */
case '0':
if (width == 0)
leadzero = TRUE;
else
width *= 10;
break;
/* 1-9 accumulate into the width */
case '1': case '2': case '3': case '4': case '5':
case '6': case '7': case '8': case '9':
width = width * 10 + (*fptr - '0');
break;
/* + means explicit sign */
case '+':
explicitsign = TRUE;
break;
/* X outputs as hexadecimal */
/* d outputs as signed decimal */
/* u outputs as unsigned decimal */
/* s outputs as custom format */
case 'X':
case 'd':
case 'u':
case 's':
totalwidth += width;
reset = TRUE;
break;
if (width == 0)
fatalerror("Width required for %%d formats\n");
totalwidth += width;
break;
/* other formats unknown */
default:
fatalerror("Unknown format character '%c'\n", *fptr);
break;
}
}
return totalwidth;
}
#endif