summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/diexec.h
blob: 663cc6ddb1c6075209a18a2e96b9c5cfce849815 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
// license:BSD-3-Clause
// copyright-holders:Aaron Giles
/***************************************************************************

    diexec.h

    Device execution interfaces.

***************************************************************************/
#ifndef MAME_EMU_DIEXEC_H
#define MAME_EMU_DIEXEC_H

#pragma once

#include "debug/debugcpu.h"


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

// suspension reasons for executing devices
constexpr u32 SUSPEND_REASON_HALT       = 0x0001;   // HALT line set (or equivalent)
constexpr u32 SUSPEND_REASON_RESET      = 0x0002;   // RESET line set (or equivalent)
constexpr u32 SUSPEND_REASON_SPIN       = 0x0004;   // currently spinning
constexpr u32 SUSPEND_REASON_TRIGGER    = 0x0008;   // waiting for a trigger
constexpr u32 SUSPEND_REASON_DISABLE    = 0x0010;   // disabled (due to disable flag)
constexpr u32 SUSPEND_REASON_TIMESLICE  = 0x0020;   // waiting for the next timeslice
constexpr u32 SUSPEND_REASON_CLOCK      = 0x0040;   // currently not clocked
constexpr u32 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
};


// I/O line definitions
enum
{
	// input lines
	MAX_INPUT_LINES = 64+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_MEMBER(func)       int func(device_t &device, int irqline)

// interrupt generator callback called as a VBLANK or periodic interrupt
#define INTERRUPT_GEN_MEMBER(func)      void func(device_t &device)



//**************************************************************************
//  TYPE DEFINITIONS
//**************************************************************************


// 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; }
	u64 clocks_to_cycles(u64 clocks) const { return execute_clocks_to_cycles(clocks); }
	u64 cycles_to_clocks(u64 cycles) const { return execute_cycles_to_clocks(cycles); }
	u32 min_cycles() const { return execute_min_cycles(); }
	u32 max_cycles() const { return execute_max_cycles(); }
	attotime cycles_to_attotime(u64 cycles) const { return device().clocks_to_attotime(cycles_to_clocks(cycles)); }
	u64 attotime_to_cycles(const attotime &duration) const { return clocks_to_cycles(device().attotime_to_clocks(duration)); }
	u32 input_lines() const { return execute_input_lines(); }
	u32 default_irq_vector(int linenum) const { return execute_default_irq_vector(linenum); }
	bool input_edge_triggered(int linenum) const { return execute_input_edge_triggered(linenum); }

	// inline configuration helpers
	void set_disable() { m_disabled = true; }
	template <typename Object> void set_vblank_int(Object &&cb, const char *tag)
	{
		m_vblank_interrupt = std::forward<Object>(cb);
		m_vblank_interrupt_screen = tag;
	}
	void set_vblank_int(device_interrupt_delegate callback, const char *tag)
	{
		m_vblank_interrupt = callback;
		m_vblank_interrupt_screen = tag;
	}
	template <class FunctionClass> void set_vblank_int(const char *tag, const char *devname, void (FunctionClass::*callback)(device_t &), const char *name)
	{
		set_vblank_int(device_interrupt_delegate(callback, name, devname, static_cast<FunctionClass *>(nullptr)), tag);
	}
	template <class FunctionClass> void set_vblank_int(const char *tag, void (FunctionClass::*callback)(device_t &), const char *name)
	{
		set_vblank_int(device_interrupt_delegate(callback, name, nullptr, static_cast<FunctionClass *>(nullptr)), tag);
	}

	template <typename Object> void set_periodic_int(Object &&cb, const attotime &rate)
	{
		m_timed_interrupt = std::forward<Object>(cb);
		m_timed_interrupt_period = rate;
	}
	void set_periodic_int(device_interrupt_delegate callback, const attotime &rate)
	{
		m_timed_interrupt = callback;
		m_timed_interrupt_period = rate;
	}
	template <class FunctionClass> void set_periodic_int(const char *devname, void (FunctionClass::*callback)(device_t &), const char *name, const attotime &rate)
	{
		set_periodic_int(device_interrupt_delegate(callback, name, devname, static_cast<FunctionClass *>(nullptr)), rate);
	}
	template <class FunctionClass> void set_periodic_int(void (FunctionClass::*callback)(device_t &), const char *name, const attotime &rate)
	{
		set_periodic_int(device_interrupt_delegate(callback, name, nullptr, static_cast<FunctionClass *>(nullptr)), rate);
	}

	template <typename Object> void set_irq_acknowledge_callback(Object &&cb) { m_driver_irq = std::forward<Object>(cb); }
	void set_irq_acknowledge_callback(device_irq_acknowledge_delegate callback) { m_driver_irq = callback; }
	template <class FunctionClass> void set_irq_acknowledge_callback(const char *devname, int (FunctionClass::*callback)(device_t &, int), const char *name)
	{
		set_irq_acknowledge_callback(device_irq_acknowledge_delegate(callback, name, devname, static_cast<FunctionClass *>(nullptr)));
	}
	template <class FunctionClass> void set_irq_acknowledge_callback(int (FunctionClass::*callback)(device_t &, int), const char *name)
	{
		set_irq_acknowledge_callback(device_irq_acknowledge_delegate(callback, name, nullptr, static_cast<FunctionClass *>(nullptr)));
	}

	// execution management
	device_scheduler &scheduler() const { assert(m_scheduler != nullptr); return *m_scheduler; }
	bool executing() const { return scheduler().currently_executing() == this; }
	s32 cycles_remaining() const { return executing() ? *m_icountptr : 0; } // cycles remaining in this timeslice
	void eat_cycles(int cycles) { if (executing()) *m_icountptr = (cycles > *m_icountptr) ? 0 : (*m_icountptr - cycles); }
	void adjust_icount(int delta) { if (executing()) *m_icountptr += 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; }
	void pulse_input_line(int irqline, const attotime &duration);
	void pulse_input_line_and_vector(int irqline, int vector, const attotime &duration);

	// suspend/resume
	void suspend(u32 reason, bool eatcycles);
	void resume(u32 reason);
	bool suspended(u32 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;
	u64 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 u64 execute_clocks_to_cycles(u64 clocks) const;
	virtual u64 execute_cycles_to_clocks(u64 cycles) const;
	virtual u32 execute_min_cycles() const;
	virtual u32 execute_max_cycles() const;

	// input line information getters
	virtual u32 execute_input_lines() const;
	virtual u32 execute_default_irq_vector(int linenum) const;
	virtual bool execute_input_edge_triggered(int linenum) const;

	// optional operation overrides
	virtual void execute_run() = 0;
	virtual void execute_burn(s32 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...
	int current_input_state(unsigned i) const { return m_input[i].m_curstate; }
	void set_icountptr(int &icount) { assert(!m_icountptr); m_icountptr = &icount; }
	IRQ_CALLBACK_MEMBER(standard_irq_callback_member);
	int standard_irq_callback(int irqline);

	// debugger hooks
	bool debugger_enabled() const { return bool(device().machine().debug_flags & DEBUG_FLAG_ENABLED); }
	void debugger_instruction_hook(offs_t curpc)
	{
		if (device().machine().debug_flags & DEBUG_FLAG_CALL_HOOK)
			device().debug()->instruction_hook(curpc);
	}
	void debugger_exception_hook(int exception)
	{
		if (device().machine().debug_flags & DEBUG_FLAG_ENABLED)
			device().debug()->exception_hook(exception);
	}

	void debugger_privilege_hook()
	{
		if (device().machine().debug_flags & DEBUG_FLAG_ENABLED)
			device().debug()->privilege_hook();
	}

private:
	// 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

		s32             m_stored_vector;    // most recently written vector
		s32             m_curvector;        // most recently processed vector
		u8              m_curstate;         // most recently processed state
		s32             m_queue[32];        // queue of pending events
		int             m_qindex;           // index within the queue

	private:
		TIMER_CALLBACK_MEMBER(empty_event_queue);
	};

	// internal debugger hooks
	void debugger_start_cpu_hook(const attotime &endtime)
	{
		if (device().machine().debug_flags & DEBUG_FLAG_ENABLED)
			device().debug()->start_hook(endtime);
	}
	void debugger_stop_cpu_hook()
	{
		if (device().machine().debug_flags & DEBUG_FLAG_ENABLED)
			device().debug()->stop_hook();
	}

	// 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
	u32                     m_suspend;                  // suspend reason mask (0 = not suspended)
	u32                     m_nextsuspend;              // pending suspend reason mask
	u8                      m_eatcycles;                // true if we eat cycles while suspended
	u8                      m_nexteatcycles;            // pending value
	s32                     m_trigger;                  // pending trigger to release a trigger suspension
	s32                     m_inttrigger;               // interrupt trigger index

	// clock and timing information
protected: // FIXME: MIPS3 accesses m_totalcycles directly from execute_burn - devise a better solution
	u64                     m_totalcycles;              // total device cycles executed
private:
	attotime                m_localtime;                // local time, relative to the timer system's global time
	s32                     m_divisor;                  // 32-bit attoseconds_per_cycle divisor
	u8                      m_divshift;                 // right shift amount to fit the divisor into 32 bits
	u32                     m_cycles_per_second;        // cycles per second, adjusted for multipliers
	attoseconds_t           m_attoseconds_per_cycle;    // attoseconds per adjusted clock cycle

	// callbacks
	TIMER_CALLBACK_MEMBER(timed_trigger_callback) { trigger(param); }

	void on_vblank(screen_device &screen, bool vblank_state);

	TIMER_CALLBACK_MEMBER(trigger_periodic_interrupt);
	TIMER_CALLBACK_MEMBER(irq_pulse_clear) { set_input_line(int(param), CLEAR_LINE); }
	void suspend_resume_changed();

	attoseconds_t minimum_quantum() const;

public:
	attotime minimum_quantum_time() const { return attotime(0, minimum_quantum()); }
};

// iterator
typedef device_interface_iterator<device_execute_interface> execute_interface_iterator;

#endif // MAME_EMU_DIEXEC_H