Hello,
I am writing a NES emulator, but am doing the 6502 CPU first. Now I was wondering: how do you make sure the timing of the CPU is correct? Do you give the CPU a tick every (1/frequency) seconds? If so, how do you make such a high-resolution timer?
In shortL how do you implement proper timing for your CPU?
Timing?
Moderator: Moderators
I operate on a fictional time system I call "master cycles"
every 1 CPU cycle takes 15 master cycles (on NTSC) or 16 (on PAL).
Every 1 dot (aka PPU cycle) takes 5 master cycles.
Therefore... since an instruction like LDA #$00 takes 2 cycles, it would take 30 master cycles (NTSC).
I keep a timestamp for each subsystem (CPU, PPU, APU, and any external system like a mapper). I run the CPU ahead of everything else, and keep adjusting its timestamp as cycles are emulated. When the CPU does something to interact with another system (usually in the form of a register read or write), I emulate the subsystem until its timestamp reaches or exceeds the CPU's timestamp. This is probably more well known as the "catch up" approach.
Basic Example:
Let's say CPU and PPU timestamps are both 0. My CPU emulator then emulates the following code:
Since the value written to $2001 will affect how the PPU operates (it is a PPU register), I would jump to my PPU emu and "catch it up" -- that is... run it until its timestamp is >= 180, then I would apply the value written to $2001 and continue with CPU emulation as normal.
I can run my emu for chunks at a time that split cleanly into frames using this method. IE: I would run the CPU for X master cycles, then catch up all subsystems, then output the generated video/audio to the user, then rinse and repeat as needed.
Since frames (practically) are a fixed length (341 dots * 262 scanlines * 5 master cycles per dot = 446710 master cycles per frame)* this system not only indicates how long I need to run each system, but also where in the frame the system is. That is.. .I can perform math on any given timestamp and know which scanline the system is currently on and whatnot.
** another technical note: Odd frames on NTSC are sometimes 5 master cycles shorter (446705 instead of 446710) due to a PPU cycle being skipped under some circumstances. Rather than reconstruct my entire timebase system, I simply have non-ppu related systems skip this cycle by adding an additional 5 to their timestamps where appropriate **
Anyway that's about the jist of it. Just tally up cycles as you go until you reach a target timestamp.
every 1 CPU cycle takes 15 master cycles (on NTSC) or 16 (on PAL).
Every 1 dot (aka PPU cycle) takes 5 master cycles.
Therefore... since an instruction like LDA #$00 takes 2 cycles, it would take 30 master cycles (NTSC).
I keep a timestamp for each subsystem (CPU, PPU, APU, and any external system like a mapper). I run the CPU ahead of everything else, and keep adjusting its timestamp as cycles are emulated. When the CPU does something to interact with another system (usually in the form of a register read or write), I emulate the subsystem until its timestamp reaches or exceeds the CPU's timestamp. This is probably more well known as the "catch up" approach.
Basic Example:
Let's say CPU and PPU timestamps are both 0. My CPU emulator then emulates the following code:
Code: Select all
LDA #$00 ; after this, CPU timestamp is 30
STA $FE ; CPU time = 75
STA $FF ; 120
STA $2001 ; 180
I can run my emu for chunks at a time that split cleanly into frames using this method. IE: I would run the CPU for X master cycles, then catch up all subsystems, then output the generated video/audio to the user, then rinse and repeat as needed.
Since frames (practically) are a fixed length (341 dots * 262 scanlines * 5 master cycles per dot = 446710 master cycles per frame)* this system not only indicates how long I need to run each system, but also where in the frame the system is. That is.. .I can perform math on any given timestamp and know which scanline the system is currently on and whatnot.
** another technical note: Odd frames on NTSC are sometimes 5 master cycles shorter (446705 instead of 446710) due to a PPU cycle being skipped under some circumstances. Rather than reconstruct my entire timebase system, I simply have non-ppu related systems skip this cycle by adding an additional 5 to their timestamps where appropriate **
Anyway that's about the jist of it. Just tally up cycles as you go until you reach a target timestamp.
You can't, the best accuracy you can hope for is the necessary ~60Hz timing for video and sync everything to that.
I found this page helpful when I was working on my timing: http://www.geisswerks.com/ryan/FAQS/timing.html
I found this page helpful when I was working on my timing: http://www.geisswerks.com/ryan/FAQS/timing.html
so many master cycles is 1 framejohnnie wrote:Thanks for your elaborate explanation on relative internal timing, but what I really meant was: how do you do timing relative to the host system? How do you make sure your emulated CPU does not run at 3GHz but at ~1.7 MHz?
and I output 60 frames a second.
If you draw 1 frame every 16.6667 or whatever milliseconds, you'll be running your emulator at 60 FPS, and thusly running the appropriate amount of master cycles (and therefore the appropriate amount of CPU cycles)