diff options
Diffstat (limited to 'trunk/src/emu/diexec.c')
-rw-r--r-- | trunk/src/emu/diexec.c | 958 |
1 files changed, 958 insertions, 0 deletions
diff --git a/trunk/src/emu/diexec.c b/trunk/src/emu/diexec.c new file mode 100644 index 00000000000..1d9f1732150 --- /dev/null +++ b/trunk/src/emu/diexec.c @@ -0,0 +1,958 @@ +/*************************************************************************** + + diexec.c + + Device execution interfaces. + +**************************************************************************** + + Copyright Aaron Giles + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name 'MAME' nor the names of its contributors may be + used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +***************************************************************************/ + +#include "emu.h" +#include "debugger.h" + + +//************************************************************************** +// DEBUGGING +//************************************************************************** + +#define VERBOSE 0 + +#define LOG(x) do { if (VERBOSE) 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), +// m_cothread(cothread_entry_delegate(FUNC(device_execute_interface::run_thread_wrapper), this)), + m_disabled(false), + m_vblank_interrupt_legacy(NULL), + m_vblank_interrupt_screen(NULL), + m_timed_interrupt_legacy(NULL), + m_timed_interrupt_period(attotime::zero), + m_is_octal(false), + m_nextexec(NULL), + m_driver_irq(0), + m_timedint_timer(NULL), + m_profiler(PROFILER_IDLE), + m_icountptr(NULL), + 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.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_func 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_legacy = function; + exec->m_vblank_interrupt_screen = tag; +} + +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_legacy = NULL; + 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_func function, 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_legacy = function; + exec->m_timed_interrupt_period = rate; +} + +void device_execute_interface::static_set_periodic_int(device_t &device, device_interrupt_delegate function, 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_legacy = NULL; + exec->m_timed_interrupt_period = rate; +} + + +//------------------------------------------------- +// executing - return true if this device is +// within its execute function +//------------------------------------------------- + +bool device_execute_interface::executing() const +{ + return (this == device().machine().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; + + // aply 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 (this != device().machine().scheduler().currently_executing()) + return; + + // swallow the remaining cycles + if (m_icountptr != NULL) + { + int delta = *m_icountptr; + m_cycles_stolen += delta; + m_cycles_running -= delta; + *m_icountptr -= delta; + } +} + + +//------------------------------------------------- +// set_irq_acknowledge_callback - install a driver-specific +// callback for IRQ acknowledge +//------------------------------------------------- + +void device_execute_interface::set_irq_acknowledge_callback(device_irq_acknowledge_callback callback) +{ + m_driver_irq = callback; +} + + +//------------------------------------------------- +// 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; + + // if we're active, synchronize + abort_timeslice(); +} + + +//------------------------------------------------- +// 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; + + // if we're active, synchronize + abort_timeslice(); +} + + +//------------------------------------------------- +// spin_until_time - burn cycles for a specific +// period of time +//------------------------------------------------- + +void device_execute_interface::spin_until_time(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 + device().machine().scheduler().timer_set(duration, FUNC(static_timed_trigger_callback), 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 + attotime result = m_localtime; + if (executing()) + { + assert(m_cycles_running >= *m_icountptr); + int cycles = m_cycles_running - *m_icountptr; + result += cycles_to_attotime(cycles); + } + return result; +} + + +//------------------------------------------------- +// 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; +} + + +//------------------------------------------------- +// run_thread_wrapper - wrapper for our cothread +// which just calls run and then returns to the +// scheduler thread, over and over +//------------------------------------------------- +/* +void device_execute_interface::run_thread_wrapper() +{ + // loop infinitely + device_scheduler &scheduler = device().machine().scheduler(); + while (1) + { + // call the classic run function, then swap back to the scheduler's thread + execute_run(); + scheduler.make_active(); + } +} +*/ + +//------------------------------------------------- +// 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() || m_vblank_interrupt_legacy != NULL) + { + screen_device_iterator iter(device().mconfig().root_device()); + if (iter.first() == NULL) + mame_printf_error("VBLANK interrupt specified, but the driver is screenless\n"); + else if (m_vblank_interrupt_screen != NULL && device().siblingdevice(m_vblank_interrupt_screen) == NULL) + mame_printf_error("VBLANK interrupt references a non-existant screen tag '%s'\n", m_vblank_interrupt_screen); + } + + if ((!m_timed_interrupt.isnull() || m_timed_interrupt_legacy != NULL) && m_timed_interrupt_period == attotime::zero) + mame_printf_error("Timed interrupt handler specified with 0 period\n"); + else if ((m_timed_interrupt.isnull() && m_timed_interrupt_legacy == NULL) && m_timed_interrupt_period != attotime::zero) + mame_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() +{ + // bind delegates + m_vblank_interrupt.bind_relative_to(device()); + m_timed_interrupt.bind_relative_to(device()); +// m_driver_irq.bind_relative_to(device()); + + // fill in the initial states + execute_interface_iterator iter(device().machine().root_device()); + int index = iter.indexof(*this); + m_suspend = SUSPEND_REASON_RESET; + m_profiler = profile_type(index + PROFILER_DEVICE_FIRST); + m_inttrigger = index + TRIGGER_INT; + + // 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); + + // allocate timers if we need them + if (m_timed_interrupt_period != attotime::zero) + m_timedint_timer = device().machine().scheduler().timer_alloc(FUNC(static_trigger_periodic_interrupt), (void *)this); + + // 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)); +} + + +//------------------------------------------------- +// 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 != NULL, "m_icountptr never initialized!"); +} + + +//------------------------------------------------- +// 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 (int line = 0; line < ARRAY_LENGTH(m_input); line++) + m_input[line].reset(); + + // reconfingure VBLANK interrupts + if (m_vblank_interrupt_screen != NULL) + { + // get the screen that will trigger the VBLANK + + // new style - use screen tag directly + screen_device *screen; + if (m_vblank_interrupt_screen != NULL) + screen = downcast<screen_device *>(device().machine().device(m_vblank_interrupt_screen)); + + // old style 'hack' setup - use screen #0 + else + screen = device().machine().first_screen(); + + assert(screen != NULL); + 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 != NULL); + 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() +{ + // 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 + device().machine().scheduler().compute_perfect_interleave(); +} + + +//------------------------------------------------- +// static_standard_irq_callback - IRQ acknowledge +// callback; handles HOLD_LINE case and signals +// to the debugger +//------------------------------------------------- + +IRQ_CALLBACK( device_execute_interface::static_standard_irq_callback ) +{ + return device_execute(device)->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(); + LOG(("static_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 != NULL) + 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 that information, 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( device_execute_interface::static_timed_trigger_callback ) +{ + device_execute_interface *device = reinterpret_cast<device_execute_interface *>(ptr); + device->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)) + { + if (m_vblank_interrupt_legacy != NULL) + (*m_vblank_interrupt_legacy)(&device()); + else if (!m_vblank_interrupt.isnull()) + m_vblank_interrupt(device()); + } +} + + +//------------------------------------------------- +// static_trigger_periodic_interrupt - timer +// callback for timed interrupts +//------------------------------------------------- + +TIMER_CALLBACK( device_execute_interface::static_trigger_periodic_interrupt ) +{ + reinterpret_cast<device_execute_interface *>(ptr)->trigger_periodic_interrupt(); +} + +void device_execute_interface::trigger_periodic_interrupt() +{ + // bail if there is no routine + if (!suspended(SUSPEND_REASON_HALT | SUSPEND_REASON_RESET | SUSPEND_REASON_DISABLE)) + { + if (m_timed_interrupt_legacy != NULL) + (*m_timed_interrupt_legacy)(&device()); + else if (!m_timed_interrupt.isnull()) + m_timed_interrupt(device()); + } +} + + + +//************************************************************************** +// DEVICE INPUT +//************************************************************************** + +//------------------------------------------------- +// device_input - constructor +//------------------------------------------------- + +device_execute_interface::device_input::device_input() + : m_execute(NULL), + m_device(NULL), + 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_device = &m_execute->device(); + m_linenum = linenum; + + reset(); + + m_device->save_item(NAME(m_stored_vector), m_linenum); + m_device->save_item(NAME(m_curvector), m_linenum); + m_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_device->tag(), m_linenum, state, vector)); + +if (TEMPLOG) printf("setline(%s,%d,%d,%d)\n", m_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_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(); + event_index = m_qindex++; + logerror("Exceeded pending input line event queue on device '%s'!\n", m_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->device().machine().scheduler().synchronize(FUNC(static_empty_event_queue), 0, (void *)this); + } +} + + +//------------------------------------------------- +// empty_event_queue - empty our event queue +//------------------------------------------------- + +TIMER_CALLBACK( device_execute_interface::device_input::static_empty_event_queue ) +{ + reinterpret_cast<device_input *>(ptr)->empty_event_queue(); +} + +void device_execute_interface::device_input::empty_event_queue() +{ +if (TEMPLOG) printf("empty_queue(%s,%d,%d)\n", m_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_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: + logerror("empty_event_queue device '%s', line %d, unknown state %d\n", m_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_device->tag(), m_linenum, CLEAR_LINE)); + m_execute->execute_set_input(m_linenum, CLEAR_LINE); + m_curstate = CLEAR_LINE; + } + return vector; +} |