Clock timing changes on different computers

208 views Asked by At

I'm working on an implementation for the DMG-01 (A.K.A gameboy 1989) on my github. I've already implemented both the APU and the PPU, with (almost) perfect timing on my pc (and the pc of my friends). However, when I run the emulator on one of my friend's pc, it runs twice as fast as mine or the rest of my friends.

The code for syncronizing the clock (between the gameboy and the pc it's running on) is as follows:

Clock.h Header File:

class Clock
{
// ...
public:
    void SyncClock();

private:
    /* API::LR35902_HZ_CLOCK is 4'194'304 */
    using lr35902_clock_period = std::chrono::duration<int64_t, std::ratio<1, API::LR35902_HZ_CLOCK>>;
    static constexpr lr35902_clock_period one_clock_period{1};
    using clock = std::chrono::high_resolution_clock;

private:
    decltype(clock::now()) _last_tick{std::chrono::time_point_cast<clock::duration>(clock::now() + one_clock_period)};
};

Clock.cpp file

void Clock::SyncClock()
{
    // Sleep until one tick has passed.
    std::this_thread::sleep_until(this->_last_tick);

    // Use time_point_cast to convert (via truncation towards zero) back to
    // the "native" duration of high_resolution_clock
    this->_last_tick = std::chrono::time_point_cast<clock::duration>(this->_last_tick + one_clock_period);
}

Which gets called in main.cpp like this:

int main()
{
    // ...
    while (true)
    {
        // processor.Clock() returns the number of clocks it took for the processor to run the
        // current instruction. We need to sleep this thread for each clock passed.
        for (std::size_t current_clock = processor.Clock(); current_clock > 0; --current_clock)
        {
            clock.SyncClock();
        }
    }
    // ...
}

Is there a reason why chrono in this case would be affected in a different way in other computers? Time is absolute, I would understand why in one pc, running the emulator would be slower, but why faster? I checked out the type of my clock (high_resolution_clock) but I don't see why this would be the case. Thanks!

1

There are 1 answers

0
Howard Hinnant On BEST ANSWER

I think you may be running into overflow under the hood of <chrono>.

The expression:

clock::now() + one_clock_period

is problematic. clock is high_resolution_clock, and it is common for this to have nanoseconds resolution. one_clock_period has units of 1/4'194'304. The resultant expression will be a time_point with a period of 1/8'192'000'000'000.

Using signed 64 bit integral types, the max() on such a precision is slightly over 13 days. So if clock::now() returns a .time_since_epoch() greater than 13 days, _last_tick is going to overflow, and may some times be negative (depending on how much clock::now() is beyond 13 days).

To correct try casting one_clock_period to the precision of clock immediately:

static constexpr clock::duration one_clock_period{
    std::chrono::duration_cast<clock::duration>(lr35902_clock_period{1})};