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:
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.
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]));
}
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.