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
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
|
/***************************************************************************
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__
// set to 1 to execute on cothread instead of directly
//#define USE_COTHREADS 1
//**************************************************************************
// 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 MCFG_DEVICE_DISABLE() \
device_execute_interface::static_set_disable(*device); \
#define MCFG_DEVICE_VBLANK_INT(_tag, _func) \
device_execute_interface::static_set_vblank_int(*device, _func, _tag); \
#define MCFG_DEVICE_PERIODIC_INT(_func, _rate) \
device_execute_interface::static_set_periodic_int(*device, _func, attotime::from_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_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(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(); }
bool is_octal() const { return m_is_octal; }
// static inline configuration helpers
static void static_set_disable(device_t &device);
static void static_set_vblank_int(device_t &device, device_interrupt_func function, const char *tag, int rate = 0);
static void static_set_periodic_int(device_t &device, device_interrupt_func function, attotime rate);
// execution management
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) { return m_input[linenum].m_curstate; }
void set_irq_callback(device_irq_callback callback);
// suspend/resume
void suspend(UINT32 reason, bool eatcycles);
void resume(UINT32 reason);
bool 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;
// required operation overrides
//#if USE_COTHREADS
// void run() { m_cothread.make_active(); }
//#else
void run() { execute_run(); }
//#endif
protected:
// internal helpers
void run_thread_wrapper();
// 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;
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();
};
// internal state
// cothread m_cothread; // thread used for execution
// configuration
bool m_disabled; // disabled from executing?
device_interrupt_func m_vblank_interrupt; // for interrupts tied to VBLANK
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
attotime m_timed_interrupt_period; // period for periodic interrupts
bool m_is_octal; // to determine if messages/debugger will show octal or hex
// 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
// 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();
attoseconds_t minimum_quantum() const;
};
// iterator
typedef device_interface_iterator<device_execute_interface> execute_interface_iterator;
//**************************************************************************
// 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);
}
// ======================> 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();
}
// 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__ */
|