new to snes environment, have some questions.

Discussion of hardware and software development for Super NES and Super Famicom. See the SNESdev wiki for more information.

Moderator: Moderators

Forum rules
  • For making cartridges of your Super NES games, see Reproduction.
infidelity
Posts: 490
Joined: Fri Mar 01, 2013 4:46 am

Re: new to snes environment, have some questions.

Post by infidelity »

Thanks! :-D yeah took me about 2 hours with all the conversions, my laptop gets a bit finicky, and different values would pop up when I was typing, so I just really took my time with them. :-)

I just inserted a working joy pad routine, and can see all the buttons functioning within the RAM. :-)

So my next thing I'm trying to get going, is getting sprites to be displayed. I'm trying to manually insert them via the memory viewer for the OAM, but nothing is happening. I'm assuming I'm missing a register to enable or something?

Having a blast with this thus far! :-)
kuja killer
Posts: 130
Joined: Mon May 25, 2009 2:20 pm

Re: new to snes environment, have some questions.

Post by kuja killer »

Im really late to the topic :( but earlier you said something about no way to see RGB values for a nes palette.
and i wanted to show that the Nestopia emulator lets you see those 0 to 255 numbers on any individual color like this:

Image
infidelity
Posts: 490
Joined: Fri Mar 01, 2013 4:46 am

Re: new to snes environment, have some questions.

Post by infidelity »

I must've overlooked that with Nestopia! That's what I was trying to find, thanks for showing that! I was able to convert the default fceux.pal file to snes values, now to just get sprites to display on screen.

And how come I can't get my imgur uploads to display within the thread? I'm using the [ /img ] brackets, so weird...

Anyway, back to figuring out the sprites.
User avatar
koitsu
Posts: 4201
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: new to snes environment, have some questions.

Post by koitsu »

For sprites, there's a tutorial on that suited for people who are familiar with NES sprites and are trying SNES: https://wiki.superfamicom.org/how-to-display-sprites

More specific details about the OAM format and general use, including some MMIO registers for OAM access and "when" it's OK to update OAM: https://wiki.superfamicom.org/sprites

And don't forget about general purpose DMA for all of this (palette, OAM, etc.).

In general:

You write to $2101 to select the CHR data address in PPU RAM for the sprites, and the sprite sizes (bits 7-5). Bits 4-0 for the former are a bit confusing and too much to cover here. Bit 2 should always be 0, by the way.

You write to $2102 and $2103 (or just a 16-bit write to $2102) to define where in OAM memory you want to write to (via $2104). Bits 8-0 define the address, and bit 15 has to do with OAM priority shifting. Bits 9-14 are unused/set them to 0.

$2104 is write-only an 8-bit double-write register (i.e. it's like $2122 but for OAM).

$2102 through $2014 can only be written do during VBlank or forced blank; so your NMI handler should do your OAM updates.

$2138 is identical to $2104, but for reading OAM data.

Which graphics mode you're in also plays a role with regards to sprite visibility. There's 2 bits (thus range 0-3) in the OAM data that control if the sprite in question should be displayed in front of or behind some backgrounds (and which BG number matters). This is a complicated topic too because of how many "layers" there are, and gets even more complicated when you start getting into main vs. sub screens or "windowing", as well as interlaced modes.

I've attached a screenshot from the official manual which depicts background vs. sprite ordering, as well as register descriptions and PPU RAM layout. I'm pretty sure there are better documents describing the BG/OBJ layering, though. And the PPU RAM layout document will probably confuse the hell out of you -- think back to earlier in this thread about the term "word", because it's used heavily.

Oh, and I'll include a couple pages about $213e too -- specifically bits 7 and 6 ("time over" and "range over"). As someone who did NES stuff, you'll probably understand them, re: too many sprites per scanline.

Edit: forgot to mention: like the NES, sprites with lower index numbers have higher priority (will be shown atop) than those with higher index numbers

(2018/08/29 Edit: attachments removed.)
Last edited by koitsu on Wed Aug 29, 2018 6:45 pm, edited 2 times in total.
User avatar
koitsu
Posts: 4201
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: new to snes environment, have some questions.

Post by koitsu »

(2018/08/29 Edit: attachments removed.)
Last edited by koitsu on Wed Aug 29, 2018 6:45 pm, edited 1 time in total.
User avatar
koitsu
Posts: 4201
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: new to snes environment, have some questions.

Post by koitsu »

(2018/08/29 Edit: attachments removed.)
Last edited by koitsu on Wed Aug 29, 2018 6:45 pm, edited 1 time in total.
93143
Posts: 1717
Joined: Fri Jul 04, 2014 9:31 pm

Re: new to snes environment, have some questions.

Post by 93143 »

$2102 through $2014 can only be written do during VBlank or forced blank
EDIT/DELETE: You mean $2102 through $2104, right? I wrote a whole post based on the premise that you meant $2114, but I suspect said premise was faulty, so I've removed it...
tepples
Posts: 22708
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: new to snes environment, have some questions.

Post by tepples »

In my own words:

OBJ CHR RAM, or sprite tile memory

OBJ CHR RAM is in VRAM. It consists of two windows, each 256 tiles or 4K words or 8 KiB in size. At any given moment, the sprite unit can see 512 tiles: 256 from the first window and 256 from the second.

PPU port $2101 contains four bits that control the starting position of these windows. The first begins at an 8K word boundary: $0000, $2000, $4000, or $6000. The second is 4K, 8K, 12K, or 16K words ($1000, $2000, $3000, or $4000) higher, with wraparound from $7FFF to $0000. Thus there are 16 possibilities:

$0000 and $1000, $2000, $3000, or $4000
$2000 and $3000, $4000, $5000, or $6000
$4000 and $5000, $6000, $7000, or $0000
$6000 and $7000, $0000, $1000, or $2000

For example, when these are set to $4000 and $6000, OBJ tiles 0-255 come from $4000-$4FFF, and OBJ tiles 256-511 come from $6000-$6FFF.

With careful programming, it should be possible to address 768 tiles, some of which appear only in the top of the screen, some in the bottom, and some throughout. You might store tiles used throughout in $4000-$4FFF, tiles used on top in $5000-$5FFF, and tiles used on bottom in $6000-$6FFF. Then set $2101 during vertical blanking to use $4000 and $5000, and set an HVIRQ or HDMA to change $2101 to use $4000 and $6000 part-way down the screen.

Tiles in both OBJ CHR data and 4bpp BG CHR data are 16 words apart. Slivers (8x1-pixel rows) within a tile are 1 word apart, and pairs of bit planes within a sliver are 8 words apart. Thus row Y (0-7) of a given tile whose base address is X ($0000-$7FF0) has its four bit planes arranged as follows:

0: Word X+Y, low byte
1: Word X+Y, high byte
2: Word X+Y+8, low byte
3: Word X+Y+8, high byte

The horizontal position of a sprite is a 9-bit two's complement signed quantity: 0-255 if the corresponding X bit in high OAM is 0, or -256 to -1 if the X bit in high OAM is true. There is a hardware bug where a sprite at X=-256 still counts toward the limit of 32 sprites on a scanline, just as if it were at X=0. It's better to fully hide such sprites by parking them at Y=225, just below the visible portion of the screen (usually Y=1 to 224).

A sprite larger than 8x8 pixels is displayed using consecutive tile numbers (horizontally) and tile numbers 16 apart (vertically).

Layer priority

Layer priority from front to back in modes 0 and 1:

BG3 tiles with tilemap priority 1 (if in mode 1 with HUD in front enabled)
Priority 3 sprites
BG1 tiles with tilemap priority 1
BG2 tiles with tilemap priority 1
Priority 2 sprites
BG1 tiles with tilemap priority 0
BG2 tiles with tilemap priority 0
Priority 1 sprites
BG3 tiles with tilemap priority 1
BG4 tiles with tilemap priority 1
Priority 0 sprites
BG3 tiles with tilemap priority 0
BG4 tiles with tilemap priority 0
Backdrop

Layer priority from front to back in modes 2 through 7:

Priority 3 sprites
BG1 tiles with tilemap priority 1
Priority 2 sprites
BG2 tiles with tilemap priority 1
Priority 1 sprites
BG1 tiles with tilemap priority 0
Priority 0 sprites
BG2 tiles with tilemap priority 0
Backdrop

The background priority stack is split into two half-stacks. In modes 0 and 1, BG1 and BG2 are in the top half-stack, and BG3 and BG4 are in the bottom (except with HUD in front in mode 1). In particular, BG3 cannot normally be placed between BG1 and BG2, though this can be faked with windowing or color math. In modes 2 through 6, tilemap priority directly chooses a half-stack. Mode 7 has tilemap priority 0.

Priority between sprites is determined before comparing them against backgrounds. This means that if a sprite with a lower OAM index and a lower priority value overlaps a sprite with a higher OAM index and higher priority, the low-index sprite is visible in the overlap area. This can cause the low-index sprite to "cut out" portions the high-index sprite, letting the background show through.

Capacity of the sprite unit

For each scanline, the S-PPU considers up to 32 lowest-index sprites overlapping the line, not counting sprites in X=-255 through X=-width. (Due to a hardware bug, sprites with X=-256 are counted.) Among these 32, called the in-range set, up to 34 slivers are displayed, not counting slivers to the left or right of the screen. There is a hardware bug in the overflow behavior when the 32 sprites have more than 34 slivers. The slivers chosen are those of the highest-index sprites among the in-range set. This causes a visible difference if at least one sprite is 32x32 pixels or larger, or if at least three sprites are 16x16 pixels or larger.

If more than 32 sprites or 34 slivers are in range on a given line, the S-PPU sets flags in port $213E for the remainder of that frame and clears them to 0 at the end of vertical blanking. It sets bit 6 for too many sprites and bit 7 for too many slivers.

Your software can use these flags to trigger reshuffling of sprites within OAM, so that excess sprites flicker instead of dropping out entirely. If your software fills OAM from front to back each frame, this can be done by drawing game objects in a pseudorandom order. Or if sprite-to-sprite priority is not important for your software, you can use hardware-accelerated reshuffling. Set bit 7 to $2103 to true, and during vertical blanking, write an OAM index to bits 7-1 of $2102 that varies from one frame to the next. This moves sprites X through 127 to the front of the sprite stack and sprites 0 through X-1 to the back, as if their OAM index were 128 through X+127.
93143
Posts: 1717
Joined: Fri Jul 04, 2014 9:31 pm

Re: new to snes environment, have some questions.

Post by 93143 »

tepples wrote:Then set $2101 during vertical blanking to use $4000 and $5000, and set an HVIRQ or HDMA to change $2101 to use $4000 and $6000 part-way down the screen.
HDMA is not recommended for this. Sprite graphics are pulled from VRAM during HBlank, and changing the OBJ data offsets with HDMA would interrupt this process, likely resulting in glitching. I believe this may be what's occurring in First Try by Magical, which I have confirmed uses HDMA for this (look at the right hand side of the screen, about halfway down, once the mountain shows up - after a few seconds there's a flickery bit).

I've tried using an HV-IRQ to ensure that the write to $2101 happens mid-scanline, while the PPU is scanning OAM, so that when it goes to look for the actual graphics after the line is finished, it picks up all of them from the new VRAM locations. This seems to work without glitches on real hardware, even with a fully-loaded 34-tile line.

I haven't thoroughly exercised this trick myself yet, but supposedly there's at least one commercial game that changes $2101 mid-screen, so I hope my results aren't a fluke.
It's better to fully hide such sprites by parking them at Y=225, just below the visible portion of the screen (usually Y=1 to 224).
Unless you're using 64x64 sprites (which you usually won't be) on NTSC (EDIT: or 32x32 on PAL), in which case VBlank the gap between the last displayed line and the maximum Y coordinate is too short to hide the whole sprite and it will wrap around to the top of the screen. Generally hiding sprites with Y is better, but watch out for this edge case.
Last edited by 93143 on Sun Apr 15, 2018 9:04 pm, edited 3 times in total.
infidelity
Posts: 490
Joined: Fri Mar 01, 2013 4:46 am

Re: new to snes environment, have some questions.

Post by infidelity »

Wow, I cannot thank you all for the abundance of information with the OAM!

However I'm having an issue. For some reason I cannot move any of the 128 sprites? Currently I set all of the Y positions to E0. But with doing that, I still have a random sprite at the upper left of the screen? I cannot seem to move them?

When I have the Sprite Viewer open, I can see the 128 sprites, i'm able to edit all of the 4 bytes for those 128 sprites, and see the changes. But nothing appears on screen?

I have $212C set to enable sprites, and BG 1 enabled. And for $2101, I do LDA #$01 STA $2101.

Now, all these tests are all within my reset vector, i'm not trying to run any type of program, everything is me testing things with the system. So right now I don't have anything being written & stored into the OAM, I'm simply editing them through the Memory Viewer on the fly.

What am I missing, doing wrong? Thank you everyone!

Image
User avatar
koitsu
Posts: 4201
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: new to snes environment, have some questions.

Post by koitsu »

Maybe I'm misunderstanding you, but:

I haven't used this feature of the debugger, but my guess is that if you change sprite details in the Sprite Viewer, you won'tsee changes in real-time, only after the next VBlank completes. That's how the console works.

It could also be a quirk/bug in the debugger. The debugger has had a long history of this type of stuff (I say that without negative judgement); there are always edge cases that have to be looked at. "Wait, do we want this actually be reflected visually on-screen immediately or only when the console actually reads from OAM + renders sprites?" What the behaviour *should* be vs. what the user *wants* are often two different things. For stuff like this, filing a GitHub Issues to ask devinacker is usually the best choice of action; he's super cool and nice.

The "Auto-update" checkbox means to automatically update the contents of the Sprite Viewer window when underlying 65816 code issues changes to OAM (and probably PPU RAM data for CHR).

In short: you need to start writing code.
Last edited by koitsu on Sun Apr 15, 2018 3:05 pm, edited 1 time in total.
tepples
Posts: 22708
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: new to snes environment, have some questions.

Post by tepples »

93143 wrote:
It's better to fully hide such sprites by parking them at Y=225, just below the visible portion of the screen (usually Y=1 to 224).
Unless you're using 64x64 sprites (which you usually won't be) on NTSC, in which case VBlank is too short to hide the whole sprite
All sprite size combinations including 64x64 also have 32x32 or smaller, and a 32x32 sprite at Y=225 occupies lines 225-255 (invisible) and 0 (also invisible). The wraparound problem with 32x32 and 64x64 mode comes not on NTSC but on PAL, where 239-line mode is expected. But again, you usually won't be using 64x64 anyway.
93143
Posts: 1717
Joined: Fri Jul 04, 2014 9:31 pm

Re: new to snes environment, have some questions.

Post by 93143 »

Right. I had confused myself slightly.

Still, the fact that you can make 64x64 sprites fit on NTSC by downsizing them doesn't really address the issue, because (a) you have to remember to do that, which is really the only reason anybody mentions the sprite-wrapping issue at all as it's easy to work around, and (b) if you have to change both the main coordinate and the high-table data, you might just as well use X.
infidelity
Posts: 490
Joined: Fri Mar 01, 2013 4:46 am

Re: new to snes environment, have some questions.

Post by infidelity »

Ok, I think I got the sprites working! I have $800 in ram, as the region that loads the sprites that are stored into OAM.

But I'm facing another weird issue. I was able to select the base in vram where the 1st OAM set of sprite tiles is located. I forget the address, but I have it set to 01. Which makes the first oam address in vram to $4000, and the 2nd OAM address $6000. My sprite tiles are at $4000.

So all of that was working perfectly. But I decided to shift code out of the reset vector, (again all I've been doing is testing code to see results, I'm not building a game or demo yet) and have it run within the NMI instead.

Now, I set $4200 I think, (I'm at work) with 80, to enable an NMI, I have that code at the end of my reset vector, then I have an infinite jmp.

The NMI address kicks on, and within that I have my sprite OAM being loaded and stored, and a functional joypad routine. I do not know if I'm supposed to have all the pushes and pulls of the stack like the NES does at the beginning and end of its NMI, I don't have that.

So what happens is, my OAM base for vram is only pointing at $0000 & $2000? I even alter the bits for the base addresses manually while the code is being ran, but the addresses do not change when I view the vram viewer?

Any ideas?

Thanks everyone! :-)
tepples
Posts: 22708
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: new to snes environment, have some questions.

Post by tepples »

Yes, you need the pushes. In fact, you may need even more pushes than on NES because of more registers: B for data bank and D for frame pointer/direct page being the big ones.
Post Reply