Develop and Download Open Source Software

Browse CVS Repository

Contents of /mame32jp/mame32jp/src/cpuexec.c

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.5 - (show annotations) (download) (as text)
Wed Apr 24 03:53:20 2002 UTC (21 years, 11 months ago) by zero
Branch: MAIN
CVS Tags: ver_0_60_1, ver0_59_13, ver0_59_14, ver0_60_2, ver0_60_3, ver0_60_4, ver0_60_5, HEAD
Changes since 1.4: +0 -0 lines
File MIME type: text/x-csrc
*** empty log message ***

1 /***************************************************************************
2
3 cpuexec.c
4
5 Core multi-CPU execution engine.
6
7 ***************************************************************************/
8
9 #include <signal.h>
10 #include "driver.h"
11 #include "timer.h"
12 #include "state.h"
13 #include "mamedbg.h"
14 #include "hiscore.h"
15
16 #if (HAS_M68000 || HAS_M68010 || HAS_M68020 || HAS_M68EC020)
17 #include "cpu/m68000/m68000.h"
18 #endif
19
20
21 /*************************************
22 *
23 * Debug logging
24 *
25 *************************************/
26
27 #define VERBOSE 0
28
29 #if VERBOSE
30 #define LOG(x) logerror x
31 #else
32 #define LOG(x)
33 #endif
34
35
36
37 /*************************************
38 *
39 * Macros to help verify active CPU
40 *
41 *************************************/
42
43 #define VERIFY_ACTIVECPU(retval, name) \
44 int activecpu = cpu_getactivecpu(); \
45 if (activecpu < 0) \
46 { \
47 logerror(#name "() called with no active cpu!\n"); \
48 return retval; \
49 }
50
51 #define VERIFY_ACTIVECPU_VOID(name) \
52 int activecpu = cpu_getactivecpu(); \
53 if (activecpu < 0) \
54 { \
55 logerror(#name "() called with no active cpu!\n"); \
56 return; \
57 }
58
59
60
61 /*************************************
62 *
63 * Triggers for the timer system
64 *
65 *************************************/
66
67 enum
68 {
69 TRIGGER_TIMESLICE = -1000,
70 TRIGGER_INT = -2000,
71 TRIGGER_YIELDTIME = -3000,
72 TRIGGER_SUSPENDTIME = -4000
73 };
74
75
76
77 /*************************************
78 *
79 * Internal CPU info structure
80 *
81 *************************************/
82
83 struct cpuinfo
84 {
85 int iloops; /* number of interrupts remaining this frame */
86 int totalcycles; /* total CPU cycles executed */
87 int vblankint_countdown; /* number of vblank callbacks left until we interrupt */
88 int vblankint_multiplier; /* number of vblank callbacks per interrupt */
89 void * vblankint_timer; /* reference to elapsed time counter */
90 double vblankint_period; /* timing period of the VBLANK interrupt */
91 void * timedint_timer; /* reference to this CPU's timer */
92 double timedint_period; /* timing period of the timed interrupt */
93 };
94
95
96
97 /*************************************
98 *
99 * General CPU variables
100 *
101 *************************************/
102
103 static struct cpuinfo cpu[MAX_CPU];
104
105 static int time_to_reset;
106 #ifdef MAME32JP
107 int time_to_quit;
108 #else
109 static int time_to_quit;
110 #endif
111
112 static int vblank;
113 static int current_frame;
114 static INT32 watchdog_counter;
115
116 static int cycles_running;
117
118
119
120 /*************************************
121 *
122 * Timer variables
123 *
124 *************************************/
125
126 static void *vblank_timer;
127 static int vblank_countdown;
128 static int vblank_multiplier;
129 static double vblank_period;
130
131 static void *refresh_timer;
132 static double refresh_period;
133 static double refresh_period_inv;
134
135 static void *timeslice_timer;
136 static double timeslice_period;
137
138 static double scanline_period;
139 static double scanline_period_inv;
140
141
142
143 /*************************************
144 *
145 * Save/load variables
146 *
147 *************************************/
148
149 static int loadsave_schedule;
150 static char loadsave_schedule_id;
151
152
153
154 /*************************************
155 *
156 * Static prototypes
157 *
158 *************************************/
159
160 static void cpu_inittimers(void);
161 static void cpu_vblankreset(void);
162 static void cpu_vblankcallback(int param);
163 static void cpu_updatecallback(int param);
164
165 static void handle_loadsave(void);
166
167
168
169 #if 0
170 #pragma mark CORE CPU
171 #endif
172
173 /*************************************
174 *
175 * Initialize all the CPUs
176 *
177 *************************************/
178
179 int cpu_init(void)
180 {
181 int cpunum;
182
183 #ifdef MAME_FIX
184 /* clear CPU info structure */
185 memset(cpu, 0, sizeof(struct cpuinfo) * MAX_CPU);
186 #endif
187
188 /* initialize the interfaces first */
189 if (cpuintrf_init())
190 return 1;
191
192 /* loop over all our CPUs */
193 for (cpunum = 0; cpunum < MAX_CPU; cpunum++)
194 {
195 int cputype = Machine->drv->cpu[cpunum].cpu_type & ~CPU_FLAGS_MASK;
196
197 /* if this is a dummy, stop looking */
198 if (cputype == CPU_DUMMY)
199 break;
200
201 /* set the save state tag */
202 state_save_set_current_tag(cpunum + 1);
203
204 /* initialize this CPU */
205 if (cpuintrf_init_cpu(cpunum, cputype))
206 return 1;
207 }
208
209 /* save some stuff in tag 0 */
210 state_save_set_current_tag(0);
211 state_save_register_INT32("cpu", 0, "watchdog count", &watchdog_counter, 1);
212
213 /* reset the IRQ lines and save those */
214 if (cpuint_init())
215 return 1;
216
217 return 0;
218 }
219
220
221
222 /*************************************
223 *
224 * Prepare the system for execution
225 *
226 *************************************/
227
228 static void cpu_pre_run(void)
229 {
230 int cpunum;
231
232 logerror("Machine reset\n");
233
234 begin_resource_tracking();
235
236 /* read hi scores information from hiscore.dat */
237 hs_open(Machine->gamedrv->name);
238 hs_init();
239
240 /* initialize the various timers (suspends all CPUs at startup) */
241 cpu_inittimers();
242 watchdog_counter = -1;
243
244 /* reset sound chips */
245 sound_reset();
246
247 /* first pass over CPUs */
248 for (cpunum = 0; cpunum < cpu_gettotalcpu(); cpunum++)
249 {
250 /* enable all CPUs (except for audio CPUs if the sound is off) */
251 if (!(Machine->drv->cpu[cpunum].cpu_type & CPU_AUDIO_CPU) || Machine->sample_rate != 0)
252 timer_suspendcpu(cpunum, 0, SUSPEND_ANY_REASON);
253 else
254 timer_suspendcpu(cpunum, 1, SUSPEND_REASON_DISABLE);
255
256 /* reset the interrupt state */
257 cpuint_reset_cpu(cpunum);
258
259 /* reset the total number of cycles */
260 cpu[cpunum].totalcycles = 0;
261 }
262
263 vblank = 0;
264
265 /* do this AFTER the above so machine_init() can use cpu_halt() to hold the */
266 /* execution of some CPUs, or disable interrupts */
267 if (Machine->drv->machine_init)
268 (*Machine->drv->machine_init)();
269
270 /* now reset each CPU */
271 for (cpunum = 0; cpunum < cpu_gettotalcpu(); cpunum++)
272 cpunum_reset(cpunum, Machine->drv->cpu[cpunum].reset_param, cpu_irq_callbacks[cpunum]);
273
274 /* reset the globals */
275 cpu_vblankreset();
276 current_frame = 0;
277 state_save_dump_registry();
278 }
279
280
281
282 /*************************************
283 *
284 * Finish up execution
285 *
286 *************************************/
287
288 static void cpu_post_run(void)
289 {
290 /* write hi scores to disk - No scores saving if cheat */
291 hs_close();
292
293 /* stop the machine */
294 if (Machine->drv->machine_stop)
295 (*Machine->drv->machine_stop)();
296
297 end_resource_tracking();
298 }
299
300
301
302 /*************************************
303 *
304 * Execute until done
305 *
306 *************************************/
307
308 void cpu_run(void)
309 {
310 int cpunum;
311
312 #ifdef MAME_DEBUG
313 /* initialize the debugger */
314 if (mame_debug)
315 mame_debug_init();
316 #endif
317
318 /* loop over multiple resets, until the user quits */
319 time_to_quit = 0;
320 while (!time_to_quit)
321 {
322 /* prepare everything to run */
323 cpu_pre_run();
324
325 /* loop until the user quits or resets */
326 time_to_reset = 0;
327 while (!time_to_quit && !time_to_reset)
328 {
329 profiler_mark(PROFILER_EXTRA);
330
331 /* if we have a load/save scheduled, handle it */
332 if (loadsave_schedule != LOADSAVE_NONE)
333 handle_loadsave();
334
335 /* ask the timer system to schedule */
336 if (timer_schedule_cpu(&cpunum, &cycles_running))
337 {
338 int ran;
339
340 /* run for the requested number of cycles */
341 profiler_mark(PROFILER_CPU1 + cpunum);
342 ran = cpunum_execute(cpunum, cycles_running);
343 profiler_mark(PROFILER_END);
344
345 /* update based on how many cycles we really ran */
346 cpu[cpunum].totalcycles += ran;
347
348 /* update the timer with how long we actually ran */
349 timer_update_cpu(cpunum, ran);
350 }
351
352 profiler_mark(PROFILER_END);
353 }
354
355 /* finish up this iteration */
356 cpu_post_run();
357 }
358
359 #ifdef MAME_DEBUG
360 /* shut down the debugger */
361 if (mame_debug)
362 mame_debug_exit();
363 #endif
364 }
365
366
367
368 /*************************************
369 *
370 * Deinitialize all the CPUs
371 *
372 *************************************/
373
374 void cpu_exit(void)
375 {
376 int cpunum;
377
378 /* shut down the CPU cores */
379 for (cpunum = 0; cpunum < cpu_gettotalcpu(); cpunum++)
380 cpuintrf_exit_cpu(cpunum);
381 }
382
383
384
385 /*************************************
386 *
387 * Force a reset at the end of this
388 * timeslice
389 *
390 *************************************/
391
392 void machine_reset(void)
393 {
394 time_to_reset = 1;
395 }
396
397
398
399
400 #if 0
401 #pragma mark -
402 #pragma mark SAVE/RESTORE
403 #endif
404
405 /*************************************
406 *
407 * Handle saves at runtime
408 *
409 *************************************/
410
411 static void handle_save(void)
412 {
413 char name[2] = { 0 };
414 void *file;
415 int cpunum;
416
417 /* open the file */
418 name[0] = loadsave_schedule_id;
419 file = osd_fopen(Machine->gamedrv->name, name, OSD_FILETYPE_STATE, 1);
420
421 /* write the save state */
422 state_save_save_begin(file);
423
424 /* write tag 0 */
425 state_save_set_current_tag(0);
426 state_save_save_continue();
427
428 /* loop over CPUs */
429 for (cpunum = 0; cpunum < cpu_gettotalcpu(); cpunum++)
430 {
431 cpuintrf_push_context(cpunum);
432
433 /* make sure banking is set */
434 activecpu_reset_banking();
435
436 /* save the CPU data */
437 state_save_set_current_tag(cpunum + 1);
438 state_save_save_continue();
439
440 cpuintrf_pop_context();
441 }
442
443 /* finish and close */
444 state_save_save_finish();
445 osd_fclose(file);
446
447 /* unschedule the save */
448 loadsave_schedule = LOADSAVE_NONE;
449 }
450
451
452
453 /*************************************
454 *
455 * Handle loads at runtime
456 *
457 *************************************/
458
459 static void handle_load(void)
460 {
461 char name[2] = { 0 };
462 void *file;
463 int cpunum;
464
465 /* open the file */
466 name[0] = loadsave_schedule_id;
467 file = osd_fopen(Machine->gamedrv->name, name, OSD_FILETYPE_STATE, 0);
468
469 /* if successful, load it */
470 if (file)
471 {
472 /* start loading */
473 if (!state_save_load_begin(file))
474 {
475 /* read tag 0 */
476 state_save_set_current_tag(0);
477 state_save_load_continue();
478
479 /* loop over CPUs */
480 for (cpunum = 0; cpunum < cpu_gettotalcpu(); cpunum++)
481 {
482 cpuintrf_push_context(cpunum);
483
484 /* make sure banking is set */
485 activecpu_reset_banking();
486
487 /* load the CPU data */
488 state_save_set_current_tag(cpunum + 1);
489 state_save_load_continue();
490
491 cpuintrf_pop_context();
492 }
493
494 /* finish and close */
495 state_save_load_finish();
496 }
497 osd_fclose(file);
498 }
499
500 /* unschedule the load */
501 loadsave_schedule = LOADSAVE_NONE;
502 }
503
504
505
506 /*************************************
507 *
508 * Handle saves & loads at runtime
509 *
510 *************************************/
511
512 static void handle_loadsave(void)
513 {
514 /* it's one or the other */
515 if (loadsave_schedule == LOADSAVE_SAVE)
516 handle_save();
517 else if (loadsave_schedule == LOADSAVE_LOAD)
518 handle_load();
519
520 /* reset the schedule */
521 loadsave_schedule = LOADSAVE_NONE;
522 }
523
524
525
526 /*************************************
527 *
528 * Schedules a save/load for later
529 *
530 *************************************/
531
532 void cpu_loadsave_schedule(int type, char id)
533 {
534 loadsave_schedule = type;
535 loadsave_schedule_id = id;
536 }
537
538
539
540 /*************************************
541 *
542 * Unschedules any saves or loads
543 *
544 *************************************/
545
546 void cpu_loadsave_reset(void)
547 {
548 loadsave_schedule = LOADSAVE_NONE;
549 }
550
551
552 #if 0
553 #pragma mark -
554 #pragma mark WATCHDOG
555 #endif
556
557 /*************************************
558 *
559 * Watchdog routines
560 *
561 *************************************/
562
563 /*--------------------------------------------------------------
564
565 Use these functions to initialize, and later maintain, the
566 watchdog. For convenience, when the machine is reset, the
567 watchdog is disabled. If you call this function, the
568 watchdog is initialized, and from that point onwards, if you
569 don't call it at least once every 3 seconds, the machine
570 will be reset.
571
572 The 3 seconds delay is targeted at qzshowby, which otherwise
573 would reset at the start of a game.
574
575 --------------------------------------------------------------*/
576
577 static void watchdog_reset(void)
578 {
579 if (watchdog_counter == -1)
580 logerror("watchdog armed\n");
581 watchdog_counter = 3 * Machine->drv->frames_per_second;
582 }
583
584
585 WRITE_HANDLER( watchdog_reset_w )
586 {
587 watchdog_reset();
588 }
589
590
591 READ_HANDLER( watchdog_reset_r )
592 {
593 watchdog_reset();
594 return 0xff;
595 }
596
597
598 WRITE16_HANDLER( watchdog_reset16_w )
599 {
600 watchdog_reset();
601 }
602
603
604 READ16_HANDLER( watchdog_reset16_r )
605 {
606 watchdog_reset();
607 return 0xffff;
608 }
609
610
611 WRITE32_HANDLER( watchdog_reset32_w )
612 {
613 watchdog_reset();
614 }
615
616
617 READ32_HANDLER( watchdog_reset32_r )
618 {
619 watchdog_reset();
620 return 0xffffffff;
621 }
622
623
624
625 #if 0
626 #pragma mark -
627 #pragma mark HALT/RESET
628 #endif
629
630 /*************************************
631 *
632 * Handle reset line changes
633 *
634 *************************************/
635
636 static void reset_callback(int param)
637 {
638 int cpunum = param & 0xff;
639 int state = param >> 8;
640
641 /* if we're asserting the line, just halt the CPU */
642 if (state == ASSERT_LINE)
643 {
644 timer_suspendcpu(cpunum, 1, SUSPEND_REASON_RESET);
645 return;
646 }
647
648 /* if we're clearing the line that was previously asserted, or if we're just */
649 /* pulsing the line, reset the CPU */
650 if ((state == CLEAR_LINE && timer_iscpususpended(cpunum, SUSPEND_REASON_RESET)) || state == PULSE_LINE)
651 cpunum_reset(cpunum, Machine->drv->cpu[cpunum].reset_param, cpu_irq_callbacks[cpunum]);
652
653 /* if we're clearing the line, make sure the CPU is not halted */
654 timer_suspendcpu(cpunum, 0, SUSPEND_REASON_RESET);
655 }
656
657
658 void cpu_set_reset_line(int cpunum, int state)
659 {
660 timer_set(TIME_NOW, (cpunum & 0xff) | (state << 8), reset_callback);
661 }
662
663
664
665 /*************************************
666 *
667 * Handle halt line changes
668 *
669 *************************************/
670
671 static void halt_callback(int param)
672 {
673 int cpunum = param & 0xff;
674 int state = param >> 8;
675
676 /* if asserting, halt the CPU */
677 if (state == ASSERT_LINE)
678 timer_suspendcpu(cpunum, 1, SUSPEND_REASON_HALT);
679
680 /* if clearing, unhalt the CPU */
681 else if (state == CLEAR_LINE)
682 timer_suspendcpu(cpunum, 0, SUSPEND_REASON_HALT);
683 }
684
685
686 void cpu_set_halt_line(int cpunum, int state)
687 {
688 timer_set(TIME_NOW, (cpunum & 0xff) | (state << 8), halt_callback);
689 }
690
691
692
693 /*************************************
694 *
695 * Return suspended status of CPU
696 *
697 *************************************/
698
699 int cpu_getstatus(int cpunum)
700 {
701 if (cpunum < cpu_gettotalcpu())
702 return !timer_iscpususpended(cpunum, SUSPEND_REASON_HALT | SUSPEND_REASON_RESET | SUSPEND_REASON_DISABLE);
703 return 0;
704 }
705
706
707
708 #if 0
709 #pragma mark -
710 #pragma mark TIMING HELPERS
711 #endif
712
713 /*************************************
714 *
715 * Return cycles ran this iteration
716 *
717 *************************************/
718
719 int cycles_currently_ran(void)
720 {
721 VERIFY_ACTIVECPU(0, cycles_currently_ran);
722 return cycles_running - activecpu_get_icount();
723 }
724
725
726
727 /*************************************
728 *
729 * Return cycles remaining in this
730 * iteration
731 *
732 *************************************/
733
734 int cycles_left_to_run(void)
735 {
736 VERIFY_ACTIVECPU(0, cycles_left_to_run);
737 return activecpu_get_icount();
738 }
739
740
741
742 /*************************************
743 *
744 * Return total number of CPU cycles
745 * for the active CPU.
746 *
747 *************************************/
748
749 /*--------------------------------------------------------------
750
751 IMPORTANT: this value wraps around in a relatively short
752 time. For example, for a 6MHz CPU, it will wrap around in
753 2^32/6000000 = 716 seconds = 12 minutes.
754 Make sure you don't do comparisons between values returned
755 by this function, but only use the difference (which will
756 be correct regardless of wraparound).
757
758 --------------------------------------------------------------*/
759
760 int cpu_gettotalcycles(void)
761 {
762 VERIFY_ACTIVECPU(0, cpu_gettotalcycles);
763 return cpu[activecpu].totalcycles + cycles_currently_ran();
764 }
765
766
767
768 /*************************************
769 *
770 * Return cycles until next interrupt
771 * handler call
772 *
773 *************************************/
774
775 int cpu_geticount(void)
776 {
777 int result;
778
779 /* remove me - only used by mamedbg, m92 */
780 VERIFY_ACTIVECPU(0, cpu_geticount);
781 result = TIME_TO_CYCLES(activecpu, cpu[activecpu].vblankint_period - timer_timeelapsed(cpu[activecpu].vblankint_timer));
782 return (result < 0) ? 0 : result;
783 }
784
785
786
787 /*************************************
788 *
789 * Scales a given value by the fraction
790 * of time elapsed between refreshes
791 *
792 *************************************/
793
794 int cpu_scalebyfcount(int value)
795 {
796 int result = (int)((double)value * timer_timeelapsed(refresh_timer) * refresh_period_inv);
797 if (value >= 0)
798 return (result < value) ? result : value;
799 else
800 return (result > value) ? result : value;
801 }
802
803
804
805 #if 0
806 #pragma mark -
807 #pragma mark VIDEO TIMING
808 #endif
809
810 /*************************************
811 *
812 * Creates the refresh timer
813 *
814 *************************************/
815
816 void cpu_init_refresh_timer(void)
817 {
818 /* allocate an infinite timer to track elapsed time since the last refresh */
819 refresh_period = TIME_IN_HZ(Machine->drv->frames_per_second);
820 refresh_period_inv = 1.0 / refresh_period;
821 refresh_timer = timer_alloc(NULL);
822
823 /* while we're at it, compute the scanline times */
824 if (Machine->drv->vblank_duration)
825 scanline_period = (refresh_period - TIME_IN_USEC(Machine->drv->vblank_duration)) /
826 (double)(Machine->drv->default_visible_area.max_y - Machine->drv->default_visible_area.min_y + 1);
827 else
828 scanline_period = refresh_period / (double)Machine->drv->screen_height;
829 scanline_period_inv = 1.0 / scanline_period;
830 }
831
832
833
834 /*************************************
835 *
836 * Returns the current scanline
837 *
838 *************************************/
839
840 /*--------------------------------------------------------------
841
842 Note: cpu_getscanline() counts from 0, 0 being the first
843 visible line. You might have to adjust this value to match
844 the hardware, since in many cases the first visible line
845 is >0.
846
847 --------------------------------------------------------------*/
848
849 int cpu_getscanline(void)
850 {
851 return (int)(timer_timeelapsed(refresh_timer) * scanline_period_inv);
852 }
853
854
855
856 /*************************************
857 *
858 * Returns time until given scanline
859 *
860 *************************************/
861
862 double cpu_getscanlinetime(int scanline)
863 {
864 double scantime = timer_starttime(refresh_timer) + (double)scanline * scanline_period;
865 double abstime = timer_get_time();
866 double result;
867
868 /* if we're already past the computed time, count it for the next frame */
869 if (abstime >= scantime)
870 scantime += TIME_IN_HZ(Machine->drv->frames_per_second);
871
872 /* compute how long from now until that time */
873 result = scantime - abstime;
874
875 /* if it's small, just count a whole frame */
876 if (result < TIME_IN_NSEC(1))
877 result = TIME_IN_HZ(Machine->drv->frames_per_second);
878 return result;
879 }
880
881
882
883 /*************************************
884 *
885 * Returns time for one scanline
886 *
887 *************************************/
888
889 double cpu_getscanlineperiod(void)
890 {
891 return scanline_period;
892 }
893
894
895
896 /*************************************
897 *
898 * Returns a crude approximation
899 * of the horizontal position of the
900 * bream
901 *
902 *************************************/
903
904 int cpu_gethorzbeampos(void)
905 {
906 double elapsed_time = timer_timeelapsed(refresh_timer);
907 int scanline = (int)(elapsed_time * scanline_period_inv);
908 double time_since_scanline = elapsed_time - (double)scanline * scanline_period;
909 return (int)(time_since_scanline * scanline_period_inv * (double)Machine->drv->screen_width);
910 }
911
912
913
914 /*************************************
915 *
916 * Returns the VBLANK state
917 *
918 *************************************/
919
920 int cpu_getvblank(void)
921 {
922 return vblank;
923 }
924
925
926
927 /*************************************
928 *
929 * Returns the current frame count
930 *
931 *************************************/
932
933 int cpu_getcurrentframe(void)
934 {
935 return current_frame;
936 }
937
938
939
940 #if 0
941 #pragma mark -
942 #pragma mark SYNCHRONIZATION
943 #endif
944
945 /*************************************
946 *
947 * Generate a specific trigger
948 *
949 *************************************/
950
951 void cpu_trigger(int trigger)
952 {
953 timer_trigger(trigger);
954 }
955
956
957
958 /*************************************
959 *
960 * Generate a trigger in the future
961 *
962 *************************************/
963
964 void cpu_triggertime(double duration, int trigger)
965 {
966 timer_set(duration, trigger, cpu_trigger);
967 }
968
969
970
971 /*************************************
972 *
973 * Generate a trigger for an int
974 *
975 *************************************/
976
977 void cpu_triggerint(int cpunum)
978 {
979 timer_trigger(TRIGGER_INT + cpunum);
980 }
981
982
983
984 /*************************************
985 *
986 * Burn/yield CPU cycles until a trigger
987 *
988 *************************************/
989
990 void cpu_spinuntil_trigger(int trigger)
991 {
992 VERIFY_ACTIVECPU_VOID(cpu_spinuntil_trigger);
993 timer_suspendcpu_trigger(activecpu, trigger);
994 }
995
996
997 void cpu_yielduntil_trigger(int trigger)
998 {
999 VERIFY_ACTIVECPU_VOID(cpu_yielduntil_trigger);
1000 timer_holdcpu_trigger(activecpu, trigger);
1001 }
1002
1003
1004
1005 /*************************************
1006 *
1007 * Burn/yield CPU cycles until an
1008 * interrupt
1009 *
1010 *************************************/
1011
1012 void cpu_spinuntil_int(void)
1013 {
1014 VERIFY_ACTIVECPU_VOID(cpu_spinuntil_int);
1015 cpu_spinuntil_trigger(TRIGGER_INT + activecpu);
1016 }
1017
1018
1019 void cpu_yielduntil_int(void)
1020 {
1021 VERIFY_ACTIVECPU_VOID(cpu_yielduntil_int);
1022 cpu_yielduntil_trigger(TRIGGER_INT + activecpu);
1023 }
1024
1025
1026
1027 /*************************************
1028 *
1029 * Burn/yield CPU cycles until the
1030 * end of the current timeslice
1031 *
1032 *************************************/
1033
1034 void cpu_spin(void)
1035 {
1036 cpu_spinuntil_trigger(TRIGGER_TIMESLICE);
1037 }
1038
1039
1040 void cpu_yield(void)
1041 {
1042 cpu_yielduntil_trigger(TRIGGER_TIMESLICE);
1043 }
1044
1045
1046
1047 /*************************************
1048 *
1049 * Burn/yield CPU cycles for a
1050 * specific period of time
1051 *
1052 *************************************/
1053
1054 void cpu_spinuntil_time(double duration)
1055 {
1056 static int timetrig = 0;
1057
1058 cpu_spinuntil_trigger(TRIGGER_SUSPENDTIME + timetrig);
1059 cpu_triggertime(duration, TRIGGER_SUSPENDTIME + timetrig);
1060 timetrig = (timetrig + 1) & 255;
1061 }
1062
1063
1064 void cpu_yielduntil_time(double duration)
1065 {
1066 static int timetrig = 0;
1067
1068 cpu_yielduntil_trigger(TRIGGER_YIELDTIME + timetrig);
1069 cpu_triggertime(duration, TRIGGER_YIELDTIME + timetrig);
1070 timetrig = (timetrig + 1) & 255;
1071 }
1072
1073
1074
1075 #if 0
1076 #pragma mark -
1077 #pragma mark CORE TIMING
1078 #endif
1079
1080 /*************************************
1081 *
1082 * Returns the number of times the
1083 * interrupt handler will be called
1084 * before the end of the current
1085 * video frame.
1086 *
1087 *************************************/
1088
1089 /*--------------------------------------------------------------
1090
1091 This can be useful to interrupt handlers to synchronize
1092 their operation. If you call this from outside an interrupt
1093 handler, add 1 to the result, i.e. if it returns 0, it means
1094 that the interrupt handler will be called once.
1095
1096 --------------------------------------------------------------*/
1097
1098 int cpu_getiloops(void)
1099 {
1100 VERIFY_ACTIVECPU(0, cpu_getiloops);
1101 return cpu[activecpu].iloops;
1102 }
1103
1104
1105
1106 /*************************************
1107 *
1108 * Hook for updating things on the
1109 * real VBLANK (once per frame)
1110 *
1111 *************************************/
1112
1113 static void cpu_vblankreset(void)
1114 {
1115 int cpunum;
1116
1117 /* read hi scores from disk */
1118 hs_update();
1119
1120 /* read keyboard & update the status of the input ports */
1121 update_input_ports();
1122
1123 /* reset the cycle counters */
1124 for (cpunum = 0; cpunum < cpu_gettotalcpu(); cpunum++)
1125 {
1126 if (!timer_iscpususpended(cpunum, SUSPEND_REASON_DISABLE))
1127 cpu[cpunum].iloops = Machine->drv->cpu[cpunum].vblank_interrupts_per_frame - 1;
1128 else
1129 cpu[cpunum].iloops = -1;
1130 }
1131 }
1132
1133
1134
1135 /*************************************
1136 *
1137 * First-run callback for VBLANKs
1138 *
1139 *************************************/
1140
1141 static void cpu_firstvblankcallback(int param)
1142 {
1143 /* now that we're synced up, pulse from here on out */
1144 timer_adjust(vblank_timer, vblank_period, param, vblank_period);
1145
1146 /* but we need to call the standard routine as well */
1147 cpu_vblankcallback(param);
1148 }
1149
1150
1151
1152 /*************************************
1153 *
1154 * VBLANK core handler
1155 *
1156 *************************************/
1157
1158 static void cpu_vblankcallback(int param)
1159 {
1160 int cpunum;
1161
1162 /* loop over CPUs */
1163 for (cpunum = 0; cpunum < cpu_gettotalcpu(); cpunum++)
1164 {
1165 /* if the interrupt multiplier is valid */
1166 if (cpu[cpunum].vblankint_multiplier != -1)
1167 {
1168 /* decrement; if we hit zero, generate the interrupt and reset the countdown */
1169 if (!--cpu[cpunum].vblankint_countdown)
1170 {
1171 /* a param of -1 means don't call any callbacks */
1172 if (param != -1)
1173 {
1174 /* if the CPU has a VBLANK handler, call it */
1175 if (Machine->drv->cpu[cpunum].vblank_interrupt && cpu_getstatus(cpunum))
1176 {
1177 cpuintrf_push_context(cpunum);
1178 (*Machine->drv->cpu[cpunum].vblank_interrupt)();
1179 cpuintrf_pop_context();
1180 }
1181
1182 /* update the counters */
1183 cpu[cpunum].iloops--;
1184 }
1185
1186 /* reset the countdown and timer */
1187 cpu[cpunum].vblankint_countdown = cpu[cpunum].vblankint_multiplier;
1188 timer_adjust(cpu[cpunum].vblankint_timer, TIME_NEVER, 0, 0);
1189 }
1190 }
1191
1192 /* else reset the VBLANK timer if this is going to be a real VBLANK */
1193 else if (vblank_countdown == 1)
1194 timer_adjust(cpu[cpunum].vblankint_timer, TIME_NEVER, 0, 0);
1195 }
1196
1197 /* is it a real VBLANK? */
1198 if (!--vblank_countdown)
1199 {
1200 /* do we update the screen now? */
1201 if (!(Machine->drv->video_attributes & VIDEO_UPDATE_AFTER_VBLANK))
1202 time_to_quit = updatescreen();
1203
1204 /* Set the timer to update the screen */
1205 timer_set(TIME_IN_USEC(Machine->drv->vblank_duration), 0, cpu_updatecallback);
1206 vblank = 1;
1207
1208 /* reset the globals */
1209 cpu_vblankreset();
1210
1211 /* reset the counter */
1212 vblank_countdown = vblank_multiplier;
1213 }
1214 }
1215
1216
1217
1218 /*************************************
1219 *
1220 * End-of-VBLANK callback
1221 *
1222 *************************************/
1223
1224 static void cpu_updatecallback(int param)
1225 {
1226 /* update the screen if we didn't before */
1227 if (Machine->drv->video_attributes & VIDEO_UPDATE_AFTER_VBLANK)
1228 time_to_quit = updatescreen();
1229 vblank = 0;
1230
1231 /* update IPT_VBLANK input ports */
1232 inputport_vblank_end();
1233
1234 /* reset partial updating */
1235 reset_partial_updates();
1236
1237 /* check the watchdog */
1238 if (watchdog_counter > 0)
1239 if (--watchdog_counter == 0)
1240 {
1241 logerror("reset caused by the watchdog\n");
1242 machine_reset();
1243 }
1244
1245 /* track total frames */
1246 current_frame++;
1247
1248 /* reset the refresh timer */
1249 timer_adjust(refresh_timer, TIME_NEVER, 0, 0);
1250 }
1251
1252
1253
1254 /*************************************
1255 *
1256 * Callback for timed interrupts
1257 * (not tied to a VBLANK)
1258 *
1259 *************************************/
1260
1261 static void cpu_timedintcallback(int param)
1262 {
1263 /* bail if there is no routine */
1264 if (Machine->drv->cpu[param].timed_interrupt && cpu_getstatus(param))
1265 {
1266 cpuintrf_push_context(param);
1267 (*Machine->drv->cpu[param].timed_interrupt)();
1268 cpuintrf_pop_context();
1269 }
1270 }
1271
1272
1273
1274 /*************************************
1275 *
1276 * Converts an integral timing rate
1277 * into a period
1278 *
1279 *************************************/
1280
1281 /*--------------------------------------------------------------
1282
1283 Rates can be specified as follows:
1284
1285 rate <= 0 -> 0
1286 rate < 50000 -> 'rate' cycles per frame
1287 rate >= 50000 -> 'rate' nanoseconds
1288
1289 --------------------------------------------------------------*/
1290
1291 static double cpu_computerate(int value)
1292 {
1293 /* values equal to zero are zero */
1294 if (value <= 0)
1295 return 0.0;
1296
1297 /* values above between 0 and 50000 are in Hz */
1298 if (value < 50000)
1299 return TIME_IN_HZ(value);
1300
1301 /* values greater than 50000 are in nanoseconds */
1302 else
1303 return TIME_IN_NSEC(value);
1304 }
1305
1306
1307
1308 /*************************************
1309 *
1310 * Callback to force a timeslice
1311 *
1312 *************************************/
1313
1314 static void cpu_timeslicecallback(int param)
1315 {
1316 timer_trigger(TRIGGER_TIMESLICE);
1317 }
1318
1319
1320
1321 /*************************************
1322 *
1323 * Setup all the core timers
1324 *
1325 *************************************/
1326
1327 static void cpu_inittimers(void)
1328 {
1329 double first_time;
1330 int cpunum, max, ipf;
1331
1332 /* allocate a dummy timer at the minimum frequency to break things up */
1333 ipf = Machine->drv->cpu_slices_per_frame;
1334 if (ipf <= 0)
1335 ipf = 1;
1336 timeslice_period = TIME_IN_HZ(Machine->drv->frames_per_second * ipf);
1337 timeslice_timer = timer_alloc(cpu_timeslicecallback);
1338 timer_adjust(timeslice_timer, timeslice_period, 0, timeslice_period);
1339
1340 /*
1341 * The following code finds all the CPUs that are interrupting in sync with the VBLANK
1342 * and sets up the VBLANK timer to run at the minimum number of cycles per frame in
1343 * order to service all the synced interrupts
1344 */
1345
1346 /* find the CPU with the maximum interrupts per frame */
1347 max = 1;
1348 for (cpunum = 0; cpunum < cpu_gettotalcpu(); cpunum++)
1349 {
1350 ipf = Machine->drv->cpu[cpunum].vblank_interrupts_per_frame;
1351 if (ipf > max)
1352 max = ipf;
1353 }
1354
1355 /* now find the LCD with the rest of the CPUs (brute force - these numbers aren't huge) */
1356 vblank_multiplier = max;
1357 while (1)
1358 {
1359 for (cpunum = 0; cpunum < cpu_gettotalcpu(); cpunum++)
1360 {
1361 ipf = Machine->drv->cpu[cpunum].vblank_interrupts_per_frame;
1362 if (ipf > 0 && (vblank_multiplier % ipf) != 0)
1363 break;
1364 }
1365 if (cpunum == cpu_gettotalcpu())
1366 break;
1367 vblank_multiplier += max;
1368 }
1369
1370 /* initialize the countdown timers and intervals */
1371 for (cpunum = 0; cpunum < cpu_gettotalcpu(); cpunum++)
1372 {
1373 ipf = Machine->drv->cpu[cpunum].vblank_interrupts_per_frame;
1374 if (ipf > 0)
1375 cpu[cpunum].vblankint_countdown = cpu[cpunum].vblankint_multiplier = vblank_multiplier / ipf;
1376 else
1377 cpu[cpunum].vblankint_countdown = cpu[cpunum].vblankint_multiplier = -1;
1378 }
1379
1380 /* allocate a vblank timer at the frame rate * the LCD number of interrupts per frame */
1381 vblank_period = TIME_IN_HZ(Machine->drv->frames_per_second * vblank_multiplier);
1382 vblank_timer = timer_alloc(cpu_vblankcallback);
1383 vblank_countdown = vblank_multiplier;
1384
1385 /*
1386 * The following code creates individual timers for each CPU whose interrupts are not
1387 * synced to the VBLANK, and computes the typical number of cycles per interrupt
1388 */
1389
1390 /* start the CPU interrupt timers */
1391 for (cpunum = 0; cpunum < cpu_gettotalcpu(); cpunum++)
1392 {
1393 ipf = Machine->drv->cpu[cpunum].vblank_interrupts_per_frame;
1394
1395 /* compute the average number of cycles per interrupt */
1396 if (ipf <= 0)
1397 ipf = 1;
1398 cpu[cpunum].vblankint_period = TIME_IN_HZ(Machine->drv->frames_per_second * ipf);
1399 cpu[cpunum].vblankint_timer = timer_alloc(NULL);
1400
1401 /* see if we need to allocate a CPU timer */
1402 ipf = Machine->drv->cpu[cpunum].timed_interrupts_per_second;
1403 if (ipf)
1404 {
1405 cpu[cpunum].timedint_period = cpu_computerate(ipf);
1406 cpu[cpunum].timedint_timer = timer_alloc(cpu_timedintcallback);
1407 timer_adjust(cpu[cpunum].timedint_timer, cpu[cpunum].timedint_period, cpunum, cpu[cpunum].timedint_period);
1408 }
1409 }
1410
1411 /* note that since we start the first frame on the refresh, we can't pulse starting
1412 immediately; instead, we back up one VBLANK period, and inch forward until we hit
1413 positive time. That time will be the time of the first VBLANK timer callback */
1414 first_time = -TIME_IN_USEC(Machine->drv->vblank_duration) + vblank_period;
1415 while (first_time < 0)
1416 {
1417 cpu_vblankcallback(-1);
1418 first_time += vblank_period;
1419 }
1420 timer_set(first_time, 0, cpu_firstvblankcallback);
1421 }
1422

Back to OSDN">Back to OSDN
ViewVC Help
Powered by ViewVC 1.1.26