PowerPak + Tetramino = sprite flicker

Discuss hardware-related topics, such as development cartridges, CopyNES, PowerPak, EPROMs, or whatever.

Moderator: Moderators

User avatar
blargg
Posts: 3715
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Post by blargg »

Celius wrote:If it leaves off on sprite 63, would it overwrite sprite #63 and sprite #0 with sprite #0 and #1?
It seems to affect pairs only, with the first of the two an even-indexed sprite. So it could modify 62 and 63, but not 63 and 0. Try running the ROM to see it in action.
Celius
Posts: 2158
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Post by Celius »

So actually, sprite #0 and #1 could never get corrupted, since they would be overwritten with #0 and #1. This is a huge relief, because I just have a project where everything depends on a sprite #0 hit.

Also, I don't have the means to test this on hardware (I'm more of a software guy, but I plan to expand my hardware knowledge/tools). I don't think this would work really testing it on an emulator, because this is a newly discovered quirk.

In my engine, I don't do sprite DMA every frame (it's more like every 5-7 frames). So are those sprites permanently overwritten until I do another DMA transfer?

Another question I have is what if it's like in my case where bars are blanked on top and on bottom of the screen, and it encounters hidden sprites in the top bar? What effect will that have if there are hidden sprites in the top bar, and visible/rendered sprites in the on-screen portion?

Sorry for all the questions, it's just something I should be concerned with. I guess if that's a problem, for this particular project I could place all unused sprites in a different area.
User avatar
blargg
Posts: 3715
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Post by blargg »

Celius wrote:So actually, sprite #0 and #1 could never get corrupted, since they would be overwritten with #0 and #1.
Assuming the cause is what I described.
In my engine, I don't do sprite DMA every frame (it's more like every 5-7 frames). So are those sprites permanently overwritten until I do another DMA transfer?
Seems so. And if you trigger it on each of these 5-7 frames, you'll have different sprites getting corrupt, so more than just two will disappear. :)

Even ignoring this hardware glitch, I don't know if it's a good idea to depend on OAM keeping its contents over several frames without being rewritten.
[...] what if it's like in my case where bars are blanked on top and on bottom of the screen, and it encounters hidden sprites in the top bar?
Hmmm, I wonder if delaying of enabling of rendering (ala Battletoads) prevents the glitch. Or by bars at the top do you mean that the PPU is enabled at the start of the frame, then you disable it near the top for a few lines, then re-enable it?
What effect will that have if there are hidden sprites in the top bar, and visible/rendered sprites in the on-screen portion?
Within the "safe" region of the scanline, the glitch seemed to occur anytime there was any sprite falling on that line.
[...] if that's a problem, for this particular project I could place all unused sprites in a different area.
Elaborate. I don't see what problem this is addressing.
Drag
Posts: 1615
Joined: Mon Sep 27, 2004 2:57 pm
Contact:

Post by Drag »

Is this possibly why a bunch of commercial games write $00 to $2003 before using the sprite DMA?

Or does that not prevent the glitch?

Edit: Nevermind, it seems that I've gotten the meanings of "DMA" and "refresh" confused. :S
Celius
Posts: 2158
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Post by Celius »

Yeah, my polygon movie engine runs at like 12 software FPS, and every software frame, I do a DMA transfer (So 60/12 = every 5 frames). So I'll come up with a solution so all my sprites won't get corrupt.

So here's how my polygon engine uses blanking in a very tiny nutshell: uses sprite #0 hit at around scanline 200 and shuts off the screen until it wraps around to scanline 40 on top in the next frame(so 80 scanlines and vblank). Scanlines 40-199 are displayed (rendering is enabled). All of my unused sprites were placed at Y = $FF, so the glitch would occur since rendering is disabled in that location.

But what I'm wondering about is if the glitch would occur if all of my unused sprites were placed in the top blanked bar, in which case all of the used sprites would be rendered after that. If the glitch wouldn't occur for some reason, then I'd just solve the problem by placing unused sprites in the top bar.

Sorry, my last sentence of the last post didn't make much sense. What I was trying to say is that if the glitch will occur by me placing unused sprites in a blanked area, I can probably put them somewhere else. I could shave off 8 pixels of the rendered area and put all the unused sprites in that 8-pixel-tall row. And by shave off, I mean just don't put anything useful there. It will still be rendered, but it will serve as a hiding place for my unused sprites without causing the glitch. And I could still do a DMA transfer every 5 frames wtihout bad glitches.

About sprite #0 hit. If I'm using a sprite #0 hit for blanking purposes, the glitch will happen, as the PPU will be shut off and sprite #0's pixels will cross the "danger" area in the next scanline. But I suppose I could place sprite #0 so that it's last row of pixels is all that is hitting the solid tile below it. This way, sprite #0 hit would occur, but the blanked region would not encompass any of sprite #0's rows (since by the time I shut off the PPU, it has already drawn the last row of sprite #0, which is the only thing that could cause the sprite #0 hit).

I hope what you're saying is really true, because it would be nice just to leave sprite #0 where it is so even if the glitch occurs, sprite #0 will be overwritten with sprite #0, which obviously is no problem.
User avatar
Banshaku
Posts: 2417
Joined: Tue Jun 24, 2008 8:38 pm
Location: Japan
Contact:

Post by Banshaku »

It's good to see that all the testing resulted in finding a new quirk regarding the PPU. It's great that Blarg found it. My technical level about the nes was too low to be able to find something like this.

In a way it's exciting to see that there's still some hidden things that we don't know.
User avatar
blargg
Posts: 3715
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Post by blargg »

Celius wrote:[... the program waits for] sprite #0 hit at around scanline 200 and shuts off the screen until it wraps around to scanline 40 on top in the next frame(so 80 scanlines and vblank). Scanlines 40-199 are displayed (rendering is enabled). All of my unused sprites were placed at Y = $FF, so the glitch would occur since rendering is disabled in that location.
How would the unused sprites be a problem if they are at Y=$FF? You're disabling rendering well before that, so they don't fall on that scanline. So if you're disabling on scanline 200, then only sprites with Y positions from 194 to 200 (assuming double-height is off) would cause the glitch.
If I'm using a sprite #0 hit for blanking purposes, the glitch will happen, as the PPU will be shut off and sprite #0's pixels will cross the "danger" area in the next scanline. But I suppose I could place sprite #0 so that it's last row of pixels is all that is hitting the solid tile below it. This way, sprite #0 hit would occur, but the blanked region would not encompass any of sprite #0's rows (since by the time I shut off the PPU, it has already drawn the last row of sprite #0, which is the only thing that could cause the sprite #0 hit).
Yes, this is the idea I mentioned before. Just remember that you must disable rendering before the end of the visible scanline. The point where you disable rendering will vary each frame since you can't synchronize exactly to when sprite #0 hits, so if that scanline isn't all black, the right edge will wiggle around (implement it and you'll see what I mean).
I hope what you're saying is really true, because it would be nice just to leave sprite #0 where it is so even if the glitch occurs, sprite #0 will be overwritten with sprite #0, which obviously is no problem.
Which sprites are overwritten is based on the exact PPU clock you disable rendering on, which makes it basically unpredictable. Maybe we can find some circumstances where the glitch reliably affects only one pair of sprites. Actually, I have seen a possibility of this, but it needs more study. For the time being, to avoid the glitch you must disable rendering around pixels 60-250 of the visible scanline, and have no sprites or only the last rows of sprites visible on that line.
EDIT: corrected sprite Y positions that cause glitch (not 193-200 as example previously claimed).
Last edited by blargg on Wed Dec 24, 2008 3:46 am, edited 1 time in total.
Celius
Posts: 2158
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Post by Celius »

Ah, I see. I was thinking that the glitch occured if any sprite was in the entire blanked area. So if you shut off the PPU at scanline 200, and there are no sprites touching that scanline, no glitches will happen?

About the scanline wiggling like SMB3's status bar; mine doesn't do that. I have a black solid sprite colliding with a black solid tile, and color #0 is black with no PPU data below it. So no wiggling will happen. I made it this way because there's a time where I have to execute code that takes a variable amount of time. When executing this, I don't do any blanking until I'm done and wait till the it reaches a sprite #0 hit. Though if it's beyond scanline 200, I'll wait till the next frame. Since I do this, I had to make a completely invisible switch from blanking to not blanking.

Let me see if I have this right. If you blank on scanline $FF, you must have all unused sprites at $F0-$F8 (If it's on scanline $F8, only the bottom row of the sprite's pixels will contact $FF, which you said is alright). But if you blank anywhere before scanline $FF, you'll be safe hiding your sprites at Y = $FF?
User avatar
blargg
Posts: 3715
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Post by blargg »

Crap, I'm dropping the ball totally. Let's try a different approach, since I'm failing at the current one. Consider numbered scanlines and possible sprite Y positions A-E (each 8 pixels tall):

Code: Select all

...
192 A
193 AB
194 ABC
195 ABC
196 ABC
197 ABC
198 ABC
199 ABC
200  BC    PPU rendering is disabled on some pixel between 66 and 254
201   CD 
202    DE
203    DE
204    DE
205    DE
206    DE
207    DE
208    DE
209     E
...
A sprite in position A, B, or E won't cause the glitch. A sprite in position C or D (and the others between these two) will.

EDIT: maybe correct this time, but who knows (ugh).

EDIT 2: further testing shows that there are 8 Y positions that cause the glitch, not 7 as shown before (even if sprite's first row is on next scanline, it causes glitch). Diagram above is fixed.
Last edited by blargg on Wed Dec 24, 2008 2:49 pm, edited 2 times in total.
Celius
Posts: 2158
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Post by Celius »

Okay, thanks for clearing that up. That's a really weird quirk. I know if I tested code out on hardware and had that error, I'd never have figured that out (I would need at least 5 more years of experience in electronics to begin forming a thought about some day having the means to do that).

So I guess after my sprite #0 hit, I'll just have to put like 20 cycles worth of wasting time to guarantee that I'm in the safe area (66-254). That should do the trick, since the sprite #0 hit waiting loop doesn't have much of an unpredictability window in terms of timing (it's only 7 cycles, so it's possible to waste a certain amount of time to guarantee that you'll fall into the 66-254 window by the time you want to disable rendering).
User avatar
blargg
Posts: 3715
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Post by blargg »

Please see my rewritten, corrected previous post first. Much better than before.

As for adjusting the rendering disable time, can you just adjust sprite #0's X position? Then you don't need any delay loop. An empirical way to fine-tune the X position is to temporarily make that last scanline non-black, allowing you to see where rendering is being turned off (as well as how much it's varying), then adjust the X position until it's comfortably between 66 and 254.
tepples
Posts: 22708
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples »

blargg wrote:An empirical way to fine-tune the X position is to temporarily make that last scanline non-black, allowing you to see where rendering is being turned off (as well as how much it's varying), then adjust the X position until it's comfortably between 66 and 254.
When I was fixing Tetramino, I made sure that the write to PPUMASK fell close to X=128 by turning on the monochrome bit.
Celius
Posts: 2158
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Post by Celius »

Oh, I'm pretty sure I understand. Your new post makes much more sense, yes.

So the safest way to go would be to have the absence of sprite rows (excluding the last one) crossing the scanline on which rendering is disabled, right?

Also, if I'm understanding correctly, it seems no matter where you start early vblank, it's possible to hide your sprites somewhere.
dvdmth
Posts: 354
Joined: Wed Mar 22, 2006 8:00 am

Post by dvdmth »

I seem to recall that if $2003 is non-zero it causes sprites 0 and 1 to change to whatever sprite pair $2003 is pointint to. For instance, if $2003 was $7D (part of sprite 31), then sprites 30 and 31 would replace sprites 0 and 1. I might not be remembering right though (it was quite a while ago when it came up). I wonder if this glitch is related to that one.

I'm also confused about exactly what is causing the glitch. If shutting off the PPU mid-frame causes sprites to be overwritten where the OAM engine was looking at the time of the shut-off, then why is it that the problem only occurs if sprites are in-range for the following scanline? It isn't like the OAM engine does nothing if no sprites are in-range - it actually has to cycle through OAM to check for in-range sprites. Does the problem only happen after a sprite is found to be in-range on a scanline? If so, then why are cycles 0 through 63 (the "init" phase) off limits for shutting off the PPU? The OAM engine doesn't even look at any sprite data during this period (reading $2004 returns $FF).

Out of curiosity, what value is returned if you read $2004 after diabling the PPU, but before writing anything to $2003 or $4014?
User avatar
blargg
Posts: 3715
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Post by blargg »

Celius wrote:So the safest way to go would be to have the absence of sprite rows (excluding the last one) crossing the scanline on which rendering is disabled, right?
Yes. Note I updated the diagram again (Y positions from scanline-6 to scanline+1 cause the glitch).
Celius wrote:Also, if I'm understanding correctly, it seems no matter where you start early vblank, it's possible to hide your sprites somewhere.
Sure, anywhere but the 8 Y positions that cause the glitch.
dvdmth wrote:I seem to recall that if $2003 is non-zero it causes sprites 0 and 1 to change to whatever sprite pair $2003 is pointint to. For instance, if $2003 was $7D (part of sprite 31), then sprites 30 and 31 would replace sprites 0 and 1. I might not be remembering right though (it was quite a while ago when it came up). I wonder if this glitch is related to that one.
Aha! I just tested and you're right. The source 8-byte group is whichever one SPRADDR ($2003) is within, and the destination is wherever refresh or whatever left off. Normally the destination is the first 8-byte group, but the glitch causes that to be another group.

So if you could predict which group the glitch leaves the write pointer at, you could set SPRADDR to that at the beginning of the frame. I've found that when only sprite #0 intersects the scanline rendering is disabled on (in a way that causes the glitch), during pixels 66-254, it always leaves the write point at the 5th 8-byte group. So setting SPRADDR to $20 at the beginning of the frame should neutralize the glitch (and indeed it works in my test ROM).

But doing further testing of different sprites intersecting the scanline rendering is disabled on, it seems only the first 6 sprites cause the glitch (and always the same pair). If later sprites intersect, it doesn't occur (as long as rendering is disabled on pixels 66-254). Very strange.
Out of curiosity, what value is returned if you read $2004 after diabling the PPU, but before writing anything to $2003 or $4014?
$80, but I'm not sure how that helps.

After disabling PPU before end of frame in a way that causes glitch next frame, SPRADDR is always zero. I determined it by writing to SPRDATA $00 followed by 255 $FF bytes, then finding which byte of OAM was zero. I also verified that only one byte was zero during the scan.

I also did a quick test of delayed PPU enabling and I'm not seeing any OAM corruption. It might be that enabling PPU rendering at a particular clock in will trigger it (the same clock the frame normally would start at), and I'm not enabling on that clock in my test. Or maybe it only occurs when the PPU is already enabled at the start of the frame.
Post Reply