void ldp::think_delay(unsigned int uMsDelay) { bool bEmulatedCpu = (get_cpu_hz(0) != 0); // whether we've got an emulated cpu // safety check: make sure that we're not using an emulated CPU if (bEmulatedCpu) { printline("think_delay() should not be used with an emulated CPU. Don't use blocking seeking maybe?"); set_quitflag(); } // safety check: make sure pre_init has already been called so that m_start_time has been initialized else if (!m_bPreInitCalled) { printline("think_delay() should not be called until pre_init() has been called."); set_quitflag(); } // call pre_think the same # of times that uMsDelay tells us to, // to ensure that we aren't caught sleeping during an important event for (unsigned int uMs = 0; uMs < uMsDelay; ++uMs) { pre_think(); unsigned int uElapsedMs = elapsed_ms_time(m_start_time); // if we're ahead of where we need to be, then it's ok to stall ... if (uElapsedMs < m_uElapsedMsSinceStart) { /* string msg = "uElapsedMs is " + numstr::ToStr(uElapsedMs) + " and m_uElapsedMssinceStart is " + numstr::ToStr(m_uElapsedMsSinceStart); printline(msg.c_str()); // REMOVE ME */ MAKE_DELAY(1); } // otherwise we're caught up or behind, so just loop so we can make sure we're caught up } }
clock_t uc3_clock (void) { static U32 last_time=0; static U32 overflow=0; clock_t time; // Get the current time. U32 curr_time=Get_system_register(AVR32_COUNT); // Track overflow situation. We made the assumtion here that we can detect 1 overflow situation. // This is true if the function is called faster than the overflow time. // For example, with a CPU at 66 MHz, you must call the function before (1<<32)/66 MHz; i.e. // 65 seconds since the last function call. if( curr_time<last_time ) overflow++; // Convert the current time into absolute UC3_CLOCKS_PER_SEC ticks, given the known overflow // situations. time = (U64)(( ((U64)overflow<<32) | curr_time) * UC3_CLOCKS_PER_SEC) / get_cpu_hz(); // Save the time for the next function call. last_time = curr_time; return time; }
static inline uint64_t ticks_to_us(uint64_t ticks) { return (ticks * 1000000) / get_cpu_hz(); }
void ldp::pre_think() { // IMPORTANT : By definition, this function must be called every millisecond to update the timer (it is called by our cpu emulation every 1 ms) // If it cannot be called every 1 ms (if we are not emulating a cpu for example), then ldp::think_delay() can be // (and should be) used instead of calling this function directly. // IMPORTANT : this must be updated no matter what because vldp relies on this timer when we're paused // START VBLANK COUNT // at the end of this function, if vblank gets asserted, we need to call an event handler in the game class, but we // need to do that after the frame number has been recalculated (if need be), hence the purpose of this variable. bool bVblankAsserted = false; ++m_uElapsedMsSinceStart; // if it's time to increase the vblank count if (m_uElapsedMsSinceStart >= m_uMsVblankBoundary) { ++m_uVblankCount; // compute the next boundary m_uMsVblankBoundary = (unsigned int) ((((Uint64) (m_uVblankCount+1)) * 1000000) / VBLANKS_PER_KILOSECOND); // only increment the mini count if we aren't waiting for vblank to start counting frames if (!m_bWaitingForVblankToPlay) { ++m_uVblankMiniCount; } // else if we have been waiting for vblank to play, then make sure we reset the vblankminicount else { // we want to start at the beginning of a new frame boundary m_uVblankMiniCount = 0; // we just got a vblank, so we're not waiting ... m_bWaitingForVblankToPlay = false; } bVblankAsserted = true; } // END VBLANK COUNT // if we're not waiting to sync up with vblank, then increase timer // (this must be done even if we're paused so VLDP will update our video overlay) if (!m_bWaitingForVblankToPlay) { ++m_uElapsedMsSincePlay; } // If the disc is supposed to be playing then compute which frame # we're on // IMPORTANT: // ldp-vldp.cpp's nonblocking_seek() RELIES on this function NOT calling get_status()!!! // Be very careful about changing this 'm_status' to a get_status() if (m_status == LDP_PLAYING) { unsigned int uDiscFPKS = g_game->get_disc_fpks(); // if our frame counter is in sync with vblank, then just increment frame every 2 vblanks ... if ((uDiscFPKS << 1) == VBLANKS_PER_KILOSECOND) { // if we've had 2 vblanks, then 1 frame has elapsed if (m_uVblankMiniCount > 1) { increment_current_frame(); m_uVblankMiniCount = 0; } // else we've not had enough vblanks, so don't do anything ... } // else if we're not in sync with the vblank (for example if we're dragon's lair with a 24 FPS disc), // then use the elapsed ms since play to determine if the frame has changed else if (m_uElapsedMsSincePlay >= m_uMsFrameBoundary) { increment_current_frame(); // compute the next boundary m_uMsFrameBoundary = (unsigned int) ((((Uint64) (m_uCurrentOffsetFrame+1)) * 1000000) / uDiscFPKS); } // else the current frame has not changed #ifdef DEBUG #ifndef GP2X // GP2X is slow enough that we don't want to do this safety check by default // SAFETY CHECK // This ensures that the number that is calculated is not far off what it should be. // (this test is only meaningful if we are emulating a cpu, because otherwise we may deliberately // be calling this function slower than every 1 ms, such as ffr() or vldp's internal tests ) if (get_cpu_hz(0)) { unsigned int uElapsedMS = elapsed_ms_time(m_play_time); // compute milliseconds unsigned int time_result = m_last_seeked_frame + m_iSkipOffsetSincePlay + (unsigned int) ((((Uint64) uElapsedMS) * g_game->get_disc_fpks()) / 1000000); Uint16 diff = 0; // this isn't necessarily the same as m_uCurrentFrame due to potential skips that can occur // (this is updated immediately, m_uCurrentFrame is updated on a time boundary) unsigned int uCurrentFrame = m_last_seeked_frame + m_iSkipOffsetSincePlay + m_uCurrentOffsetFrame; if (uCurrentFrame > time_result) diff = uCurrentFrame - time_result; else diff = time_result - uCurrentFrame; // if the difference is noticeable, then alert the developer if (diff > 5) { static unsigned int last_warning = 0; if (elapsed_ms_time(last_warning) > 1000) { string s = "WARNING : cycle frame is "; s += numstr::ToStr(uCurrentFrame) + " but time frame is "; s += numstr::ToStr(time_result) + " which has a diff of "; s += numstr::ToStr(diff); printline(s.c_str()); last_warning = refresh_ms_time(); } } } #endif // not GP2X #endif // DEBUG } // otherwise the disc is idle, so we need not change the current frame think(); // call implementation-specific function // If vblank was asserted, let game know about it... // NOTE : this should probably come at the end of this function if (bVblankAsserted) { // let game know about vblank ... g_game->OnVblank(); } }