1

I'm planning to write a nes emulator. But first, to understand how emulation works, I'll write a Chip-8 emulator.

The emulator is nearly finished. I've some bugs in games, but this will be fixed soon. My problem number 1 is to synchronize the emulator with the clock speed of the Chip-8. In the internet I've often read, that the general clock speed should be ~ 540Hz. The timers of the chip should be ticked at a frequenz of 60Hz.

To synchronize my emulator with the Chip-8 I've written follow logic:

private void GameTick()
{
    Stopwatch watch = new Stopwatch();
    var instructionCount = 0;
    _gameIsRunning = true;

    while (_gameIsRunning)
    {
        watch.Restart();

        EmulateCycle();

        //Updates the internal timer at a 60hz frequenz
        //540hz (game tick) divided by 9 equals 60hz (timer tick)
        instructionCount++;
        if(instructionCount == 9)
        {
            UpdateSoundAndDelay();
            instructionCount = 0;
        }

        if (_readyToDraw)
        {
            DrawGraphics();
            _readyToDraw = false;
        }

        SetKeys();

        //Pause the game to get a virtual clock speed of ca. 540mhz
        var elapsedMicroseconds = watch.ElapsedTicks / (Stopwatch.Frequency / (1000L * 1000L));    
        while(elapsedMicroseconds < 1852)
        {
            elapsedMicroseconds = watch.ElapsedTicks / (Stopwatch.Frequency / (1000L * 1000L));
        }     
    }
}

For more detailed information look at my repo: https://github.com/Marcel-Hoffmann/Chip-8-Emulator

As you can see, for each cpu cycle, I'll wait for 1852 microseconds. The result will be ~ 540 cycles in a second equals to 540Hz. But I'm not very happy with this logic.

Has someone a better Idea, how to synchronize the clock speed?

user3149497
  • 67
  • 1
  • 8

1 Answers1

2

This is the typical approach, and has many drawbacks - most notably, unnecessary CPU usage and potentially scheduling issues (your application will be seen as 100% CPU beast, so other applications might get their thread quanta before you under load).

A better approach would use a sleep instead - however, by default, the system timer has nowhere near the frequency to accommodate a wait that's less than 2ms. So if you want to use a sleep, you'll need to change the system timer. This is a bit tricky on older Windows (it's a system-wide setting and has noticeable impact on other applications and general CPU usage), but even in that case, it's better than a "busy loop" - as long as you restore the system settings afterwards. On Windows 8 (and to some extent, 7 and Vista), the timer is asynchronous and no longer requires a busy loop, so it's a lot easier to have higher timer resolution.

The system timer APIs are not exposed by .NET, so you'll need to use P/Invokes (timeBeginPeriod and timeEndPeriod for the old-style API). If this isn't available, you can always fall back to your busy loop :)

Luaan
  • 62,244
  • 7
  • 97
  • 116