void video_frame_update(void) { int skipped_it = video_skip_this_frame(); int paused = mame_is_paused(Machine); int phase = mame_get_phase(Machine); int livemask; int scrnum; /* only render sound and video if we're in the running phase */ if (phase == MAME_PHASE_RUNNING) { /* update sound */ sound_frame_update(); /* finish updating the screens */ for (scrnum = 0; scrnum < MAX_SCREENS; scrnum++) if (Machine->drv->screen[scrnum].tag != NULL) video_screen_update_partial(scrnum, Machine->screen[scrnum].visarea.max_y); /* now add the quads for all the screens */ livemask = render_get_live_screens_mask(); for (scrnum = 0; scrnum < MAX_SCREENS; scrnum++) { if (livemask & (1 << scrnum)) { internal_screen_info *screen = &scrinfo[scrnum]; /* only update if empty and not a vector game; otherwise assume the driver did it directly */ if (render_container_is_empty(render_container_get_screen(scrnum)) && !(Machine->drv->video_attributes & VIDEO_TYPE_VECTOR)) { mame_bitmap *bitmap = screen->bitmap[screen->curbitmap]; if (!skipping_this_frame && screen->changed) { rectangle fixedvis = Machine->screen[scrnum].visarea; fixedvis.max_x++; fixedvis.max_y++; render_texture_set_bitmap(screen->texture, bitmap, &fixedvis, Machine->drv->screen[scrnum].palette_base, screen->format); screen->curbitmap = 1 - screen->curbitmap; } render_screen_add_quad(scrnum, 0.0f, 0.0f, 1.0f, 1.0f, MAKE_ARGB(0xff,0xff,0xff,0xff), screen->texture, PRIMFLAG_BLENDMODE(BLENDMODE_NONE) | PRIMFLAG_SCREENTEX(1)); } } } /* update our movie recording state */ if (!paused) movie_record_frame(0); /* reset the screen changed flags */ for (scrnum = 0; scrnum < MAX_SCREENS; scrnum++) scrinfo[scrnum].changed = 0; } /* draw any crosshairs */ crosshair_render(); /* draw the user interface */ ui_update_and_render(); /* call the OSD to update */ skipping_this_frame = osd_update(mame_timer_get_time()); /* empty the containers */ for (scrnum = 0; scrnum < MAX_SCREENS; scrnum++) if (Machine->drv->screen[scrnum].tag != NULL) render_container_empty(render_container_get_screen(scrnum)); /* update FPS */ recompute_fps(skipped_it); /* call the end-of-frame callback */ if (phase == MAME_PHASE_RUNNING) { /* reset partial updates if we're paused or if the debugger is active */ if (paused || mame_debug_is_active()) video_reset_partial_updates(); /* otherwise, call the video EOF callback */ else if (Machine->drv->video_eof != NULL) { profiler_mark(PROFILER_VIDEO); (*Machine->drv->video_eof)(Machine); profiler_mark(PROFILER_END); } } }
/* Update the display. */ void osd_update_video_and_audio(struct osd_bitmap *bitmap) { static const int waittable[FRAMESKIP_LEVELS][FRAMESKIP_LEVELS] = { { 1,1,1,1,1,1,1,1,1,1,1,1 }, { 2,1,1,1,1,1,1,1,1,1,1,0 }, { 2,1,1,1,1,0,2,1,1,1,1,0 }, { 2,1,1,0,2,1,1,0,2,1,1,0 }, { 2,1,0,2,1,0,2,1,0,2,1,0 }, { 2,0,2,1,0,2,0,2,1,0,2,0 }, { 2,0,2,0,2,0,2,0,2,0,2,0 }, { 2,0,2,0,0,3,0,2,0,0,3,0 }, { 3,0,0,3,0,0,3,0,0,3,0,0 }, { 4,0,0,0,4,0,0,0,4,0,0,0 }, { 6,0,0,0,0,0,6,0,0,0,0,0 }, {12,0,0,0,0,0,0,0,0,0,0,0 } }; int i; static int showfps,showfpstemp; TICKER curr; static TICKER prev_measure=0,this_frame_base,prev; static int speed = 100; static int vups,vfcount; int have_to_clear_bitmap = 0; if (prev_measure==0) { /* first time through, initialize timer */ prev_measure = ticker() - FRAMESKIP_LEVELS * TICKS_PER_SEC/video_fps; } if (frameskip_counter == 0) this_frame_base = prev_measure + FRAMESKIP_LEVELS * TICKS_PER_SEC/video_fps; /* update audio */ msdos_update_audio(); if (osd_skip_this_frame() == 0) { if (showfpstemp) { showfpstemp--; if (showfps == 0 && showfpstemp == 0) { have_to_clear_bitmap = 1; } } if (input_ui_pressed(IPT_UI_SHOW_FPS)) { if (showfpstemp) { showfpstemp = 0; have_to_clear_bitmap = 1; } else { showfps ^= 1; if (showfps == 0) { have_to_clear_bitmap = 1; } } } /* now wait until it's time to update the screen */ if (throttle) { profiler_mark(PROFILER_IDLE); if (video_sync) { static TICKER last; do { vsync(); curr = ticker(); } while (TICKS_PER_SEC / (curr - last) > video_fps * 11 /10); last = curr; } else { TICKER target; /* wait for video sync but use normal throttling */ if (wait_vsync) vsync(); curr = ticker(); target = this_frame_base + frameskip_counter * TICKS_PER_SEC/video_fps; if ((curr < target) && (target-curr<TICKS_PER_SEC)) { do { #ifdef WIZ spend_cycles(1024); // WIZ #endif curr = ticker(); } while ((curr < target) && (target-curr<TICKS_PER_SEC)); } } profiler_mark(PROFILER_END); } else curr = ticker(); if (frameskip_counter == 0) { int divdr; divdr = video_fps * (curr - prev_measure) / (100 * FRAMESKIP_LEVELS); if (divdr==0) divdr=1; speed = (TICKS_PER_SEC + divdr/2) / divdr; prev_measure = curr; } prev = curr; vfcount += waittable[frameskip][frameskip_counter]; if (vfcount >= video_fps) { extern int vector_updates; /* avgdvg_go_w()'s per Mame frame, should be 1 */ vfcount = 0; vups = vector_updates; vector_updates = 0; } if (showfps || showfpstemp) { int fps; char buf[30]; int divdr; divdr = 100 * FRAMESKIP_LEVELS; fps = (video_fps * (FRAMESKIP_LEVELS - frameskip) * speed + (divdr / 2)) / divdr; sprintf(buf,"%s%2d%4d%%%4d/%d fps",autoframeskip?"auto":"fskp",frameskip,speed,fps,(int)(video_fps+0.5)); ui_text(bitmap,buf,Machine->uiwidth-strlen(buf)*Machine->uifontwidth,0); if (vector_game) { sprintf(buf," %d vector updates",vups); ui_text(bitmap,buf,Machine->uiwidth-strlen(buf)*Machine->uifontwidth,Machine->uifontheight); } } if (bitmap->depth == 8) { if (dirty_bright) { dirty_bright = 0; for (i = 0;i < 256;i++) { float rate = brightness * brightness_paused_adjust * pow(i / 255.0, 1 / osd_gamma_correction) / 100; bright_lookup[i] = 255 * rate + 0.5; } } if (dirtypalette) { dirtypalette = 0; for (i = 0;i < screen_colors;i++) { if (dirtycolor[i]) { unsigned char r,g,b; dirtycolor[i] = 0; r = current_palette[3*i+0]; g = current_palette[3*i+1]; b = current_palette[3*i+2]; if (i != Machine->uifont->colortable[1]) /* don't adjust the user interface text */ { r = bright_lookup[r]; g = bright_lookup[g]; b = bright_lookup[b]; } wiz_video_color8(i,r,g,b); } } wiz_video_setpalette(); } } else { if (dirty_bright) { dirty_bright = 0; for (i = 0;i < 256;i++) { float rate = brightness * brightness_paused_adjust * pow(i / 255.0, 1 / osd_gamma_correction) / 100; bright_lookup[i] = 255 * rate + 0.5; } } if (dirtypalette) { if (use_dirty) init_dirty(1); /* have to redraw the whole screen */ dirtypalette = 0; for (i = 0;i < screen_colors;i++) { if (dirtycolor[i]) { int r,g,b; dirtycolor[i] = 0; r = current_palette[3*i+0]; g = current_palette[3*i+1]; b = current_palette[3*i+2]; if (i != Machine->uifont->colortable[1]) /* don't adjust the user interface text */ { r = bright_lookup[r]; g = bright_lookup[g]; b = bright_lookup[b]; } palette_16bit_lookup[i] = makecol(r,g,b); } } } } /* copy the bitmap to screen memory */ profiler_mark(PROFILER_BLIT); update_screen(bitmap); profiler_mark(PROFILER_END); if (have_to_clear_bitmap) osd_clearbitmap(bitmap); if (use_dirty) { if (!vector_game) swap_dirty(); init_dirty(0); } if (have_to_clear_bitmap) osd_clearbitmap(bitmap); if (throttle && autoframeskip && frameskip_counter == 0) { static int frameskipadjust; int adjspeed; /* adjust speed to video refresh rate if vsync is on */ adjspeed = speed * video_fps / vsync_frame_rate; if (adjspeed >= 92) { frameskipadjust++; if (frameskipadjust >= 3) { frameskipadjust = 0; if (frameskip > 0) frameskip--; } } else { if (adjspeed < 80) frameskipadjust -= (90 - adjspeed) / 5; else { /* don't push frameskip too far if we are close to 100% speed */ if (frameskip < 8) frameskipadjust--; } while (frameskipadjust <= -2) { frameskipadjust += 2; #ifdef WIZ if (frameskip < 7) frameskip++; #else if (frameskip < FRAMESKIP_LEVELS-1) frameskip++; #endif } } } } /* Check for PGUP, PGDN and pan screen */ pan_display(); if (input_ui_pressed(IPT_UI_FRAMESKIP_INC)) { if (autoframeskip) { autoframeskip = 0; frameskip = 0; } else { if (frameskip == FRAMESKIP_LEVELS-1) { frameskip = 0; autoframeskip = 1; } else frameskip++; } if (showfps == 0) showfpstemp = 2*video_fps; } if (input_ui_pressed(IPT_UI_FRAMESKIP_DEC)) { if (autoframeskip) { autoframeskip = 0; frameskip = FRAMESKIP_LEVELS-1; } else { if (frameskip == 0) autoframeskip = 1; else frameskip--; } if (showfps == 0) showfpstemp = 2*video_fps; } if (input_ui_pressed(IPT_UI_THROTTLE)) { throttle ^= 1; } frameskip_counter = (frameskip_counter + 1) % FRAMESKIP_LEVELS; }
int dos_skip_next_frame() { static const int skiptable[FRAMESKIP_LEVELS][FRAMESKIP_LEVELS] = { { 0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,0,0,0,1 }, { 0,0,0,0,0,1,0,0,0,0,0,1 }, { 0,0,0,1,0,0,0,1,0,0,0,1 }, { 0,0,1,0,0,1,0,0,1,0,0,1 }, { 0,1,0,0,1,0,1,0,0,1,0,1 }, { 0,1,0,1,0,1,0,1,0,1,0,1 }, { 0,1,0,1,1,0,1,0,1,1,0,1 }, { 0,1,1,0,1,1,0,1,1,0,1,1 }, { 0,1,1,1,0,1,1,1,0,1,1,1 }, { 0,1,1,1,1,1,0,1,1,1,1,1 }, { 0,1,1,1,1,1,1,1,1,1,1,1 } }; static const int waittable[FRAMESKIP_LEVELS][FRAMESKIP_LEVELS] = { { 1,1,1,1,1,1,1,1,1,1,1,1 }, { 2,1,1,1,1,1,1,1,1,1,1,0 }, { 2,1,1,1,1,0,2,1,1,1,1,0 }, { 2,1,1,0,2,1,1,0,2,1,1,0 }, { 2,1,0,2,1,0,2,1,0,2,1,0 }, { 2,0,2,1,0,2,0,2,1,0,2,0 }, { 2,0,2,0,2,0,2,0,2,0,2,0 }, { 2,0,2,0,0,3,0,2,0,0,3,0 }, { 3,0,0,3,0,0,3,0,0,3,0,0 }, { 4,0,0,0,4,0,0,0,4,0,0,0 }, { 6,0,0,0,0,0,6,0,0,0,0,0 }, {12,0,0,0,0,0,0,0,0,0,0,0 } }; int i; uclock_t curr; static uclock_t prev_frames[FRAMESKIP_LEVELS]={0,0,0,0,0,0,0,0,0,0,0,0}; static uclock_t prev=0; static int speed=100; /* now wait until it's time to update the screen */ if (skiptable[frameskip][frameskip_counter] == 0) { if (throttle) { uclock_t target,target2; profiler_mark(PROFILER_IDLE); /* wait until enough time has passed since last frame... */ target = prev + waittable[frameskip][frameskip_counter] * UCLOCKS_PER_SEC/video_fps; /* ... OR since FRAMESKIP_LEVELS frames ago. This way, if a frame takes */ /* longer than the allotted time, we can compensate in the following frames. */ target2 = prev_frames[frameskip_counter] + FRAMESKIP_LEVELS * UCLOCKS_PER_SEC/video_fps; if (target - target2 > 0) target = target2; curr = uclock(); /* If we need to sleep more then half a second, we've somehow got totally out of sync. So if this happens we reset all counters */ if ((target - curr) > (UCLOCKS_PER_SEC / 2)) for (i=0; i < FRAMESKIP_LEVELS; i++) prev_frames[i] = curr; else while ((curr - target) < 0) { curr = uclock(); if ((target - curr) > (UCLOCKS_PER_SEC / 1000) && should_sleep_idle()) usleep(100); } profiler_mark(PROFILER_END); } else curr = uclock(); if (frameskip_counter == 0 && (curr - prev_frames[frameskip_counter])) { int divdr; divdr = video_fps * (curr - prev_frames[frameskip_counter]) / (100 * FRAMESKIP_LEVELS); speed = (UCLOCKS_PER_SEC + divdr/2) / divdr; } prev = curr; for (i = 0;i < waittable[frameskip][frameskip_counter];i++) prev_frames[(frameskip_counter + FRAMESKIP_LEVELS - i) % FRAMESKIP_LEVELS] = curr; if (throttle && autoframeskip && frameskip_counter == 0) { static int frameskipadjust; if (speed >= 100) { frameskipadjust++; if (frameskipadjust >= 3) { frameskipadjust = 0; if (frameskip > 0) frameskip--; } } else { if (speed < 80) frameskipadjust -= (90 - speed) / 5; else { /* don't push frameskip too far if we are close to 100% speed */ if (frameskip < 8) frameskipadjust--; } while (frameskipadjust <= -2) { frameskipadjust += 2; if (frameskip < max_autoframeskip) frameskip++; } } } } frameskip_counter = (frameskip_counter + 1) % FRAMESKIP_LEVELS; return skiptable[frameskip][frameskip_counter]; }
void profiler_show(struct mame_bitmap *bitmap) { int i,j; UINT64 total,normalize; UINT64 computed; int line; char buf[30]; static const char *names[PROFILER_TOTAL] = { "CPU 1 ", "CPU 2 ", "CPU 3 ", "CPU 4 ", "CPU 5 ", "CPU 6 ", "CPU 7 ", "CPU 8 ", "Mem rd ", "Mem wr ", "Video ", "drawgfx", "copybmp", "tmdraw ", "tmdrroz", "tmupdat", "Artwork", "Blit ", "Sound ", "Mixer ", "Callbck", "Hiscore", "Input ", "Extra ", "User1 ", "User2 ", "User3 ", "User4 ", "Profilr", "Idle ", }; static int showdelay[PROFILER_TOTAL]; if (!use_profiler) return; profiler_mark(PROFILER_PROFILER); computed = 0; i = 0; while (i < PROFILER_PROFILER) { for (j = 0;j < MEMORY;j++) computed += profile.count[j][i]; i++; } normalize = computed; while (i < PROFILER_TOTAL) { for (j = 0;j < MEMORY;j++) computed += profile.count[j][i]; i++; } total = computed; if (total == 0 || normalize == 0) return; /* we have been just reset */ line = 0; for (i = 0;i < PROFILER_TOTAL;i++) { computed = 0; { for (j = 0;j < MEMORY;j++) computed += profile.count[j][i]; } if (computed || showdelay[i]) { if (computed) showdelay[i] = (int)Machine->drv->frames_per_second; showdelay[i]--; if (i < PROFILER_PROFILER) sprintf(buf,"%s%3d%%%3d%%",names[i], (int)((computed * 100 + total/2) / total), (int)((computed * 100 + normalize/2) / normalize)); else sprintf(buf,"%s%3d%%",names[i], (int)((computed * 100 + total/2) / total)); ui_text(bitmap,buf,0,(line++)*uirotcharheight); } } i = 0; for (j = 0;j < MEMORY;j++) i += profile.cpu_context_switches[j]; sprintf(buf,"CPU switches%4d",i / MEMORY); ui_text(bitmap,buf,0,(line++)*uirotcharheight); /* reset the counters */ memory = (memory + 1) % MEMORY; profile.cpu_context_switches[memory] = 0; for (i = 0;i < PROFILER_TOTAL;i++) profile.count[memory][i] = 0; profiler_mark(PROFILER_END); }
int run_game(int game) { running_machine *machine; int error = MAMERR_NONE; mame_private *mame; callback_item *cb; /* create the machine structure and driver */ machine = create_machine(game); mame = machine->mame_data; /* looooong term: remove this */ Machine = machine; /* start in the "pre-init phase" */ mame->current_phase = MAME_PHASE_PREINIT; /* perform validity checks before anything else */ if (mame_validitychecks(game) != 0) return MAMERR_FAILED_VALIDITY; /* loop across multiple hard resets */ mame->exit_pending = FALSE; while (error == 0 && !mame->exit_pending) { init_resource_tracking(); add_free_resources_callback(timer_free); add_free_resources_callback(state_save_free); /* use setjmp/longjmp for deep error recovery */ mame->fatal_error_jmpbuf_valid = TRUE; error = setjmp(mame->fatal_error_jmpbuf); if (error == 0) { int settingsloaded; /* move to the init phase */ mame->current_phase = MAME_PHASE_INIT; /* start tracking resources for real */ begin_resource_tracking(); /* if we have a logfile, set up the callback */ mame->logerror_callback_list = NULL; if (options.logfile) add_logerror_callback(machine, logfile_callback); /* then finish setting up our local machine */ init_machine(machine); /* load the configuration settings and NVRAM */ settingsloaded = config_load_settings(); nvram_load(); /* display the startup screens */ ui_display_startup_screens(!settingsloaded && !options.skip_disclaimer, !options.skip_warnings, !options.skip_gameinfo); /* ensure we don't show the opening screens on a reset */ options.skip_disclaimer = options.skip_warnings = options.skip_gameinfo = TRUE; /* start resource tracking; note that soft_reset assumes it can */ /* call end_resource_tracking followed by begin_resource_tracking */ /* to clear out resources allocated between resets */ begin_resource_tracking(); /* perform a soft reset -- this takes us to the running phase */ soft_reset(0); /* run the CPUs until a reset or exit */ mame->hard_reset_pending = FALSE; while ((!mame->hard_reset_pending && !mame->exit_pending) || mame->saveload_pending_file != NULL) { profiler_mark(PROFILER_EXTRA); /* execute CPUs if not paused */ if (!mame->paused) cpuexec_timeslice(); /* otherwise, just pump video updates through */ else video_frame_update(); /* handle save/load */ if (mame->saveload_schedule_callback) (*mame->saveload_schedule_callback)(machine); profiler_mark(PROFILER_END); } /* and out via the exit phase */ mame->current_phase = MAME_PHASE_EXIT; /* stop tracking resources at this level */ end_resource_tracking(); /* save the NVRAM and configuration */ nvram_save(); config_save_settings(); } mame->fatal_error_jmpbuf_valid = FALSE; /* call all exit callbacks registered */ for (cb = mame->exit_callback_list; cb; cb = cb->next) (*cb->func.exit)(machine); /* close all inner resource tracking */ exit_resource_tracking(); /* free our callback lists */ free_callback_list(&mame->exit_callback_list); free_callback_list(&mame->reset_callback_list); free_callback_list(&mame->pause_callback_list); } /* destroy the machine */ destroy_machine(machine); /* return an error */ return error; }
void throttle_speed_part(int part, int totalparts) { static double ticks_per_sleep_msec = 0; cycles_t target, curr, cps; #ifdef VPINMAME if ((g_hEnterThrottle != INVALID_HANDLE_VALUE) && g_iSyncFactor) { if (g_iSyncFactor >= 1024) SetEvent(g_hEnterThrottle); else { iCurrentSyncValue += g_iSyncFactor; if (iCurrentSyncValue >= 1024) { SetEvent(g_hEnterThrottle); iCurrentSyncValue -= 1024; } } } #endif //// if we're only syncing to the refresh, bail now //if (win_sync_refresh) // return; // this counts as idle time profiler_mark(PROFILER_IDLE); // get the current time and the target time curr = osd_cycles(); cps = osd_cycles_per_second(); target = this_frame_base + (int)((double)frameskip_counter * (double)cps / video_fps); // If we are throttling to a fractional vsync, adjust target to the partial target. if (totalparts != 1) { // Meh. The points in the code where frameskip counter gets updated is different from where the frame base is // reset. Makes this delay computation complicated. if (frameskip_counter == 0) target += (int)((double)(FRAMESKIP_LEVELS) * (double)cps / video_fps); // MAGIC: Experimentation with actual resuts show the most even distribution if I throttle to 1/7th increments at each 25% timestep. target -= ((cycles_t)((double)cps / (video_fps * (totalparts + 3)))) * (totalparts - part + 3); } // initialize the ticks per sleep if (ticks_per_sleep_msec == 0) ticks_per_sleep_msec = (double)cps / 1000.; // Adjust target for sound catchup if (g_iThrottleAdj) { target -= (cycles_t)(g_iThrottleAdj*ticks_per_sleep_msec); } // sync if (curr - target < 0) { #ifdef DEBUG_THROTTLE { char tmp[91]; sprintf(tmp, "Throt: part %d of %d FS: %d Delta: %lld\n", part, totalparts, frameskip_counter, curr - target); OutputDebugString(tmp); } #endif // loop until we reach the target time while (curr - target < 0) { #if 1 // VPINMAME //if((INT64)((target - curr)/(ticks_per_sleep_msec*1.1))-1 > 0) // pessimistic estimate of stuff below, but still stutters then // uSleep((UINT64)((target - curr)*1000/(ticks_per_sleep_msec*1.1))-1); if (totalparts > 1) uUnderSleep((UINT64)((target - curr) * 1000 / ticks_per_sleep_msec)); // will sleep too short else uOverSleep((UINT64)((target - curr) * 1000 / ticks_per_sleep_msec)); // will sleep too long #else // if we have enough time to sleep, do it // ...but not if we're autoframeskipping and we're behind if (allow_sleep && (!autoframeskip || frameskip == 0) && (target - curr) > (cycles_t)(ticks_per_sleep_msec * 1.1)) { cycles_t next; // keep track of how long we actually slept uSleep(100); //1000? next = osd_cycles(); ticks_per_sleep_msec = (ticks_per_sleep_msec * 0.90) + ((double)(next - curr) * 0.10); curr = next; } else #endif { // update the current time curr = osd_cycles(); } } } else if (curr - target >= (int)(cps / video_fps) && totalparts == 1) { // We're behind schedule by a frame or more. Something must // have taken longer than it should have (e.g., a CPU emulator // time slice must have gone on too long). We don't have a // time machine, so we can't go back and sync this frame to a // time in the past, but we can at least sync up the current // frame with the current real time. // // Note that the 12-frame "skip" cycle would eventually get // things back in sync even without this adjustment, but it // can cause audio glitching if we wait until then. The skip // cycle will try to make up for the lost time by giving shorter // time slices to the next batch of 12 frames, but the way it // does its calculation, the time taken out of those short // frames will pile up in the *next next* skip cycle, causing // a long (order of 100ms) pause that can manifset as an audio // glitch and/or video hiccup. // // The adjustment here is simply the amount of real time by // which we're behind schedule. Add this to the base time, // since the real time for this frame is later than we expected. this_frame_base += curr - target; } // idle time done profiler_mark(PROFILER_END); }
static void update_throttle(mame_time emutime) { static double ticks_per_sleep_msec = 0; osd_ticks_t target, curr, cps, diffticks; int allowed_to_sleep; subseconds_t subsecs_per_cycle; int paused = mame_is_paused(Machine); // if we're only syncing to the refresh, bail now if (video_config.syncrefresh) return; // if we're paused, emutime will not advance; explicitly resync and set us backwards // 1/60th of a second if (paused) throttle_realtime = throttle_emutime = sub_subseconds_from_mame_time(emutime, MAX_SUBSECONDS / PAUSED_REFRESH_RATE); // if time moved backwards (reset), or if it's been more than 1 second in emulated time, resync if (compare_mame_times(emutime, throttle_emutime) < 0 || sub_mame_times(emutime, throttle_emutime).seconds > 0) goto resync; // get the current realtime; if it's been more than 1 second realtime, just resync cps = osd_ticks_per_second(); diffticks = osd_ticks() - throttle_last_ticks; throttle_last_ticks += diffticks; if (diffticks >= cps) goto resync; // add the time that has passed to the real time subsecs_per_cycle = MAX_SUBSECONDS / cps; throttle_realtime = add_subseconds_to_mame_time(throttle_realtime, diffticks * subsecs_per_cycle); // update the emulated time throttle_emutime = emutime; // if we're behind, just sync if (compare_mame_times(throttle_emutime, throttle_realtime) <= 0) goto resync; // determine our target ticks value target = throttle_last_ticks + sub_mame_times(throttle_emutime, throttle_realtime).subseconds / subsecs_per_cycle; // initialize the ticks per sleep if (ticks_per_sleep_msec == 0) ticks_per_sleep_msec = (double)(cps / 1000); // this counts as idle time profiler_mark(PROFILER_IDLE); // determine whether or not we are allowed to sleep allowed_to_sleep = video_config.sleep && (!effective_autoframeskip() || effective_frameskip() == 0); // sync for (curr = osd_ticks(); curr - target < 0; curr = osd_ticks()) { // if we have enough time to sleep, do it // ...but not if we're autoframeskipping and we're behind if (paused || (allowed_to_sleep && (target - curr) > (osd_ticks_t)(ticks_per_sleep_msec * 1.1))) { osd_ticks_t next; // keep track of how long we actually slept Sleep(1); next = osd_ticks(); ticks_per_sleep_msec = (ticks_per_sleep_msec * 0.90) + ((double)(next - curr) * 0.10); } } // idle time done profiler_mark(PROFILER_END); // update realtime diffticks = osd_ticks() - throttle_last_ticks; throttle_last_ticks += diffticks; throttle_realtime = add_subseconds_to_mame_time(throttle_realtime, diffticks * subsecs_per_cycle); return; resync: // reset realtime and emutime to the same value throttle_realtime = throttle_emutime = emutime; }
static void updateaudiostream(void) { extern int throttle; INT16 *data = stream_cache_data; int stereo = stream_cache_stereo; int len = stream_cache_len; int buflen; int start,end; if (!stream_playing) return; /* error */ buflen = audio_buffer_length; start = voice_pos; end = voice_pos + len; if (end > buflen) end -= buflen; #ifdef USE_SEAL if (throttle) /* sync with audio only when speed throttling is not turned off */ { profiler_mark(PROFILER_IDLE); for (;;) { LONG curpos; AGetVoicePosition(hVoice[0],&curpos); if (start < end) { if (curpos < start || curpos >= end) break; } else { if (curpos < start && curpos >= end) break; } AUpdateAudioEx(Machine->sample_rate / Machine->drv->frames_per_second); } profiler_mark(PROFILER_END); } if (stereo) { INT16 *bufL,*bufR; int p; bufL = (INT16 *)lpWave[0]->lpData; bufR = (INT16 *)lpWave[1]->lpData; p = start; while (p != end) { if (p >= buflen) p -= buflen; bufL[p] = *data++; bufR[p] = *data++; p++; } } else { INT16 *buf; int p; buf = (INT16 *)lpWave[0]->lpData; p = start; while (p != end) { if (p >= buflen) p -= buflen; buf[p] = *data++; p++; } } if (start < end) { AWriteAudioData(lpWave[0],2*start,2*len); if (stereo) AWriteAudioData(lpWave[1],2*start,2*len); } else { int remain = buflen-start; AWriteAudioData(lpWave[0],2*start,2*remain); AWriteAudioData(lpWave[0],0,2*(len-remain)); if (stereo) { AWriteAudioData(lpWave[1],2*start,2*remain); AWriteAudioData(lpWave[1],0,2*(len-remain)); } } #endif #ifdef USE_ALLEGRO { if (throttle) /* sync with audio only when speed throttling is not turned off */ { profiler_mark(PROFILER_IDLE); for (;;) { int curpos; curpos = voice_get_position(myvoice); if (start < end) { if (curpos < start || curpos >= end) break; } else { if (curpos < start && curpos >= end) break; } } profiler_mark(PROFILER_END); } if (stereo) { INT16 *buf = mysample->data; int p = start; while (p != end) { if (p >= buflen) p -= buflen; buf[2*p] = (*data++ * master_volume / 256) ^ 0x8000; buf[2*p+1] = (*data++ * master_volume / 256) ^ 0x8000; p++; } } else { INT16 *buf = mysample->data; int p = start; while (p != end) { if (p >= buflen) p -= buflen; buf[p] = (*data++ * master_volume / 256) ^ 0x8000; p++; } } } #endif voice_pos = end; if (voice_pos == buflen) voice_pos = 0; }
void mixer_sh_update(void) { struct mixer_channel_data * channel; UINT32 accum_pos = accum_base; INT16 *mix; INT32 sample; int i; profiler_mark(PROFILER_MIXER); /* update all channels (for streams this is a no-op) */ for (i = 0, channel = mixer_channel; i < first_free_channel; i++, channel++) { mixer_update_channel(channel, samples_this_frame); /* if we needed more than they could give, adjust their pointers */ if (samples_this_frame > channel->samples_available) channel->samples_available = 0; else channel->samples_available -= samples_this_frame; } /* copy the mono 32-bit data to a 16-bit buffer, clipping along the way */ if (!is_stereo) { mix = mix_buffer; for (i = 0; i < samples_this_frame; i++) { /* fetch and clip the sample */ sample = left_accum[accum_pos]; #if !DISABLE_CLIPPING if (sample < -32768) sample = -32768; else if (sample > 32767) sample = 32767; #endif /* store and zero out behind us */ *mix++ = sample; //SQ Force stereo for mono sound so just copy left to both channels (interleaved) if (options.force_stereo) *mix++ = sample; left_accum[accum_pos] = 0; /* advance to the next sample */ accum_pos = (accum_pos + 1) & ACCUMULATOR_MASK; } } /* copy the stereo 32-bit data to a 16-bit buffer, clipping along the way */ else { mix = mix_buffer; for (i = 0; i < samples_this_frame; i++) { /* fetch and clip the left sample */ sample = left_accum[accum_pos]; #if !DISABLE_CLIPPING if (sample < -32768) sample = -32768; else if (sample > 32767) sample = 32767; #endif /* store and zero out behind us */ *mix++ = sample; left_accum[accum_pos] = 0; /* fetch and clip the right sample */ sample = right_accum[accum_pos]; #if !DISABLE_CLIPPING if (sample < -32768) sample = -32768; else if (sample > 32767) sample = 32767; #endif /* store and zero out behind us */ *mix++ = sample; right_accum[accum_pos] = 0; /* advance to the next sample */ accum_pos = (accum_pos + 1) & ACCUMULATOR_MASK; } } /* play the result */ samples_this_frame = osd_update_audio_stream(mix_buffer); accum_base = accum_pos; profiler_mark(PROFILER_END); }
void sound_frame_update(void) { int sample, spknum; VPRINTF(("sound_frame_update\n")); profiler_mark(PROFILER_SOUND); /* reset the mixing streams */ memset(leftmix, 0, samples_this_frame * sizeof(*leftmix)); memset(rightmix, 0, samples_this_frame * sizeof(*rightmix)); /* if we're not paused, keep the sounds going */ if (!mame_is_paused(Machine)) { /* force all the speaker streams to generate the proper number of samples */ for (spknum = 0; spknum < totalspeakers; spknum++) { speaker_info *spk = &speaker[spknum]; stream_sample_t *stream_buf; /* get the output buffer */ if (spk->mixer_stream) { stream_buf = stream_consume_output(spk->mixer_stream, 0, samples_this_frame); #ifdef MAME_DEBUG /* debug version: keep track of the maximum sample */ for (sample = 0; sample < samples_this_frame; sample++) { if (stream_buf[sample] > spk->max_sample) spk->max_sample = stream_buf[sample]; else if (-stream_buf[sample] > spk->max_sample) spk->max_sample = -stream_buf[sample]; if (stream_buf[sample] > 32767 || stream_buf[sample] < -32768) spk->clipped_samples++; spk->total_samples++; } #endif /* mix if sound is enabled */ if (global_sound_enabled && !nosound_mode) { /* if the speaker is centered, send to both left and right */ if (spk->speaker->x == 0) for (sample = 0; sample < samples_this_frame; sample++) { leftmix[sample] += stream_buf[sample]; rightmix[sample] += stream_buf[sample]; } /* if the speaker is to the left, send only to the left */ else if (spk->speaker->x < 0) for (sample = 0; sample < samples_this_frame; sample++) leftmix[sample] += stream_buf[sample]; /* if the speaker is to the right, send only to the right */ else for (sample = 0; sample < samples_this_frame; sample++) rightmix[sample] += stream_buf[sample]; } } } } /* now downmix the final result */ for (sample = 0; sample < samples_this_frame; sample++) { INT32 samp; /* clamp the left side */ samp = leftmix[sample]; if (samp < -32768) samp = -32768; else if (samp > 32767) samp = 32767; finalmix[sample*2+0] = samp; /* clamp the right side */ samp = rightmix[sample]; if (samp < -32768) samp = -32768; else if (samp > 32767) samp = 32767; finalmix[sample*2+1] = samp; } if (wavfile && !mame_is_paused(Machine)) wav_add_data_16(wavfile, finalmix, samples_this_frame * 2); /* play the result */ samples_this_frame = osd_update_audio_stream(finalmix); /* update the streamer */ streams_frame_update(); /* reset the timer to resync for this frame */ mame_timer_adjust(sound_update_timer, time_never, 0, time_never); profiler_mark(PROFILER_END); }
static void draw_sprites(mame_bitmap *bitmap) { int offs; profiler_mark(PROFILER_USER1); for (offs = 0;offs < spriteram_size;offs += 4) { int sx,sy,flipy,zoom,ch,x,px,y; const unsigned char *lookup; const unsigned char *zoomx_rom,*zoomy_rom; sx = spriteram[offs+3] - ((spriteram[offs+2] & 0x80) << 1); sy = 256-64 - spriteram[offs] + ((spriteram[offs+1] & 0x80) << 1); flipy = spriteram[offs+1] & 0x40; zoom = spriteram[offs+1] & 0x3f; zoomy_rom = memory_region(REGION_GFX2) + (zoom << 6); zoomx_rom = memory_region(REGION_GFX2) + 0x2000 + (zoom << 3); lookup = buggychl_sprite_lookup + ((spriteram[offs+2] & 0x7f) << 6); for (y = 0;y < 64;y++) { int dy = flip_screen_y ? (255 - sy - y) : (sy + y); if ((dy & ~0xff) == 0) { int charline,base_pos; charline = zoomy_rom[y] & 0x07; base_pos = zoomy_rom[y] & 0x38; if (flipy) base_pos ^= 0x38; px = 0; for (ch = 0;ch < 4;ch++) { int pos,code,realflipy; const UINT8 *pendata; pos = base_pos + 2*ch; code = 8 * (lookup[pos] | ((lookup[pos+1] & 0x07) << 8)); realflipy = (lookup[pos+1] & 0x80) ? !flipy : flipy; code += (realflipy ? (charline ^ 7) : charline); pendata = Machine->gfx[1]->gfxdata + code*16; for (x = 0;x < 16;x++) { int col; col = pendata[x]; if (col) { int dx = flip_screen_x ? (255 - sx - px) : (sx + px); if ((dx & ~0xff) == 0) plot_pixel(bitmap,dx,dy,Machine->pens[sprite_color_base+col]); } /* the following line is almost certainly wrong */ if (zoomx_rom[7-(2*ch+x/8)] & (1 << (x & 7))) px++; } } } } } profiler_mark(PROFILER_END); }
static void cpu_timeslice(void) { double target = timer_time_until_next_timer(); int cpunum, ran; LOG(("------------------\n")); LOG(("cpu_timeslice: target = %.9f\n", target)); /* process any pending suspends */ for (cpunum = 0; Machine->drv->cpu[cpunum].cpu_type != CPU_DUMMY; cpunum++) { if (cpu[cpunum].suspend != cpu[cpunum].nextsuspend) LOG(("--> updated CPU%d suspend from %X to %X\n", cpunum, cpu[cpunum].suspend, cpu[cpunum].nextsuspend)); cpu[cpunum].suspend = cpu[cpunum].nextsuspend; cpu[cpunum].eatcycles = cpu[cpunum].nexteatcycles; } /* loop over CPUs */ for (cpunum = 0; Machine->drv->cpu[cpunum].cpu_type != CPU_DUMMY; cpunum++) { /* only process if we're not suspended */ if (!cpu[cpunum].suspend) { /* compute how long to run */ cycles_running = TIME_TO_CYCLES(cpunum, target - cpu[cpunum].localtime); LOG((" cpu %d: %d cycles\n", cpunum, cycles_running)); /* run for the requested number of cycles */ if (cycles_running > 0) { profiler_mark(PROFILER_CPU1 + cpunum); cycles_stolen = 0; ran = cpunum_execute(cpunum, cycles_running); ran -= cycles_stolen; profiler_mark(PROFILER_END); /* account for these cycles */ cpu[cpunum].totalcycles += ran; cpu[cpunum].localtime += TIME_IN_CYCLES(ran, cpunum); LOG((" %d ran, %d total, time = %.9f\n", ran, (INT32)cpu[cpunum].totalcycles, cpu[cpunum].localtime)); /* if the new local CPU time is less than our target, move the target up */ if (cpu[cpunum].localtime < target && cpu[cpunum].localtime > 0) { target = cpu[cpunum].localtime; LOG((" (new target)\n")); } } } } /* update the local times of all CPUs */ for (cpunum = 0; Machine->drv->cpu[cpunum].cpu_type != CPU_DUMMY; cpunum++) { /* if we're suspended and counting, process */ if (cpu[cpunum].suspend && cpu[cpunum].eatcycles && cpu[cpunum].localtime < target) { /* compute how long to run */ cycles_running = TIME_TO_CYCLES(cpunum, target - cpu[cpunum].localtime); LOG((" cpu %d: %d cycles (suspended)\n", cpunum, cycles_running)); cpu[cpunum].totalcycles += cycles_running; cpu[cpunum].localtime += TIME_IN_CYCLES(cycles_running, cpunum); LOG((" %d skipped, %d total, time = %.9f\n", cycles_running, (INT32)cpu[cpunum].totalcycles, cpu[cpunum].localtime)); } /* update the suspend state */ if (cpu[cpunum].suspend != cpu[cpunum].nextsuspend) LOG(("--> updated CPU%d suspend from %X to %X\n", cpunum, cpu[cpunum].suspend, cpu[cpunum].nextsuspend)); cpu[cpunum].suspend = cpu[cpunum].nextsuspend; cpu[cpunum].eatcycles = cpu[cpunum].nexteatcycles; /* adjust to be relative to the global time */ cpu[cpunum].localtime -= target; } /* update the global time */ timer_adjust_global_time(target); /* huh? something for the debugger */ #ifdef MAME_DEBUG { extern int debug_key_delay; debug_key_delay = 0x7ffe; } #endif }
static void DirectSound_update_audio(void) { HRESULT hr; int next_voice_pos; VOID *area1,*area2; DWORD len_area1,len_area2; if (dsb == NULL || new_sound_data == FALSE) return; profiler_mark(PROFILER_MIXER); next_voice_pos = voice_pos + stream_cache_len*sizeof(INT16)*stereo_factor; if (next_voice_pos >= buffer_length) next_voice_pos -= buffer_length; if (Display_Throttled()) /* sync with audio only when speed throttling is not turned off */ { profiler_mark(PROFILER_IDLE); for (;;) { LONG curpos; LONG writepos; IDirectSoundBuffer_GetCurrentPosition(dsb,&curpos,&writepos); if (voice_pos < next_voice_pos) { if (curpos < voice_pos || curpos >= next_voice_pos) break; } else { if (curpos < voice_pos && curpos >= next_voice_pos) break; } } profiler_mark(PROFILER_END); } hr = IDirectSoundBuffer_Lock(dsb, voice_pos, stream_cache_len * sizeof(INT16) * stereo_factor, &area1, &len_area1, &area2, &len_area2, 0); if (FAILED(hr)) { ErrorMsg("Unable to lock secondary sound buffer: %s",DirectXDecodeError(hr)); } else { memcpy(area1, stream_cache_data, len_area1); memcpy(area2, ((byte *)stream_cache_data) + len_area1, len_area2); hr = IDirectSoundBuffer_Unlock(dsb,area1,len_area1,area2,len_area2); if (FAILED(hr)) ErrorMsg("Unable to unlock secondary sound buffer: %s",DirectXDecodeError(hr)); } voice_pos = next_voice_pos; profiler_mark(PROFILER_END); new_sound_data = FALSE; }
const char *profiler_get_text(void) { int i,j; UINT64 total,normalize; UINT64 computed; static const char *names[PROFILER_TOTAL] = { "CPU 1 ", "CPU 2 ", "CPU 3 ", "CPU 4 ", "CPU 5 ", "CPU 6 ", "CPU 7 ", "CPU 8 ", "Mem rd ", "Mem wr ", "Video ", "drawgfx", "copybmp", "tmdraw ", "tmdrroz", "tmupdat", "Artwork", "Blit ", "Sound ", "Mixer ", "Callbck", "Hiscore", "Input ", "Movie ", "Logerr ", "Extra ", "User1 ", "User2 ", "User3 ", "User4 ", "Profilr", "Idle ", }; static int showdelay[PROFILER_TOTAL]; static char buf[30*20]; char *bufptr = buf; if (!use_profiler) return ""; profiler_mark(PROFILER_PROFILER); computed = 0; i = 0; while (i < PROFILER_PROFILER) { for (j = 0;j < MEMORY;j++) computed += profile.count[j][i]; i++; } normalize = computed; while (i < PROFILER_TOTAL) { for (j = 0;j < MEMORY;j++) computed += profile.count[j][i]; i++; } total = computed; if (total == 0 || normalize == 0) return ""; /* we have been just reset */ for (i = 0;i < PROFILER_TOTAL;i++) { computed = 0; { for (j = 0;j < MEMORY;j++) computed += profile.count[j][i]; } if (computed || showdelay[i]) { if (computed) showdelay[i] = Machine->refresh_rate; showdelay[i]--; if (i < PROFILER_PROFILER) bufptr += sprintf(bufptr,"%s%3d%%%3d%%\n",names[i], (int)((computed * 100 + total/2) / total), (int)((computed * 100 + normalize/2) / normalize)); else bufptr += sprintf(bufptr,"%s%3d%%\n",names[i], (int)((computed * 100 + total/2) / total)); } } i = 0; for (j = 0;j < MEMORY;j++) i += profile.cpu_context_switches[j]; bufptr += sprintf(bufptr,"CPU switches%4d\n",i / MEMORY); /* reset the counters */ memory = (memory + 1) % MEMORY; profile.cpu_context_switches[memory] = 0; for (i = 0;i < PROFILER_TOTAL;i++) profile.count[memory][i] = 0; profiler_mark(PROFILER_END); return buf; }
void timer_adjust_global_time(double delta) { mame_timer *timer; /* add the delta to the global offset */ global_offset += delta; /* scan the list and adjust the times */ for (timer = timer_head; timer != NULL; timer = timer->next) { timer->start -= delta; timer->expire -= delta; } LOG(("timer_adjust_global_time: delta=%.9f head->expire=%.9f\n", delta, timer_head->expire)); /* now process any timers that are overdue */ while (timer_head->expire < TIME_IN_NSEC(1)) { int was_enabled = timer_head->enabled; /* if this is a one-shot timer, disable it now */ timer = timer_head; if (timer->period == 0) timer->enabled = 0; /* set the global state of which callback we're in */ callback_timer_modified = 0; callback_timer = timer; callback_timer_expire_time = timer->expire; /* call the callback */ if (was_enabled && timer->callback) { LOG(("Timer %08X fired (expire=%.9f)\n", (UINT32)timer, timer->expire)); profiler_mark(PROFILER_TIMER_CALLBACK); (*timer->callback)(timer->callback_param); profiler_mark(PROFILER_END); } /* clear the callback timer global */ callback_timer = NULL; /* reset or remove the timer, but only if it wasn't modified during the callback */ if (!callback_timer_modified) { /* if the timer is temporary, remove it now */ if (timer->temporary) timer_remove(timer); /* otherwise, reschedule it */ else { timer->start = timer->expire; timer->expire += timer->period; timer_list_remove(timer); timer_list_insert(timer); } } } }
static TIMER_CALLBACK( sound_update ) { UINT32 finalmix_step, finalmix_offset; int samples_this_update = 0; int sample, spknum; VPRINTF(("sound_update\n")); profiler_mark(PROFILER_SOUND); /* force all the speaker streams to generate the proper number of samples */ for (spknum = 0; spknum < totalspeakers; spknum++) { speaker_info *spk = &speaker[spknum]; const stream_sample_t *stream_buf; /* get the output buffer */ if (spk->mixer_stream != NULL) { int numsamples; /* update the stream, getting the start/end pointers around the operation */ stream_buf = stream_get_output_since_last_update(spk->mixer_stream, 0, &numsamples); /* set or assert that all streams have the same count */ if (samples_this_update == 0) { samples_this_update = numsamples; /* reset the mixing streams */ memset(leftmix, 0, samples_this_update * sizeof(*leftmix)); memset(rightmix, 0, samples_this_update * sizeof(*rightmix)); } assert(samples_this_update == numsamples); #ifdef MAME_DEBUG /* debug version: keep track of the maximum sample */ for (sample = 0; sample < samples_this_update; sample++) { if (stream_buf[sample] > spk->max_sample) spk->max_sample = stream_buf[sample]; else if (-stream_buf[sample] > spk->max_sample) spk->max_sample = -stream_buf[sample]; if (stream_buf[sample] > 32767 || stream_buf[sample] < -32768) spk->clipped_samples++; spk->total_samples++; } #endif /* mix if sound is enabled */ if (global_sound_enabled && !nosound_mode) { /* if the speaker is centered, send to both left and right */ if (spk->speaker->x == 0) for (sample = 0; sample < samples_this_update; sample++) { leftmix[sample] += stream_buf[sample]; rightmix[sample] += stream_buf[sample]; } /* if the speaker is to the left, send only to the left */ else if (spk->speaker->x < 0) for (sample = 0; sample < samples_this_update; sample++) leftmix[sample] += stream_buf[sample]; /* if the speaker is to the right, send only to the right */ else for (sample = 0; sample < samples_this_update; sample++) rightmix[sample] += stream_buf[sample]; } } } /* now downmix the final result */ finalmix_step = video_get_speed_factor(); finalmix_offset = 0; for (sample = finalmix_leftover; sample < samples_this_update * 100; sample += finalmix_step) { int sampindex = sample / 100; INT32 samp; /* clamp the left side */ samp = leftmix[sampindex]; if (samp < -32768) samp = -32768; else if (samp > 32767) samp = 32767; finalmix[finalmix_offset++] = samp; /* clamp the right side */ samp = rightmix[sampindex]; if (samp < -32768) samp = -32768; else if (samp > 32767) samp = 32767; finalmix[finalmix_offset++] = samp; } finalmix_leftover = sample - samples_this_update * 100; /* play the result */ if (finalmix_offset > 0) { osd_update_audio_stream(finalmix, finalmix_offset / 2); if (wavfile != NULL) wav_add_data_16(wavfile, finalmix, finalmix_offset); } /* update the streamer */ streams_update(machine); profiler_mark(PROFILER_END); }
int run_game(int game) { callback_item *cb; int error = 0; /* start in the "pre-init phase" */ current_phase = MAME_PHASE_PREINIT; /* AdvanceMAME: Disable validity checks */ #if 0 /* perform validity checks before anything else */ if (mame_validitychecks(game) != 0) return 1; #endif /* loop across multiple hard resets */ exit_pending = FALSE; while (error == 0 && !exit_pending) { /* use setjmp/longjmp for deep error recovery */ fatal_error_jmpbuf_valid = TRUE; error = setjmp(fatal_error_jmpbuf); if (error == 0) { int settingsloaded; /* move to the init phase */ current_phase = MAME_PHASE_INIT; /* start tracking resources for real */ begin_resource_tracking(); /* if we have a logfile, set up the callback */ logerror_callback_list = NULL; if (options.logfile) add_logerror_callback(logfile_callback); /* create the Machine structure and driver */ create_machine(game); /* then finish setting up our local machine */ init_machine(); /* load the configuration settings and NVRAM */ settingsloaded = config_load_settings(); nvram_load(); /* initialize the UI and display the startup screens */ if (ui_init(!settingsloaded && !options.skip_disclaimer, !options.skip_warnings, !options.skip_gameinfo) != 0) fatalerror("User cancelled"); /* ensure we don't show the opening screens on a reset */ options.skip_disclaimer = options.skip_warnings = options.skip_gameinfo = TRUE; /* start resource tracking; note that soft_reset assumes it can */ /* call end_resource_tracking followed by begin_resource_tracking */ /* to clear out resources allocated between resets */ begin_resource_tracking(); /* perform a soft reset -- this takes us to the running phase */ soft_reset(0); /* run the CPUs until a reset or exit */ hard_reset_pending = FALSE; while ((!hard_reset_pending && !exit_pending) || saveload_pending_file != NULL) { profiler_mark(PROFILER_EXTRA); /* execute CPUs if not paused */ if (!mame_paused) cpuexec_timeslice(); /* otherwise, just pump video updates through */ else { updatescreen(); reset_partial_updates(); } /* handle save/load */ if (saveload_schedule_callback) (*saveload_schedule_callback)(); profiler_mark(PROFILER_END); } /* and out via the exit phase */ current_phase = MAME_PHASE_EXIT; /* stop tracking resources at this level */ end_resource_tracking(); /* save the NVRAM and configuration */ nvram_save(); config_save_settings(); } fatal_error_jmpbuf_valid = FALSE; /* call all exit callbacks registered */ for (cb = exit_callback_list; cb; cb = cb->next) (*cb->func.exit)(); /* close all inner resource tracking */ while (resource_tracking_tag != 0) end_resource_tracking(); /* free our callback lists */ free_callback_list(&exit_callback_list); free_callback_list(&reset_callback_list); free_callback_list(&pause_callback_list); } /* return an error */ return error; }