Questions about SNES programming

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.
User avatar
SusiKette
Posts: 147
Joined: Fri Mar 16, 2018 1:52 pm
Location: Finland

Questions about SNES programming

Post by SusiKette »

I was thinking of trying SNES programming, but there are a few things that I need to ask about before I can start.

The first thing is to understand how the SNES uses the available memory. As far as I know, RAM and registers are at $xx:0000 - $xx:7FFF and ROM is at $xx:8000 - $xx:FFFF. xx is the value at Program Bank or Data Bank register depending on whether its accessed by read/write instruction or instruction fetch. Do reads/writes at $0000 - $7FFF care what value xx is?

Now, PPU memory is a bit more of a mystery to me. How do you assign pattern tables and how many tiles can they hold at once? Can parts of the pattern table be swapped or are you limited to certain minimum sizes you can swap at once? Or can you swap individual tiles kinda like CHR RAM on some NES mappers? What about nametables and attributes?
Avatar is pixel art of Noah Prime from Astral Chain
lidnariq
Posts: 11432
Joined: Sun Apr 13, 2008 11:12 am

Re: Questions about SNES programming

Post by lidnariq »

SusiKette wrote:The first thing is to understand how the SNES uses the available memory.
There are multiple different ways that cartridges work. What you described is "Mode 20h", mostly, or "LoROM".

Divide the 24-bit address of the SNES's CPU into three eight-bit quantities. In order, call those the "bank", "page", and ... nothing.

The SNES itself only imposes a few limits:
  • 8K of RAM can be accessed in banks $00-$3F, $7E, and $80-$BF, across pages $00-$1F.
  • 128K of RAM can be accessed in banks $7E and $7F, across all pages, not redundant.
  • registers to configure how everything operates can be accessed in banks $00-$3F and $80-$BF, in pages $21 and $40-$43.
But "bank" is misleading: the SNES's CPU provides the ability to natively specify the full 24-bit address without using bankswitching.

The SNES hardware makes specific memory layouts easier. It provides a specific signal on the connection to the cartridge that lets the cart know when ("bank" is $40-$7D or $C0-$FF) or ("bank" is $00-$3F or $80-$BF and "page" is $80-$FF).
How do you assign pattern tables and how many tiles can they hold at once? Can parts of the pattern table be swapped or are you limited to certain minimum sizes you can swap at once? Or can you swap individual tiles kinda like CHR RAM on some NES mappers? What about nametables and attributes?
In the SNES, 64KB of RAM is available to hold everything to be drawn. Unlike the NES, this cannot be changed by the cartridge. Also unlike the NES, there's a wide variety of ways to interpret the data. There is no attribute table; nametable entries are always 16 bits wide. Up to four background layers plus a layer of sprites can be drawn, each from their own location in memory. Each background layer has its own nametable, each nametable can be any of the four combinations of 32 or 64 tiles wide and tall; each nametable entry can represent an 8x8 (or 16x8 in specific cases) or a 16x16 pixel region.

I'd suggest using some SNES emulator with a debugger—NO$SNS, bsnes-plus, or Mesen-S—and looking at what's going on.
User avatar
SusiKette
Posts: 147
Joined: Fri Mar 16, 2018 1:52 pm
Location: Finland

Re: Questions about SNES programming

Post by SusiKette »

lidnariq wrote:In the SNES, 64KB of RAM is available to hold everything to be drawn. Unlike the NES, this cannot be changed by the cartridge. Also unlike the NES, there's a wide variety of ways to interpret the data. There is no attribute table; nametable entries are always 16 bits wide. Up to four background layers plus a layer of sprites can be drawn, each from their own location in memory. Each background layer has its own nametable, each nametable can be any of the four combinations of 32 or 64 tiles wide and tall; each nametable entry can represent an 8x8 (or 16x8 in specific cases) or a 16x16 pixel region.
So in other words, the programmer defines where each data is located in VRAM? Still I don't know how you define sprite graphics location, or is it fixed?
Avatar is pixel art of Noah Prime from Astral Chain
User avatar
dougeff
Posts: 3079
Joined: Fri May 08, 2015 7:17 pm

Re: Questions about SNES programming

Post by dougeff »

Register 2101, Name Base Select is the last 3 bits... actually the last 2 bits, the 3rd one is unused.

So sprite graphics can be at $0000, $2000, $4000 or $6000 in the VRAM.
nesdoug.com -- blog/tutorial on programming for the NES
Pokun
Posts: 2681
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: Questions about SNES programming

Post by Pokun »

So in other words, the programmer defines where each data is located in VRAM? Still I don't know how you define sprite graphics location
Exactly! You set the base address in VRAM for each "SC" (tilemap nametable) for each background layer, the "BG CHR name" (pattern table for background characters) and the "OBJ CHR name" (pattern table for sprites) using hardware registers. For example the SC base addresses are set by writing the addresses to registers $2107-$210A. Depending what modes, formats and sizes you use, you have to plan and set up VRAM accordingly.

The Color Generator (palette) and OBJ attributes (sprite attributes) however have their own fixed memory and are not set in VRAM. The palette is set into CGRAM and sprite attributes are in OAM like for NES.
User avatar
koitsu
Posts: 4201
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: Questions about SNES programming

Post by koitsu »

In short: yes, you choose where things in PPU RAM end up. You get to manage/define the 64KByte of space yourself.

Be aware, however:

1. There are alignment requirements for each "type" of data:

a) Background nametable (officially called "BG-SC" or "SC" data) base address is 2KByte / 1KWord aligned; see MMIO registers $2107-210A
b) Background CHR (officially called "BG character data") base address is 8KByte / 4KWord aligned; see MMIO registers $210B-210C
c) Sprites/OAM are more complicated; there is an 16KByte / 8KWord alignment; see MMIO register $2101. Sprite alignment in PPU RAM is somewhat complicated; refer to other docs
d) Palette is stored independently elsewhere (i.e. not in PPU RAM), and specific MMIO registers are used to interface with it

All backgrounds/sprites/everything share the same PPU RAM, and with higher bit depths on the SNES than the NES, it can be very easy to run out of PPU RAM (considering alignments, etc.).

Mirroring/etc. is controlled via MMIO registers as well (commonly called "SC size"), with native support for 1x1, 1x2, 2x1, and 2x2 (i.e. 4-screen). Larger sizes require more PPU RAM, obviously.

2. How the data is used/accessed/utilised depends on video mode (MMIO register $2105) and background number. For example, mode 1 has 3 backgrounds, 2 of which are 4 bits per pixel, with the 3rd BG being 2bpp.

3. SNES documentation and MMIO registers often refer to things/offsets __in words__, not bytes. As such, you'll become extremely reliant on macros to do the byte-to-word conversions, and will often spend time debugging problems relating to this when using PPU RAM viewers and so on (some show things in "raw values the PPU has", others show things in bytes, some intermix both). Here are 4 ca65 macros I use, and some equates, with some examples, for mode 1 (I chose not to include details about BG3):

Code: Select all

.define ppuaddr(addr)             (addr / 2)
.define bgmap(addr, size)         ((((addr / 2) & $FC00) >> 8) | size)
.define bg12chr(bg1addr, bg2addr) (((bg2addr / 2) >> 8) | ((bg1addr / 2) >> 12))
.define bg34chr(bg3addr, bg4addr) (((bg4addr / 2) >> 8) | ((bg3addr / 2) >> 12))
SC_SIZE_32X32         = %00
SC_SIZE_64X32         = %01
SC_SIZE_32X64         = %10
SC_SIZE_64X64         = %11

bg1mapaddr  = $0000          ; $0000-0FFF: BG1 map, 2 horizontal screens
bg2mapaddr  = $1000          ; $1000-17FF: BG2 map, single screen
bg1chraddr  = $2000          ; $2000-xxxx: BG1 CHR data
bg2chraddr  = $6000          ; $6000-xxxx: BG2 CHR data

sep #$30    ; 8-bit A/X/Y
rep #$10    ; 16-bit X/Y

lda #bgmap(bg1mapaddr, SC_SIZE_64X32)
sta $2107
lda #bgmap(bg2mapaddr, SC_SIZE_32X32)
sta $2108

lda #bg12chr(bg1chraddr, bg2chraddr)
sta $210B

ldx #ppuaddr(bg1mapaddr)
stx $2116
...

ldx #ppuaddr(bg1chraddr)
stx $2116
...
The model used on the SNES is that since ROM can be so large, and native DMA transfers between CPU addressing space (e.g. ROM) and PPU are available, don't be afraid to use ROM space for all your stuff. You have a lot more breathing room that on the NES.
User avatar
SusiKette
Posts: 147
Joined: Fri Mar 16, 2018 1:52 pm
Location: Finland

Re: Questions about SNES programming

Post by SusiKette »

Okay, I think I now understand how the SNES memory space works. I also found a text document that has memory maps for both LoROM and HiROM. I need probably need to refer to it in the future.
Avatar is pixel art of Noah Prime from Astral Chain
lidnariq
Posts: 11432
Joined: Sun Apr 13, 2008 11:12 am

Re: Questions about SNES programming

Post by lidnariq »

I would strongly recommend using tepples's memory map diagrams because we've had problems with some of the other depictions of this being confusing, or wrong, or both.
93143
Posts: 1717
Joined: Fri Jul 04, 2014 9:31 pm

Re: Questions about SNES programming

Post by 93143 »

If I could recommend a few sources...

https://wiki.superfamicom.org/tags/reference

In particular,
https://wiki.superfamicom.org/memory-mapping
https://wiki.superfamicom.org/registers
https://wiki.superfamicom.org/backgrounds
https://wiki.superfamicom.org/sprites
https://wiki.superfamicom.org/dma-and-hdma
and
https://wiki.superfamicom.org/65816-reference
if you need it.

Lots of great information on there. A lot of it is based on anomie's docs, but there are some original contributions.

Also: https://problemkaputt.de/fullsnes.htm is nocash's doc, which is more recent than anomie's stuff and contains (IIRC) some info that isn't in the above. I find it harder to navigate, but maybe that's just me.

If you already know about these, great.
User avatar
SusiKette
Posts: 147
Joined: Fri Mar 16, 2018 1:52 pm
Location: Finland

Re: Questions about SNES programming

Post by SusiKette »

By the way, since portion of the RAM is called "Low RAM", does it differ in any way from the rest of the RAM available?
Avatar is pixel art of Noah Prime from Astral Chain
tepples
Posts: 22708
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Questions about SNES programming

Post by tepples »

No. Low RAM is just the 8K mirror of $7E0000-$7E1FFF that appears in banks $00-$3F and $80-$BF. It has the same "slow" (8 master clocks) speed as banks $7E and $7F.
lidnariq
Posts: 11432
Joined: Sun Apr 13, 2008 11:12 am

Re: Questions about SNES programming

Post by lidnariq »

It's available in multiple difference places because several instructions can only access things in bank 0, and many instructions can only access things within the same program ("K") or data ("B") bank.
User avatar
dougeff
Posts: 3079
Joined: Fri May 08, 2015 7:17 pm

Re: Questions about SNES programming

Post by dougeff »

Do you have to put the stack and direct page in the lowRAM?
nesdoug.com -- blog/tutorial on programming for the NES
tepples
Posts: 22708
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Questions about SNES programming

Post by tepples »

You can place them anywhere in bank $00. But on the Super NES, under the standard cartridge memory mapping, the most useful place for the stack is low RAM, and the most useful places for the direct page are low RAM and the DMA registers.
93143
Posts: 1717
Joined: Fri Jul 04, 2014 9:31 pm

Re: Questions about SNES programming

Post by 93143 »

And the PPU registers, depending on what you're doing. If you happen to need fast access to a data table in ROM, you can use direct page for that too, as long as the cart mapping puts the data you want in bank $00.

The great part about direct page is that it's fairly quick and painless to move it, so you don't have to settle on one setting and leave it there for the whole game. In fact you can use it almost as a third index register, which then provides the ability to do nested free indexing because most direct-page instructions have indexed variants. You just have to get in the habit of pushing and pulling D in interrupts.

But yeah, the stack should probably go in RAM...
Post Reply