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

Sprite logic

Sprite logic
by on (#29406)
When I move a sprite partially off the top or left of the screen in emulators (I can't see the behavior on a real NES with my TV), after a few pixels it clips the sprite. Why is this and how can I prevent it? This logic isn't implemented in my emulator, but if it's really how it works I'd like to put it in and well, understand it.

Also are we sure that Nintendulator has a perfect renderer? I ask because my PowerPak is having problems with a little test ROM I wrote to test my TV's safe area while emulators are not. I also don't think this has anything to do with the hardware since sprite DMA in games seems to work fine and I can't imagine where the PowerPak's initialization would break my code.

Anyone mind looking at my program? I suppose there's something I could be overlooking but it's pretty straight forward.

http://files-upload.com/files/675089/screentest.zip

It assembles with ASM6. Very sorry about the free file upload site!

by on (#29412)
I believe your sprite clipping is due to the fact that it wraps around to be at Y Coordinate $FF, it's going to a pixel that is not displayed, since the screen is only $F0 pixels tall. Once it reaches $EF, you'll be able to see it. This may be total garbage, but this is how I think it works. Say your sprite is at coordinates $FF. At Scanline $FF (Off screen), the game will try to render the sprite. The rest of the sprite should be on the top of the screen, but the NES already rendered stuff up there, and by the time it gets back to the top of the screen, it forgets that there was a sprite at $FF. I'm not certain, but that might be it.

In Nintendulator, It seems that if you put a sprite at coordinates 0,0, it's not exactly in the corner of the screen. It will appear to be at pixel 0,1. I don't know why this is, but I had to mention it while I'm here.

EDIT: You should get a freewebs account if you want a place to store stuff that people can download. It's of course, free, and doesn't require the waiting.

by on (#29415)
-deleted-

by on (#29419)
But in this case Nintendulator is correct, sprites are delayed 1 scanline. If you put it at 0,0 then displaying at 0,1 is right.

If your sprite is at 0,0 and you move it left, it will now be at 255,0 and should display on the right side. If you move it up it will be at 0,255 which is off the viewing area at the bottom.

by on (#29421)
I didn't know sprites were delayed a scanline. That's wierd.

So yeah, according to NESdev wiki, sprites do not wrap around if they are in positions $F9-$FF. If your sprite is at $00, and you move left one pixel, it'll be at $FF, so it will jump to the other side of the screen. Since there are no wrap arounds, the rest of your sprite will not be visible. That is also the case for Y coords.

There aren't many ways to prevent this. If you want, you can use the sprite clipping so it won't be visible in the left column of the screen. For vertical movement, it's not that big of a deal if you're using an NTSC system, since the top two bars are cut off. I'd use sprite clipping, because the sudden disappearing is ugly.

I looked over your code, and I noticed some things that looked like they may cause some sorts of problems. Not anything too big though.

Code:
   lda sprite_y
   clc
   sbc #1      ; compensate for ppu


Is there any reason for clearing the carry? Maybe there is, but this could be a typo. Just wanted to point it out.

And I see you do this:
Code:

   lda #0
   ldx #$ff
-   sta $200, x
   dex
   bne -      ; clear sprite page


You know you can just load X with $00 and it will clear everything? If you do "ldx #$FF" for the loop, it'll just clear everything but the last byte.

EDIT: I also don't think you're clearing the Sprite_X or Sprite_Y values before using them. That could be a problem. You'd do well to clear all of RAM with this simple loop:

Code:
 lda #0
 tax
-
 sta $0,x
 sta $100,x
 sta $200,x
 sta $300,x
 sta $400,x
 sta $500,x
 sta $600,x
 sta $700,x
 inx
 bne -


I'm pretty sure the NES has random values in RAM on reset. Counting on them being #$00 is not a good thing to do.

by on (#29422)
I was briefly looking at Super Mario Bros. Apparently, the programmers of that game didn't realize that sprites were lagged one scanline either.

by on (#29424)
It's obvious to me when I look at it. In my game, I have portraits for each of the characters. There aren't enough colors in one attribute section to do shading, hair color, and whatnot. So I have to do sprite overlapping. When I first entered in all the values for the sprites, I had them all multiples of 8. They ended up being 1 pixel too low, so I had to go back and subtract 1 from all the Y values.

by on (#29426)
bunnyboy wrote:
If your sprite is at 0,0 and you move it left, it will now be at 255,0 and should display on the right side. If you move it up it will be at 0,255 which is off the viewing area at the bottom.

Any idea why the top and left clip while sprites can scroll smoothly off the right and bottom?

Celius wrote:
I didn't know sprites were delayed a scanline. That's wierd.

Not weird at all considering evaluation is carried out one line in advance.

Celius wrote:
So yeah, according to NESdev wiki, sprites do not wrap around if they are in positions $F9-$FF. If your sprite is at $00, and you move left one pixel, it'll be at $FF, so it will jump to the other side of the screen. Since there are no wrap arounds, the rest of your sprite will not be visible. That is also the case for Y coords.

I know, but it doesn't matter, I don't mind if the sprite goes off screen and takes longer to wrap around. I just don't want the sprite to clip before it's fully off screen as it does only on the left and top.

Celius wrote:
There aren't many ways to prevent this. If you want, you can use the sprite clipping so it won't be visible in the left column of the screen. For vertical movement, it's not that big of a deal if you're using an NTSC system, since the top two bars are cut off. I'd use sprite clipping, because the sudden disappearing is ugly.

To me it doesn't matter if it's ugly, I just want to see the sprite scroll off screen without clipping. Otherwise my program will have to scroll the background.

Celius wrote:
Is there any reason for clearing the carry? Maybe there is, but this could be a typo. Just wanted to point it out.

You might be onto something... I meant to set carry, maybe I'm not having a clean underflow...

Celius wrote:
You know you can just load X with $00 and it will clear everything? If you do "ldx #$FF" for the loop, it'll just clear everything but the last byte.

How do you figure? $200+FF = $2FF = last byte in the page; everything should be cleared.

Celius wrote:
I'm pretty sure the NES has random values in RAM on reset. Counting on them being #$00 is not a good thing to do.

Really it doesn't matter what the variables are initialized to, it will just make the sprites appear somewhere else, no biggie.

Edit: nope, the SEC didn't fix anything :)

by on (#29427)
kyuusaku wrote:
Celius wrote:
You know you can just load X with $00 and it will clear everything? If you do "ldx #$FF" for the loop, it'll just clear everything but the last byte.

How do you figure? $200+FF = $2FF = last byte in the page; everything should be cleared.

Celius is right. Your code will fail to clear $200, it will only clear $201-$2FF. When X reaches 0, the branch is not taken, so the value at $200 will not be cleared. By initializing X with 0, you take care of that location right on the first write.

Back to the topic, there is no way to smoothly scroll sprites from/to the top or from/to the left of the screen and still see the whole background, this is a limition of the NES. The NES has an option to not render the leftmost 8 pixels, and by using that the sprites can scroll smoothly from/to the left of the screen. To have them scroll smoothly from/to the top, you may keep rendering disabled for a few scanlines after the end of VBlank, achieving a similar effect.

I don't think you should be to worried about this. Most TV's cut the parts of the image affected by this glitch. And almost all commercial games have that effect, and no one has complained until now.

by on (#29439)
Dwedit wrote:
I was briefly looking at Super Mario Bros. Apparently, the programmers of that game didn't realize that sprites were lagged one scanline either.

Yes they did, or sprite 0 wouldn't hit.

kyuusaku wrote:
Any idea why the top and left clip while sprites can scroll smoothly off the right and bottom?

Clipping to the top and left would require the PPU's registers to be able to represent negative coordinates. The Super NES PPU can do this; the NES PPU cannot.

by on (#29449)
Celuis wrote:
I'd use sprite clipping, because the sudden disappearing is ugly.

I remeber you said me the other way arround one time. You said this was unnoticeable.
Quote:
Code:
   lda #0
   ldx #$ff
-   sta $200, x
   dex
   bne -      ; clear sprite page

In fact to be correct this code just have to be the other way arround : lda #$ff and ldx #$00 so that it will clear all bytes, and SRAM has to be cleared with any value in the $f0-$ff range, NOT $00 to be effectively cleared !

When it comes to scroll smoothly horizontally the only solution is to use hardware clipping, as someone already mentionned. That's most likely why this feature is existing at all. In function of your game engine and scrolling workings, you'll want this area to be clipped or not (most games either always clips it or never clips it). Also you cannot rely to the leftmost&rightmost 8 pixels to be rendered as far I've heard some monitors will hide this. If you use leftmost hardware clipping, a X coordinate of zero will completely hide the sprite and it will have no effect at all (exept to be counted in the 8 sprites per line limitation).

When it comes to scroll smoothly vertically it's not too hard as most monitors and emulators will hide the topmost and bottommost 8 pixels, so this is just enough to scroll smoothly. If you want to do PAL coding or if you are using 8x16 sprites, this isn't true any longer. In both cases, leaving the rendering off ala Battletoads would be one solution, however scrolling becomes a pain in the ass.
What you can do is abuse the 8 sprites per line limitation and place 8 top-priorities sprites at Y coordinate 0. Then you just have to turn the BG (and BG only, not sprites) off via $2001 in order to get a black background on the top of invisible sprites, effectively blanking the area. You'd want to enable BG back at either scanline #9 (PAL) or scanliine #17 (8x16 sprites) for completely smooth movement. I guess the scrolling will be right because the sprites were left enabled technically. It that's not the case, you may consider bankwitching an all-blank CHRROM page for the top scanlines, ending with a similar effect.

by on (#29458)
tokumaru wrote:
Celius is right. Your code will fail to clear $200, it will only clear $201-$2FF. When X reaches 0, the branch is not taken, so the value at $200 will not be cleared. By initializing X with 0, you take care of that location right on the first write.

I see, good catch.

tokumaru wrote:
I don't think you should be to worried about this. Most TV's cut the parts of the image affected by this glitch. And almost all commercial games have that effect, and no one has complained until now.

Well it does matter since I can see the left side clipping with my TV so it makes it frustrating to get a reading. I guess I'll have to switch to the background then.

tepples wrote:
Clipping to the top and left would require the PPU's registers to be able to represent negative coordinates. The Super NES PPU can do this; the NES PPU cannot.

That wouldn't be necessary if the sprites wrapped from $FF to $00 correctly. For the PPU to wrap like that though, I guess it would need to fetch coordinates 8/16 lines early and cycle through all the horizontal pixels.

Bregalad wrote:
SRAM has to be cleared with any value in the $f0-$ff range, NOT $00 to be effectively cleared !

In this case I want $00 since I only really care to initialize byte $2 of the sprites.

by on (#29470)
kyuusaku wrote:
bunnyboy wrote:
If your sprite is at 0,0 and you move it left, it will now be at 255,0 and should display on the right side. If you move it up it will be at 0,255 which is off the viewing area at the bottom.

Any idea why the top and left clip while sprites can scroll smoothly off the right and bottom?


This is because wrap arounds don't occur. And also, the sprite is rendered left to right. Sprites are kept track of from the top left corner's X, Y coords. When the top left corner's X coordinate is $FF, it will be rendered first, and by the time the rest is supposed to be rendered, it's already too late. The rest of the sprite will not be rendered. The clipping is not present because of the fact that it keeps track of the sprites top left corner. If it were the top right corner, and they were rendered right to left, the clipping would be for the right side, and not the left.

EDIT:
Bregalad wrote:
Celuis wrote:
I'd use sprite clipping, because the sudden disappearing is ugly.

I remeber you said me the other way arround one time. You said this was unnoticeable.


Well, I guess I changed my mind. It doesn't look good.

by on (#29473)
Celius wrote:
This is because wrap arounds don't occur.

Wrap around does occur from $FF to $00, just not from $00 to $FF like it should.

by on (#29474)
I get the feeling that we're talking about two different things. Are you asking why the sprite suddenly dissapears as it begins to move off the left side of the screen, and it doesn't when it begins to move off the right?

by on (#29482)
Yes, but you're right, I recalled the behavior quite incorrectly. Much simpler than I thought, when $00 underflows, the sprite is at $FF which is valid, rest is clipped.

by on (#29495)
What just happens is that there is NEVER negative coordinate on a sprite pos on the NES (at least not in hardware) so you cannot display part a sprite from the top or the left of the screen without tricks.
And yes OAM has to always be cleaned with any value between $f0-$ff for sprites you don't use, $00 isn't what you want. "Cleaning" the OAM with $00 will have the effect to supperpose 64 sprites using tile $00 and palette 0 at position 0,0. This is bad.

by on (#29497)
I need it to be $00 for the correct palette since I always update $0, $1 and $3 but never $2 of sprites. I know that all the excess sprites may appear at 0,0 depending on how the RAM is starts up, it doesn't matter. This is just a hacky program I will probably delete tomorrow, you guys are being way too serious about the small stuff that I already said doesn't matter and I'm very aware of.

I now know that there isn't anything logically wrong with my program and that the clipping is just a quirk of the PPU. Nintendo could have done better but they didn't, and it's not something I can change by thinking about it, so I'll just have to move my square to the background and scroll that, no big deal.