diff options
Diffstat (limited to 'src/emu/diexec.cpp')
-rw-r--r-- | src/emu/diexec.cpp | 188 |
1 files changed, 77 insertions, 111 deletions
diff --git a/src/emu/diexec.cpp b/src/emu/diexec.cpp index fba63aa1e7d..1cd5266c518 100644 --- a/src/emu/diexec.cpp +++ b/src/emu/diexec.cpp @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles /*************************************************************************** - diexec.c + diexec.cpp Device execution interfaces. @@ -46,9 +46,12 @@ device_execute_interface::device_execute_interface(const machine_config &mconfig : device_interface(device, "execute") , m_scheduler(nullptr) , m_disabled(false) + , m_vblank_interrupt(device) , m_vblank_interrupt_screen(nullptr) + , m_timed_interrupt(device) , m_timed_interrupt_period(attotime::zero) , m_nextexec(nullptr) + , m_driver_irq(device) , m_timedint_timer(nullptr) , m_profiler(PROFILER_IDLE) , m_icountptr(nullptr) @@ -65,9 +68,8 @@ device_execute_interface::device_execute_interface(const machine_config &mconfig , m_divshift(0) , m_cycles_per_second(0) , m_attoseconds_per_cycle(0) + , m_spin_end_timer(nullptr) { - memset(&m_localtime, 0, sizeof(m_localtime)); - // configure the fast accessor assert(!device.interfaces().m_execute); device.interfaces().m_execute = this; @@ -89,19 +91,18 @@ device_execute_interface::~device_execute_interface() // run before we run again //------------------------------------------------- -void device_execute_interface::abort_timeslice() +void device_execute_interface::abort_timeslice() noexcept { // ignore if not the executing device if (!executing()) return; // swallow the remaining cycles - if (m_icountptr != nullptr) + if (m_icountptr != nullptr && *m_icountptr > 0) { - int delta = *m_icountptr; - m_cycles_stolen += delta; - m_cycles_running -= delta; - *m_icountptr -= delta; + m_cycles_stolen += *m_icountptr; + m_cycles_running -= *m_icountptr; + *m_icountptr = 0; } } @@ -126,7 +127,8 @@ void device_execute_interface::suspend_resume_changed() void device_execute_interface::suspend(u32 reason, bool eatcycles) { -if (TEMPLOG) printf("suspend %s (%X)\n", device().tag(), reason); + if (TEMPLOG) printf("suspend %s (%X)\n", device().tag(), reason); + // set the suspend reason and eat cycles flag m_nextsuspend |= reason; m_nexteatcycles = eatcycles; @@ -141,7 +143,8 @@ if (TEMPLOG) printf("suspend %s (%X)\n", device().tag(), reason); void device_execute_interface::resume(u32 reason) { -if (TEMPLOG) printf("resume %s (%X)\n", device().tag(), 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(); @@ -161,7 +164,7 @@ void device_execute_interface::spin_until_time(const attotime &duration) 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); + m_spin_end_timer->adjust(duration, TRIGGER_SUSPENDTIME + timetrig); timetrig = (timetrig + 1) % 256; } @@ -204,7 +207,7 @@ void device_execute_interface::trigger(int trigid) // for a device //------------------------------------------------- -attotime device_execute_interface::local_time() const +attotime device_execute_interface::local_time() const noexcept { // if we're active, add in the time from the current slice if (executing()) @@ -222,7 +225,7 @@ attotime device_execute_interface::local_time() const // cycles executed on this device //------------------------------------------------- -u64 device_execute_interface::total_cycles() const +u64 device_execute_interface::total_cycles() const noexcept { if (executing()) { @@ -239,7 +242,7 @@ u64 device_execute_interface::total_cycles() const // of clocks to cycles, rounding down if necessary //------------------------------------------------- -u64 device_execute_interface::execute_clocks_to_cycles(u64 clocks) const +u64 device_execute_interface::execute_clocks_to_cycles(u64 clocks) const noexcept { return clocks; } @@ -250,7 +253,7 @@ u64 device_execute_interface::execute_clocks_to_cycles(u64 clocks) const // of cycles to clocks, rounding down if necessary //------------------------------------------------- -u64 device_execute_interface::execute_cycles_to_clocks(u64 cycles) const +u64 device_execute_interface::execute_cycles_to_clocks(u64 cycles) const noexcept { return cycles; } @@ -262,7 +265,7 @@ u64 device_execute_interface::execute_cycles_to_clocks(u64 cycles) const // operation can take //------------------------------------------------- -u32 device_execute_interface::execute_min_cycles() const +u32 device_execute_interface::execute_min_cycles() const noexcept { return 1; } @@ -274,29 +277,18 @@ u32 device_execute_interface::execute_min_cycles() const // operation can take //------------------------------------------------- -u32 device_execute_interface::execute_max_cycles() const +u32 device_execute_interface::execute_max_cycles() const noexcept { return 1; } //------------------------------------------------- -// execute_input_lines - return the total number -// of input lines for the device -//------------------------------------------------- - -u32 device_execute_interface::execute_input_lines() const -{ - return 0; -} - - -//------------------------------------------------- // execute_default_irq_vector - return the default // IRQ vector when an acknowledge is processed //------------------------------------------------- -u32 device_execute_interface::execute_default_irq_vector(int linenum) const +u32 device_execute_interface::execute_default_irq_vector(int linenum) const noexcept { return 0; } @@ -307,25 +299,13 @@ u32 device_execute_interface::execute_default_irq_vector(int linenum) const // the input line has an asynchronous edge trigger //------------------------------------------------- -bool device_execute_interface::execute_input_edge_triggered(int linenum) const +bool device_execute_interface::execute_input_edge_triggered(int linenum) const noexcept { return false; } //------------------------------------------------- -// 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(s32 cycles) -{ - // by default, do nothing -} - - -//------------------------------------------------- // execute_set_input - called when a synchronized // input is changed //------------------------------------------------- @@ -347,7 +327,7 @@ void device_execute_interface::interface_validity_check(validity_checker &valid) // validate the interrupts if (!m_vblank_interrupt.isnull()) { - screen_device_iterator iter(device().mconfig().root_device()); + screen_device_enumerator 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) @@ -371,19 +351,26 @@ 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()); + m_vblank_interrupt.resolve(); + m_timed_interrupt.resolve(); + m_driver_irq.resolve(); // fill in the initial states - int const index = device_iterator(device().machine().root_device()).indexof(*this); + int const index = device_enumerator(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 + // allocate a timed-interrupt timer if we need it 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)); + + // allocate a timer for triggering the end of spin-until conditions + m_spin_end_timer = m_scheduler->timer_alloc(timer_expired_delegate(FUNC(device_execute_interface::timed_trigger_callback), this)); + + // allocate input-pulse timers if we have input lines + for (u32 i = 0; i < MAX_INPUT_LINES; i++) + m_pulse_end_timers[i] = m_scheduler->timer_alloc(timer_expired_delegate(FUNC(device_execute_interface::irq_pulse_clear), this)); } @@ -407,9 +394,14 @@ void device_execute_interface::interface_post_start() device().save_item(NAME(m_totalcycles)); device().save_item(NAME(m_localtime)); + // it's more efficient and causes less clutter to save these this way + device().save_item(STRUCT_MEMBER(m_input, m_stored_vector)); + device().save_item(STRUCT_MEMBER(m_input, m_curvector)); + device().save_item(STRUCT_MEMBER(m_input, m_curstate)); + // 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); + for (int line = 0; line < std::size(m_input); line++) + m_input[line].start(*this, line); } @@ -446,7 +438,7 @@ void device_execute_interface::interface_post_reset() if (m_vblank_interrupt_screen != nullptr) { // get the screen that will trigger the VBLANK - screen_device * screen = device().siblingdevice<screen_device>(m_vblank_interrupt_screen); + screen_device *const screen = device().siblingdevice<screen_device>(m_vblank_interrupt_screen); assert(screen != nullptr); screen->register_vblank_callback(vblank_state_delegate(&device_execute_interface::on_vblank, this)); @@ -467,7 +459,7 @@ void device_execute_interface::interface_post_reset() // information for this device //------------------------------------------------- -void device_execute_interface::interface_clock_changed() +void device_execute_interface::interface_clock_changed(bool sync_on_new_clock_domain) { // a clock of zero disables the device if (device().clock() == 0) @@ -484,6 +476,10 @@ void device_execute_interface::interface_clock_changed() m_cycles_per_second = clocks_to_cycles(device().clock()); m_attoseconds_per_cycle = HZ_TO_ATTOSECONDS(m_cycles_per_second); + // resynchronize the localtime to the clock domain when asked to + if (sync_on_new_clock_domain) + m_localtime = attotime::from_ticks(m_localtime.as_ticks(device().clock())+1, device().clock()); + // update the device's divisor s64 attos = m_attoseconds_per_cycle; m_divshift = 0; @@ -500,17 +496,12 @@ void device_execute_interface::interface_clock_changed() //------------------------------------------------- -// standard_irq_callback_member - IRQ acknowledge +// standard_irq_callback - 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) +int device_execute_interface::standard_irq_callback(int irqline, offs_t pc) { // get the default vector and acknowledge the interrupt if needed int vector = m_input[irqline].default_irq_callback(); @@ -522,8 +513,8 @@ int device_execute_interface::standard_irq_callback(int irqline) vector = m_driver_irq(device(), irqline); // notify the debugger - if (device().machine().debug_flags & DEBUG_FLAG_ENABLED) - device().debug()->interrupt_hook(irqline); + if (debugger_enabled()) + device().debug()->interrupt_hook(irqline, pc); return vector; } @@ -593,48 +584,28 @@ TIMER_CALLBACK_MEMBER(device_execute_interface::trigger_periodic_interrupt) void device_execute_interface::pulse_input_line(int irqline, const attotime &duration) { - // treat instantaneous pulses as ASSERT+CLEAR - if (duration == attotime::zero) - { - if (irqline != INPUT_LINE_RESET && !input_edge_triggered(irqline)) - throw emu_fatalerror("device '%s': zero-width pulse is not allowed for input line %d\n", device().tag(), irqline); - - set_input_line(irqline, ASSERT_LINE); - set_input_line(irqline, CLEAR_LINE); - } - else - { - set_input_line(irqline, ASSERT_LINE); - - attotime target_time = local_time() + duration; - m_scheduler->timer_set(target_time - m_scheduler->time(), timer_expired_delegate(FUNC(device_execute_interface::irq_pulse_clear), this), irqline); - } -} - - -//------------------------------------------------- -// pulse_input_line_and_vector - "pulse" an -// input line by asserting it and then clearing it -// later, specifying a vector -//------------------------------------------------- - -void device_execute_interface::pulse_input_line_and_vector(int irqline, int vector, const attotime &duration) -{ - // treat instantaneous pulses as ASSERT+CLEAR + const attotime expiry = m_pulse_end_timers[irqline]->expire(); if (duration == attotime::zero) { - if (irqline != INPUT_LINE_RESET && !input_edge_triggered(irqline)) + // treat instantaneous pulses as ASSERT+CLEAR + if ((irqline != INPUT_LINE_RESET) && !input_edge_triggered(irqline)) throw emu_fatalerror("device '%s': zero-width pulse is not allowed for input line %d\n", device().tag(), irqline); - set_input_line_and_vector(irqline, ASSERT_LINE, vector); - set_input_line_and_vector(irqline, CLEAR_LINE, vector); + if (expiry.is_never() || (expiry <= m_scheduler->time())) + { + set_input_line(irqline, ASSERT_LINE); + set_input_line(irqline, CLEAR_LINE); + } } else { - set_input_line_and_vector(irqline, ASSERT_LINE, vector); + const attotime target_time = local_time() + duration; + if (expiry.is_never() || (target_time > expiry)) + { + set_input_line(irqline, ASSERT_LINE); - attotime target_time = local_time() + duration; - m_scheduler->timer_set(target_time - m_scheduler->time(), timer_expired_delegate(FUNC(device_execute_interface::irq_pulse_clear), this), irqline); + m_pulse_end_timers[irqline]->adjust(target_time - m_scheduler->time(), irqline); + } } } @@ -665,17 +636,12 @@ device_execute_interface::device_input::device_input() // can set ourselves up //------------------------------------------------- -void device_execute_interface::device_input::start(device_execute_interface *execute, int linenum) +void device_execute_interface::device_input::start(device_execute_interface &execute, int linenum) { - m_execute = execute; + 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); } @@ -686,7 +652,6 @@ void device_execute_interface::device_input::start(device_execute_interface *exe void device_execute_interface::device_input::reset() { m_curvector = m_stored_vector = m_execute->default_irq_vector(m_linenum); - m_qindex = 0; } @@ -699,21 +664,21 @@ void device_execute_interface::device_input::set_state_synced(int state, int vec { 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); + 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); // 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)) + if (event_index >= std::size(m_queue)) { m_qindex--; - empty_event_queue(nullptr,0); + empty_event_queue(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 (event_index < std::size(m_queue)) { if (vector == USE_STORED_VECTOR) vector = m_stored_vector; @@ -721,7 +686,7 @@ if (TEMPLOG) printf("setline(%s,%d,%d,%d)\n", m_execute->device().tag(), m_linen // 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); + m_execute->scheduler().synchronize(timer_expired_delegate(FUNC(device_execute_interface::device_input::empty_event_queue),this)); } } @@ -732,7 +697,8 @@ if (TEMPLOG) printf("setline(%s,%d,%d,%d)\n", m_execute->device().tag(), m_linen 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); + 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++) { @@ -741,7 +707,7 @@ if (TEMPLOG) printf("empty_queue(%s,%d,%d)\n", m_execute->device().tag(), m_line // 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); + if (TEMPLOG) printf(" (%d,%d)\n", m_curstate, m_curvector); assert(m_curstate == ASSERT_LINE || m_curstate == HOLD_LINE || m_curstate == CLEAR_LINE); |