It is currently Thu May 23, 2019 9:00 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 4 posts ] 
Author Message
PostPosted: Wed Dec 12, 2018 2:24 pm 
Offline
User avatar

Joined: Wed Dec 12, 2018 1:30 pm
Posts: 7
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:
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. :)


Top
 Profile  
 
PostPosted: Wed Dec 12, 2018 2:38 pm 
Online

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 21394
Location: NE Indiana, USA (NTSC)
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.

_________________
Pin Eight | Twitter | GitHub | Patreon


Top
 Profile  
 
PostPosted: Wed Dec 12, 2018 11:23 pm 
Offline
User avatar

Joined: Wed Dec 12, 2018 1:30 pm
Posts: 7
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.


Top
 Profile  
 
PostPosted: Thu Dec 13, 2018 5:40 am 
Offline

Joined: Thu Sep 15, 2005 9:23 am
Posts: 1236
Location: Berlin, Germany
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.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 4 posts ] 

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 11 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group