~ubuntu-branches/ubuntu/precise/mame/precise-proposed

« back to all changes in this revision

Viewing changes to src/emu/schedule.c

  • Committer: Bazaar Package Importer
  • Author(s): Cesare Falco
  • Date: 2010-08-01 23:18:31 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20100801231831-9dj2qbsynutixsoe
Tags: 0.139-0ubuntu1
* New upstream release (LP: #612070)
* debian/contrib/manpages: removed, accepted upstream
* debian/patches/:
  - deprecated_gtk_macros: removed, fixed upstream
  - missing_newline.patch: removed, fixed upstream
  - typos.patch: removed, fixed upstream
* debian/rules: new variables SYMLEVEL and DUMPSYM
* mame-tools: ldplayer removed, possibly for good (FTBFS upstream)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/***************************************************************************
 
2
 
 
3
    schedule.c
 
4
 
 
5
    Core device execution and scheduling engine.
 
6
 
 
7
****************************************************************************
 
8
 
 
9
    Copyright Aaron Giles
 
10
    All rights reserved.
 
11
 
 
12
    Redistribution and use in source and binary forms, with or without
 
13
    modification, are permitted provided that the following conditions are
 
14
    met:
 
15
 
 
16
        * Redistributions of source code must retain the above copyright
 
17
          notice, this list of conditions and the following disclaimer.
 
18
        * Redistributions in binary form must reproduce the above copyright
 
19
          notice, this list of conditions and the following disclaimer in
 
20
          the documentation and/or other materials provided with the
 
21
          distribution.
 
22
        * Neither the name 'MAME' nor the names of its contributors may be
 
23
          used to endorse or promote products derived from this software
 
24
          without specific prior written permission.
 
25
 
 
26
    THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
 
27
    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 
28
    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 
29
    DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
 
30
    INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 
31
    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 
32
    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 
33
    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 
34
    STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 
35
    IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 
36
    POSSIBILITY OF SUCH DAMAGE.
 
37
 
 
38
***************************************************************************/
 
39
 
 
40
#include "emu.h"
 
41
#include "profiler.h"
 
42
#include "debugger.h"
 
43
 
 
44
 
 
45
//**************************************************************************
 
46
//  DEBUGGING
 
47
//**************************************************************************
 
48
 
 
49
#define VERBOSE 0
 
50
 
 
51
#define LOG(x)  do { if (VERBOSE) logerror x; } while (0)
 
52
 
 
53
#define TEMPLOG 0
 
54
 
 
55
 
 
56
 
 
57
//**************************************************************************
 
58
//  CONSTANTS
 
59
//**************************************************************************
 
60
 
 
61
// internal trigger IDs
 
62
enum
 
63
{
 
64
        TRIGGER_INT             = -2000,
 
65
        TRIGGER_YIELDTIME       = -3000,
 
66
        TRIGGER_SUSPENDTIME = -4000
 
67
};
 
68
 
 
69
 
 
70
 
 
71
//**************************************************************************
 
72
//  MACROS
 
73
//**************************************************************************
 
74
 
 
75
// these are macros to ensure inlining in device_scheduler::timeslice
 
76
#define ATTOTIME_LT(a,b)                ((a).seconds < (b).seconds || ((a).seconds == (b).seconds && (a).attoseconds < (b).attoseconds))
 
77
#define ATTOTIME_NORMALIZE(a)   do { if ((a).attoseconds >= ATTOSECONDS_PER_SECOND) { (a).seconds++; (a).attoseconds -= ATTOSECONDS_PER_SECOND; } } while (0)
 
78
 
 
79
 
 
80
 
 
81
//**************************************************************************
 
82
//  CORE CPU EXECUTION
 
83
//**************************************************************************
 
84
 
 
85
//-------------------------------------------------
 
86
//  device_scheduler - constructor
 
87
//-------------------------------------------------
 
88
 
 
89
device_scheduler::device_scheduler(running_machine &machine) :
 
90
        m_machine(machine),
 
91
        m_quantum_set(false),
 
92
        m_executing_device(NULL),
 
93
        m_execute_list(NULL)
 
94
{
 
95
}
 
96
 
 
97
 
 
98
//-------------------------------------------------
 
99
//  device_scheduler - destructor
 
100
//-------------------------------------------------
 
101
 
 
102
device_scheduler::~device_scheduler()
 
103
{
 
104
}
 
105
 
 
106
 
 
107
//-------------------------------------------------
 
108
//  timeslice - execute all devices for a single
 
109
//  timeslice
 
110
//-------------------------------------------------
 
111
 
 
112
void device_scheduler::timeslice()
 
113
{
 
114
        bool call_debugger = ((m_machine.debug_flags & DEBUG_FLAG_ENABLED) != 0);
 
115
        timer_execution_state *timerexec = timer_get_execution_state(&m_machine);
 
116
if (TEMPLOG) printf("Timeslice start\n");
 
117
 
 
118
        // build the execution list if we don't have one yet
 
119
        if (m_execute_list == NULL)
 
120
                rebuild_execute_list();
 
121
 
 
122
        // loop until we hit the next timer
 
123
        while (ATTOTIME_LT(timerexec->basetime, timerexec->nextfire))
 
124
        {
 
125
if (TEMPLOG)
 
126
{
 
127
        void timer_print_first_timer(running_machine *machine);
 
128
        printf("Timeslice loop: basetime=%15.6f\n", attotime_to_double(timerexec->basetime));
 
129
        timer_print_first_timer(&m_machine);
 
130
}
 
131
 
 
132
 
 
133
                // by default, assume our target is the end of the next quantum
 
134
                attotime target;
 
135
                target.seconds = timerexec->basetime.seconds;
 
136
                target.attoseconds = timerexec->basetime.attoseconds + timerexec->curquantum;
 
137
                ATTOTIME_NORMALIZE(target);
 
138
 
 
139
                // however, if the next timer is going to fire before then, override
 
140
                assert(attotime_sub(timerexec->nextfire, target).seconds <= 0);
 
141
                if (ATTOTIME_LT(timerexec->nextfire, target))
 
142
                        target = timerexec->nextfire;
 
143
 
 
144
                LOG(("------------------\n"));
 
145
                LOG(("cpu_timeslice: target = %s\n", attotime_string(target, 9)));
 
146
 
 
147
                // apply pending suspension changes
 
148
                UINT32 suspendchanged = 0;
 
149
                for (device_execute_interface *exec = m_execute_list; exec != NULL; exec = exec->m_nextexec)
 
150
                {
 
151
                        suspendchanged |= (exec->m_suspend ^ exec->m_nextsuspend);
 
152
                        exec->m_suspend = exec->m_nextsuspend;
 
153
                        exec->m_nextsuspend &= ~SUSPEND_REASON_TIMESLICE;
 
154
                        exec->m_eatcycles = exec->m_nexteatcycles;
 
155
                }
 
156
 
 
157
                // recompute the execute list if any CPUs changed their suspension state
 
158
                if (suspendchanged != 0)
 
159
                        rebuild_execute_list();
 
160
 
 
161
                // loop over non-suspended CPUs
 
162
                for (device_execute_interface *exec = m_execute_list; exec != NULL; exec = exec->m_nextexec)
 
163
                {
 
164
                        // only process if our target is later than the CPU's current time (coarse check)
 
165
                        if (target.seconds >= exec->m_localtime.seconds)
 
166
                        {
 
167
                                // compute how many attoseconds to execute this CPU
 
168
                                attoseconds_t delta = target.attoseconds - exec->m_localtime.attoseconds;
 
169
                                if (delta < 0 && target.seconds > exec->m_localtime.seconds)
 
170
                                        delta += ATTOSECONDS_PER_SECOND;
 
171
                                assert(delta == attotime_to_attoseconds(attotime_sub(target, exec->m_localtime)));
 
172
 
 
173
                                // if we have enough for at least 1 cycle, do the math
 
174
                                if (delta >= exec->m_attoseconds_per_cycle)
 
175
                                {
 
176
                                        // compute how many cycles we want to execute
 
177
                                        int ran = exec->m_cycles_running = divu_64x32((UINT64)delta >> exec->m_divshift, exec->m_divisor);
 
178
                                        LOG(("  cpu '%s': %d cycles\n", exec->device().tag(), exec->m_cycles_running));
 
179
 
 
180
                                        // if we're not suspended, actually execute
 
181
                                        if (exec->m_suspend == 0)
 
182
                                        {
 
183
                                                profiler_mark_start(exec->m_profiler);
 
184
 
 
185
                                                // note that this global variable cycles_stolen can be modified
 
186
                                                // via the call to cpu_execute
 
187
                                                exec->m_cycles_stolen = 0;
 
188
if (TEMPLOG) printf("Executing %s for %d cycles\n", exec->device().tag(), ran);
 
189
                                                m_executing_device = exec;
 
190
                                                *exec->m_icount = exec->m_cycles_running;
 
191
                                                if (!call_debugger)
 
192
                                                        exec->execute_run();
 
193
                                                else
 
194
                                                {
 
195
                                                        debugger_start_cpu_hook(&exec->device(), target);
 
196
                                                        exec->execute_run();
 
197
                                                        debugger_stop_cpu_hook(&exec->device());
 
198
                                                }
 
199
 
 
200
                                                // adjust for any cycles we took back
 
201
                                                assert(ran >= *exec->m_icount);
 
202
                                                ran -= *exec->m_icount;
 
203
                                                assert(ran >= exec->m_cycles_stolen);
 
204
                                                ran -= exec->m_cycles_stolen;
 
205
                                                profiler_mark_end();
 
206
                                        }
 
207
else
 
208
if (TEMPLOG) printf("Skipping %s for %d cycles\n", exec->device().tag(), ran);
 
209
 
 
210
                                        // account for these cycles
 
211
                                        exec->m_totalcycles += ran;
 
212
 
 
213
                                        // update the local time for this CPU
 
214
                                        attoseconds_t actualdelta = exec->m_attoseconds_per_cycle * ran;
 
215
                                        exec->m_localtime.attoseconds += actualdelta;
 
216
                                        ATTOTIME_NORMALIZE(exec->m_localtime);
 
217
                                        LOG(("         %d ran, %d total, time = %s\n", ran, (INT32)exec->m_totalcycles, attotime_string(exec->m_localtime, 9)));
 
218
 
 
219
                                        // if the new local CPU time is less than our target, move the target up
 
220
                                        if (ATTOTIME_LT(exec->m_localtime, target))
 
221
                                        {
 
222
                                                assert(attotime_compare(exec->m_localtime, target) < 0);
 
223
                                                target = exec->m_localtime;
 
224
 
 
225
                                                // however, if this puts us before the base, clamp to the base as a minimum
 
226
                                                if (ATTOTIME_LT(target, timerexec->basetime))
 
227
                                                {
 
228
                                                        assert(attotime_compare(target, timerexec->basetime) < 0);
 
229
                                                        target = timerexec->basetime;
 
230
                                                }
 
231
                                                LOG(("         (new target)\n"));
 
232
                                        }
 
233
                                }
 
234
                        }
 
235
                }
 
236
                m_executing_device = NULL;
 
237
 
 
238
                // update the base time
 
239
                timerexec->basetime = target;
 
240
        }
 
241
if (TEMPLOG) printf("Timeslice end\n");
 
242
 
 
243
        // execute timers
 
244
        timer_execute_timers(&m_machine);
 
245
}
 
246
 
 
247
 
 
248
//-------------------------------------------------
 
249
//  boost_interleave - temporarily boosts the
 
250
//  interleave factor
 
251
//-------------------------------------------------
 
252
 
 
253
void device_scheduler::boost_interleave(attotime timeslice_time, attotime boost_duration)
 
254
{
 
255
        // ignore timeslices > 1 second
 
256
        if (timeslice_time.seconds > 0)
 
257
                return;
 
258
        timer_add_scheduling_quantum(&m_machine, timeslice_time.attoseconds, boost_duration);
 
259
}
 
260
 
 
261
 
 
262
//-------------------------------------------------
 
263
//  eat_all_cycles - eat a ton of cycles on all
 
264
//  CPUs to force a quick exit
 
265
//-------------------------------------------------
 
266
 
 
267
void device_scheduler::eat_all_cycles()
 
268
{
 
269
        for (device_execute_interface *exec = m_execute_list; exec != NULL; exec = exec->m_nextexec)
 
270
                exec->eat_cycles(1000000000);
 
271
}
 
272
 
 
273
 
 
274
 
 
275
//**************************************************************************
 
276
//  GLOBAL HELPERS
 
277
//**************************************************************************
 
278
 
 
279
//-------------------------------------------------
 
280
//  cpuexec_abort_timeslice - abort execution
 
281
//  for the current timeslice
 
282
//-------------------------------------------------
 
283
 
 
284
void device_scheduler::abort_timeslice()
 
285
{
 
286
        if (m_executing_device != NULL)
 
287
                m_executing_device->abort_timeslice();
 
288
}
 
289
 
 
290
 
 
291
//-------------------------------------------------
 
292
//  trigger - generate a global trigger
 
293
//-------------------------------------------------
 
294
 
 
295
void device_scheduler::trigger(int trigid, attotime after)
 
296
{
 
297
        // ensure we have a list of executing devices
 
298
        if (m_execute_list == NULL)
 
299
                rebuild_execute_list();
 
300
 
 
301
        // if we have a non-zero time, schedule a timer
 
302
        if (after.attoseconds != 0 || after.seconds != 0)
 
303
                timer_set(&m_machine, after, (void *)this, trigid, static_timed_trigger);
 
304
 
 
305
        // send the trigger to everyone who cares
 
306
        else
 
307
                for (device_execute_interface *exec = m_execute_list; exec != NULL; exec = exec->m_nextexec)
 
308
                        exec->trigger(trigid);
 
309
}
 
310
 
 
311
 
 
312
//-------------------------------------------------
 
313
//  static_timed_trigger - generate a trigger
 
314
//  after a given amount of time
 
315
//-------------------------------------------------
 
316
 
 
317
TIMER_CALLBACK( device_scheduler::static_timed_trigger )
 
318
{
 
319
        reinterpret_cast<device_scheduler *>(ptr)->trigger(param);
 
320
}
 
321
 
 
322
 
 
323
//-------------------------------------------------
 
324
//  compute_perfect_interleave - compute the
 
325
//  "perfect" interleave interval
 
326
//-------------------------------------------------
 
327
 
 
328
void device_scheduler::compute_perfect_interleave()
 
329
{
 
330
        // ensure we have a list of executing devices
 
331
        if (m_execute_list == NULL)
 
332
                rebuild_execute_list();
 
333
 
 
334
        // start with the first one
 
335
        device_execute_interface *first = m_execute_list;
 
336
        if (first != NULL)
 
337
        {
 
338
                attoseconds_t smallest = first->minimum_quantum();
 
339
                attoseconds_t perfect = ATTOSECONDS_PER_SECOND - 1;
 
340
 
 
341
                // start with a huge time factor and find the 2nd smallest cycle time
 
342
                for (device_execute_interface *exec = first->m_nextexec; exec != NULL; exec = exec->m_nextexec)
 
343
                {
 
344
                        attoseconds_t curquantum = exec->minimum_quantum();
 
345
 
 
346
                        // find the 2nd smallest cycle interval
 
347
                        if (curquantum < smallest)
 
348
                        {
 
349
                                perfect = smallest;
 
350
                                smallest = curquantum;
 
351
                        }
 
352
                        else if (curquantum < perfect)
 
353
                                perfect = curquantum;
 
354
                }
 
355
 
 
356
                // adjust the final value
 
357
                timer_set_minimum_quantum(&m_machine, perfect);
 
358
 
 
359
                LOG(("Perfect interleave = %.9f, smallest = %.9f\n", ATTOSECONDS_TO_DOUBLE(perfect), ATTOSECONDS_TO_DOUBLE(smallest)));
 
360
        }
 
361
}
 
362
 
 
363
 
 
364
//-------------------------------------------------
 
365
//  rebuild_execute_list - rebuild the list of
 
366
//  executing CPUs, moving suspended CPUs to the
 
367
//  end
 
368
//-------------------------------------------------
 
369
 
 
370
void device_scheduler::rebuild_execute_list()
 
371
{
 
372
        // if we haven't yet set a scheduling quantum, do it now
 
373
        if (!m_quantum_set)
 
374
        {
 
375
                // set the core scheduling quantum
 
376
                attotime min_quantum = m_machine.config->m_minimum_quantum;
 
377
 
 
378
                // if none specified default to 60Hz
 
379
                if (attotime_compare(min_quantum, attotime_zero) == 0)
 
380
                        min_quantum = ATTOTIME_IN_HZ(60);
 
381
 
 
382
                // if the configuration specifies a device to make perfect, pick that as the minimum
 
383
                if (m_machine.config->m_perfect_cpu_quantum != NULL)
 
384
                {
 
385
                        device_t *device = m_machine.device(m_machine.config->m_perfect_cpu_quantum);
 
386
                        if (device == NULL)
 
387
                                fatalerror("Device '%s' specified for perfect interleave is not present!", m_machine.config->m_perfect_cpu_quantum);
 
388
 
 
389
                        device_execute_interface *exec;
 
390
                        if (!device->interface(exec))
 
391
                                fatalerror("Device '%s' specified for perfect interleave is not an executing device!", m_machine.config->m_perfect_cpu_quantum);
 
392
 
 
393
                        attotime cpu_quantum = attotime_make(0, exec->minimum_quantum());
 
394
                        min_quantum = attotime_min(cpu_quantum, min_quantum);
 
395
                }
 
396
 
 
397
                // inform the timer system of our decision
 
398
                assert(min_quantum.seconds == 0);
 
399
                timer_add_scheduling_quantum(&m_machine, min_quantum.attoseconds, attotime_never);
 
400
if (TEMPLOG) printf("Setting quantum: %08X%08X\n", (UINT32)(min_quantum.attoseconds >> 32), (UINT32)min_quantum.attoseconds);
 
401
                m_quantum_set = true;
 
402
        }
 
403
 
 
404
        // start with an empty list
 
405
        device_execute_interface **active_tailptr = &m_execute_list;
 
406
        *active_tailptr = NULL;
 
407
 
 
408
        // also make an empty list of suspended devices
 
409
        device_execute_interface *suspend_list = NULL;
 
410
        device_execute_interface **suspend_tailptr = &suspend_list;
 
411
 
 
412
        // iterate over all devices
 
413
        device_execute_interface *exec = NULL;
 
414
        for (bool gotone = m_machine.m_devicelist.first(exec); gotone; gotone = exec->next(exec))
 
415
        {
 
416
                // append to the appropriate list
 
417
                exec->m_nextexec = NULL;
 
418
                if (exec->m_suspend == 0)
 
419
                {
 
420
                        *active_tailptr = exec;
 
421
                        active_tailptr = &exec->m_nextexec;
 
422
                }
 
423
                else
 
424
                {
 
425
                        *suspend_tailptr = exec;
 
426
                        suspend_tailptr = &exec->m_nextexec;
 
427
                }
 
428
        }
 
429
 
 
430
        // append the suspend list to the end of the active list
 
431
        *active_tailptr = suspend_list;
 
432
if (TEMPLOG)
 
433
{
 
434
        printf("Execute list:");
 
435
        for (exec = m_execute_list; exec != NULL; exec = exec->m_nextexec)
 
436
                printf(" %s", exec->device().tag());
 
437
        printf("\n");
 
438
}
 
439
}