Sprite wackyness

Are you new to 6502, NES, or even programming in general? Post any of your questions here. Remember - the only dumb question is the question that remains unasked.

Moderator: Moderators

Post Reply
User avatar
battagline
Posts: 152
Joined: Wed Sep 05, 2018 11:13 am
Location: Colorado
Contact:

Sprite wackyness

Post by battagline »

So I've been messing around working on an Asteroids game, and I'm getting some sprite wackyness when I fire more than a single bullet at a time. I'm stepping through my NMI code and looking at the OAM data and everything looks like it's ok to me. I'm guessing I'm doing something stupid/simple that I'm just not aware of.

If anyone is willing to take a quick look I'd appreciate it. I've attached the .nes and the Messen debug .dbg file (which I had to change to a .txt to get it to upload).

Thanks
Attachments
nesteroids.nes.dbg.txt
Nesteroids Label File (change to .dbg)
(118.05 KiB) Downloaded 153 times
nesteroids.nes
Failing Nesteroids
(40.02 KiB) Downloaded 167 times
A few of my web games
https://www.embed.com
Or if you're bored at work
https://www.classicsolitaire.com
User avatar
Quietust
Posts: 1918
Joined: Sun Sep 19, 2004 10:59 pm
Contact:

Re: Sprite wackyness

Post by Quietust »

When I ran it in my emulator, I got a totally blank screen, likely because your Sprite DMA is being done too close to the end of VBlank (it starts at the beginning of scanline 259 and then ends in the middle of scanline 1) - when I switched to PAL mode, everything worked great (likely because I'm not emulating the extra OAM refreshing that the PAL PPU does to prevent the memory from decaying).
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.
User avatar
battagline
Posts: 152
Joined: Wed Sep 05, 2018 11:13 am
Location: Colorado
Contact:

Re: Sprite wackyness

Post by battagline »

Quietust wrote:When I ran it in my emulator, I got a totally blank screen, likely because your Sprite DMA is being done too close to the end of VBlank (it starts at the beginning of scanline 259 and then ends in the middle of scanline 1) - when I switched to PAL mode, everything worked great (likely because I'm not emulating the extra OAM refreshing that the PAL PPU does to prevent the memory from decaying).
Part of my confusion in this new world of NES Dev, is I don't fully understand what I should be doing in the NMI and what I should be doing in the main game loop.

I'm also not clear on how much I can do in the NMI vs. the main game loop

Any suggestions?

Thanks
A few of my web games
https://www.embed.com
Or if you're bored at work
https://www.classicsolitaire.com
tepples
Posts: 22705
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Sprite wackyness

Post by tepples »

In vblank, you have 2270 cycles. To make the best use of them, you'll need to buffer the changes to sprites and backgrounds and push them to the PPU before you start calculating the changes for the next frame. There are a few video memory buffer libraries floating around; I wrote one called Popslide.

As for how NMI relates to vblank, there are 3 different ways to divide the tasks associated with every frame: everything in NMI, everything in main, or calculate updates in main and apply them in NMI. Each approach has its pros and cons.

Everything in main

NMI:
1. Increment a variable

Main:
1. Read the controller
2. Move the game objects
3. Calculate new sprite display list and background and palette updates
4. Wait for NMI handler to increment the variable
5. If a new sprite display list is ready, push it to OAM
6. If background and palette updates are ready, push them to video memory
7. Run music as many times as the variable was incremented

Everything in NMI

NMI:
1. If a new sprite display list is ready, push it to OAM
2. If background and palette updates are ready, push them to video memory
3. Run music
4. If in a lag frame, return
5. Read the controller
6. Move the game objects
7. Calculate new sprite display list and background and palette updates

Main:
1. Goto 1

Split responsibility

NMI:
1. If a new sprite display list is ready, push it to OAM
2. If background and palette updates are ready, push them to video memory
3. Run music (bank switching for this can prove tricky)
4. Increment a variable

Main:
1. Read the controller
2. Move the game objects
3. Calculate new sprite display list and background and palette updates
4. Wait for NMI handler to increment the variable

See also tokumaru's explanation
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Sprite wackyness

Post by tokumaru »

Even thought I've been ninja'd by tepples (who even linked to a previous explanation I did on this subject, which I totally forgot BTW), I'm still gonna post my reply because I took the time to write it:

The main thing to keep in mind is that PPU updates must take place during vblank, and never spill into the visible frame, otherwise Bad Things (tm) will happen. As for *how* exactly you can make it so this rule is respected is up to you. There are 3 main possible structures for an NES program:

1- Everything in the main loop: Do your game logic first (buffering all PPU updates along the way), and wait for vblank by polling a flag modified by the NMI handler (all the NMI does is change this flag). When the flag changes, you know you're in vblank and you have about 2273 CPU cycles to carry out the buffered PPU updates. Then you can do sound. Repeat. This is a very simple structure, but has poor handling of lag frames (music will slow down, raster effects will break, etc.).

2- Everything in the NMI handler: This is more versatile, as long as you don't disable NMIs while doing game logic. As soon as the NMI fires, you're in vblank, so you have to carry out any buffered PPU updates. Normally there'll be one or more flags indicating what updates must be performed. Then comes the audio, and finally the game logic, during which PPU updates for the next frame will be buffered. The advantage this method has over the last one is that if an NMI fires before the previous frame's game logic is done, you can still handle the things that must keep working at 60Hz even when the gameplay lags, such as music and raster effects (status bar, parallax layers, etc.).

3- Game logic in the main loop, PPU updates in the NMI handler: this works much like the last method, only with better separation between the game logic and the PPU update code. This structure makes it easier to have multiple game logic loops scattered around your ROM, but they're all interrupted by the same NMI handler when vblank starts. This works very well if your PPU update system is generic enough to work with all game modules, but if you really need it, you can have different sets of PPU update logic. The same system of flags is used here, so the PPU code knows which operations can and can't be done.
User avatar
gauauu
Posts: 779
Joined: Sat Jan 09, 2016 9:21 pm
Location: Central Illinois, USA
Contact:

Re: Sprite wackyness

Post by gauauu »

And in case you feel overwhelmed by the 3 options and pros/cons, keep in mind that if you're doing a small simple project for the sake of learning the NES, most likely any of these 3 methods will be JUST FINE.
The main thing to keep in mind is that PPU updates must take place during vblank, and never spill into the visible frame, otherwise Bad Things (tm) will happen. As for *how* exactly you can make it so this rule is respected is up to you.
This is the important part. As long as all PPU updates happen during vblank, you'll generally be ok for a simple project.
User avatar
battagline
Posts: 152
Joined: Wed Sep 05, 2018 11:13 am
Location: Colorado
Contact:

Re: Sprite wackyness

Post by battagline »

Thanks for the explanation. Does it make sense to double buffer the oam data? Or is there a more efficient way to do all my sprite calculations in the game loop so I can just move the data over in the nmi code?

Thanks
A few of my web games
https://www.embed.com
Or if you're bored at work
https://www.classicsolitaire.com
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Sprite wackyness

Post by tokumaru »

gauauu wrote:keep in mind that if you're doing a small simple project for the sake of learning the NES, most likely any of these 3 methods will be JUST FINE.
Just NEVER do what Nerdy Nights does in the first few lessons (I think), which's everything in the NMI with the game logic first. That just wastes your precious vblank time with things that don't need it.
User avatar
battagline
Posts: 152
Joined: Wed Sep 05, 2018 11:13 am
Location: Colorado
Contact:

Re: Sprite wackyness

Post by battagline »

@clearvus set me straight. I didn't realize that the OAM data was being copied during a DMA and not somehow shared with the PPU. So double buffering makes no sense.

Thanks everyone for setting me straight on this stuff... hopefully I get this part now.
A few of my web games
https://www.embed.com
Or if you're bored at work
https://www.classicsolitaire.com
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Sprite wackyness

Post by tokumaru »

Yeah, the memory page where you write OAM data to is already a buffer, the actual OAM inside the PPU remains unchanged until you do a sprite DMA. Actually, it will decay if PPU rendering is off for too long, but under normal circumstances, just skipping a DMA if the buffer isn't ready will cause the previous frame's sprites to be displayed again.
Post Reply