mirror of
https://github.com/holub/mame
synced 2025-05-07 23:02:33 +03:00
311 lines
13 KiB
C++
311 lines
13 KiB
C++
// license:BSD-3-Clause
|
|
// copyright-holders:Aaron Giles
|
|
/***************************************************************************
|
|
|
|
diexec.h
|
|
|
|
Device execution interfaces.
|
|
|
|
***************************************************************************/
|
|
|
|
#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_REASON_CLOCK = 0x0040; // currently not clocked
|
|
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)
|
|
#define IRQ_CALLBACK_MEMBER(func) int func(device_t &device, int irqline)
|
|
|
|
// interrupt generator callback called as a VBLANK or periodic interrupt
|
|
#define INTERRUPT_GEN(func) void func(device_t *device)
|
|
#define INTERRUPT_GEN_MEMBER(func) void func(device_t &device)
|
|
|
|
|
|
|
|
//**************************************************************************
|
|
// INTERFACE CONFIGURATION MACROS
|
|
//**************************************************************************
|
|
|
|
#define MCFG_DEVICE_DISABLE() \
|
|
device_execute_interface::static_set_disable(*device);
|
|
#define MCFG_DEVICE_VBLANK_INT_DRIVER(_tag, _class, _func) \
|
|
device_execute_interface::static_set_vblank_int(*device, device_interrupt_delegate(&_class::_func, #_class "::" #_func, DEVICE_SELF, (_class *)0), _tag);
|
|
#define MCFG_DEVICE_VBLANK_INT_DEVICE(_tag, _devtag, _class, _func) \
|
|
device_execute_interface::static_set_vblank_int(*device, device_interrupt_delegate(&_class::_func, #_class "::" #_func, _devtag, (_class *)0), _tag);
|
|
#define MCFG_DEVICE_VBLANK_INT_REMOVE() \
|
|
device_execute_interface::static_set_vblank_int(*device, device_interrupt_delegate(), NULL);
|
|
#define MCFG_DEVICE_PERIODIC_INT_DRIVER(_class, _func, _rate) \
|
|
device_execute_interface::static_set_periodic_int(*device, device_interrupt_delegate(&_class::_func, #_class "::" #_func, DEVICE_SELF, (_class *)0), attotime::from_hz(_rate));
|
|
#define MCFG_DEVICE_PERIODIC_INT_DEVICE(_devtag, _class, _func, _rate) \
|
|
device_execute_interface::static_set_periodic_int(*device, device_interrupt_delegate(&_class::_func, #_class "::" #_func, _devtag, (_class *)0), attotime::from_hz(_rate));
|
|
#define MCFG_DEVICE_PERIODIC_INT_REMOVE() \
|
|
device_execute_interface::static_set_periodic_int(*device, device_interrupt_delegate(), attotime());
|
|
#define MCFG_DEVICE_IRQ_ACKNOWLEDGE_DRIVER(_class, _func) \
|
|
device_execute_interface::static_set_irq_acknowledge_callback(*device, device_irq_acknowledge_delegate(&_class::_func, #_class "::" #_func, DEVICE_SELF, (_class *)0));
|
|
#define MCFG_DEVICE_IRQ_ACKNOWLEDGE_DEVICE(_devtag, _class, _func) \
|
|
device_execute_interface::static_set_irq_acknowledge_callback(*device, device_irq_acknowledge_delegate(&_class::_func, #_class "::" #_func, _devtag, (_class *)0));
|
|
#define MCFG_DEVICE_IRQ_ACKNOWLEDGE_REMOVE() \
|
|
device_execute_interface::static_set_irq_acknowledge_callback(*device, device_irq_acknowledge_delegate());
|
|
|
|
|
|
//**************************************************************************
|
|
// TYPE DEFINITIONS
|
|
//**************************************************************************
|
|
|
|
class emu_timer;
|
|
class screen_device;
|
|
class device_scheduler;
|
|
|
|
|
|
// interrupt callback for VBLANK and timed interrupts
|
|
typedef device_delegate<void (device_t &)> device_interrupt_delegate;
|
|
|
|
// IRQ callback to be called by executing devices when an IRQ is actually taken
|
|
typedef device_delegate<int (device_t &, int)> device_irq_acknowledge_delegate;
|
|
|
|
|
|
|
|
// ======================> device_execute_interface
|
|
|
|
class device_execute_interface : public device_interface
|
|
{
|
|
friend class device_scheduler;
|
|
friend class testcpu_state;
|
|
|
|
public:
|
|
// construction/destruction
|
|
device_execute_interface(const machine_config &mconfig, device_t &device);
|
|
virtual ~device_execute_interface();
|
|
|
|
// configuration access
|
|
bool disabled() const { return m_disabled; }
|
|
UINT64 clocks_to_cycles(UINT64 clocks) const { return execute_clocks_to_cycles(clocks); }
|
|
UINT64 cycles_to_clocks(UINT64 cycles) const { return execute_cycles_to_clocks(cycles); }
|
|
UINT32 min_cycles() const { return execute_min_cycles(); }
|
|
UINT32 max_cycles() const { return execute_max_cycles(); }
|
|
attotime cycles_to_attotime(UINT64 cycles) const { return device().clocks_to_attotime(cycles_to_clocks(cycles)); }
|
|
UINT64 attotime_to_cycles(const attotime &duration) const { return clocks_to_cycles(device().attotime_to_clocks(duration)); }
|
|
UINT32 input_lines() const { return execute_input_lines(); }
|
|
UINT32 default_irq_vector() const { return execute_default_irq_vector(); }
|
|
|
|
// static inline configuration helpers
|
|
static void static_set_disable(device_t &device);
|
|
static void static_set_vblank_int(device_t &device, device_interrupt_delegate function, const char *tag, int rate = 0);
|
|
static void static_set_periodic_int(device_t &device, device_interrupt_delegate function, const attotime &rate);
|
|
static void static_set_irq_acknowledge_callback(device_t &device, device_irq_acknowledge_delegate callback);
|
|
|
|
// execution management
|
|
device_scheduler &scheduler() const { assert(m_scheduler != nullptr); return *m_scheduler; }
|
|
bool 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) const { return m_input[linenum].m_curstate; }
|
|
|
|
// suspend/resume
|
|
void suspend(UINT32 reason, bool eatcycles);
|
|
void resume(UINT32 reason);
|
|
bool suspended(UINT32 reason = SUSPEND_ANY_REASON) const { 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(const 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;
|
|
|
|
// required operation overrides
|
|
void run() { execute_run(); }
|
|
|
|
// deliberately ambiguous functions; if you have the execute interface
|
|
// just use it
|
|
device_execute_interface &execute() { return *this; }
|
|
|
|
protected:
|
|
// clock and cycle information getters
|
|
virtual UINT64 execute_clocks_to_cycles(UINT64 clocks) const;
|
|
virtual UINT64 execute_cycles_to_clocks(UINT64 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 void execute_run() = 0;
|
|
virtual void execute_burn(INT32 cycles);
|
|
virtual void execute_set_input(int linenum, int state);
|
|
|
|
// interface-level overrides
|
|
virtual void interface_validity_check(validity_checker &valid) const override;
|
|
virtual void interface_pre_start() override;
|
|
virtual void interface_post_start() override;
|
|
virtual void interface_pre_reset() override;
|
|
virtual void interface_post_reset() override;
|
|
virtual void interface_clock_changed() override;
|
|
|
|
// for use by devcpu for now...
|
|
IRQ_CALLBACK_MEMBER(standard_irq_callback_member);
|
|
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
|
|
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();
|
|
};
|
|
|
|
// scheduler
|
|
device_scheduler * m_scheduler; // pointer to the machine scheduler
|
|
|
|
// configuration
|
|
bool m_disabled; // disabled from executing?
|
|
device_interrupt_delegate m_vblank_interrupt; // for interrupts tied to VBLANK
|
|
const char * m_vblank_interrupt_screen; // the screen that causes the VBLANK interrupt
|
|
device_interrupt_delegate m_timed_interrupt; // for interrupts not tied to VBLANK
|
|
attotime m_timed_interrupt_period; // period for periodic interrupts
|
|
|
|
// execution lists
|
|
device_execute_interface *m_nextexec; // pointer to the next device to execute, in order
|
|
|
|
// input states and IRQ callbacks
|
|
device_irq_acknowledge_delegate 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
|
|
|
|
// cycle counting and executing
|
|
profile_type m_profiler; // profiler tag
|
|
int * m_icountptr; // 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);
|
|
|
|
void on_vblank(screen_device &screen, bool vblank_state);
|
|
|
|
static void static_trigger_periodic_interrupt(running_machine &machine, void *ptr, int param);
|
|
void trigger_periodic_interrupt();
|
|
void suspend_resume_changed();
|
|
|
|
attoseconds_t minimum_quantum() const;
|
|
};
|
|
|
|
// iterator
|
|
typedef device_interface_iterator<device_execute_interface> execute_interface_iterator;
|
|
|
|
|
|
#endif /* __DIEXEC_H__ */
|