cpu main loop

Discuss emulation of the Nintendo Entertainment System and Famicom.

Moderator: Moderators

tanoatnd
Posts: 37
Joined: Sat Apr 18, 2009 12:45 am

cpu main loop

Post by tanoatnd » Tue May 19, 2009 7:30 am

Code: Select all

Hello,
I am starting to develop my first emulator so I have a few questions for you.
I have already written cpu emulator, I did not realize it to be cycle exact
about reads and writes, however.
Tell me if I am in the right way about cpu main loop:

void cpu_main_loop (void)
{
	scanline = 0;
	vblank = 0;

	for (;;) {
		remaining_cycles = cpu_cycles_per_scanline;
/* 113 NTSC, 107 PAL */
		while (remaining_cycles > 0) {
			..
			(sssttt! cpu at work!)
			..
			if (vblank & nmi_enabled)
				nmi();
		}
		++scanline;
		if (scanline == scanlines_per_frame) {		/* 262 NTSC, 312 PAL */
			scanline = 0;
			vblank = 0;
			update_screen();
		}
		else if (scanline == 242)
			vblank = 1;
	}
}

Bye,
	tano

User avatar
Dwedit
Posts: 4435
Joined: Fri Nov 19, 2004 7:35 pm
Contact:

Post by Dwedit » Tue May 19, 2009 9:13 am

It's not 113, it's 341/3, or 113.666 cpu cycles per scanline.
Also, the code does not seem to count leftover cycles, since with 2 cycles left, it might run an instruction which takes 3 cycles. Where does the leftover cycle go?

Why is NMI inside the while loop?
Here come the fortune cookies! Here come the fortune cookies! They're wearing paper hats!

tanoatnd
Posts: 37
Joined: Sat Apr 18, 2009 12:45 am

Post by tanoatnd » Tue May 19, 2009 4:42 pm

Ok, you are right.

I think I can do the following:

Code: Select all

unsigned scanline_cycles[] = { 113, 113, 114 }; /* NTSC, PAL would be
{ 106, 107, 106, 107, 106, 107, 106, 107, 107, 106, 107, 106, 107, 106, 107, 107 } */
remaining_cycles = scanline_cycles[scanline];
to get correct scanline timings.
Nmi() could be ported out of the while, sure.
But I do not understand when you are speaking about leftover cycles.
Excuse my ignorance.

tanoatnd
Posts: 37
Joined: Sat Apr 18, 2009 12:45 am

Post by tanoatnd » Tue May 19, 2009 4:45 pm

Errata corrige: It is [scanline % 3] for NTSC, % 16 for PAL.

User avatar
cpow
NESICIDE developer
Posts: 1099
Joined: Mon Oct 13, 2008 7:55 pm
Location: Minneapolis, MN
Contact:

Post by cpow » Tue May 19, 2009 5:00 pm

tanoatnd wrote:Errata corrige: It is [scanline % 3] for NTSC, % 16 for PAL.
The "extra cycle" could be taken care of by doing:

Code: Select all

remaining_cycles += scanline_cycles[scanline];
instead of

Code: Select all

remaining_cycles = scanline_cycles[scanline];
Assuming, of course, that remaining_cycles is signed. That way you won't lose a cycle if you started a 3-cycle instruction with only 2 cycles left in remaining_cycles.

An aside: I wonder if using a float to keep track of cycles is ok? Adding 341/3 to float remaining_cycles would seem to be more "accurate" but is it necessary? Ie. does the += take care of it?

User avatar
Dwedit
Posts: 4435
Joined: Fri Nov 19, 2004 7:35 pm
Contact:

Post by Dwedit » Wed May 20, 2009 12:46 am

You know, this kind of structure is exactly what I used to use before I rewrote my emulator to get away from it.
Here come the fortune cookies! Here come the fortune cookies! They're wearing paper hats!

tanoatnd
Posts: 37
Joined: Sat Apr 18, 2009 12:45 am

Post by tanoatnd » Wed May 20, 2009 1:00 am

Ahhhh, that are LOST CYCLES! I remember about them, take note
about them, and then forget them :-)
Thanks!

User avatar
cpow
NESICIDE developer
Posts: 1099
Joined: Mon Oct 13, 2008 7:55 pm
Location: Minneapolis, MN
Contact:

Post by cpow » Wed May 20, 2009 5:00 am

Dwedit wrote:You know, this kind of structure is exactly what I used to use before I rewrote my emulator to get away from it.
What kind of structure?

User avatar
dreampeppers99
Posts: 77
Joined: Mon Aug 21, 2006 4:19 am

Post by dreampeppers99 » Wed May 20, 2009 5:09 am

I divide my into three units.
Emulator - contains the main loop, pause, run, stop function... (know cpu and ppu)
Cpu - running only cpu code context..
Ppu - running only ppu functions...

Each cpu.step() is responsable for execute and add the correct cycles to cycles counter. The same is with ppu.scanline() ... itself responsable for deal with scanline counter...

Code: Select all

while (running) {
                        while (cpu.cycles < CYCLES_TO_SCANLINE) {
                            cpu.step();
                        }
                        cpu.cycles = cpu.cycles - CYCLES_TO_SCANLINE;
                        ppu.scanLine();
                    }
I still don't know where I can put the joystick (press and release) events..

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

Post by tepples » Wed May 20, 2009 5:34 am

You might have it update the joystick on line 240 (the post-render scanline), just before the vblank interrupt.

User avatar
dreampeppers99
Posts: 77
Joined: Mon Aug 21, 2006 4:19 am

Post by dreampeppers99 » Wed May 20, 2009 5:58 am

tepples wrote:You might have it update the joystick on line 240 (the post-render scanline), just before the vblank interrupt.
Should I do in each 240th line the Press and so after the second 240th line I Release the buttons?
( I was thinking in use the OpenGL events... however is faster (or opengl or my emulator...) then I need to syncronize it.)
By the way the Standard Joystick has an correct timming to press and release the buttons?

User avatar
tokumaru
Posts: 12077
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru » Wed May 20, 2009 6:38 am

dreampeppers99 wrote:Should I do in each 240th line the Press and so after the second 240th line I Release the buttons?
You shouldn't be worried about events, because the NES is not event-based, it just wants to know what is pressed when it checks the joypad. So do whatever you have to do in your emulator to read input, use events, whatever, but feed the data to the NES only once per frame, and leave it that way until you update it the next frame.

User avatar
dreampeppers99
Posts: 77
Joined: Mon Aug 21, 2006 4:19 am

Post by dreampeppers99 » Wed May 20, 2009 6:51 am

So do whatever you have to do in your emulator to read input, use events, whatever, but feed the data to the NES only once per frame, and leave it that way until you update it the next frame.
Thanks very much!
Actually I just write return 1 to port!
Question: I still don't have nothing about APU implemented, this could raise something which can stop the Super Mario 1?

User avatar
tokumaru
Posts: 12077
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru » Wed May 20, 2009 7:06 am

dreampeppers99 wrote:Question: I still don't have nothing about APU implemented, this could raise something which can stop the Super Mario 1?
I think SMB1 will run fine without the APU implemented. I believe most games just write data to the APU, and very few rely on DMC or Frame IRQs, or data returned by APU registers. But I'm not 100% sure, so if anyone thinks I'm missing something please say something.

User avatar
MottZilla
Posts: 2835
Joined: Wed Dec 06, 2006 8:18 pm

Post by MottZilla » Wed May 20, 2009 10:17 am

Super Mario 1 won't appear to do anything (it'll look frozen) until you add Sprite 0 Hit flag emulation.

Post Reply