diff options
Diffstat (limited to 'src/emu/cpuexec.c')
-rw-r--r-- | src/emu/cpuexec.c | 1261 |
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); + } } + |