Background: The NES operated with a simple timing crystal to control
its clock speed. For the NTSC version this speed was 1.7897725MHz and for the
PAL version it was 1.773447MHz.
Problem: Under most operating systems you do not have access to
anything that can provide detailed information about the time down to the
level of 1.79 million ticks per second. In fact in most systems you can get
access to about 10ms chunks of time. There are only 100x 10ms time slices in
a second and that number clearly falls far short of the 1.79 million that are
needed to accurately emulate a system with the clock speed of the NES.
Solution: I created a class called
that provides information about how many clock ticks should have gone by at a
given clock speed since the last time you asked. This unique approach allows
me to maintain the right clock speed within reasonable margins.
Problem: Keeping everything properly synchronized when clock ticks are handed to
you in batches of around 28000 instead of one at a time.
Solution: A new batch of cycles is retrieved from the
and then the cycles are individually iterated over for each of the components
that use them. This forces everything to be properly synchronized allowing for
things such as palette hacks and other fancy PPU trickery.
Problem: The above solution presents another interesting problem however.
Most of the instructions for the NES CPU require more than one cycle to complete.
For that reason most of the time when you hand a cycle to the CPU, it would be jumping
the gun to execute the next instruction. This would clearly throw off the precious
system wide synchronization.
Solution: The last thing the CPU does when it completes an instruction is a
quick calculation to determine how many cycles the next instruction will take. Each
time a cycle is handed to the CPU it adds it to the ones it has collected so far. Once
the number it has is equal to the number it needs to do the next instruction it executes
the instruction. This system maintains synchronization because the instruction still
completes when it should have based on cycle counts.