void ContinueExecution() { static BOOL pageflipping = 0; //? const double fUsecPerSec = 1.e6; #if 1 const UINT nExecutionPeriodUsec = 1000; // 1.0ms // const UINT nExecutionPeriodUsec = 100; // 0.1ms const double fExecutionPeriodClks = g_fCurrentCLK6502 * ((double)nExecutionPeriodUsec / fUsecPerSec); #else const double fExecutionPeriodClks = 1800.0; const UINT nExecutionPeriodUsec = (UINT) (fUsecPerSec * (fExecutionPeriodClks / g_fCurrentCLK6502)); #endif // bool bScrollLock_FullSpeed = g_uScrollLockToggle ? g_bScrollLock_FullSpeed : (GetKeyState(VK_SCROLL) < 0); g_bFullSpeed = ( (g_dwSpeed == SPEED_MAX) || bScrollLock_FullSpeed || (DiskIsSpinning() && enhancedisk && !Spkr_IsActive() && !MB_IsActive()) ); if(g_bFullSpeed) { // Don't call Spkr_Mute() - will get speaker clicks MB_Mute(); SysClk_StopTimer(); g_nCpuCyclesFeedback = 0; // For the case when this is a big -ve number } else { // Don't call Spkr_Demute() MB_Demute(); SysClk_StartTimerUsec(nExecutionPeriodUsec); } // int nCyclesToExecute = (int) fExecutionPeriodClks + g_nCpuCyclesFeedback; if(nCyclesToExecute < 0) nCyclesToExecute = 0; DWORD dwExecutedCycles = CpuExecute(nCyclesToExecute); g_dwCyclesThisFrame += dwExecutedCycles; // cyclenum = dwExecutedCycles; DiskUpdatePosition(dwExecutedCycles); JoyUpdatePosition(); VideoUpdateVbl(g_dwCyclesThisFrame); SpkrUpdate(cyclenum); sg_SSC.CommUpdate(cyclenum); PrintUpdate(cyclenum); // const DWORD CLKS_PER_MS = (DWORD)g_fCurrentCLK6502 / 1000; emulmsec_frac += dwExecutedCycles; if(emulmsec_frac > CLKS_PER_MS) { emulmsec += emulmsec_frac / CLKS_PER_MS; emulmsec_frac %= CLKS_PER_MS; } // // DETERMINE WHETHER THE SCREEN WAS UPDATED, THE DISK WAS SPINNING, // OR THE KEYBOARD I/O PORTS WERE BEING EXCESSIVELY QUERIED THIS CLOCKTICK VideoCheckPage(0); BOOL screenupdated = VideoHasRefreshed(); BOOL systemidle = 0; //(KeybGetNumQueries() > (clockgran << 2)); // && (!ranfinegrain); // TO DO if(screenupdated) pageflipping = 3; // if(g_dwCyclesThisFrame >= dwClksPerFrame) { g_dwCyclesThisFrame -= dwClksPerFrame; if(g_nAppMode != MODE_LOGO) { VideoUpdateFlash(); static BOOL anyupdates = 0; static DWORD lastcycles = 0; static BOOL lastupdates[2] = {0,0}; anyupdates |= screenupdated; // lastcycles = cumulativecycles; if ((!anyupdates) && (!lastupdates[0]) && (!lastupdates[1]) && VideoApparentlyDirty()) { VideoCheckPage(1); static DWORD lasttime = 0; DWORD currtime = GetTickCount(); if ((!g_bFullSpeed) || (currtime-lasttime >= (DWORD)((graphicsmode || !systemidle) ? 100 : 25))) { VideoRefreshScreen(); lasttime = currtime; } screenupdated = 1; } lastupdates[1] = lastupdates[0]; lastupdates[0] = anyupdates; anyupdates = 0; if (pageflipping) pageflipping--; } MB_EndOfVideoFrame(); } // if(!g_bFullSpeed) { SysClk_WaitTimer(); #if DBG_CALC_FREQ if(g_nPerfFreq) { QueryPerformanceCounter((LARGE_INTEGER*)&nTime1); LONGLONG nTimeDiff = nTime1 - nTime0; double fTime = (double)nTimeDiff / (double)(LONGLONG)g_nPerfFreq; g_fDbg[g_nIdx] = fTime; g_nIdx = (g_nIdx+1) & (MAX_CNT-1); g_fMeanPeriod = 0.0; for(UINT n=0; n<MAX_CNT; n++) g_fMeanPeriod += g_fDbg[n]; g_fMeanPeriod /= (double)MAX_CNT; g_fMeanFreq = 1.0 / g_fMeanPeriod; } #endif } }
void ContinueExecution(void) { const double fUsecPerSec = 1.e6; #if 1 const UINT nExecutionPeriodUsec = 1000; // 1.0ms // const UINT nExecutionPeriodUsec = 100; // 0.1ms const double fExecutionPeriodClks = g_fCurrentCLK6502 * ((double)nExecutionPeriodUsec / fUsecPerSec); #else const double fExecutionPeriodClks = 1800.0; const UINT nExecutionPeriodUsec = (UINT) (fUsecPerSec * (fExecutionPeriodClks / g_fCurrentCLK6502)); #endif // bool bScrollLock_FullSpeed = sg_PropertySheet.GetScrollLockToggle() ? g_bScrollLock_FullSpeed : (GetKeyState(VK_SCROLL) < 0); g_bFullSpeed = ( (g_dwSpeed == SPEED_MAX) || bScrollLock_FullSpeed || (DiskIsSpinning() && enhancedisk && !Spkr_IsActive() && !MB_IsActive()) ); if (g_bFullSpeed) { // Don't call Spkr_Mute() - will get speaker clicks MB_Mute(); SysClk_StopTimer(); #ifdef USE_SPEECH_API g_Speech.Reset(); // TODO: Put this on a timer (in emulated cycles)... otherwise CATALOG cuts out #endif g_nCpuCyclesFeedback = 0; // For the case when this is a big -ve number // Switch to normal priority so that APPLEWIN process doesn't hog machine! //. EG: No disk in Drive-1, and boot Apple: Windows will start to crawl! SetPriorityNormal(); } else { // Don't call Spkr_Demute() MB_Demute(); SysClk_StartTimerUsec(nExecutionPeriodUsec); // Switch to higher priority, eg. for audio (BUG #015394) SetPriorityAboveNormal(); } // int nCyclesToExecute = (int) fExecutionPeriodClks + g_nCpuCyclesFeedback; if (nCyclesToExecute < 0) nCyclesToExecute = 0; DWORD dwExecutedCycles = CpuExecute(nCyclesToExecute); g_dwCyclesThisFrame += dwExecutedCycles; // cyclenum = dwExecutedCycles; DiskUpdatePosition(dwExecutedCycles); JoyUpdatePosition(); SpkrUpdate(cyclenum); sg_SSC.CommUpdate(cyclenum); PrintUpdate(cyclenum); // const DWORD CLKS_PER_MS = (DWORD)g_fCurrentCLK6502 / 1000; emulmsec_frac += dwExecutedCycles; if (emulmsec_frac > CLKS_PER_MS) { emulmsec += emulmsec_frac / CLKS_PER_MS; emulmsec_frac %= CLKS_PER_MS; } // DETERMINE WHETHER THE SCREEN WAS UPDATED THIS CLOCKTICK static BOOL anyupdates = 0; VideoCheckPage(0); // force=0 anyupdates |= VideoHasRefreshed(); // Only called from here. Returns & clears 'hasrefreshed' flag // if (g_dwCyclesThisFrame >= dwClksPerFrame) { g_dwCyclesThisFrame -= dwClksPerFrame; VideoUpdateFlash(); static BOOL lastupdates[2] = {0,0}; if (!anyupdates && !lastupdates[0] && !lastupdates[1] && VideoApparentlyDirty()) { VideoCheckPage(1); // force=1 static DWORD lasttime = 0; DWORD currtime = GetTickCount(); if ((!g_bFullSpeed) || (currtime-lasttime >= (DWORD)(g_bGraphicsMode ? 100 : 25))) { VideoRefreshScreen(); lasttime = currtime; } } lastupdates[1] = lastupdates[0]; lastupdates[0] = anyupdates; anyupdates = 0; MB_EndOfVideoFrame(); } // if (!g_bFullSpeed) { SysClk_WaitTimer(); #if DBG_CALC_FREQ if (g_nPerfFreq) { QueryPerformanceCounter((LARGE_INTEGER*)&nTime1); LONGLONG nTimeDiff = nTime1 - nTime0; double fTime = (double)nTimeDiff / (double)(LONGLONG)g_nPerfFreq; g_fDbg[g_nIdx] = fTime; g_nIdx = (g_nIdx+1) & (MAX_CNT-1); g_fMeanPeriod = 0.0; for(UINT n=0; n<MAX_CNT; n++) g_fMeanPeriod += g_fDbg[n]; g_fMeanPeriod /= (double)MAX_CNT; g_fMeanFreq = 1.0 / g_fMeanPeriod; } #endif } }
void ContinueExecution() { static BOOL pageflipping = 0; //? const double fUsecPerSec = 1.e6; #if 1 const UINT nExecutionPeriodUsec = 1000; // 1.0ms // const UINT nExecutionPeriodUsec = 100; // 0.1ms const double fExecutionPeriodClks = g_fCurrentCLK6502 * ((double)nExecutionPeriodUsec / fUsecPerSec); #else const double fExecutionPeriodClks = 1800.0; const UINT nExecutionPeriodUsec = (UINT) (fUsecPerSec * (fExecutionPeriodClks / g_fCurrentCLK6502)); #endif // bool bScrollLock_FullSpeed = g_uScrollLockToggle ? g_bScrollLock_FullSpeed : (GetKeyState(VK_SCROLL) < 0); g_bFullSpeed = ( (g_dwSpeed == SPEED_MAX) || bScrollLock_FullSpeed || (DiskIsSpinning() && enhancedisk && !Spkr_IsActive() && !MB_IsActive()) ); if(g_bFullSpeed) { // Don't call Spkr_Mute() - will get speaker clicks MB_Mute(); SysClk_StopTimer(); #ifdef USE_SPEECH_API g_Speech.Reset(); // TODO: Put this on a timer (in emulated cycles)... otherwise CATALOG cuts out #endif g_nCpuCyclesFeedback = 0; // For the case when this is a big -ve number // Switch to normal priority so that APPLEWIN process doesn't hog machine! //. EG: No disk in Drive-1, and boot Apple: Windows will start to crawl! SetPriorityNormal(); } else { // Don't call Spkr_Demute() MB_Demute(); SysClk_StartTimerUsec(nExecutionPeriodUsec); // Switch to higher priority, eg. for audio (BUG #015394) SetPriorityAboveNormal(); } #ifdef WS_VIDEO wsVideoNewDirtyRect.ulx = FRAMEBUFFER_W; wsVideoNewDirtyRect.uly = FRAMEBUFFER_H; wsVideoNewDirtyRect.lrx = 0; wsVideoNewDirtyRect.lry = 0; #endif // int nCyclesToExecute = (int) fExecutionPeriodClks + g_nCpuCyclesFeedback; if(nCyclesToExecute < 0) nCyclesToExecute = 0; DWORD dwExecutedCycles = CpuExecute(nCyclesToExecute); g_dwCyclesThisFrame += dwExecutedCycles; // cyclenum = dwExecutedCycles; DiskUpdatePosition(dwExecutedCycles); JoyUpdatePosition(); SpkrUpdate(cyclenum); sg_SSC.CommUpdate(cyclenum); PrintUpdate(cyclenum); // const DWORD CLKS_PER_MS = (DWORD)g_fCurrentCLK6502 / 1000; emulmsec_frac += dwExecutedCycles; if(emulmsec_frac > CLKS_PER_MS) { emulmsec += emulmsec_frac / CLKS_PER_MS; emulmsec_frac %= CLKS_PER_MS; } #ifdef WS_VIDEO if (wsVideoNewDirtyRect.lrx > wsVideoNewDirtyRect.ulx) { if (wsVideoNewDirtyRect.ulx < wsVideoAllDirtyRect.ulx) wsVideoAllDirtyRect.ulx = wsVideoNewDirtyRect.ulx; if (wsVideoNewDirtyRect.uly < wsVideoAllDirtyRect.uly) wsVideoAllDirtyRect.uly = wsVideoNewDirtyRect.uly; if (wsVideoNewDirtyRect.lrx > wsVideoAllDirtyRect.lrx) wsVideoAllDirtyRect.lrx = wsVideoNewDirtyRect.lrx; if (wsVideoNewDirtyRect.lry > wsVideoAllDirtyRect.lry) wsVideoAllDirtyRect.lry = wsVideoNewDirtyRect.lry; } if(g_dwCyclesThisFrame >= dwClksPerFrame) { g_dwCyclesThisFrame -= dwClksPerFrame; wsVideoRefresh(); } #else // // DETERMINE WHETHER THE SCREEN WAS UPDATED, THE DISK WAS SPINNING, // OR THE KEYBOARD I/O PORTS WERE BEING EXCESSIVELY QUERIED THIS CLOCKTICK VideoCheckPage(0); BOOL screenupdated = VideoHasRefreshed(); BOOL systemidle = 0; //(KeybGetNumQueries() > (clockgran << 2)); // && (!ranfinegrain); // TO DO if(screenupdated) pageflipping = 3; // if(g_dwCyclesThisFrame >= dwClksPerFrame) { g_dwCyclesThisFrame -= dwClksPerFrame; if(g_nAppMode != MODE_LOGO) { VideoUpdateFlash(); static BOOL anyupdates = 0; static DWORD lastcycles = 0; static BOOL lastupdates[2] = {0,0}; anyupdates |= screenupdated; // lastcycles = cumulativecycles; if ((!anyupdates) && (!lastupdates[0]) && (!lastupdates[1]) && VideoApparentlyDirty()) { VideoCheckPage(1); static DWORD lasttime = 0; DWORD currtime = GetTickCount(); if ((!g_bFullSpeed) || (currtime-lasttime >= (DWORD)((g_bGraphicsMode || !systemidle) ? 100 : 25))) { VideoRefreshScreen(); lasttime = currtime; } screenupdated = 1; } lastupdates[1] = lastupdates[0]; lastupdates[0] = anyupdates; anyupdates = 0; if (pageflipping) pageflipping--; } MB_EndOfVideoFrame(); } // #endif // ifndef WS_VIDEO if(!g_bFullSpeed) { SysClk_WaitTimer(); #if DBG_CALC_FREQ if(g_nPerfFreq) { QueryPerformanceCounter((LARGE_INTEGER*)&nTime1); LONGLONG nTimeDiff = nTime1 - nTime0; double fTime = (double)nTimeDiff / (double)(LONGLONG)g_nPerfFreq; g_fDbg[g_nIdx] = fTime; g_nIdx = (g_nIdx+1) & (MAX_CNT-1); g_fMeanPeriod = 0.0; for(UINT n=0; n<MAX_CNT; n++) g_fMeanPeriod += g_fDbg[n]; g_fMeanPeriod /= (double)MAX_CNT; g_fMeanFreq = 1.0 / g_fMeanPeriod; } #endif } }
static void *cpu_thread(void *dummyptr) { assert(pthread_self() == cpu_thread_id); LOG("cpu_thread : initialized..."); struct timespec deltat; #if !MOBILE_DEVICE struct timespec disk_motor_time; #endif struct timespec t0; // the target timer struct timespec ti, tj; // actual time samples bool negative = false; long drift_adj_nsecs = 0; // generic drift adjustment between target and actual int debugging_cycles0 = 0; int debugging_cycles = 0; #if DEBUG_TIMING unsigned long dbg_ticks = 0; int speaker_neg_feedback = 0; int speaker_pos_feedback = 0; unsigned int dbg_cycles_executed = 0; #endif do { #ifdef AUDIO_ENABLED LOG("CPUTHREAD %lu LOCKING FOR MAYBE INITIALIZING AUDIO ...", cpu_thread_id); pthread_mutex_lock(&interface_mutex); if (emul_reinitialize_audio) { emul_reinitialize_audio = false; speaker_destroy(); MB_Destroy(); audio_shutdown(); audio_init(); speaker_init(); MB_Initialize(); } pthread_mutex_unlock(&interface_mutex); LOG("UNLOCKING FOR MAYBE INITIALIZING AUDIO ..."); #endif if (emul_reinitialize) { reinitialize(); } LOG("cpu_thread : begin main loop ..."); clock_gettime(CLOCK_MONOTONIC, &t0); do { SCOPE_TRACE_CPU("CPU mainloop"); // -LOCK----------------------------------------------------------------------------------------- SAMPLE ti #ifdef AUDIO_ENABLED if (UNLIKELY(emul_pause_audio)) { emul_pause_audio = false; audio_pause(); } #endif pthread_mutex_lock(&interface_mutex); #ifdef AUDIO_ENABLED if (UNLIKELY(emul_resume_audio)) { emul_resume_audio = false; audio_resume(); } #endif clock_gettime(CLOCK_MONOTONIC, &ti); deltat = timespec_diff(t0, ti, &negative); if (deltat.tv_sec) { if (!is_fullspeed) { TIMING_LOG("NOTE : serious divergence from target time ..."); } t0 = ti; deltat = timespec_diff(t0, ti, &negative); } t0 = timespec_add(t0, EXECUTION_PERIOD_NSECS); // expected interval drift_adj_nsecs = negative ? ~deltat.tv_nsec : deltat.tv_nsec; // set up increment & decrement counters cpu65_cycles_to_execute = (cycles_persec_target / 1000); // cycles_persec_target * EXECUTION_PERIOD_NSECS / NANOSECONDS_PER_SECOND if (!is_fullspeed) { cpu65_cycles_to_execute += cycles_speaker_feedback; } if (cpu65_cycles_to_execute < 0) { cpu65_cycles_to_execute = 0; } #ifdef AUDIO_ENABLED MB_StartOfCpuExecute(); #endif if (is_debugging) { debugging_cycles0 = cpu65_cycles_to_execute; debugging_cycles = cpu65_cycles_to_execute; } do { if (is_debugging) { cpu65_cycles_to_execute = 1; } cpu65_cycle_count = 0; cycles_checkpoint_count = 0; cpu65_run(); // run emulation for cpu65_cycles_to_execute cycles ... if (is_debugging) { debugging_cycles -= cpu65_cycle_count; if (c_debugger_should_break() || (debugging_cycles <= 0)) { int err = 0; if ((err = pthread_cond_signal(&dbg_thread_cond))) { ERRLOG("pthread_cond_signal : %d", err); } if ((err = pthread_cond_wait(&cpu_thread_cond, &interface_mutex))) { ERRLOG("pthread_cond_wait : %d", err); } if (debugging_cycles <= 0) { cpu65_cycle_count = debugging_cycles0 - debugging_cycles/*<=0*/; break; } } } if (emul_reinitialize) { reinitialize(); } } while (is_debugging); #if DEBUG_TIMING dbg_cycles_executed += cpu65_cycle_count; #endif g_dwCyclesThisFrame += cpu65_cycle_count; #ifdef AUDIO_ENABLED MB_UpdateCycles(); // update 6522s (NOTE: do this before updating cycles_count_total) #endif timing_checkpoint_cycles(); #if CPU_TRACING cpu65_trace_checkpoint(); #endif #ifdef AUDIO_ENABLED speaker_flush(); // play audio #endif if (g_dwCyclesThisFrame >= dwClksPerFrame) { g_dwCyclesThisFrame -= dwClksPerFrame; #ifdef AUDIO_ENABLED MB_EndOfVideoFrame(); #endif } clock_gettime(CLOCK_MONOTONIC, &tj); pthread_mutex_unlock(&interface_mutex); // -UNLOCK--------------------------------------------------------------------------------------- SAMPLE tj #if !MOBILE_DEVICE if (timing_shouldAutoAdjustSpeed()) { disk_motor_time = timespec_diff(disk6.motor_time, tj, &negative); assert(!negative); if (!is_fullspeed && #ifdef AUDIO_ENABLED !speaker_isActive() && #endif !video_isDirty() && (!disk6.motor_off && (disk_motor_time.tv_sec || disk_motor_time.tv_nsec > DISK_MOTOR_QUIET_NSECS)) ) { TIMING_LOG("auto switching to full speed"); _timing_initialize(CPU_SCALE_FASTEST); } } #endif if (!is_fullspeed) { deltat = timespec_diff(ti, tj, &negative); assert(!negative); long sleepfor = 0; if (!deltat.tv_sec) { sleepfor = EXECUTION_PERIOD_NSECS - drift_adj_nsecs - deltat.tv_nsec; } if (sleepfor <= 0) { // lagging ... static time_t throttle_warning = 0; if (t0.tv_sec - throttle_warning > 0) { TIMING_LOG("not sleeping to catch up ... %ld . %ld", deltat.tv_sec, deltat.tv_nsec); throttle_warning = t0.tv_sec; } } else { deltat.tv_sec = 0; deltat.tv_nsec = sleepfor; TRACE_CPU_BEGIN("sleep"); nanosleep(&deltat, NULL); TRACE_CPU_END(); } #if DEBUG_TIMING // collect timing statistics if (speaker_neg_feedback > cycles_speaker_feedback) { speaker_neg_feedback = cycles_speaker_feedback; } if (speaker_pos_feedback < cycles_speaker_feedback) { speaker_pos_feedback = cycles_speaker_feedback; } dbg_ticks += EXECUTION_PERIOD_NSECS; if ((dbg_ticks % NANOSECONDS_PER_SECOND) == 0) { TIMING_LOG("tick:(%ld.%ld) real:(%ld.%ld) cycles exe: %d ... speaker feedback: %d/%d", t0.tv_sec, t0.tv_nsec, ti.tv_sec, ti.tv_nsec, dbg_cycles_executed, speaker_neg_feedback, speaker_pos_feedback); dbg_cycles_executed = 0; dbg_ticks = 0; speaker_neg_feedback = 0; speaker_pos_feedback = 0; } #endif } #if !MOBILE_DEVICE if (timing_shouldAutoAdjustSpeed()) { if (is_fullspeed && ( #ifdef AUDIO_ENABLED speaker_isActive() || #endif video_isDirty() || (disk6.motor_off && (disk_motor_time.tv_sec || disk_motor_time.tv_nsec > DISK_MOTOR_QUIET_NSECS))) ) { double speed = alt_speed_enabled ? cpu_altscale_factor : cpu_scale_factor; if (speed < CPU_SCALE_FASTEST) { TIMING_LOG("auto switching to configured speed"); _timing_initialize(speed); } } } #endif if (UNLIKELY(emul_reinitialize)) { break; } #ifdef AUDIO_ENABLED if (UNLIKELY(emul_reinitialize_audio)) { break; } #endif if (UNLIKELY(cpu_shutting_down)) { break; } } while (1); if (UNLIKELY(cpu_shutting_down)) { break; } } while (1); #ifdef AUDIO_ENABLED speaker_destroy(); MB_Destroy(); audio_shutdown(); #endif return NULL; }