This page is a mirror of Tepples' nesdev forum mirror (URL TBD).
Last updated on Oct-18-2019 Download

OAM rotation routine to reduce 9th sprite issues.

OAM rotation routine to reduce 9th sprite issues.
by on (#99443)
I am still waiting for my PowerPak flash cart to arrive. So I am unable to test my thoughts on real HW. But perhaps my idea is totally wrong and not possible in the first place.

At the moment I am rewriting my default game functions library I have for Z80 to 6502. One aspect is a routine to reduce the effect of the 9th sprite issue by rotating the sprite attribute table. I have been searching and reading but didn't find any exact information of the behaviour of the involved registers. But I was hoping to have found a low-cost OAM rotate routine.

My idea:
1- Use the OAMADDR register to set the offset to start writing. Each NMI the offset is increased with 5*4 bytes (5 sprites).
2- Use $4014 to copy the local OAM to the PPU OAM.

Will this work?
Is OAMADDR of influence of the DMA copy process?
Will OAMADDR wrap around to the start of the PPU OAM?
Re: OAM rotation routine to reduce 9th sprite issues.
by on (#99444)
Duh!

I've overlooked this thread: viewtopic.php?t=8057

Seems to be a workable implementation. :o
I'll try to test it on real HW asap.
Re: OAM rotation routine to reduce 9th sprite issues.
by on (#99446)
Honnestly, your best bet is to do the sprite rotation by software.

Personally I went with the road of having my meta-sprite mazing routine copied in RAM and use self-modifying code to make the sprites "forwards" in a frame, "backwards" the next frame, and repeat. Because the code itself is automatically modified there's no need to constantly check anything and it doesn't hurt the performance of the sprite mazing routine, which is important because it is typically called a lot of times every frame.
Re: OAM rotation routine to reduce 9th sprite issues.
by on (#99447)
Quote:
Honnestly, your best bet is to do the sprite rotation by software.


If the OAMADDR method works it would be much easier and faster then software OAM rotation.
I guess it won't work best for all purposes (Zelda like top down games).

Your routine sounds quite interesting. Could you tell more about it?
Re: OAM rotation routine to reduce 9th sprite issues.
by on (#99448)
Bregalad wrote:
Honnestly, your best bet is to do the sprite rotation by software.

I agree. Not many games have sprites hardcoded to specific OAM locations, due to game objects being constantly loaded, unloaded, moved and animated, all of which cause the data to change a lot, meaning there's little advantage in keeping objects mapped to the same OAM slots over time. The few games that do hardcode OAM positions are either pong-like games (with a fixed number of objects on screen) or newbie attempts at more complex games.

AFAIK, the effect of OAMADDR on sprite DMAs is not that straightforward anyway. I seem to remember there's something weird going on with sprites 0 and 1 not being in order or something like that (I hope someone will clarify this). Personally, I wouldn't trust that rotating OAMADDR would successfully rotate all sprites, even if it appears to work in some circumstances. Even if it did, there would be disadvantages, like not being able to use sprite 0 hits (since "random" sprites would occupy the first OAM position each time).

If you look at commercial games, you'll see that the majority of them regenerate the OAM table every frame (I honestly can't think of a single one that I have personally analyzed that doesn't). This is not only safer (since you don't have to rely on weird OAMADDR behavior) but it's also much more versatile, because you can implement the type of OAM cycling that best fits each game. You can even implement methods that will not break sprite layering in case the game's view needs depth.
Re: OAM rotation routine to reduce 9th sprite issues.
by on (#99449)
Sounds to me like the best way to go about it is a sprite system that has a window that reserves OAM slots for each object, and then puts the sprite data in there slot at different offsets each time so you keep the layering, sprite 0, etc in order.
Re: OAM rotation routine to reduce 9th sprite issues.
by on (#99451)
Quote:
I guess it won't work best for all purposes (Zelda like top down games).

Your routine sounds quite interesting. Could you tell more about it?

Quote:
You can even implement methods that will not break sprite layering in case the game's view needs depth.

In fact my method does something like this.

Because my work-in-progress game is a has Zelda-like top down graphics (but better graphics of course :) ), objects that are on the bottom needs to be displayed "above" the object that are on the top.

Therefore my metasprite mazing routine will always maze the metasprites in a known order (high priority -> low priority), sorted by the main engine. But the order for sprites behind the metasprite themselves is what will be rotated.
To switch between both routines, I could test a flag and jump to the desired routine, but this would be a waste of CPU time - instead I put the system RAM to good use, and copy my routine here. Somewhere near the end of my VBlank interrupt routine I alternate between both versions of the sprite mazing routine with a few EOR instructions (only one routine is stored in ROM and copied to RAM at startup, the switching is done by EOR-ing the bytes who needs to be changed with the appropriate value), and this does the trick.

Basically the code is :
Code:
   ldy #$06
-   ldx SetupSpriteTableIndexTbl.w,Y
   lda SetupSpriteTableRAM.w,X
   eor SetupSpriteTableXOR.w,Y
   sta SetupSpriteTableRAM.w,X
   dey
   bpl -

[...]
SetupSpriteTableIndexTbl   ;Index to bytes that should be changed in setup sprite routine
   .db $0a, $0b, $0e, $70, $71, $6e, $6f

SetupSpriteTableXOR
   .db $85~$0a      ;Change from sta zero page to asl A opcode
   .db SpriteCtr~$0a   ;Change argument to asl A opcode
   .db $ea~$a8      ;Change nop to tay instruction
   .db $ea~$a8      ;Change nop to tay instruction
   .db $b0~$d0      ;Change from bne to bcs opcode
   .db $c6~$e9      ;Change from dec to sbc immediate opcode
   .db SpriteCtr~$08   ;Change argument of spc/dec instruction


SetupSpriteTableRAM is the routine that needs to be called. I can also call plain SetupSpriteTable in ROM to disable the cycling effect.

I have written basically two metasprite mazing routines, one that copies individual sprites in the first -> last order, and the other which does last -> first order. Sprite layering in a single metasprite is unfortunately not possible (well it's possible but it will ficker), but layering of multiple metasprites works well.

However I should probably improve it to use a different metasprite format that uses less bytes. Currently I use 4 bytes per single sprites in each metasprites, and it takes a lot of space in my little 32k ROM.
Re: OAM rotation routine to reduce 9th sprite issues.
by on (#99452)
3gengames wrote:
Sounds to me like the best way to go about it is a sprite system that has a window that reserves OAM slots for each object, and then puts the sprite data in there slot at different offsets each time so you keep the layering, sprite 0, etc in order.

I do something like that. Instead of cycling individual sprites I cycle entire objects. I just process the list of objects in "random" order each frame, so their graphics always end up on different slots, but the layering within each object is respected (this allows the layering of child objects, such as weapons the player carries). In addition to that, I have 2 global "layers", the top one starts at OAM position 0 and goes up and the bottom one starts at position 63 and goes down. With this I can, for example, make sure that explosions will always be in front of the objects that are exploding.
Re: OAM rotation routine to reduce 9th sprite issues.
by on (#99453)
Toku : It's fun you do exactly the opposite of what I do - I cycle only the internals of the metasprite, and you only cycle the metasprites themselves, keeping their sprites in the same order.

I think your solution is good for platforming games, and that my solution is good for top-down games.

The "ultimate" solution would be to have a real priority system, have a buffer that holds all the sprites and their priorities, and then copy it to the actual shadow-OAM keeping the priorities intact, but with sprites of the same priority in a "random" order. This would most certainly be very slow unfortunately.
Re: OAM rotation routine to reduce 9th sprite issues.
by on (#99455)
Bregalad wrote:
I think your solution is good for platforming games, and that my solution is good for top-down games.

True. They're very different scenarios.

Quote:
The "ultimate" solution would be to have a real priority system, have a buffer that holds all the sprites and their priorities, and then copy it to the actual shadow-OAM keeping the priorities intact, but with sprites of the same priority in a "random" order. This would most certainly be very slow unfortunately.

But even then, if many sprites of different depths are close to each other, the ones in the back will likely disappear because the frontmost ones will have higher priority. I honestly can't think of a solution that would fully respect the layering and not cause the sprites that are farther away to disappear completely in extreme situations.
Re: OAM rotation routine to reduce 9th sprite issues.
by on (#99458)
One CPU-intensive solution would be to walk the display list, find groups of sprites that are overlapping, and sort entire groups from left to right and right to left in alternate frames.
Re: OAM rotation routine to reduce 9th sprite issues.
by on (#99459)
@Bregalad: so you are rotating inside the metasprite itself. Right? Not among the metasprites.
Btw what do you mean with 'mazing'. Is it some sort of technique or do you mean sorting?
Re: OAM rotation routine to reduce 9th sprite issues.
by on (#99461)
As far as I can tell, "mazing" is a franglais word for translating the position and state of each game critter to several entries in OAM.
Re: OAM rotation routine to reduce 9th sprite issues.
by on (#99468)
I did some little tests running on emulators but they give different (akward) results.
I have to wait for my powerpak to arrive to see the real effect and investigate more.

I'd probably revert to my old method of rotation by processing my NPC-list (enemies/bullets etc) as it was a ring with a different offset each iteration. And write the meta sprites in that order to the shadow OAM.
Re: OAM rotation routine to reduce 9th sprite issues.
by on (#100683)
After a long long long wait I finally got my POWERPAK.

I just did a quick test and conclude that the OAM rotation works on real HW (USA NES 01). The offset is taken into account and the writes wrap around.
But it still has to be seen how it could look in a game engine. Perhaps offsets of 5 or 6 sprites would improve the effects on screen. We'll have to see.
Re: OAM rotation routine to reduce 9th sprite issues.
by on (#100688)
The main problem with the OAMADDR method is that it is not very effective at changing the order of your sprites. Effectively you're just dividing your original list in to two halves, and each half is in the same order it was before. So, if your 9-sprite-line is completely on either side of your list, it won't get broken up. If you are cycling through all possible addresses, any 9-sprite-line will eventually flicker, but it may take several frames to change.

If I was using this method, and presumed all sprites could be treated equally, I would probably add 156 to my starting address each frame. This number is 39 * 4 bytes, and 39 is relatively prime to 64, so over 64 frames it will start on every possible address, but it's also large enough to significantly change the split point each frame, and over short periods of time it has good coverage of the available spaces (i.e. nicely spread out). I picked 39 specifically because it was relatively prime and close to the golden ratio, which is effective for spread. The goal is to maximize the address change from frame to frame so that the split point is likely to break and unbreak 9-sprite-lines more frequently.

(edited to correct error pointed out by bregalad)
Re: OAM rotation routine to reduce 9th sprite issues.
by on (#100689)
Errr... there is not 256 sprites but only 64 of them.
Re: OAM rotation routine to reduce 9th sprite issues.
by on (#100690)
Oh right, let me correct that... (fixed, see above)

Also, my suggestion is for a generic case (all sprites considered equal), but your sprite usage will undoubtably have patterns in it, for which a better solution tuned to those patterns might be found.
Re: OAM rotation routine to reduce 9th sprite issues.
by on (#100693)
Sprites are discarded based on their OAM position, so this is to cycle the OAM positions so that you have a rapid flicker across all the sprites rather than just completely losing them entirely past the 8th?

And the DMA is able to auto wrap both the PPU OAM and the source page?

Also getting used to 8 bit is weird. Seeing odd addresses like $17 makes me scream inside but there is no penalty on the 6502 :P
Re: OAM rotation routine to reduce 9th sprite issues.
by on (#100696)
You can generalize various ideas to flicker sprites so everyone gets a turn being drawn but if you know your game you can probably optimize the situation. Since different games will have different ways in which sprite drop will cause problems you may have different solutions. You can prioritize how you wish to fill the OAM table, even perform logic to try to optimize the order of filling it based on game objects in play.
Re: OAM rotation routine to reduce 9th sprite issues.
by on (#100697)
Yeah farthest enemies on the screen be they left or right side of the screen, etc.
Re: OAM rotation routine to reduce 9th sprite issues.
by on (#100722)
Yes. Giving whatever the most threatening objects to the player, and the player itself, priority over other objects is probably a good idea.