summaryrefslogblamecommitdiffstatshomepage
path: root/src/emu/cpuexec.c
blob: ec9282c72023896fb4c56ff95ec36845c4e46158 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11





                                                                            
                                                



                                                                            
                   
                     
                     
                     
 

                                                  


 


                                                                            


                 
                                                         


 


                                                                            













                                                                                                        


                                                                            
 
                          

    






                                        


                                                                            
 
                                 


                                          




                                                                                                                     
 



                                                                                                                            

                                                                                                                        
 
                                                                                                              




                                                                                                               



  


                                                                            
 
                           

                                 



                          
 


                                                                            



                                                     
                                                                           
                                                         
                                                                 
 

 


                                                                            
 



                                                   




                                           
                                    
                                    

                                                    
                                                                     






                                                      
                                                           
                                                                                                                                                    



                                                      
                                                          















                                                                                           


                                                      
                                                                                                                                               













                                                                                                                              


 



                                                   






                                                                          
 



                                                                
                                                                        









                                                                          


 


                                                   










                                                              
                                                   
                                              

                                                   
 
                                                
 
                                                                               






                                                                          
                                              
                                                                                  
         



                                                                  

         
                                          
                                                                                  
         

                                                     
                 

                                                                                                     
                         
                                                             
                                                                                                                     
                                                                                       
 




                                                                                                  







                                                                                         









                                                                                


                                                                                                                                                          

                                                                                                           
                                                                                     
                                 
                                                                                        






                                                                         
                                                                                  
         

                                                     
                                                              
                                                                                                                    
                 

                                                                                  
                                                     
                                                                                                             

                                                                                           
                                                               
                                                                                                                                          
                                                                                                                                                                 
                 
 


                                                                            


                                    
                                               



 




                                                                            
                                                 


                                                   
                                                                                                     
 



                                                                                          







                                                   















                                                                                                                         

                                                   
             
                                                   




                                                                                    

                                              

                                            


 

                                                   
             
                                                   




                                                                
                                           

                                            


 
                                                   

                                               

                                                   








                                                         







                                                                            
 
                                                                          
 
                    
 


                                                                                                                      









                                              
 

                                                      
 

 
                                                   
                                           

                                                   
 
                                
 

                                        


 
                                                   
                                           

                                                   
 
                                                                      


                                        

                                                  


 
                                                   
                                               

                                                   







                                             
                                                   
                                            

                                                   
 
                                                                                   



                                             
                                                  



 


                                                                            
 
                                                   
                                              

                                                   
 


                                         
 
                                            
 







                                                                                  


 




                                                   
 
                                     


                                                   
                                                                                            



                                                  


                                                   
                                        


                                                   
                                        


                                             
                                                                                         




                                               



                                                   










                                                


                                                                            
 





                                                                                
 

                                                                  
 
                             
                                      


 


                                                   
 


                                           

                                                                
 
 
 
                                                   
                                                  



                                                   
 
                                           

                                                               
 
 
 
                                                   
                                                 

                                                   
 





                                                            

 
                                                   
                                                 

                                                   
 
                                                      
 

                                                            


 
                                                   
                                                 

                                                   
 





                                                                         
 

                                                   
                                              



                                                   
 





                                                                                   



 


                                                                            
 


                                                   
 
                                                       










                                                                                 
                                                                   











                                                                          
                                                   
                                                

                                                   


                                                 
                                    




                                                    
                                                                     


 
                                                   
                                       

                                                   
 
                                                         
 
                                                   



 
                                                                            

                                                                            
 
                                                   


                                                   
 




                                        
 
 




                                                                            

                                            
                                                   
 
                                                                                 
 



                             
 



                                                                                          
                                                                                         
 

                                                                            
                                                       

                                                        
 
                                                                                            

                                                                    
 

                                                                                    
                                                                                                             






                                                                       
                         
                                                                                                                                      
                                 
                                                                      
                                                                                             
                                                               

                                 



                                                                                                                                   
                                                                                                                                                                                             

                                                                                                                                        

                         



         
                                                   

                                               
                                                   
 
                                                        
 
                           



                                                                         
 
                             
 



                                                                                                              
                                                             

                                       
 
                   
                                   
                                                                                                                


 



                                                   



                                             
                                                                                                                                                                    

                                             
                                                                               




                                       
                                                   
                                            

                                                   
 
                                                                
 




                                                                                                                     
 


                                                                                          
                         
                                                                                                                                              
 







                                                                                
                         
 

                                                            
 

                                                                                                                                       


 


                                                   


                                                    

                                                             
                        

                                                                                
                                                    

                        
                                                                                                                                          
                                                                                      
 
                                                      

                                                              
                                                                         
 
                                       
                                                            
                 
                                                    
 
                                                                         
 

                                                                     
                                                                                                                                             
 


                                                                    
                                                                                                      
 


                                                                                                                  
 

                                               
                                                                                       
                 
 
                                         
                                                        
                 
                                                                                                    
                                                                                             
                                                                                                                    

                 
 
/***************************************************************************

    cpuexec.c

    Core multi-CPU execution engine.

    Copyright Nicola Salmoria and the MAME Team.
    Visit http://mamedev.org for licensing and usage restrictions.

***************************************************************************/

#include "driver.h"
#include "profiler.h"
#include "eminline.h"
#include "debugger.h"

extern const cpu_irq_callback cpu_irq_callbacks[];




/***************************************************************************
    DEBUGGING
***************************************************************************/

#define VERBOSE 0

#define LOG(x)	do { if (VERBOSE) logerror x; } while (0)



/***************************************************************************
    MACROS
***************************************************************************/

#define VERIFY_ACTIVECPU(name) \
	int activecpu = cpu_getactivecpu(); \
	assert_always(activecpu >= 0, #name "() called with no active cpu!")

#define VERIFY_EXECUTINGCPU(name) \
	int activecpu = cpu_getexecutingcpu(); \
	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
***************************************************************************/

/* internal trigger IDs */
enum
{
	TRIGGER_INT 		= -2000,
	TRIGGER_YIELDTIME 	= -3000,
	TRIGGER_SUSPENDTIME = -4000
};



/***************************************************************************
    TYPE DEFINITIONS
***************************************************************************/

/* Internal CPU info structure */
typedef struct _cpuexec_data cpuexec_data;
struct _cpuexec_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 */

	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 */
};



/***************************************************************************
    GLOBAL VARIABLES
***************************************************************************/

/* general CPU variables */
static cpuexec_data cpu[MAX_CPU];

static int cycles_running;
static int cycles_stolen;



/***************************************************************************
    FUNCTION PROTOTYPES
***************************************************************************/

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 compute_perfect_interleave(running_machine *machine);



/***************************************************************************
    CORE CPU EXECUTION
***************************************************************************/

/*-------------------------------------------------
    cpuexec_init - initialize internal states of
    all CPUs
-------------------------------------------------*/

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++)
	{
		cpu_type cputype = machine->config->cpu[cpunum].type;
		int num_regs;

		/* 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);

		/* 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));
		num_regs = state_save_get_reg_count() - num_regs;
		state_save_pop_tag();

		/* 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));
			if (machine->gamedrv->flags & GAME_SUPPORTS_SAVE)
				fatalerror("CPU #%d (%s) did not register any state to save!", cpunum, cputype_name(cputype));
		}
	}
	add_reset_callback(machine, cpuexec_reset);
	add_exit_callback(machine, cpuexec_exit);
}


/*-------------------------------------------------
    cpuexec_reset - reset CPU states on a soft
    reset
-------------------------------------------------*/

static void cpuexec_reset(running_machine *machine)
{
	int cpunum;

	/* initialize the various timers (suspends all CPUs at startup) */
	cpu_inittimers(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);

		/* reset the total number of cycles */
		cpu[cpunum].totalcycles = 0;

		/* then reset the CPU directly */
		cpunum_reset(cpunum);
	}
}


/*-------------------------------------------------
    cpuexec_exit - cleanup all CPUs on exit
-------------------------------------------------*/

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);
}


/*-------------------------------------------------
    cpuexec_timeslice - execute all CPUs for a
    single timeslice
-------------------------------------------------*/

void cpuexec_timeslice(running_machine *machine)
{
	int call_debugger = ((machine->debug_flags & DEBUG_FLAG_ENABLED) != 0);
	attotime target = timer_next_fire_time();
	attotime base = timer_get_time();
	int cpunum, ran;

	LOG(("------------------\n"));
	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++)
	{
		cpuexec_data *cpudata = &cpu[cpunum];
		cpudata->suspend = cpudata->nextsuspend;
		cpudata->nextsuspend &= ~SUSPEND_REASON_TIMESLICE;
		cpudata->eatcycles = cpudata->nexteatcycles;
	}

	/* loop over non-suspended CPUs */
	for (cpunum = 0; machine->config->cpu[cpunum].type != CPU_DUMMY; cpunum++)
	{
		cpuexec_data *cpudata = &cpu[cpunum];
		if (cpudata->suspend == 0)
		{
			attotime delta = attotime_sub(target, cpudata->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);
				LOG(("  cpu %d: %d cycles\n", cpunum, cycles_running));

				profiler_mark(PROFILER_CPU1 + cpunum);

				/* note that this global variable cycles_stolen can be modified */
				/* via the call to the cpunum_execute */
				cycles_stolen = 0;
				if (!call_debugger)
					ran = cpunum_execute(cpunum, cycles_running);
				else
				{
					debugger_start_cpu_hook(machine, cpunum, target);
					ran = cpunum_execute(cpunum, cycles_running);
					debugger_stop_cpu_hook(machine, cpunum);
				}

#ifdef MAME_DEBUG
				if (ran < cycles_stolen)
					fatalerror("Negative CPU cycle count!");
#endif /* MAME_DEBUG */

				ran -= cycles_stolen;
				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)));

				/* if the new local CPU time is less than our target, move the target up */
				if (attotime_compare(cpudata->localtime, target) < 0)
				{
					target = attotime_max(cpudata->localtime, base);
					LOG(("         (new target)\n"));
				}
			}
		}
	}

	/* update the local times of all CPUs */
	for (cpunum = 0; machine->config->cpu[cpunum].type != CPU_DUMMY; cpunum++)
	{
		cpuexec_data *cpudata = &cpu[cpunum];

		/* if we're suspended and counting, process */
		if (cpudata->suspend != 0 && cpudata->eatcycles && attotime_compare(cpudata->localtime, target) < 0)
		{
			attotime delta = attotime_sub(target, cpudata->localtime);

			/* compute how long to run */
			cycles_running = div_64x32(delta.attoseconds >> cpudata->divshift, cpudata->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)));
		}

		/* update the suspend state (breaks steeltal if we don't) */
		cpudata->suspend = cpudata->nextsuspend;
		cpudata->eatcycles = cpudata->nexteatcycles;
	}

	/* update the global time */
	timer_set_global_time(machine, target);
}



/***************************************************************************
    CPU SCHEDULING
***************************************************************************/

/*-------------------------------------------------
    cpu_boost_interleave - temporarily boosts the
    interleave factor
-------------------------------------------------*/

void cpu_boost_interleave(running_machine *machine, attotime timeslice_time, attotime boost_duration)
{
	/* ignore timeslices > 1 second */
	if (timeslice_time.seconds > 0)
		return;
	timer_add_scheduling_quantum(machine, timeslice_time.attoseconds, boost_duration);
}


/*-------------------------------------------------
    activecpu_abort_timeslice - abort execution
    for the current timeslice, allowing other
    CPUs to run before we run again
-------------------------------------------------*/

void activecpu_abort_timeslice(void)
{
	int current_icount;

	VERIFY_EXECUTINGCPU(activecpu_abort_timeslice);
	LOG(("activecpu_abort_timeslice (CPU=%d, cycles_left=%d)\n", cpu_getexecutingcpu(), 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);
}


/*-------------------------------------------------
    cpunum_suspend - set a suspend reason for the
    given CPU
-------------------------------------------------*/

void cpunum_suspend(int cpunum, 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 (cpu_getexecutingcpu() >= 0)
		activecpu_abort_timeslice();
}


/*-------------------------------------------------
    cpunum_resume - clear a suspend reason for the
    given CPU
-------------------------------------------------*/

void cpunum_resume(int cpunum, int reason)
{
	VERIFY_CPUNUM(cpunum_resume);
	LOG(("cpunum_resume (CPU=%d, r=%X)\n", cpunum, reason));
	cpu[cpunum].nextsuspend &= ~reason;
	if (cpu_getexecutingcpu() >= 0)
		activecpu_abort_timeslice();
}


/*-------------------------------------------------
    cpunum_is_suspended - returns true if the
    given CPU is suspended for any of the given
    reasons
-------------------------------------------------*/

int cpunum_is_suspended(int cpunum, int reason)
{
	VERIFY_CPUNUM(cpunum_suspend);
	return ((cpu[cpunum].nextsuspend & reason) != 0);
}



/***************************************************************************
    CPU CLOCK MANAGEMENT
***************************************************************************/

/*-------------------------------------------------
    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
    clock speed
-------------------------------------------------*/

int cpunum_get_clock(int cpunum)
{
	VERIFY_CPUNUM(cpunum_get_clock);
	return cpu[cpunum].clock;
}


/*-------------------------------------------------
    cpunum_set_clock - sets the given CPU's
    clock speed
-------------------------------------------------*/

void cpunum_set_clock(running_machine *machine, int cpunum, int clock)
{
	VERIFY_CPUNUM(cpunum_set_clock);

	cpu[cpunum].clock = clock;
	update_clock_information(machine, cpunum);
}


/*-------------------------------------------------
    cpunum_get_clockscale - returns the current
    scaling factor for a CPU's clock speed
-------------------------------------------------*/

double cpunum_get_clockscale(int cpunum)
{
	VERIFY_CPUNUM(cpunum_get_clockscale);
	return cpu[cpunum].clockscale;
}


/*-------------------------------------------------
    cpunum_set_clockscale - sets the current
    scaling factor for a CPU's clock speed
-------------------------------------------------*/

void cpunum_set_clockscale(running_machine *machine, int cpunum, double clockscale)
{
	VERIFY_CPUNUM(cpunum_set_clockscale);

	cpu[cpunum].clockscale = clockscale;
	update_clock_information(machine, cpunum);
}



/***************************************************************************
    CPU TIMING
***************************************************************************/

/*-------------------------------------------------
    cpunum_get_localtime - returns the current
    local time for a CPU
-------------------------------------------------*/

attotime cpunum_get_localtime(int cpunum)
{
	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 == cpu_getexecutingcpu())
	{
		int cycles = cycles_running - activecpu_get_icount();
		result = attotime_add(result, ATTOTIME_IN_CYCLES(cycles, cpunum));
	}
	return result;
}


/*-------------------------------------------------
    activecpu_gettotalcycles - return the total
    number of CPU cycles executed on the active
    CPU
-------------------------------------------------*/

UINT64 activecpu_gettotalcycles(void)
{
	VERIFY_ACTIVECPU(activecpu_gettotalcycles);
	if (activecpu == cpu_getexecutingcpu())
		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
-------------------------------------------------*/

UINT64 cpunum_gettotalcycles(int cpunum)
{
	VERIFY_CPUNUM(cpunum_gettotalcycles);
	if (cpunum == cpu_getexecutingcpu())
		return cpu[cpunum].totalcycles + cycles_running - activecpu_get_icount();
	else
		return cpu[cpunum].totalcycles;
}


/*-------------------------------------------------
    activecpu_eat_cycles - safely eats cycles so
    we don't cross a timeslice boundary
-------------------------------------------------*/

void activecpu_eat_cycles(int cycles)
{
	int cyclesleft = activecpu_get_icount();
	if (cycles > cyclesleft)
		cycles = cyclesleft;
	activecpu_adjust_icount(-cycles);
}



/***************************************************************************
    SYNCHRONIZATION HELPERS
***************************************************************************/

/*-------------------------------------------------
    cpu_suspend_until_trigger - suspend execution
    until the given trigger fires
-------------------------------------------------*/

static void cpunum_suspend_until_trigger(int cpunum, int trigger, int eatcycles)
{
	/* suspend the CPU immediately if it's not already */
	cpunum_suspend(cpunum, SUSPEND_REASON_TRIGGER, eatcycles);

	/* set the trigger */
	cpu[cpunum].trigger = trigger;
}


/*-------------------------------------------------
    cpu_yield - yield our current timeslice
-------------------------------------------------*/

void cpu_yield(void)
{
	int cpunum = cpu_getexecutingcpu();
	VERIFY_EXECUTINGCPU(cpu_yield);
	cpunum_suspend(cpunum, SUSPEND_REASON_TIMESLICE, FALSE);
}


/*-------------------------------------------------
    cpu_spin - burn CPU cycles until our timeslice
    is up
-------------------------------------------------*/

void cpu_spin(void)
{
	int cpunum = cpu_getexecutingcpu();
	VERIFY_EXECUTINGCPU(cpu_spin);
	cpunum_suspend(cpunum, SUSPEND_REASON_TIMESLICE, TRUE);
}


/*-------------------------------------------------
    cpu_spinuntil_trigger - burn CPU cycles until
    a timer trigger
-------------------------------------------------*/

void cpu_spinuntil_trigger(int trigger)
{
	int cpunum = cpu_getexecutingcpu();
	VERIFY_EXECUTINGCPU(cpu_yielduntil_trigger);
	cpunum_suspend_until_trigger(cpunum, trigger, TRUE);
}


/*-------------------------------------------------
    cpunum_spinuntil_trigger - burn specified CPU
    cycles until a timer trigger
-------------------------------------------------*/

void cpunum_spinuntil_trigger(int cpunum, int trigger)
{
	VERIFY_CPUNUM(cpunum_spinuntil_trigger);
	cpunum_suspend_until_trigger(cpunum, trigger, TRUE);
}


/*-------------------------------------------------
    cpu_spinuntil_int - burn CPU cycles until the
    next interrupt
-------------------------------------------------*/

void cpu_spinuntil_int(void)
{
	int cpunum = cpu_getexecutingcpu();
	VERIFY_EXECUTINGCPU(cpu_spinuntil_int);
	cpunum_suspend_until_trigger(cpunum, TRIGGER_INT + cpunum, TRUE);
}


/*-------------------------------------------------
    cpu_spinuntil_time - burn CPU cycles for a
    specific period of time
-------------------------------------------------*/

void cpu_spinuntil_time(attotime duration)
{
	static int timetrig = 0;
	int cpunum = cpu_getexecutingcpu();
	VERIFY_EXECUTINGCPU(cpu_spinuntil_time);
	cpunum_suspend_until_trigger(cpunum, TRIGGER_SUSPENDTIME + timetrig, TRUE);
	cpu_triggertime(duration, TRIGGER_SUSPENDTIME + timetrig);
	timetrig = (timetrig + 1) % 256;
}



/***************************************************************************
    TRIGGERS
***************************************************************************/

/*-------------------------------------------------
    cpu_trigger - generate a trigger now
-------------------------------------------------*/

void cpu_trigger(running_machine *machine, int trigger)
{
	int cpunum;

	/* cause an immediate resynchronization */
	if (cpu_getexecutingcpu() >= 0)
		activecpu_abort_timeslice();

	/* look for suspended CPUs waiting for this trigger and unsuspend them */
	for (cpunum = 0; cpunum < MAX_CPU; cpunum++)
	{
		/* if this is a dummy, stop looking */
		if (machine->config->cpu[cpunum].type == CPU_DUMMY)
			break;

		/* see if this is a matching trigger */
		if (cpu[cpunum].suspend && cpu[cpunum].trigger == trigger)
		{
			cpunum_resume(cpunum, SUSPEND_REASON_TRIGGER);
			cpu[cpunum].trigger = 0;
		}
	}
}


/*-------------------------------------------------
    cpu_triggertime - generate a trigger after a
    specific period of time
-------------------------------------------------*/

static TIMER_CALLBACK( cpu_triggertime_callback )
{
	cpu_trigger(machine, param);
}


void cpu_triggertime(attotime duration, int trigger)
{
	timer_set(duration, NULL, trigger, cpu_triggertime_callback);
}


/*-------------------------------------------------
    cpu_triggerint - generate a trigger
    corresponding to an interrupt on the given CPU
-------------------------------------------------*/

void cpu_triggerint(running_machine *machine, int cpunum)
{
	cpu_trigger(machine, TRIGGER_INT + cpunum);
}



/***************************************************************************
    CHEESY FAKE VIDEO TIMING
***************************************************************************/

/*-------------------------------------------------
    cpu_getiloops - return the cheesy VBLANK
    interrupt counter (deprecated)
-------------------------------------------------*/

int cpu_getiloops(void)
{
	VERIFY_ACTIVECPU(cpu_getiloops);
	return cpu[activecpu].iloops;
}


/***************************************************************************
    INTERNAL TIMING
***************************************************************************/

/*-------------------------------------------------
    on_vblank - calls any external callbacks
    for this screen
-------------------------------------------------*/

static void on_vblank(const device_config *device, void *param, int vblank_state)
{
	/* VBLANK starting */
	if (vblank_state)
	{
		int cpunum;

		/* 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;

			/* 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 (!cpunum_is_suspended(cpunum, SUSPEND_REASON_HALT | SUSPEND_REASON_RESET | SUSPEND_REASON_DISABLE))
				{
					cpuintrf_push_context(cpunum);
					(*config->vblank_interrupt)(device->machine, cpunum);
					cpuintrf_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) &&
					!(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);
				}
			}
		}
	}
}


/*-------------------------------------------------
    trigger_partial_frame_interrupt - called to
    trigger a partial frame interrupt
-------------------------------------------------*/

static TIMER_CALLBACK( trigger_partial_frame_interrupt )
{
	int cpunum = param;
	const cpu_config *config = machine->config->cpu + cpunum;

	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))
	{
		cpuintrf_push_context(cpunum);
		(*config->vblank_interrupt)(machine, cpunum);
		cpuintrf_pop_context();
	}

	/* more? */
	if (cpu[cpunum].iloops > 1)
		timer_adjust_oneshot(cpu[cpunum].partial_frame_timer, cpu[cpunum].partial_frame_period, cpunum);
}


/*-------------------------------------------------
    cpu_timedintcallback - timer callback for
    timed interrupts
-------------------------------------------------*/

static TIMER_CALLBACK( cpu_timedintcallback )
{
	/* 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))
	{
		cpuintrf_push_context(param);
		(*machine->config->cpu[param].timed_interrupt)(machine, param);
		cpuintrf_pop_context();
	}
}


/*-------------------------------------------------
    compute_perfect_interleave - compute the
    "perfect" interleave interval
-------------------------------------------------*/

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 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++)
			if (attoseconds_per_cycle[cpunum] != 0)
			{
				attoseconds_t curtime = attoseconds_per_cycle[cpunum] * cputype_min_cycles(machine->config->cpu[cpunum].type);

				/* find the 2nd smallest cycle interval */
				if (curtime < smallest)
				{
					perfect = smallest;
					smallest = curtime;
				}
				else if (curtime < perfect)
					perfect = attoseconds_per_cycle[cpunum];
			}

		/* adjust the final value */
		timer_set_minimum_quantum(machine, perfect);

		LOG(("Perfect interleave = %.9f, smallest = %.9f\n", ATTOSECONDS_TO_DOUBLE(perfect), ATTOSECONDS_TO_DOUBLE(smallest)));
	}
}


/*-------------------------------------------------
    cpu_inittimers - set up all the core timers
-------------------------------------------------*/

static void cpu_inittimers(running_machine *machine)
{
	int numscreens = video_screen_count(machine->config);
	attoseconds_t refresh_attosecs;
	int cpunum, ipf;

	/* 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);

	/* register the interrupt handler callbacks */
	for (cpunum = 0; cpunum < cpu_gettotalcpu(); cpunum++)
	{
		const cpu_config *config = machine->config->cpu + cpunum;

		/* 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);

				/* allocate timer that will trigger the partial frame updates */
				cpu[cpunum].partial_frame_timer = timer_alloc(trigger_partial_frame_interrupt, 0);
			}

			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);
			cpu[cpunum].timedint_timer = timer_alloc(cpu_timedintcallback, NULL);
			timer_adjust_periodic(cpu[cpunum].timedint_timer, timedint_period, cpunum, timedint_period);
		}
	}
}