diff options
Diffstat (limited to 'src/emu/diexec.h')
-rw-r--r-- | src/emu/diexec.h | 519 |
1 files changed, 519 insertions, 0 deletions
diff --git a/src/emu/diexec.h b/src/emu/diexec.h new file mode 100644 index 00000000000..51c828b9d98 --- /dev/null +++ b/src/emu/diexec.h @@ -0,0 +1,519 @@ +/*************************************************************************** + + diexec.h + + 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. + +***************************************************************************/ + +#pragma once + +#ifndef __EMU_H__ +#error Dont include this file directly; include emu.h instead. +#endif + +#ifndef __DIEXEC_H__ +#define __DIEXEC_H__ + + +//************************************************************************** +// CONSTANTS +//************************************************************************** + +// suspension reasons for executing devices +const UINT32 SUSPEND_REASON_HALT = 0x0001; // HALT line set (or equivalent) +const UINT32 SUSPEND_REASON_RESET = 0x0002; // RESET line set (or equivalent) +const UINT32 SUSPEND_REASON_SPIN = 0x0004; // currently spinning +const UINT32 SUSPEND_REASON_TRIGGER = 0x0008; // waiting for a trigger +const UINT32 SUSPEND_REASON_DISABLE = 0x0010; // disabled (due to disable flag) +const UINT32 SUSPEND_REASON_TIMESLICE = 0x0020; // waiting for the next timeslice +const UINT32 SUSPEND_ANY_REASON = ~0; // all of the above + + +// I/O line states +enum line_state +{ + CLEAR_LINE = 0, // clear (a fired or held) line + ASSERT_LINE, // assert an interrupt immediately + HOLD_LINE, // hold interrupt line until acknowledged + PULSE_LINE // pulse interrupt line instantaneously (only for NMI, RESET) +}; + + +// I/O line definitions +enum +{ + // input lines + MAX_INPUT_LINES = 32+3, + INPUT_LINE_IRQ0 = 0, + INPUT_LINE_IRQ1 = 1, + INPUT_LINE_IRQ2 = 2, + INPUT_LINE_IRQ3 = 3, + INPUT_LINE_IRQ4 = 4, + INPUT_LINE_IRQ5 = 5, + INPUT_LINE_IRQ6 = 6, + INPUT_LINE_IRQ7 = 7, + INPUT_LINE_IRQ8 = 8, + INPUT_LINE_IRQ9 = 9, + INPUT_LINE_NMI = MAX_INPUT_LINES - 3, + + // special input lines that are implemented in the core + INPUT_LINE_RESET = MAX_INPUT_LINES - 2, + INPUT_LINE_HALT = MAX_INPUT_LINES - 1 +}; + + + +//************************************************************************** +// MACROS +//************************************************************************** + +// IRQ callback to be called by device implementations when an IRQ is actually taken +#define IRQ_CALLBACK(func) int func(device_t *device, int irqline) + + + +//************************************************************************** +// INTERFACE CONFIGURATION MACROS +//************************************************************************** + +#define MDRV_DEVICE_DISABLE() \ + TOKEN_UINT32_PACK1(MCONFIG_TOKEN_DIEXEC_DISABLE, 8), \ + +#define MDRV_DEVICE_VBLANK_INT(_tag, _func) \ + TOKEN_UINT32_PACK2(MCONFIG_TOKEN_DIEXEC_VBLANK_INT, 8, 0, 24), \ + TOKEN_PTR(device_interrupt, _func), \ + TOKEN_PTR(stringptr, _tag), \ + +#define MDRV_DEVICE_PERIODIC_INT(_func, _rate) \ + TOKEN_UINT32_PACK1(MCONFIG_TOKEN_DIEXEC_PERIODIC_INT, 8), \ + TOKEN_PTR(device_interrupt, _func), \ + TOKEN_UINT64(UINT64_ATTOTIME_IN_HZ(_rate)), \ + + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +class emu_timer; +class screen_device; + + +// interrupt callback for VBLANK and timed interrupts +typedef void (*device_interrupt_func)(device_t *device); + +// IRQ callback to be called by executing devices when an IRQ is actually taken +typedef int (*device_irq_callback)(device_t *device, int irqnum); + + + +// ======================> device_config_execute_interface + +// class representing interface-specific configuration state +class device_config_execute_interface : public device_config_interface +{ + friend class device_execute_interface; + +public: + // construction/destruction + device_config_execute_interface(const machine_config &mconfig, device_config &devconfig); + virtual ~device_config_execute_interface(); + + // basic information getters + bool disabled() const { return m_disabled; } + + // clock and cycle information getters + UINT32 clocks_to_cycles(UINT32 clocks) const { return execute_clocks_to_cycles(clocks); } + UINT32 cycles_to_clocks(UINT32 cycles) const { return execute_cycles_to_clocks(cycles); } + UINT32 min_cycles() const { return execute_min_cycles(); } + UINT32 max_cycles() const { return execute_max_cycles(); } + + // input line information getters + UINT32 input_lines() const { return execute_input_lines(); } + UINT32 default_irq_vector() const { return execute_default_irq_vector(); } + +protected: + // clock and cycle information getters + virtual UINT32 execute_clocks_to_cycles(UINT32 clocks) const; + virtual UINT32 execute_cycles_to_clocks(UINT32 cycles) const; + virtual UINT32 execute_min_cycles() const; + virtual UINT32 execute_max_cycles() const; + + // input line information getters + virtual UINT32 execute_input_lines() const; + virtual UINT32 execute_default_irq_vector() const; + + // optional operation overrides + virtual bool interface_process_token(UINT32 entrytype, const machine_config_token *&tokens); + virtual bool interface_validity_check(const game_driver &driver) const; + + bool m_disabled; + device_interrupt_func m_vblank_interrupt; // for interrupts tied to VBLANK + int m_vblank_interrupts_per_frame; // usually 1 + const char * m_vblank_interrupt_screen; // the screen that causes the VBLANK interrupt + device_interrupt_func m_timed_interrupt; // for interrupts not tied to VBLANK + UINT64 m_timed_interrupt_period; // period for periodic interrupts +}; + + + +// ======================> device_execute_interface + +class device_execute_interface : public device_interface +{ + friend class device_scheduler; + +public: + // construction/destruction + device_execute_interface(running_machine &machine, const device_config &config, device_t &device); + virtual ~device_execute_interface(); + + // configuration access + const device_config_execute_interface &execute_config() const { return m_execute_config; } + + // basic information getters + bool disabled() const { return m_execute_config.disabled(); } + + // execution management + bool is_executing() const; + INT32 cycles_remaining() const; + void eat_cycles(int cycles); + void adjust_icount(int delta); + void abort_timeslice(); + + // input and interrupt management + void set_input_line(int linenum, int state) { m_input[linenum].set_state_synced(state); } + void set_input_line_vector(int linenum, int vector) { m_input[linenum].set_vector(vector); } + void set_input_line_and_vector(int linenum, int state, int vector) { m_input[linenum].set_state_synced(state, vector); } + int input_state(int linenum) { return m_input[linenum].m_curstate; } + void set_irq_callback(device_irq_callback callback); + + // deprecated, but still needed for older drivers + int iloops() const { return m_iloops; } + + // suspend/resume + void suspend(UINT32 reason, bool eatcycles); + void resume(UINT32 reason); + bool is_suspended(UINT32 reason = SUSPEND_ANY_REASON) { return (m_nextsuspend & reason) != 0; } + void yield() { suspend(SUSPEND_REASON_TIMESLICE, false); } + void spin() { suspend(SUSPEND_REASON_TIMESLICE, true); } + void spin_until_trigger(int trigid) { suspend_until_trigger(trigid, true); } + void spin_until_time(attotime duration); + void spin_until_interrupt() { spin_until_trigger(m_inttrigger); } + + // triggers + void suspend_until_trigger(int trigid, bool eatcycles); + void trigger(int trigid); + void signal_interrupt_trigger() { trigger(m_inttrigger); } + + // time and cycle accounting + attotime local_time() const; + UINT64 total_cycles() const; + + // clock and cycle information getters ... pass through to underlying config + UINT32 clocks_to_cycles(UINT32 clocks) const { return m_execute_config.clocks_to_cycles(clocks); } + UINT32 cycles_to_clocks(UINT32 cycles) const { return m_execute_config.cycles_to_clocks(cycles); } + UINT32 min_cycles() const { return m_execute_config.min_cycles(); } + UINT32 max_cycles() const { return m_execute_config.max_cycles(); } + + // input line information getters + UINT32 input_lines() const { return m_execute_config.input_lines(); } + UINT32 default_irq_vector() const { return m_execute_config.default_irq_vector(); } + + // required operation overrides + INT32 run(INT32 clocks) { return execute_run(clocks); } + +protected: + // optional operation overrides + virtual INT32 execute_run(INT32 clocks) = 0; + virtual void execute_burn(INT32 cycles); + virtual void execute_set_input(int linenum, int state); + + // interface-level overrides + virtual void interface_pre_start(); + virtual void interface_post_start(); + virtual void interface_pre_reset(); + virtual void interface_post_reset(); + virtual void interface_clock_changed(); + + // for use by devcpu for now... + static IRQ_CALLBACK( static_standard_irq_callback ); + int standard_irq_callback(int irqline); + + // internal information about the state of inputs + class device_input + { + static const int USE_STORED_VECTOR = 0xff000000; + + public: + device_input(); + + void start(device_execute_interface *execute, int linenum); + void reset(); + + void set_state_synced(int state, int vector = USE_STORED_VECTOR); + void set_vector(int vector) { m_stored_vector = vector; } + int default_irq_callback(); + + device_execute_interface *m_execute;// pointer to the execute interface + device_t * m_device; // pointer to our device + int m_linenum; // which input line we are + + INT32 m_stored_vector; // most recently written vector + INT32 m_curvector; // most recently processed vector + UINT8 m_curstate; // most recently processed state + INT32 m_queue[32]; // queue of pending events + int m_qindex; // index within the queue + + private: + static void static_empty_event_queue(running_machine *machine, void *ptr, int param); + void empty_event_queue(); + }; + + // configuration + running_machine & m_machine; // reference to owning machine + const device_config_execute_interface &m_execute_config; // reference to our device_config_execute_interface + + // execution lists + device_execute_interface *m_nextexec; // pointer to the next device to execute, in order + + // input states and IRQ callbacks + device_irq_callback m_driver_irq; // driver-specific IRQ callback + device_input m_input[MAX_INPUT_LINES]; // data about inputs + emu_timer * m_timedint_timer; // reference to this device's periodic interrupt timer + + // these below are hacks to support multiple interrupts per frame + INT32 m_iloops; // number of interrupts remaining this frame + emu_timer * m_partial_frame_timer; // the timer that triggers partial frame interrupts + attotime m_partial_frame_period; // the length of one partial frame for interrupt purposes + + // cycle counting and executing + int m_profiler; // profiler tag + int * m_icount; // pointer to the icount + int m_cycles_running; // number of cycles we are executing + int m_cycles_stolen; // number of cycles we artificially stole + + // suspend states + UINT32 m_suspend; // suspend reason mask (0 = not suspended) + UINT32 m_nextsuspend; // pending suspend reason mask + UINT8 m_eatcycles; // true if we eat cycles while suspended + UINT8 m_nexteatcycles; // pending value + INT32 m_trigger; // pending trigger to release a trigger suspension + INT32 m_inttrigger; // interrupt trigger index + + // clock and timing information + UINT64 m_totalcycles; // total device cycles executed + attotime m_localtime; // local time, relative to the timer system's global time + INT32 m_divisor; // 32-bit attoseconds_per_cycle divisor + UINT8 m_divshift; // right shift amount to fit the divisor into 32 bits + UINT32 m_cycles_per_second; // cycles per second, adjusted for multipliers + attoseconds_t m_attoseconds_per_cycle; // attoseconds per adjusted clock cycle + +private: + // callbacks + static void static_timed_trigger_callback(running_machine *machine, void *ptr, int param); + + static void static_on_vblank(screen_device &screen, void *param, bool vblank_state); + void on_vblank_start(screen_device &screen); + + static void static_trigger_partial_frame_interrupt(running_machine *machine, void *ptr, int param); + void trigger_partial_frame_interrupt(); + + static void static_trigger_periodic_interrupt(running_machine *machine, void *ptr, int param); + void trigger_periodic_interrupt(); + + attoseconds_t minimum_quantum() const; +}; + + + +//************************************************************************** +// INLINE HELPERS +//************************************************************************** + +//------------------------------------------------- +// device_execute - return a pointer to the device +// execute interface for this device +//------------------------------------------------- + +inline device_execute_interface *device_execute(device_t *device) +{ + device_execute_interface *intf; + if (!device->interface(intf)) + throw emu_fatalerror("Device '%s' does not have execute interface", device->tag()); + return intf; +} + + + +// ======================> device scheduling + +// suspend the given device for a specific reason +inline void device_suspend(device_t *device, int reason, bool eatcycles) +{ + device_execute(device)->suspend(reason, eatcycles); +} + +// resume the given device for a specific reason +inline void device_resume(device_t *device, int reason) +{ + device_execute(device)->resume(reason); +} + +// return TRUE if the given device is within its execute function +inline bool device_is_executing(device_t *device) +{ + return device_execute(device)->is_executing(); +} + +// returns TRUE if the given device is suspended for any of the given reasons +inline int device_is_suspended(device_t *device, int reason) +{ + return device_execute(device)->is_suspended(reason); +} + + + +// ======================> synchronization helpers + +// yield the given device until the end of the current timeslice +inline void device_yield(device_t *device) +{ + device_execute(device)->yield(); +} + +// burn device cycles until the end of the current timeslice +inline void device_spin(device_t *device) +{ + device_execute(device)->spin(); +} + +// burn specified device cycles until a trigger +inline void device_spin_until_trigger(device_t *device, int trigger) +{ + device_execute(device)->spin_until_trigger(trigger); +} + +// burn device cycles for a specific period of time +inline void device_spin_until_time(device_t *device, attotime duration) +{ + device_execute(device)->spin_until_time(duration); +} + + + +// ======================> device timing + +// returns the current local time for a device +inline attotime device_get_local_time(device_t *device) +{ + return device_execute(device)->local_time(); +} + +// returns the total number of executed cycles for a given device +inline UINT64 device_get_total_cycles(device_t *device) +{ + return device_execute(device)->total_cycles(); +} + +// safely eats cycles so we don't cross a timeslice boundary +inline void device_eat_cycles(device_t *device, int cycles) +{ + device_execute(device)->eat_cycles(cycles); +} + +// apply a +/- to the current icount +inline void device_adjust_icount(device_t *device, int delta) +{ + device_execute(device)->adjust_icount(delta); +} + +// abort execution for the current timeslice, allowing other devices to run before we run again +inline void device_abort_timeslice(device_t *device) +{ + device_execute(device)->abort_timeslice(); +} + + + +// ======================> triggers + +// generate a trigger corresponding to an interrupt on the given device +inline void device_triggerint(device_t *device) +{ + device_execute(device)->signal_interrupt_trigger(); +} + + + +// ======================> interrupts + +// set the logical state (ASSERT_LINE/CLEAR_LINE) of the an input line on a device +inline void device_set_input_line(device_t *device, int line, int state) +{ + device_execute(device)->set_input_line(line, state); +} + +// set the vector to be returned during a device's interrupt acknowledge cycle +inline void device_set_input_line_vector(device_t *device, int line, int vector) +{ + device_execute(device)->set_input_line_vector(line, vector); +} + +// set the logical state (ASSERT_LINE/CLEAR_LINE) of the an input line on a device and its associated vector +inline void device_set_input_line_and_vector(device_t *device, int line, int state, int vector) +{ + device_execute(device)->set_input_line_and_vector(line, state, vector); +} + +// install a driver-specific callback for IRQ acknowledge +inline void device_set_irq_callback(device_t *device, device_irq_callback callback) +{ + device_execute(device)->set_irq_callback(callback); +} + + + +// ======================> additional helpers + +// burn device cycles until the next interrupt +inline void device_spin_until_interrupt(device_t *device) +{ + device_execute(device)->spin_until_interrupt(); +} + + + +#endif /* __DIEXEC_H__ */ |