summaryrefslogblamecommitdiffstatshomepage
path: root/src/emu/diexec.cpp
blob: b92a5a8b31cfe134f1ac8edbfa708517a7eca667 (plain) (tree)
1
2
3
4
5
6
7
8

                                





                                                                            


                                                                            








                                                                            
                                                                             
 
                 
 





                                                                            

                                        







                                                                            
                                          

                                                   
                                                                                                   
                                              
                                  
                                                   
                                                         

                                          
                                          
                                     












                                          
 


                                                     
                                             



                                                   
                                         

                                                   
                                                     




                                                   
                                                


                                                   
                                                                   
 

                                       
                                                                                                                          




                                                   
                                                


                                                   
                                                                                                                                     


                                       
                                                                                                                             
                                            




                                                   
                                                  


                                                   
                                                                                                                                  


                                       
                                                                                                                               





                                                   







                                                                                                                              
                                                                                                                                  
                                      



                                                   

                                             

                                                   
                                                
 
                                                            









                                                        
                                              



                                                   
                                               





                                                     
                         


                                                


                                      










                                                       
                         

                       
                                   
                              











                                                   
                         


                                       
                                   
         
                                         

                                          
                                      




                                                   





                                                       
                                              





                                       
                                                   




                                                                     
                                                                 


                                                     
                                 



                                                   
                                            




                                                    
                                                                

                                                       
                                 



                                                   
                                                


                                                   
                                                                        






                                                                    
                                                                                                                                                                    












































                                                                                 
                        
         

                                                             
                                                                
         
                           



                                                   
                                             




                                                     
                        
         

                                                                       
         





                                                   



































































                                                                              

                                                  




















                                                                        




                                                   
                                                                                      
 
                                  
                                         
         
                                                                              
                                            
                                                                                                       

                                                                                                                                    

         
                                                                                      
                                                                                      
                                                                                          
                                                                                                         



                                                   





                                                    

                                                      
                         

                                                               
                                                         
 
                                     
                                                                                     
                                         
                                                                 

                                           
                                          
                                                       
                                                                                                                                                      










                                                     
                                                                                












                                                                











                                                    
 
                                                           
                        













                                                      

                                   

                                         
                                                 

                                                              
                                                                                                                                                     
 
                                          
                                                                                                                         


                                          
                                                       
         
                                                                    
                                                    
                                                                              










                                                        










                                                                      
                                
                                                                 












                                                                         
                                                  



                                                   
                                                  



                                                   
                                                                             
 
                                                               





                                                                         
 
                                                                                                                   

                                                                 
                                   
                                                        

                              
                                                    






                                                   



                                                               




                                                         

                                                         
                                                                                 










                                                   
                                                                         
 
                       







                                                   
                                                                                  
 


                            
 
                                          
                                                                                                                   
         
                                                 

                                                     

 
 
                                                   
                                      


                                                   
                                                                           

                                      
                                                                                                                   
         
                                                

                                                    












                                                                            
                             




                                       












                                                                                                  
                            
 

                



                                                           




















                                                                                    
                                                                                                          
 
                                                                                                                                       

                                                                                                         




                                                                                             
                                                                                                                                              










                                                                     
                                             
                                         
                                                                                                                                     










                                                                      
                                                                                                                                                                  







                                                   
                                                                                
 
                                                                                               







                                                               
                                                           










                                                                                                         
                                                                            
                         
                                                            































                                                                                             
                                                                                                                                                                                     














                                                                                               
                                          





                                                                  
 


                                                  
                                                                                                        




                                                                    
// license:BSD-3-Clause
// copyright-holders:Aaron Giles
/***************************************************************************

    diexec.c

    Device execution interfaces.

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

#include "emu.h"
#include "debugger.h"


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

#define VERBOSE 0

#define LOG(x)  do { if (VERBOSE) m_execute->device().logerror x; } while (0)

#define TEMPLOG 0



//**************************************************************************
//  CONSTANTS
//**************************************************************************

const int TRIGGER_INT           = -2000;
const int TRIGGER_SUSPENDTIME   = -4000;



//**************************************************************************
//  EXECUTING DEVICE CONFIG
//**************************************************************************

//-------------------------------------------------
//  device_execute_interface - constructor
//-------------------------------------------------

device_execute_interface::device_execute_interface(const machine_config &mconfig, device_t &device)
	: device_interface(device, "execute"),
		m_disabled(false),
		m_vblank_interrupt_screen(nullptr),
		m_timed_interrupt_period(attotime::zero),
		m_nextexec(nullptr),
		m_timedint_timer(nullptr),
		m_profiler(PROFILER_IDLE),
		m_icountptr(nullptr),
		m_cycles_running(0),
		m_cycles_stolen(0),
		m_suspend(0),
		m_nextsuspend(0),
		m_eatcycles(0),
		m_nexteatcycles(0),
		m_trigger(0),
		m_inttrigger(0),
		m_totalcycles(0),
		m_divisor(0),
		m_divshift(0),
		m_cycles_per_second(0),
		m_attoseconds_per_cycle(0)
{
	memset(&m_localtime, 0, sizeof(m_localtime));

	// configure the fast accessor
	device.interfaces().m_execute = this;
}


//-------------------------------------------------
//  device_execute_interface - destructor
//-------------------------------------------------

device_execute_interface::~device_execute_interface()
{
}


//-------------------------------------------------
//  static_set_disable - configuration helper to
//  set the disabled state of a device
//-------------------------------------------------

void device_execute_interface::static_set_disable(device_t &device)
{
	device_execute_interface *exec;
	if (!device.interface(exec))
		throw emu_fatalerror("MCFG_DEVICE_DISABLE called on device '%s' with no execute interface", device.tag());
	exec->m_disabled = true;
}


//-------------------------------------------------
//  static_set_vblank_int - configuration helper
//  to set up VBLANK interrupts on the device
//-------------------------------------------------

void device_execute_interface::static_set_vblank_int(device_t &device, device_interrupt_delegate function, const char *tag, int rate)
{
	device_execute_interface *exec;
	if (!device.interface(exec))
		throw emu_fatalerror("MCFG_DEVICE_VBLANK_INT called on device '%s' with no execute interface", device.tag());
	exec->m_vblank_interrupt = function;
	exec->m_vblank_interrupt_screen = tag;
}


//-------------------------------------------------
//  static_set_periodic_int - configuration helper
//  to set up periodic interrupts on the device
//-------------------------------------------------

void device_execute_interface::static_set_periodic_int(device_t &device, device_interrupt_delegate function, const attotime &rate)
{
	device_execute_interface *exec;
	if (!device.interface(exec))
		throw emu_fatalerror("MCFG_DEVICE_PERIODIC_INT called on device '%s' with no execute interface", device.tag());
	exec->m_timed_interrupt = function;
	exec->m_timed_interrupt_period = rate;
}


//-------------------------------------------------
//  set_irq_acknowledge_callback - configuration helper
//  to setup callback for IRQ acknowledge
//-------------------------------------------------

void device_execute_interface::static_set_irq_acknowledge_callback(device_t &device, device_irq_acknowledge_delegate callback)
{
	device_execute_interface *exec;
	if (!device.interface(exec))
		throw emu_fatalerror("MCFG_DEVICE_IRQ_ACKNOWLEDGE called on device '%s' with no execute interface", device.tag());
	exec->m_driver_irq = callback;
}


//-------------------------------------------------
//  executing - return true if this device is
//  within its execute function
//-------------------------------------------------

bool device_execute_interface::executing() const
{
	return (this == m_scheduler->currently_executing());
}


//-------------------------------------------------
//  cycles_remaining - return the number of cycles
//  remaining in this timeslice
//-------------------------------------------------

INT32 device_execute_interface::cycles_remaining() const
{
	return executing() ? *m_icountptr : 0;
}


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

void device_execute_interface::eat_cycles(int cycles)
{
	// ignore if not the executing device
	if (!executing())
		return;

	// clamp cycles to the icount and update
	if (cycles > *m_icountptr)
		cycles = *m_icountptr;
	*m_icountptr -= cycles;
}


//-------------------------------------------------
//  adjust_icount - apply a +/- to the current
//  icount
//-------------------------------------------------

void device_execute_interface::adjust_icount(int delta)
{
	// ignore if not the executing device
	if (!executing())
		return;

	// apply the delta directly
	*m_icountptr += delta;
}


//-------------------------------------------------
//  abort_timeslice - abort execution for the
//  current timeslice, allowing other devices to
//  run before we run again
//-------------------------------------------------

void device_execute_interface::abort_timeslice()
{
	// ignore if not the executing device
	if (!executing())
		return;

	// swallow the remaining cycles
	if (m_icountptr != nullptr)
	{
		int delta = *m_icountptr;
		m_cycles_stolen += delta;
		m_cycles_running -= delta;
		*m_icountptr -= delta;
	}
}


//-------------------------------------------------
//  suspend_resume_changed
//-------------------------------------------------

void device_execute_interface::suspend_resume_changed()
{
	// inform the scheduler
	m_scheduler->suspend_resume_changed();

	// if we're active, synchronize
	abort_timeslice();
}


//-------------------------------------------------
//  suspend - set a suspend reason for this device
//-------------------------------------------------

void device_execute_interface::suspend(UINT32 reason, bool eatcycles)
{
if (TEMPLOG) printf("suspend %s (%X)\n", device().tag(), reason);
	// set the suspend reason and eat cycles flag
	m_nextsuspend |= reason;
	m_nexteatcycles = eatcycles;
	suspend_resume_changed();
}


//-------------------------------------------------
//  resume - clear a suspend reason for this
//  device
//-------------------------------------------------

void device_execute_interface::resume(UINT32 reason)
{
if (TEMPLOG) printf("resume %s (%X)\n", device().tag(), reason);
	// clear the suspend reason and eat cycles flag
	m_nextsuspend &= ~reason;
	suspend_resume_changed();
}


//-------------------------------------------------
//  spin_until_time - burn cycles for a specific
//  period of time
//-------------------------------------------------

void device_execute_interface::spin_until_time(const attotime &duration)
{
	static int timetrig = 0;

	// suspend until the given trigger fires
	suspend_until_trigger(TRIGGER_SUSPENDTIME + timetrig, true);

	// then set a timer for it
	m_scheduler->timer_set(duration, timer_expired_delegate(FUNC(device_execute_interface::timed_trigger_callback),this), TRIGGER_SUSPENDTIME + timetrig, this);
	timetrig = (timetrig + 1) % 256;
}


//-------------------------------------------------
//  suspend_until_trigger - suspend execution
//  until the given trigger fires
//-------------------------------------------------

void device_execute_interface::suspend_until_trigger(int trigid, bool eatcycles)
{
	// suspend the device immediately if it's not already
	suspend(SUSPEND_REASON_TRIGGER, eatcycles);

	// set the trigger
	m_trigger = trigid;
}


//-------------------------------------------------
//  trigger - respond to a trigger event
//-------------------------------------------------

void device_execute_interface::trigger(int trigid)
{
	// if we're executing, for an immediate abort
	abort_timeslice();

	// see if this is a matching trigger
	if ((m_nextsuspend & SUSPEND_REASON_TRIGGER) != 0 && m_trigger == trigid)
	{
		resume(SUSPEND_REASON_TRIGGER);
		m_trigger = 0;
	}
}


//-------------------------------------------------
//  local_time - returns the current local time
//  for a device
//-------------------------------------------------

attotime device_execute_interface::local_time() const
{
	// if we're active, add in the time from the current slice
	if (executing())
	{
		assert(m_cycles_running >= *m_icountptr);
		int cycles = m_cycles_running - *m_icountptr;
		return m_localtime + cycles_to_attotime(cycles);
	}
	return m_localtime;
}


//-------------------------------------------------
//  total_cycles - return the total number of
//  cycles executed on this device
//-------------------------------------------------

UINT64 device_execute_interface::total_cycles() const
{
	if (executing())
	{
		assert(m_cycles_running >= *m_icountptr);
		return m_totalcycles + m_cycles_running - *m_icountptr;
	}
	else
		return m_totalcycles;
}


//-------------------------------------------------
//  execute_clocks_to_cycles - convert the number
//  of clocks to cycles, rounding down if necessary
//-------------------------------------------------

UINT64 device_execute_interface::execute_clocks_to_cycles(UINT64 clocks) const
{
	return clocks;
}


//-------------------------------------------------
//  execute_cycles_to_clocks - convert the number
//  of cycles to clocks, rounding down if necessary
//-------------------------------------------------

UINT64 device_execute_interface::execute_cycles_to_clocks(UINT64 cycles) const
{
	return cycles;
}


//-------------------------------------------------
//  execute_min_cycles - return the smallest number
//  of cycles that a single instruction or
//  operation can take
//-------------------------------------------------

UINT32 device_execute_interface::execute_min_cycles() const
{
	return 1;
}


//-------------------------------------------------
//  execute_max_cycles - return the maximum number
//  of cycles that a single instruction or
//  operation can take
//-------------------------------------------------

UINT32 device_execute_interface::execute_max_cycles() const
{
	return 1;
}


//-------------------------------------------------
//  execute_input_lines - return the total number
//  of input lines for the device
//-------------------------------------------------

UINT32 device_execute_interface::execute_input_lines() const
{
	return 0;
}


//-------------------------------------------------
//  execute_default_irq_vector - return the default
//  IRQ vector when an acknowledge is processed
//-------------------------------------------------

UINT32 device_execute_interface::execute_default_irq_vector() const
{
	return 0;
}


//-------------------------------------------------
//  execute_burn - called after we consume a bunch
//  of cycles for artifical reasons (such as
//  spinning devices for performance optimization)
//-------------------------------------------------

void device_execute_interface::execute_burn(INT32 cycles)
{
	// by default, do nothing
}


//-------------------------------------------------
//  execute_set_input - called when a synchronized
//  input is changed
//-------------------------------------------------

void device_execute_interface::execute_set_input(int linenum, int state)
{
	// by default, do nothing
}


//-------------------------------------------------
//  interface_validity_check - validation for a
//  device after the configuration has been
//  constructed
//-------------------------------------------------

void device_execute_interface::interface_validity_check(validity_checker &valid) const
{
	// validate the interrupts
	if (!m_vblank_interrupt.isnull())
	{
		screen_device_iterator iter(device().mconfig().root_device());
		if (iter.first() == nullptr)
			osd_printf_error("VBLANK interrupt specified, but the driver is screenless\n");
		else if (m_vblank_interrupt_screen != nullptr && device().siblingdevice(m_vblank_interrupt_screen) == nullptr)
			osd_printf_error("VBLANK interrupt references a non-existant screen tag '%s'\n", m_vblank_interrupt_screen);
	}

	if (!m_timed_interrupt.isnull() && m_timed_interrupt_period == attotime::zero)
		osd_printf_error("Timed interrupt handler specified with 0 period\n");
	else if (m_timed_interrupt.isnull() && m_timed_interrupt_period != attotime::zero)
		osd_printf_error("No timer interrupt handler specified, but has a non-0 period given\n");
}


//-------------------------------------------------
//  interface_pre_start - work to be done prior to
//  actually starting a device
//-------------------------------------------------

void device_execute_interface::interface_pre_start()
{
	m_scheduler = &device().machine().scheduler();

	// bind delegates
	m_vblank_interrupt.bind_relative_to(*device().owner());
	m_timed_interrupt.bind_relative_to(*device().owner());
	m_driver_irq.bind_relative_to(*device().owner());

	// fill in the initial states
	int index = device_iterator(device().machine().root_device()).indexof(*this);
	m_suspend = SUSPEND_REASON_RESET;
	m_profiler = profile_type(index + PROFILER_DEVICE_FIRST);
	m_inttrigger = index + TRIGGER_INT;

	// allocate timers if we need them
	if (m_timed_interrupt_period != attotime::zero)
		m_timedint_timer = m_scheduler->timer_alloc(timer_expired_delegate(FUNC(device_execute_interface::trigger_periodic_interrupt), this));
}


//-------------------------------------------------
//  interface_post_start - work to be done after
//  actually starting a device
//-------------------------------------------------

void device_execute_interface::interface_post_start()
{
	// make sure somebody set us up the icount
	assert_always(m_icountptr != nullptr, "m_icountptr never initialized!");

	// register for save states
	device().save_item(NAME(m_suspend));
	device().save_item(NAME(m_nextsuspend));
	device().save_item(NAME(m_eatcycles));
	device().save_item(NAME(m_nexteatcycles));
	device().save_item(NAME(m_trigger));
	device().save_item(NAME(m_totalcycles));
	device().save_item(NAME(m_localtime));

	// fill in the input states and IRQ callback information
	for (int line = 0; line < ARRAY_LENGTH(m_input); line++)
		m_input[line].start(this, line);
}


//-------------------------------------------------
//  interface_pre_reset - work to be done prior to
//  actually resetting a device
//-------------------------------------------------

void device_execute_interface::interface_pre_reset()
{
	// reset the total number of cycles
	m_totalcycles = 0;

	// enable all devices (except for disabled devices)
	if (!disabled())
		resume(SUSPEND_ANY_REASON);
	else
		suspend(SUSPEND_REASON_DISABLE, true);
}


//-------------------------------------------------
//  interface_post_reset - work to be done after a
//  device is reset
//-------------------------------------------------

void device_execute_interface::interface_post_reset()
{
	// reset the interrupt vectors and queues
	for (auto & elem : m_input)
		elem.reset();

	// reconfingure VBLANK interrupts
	if (m_vblank_interrupt_screen != nullptr)
	{
		// get the screen that will trigger the VBLANK
		screen_device *screen = downcast<screen_device *>(device().machine().device(device().siblingtag(m_vblank_interrupt_screen).c_str()));

		assert(screen != nullptr);
		screen->register_vblank_callback(vblank_state_delegate(FUNC(device_execute_interface::on_vblank), this));
	}

	// reconfigure periodic interrupts
	if (m_timed_interrupt_period != attotime::zero)
	{
		attotime timedint_period = m_timed_interrupt_period;
		assert(m_timedint_timer != nullptr);
		m_timedint_timer->adjust(timedint_period, 0, timedint_period);
	}
}


//-------------------------------------------------
//  interface_clock_changed - recomputes clock
//  information for this device
//-------------------------------------------------

void device_execute_interface::interface_clock_changed()
{
	// a clock of zero disables the device
	if (device().clock() == 0)
	{
		suspend(SUSPEND_REASON_CLOCK, true);
		return;
	}

	// if we were suspended because we had no clock, enable us now
	if (suspended(SUSPEND_REASON_CLOCK))
		resume(SUSPEND_REASON_CLOCK);

	// recompute cps and spc
	m_cycles_per_second = clocks_to_cycles(device().clock());
	m_attoseconds_per_cycle = HZ_TO_ATTOSECONDS(m_cycles_per_second);

	// update the device's divisor
	INT64 attos = m_attoseconds_per_cycle;
	m_divshift = 0;
	while (attos >= (1UL << 31))
	{
		m_divshift++;
		attos >>= 1;
	}
	m_divisor = attos;

	// re-compute the perfect interleave factor
	m_scheduler->compute_perfect_interleave();
}


//-------------------------------------------------
//  standard_irq_callback_member - IRQ acknowledge
//  callback; handles HOLD_LINE case and signals
//  to the debugger
//-------------------------------------------------

IRQ_CALLBACK_MEMBER( device_execute_interface::standard_irq_callback_member )
{
	return device.execute().standard_irq_callback(irqline);
}

int device_execute_interface::standard_irq_callback(int irqline)
{
	// get the default vector and acknowledge the interrupt if needed
	int vector = m_input[irqline].default_irq_callback();

	if (VERBOSE) device().logerror("standard_irq_callback('%s', %d) $%04x\n", device().tag(), irqline, vector);

	// if there's a driver callback, run it to get the vector
	if (!m_driver_irq.isnull())
		vector = m_driver_irq(device(),irqline);

	// notify the debugger
	debugger_interrupt_hook(&device(), irqline);
	return vector;
}


//-------------------------------------------------
//  minimum_quantum - return the minimum quantum
//  required for this device
//-------------------------------------------------

attoseconds_t device_execute_interface::minimum_quantum() const
{
	// if we don't have a clock, return a huge factor
	if (device().clock() == 0)
		return ATTOSECONDS_PER_SECOND - 1;

	// if we don't have the quantum time, compute it
	attoseconds_t basetick = m_attoseconds_per_cycle;
	if (basetick == 0)
		basetick = HZ_TO_ATTOSECONDS(clocks_to_cycles(device().clock()));

	// apply the minimum cycle count
	return basetick * min_cycles();
}


//-------------------------------------------------
//  static_timed_trigger_callback - signal a timed
//  trigger
//-------------------------------------------------

TIMER_CALLBACK_MEMBER( device_execute_interface::timed_trigger_callback )
{
	trigger(param);
}


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

void device_execute_interface::on_vblank(screen_device &screen, bool vblank_state)
{
	// ignore VBLANK end
	if (!vblank_state)
		return;

	// generate the interrupt callback
	if (!suspended(SUSPEND_REASON_HALT | SUSPEND_REASON_RESET | SUSPEND_REASON_DISABLE | SUSPEND_REASON_CLOCK))
	{
		if (!m_vblank_interrupt.isnull())
			m_vblank_interrupt(device());
	}
}


//-------------------------------------------------
//  trigger_periodic_interrupt - timer
//  callback for timed interrupts
//-------------------------------------------------

TIMER_CALLBACK_MEMBER(device_execute_interface::trigger_periodic_interrupt)
{
	// bail if there is no routine
	if (!suspended(SUSPEND_REASON_HALT | SUSPEND_REASON_RESET | SUSPEND_REASON_DISABLE | SUSPEND_REASON_CLOCK))
	{
		if (!m_timed_interrupt.isnull())
			m_timed_interrupt(device());
	}
}



//**************************************************************************
//  DEVICE INPUT
//**************************************************************************

//-------------------------------------------------
//  device_input - constructor
//-------------------------------------------------

device_execute_interface::device_input::device_input()
	: m_execute(nullptr),
		m_linenum(0),
		m_stored_vector(0),
		m_curvector(0),
		m_curstate(CLEAR_LINE),
		m_qindex(0)
{
	memset(m_queue, 0, sizeof(m_queue));
}


//-------------------------------------------------
//  start - called by interface_pre_start so we
//  can set ourselves up
//-------------------------------------------------

void device_execute_interface::device_input::start(device_execute_interface *execute, int linenum)
{
	m_execute = execute;
	m_linenum = linenum;

	reset();

	device_t &device = m_execute->device();
	device.save_item(NAME(m_stored_vector), m_linenum);
	device.save_item(NAME(m_curvector), m_linenum);
	device.save_item(NAME(m_curstate), m_linenum);
}


//-------------------------------------------------
//  reset - reset our input states
//-------------------------------------------------

void device_execute_interface::device_input::reset()
{
	m_curvector = m_stored_vector = m_execute->default_irq_vector();
	m_qindex = 0;
}


//-------------------------------------------------
//  set_state_synced - enqueue an event for later
//  execution via timer
//-------------------------------------------------

void device_execute_interface::device_input::set_state_synced(int state, int vector)
{
	LOG(("set_state_synced('%s',%d,%d,%02x)\n", m_execute->device().tag(), m_linenum, state, vector));

if (TEMPLOG) printf("setline(%s,%d,%d,%d)\n", m_execute->device().tag(), m_linenum, state, (vector == USE_STORED_VECTOR) ? 0 : vector);
	assert(state == ASSERT_LINE || state == HOLD_LINE || state == CLEAR_LINE || state == PULSE_LINE);

	// treat PULSE_LINE as ASSERT+CLEAR
	if (state == PULSE_LINE)
	{
		// catch errors where people use PULSE_LINE for devices that don't support it
		if (m_linenum != INPUT_LINE_NMI && m_linenum != INPUT_LINE_RESET)
			throw emu_fatalerror("device '%s': PULSE_LINE can only be used for NMI and RESET lines\n", m_execute->device().tag());

		set_state_synced(ASSERT_LINE, vector);
		set_state_synced(CLEAR_LINE, vector);
		return;
	}

	// if we're full of events, flush the queue and log a message
	int event_index = m_qindex++;
	if (event_index >= ARRAY_LENGTH(m_queue))
	{
		m_qindex--;
		empty_event_queue(nullptr,0);
		event_index = m_qindex++;
		m_execute->device().logerror("Exceeded pending input line event queue on device '%s'!\n", m_execute->device().tag());
	}

	// enqueue the event
	if (event_index < ARRAY_LENGTH(m_queue))
	{
		if (vector == USE_STORED_VECTOR)
			vector = m_stored_vector;
		m_queue[event_index] = (state & 0xff) | (vector << 8);

		// if this is the first one, set the timer
		if (event_index == 0)
			m_execute->scheduler().synchronize(timer_expired_delegate(FUNC(device_execute_interface::device_input::empty_event_queue),this), 0, this);
	}
}


//-------------------------------------------------
//  empty_event_queue - empty our event queue
//-------------------------------------------------

TIMER_CALLBACK_MEMBER(device_execute_interface::device_input::empty_event_queue)
{
if (TEMPLOG) printf("empty_queue(%s,%d,%d)\n", m_execute->device().tag(), m_linenum, m_qindex);
	// loop over all events
	for (int curevent = 0; curevent < m_qindex; curevent++)
	{
		INT32 input_event = m_queue[curevent];

		// set the input line state and vector
		m_curstate = input_event & 0xff;
		m_curvector = input_event >> 8;
if (TEMPLOG) printf(" (%d,%d)\n", m_curstate, m_curvector);

		assert(m_curstate == ASSERT_LINE || m_curstate == HOLD_LINE || m_curstate == CLEAR_LINE);

		// special case: RESET
		if (m_linenum == INPUT_LINE_RESET)
		{
			// if we're asserting the line, just halt the device
			if (m_curstate == ASSERT_LINE)
				m_execute->suspend(SUSPEND_REASON_RESET, true);

			// if we're clearing the line that was previously asserted, reset the device
			else if (m_execute->suspended(SUSPEND_REASON_RESET))
			{
				m_execute->device().reset();
				m_execute->resume(SUSPEND_REASON_RESET);
			}
		}

		// special case: HALT
		else if (m_linenum == INPUT_LINE_HALT)
		{
			// if asserting, halt the device
			if (m_curstate == ASSERT_LINE)
				m_execute->suspend(SUSPEND_REASON_HALT, true);

			// if clearing, unhalt the device
			else if (m_curstate == CLEAR_LINE)
				m_execute->resume(SUSPEND_REASON_HALT);
		}

		// all other cases
		else
		{
			// switch off the requested state
			switch (m_curstate)
			{
				case HOLD_LINE:
				case ASSERT_LINE:
					m_execute->execute_set_input(m_linenum, ASSERT_LINE);
					break;

				case CLEAR_LINE:
					m_execute->execute_set_input(m_linenum, CLEAR_LINE);
					break;

				default:
					m_execute->device().logerror("empty_event_queue device '%s', line %d, unknown state %d\n", m_execute->device().tag(), m_linenum, m_curstate);
					break;
			}

			// generate a trigger to unsuspend any devices waiting on the interrupt
			if (m_curstate != CLEAR_LINE)
				m_execute->signal_interrupt_trigger();
		}
	}

	// reset counter
	m_qindex = 0;
}


//-------------------------------------------------
//  default_irq_callback - the default IRQ
//  callback for this input line
//-------------------------------------------------

int device_execute_interface::device_input::default_irq_callback()
{
	int vector = m_curvector;

	// if the IRQ state is HOLD_LINE, clear it
	if (m_curstate == HOLD_LINE)
	{
		LOG(("->set_irq_line('%s',%d,%d)\n", m_execute->device().tag(), m_linenum, CLEAR_LINE));
		m_execute->execute_set_input(m_linenum, CLEAR_LINE);
		m_curstate = CLEAR_LINE;
	}
	return vector;
}