summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/cpuexec.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/cpuexec.c')
-rw-r--r--src/emu/cpuexec.c1261
1 files changed, 849 insertions, 412 deletions
diff --git a/src/emu/cpuexec.c b/src/emu/cpuexec.c
index 47239af2a87..0d69eee1c75 100644
--- a/src/emu/cpuexec.c
+++ b/src/emu/cpuexec.c
@@ -14,8 +14,6 @@
#include "eminline.h"
#include "debugger.h"
-extern const cpu_irq_callback cpu_irq_callbacks[];
-
@@ -30,23 +28,6 @@ extern const cpu_irq_callback cpu_irq_callbacks[];
/***************************************************************************
- MACROS
-***************************************************************************/
-
-#define VERIFY_ACTIVECPU(name) \
- int activecpu = cpunum_get_active(); \
- assert_always(activecpu >= 0, #name "() called with no active cpu!")
-
-#define VERIFY_EXECUTINGCPU(name) \
- int activecpu = cpunum_get_executing(); \
- assert_always(activecpu >= 0, #name "() called with no executing cpu!")
-
-#define VERIFY_CPUNUM(name) \
- assert_always(cpunum >= 0 && cpunum < cpu_gettotalcpu(), #name "() called for invalid cpu num!")
-
-
-
-/***************************************************************************
CONSTANTS
***************************************************************************/
@@ -64,29 +45,54 @@ enum
TYPE DEFINITIONS
***************************************************************************/
-/* Internal CPU info structure */
-typedef struct _cpuexec_data cpuexec_data;
-struct _cpuexec_data
+/* internal information about the state of inputs */
+typedef struct _cpuinput_data cpuinput_data;
+struct _cpuinput_data
{
- 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 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 */
+};
- 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 */
+/* internal data hanging off of the classtoken */
+typedef struct _cpu_class_data cpu_class_data;
+struct _cpu_class_data
+{
+ /* this item must remain first */
+ cpu_class_header header; /* header containing public data */
+
+ /* core interface */
+ device_config * device; /* dummy device for now */
+ int * icount; /* pointer to the icount */
+
+ /* input states and IRQ callbacks */
+ cpu_irq_callback driver_irq; /* driver-specific IRQ callback */
+ cpuinput_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 */
/* 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 */
+ 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 */
};
@@ -96,8 +102,6 @@ struct _cpuexec_data
***************************************************************************/
/* general CPU variables */
-static cpuexec_data cpu[MAX_CPU];
-
static int cycles_running;
static int cycles_stolen;
@@ -109,10 +113,55 @@ static int cycles_stolen;
static void cpuexec_exit(running_machine *machine);
static void cpuexec_reset(running_machine *machine);
-static void cpu_inittimers(running_machine *machine);
-static void update_clock_information(running_machine *machine, int cpunum);
-static TIMER_CALLBACK( trigger_partial_frame_interrupt );
+
+static void cpu_timers_init(running_machine *machine);
+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);
+
+
+
+/***************************************************************************
+ INLINE FUNCTIONS
+***************************************************************************/
+
+/*-------------------------------------------------
+ get_safe_classtoken - makes sure that the
+ passed in device is, in fact, a CPU, and
+ return the class token
+-------------------------------------------------*/
+
+INLINE cpu_class_data *get_safe_classtoken(const device_config *device)
+{
+ assert(device != NULL);
+ assert(device->classtoken != NULL);
+ assert(device->class == DEVICE_CLASS_CPU_CHIP);
+
+ return (cpu_class_data *)device->classtoken;
+}
+
+
+/*-------------------------------------------------
+ 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_safe_classtoken(device);
+
+ /* suspend the CPU immediately if it's not already */
+ cpu_suspend(device, SUSPEND_REASON_TRIGGER, eatcycles);
+
+ /* set the trigger */
+ classdata->trigger = trigger;
+}
@@ -130,55 +179,96 @@ void cpuexec_init(running_machine *machine)
int cpunum;
/* loop over all our CPUs */
- memset(cpu, 0, sizeof(cpu));
- for (cpunum = 0; cpunum < MAX_CPU; cpunum++)
+ for (cpunum = 0; cpunum < ARRAY_LENGTH(machine->config->cpu); cpunum++)
{
+ const cpu_config *config = &machine->config->cpu[cpunum];
cpu_type cputype = machine->config->cpu[cpunum].type;
+ cpu_class_data *classdata;
int num_regs;
+ int line;
/* if this is a dummy, stop looking */
if (cputype == CPU_DUMMY)
break;
-
- /* initialize the cpuinfo struct */
- cpu[cpunum].suspend = SUSPEND_REASON_RESET;
- cpu[cpunum].clock = (UINT64)machine->config->cpu[cpunum].clock * cputype_clock_multiplier(cputype) / cputype_clock_divider(cputype);
- cpu[cpunum].clockscale = 1.0;
- cpu[cpunum].localtime = attotime_zero;
-
- /* compute the cycle times */
- update_clock_information(machine, cpunum);
-
- /* register some of our variables for later */
- state_save_register_item("cpu", cpunum, cpu[cpunum].suspend);
- state_save_register_item("cpu", cpunum, cpu[cpunum].nextsuspend);
- state_save_register_item("cpu", cpunum, cpu[cpunum].eatcycles);
- state_save_register_item("cpu", cpunum, cpu[cpunum].nexteatcycles);
- state_save_register_item("cpu", cpunum, cpu[cpunum].trigger);
-
- state_save_register_item("cpu", cpunum, cpu[cpunum].iloops);
-
- state_save_register_item("cpu", cpunum, cpu[cpunum].totalcycles);
- state_save_register_item("cpu", cpunum, cpu[cpunum].localtime.seconds);
- state_save_register_item("cpu", cpunum, cpu[cpunum].localtime.attoseconds);
- state_save_register_item("cpu", cpunum, cpu[cpunum].clock);
- state_save_register_item("cpu", cpunum, cpu[cpunum].clockscale);
+
+ /* allocate memory for our class state */
+ classdata = auto_malloc(sizeof(*classdata));
+ memset(classdata, 0, sizeof(*classdata));
+
+ /* fill in the header */
+ classdata->header = *cputype_get_header_template(cputype);
+
+ /* create a fake device for the CPU -- this will be done automatically in the future */
+ classdata->device = auto_malloc(sizeof(*classdata->device) + strlen(config->tag));
+ memset(classdata->device, 0, sizeof(*classdata->device));
+ strcpy(classdata->device->tag, config->tag);
+ classdata->device->type = (device_type)cputype;
+ classdata->device->class = DEVICE_CLASS_CPU_CHIP;
+ classdata->device->inline_config = (void *)&machine->config->cpu[cpunum];
+ classdata->device->static_config = config->reset_param;
+ classdata->device->started = TRUE;
+ classdata->device->machine = machine;
+ classdata->device->region = memory_region(machine, classdata->device->tag);
+ classdata->device->regionbytes = memory_region_length(machine, classdata->device->tag);
+
+ /* allocate a context token */
+ classdata->device->token = auto_malloc(cputype_get_context_size(cputype));
+ memset(classdata->device->token, 0, cputype_get_context_size(cputype));
+
+ /* set up the class token */
+ classdata->device->classtoken = classdata;
+
+ /* fill in the input states and IRQ callback information */
+ for (line = 0; line < ARRAY_LENGTH(classdata->input); line++)
+ {
+ cpuinput_data *inputline = &classdata->input[line];
+ /* vector and curvector are initialized later */
+ inputline->curstate = CLEAR_LINE;
+ inputline->qindex = 0;
+ }
+
+ /* fill in the suspend states */
+ classdata->suspend = SUSPEND_REASON_RESET;
+ classdata->inttrigger = cpunum + TRIGGER_INT;
+
+ /* fill in the clock and timing information */
+ classdata->clock = (UINT64)config->clock * classdata->header.clock_multiplier / classdata->header.clock_divider;
+ classdata->clockscale = 1.0;
+
+ /* allocate timers if we need them */
+ if (config->vblank_interrupts_per_frame > 1)
+ classdata->partial_frame_timer = timer_alloc(trigger_partial_frame_interrupt, (void *)classdata->device);
+ if (config->timed_interrupt_period != 0)
+ classdata->timedint_timer = timer_alloc(trigger_periodic_interrupt, (void *)classdata->device);
/* initialize this CPU */
state_save_push_tag(cpunum + 1);
num_regs = state_save_get_reg_count();
- if (cpuintrf_init_cpu(cpunum, cputype, cpu[cpunum].clock, machine->config->cpu[cpunum].reset_param, cpu_irq_callbacks[cpunum]))
- fatalerror("Unable to initialize CPU #%d (%s)", cpunum, cputype_name(cputype));
+ machine->cpu[cpunum] = classdata->device;
+ cpu_init(classdata->device, cpunum, classdata->clock, standard_irq_callback);
num_regs = state_save_get_reg_count() - num_regs;
state_save_pop_tag();
+ /* fetch post-initialization data */
+ classdata->icount = cpu_get_icount_ptr(classdata->device);
+ for (line = 0; line < ARRAY_LENGTH(classdata->input); line++)
+ {
+ cpuinput_data *inputline = &classdata->input[line];
+ inputline->vector = cpu_get_default_irq_vector(classdata->device);
+ inputline->curvector = inputline->vector;
+ }
+ update_clock_information(classdata->device);
+
/* if no state registered for saving, we can't save */
if (num_regs == 0)
{
- logerror("CPU #%d (%s) did not register any state to save!\n", cpunum, cputype_name(cputype));
+ logerror("CPU #%d (%s) did not register any state to save!\n", cpunum, cputype_get_name(cputype));
if (machine->gamedrv->flags & GAME_SUPPORTS_SAVE)
- fatalerror("CPU #%d (%s) did not register any state to save!", cpunum, cputype_name(cputype));
+ fatalerror("CPU #%d (%s) did not register any state to save!", cpunum, cputype_get_name(cputype));
}
+
+ /* register some internal states as well */
+ register_save_states(classdata->device);
}
add_reset_callback(machine, cpuexec_reset);
add_exit_callback(machine, cpuexec_exit);
@@ -194,24 +284,38 @@ static void cpuexec_reset(running_machine *machine)
{
int cpunum;
- /* initialize the various timers (suspends all CPUs at startup) */
- cpu_inittimers(machine);
+ /* initialize the various timers */
+ cpu_timers_init(machine);
/* first pass over CPUs */
- for (cpunum = 0; cpunum < cpu_gettotalcpu(); cpunum++)
- {
- /* enable all CPUs (except for disabled CPUs) */
- if (!(machine->config->cpu[cpunum].flags & CPU_DISABLE))
- cpunum_resume(cpunum, SUSPEND_ANY_REASON);
- else
- cpunum_suspend(cpunum, SUSPEND_REASON_DISABLE, 1);
+ for (cpunum = 0; cpunum < ARRAY_LENGTH(machine->cpu); cpunum++)
+ if (machine->cpu[cpunum] != NULL)
+ {
+ const device_config *device = machine->cpu[cpunum];
+ const cpu_config *config = device->inline_config;
+ cpu_class_data *classdata = device->classtoken;
+ 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 */
- cpu[cpunum].totalcycles = 0;
+ /* reset the total number of cycles */
+ classdata->totalcycles = 0;
- /* then reset the CPU directly */
- cpu_reset(machine->cpu[cpunum]);
- }
+ /* then reset the CPU directly */
+ cpu_reset(device);
+
+ /* reset the interrupt vectors and queues */
+ for (line = 0; line < ARRAY_LENGTH(classdata->input); line++)
+ {
+ cpuinput_data *inputline = &classdata->input[line];
+ inputline->vector = cpu_get_default_irq_vector(device);
+ inputline->qindex = 0;
+ }
+ }
}
@@ -224,8 +328,9 @@ static void cpuexec_exit(running_machine *machine)
int cpunum;
/* shut down the CPU cores */
- for (cpunum = 0; cpunum < cpu_gettotalcpu(); cpunum++)
- cpuintrf_exit_cpu(cpunum);
+ for (cpunum = 0; cpunum < ARRAY_LENGTH(machine->cpu); cpunum++)
+ if (machine->cpu[cpunum] != NULL)
+ cpu_exit(machine->cpu[cpunum]);
}
@@ -245,25 +350,25 @@ void cpuexec_timeslice(running_machine *machine)
LOG(("cpu_timeslice: target = %s\n", attotime_string(target, 9)));
/* apply pending suspension changes */
- for (cpunum = 0; machine->config->cpu[cpunum].type != CPU_DUMMY; cpunum++)
+ for (cpunum = 0; machine->cpu[cpunum] != NULL; cpunum++)
{
- cpuexec_data *cpudata = &cpu[cpunum];
- cpudata->suspend = cpudata->nextsuspend;
- cpudata->nextsuspend &= ~SUSPEND_REASON_TIMESLICE;
- cpudata->eatcycles = cpudata->nexteatcycles;
+ cpu_class_data *classdata = machine->cpu[cpunum]->classtoken;
+ classdata->suspend = classdata->nextsuspend;
+ classdata->nextsuspend &= ~SUSPEND_REASON_TIMESLICE;
+ classdata->eatcycles = classdata->nexteatcycles;
}
/* loop over non-suspended CPUs */
- for (cpunum = 0; machine->config->cpu[cpunum].type != CPU_DUMMY; cpunum++)
+ for (cpunum = 0; machine->cpu[cpunum] != NULL; cpunum++)
{
- cpuexec_data *cpudata = &cpu[cpunum];
- if (cpudata->suspend == 0)
+ cpu_class_data *classdata = machine->cpu[cpunum]->classtoken;
+ if (classdata->suspend == 0)
{
- attotime delta = attotime_sub(target, cpudata->localtime);
+ attotime delta = attotime_sub(target, classdata->localtime);
if (delta.seconds >= 0 && delta.attoseconds >= attoseconds_per_cycle[cpunum])
{
/* compute how long to run */
- cycles_running = div_64x32(delta.attoseconds >> cpudata->divshift, cpudata->divisor);
+ cycles_running = div_64x32(delta.attoseconds >> classdata->divshift, classdata->divisor);
LOG((" cpu %d: %d cycles\n", cpunum, cycles_running));
profiler_mark(PROFILER_CPU1 + cpunum);
@@ -289,14 +394,14 @@ void cpuexec_timeslice(running_machine *machine)
profiler_mark(PROFILER_END);
/* account for these cycles */
- cpudata->totalcycles += ran;
- cpudata->localtime = attotime_add_attoseconds(cpudata->localtime, ran * attoseconds_per_cycle[cpunum]);
- LOG((" %d ran, %d total, time = %s\n", ran, (INT32)cpudata->totalcycles, attotime_string(cpudata->localtime, 9)));
+ classdata->totalcycles += ran;
+ classdata->localtime = attotime_add_attoseconds(classdata->localtime, ran * attoseconds_per_cycle[cpunum]);
+ 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_compare(cpudata->localtime, target) < 0)
+ if (attotime_compare(classdata->localtime, target) < 0)
{
- target = attotime_max(cpudata->localtime, base);
+ target = attotime_max(classdata->localtime, base);
LOG((" (new target)\n"));
}
}
@@ -304,27 +409,27 @@ void cpuexec_timeslice(running_machine *machine)
}
/* update the local times of all CPUs */
- for (cpunum = 0; machine->config->cpu[cpunum].type != CPU_DUMMY; cpunum++)
+ for (cpunum = 0; cpunum < ARRAY_LENGTH(machine->cpu) && machine->cpu[cpunum] != NULL; cpunum++)
{
- cpuexec_data *cpudata = &cpu[cpunum];
+ cpu_class_data *classdata = machine->cpu[cpunum]->classtoken;
/* if we're suspended and counting, process */
- if (cpudata->suspend != 0 && cpudata->eatcycles && attotime_compare(cpudata->localtime, target) < 0)
+ if (classdata->suspend != 0 && classdata->eatcycles && attotime_compare(classdata->localtime, target) < 0)
{
- attotime delta = attotime_sub(target, cpudata->localtime);
+ attotime delta = attotime_sub(target, classdata->localtime);
/* compute how long to run */
- cycles_running = div_64x32(delta.attoseconds >> cpudata->divshift, cpudata->divisor);
+ cycles_running = div_64x32(delta.attoseconds >> classdata->divshift, classdata->divisor);
LOG((" cpu %d: %d cycles (suspended)\n", cpunum, cycles_running));
- cpudata->totalcycles += cycles_running;
- cpudata->localtime = attotime_add_attoseconds(cpudata->localtime, cycles_running * attoseconds_per_cycle[cpunum]);
- LOG((" %d skipped, %d total, time = %s\n", cycles_running, (INT32)cpudata->totalcycles, attotime_string(cpudata->localtime, 9)));
+ classdata->totalcycles += cycles_running;
+ classdata->localtime = attotime_add_attoseconds(classdata->localtime, cycles_running * attoseconds_per_cycle[cpunum]);
+ LOG((" %d skipped, %d total, time = %s\n", cycles_running, (INT32)classdata->totalcycles, attotime_string(classdata->localtime, 9)));
}
/* update the suspend state (breaks steeltal if we don't) */
- cpudata->suspend = cpudata->nextsuspend;
- cpudata->eatcycles = cpudata->nexteatcycles;
+ classdata->suspend = classdata->nextsuspend;
+ classdata->eatcycles = classdata->nexteatcycles;
}
/* update the global time */
@@ -332,17 +437,12 @@ void cpuexec_timeslice(running_machine *machine)
}
-
-/***************************************************************************
- CPU SCHEDULING
-***************************************************************************/
-
/*-------------------------------------------------
- cpu_boost_interleave - temporarily boosts the
- interleave factor
+ cpuexec_boost_interleave - temporarily boosts
+ the interleave factor
-------------------------------------------------*/
-void cpu_boost_interleave(running_machine *machine, attotime timeslice_time, attotime boost_duration)
+void cpuexec_boost_interleave(running_machine *machine, attotime timeslice_time, attotime boost_duration)
{
/* ignore timeslices > 1 second */
if (timeslice_time.seconds > 0)
@@ -352,67 +452,76 @@ void cpu_boost_interleave(running_machine *machine, attotime timeslice_time, att
/*-------------------------------------------------
- activecpu_abort_timeslice - abort execution
- for the current timeslice, allowing other
- CPUs to run before we run again
+ cputag_get_cpu - return a pointer to the given
+ CPU by tag
-------------------------------------------------*/
-void activecpu_abort_timeslice(void)
+const device_config *cputag_get_cpu(running_machine *machine, const char *tag)
{
- int current_icount;
+ int cpunum;
+
+ for (cpunum = 0; cpunum < ARRAY_LENGTH(machine->cpu); cpunum++)
+ if (machine->cpu[cpunum] != NULL && strcmp(tag, machine->cpu[cpunum]->tag) == 0)
+ return machine->cpu[cpunum];
+
+ return NULL;
+}
- VERIFY_EXECUTINGCPU(activecpu_abort_timeslice);
- LOG(("activecpu_abort_timeslice (CPU=%d, cycles_left=%d)\n", cpunum_get_executing(), activecpu_get_icount() + 1));
- /* swallow the remaining cycles */
- current_icount = activecpu_get_icount() + 1;
- cycles_stolen += current_icount;
- cycles_running -= current_icount;
- activecpu_adjust_icount(-current_icount);
-}
+/***************************************************************************
+ CPU SCHEDULING
+***************************************************************************/
/*-------------------------------------------------
- cpunum_suspend - set a suspend reason for the
+ cpu_suspend - set a suspend reason for the
given CPU
-------------------------------------------------*/
-void cpunum_suspend(int cpunum, int reason, int eatcycles)
+void cpu_suspend(const device_config *device, int reason, int eatcycles)
{
- VERIFY_CPUNUM(cpunum_suspend);
- LOG(("cpunum_suspend (CPU=%d, r=%X, eat=%d)\n", cpunum, reason, eatcycles));
- cpu[cpunum].nextsuspend |= reason;
- cpu[cpunum].nexteatcycles = eatcycles;
- if (cpunum_get_executing() >= 0)
- activecpu_abort_timeslice();
+ cpu_class_data *classdata = get_safe_classtoken(device);
+
+ /* set the suspend reason and eat cycles flag */
+ classdata->nextsuspend |= reason;
+ classdata->nexteatcycles = eatcycles;
+
+ /* if we're active, synchronize */
+ if (device == device->machine->activecpu)
+ cpu_abort_timeslice(device);
}
/*-------------------------------------------------
- cpunum_resume - clear a suspend reason for the
+ cpu_resume - clear a suspend reason for the
given CPU
-------------------------------------------------*/
-void cpunum_resume(int cpunum, int reason)
+void cpu_resume(const device_config *device, int reason)
{
- VERIFY_CPUNUM(cpunum_resume);
- LOG(("cpunum_resume (CPU=%d, r=%X)\n", cpunum, reason));
- cpu[cpunum].nextsuspend &= ~reason;
- if (cpunum_get_executing() >= 0)
- activecpu_abort_timeslice();
+ cpu_class_data *classdata = get_safe_classtoken(device);
+
+ /* clear the suspend reason and eat cycles flag */
+ classdata->nextsuspend &= ~reason;
+
+ /* if we're active, synchronize */
+ if (device == device->machine->activecpu)
+ cpu_abort_timeslice(device);
}
/*-------------------------------------------------
- cpunum_is_suspended - returns true if the
+ cpu_is_suspended - returns true if the
given CPU is suspended for any of the given
reasons
-------------------------------------------------*/
-int cpunum_is_suspended(int cpunum, int reason)
+int cpu_is_suspended(const device_config *device, int reason)
{
- VERIFY_CPUNUM(cpunum_suspend);
- return ((cpu[cpunum].nextsuspend & reason) != 0);
+ cpu_class_data *classdata = get_safe_classtoken(device);
+
+ /* return true if the given reason is indicated */
+ return ((classdata->nextsuspend & reason) != 0);
}
@@ -422,82 +531,60 @@ int cpunum_is_suspended(int cpunum, int reason)
***************************************************************************/
/*-------------------------------------------------
- update_clock_information - recomputes clock
- information for the specified CPU
--------------------------------------------------*/
-
-static void update_clock_information(running_machine *machine, int cpunum)
-{
- INT64 attos;
-
- /* recompute cps and spc */
- cycles_per_second[cpunum] = (double)cpu[cpunum].clock * cpu[cpunum].clockscale;
- attoseconds_per_cycle[cpunum] = ATTOSECONDS_PER_SECOND / ((double)cpu[cpunum].clock * cpu[cpunum].clockscale);
-
- /* update the CPU's divisor */
- attos = attoseconds_per_cycle[cpunum];
- cpu[cpunum].divshift = 0;
- while (attos >= (1UL << 31))
- {
- cpu[cpunum].divshift++;
- attos >>= 1;
- }
- cpu[cpunum].divisor = attos;
-
- /* re-compute the perfect interleave factor */
- compute_perfect_interleave(machine);
-}
-
-
-/*-------------------------------------------------
- cpunum_get_clock - gets the given CPU's
+ cpu_get_clock - gets the given CPU's
clock speed
-------------------------------------------------*/
-int cpunum_get_clock(int cpunum)
+int cpu_get_clock(const device_config *device)
{
- VERIFY_CPUNUM(cpunum_get_clock);
- return cpu[cpunum].clock;
+ cpu_class_data *classdata = get_safe_classtoken(device);
+
+ /* return the current clock value */
+ return classdata->clock;
}
/*-------------------------------------------------
- cpunum_set_clock - sets the given CPU's
+ cpu_set_clock - sets the given CPU's
clock speed
-------------------------------------------------*/
-void cpunum_set_clock(running_machine *machine, int cpunum, int clock)
+void cpu_set_clock(const device_config *device, int clock)
{
- VERIFY_CPUNUM(cpunum_set_clock);
+ cpu_class_data *classdata = get_safe_classtoken(device);
- cpu[cpunum].clock = clock;
- update_clock_information(machine, cpunum);
+ /* set the clock and update the information */
+ classdata->clock = clock;
+ update_clock_information(device);
}
/*-------------------------------------------------
- cpunum_get_clockscale - returns the current
+ cpu_get_clockscale - returns the current
scaling factor for a CPU's clock speed
-------------------------------------------------*/
-double cpunum_get_clockscale(int cpunum)
+double cpu_get_clockscale(const device_config *device)
{
- VERIFY_CPUNUM(cpunum_get_clockscale);
- return cpu[cpunum].clockscale;
+ cpu_class_data *classdata = get_safe_classtoken(device);
+
+ /* return the current clock scale factor */
+ return classdata->clockscale;
}
/*-------------------------------------------------
- cpunum_set_clockscale - sets the current
+ cpu_set_clockscale - sets the current
scaling factor for a CPU's clock speed
-------------------------------------------------*/
-void cpunum_set_clockscale(running_machine *machine, int cpunum, double clockscale)
+void cpu_set_clockscale(const device_config *device, double clockscale)
{
- VERIFY_CPUNUM(cpunum_set_clockscale);
+ cpu_class_data *classdata = get_safe_classtoken(device);
- cpu[cpunum].clockscale = clockscale;
- update_clock_information(machine, cpunum);
+ /* set the scale factor and update the information */
+ classdata->clockscale = clockscale;
+ update_clock_information(device);
}
@@ -507,140 +594,137 @@ void cpunum_set_clockscale(running_machine *machine, int cpunum, double clocksca
***************************************************************************/
/*-------------------------------------------------
- cpunum_get_localtime - returns the current
+ cpu_get_local_time - returns the current
local time for a CPU
-------------------------------------------------*/
-attotime cpunum_get_localtime(int cpunum)
+attotime cpu_get_local_time(const device_config *device)
{
+ cpu_class_data *classdata = get_safe_classtoken(device);
attotime result;
- VERIFY_CPUNUM(cpunum_get_localtime);
-
/* if we're active, add in the time from the current slice */
- result = cpu[cpunum].localtime;
- if (cpunum == cpunum_get_executing())
+ result = classdata->localtime;
+ if (device == device->machine->activecpu && classdata->icount != NULL)
{
- int cycles = cycles_running - activecpu_get_icount();
- result = attotime_add(result, ATTOTIME_IN_CYCLES(cycles, cpunum));
+ int cycles = cycles_running - *classdata->icount;
+ result = attotime_add(result, ATTOTIME_IN_CYCLES(cycles, classdata->header.index));
}
return result;
}
/*-------------------------------------------------
- activecpu_gettotalcycles - return the total
+ cpu_get_total_cycles - return the total
number of CPU cycles executed on the active
CPU
-------------------------------------------------*/
-UINT64 activecpu_gettotalcycles(void)
+UINT64 cpu_get_total_cycles(const device_config *device)
{
- VERIFY_ACTIVECPU(activecpu_gettotalcycles);
- if (activecpu == cpunum_get_executing())
- return cpu[activecpu].totalcycles + cycles_running - activecpu_get_icount();
- else
- return cpu[activecpu].totalcycles;
-}
-
-
-/*-------------------------------------------------
- cpunum_gettotalcycles - return the total
- number of CPU cycles executed on the
- specified CPU
--------------------------------------------------*/
+ cpu_class_data *classdata = get_safe_classtoken(device);
-UINT64 cpunum_gettotalcycles(int cpunum)
-{
- VERIFY_CPUNUM(cpunum_gettotalcycles);
- if (cpunum == cpunum_get_executing())
- return cpu[cpunum].totalcycles + cycles_running - activecpu_get_icount();
+ if (device == device->machine->activecpu)
+ return classdata->totalcycles + cycles_running - *classdata->icount;
else
- return cpu[cpunum].totalcycles;
+ return classdata->totalcycles;
}
/*-------------------------------------------------
- activecpu_eat_cycles - safely eats cycles so
+ cpu_eat_cycles - safely eats cycles so
we don't cross a timeslice boundary
-------------------------------------------------*/
-void activecpu_eat_cycles(int cycles)
+void cpu_eat_cycles(const device_config *device, int cycles)
{
- int cyclesleft = activecpu_get_icount();
- if (cycles > cyclesleft)
- cycles = cyclesleft;
- activecpu_adjust_icount(-cycles);
-}
+ cpu_class_data *classdata = get_safe_classtoken(device);
+ assert(device == device->machine->activecpu);
+ if (cycles > *classdata->icount)
+ cycles = *classdata->icount + 1;
+ *classdata->icount -= cycles;
+}
-/***************************************************************************
- SYNCHRONIZATION HELPERS
-***************************************************************************/
/*-------------------------------------------------
- cpu_suspend_until_trigger - suspend execution
- until the given trigger fires
+ cpu_adjust_icount - apply a +/- to the current
+ icount
-------------------------------------------------*/
-static void cpunum_suspend_until_trigger(int cpunum, int trigger, int eatcycles)
+void cpu_adjust_icount(const device_config *device, int delta)
{
- /* suspend the CPU immediately if it's not already */
- cpunum_suspend(cpunum, SUSPEND_REASON_TRIGGER, eatcycles);
+ cpu_class_data *classdata = get_safe_classtoken(device);
- /* set the trigger */
- cpu[cpunum].trigger = trigger;
+ assert(device == device->machine->activecpu);
+
+ *classdata->icount += delta;
}
/*-------------------------------------------------
- cpu_yield - yield our current timeslice
+ cpu_abort_timeslice - abort execution
+ for the current timeslice, allowing other
+ CPUs to run before we run again
-------------------------------------------------*/
-void cpu_yield(void)
+void cpu_abort_timeslice(const device_config *device)
{
- int cpunum = cpunum_get_executing();
- VERIFY_EXECUTINGCPU(cpu_yield);
- cpunum_suspend(cpunum, SUSPEND_REASON_TIMESLICE, FALSE);
+ cpu_class_data *classdata = get_safe_classtoken(device);
+ int delta;
+
+ assert(device == device->machine->activecpu);
+
+ /* swallow the remaining cycles */
+ if (classdata->icount != NULL)
+ {
+ delta = *classdata->icount + 1;
+ cycles_stolen += delta;
+ cycles_running -= delta;
+ *classdata->icount -= delta;
+ }
}
+
+/***************************************************************************
+ SYNCHRONIZATION HELPERS
+***************************************************************************/
+
/*-------------------------------------------------
- cpu_spin - burn CPU cycles until our timeslice
- is up
+ cpu_yield - yield the given CPU until the end
+ of the current timeslice
-------------------------------------------------*/
-void cpu_spin(void)
+void cpu_yield(const device_config *device)
{
- int cpunum = cpunum_get_executing();
- VERIFY_EXECUTINGCPU(cpu_spin);
- cpunum_suspend(cpunum, SUSPEND_REASON_TIMESLICE, TRUE);
+ /* suspend against the timeslice */
+ cpu_suspend(device, SUSPEND_REASON_TIMESLICE, FALSE);
}
/*-------------------------------------------------
- cpu_spinuntil_trigger - burn CPU cycles until
- a timer trigger
+ cpu_spin - burn CPU cycles until our timeslice
+ is up
-------------------------------------------------*/
-void cpu_spinuntil_trigger(int trigger)
+void cpu_spin(const device_config *device)
{
- int cpunum = cpunum_get_executing();
- VERIFY_EXECUTINGCPU(cpu_yielduntil_trigger);
- cpunum_suspend_until_trigger(cpunum, trigger, TRUE);
+ /* suspend against the timeslice */
+ cpu_suspend(device, SUSPEND_REASON_TIMESLICE, TRUE);
}
/*-------------------------------------------------
- cpunum_spinuntil_trigger - burn specified CPU
+ cpu_spinuntil_trigger - burn specified CPU
cycles until a timer trigger
-------------------------------------------------*/
-void cpunum_spinuntil_trigger(int cpunum, int trigger)
+void cpu_spinuntil_trigger(const device_config *device, int trigger)
{
- VERIFY_CPUNUM(cpunum_spinuntil_trigger);
- cpunum_suspend_until_trigger(cpunum, trigger, TRUE);
+ /* suspend until the given trigger fires */
+ suspend_until_trigger(device, trigger, TRUE);
}
@@ -649,11 +733,12 @@ void cpunum_spinuntil_trigger(int cpunum, int trigger)
next interrupt
-------------------------------------------------*/
-void cpu_spinuntil_int(void)
+void cpu_spinuntil_int(const device_config *device)
{
- int cpunum = cpunum_get_executing();
- VERIFY_EXECUTINGCPU(cpu_spinuntil_int);
- cpunum_suspend_until_trigger(cpunum, TRIGGER_INT + cpunum, TRUE);
+ cpu_class_data *classdata = get_safe_classtoken(device);
+
+ /* suspend until the given trigger fires */
+ suspend_until_trigger(device, classdata->inttrigger, TRUE);
}
@@ -662,13 +747,15 @@ void cpu_spinuntil_int(void)
specific period of time
-------------------------------------------------*/
-void cpu_spinuntil_time(attotime duration)
+void cpu_spinuntil_time(const device_config *device, attotime duration)
{
static int timetrig = 0;
- int cpunum = cpunum_get_executing();
- VERIFY_EXECUTINGCPU(cpu_spinuntil_time);
- cpunum_suspend_until_trigger(cpunum, TRIGGER_SUSPENDTIME + timetrig, TRUE);
- cpu_triggertime(duration, TRIGGER_SUSPENDTIME + timetrig);
+
+ /* 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;
}
@@ -679,48 +766,40 @@ void cpu_spinuntil_time(attotime duration)
***************************************************************************/
/*-------------------------------------------------
- cpu_trigger - generate a trigger now
+ cpuexec_trigger - generate a trigger now
-------------------------------------------------*/
-void cpu_trigger(running_machine *machine, int trigger)
+void cpuexec_trigger(running_machine *machine, int trigger)
{
int cpunum;
/* cause an immediate resynchronization */
- if (cpunum_get_executing() >= 0)
- activecpu_abort_timeslice();
+ if (machine->activecpu != NULL)
+ cpu_abort_timeslice(machine->activecpu);
/* look for suspended CPUs waiting for this trigger and unsuspend them */
- for (cpunum = 0; cpunum < MAX_CPU; cpunum++)
+ for (cpunum = 0; cpunum < ARRAY_LENGTH(machine->cpu) && machine->cpu[cpunum] != NULL; cpunum++)
{
- /* if this is a dummy, stop looking */
- if (machine->config->cpu[cpunum].type == CPU_DUMMY)
- break;
-
+ cpu_class_data *classdata = machine->cpu[cpunum]->classtoken;
+
/* see if this is a matching trigger */
- if (cpu[cpunum].suspend && cpu[cpunum].trigger == trigger)
+ if (classdata->suspend != 0 && classdata->trigger == trigger)
{
- cpunum_resume(cpunum, SUSPEND_REASON_TRIGGER);
- cpu[cpunum].trigger = 0;
+ cpu_resume(machine->cpu[cpunum], SUSPEND_REASON_TRIGGER);
+ classdata->trigger = 0;
}
}
}
/*-------------------------------------------------
- cpu_triggertime - generate a trigger after a
+ cpuexec_triggertime - generate a trigger after a
specific period of time
-------------------------------------------------*/
-static TIMER_CALLBACK( cpu_triggertime_callback )
+void cpuexec_triggertime(running_machine *machine, int trigger, attotime duration)
{
- cpu_trigger(machine, param);
-}
-
-
-void cpu_triggertime(attotime duration, int trigger)
-{
- timer_set(duration, NULL, trigger, cpu_triggertime_callback);
+ timer_set(duration, NULL, trigger, triggertime_callback);
}
@@ -729,135 +808,262 @@ void cpu_triggertime(attotime duration, int trigger)
corresponding to an interrupt on the given CPU
-------------------------------------------------*/
-void cpu_triggerint(running_machine *machine, int cpunum)
+void cpu_triggerint(const device_config *device)
{
- cpu_trigger(machine, TRIGGER_INT + cpunum);
+ cpu_class_data *classdata = get_safe_classtoken(device);
+
+ /* signal this CPU's interrupt trigger */
+ cpuexec_trigger(device->machine, classdata->inttrigger);
}
/***************************************************************************
- CHEESY FAKE VIDEO TIMING
+ INTERRUPTS
***************************************************************************/
/*-------------------------------------------------
- cpu_getiloops - return the cheesy VBLANK
- interrupt counter (deprecated)
+ cpu_set_input_line - set the logical state
+ (ASSERT_LINE/CLEAR_LINE) of an input line
+ on a CPU
-------------------------------------------------*/
-int cpu_getiloops(void)
+void cpu_set_input_line(const device_config *device, int line, int state)
{
- VERIFY_ACTIVECPU(cpu_getiloops);
- return cpu[activecpu].iloops;
+ cpu_class_data *classdata = get_safe_classtoken(device);
+ int vector = (line >= 0 && line < MAX_INPUT_LINES) ? classdata->input[line].vector : 0xff;
+ cpu_set_input_line_and_vector(device, line, state, vector);
}
-/***************************************************************************
- INTERNAL TIMING
-***************************************************************************/
-
/*-------------------------------------------------
- on_vblank - calls any external callbacks
- for this screen
+ cpu_set_input_line_vector - set the vector to
+ be returned during a CPU's interrupt
+ acknowledge cycle
-------------------------------------------------*/
-static void on_vblank(const device_config *device, void *param, int vblank_state)
+void cpu_set_input_line_vector(const device_config *device, int line, int vector)
{
- /* VBLANK starting */
- if (vblank_state)
+ cpu_class_data *classdata = get_safe_classtoken(device);
+ if (line >= 0 && line < MAX_INPUT_LINES)
{
- int cpunum;
+ classdata->input[line].vector = vector;
+ return;
+ }
+ LOG(("cpunum_set_input_line_vector CPU '%s' line %d > max input lines\n", device->tag, line));
+}
- /* find any CPUs that have this screen as their VBLANK interrupt source */
- for (cpunum = 0; cpunum < cpu_gettotalcpu(); cpunum++)
- {
- int cpu_interested;
- const cpu_config *config = device->machine->config->cpu + cpunum;
- /* start the interrupt counter */
- if (!(cpu[cpunum].suspend & SUSPEND_REASON_DISABLE))
- cpu[cpunum].iloops = 0;
- else
- cpu[cpunum].iloops = -1;
+/*-------------------------------------------------
+ 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
+-------------------------------------------------*/
- /* the hack style VBLANK decleration always uses the first screen */
- if (config->vblank_interrupts_per_frame > 1)
- cpu_interested = TRUE;
+void cpu_set_input_line_and_vector(const device_config *device, int line, int state, int vector)
+{
+ cpu_class_data *classdata = get_safe_classtoken(device);
- /* for new style decleration, we need to compare the tags */
- else if (config->vblank_interrupts_per_frame == 1)
- cpu_interested = (strcmp(config->vblank_interrupt_screen, device->tag) == 0);
+#ifdef MAME_DEBUG
+ /* 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)
+ {
+ switch ((cpu_type)device->type)
+ {
+ case CPU_Z80:
+ case CPU_Z180:
+ case CPU_M68000:
+ case CPU_M68008:
+ case CPU_M68010:
+ case CPU_M68EC020:
+ case CPU_M68020:
+ case CPU_M68040:
+ case CPU_R4600BE:
+ case CPU_R4600LE:
+ case CPU_R4650BE:
+ case CPU_R4650LE:
+ case CPU_R4700BE:
+ case CPU_R4700LE:
+ case CPU_R5000BE:
+ case CPU_R5000LE:
+ case CPU_QED5271BE:
+ case CPU_QED5271LE:
+ case CPU_RM7000BE:
+ case CPU_RM7000LE:
+ case CPU_PPC403GA:
+ case CPU_PPC403GCX:
+ case CPU_PPC601:
+ case CPU_PPC602:
+ case CPU_PPC603:
+ case CPU_PPC603E:
+ case CPU_PPC603R:
+ case CPU_PPC604:
+ case CPU_I8035:
+ case CPU_I8041:
+ case CPU_I8048:
+ case CPU_I8648:
+ case CPU_I8748:
+ case CPU_MB8884:
+ case CPU_N7751:
+ case CPU_TMS34010:
+ case CPU_TMS34020:
+ case CPU_TMS32010:
+ case CPU_TMS32025:
+ case CPU_TMS32026:
+ fatalerror("CPU %s: PULSE_LINE used with level-detected IRQ %d\n", device->tag, line);
+ break;
+
+ default:
+ break;
+ }
+ }
+#endif
- /* no VBLANK interrupt, not interested */
- else
- cpu_interested = FALSE;
+ if (line >= 0 && line < MAX_INPUT_LINES)
+ {
+ cpuinput_data *inputline = &classdata->input[line];
+ INT32 input_event = (state & 0xff) | (vector << 8);
+ int event_index = inputline->qindex++;
- /* if interested, call the interrupt handler */
- if (cpu_interested)
- {
- if (!cpunum_is_suspended(cpunum, SUSPEND_REASON_HALT | SUSPEND_REASON_RESET | SUSPEND_REASON_DISABLE))
- {
- cpu_push_context(device->machine->cpu[cpunum]);
- (*config->vblank_interrupt)(device->machine, cpunum);
- cpu_pop_context();
- }
+ LOG(("cpunum_set_input_line_and_vector('%s',%d,%d,%02x)\n", device->tag, line, state, vector));
- /* 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) &&
- !(cpu[cpunum].suspend & SUSPEND_REASON_DISABLE))
- {
- cpu[cpunum].partial_frame_period = attotime_div(video_screen_get_frame_period(device->machine->primary_screen), config->vblank_interrupts_per_frame);
- timer_adjust_oneshot(cpu[cpunum].partial_frame_timer, cpu[cpunum].partial_frame_period, cpunum);
- }
- }
+ /* 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((void *)device, line, empty_event_queue);
}
}
}
/*-------------------------------------------------
- trigger_partial_frame_interrupt - called to
- trigger a partial frame interrupt
+ cpu_set_irq_callback - install a driver-
+ specific callback for IRQ acknowledge
-------------------------------------------------*/
-static TIMER_CALLBACK( trigger_partial_frame_interrupt )
+void cpu_set_irq_callback(const device_config *device, cpu_irq_callback callback)
{
- int cpunum = param;
- const cpu_config *config = machine->config->cpu + cpunum;
+ cpu_class_data *classdata = get_safe_classtoken(device);
+ classdata->driver_irq = callback;
+}
- if (cpu[cpunum].iloops == 0)
- cpu[cpunum].iloops = config->vblank_interrupts_per_frame;
- cpu[cpunum].iloops--;
- /* call the interrupt handler */
- if (!cpunum_is_suspended(cpunum, SUSPEND_REASON_HALT | SUSPEND_REASON_RESET | SUSPEND_REASON_DISABLE))
- {
- cpu_push_context(machine->cpu[cpunum]);
- (*config->vblank_interrupt)(machine, cpunum);
- cpu_pop_context();
- }
+/***************************************************************************
+ CHEESY FAKE VIDEO TIMING (OBSOLETE)
+***************************************************************************/
- /* more? */
- if (cpu[cpunum].iloops > 1)
- timer_adjust_oneshot(cpu[cpunum].partial_frame_timer, cpu[cpunum].partial_frame_period, cpunum);
+/*-------------------------------------------------
+ cpu_getiloops - return the cheesy VBLANK
+ interrupt counter (deprecated)
+-------------------------------------------------*/
+
+int cpu_getiloops(const device_config *device)
+{
+ cpu_class_data *classdata = get_safe_classtoken(device);
+ return classdata->iloops;
}
+
+/***************************************************************************
+ INTERNAL FUNCTIONS
+***************************************************************************/
+
/*-------------------------------------------------
- cpu_timedintcallback - timer callback for
- timed interrupts
+ cpu_timers_init - set up all the core timers
-------------------------------------------------*/
-static TIMER_CALLBACK( cpu_timedintcallback )
+static void cpu_timers_init(running_machine *machine)
{
- /* bail if there is no routine */
- if (machine->config->cpu[param].timed_interrupt != NULL && !cpunum_is_suspended(param, SUSPEND_REASON_HALT | SUSPEND_REASON_RESET | SUSPEND_REASON_DISABLE))
+ int numscreens = video_screen_count(machine->config);
+ attoseconds_t refresh_attosecs;
+ int cpunum, ipf;
+
+ /* set the core scheduling quantum */
+ ipf = machine->config->cpu_slices_per_frame;
+ if (ipf <= 0)
+ ipf = 1;
+ refresh_attosecs = (numscreens == 0) ? HZ_TO_ATTOSECONDS(60) : video_screen_get_frame_period(machine->primary_screen).attoseconds;
+ timer_add_scheduling_quantum(machine, refresh_attosecs / ipf, attotime_never);
+
+ /* register the interrupt handler callbacks */
+ for (cpunum = 0; cpunum < ARRAY_LENGTH(machine->cpu); cpunum++)
+ if (machine->cpu[cpunum] != NULL)
+ {
+ const device_config *device = machine->cpu[cpunum];
+ const cpu_config *config = device->inline_config;
+ cpu_class_data *classdata = device->classtoken;
+
+ /* VBLANK interrupts */
+ if (config->vblank_interrupts_per_frame > 0)
+ {
+ const device_config *screen;
+
+ /* get the screen that will trigger the VBLANK */
+
+ /* new style - use screen tag directly */
+ if (config->vblank_interrupts_per_frame == 1)
+ screen = device_list_find_by_tag(machine->config->devicelist, VIDEO_SCREEN, config->vblank_interrupt_screen);
+
+ /* old style 'hack' setup - use screen #0 */
+ else
+ screen = device_list_first(machine->config->devicelist, VIDEO_SCREEN);
+
+ assert(screen != NULL);
+ video_screen_register_vblank_callback(screen, on_vblank, NULL);
+ }
+
+ /* periodic interrupts */
+ if (config->timed_interrupt_period != 0)
+ {
+ attotime timedint_period = attotime_make(0, config->timed_interrupt_period);
+ assert(classdata->timedint_timer != NULL);
+ timer_adjust_periodic(classdata->timedint_timer, timedint_period, cpunum, timedint_period);
+ }
+ }
+}
+
+
+/*-------------------------------------------------
+ update_clock_information - recomputes clock
+ information for the specified CPU
+-------------------------------------------------*/
+
+static void update_clock_information(const device_config *device)
+{
+ cpu_class_data *classdata = device->classtoken;
+ INT64 attos;
+
+ /* recompute cps and spc */
+ cycles_per_second[classdata->header.index] = (double)classdata->clock * classdata->clockscale;
+ attoseconds_per_cycle[classdata->header.index] = ATTOSECONDS_PER_SECOND / ((double)classdata->clock * classdata->clockscale);
+
+ /* update the CPU's divisor */
+ attos = attoseconds_per_cycle[classdata->header.index];
+ classdata->divshift = 0;
+ while (attos >= (1UL << 31))
{
- cpu_push_context(machine->cpu[param]);
- (*machine->config->cpu[param].timed_interrupt)(machine, param);
- cpu_pop_context();
+ classdata->divshift++;
+ attos >>= 1;
}
+ classdata->divisor = attos;
+
+ /* re-compute the perfect interleave factor */
+ compute_perfect_interleave(device->machine);
}
@@ -870,15 +1076,15 @@ static void compute_perfect_interleave(running_machine *machine)
{
if (attoseconds_per_cycle[0] != 0)
{
- attoseconds_t smallest = attoseconds_per_cycle[0] * cputype_min_cycles(machine->config->cpu[0].type);
+ attoseconds_t smallest = attoseconds_per_cycle[0] * cputype_get_min_cycles(machine->config->cpu[0].type);
attoseconds_t perfect = ATTOSECONDS_PER_SECOND - 1;
int cpunum;
/* start with a huge time factor and find the 2nd smallest cycle time */
- for (cpunum = 1; machine->config->cpu[cpunum].type != CPU_DUMMY; cpunum++)
+ for (cpunum = 1; cpunum < ARRAY_LENGTH(machine->cpu) && machine->cpu[cpunum] != NULL; cpunum++)
if (attoseconds_per_cycle[cpunum] != 0)
{
- attoseconds_t curtime = attoseconds_per_cycle[cpunum] * cputype_min_cycles(machine->config->cpu[cpunum].type);
+ attoseconds_t curtime = attoseconds_per_cycle[cpunum] * cputype_get_min_cycles(machine->config->cpu[cpunum].type);
/* find the 2nd smallest cycle interval */
if (curtime < smallest)
@@ -899,58 +1105,289 @@ static void compute_perfect_interleave(running_machine *machine)
/*-------------------------------------------------
- cpu_inittimers - set up all the core timers
+ on_vblank - calls any external callbacks
+ for this screen
-------------------------------------------------*/
-static void cpu_inittimers(running_machine *machine)
+static void on_vblank(const device_config *device, void *param, int vblank_state)
{
- int numscreens = video_screen_count(machine->config);
- attoseconds_t refresh_attosecs;
- int cpunum, ipf;
+ /* VBLANK starting */
+ if (vblank_state)
+ {
+ int cpunum;
- /* allocate a dummy timer at the minimum frequency to break things up */
- ipf = machine->config->cpu_slices_per_frame;
- if (ipf <= 0)
- ipf = 1;
- refresh_attosecs = (numscreens == 0) ? HZ_TO_ATTOSECONDS(60) : video_screen_get_frame_period(machine->primary_screen).attoseconds;
- timer_add_scheduling_quantum(machine, refresh_attosecs / ipf, attotime_never);
+ /* find any CPUs that have this screen as their VBLANK interrupt source */
+ for (cpunum = 0; cpunum < ARRAY_LENGTH(device->machine->cpu); cpunum++)
+ if (device->machine->cpu[cpunum] != NULL)
+ {
+ const device_config *cpudevice = device->machine->cpu[cpunum];
+ const cpu_config *config = cpudevice->inline_config;
+ cpu_class_data *classdata = cpudevice->classtoken;
+ int cpu_interested;
+
+ /* start the interrupt counter */
+ if (!(classdata->suspend & SUSPEND_REASON_DISABLE))
+ classdata->iloops = 0;
+ else
+ classdata->iloops = -1;
- /* register the interrupt handler callbacks */
- for (cpunum = 0; cpunum < cpu_gettotalcpu(); cpunum++)
+ /* the hack style VBLANK decleration always uses the first screen */
+ if (config->vblank_interrupts_per_frame > 1)
+ cpu_interested = TRUE;
+
+ /* for new style decleration, we need to compare the tags */
+ else if (config->vblank_interrupts_per_frame == 1)
+ 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)))
+ {
+ cpu_push_context(cpudevice);
+ (*config->vblank_interrupt)(cpudevice);
+ cpu_pop_context();
+ }
+
+ /* 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, cpunum);
+ }
+ }
+ }
+ }
+}
+
+
+/*-------------------------------------------------
+ trigger_partial_frame_interrupt - called to
+ trigger a partial frame interrupt
+-------------------------------------------------*/
+
+static TIMER_CALLBACK( trigger_partial_frame_interrupt )
+{
+ const device_config *device = ptr;
+ const cpu_config *config = device->inline_config;
+ cpu_class_data *classdata = device->classtoken;
+
+ 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))
+ {
+ cpu_push_context(device);
+ (*config->vblank_interrupt)(device);
+ cpu_pop_context();
+ }
+
+ /* 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 = ptr;
+ const cpu_config *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))
{
- const cpu_config *config = machine->config->cpu + cpunum;
+ cpu_push_context(device);
+ (*config->timed_interrupt)(device);
+ cpu_pop_context();
+ }
+}
+
+
+/*-------------------------------------------------
+ triggertime_callback - signal a global trigger
+-------------------------------------------------*/
+
+static TIMER_CALLBACK( triggertime_callback )
+{
+ cpuexec_trigger(machine, param);
+}
+
- /* VBLANK interrupts */
- if (config->vblank_interrupts_per_frame > 0)
- {
- const device_config *screen;
- /* get the screen that will trigger the VBLANK */
+/*-------------------------------------------------
+ empty_event_queue - empty a CPU's event queue
+ for a specific input line
+-------------------------------------------------*/
- /* new style - use screen tag directly */
- if (config->vblank_interrupts_per_frame == 1)
- screen = device_list_find_by_tag(machine->config->devicelist, VIDEO_SCREEN, config->vblank_interrupt_screen);
+static TIMER_CALLBACK( empty_event_queue )
+{
+ const device_config *device = ptr;
+ cpu_class_data *classdata = device->classtoken;
+ cpuinput_data *inputline = &classdata->input[param];
+ int curevent;
- /* old style 'hack' setup - use screen #0 */
+ /* swap to the CPU's context */
+ cpu_push_context(device);
+
+ /* 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
{
- screen = device_list_first(machine->config->devicelist, VIDEO_SCREEN);
+ /* 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)
+ cpu_reset(device);
- /* allocate timer that will trigger the partial frame updates */
- cpu[cpunum].partial_frame_timer = timer_alloc(trigger_partial_frame_interrupt, 0);
+ /* if we're clearing the line, make sure the CPU is not halted */
+ cpu_resume(device, SUSPEND_REASON_RESET);
}
+ }
- assert(screen != NULL);
+ /* 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);
- video_screen_register_vblank_callback(screen, on_vblank, NULL);
+ /* if clearing, unhalt the CPU */
+ else if (state == CLEAR_LINE)
+ cpu_resume(device, SUSPEND_REASON_HALT);
}
- /* periodic interrupts */
- if (config->timed_interrupt_period != 0)
+ /* all other cases */
+ else
{
- attotime timedint_period = attotime_make(0, config->timed_interrupt_period);
- cpu[cpunum].timedint_timer = timer_alloc(cpu_timedintcallback, NULL);
- timer_adjust_periodic(cpu[cpunum].timedint_timer, timedint_period, cpunum, timedint_period);
+ /* switch off the requested state */
+ switch (state)
+ {
+ case PULSE_LINE:
+ /* temporary: PULSE_LINE only makes sense for NMI lines on Z80 */
+ assert((cpu_type)device->type != CPU_Z80 || param == INPUT_LINE_NMI);
+ cpu_set_info_int(device, CPUINFO_INT_INPUT_STATE + param, ASSERT_LINE);
+ cpu_set_info_int(device, CPUINFO_INT_INPUT_STATE + param, CLEAR_LINE);
+ break;
+
+ case HOLD_LINE:
+ case ASSERT_LINE:
+ cpu_set_info_int(device, CPUINFO_INT_INPUT_STATE + param, ASSERT_LINE);
+ break;
+
+ case CLEAR_LINE:
+ cpu_set_info_int(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);
}
}
+
+ /* swap back */
+ cpu_pop_context();
+
+ /* 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 = device->classtoken;
+ cpuinput_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_int(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 */
+ debug_cpu_interrupt_hook(device->machine, classdata->header.index, 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 = device->classtoken;
+ int line;
+
+ state_save_register_item("cpu", classdata->header.index, classdata->suspend);
+ state_save_register_item("cpu", classdata->header.index, classdata->nextsuspend);
+ state_save_register_item("cpu", classdata->header.index, classdata->eatcycles);
+ state_save_register_item("cpu", classdata->header.index, classdata->nexteatcycles);
+ state_save_register_item("cpu", classdata->header.index, classdata->trigger);
+
+ state_save_register_item("cpu", classdata->header.index, classdata->iloops);
+
+ state_save_register_item("cpu", classdata->header.index, classdata->totalcycles);
+ state_save_register_item("cpu", classdata->header.index, classdata->localtime.seconds);
+ state_save_register_item("cpu", classdata->header.index, classdata->localtime.attoseconds);
+ state_save_register_item("cpu", classdata->header.index, classdata->clock);
+ state_save_register_item("cpu", classdata->header.index, classdata->clockscale);
+
+ for (line = 0; line < ARRAY_LENGTH(classdata->input); line++)
+ {
+ cpuinput_data *inputline = &classdata->input[line];
+ int index = classdata->header.index * ARRAY_LENGTH(classdata->input) + line;
+ state_save_register_item("cpu", index, inputline->vector);
+ state_save_register_item("cpu", index, inputline->curvector);
+ state_save_register_item("cpu", index, inputline->curstate);
+ }
}
+