Cpu Timing of Instructions Regarding Scanlines

Discuss emulation of the Nintendo Entertainment System and Famicom.

Moderator: Moderators

Post Reply
User avatar
psychopathetica
Posts: 7
Joined: Wed Dec 12, 2018 1:30 pm

Cpu Timing of Instructions Regarding Scanlines

Post by psychopathetica » Wed Dec 12, 2018 2:24 pm

Hello. This is the first time I posted here. But hopefully I can get some answers.

Basically I'm redeveloping an NES emulator and improving on the old one I wrote by adding accurate cycle timing. So basically, from all the docs and nes.wikis I've read, each scanline that is fired lasts for exactly 113.666666666667 cpu cycles, which is basically 341 ppu cycles divided by 3 since ppu cycles are 3 times faster than cpu cycles. Now with that said, that would mean for every cpu cycle, it cycles through 3 pixels. To get through each cycle of 113.666666666667 which is 113 of them per scanline, that would mean that each cpu cycle moves at a rate of 1.00589970501475 cycles, which is derived from 113.666666666667 / 113. Cpu cycles are fired from 6502 instructions. Some instructions have 2 to 7 cpu cycles, and some cycles are even added when it exceeds a page boundary. At the moment, I have it coded to where each 6502 instruction called goes through a do loop cycling through each cycle until it exceeds the number of cycles they are capable of. For example, if ADC Absolute is called, which as 4 cycles, it will do like so:

Code: Select all

void MOS6502::Fire_Opcode(byte opcode) {
    switch(opcode){
        ....
        Case ADC_ABSOLUTE: cpu.instruction.ADC.Absolute(cpu.address); break;
        ....
    }
}

void MOS6502::Execute() {
    while (true) {
	 if (cpu.paused == false) {
		opcode = Read_Memory(Registers::pc);
		Fire_Opcode(opcode);
		ppu.Fire_Scanline();
	}
    }
}

void PPU::Fire_Scanline() {
    ushort temp_scanline_cycle = 0;

    do {
        temp_scanline_cycle++;
        scanline_cycle += ppu.NTSC_CPU_CYCLE_SPEED_FOR_SCANLINE;
        ppu_cycle_position += (ppu.NTSC_CPU_CYCLE_SPEED_FOR_SCANLINE * 3.0);
        if (scanline_cycle > cpu.NTSC_CPU_CYCLES_PER_SCANLINE) {
            // Render scanline here
            ppu_cycle_position = 0.0;
            scanline_cycle = static_cast<double>(static_cast<int>(scanline_cycle) % static_cast<int>(cpu.NTSC_CPU_CYCLES_PER_SCANLINE)); // Loop it back to the beginning
            scanline++; // Add a scanline
        }

        if (scanline > ppu.NTSC_TOTAL_PICTURE_HEIGHT) {
            scanline = 0;
            ppu.cycles = 0; 
            scanline_cycle = 0.0000000000;
        }
    } while (!(temp_scanline_cycle >= cpu.cycle_list[opcode]));
}
Where scanline_cycle is a double precision variable, ppu.NTSC_CPU_CYCLE_SPEED_FOR_SCANLINE is 1.00589970501475, ppu.NTSC_TOTAL_PICTURE_HEIGHT is 262, and cpu.cycle_list[opcode] is a list of cycles the opcode makes when called. Note that I excluded any of the stuff that has to do with the 341 cycles of the ppu since I'm not focusing on that. Now when this is called, it will be locked and will not execute the next opcode until all the cycles of the instruction are done one at a time. All the ppu stuff obviously will be fired then. So the temp_scanline_cycle will moves like so:

1 of 4
2 of 4
3 of 4
4 of 4

Then it exits the do loop and fires the next 6502 instruction over, and over, until ultimately, it reaches the end of the scanline.

Now on to the question and my point. Most of the time the cpu cycles end up over exceeding the scanline and carry over to the next scanline. But somehow I feel this is wrong:

Scanline 0:
1 of 4 at cpu cycle 112
2 of 4 at cpu cycle 113

Scanline 1:
3 of 4 at cpu cycle 1
4 of 4 at cpu cycle 2
fire next instruction
1 of 2 at cpu cycle 3
2 of 2 at cpu cycle 4
....
....

If the cycles surpass a scanline or even a frame, do they get reset or carry over. Thanks in advance. :)

tepples
Posts: 22288
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Cpu Timing of Instructions Regarding Scanlines

Post by tepples » Wed Dec 12, 2018 2:38 pm

The 1.00589970501475 thing is not accurate to how the NES hardware works.

The CPU and PPU run independently of each other. The PPU divides a master clock by 4 to produce the dot clock. The CPU divides the same master clock by 12 to produce its clock. During three scanlines, the CPU executes 341 cycles, not 339 or 342. The CPU's phase relative to the PPU does not reset at the start of each scanline; if the scanline begins halfway into an instruction or even one-third of the way into a CPU cycle, so be it.

User avatar
psychopathetica
Posts: 7
Joined: Wed Dec 12, 2018 1:30 pm

Re: Cpu Timing of Instructions Regarding Scanlines

Post by psychopathetica » Wed Dec 12, 2018 11:23 pm

Oh ok. The thing is though is that I need to somehow keep the ppu to run 3 times faster than the cpu and have it be consistent. The only method I can think of is for every cpu cycle that gets fired, do 3 ppu cycles. Thats the thing I need to figure out. As for the 341, that's ppu cycles per scanline, not 341 cpu cycles for 3 scanlines. So when you divide that by 3, you get the 113.666666666667 cpu cycles executed per scanline, which is how I got the speed for 1.005899705014749. I know already that on the real hardware they run independently, but if the rate is consistent on real hardware, then somehow I need to code it like that. I also just realized that I shouldn't worry too much about the cycles carrying over to the next scanline because of the fact that the cpu and ppu do run independently.

WedNESday
Posts: 1236
Joined: Thu Sep 15, 2005 9:23 am
Location: Berlin, Germany
Contact:

Re: Cpu Timing of Instructions Regarding Scanlines

Post by WedNESday » Thu Dec 13, 2018 5:40 am

Forget about all of that. We, as emulator authors don't need to concern ourselves with the likes of 113.67 CPU cycles per scanline. No doubt you've been reading the wiki page on timing. Just remember this. For every 1 CPU cycle there are 3 PPU cycles (NTSC). When your PPU is running at 3 times the speed of the CPU they will be perfectly synced.

In fact, it's common practice to run the PPU first for x cycles before running the CPU for x cycles. But maybe this will be a bit too advanced for you at this stage.

Any 'extra' cycles executed by your emulator this frame will simply roll over to the next one. This is perfectly normal and nothing to worry about. Don't forget, any game being run will have no concept of it happening.

Post Reply