SNES Beginner Questions

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
SNES Beginner Questions
by on (#161904)
I am making a new year's resolution to learn programming for the SNES! I am a fairly experienced web dev who's also done a fair bit of spare-time hardware projects, so low-level stuff isn't completely foreign to me. However, I'm going to have many, many questions as I go, and it's considerably less clutter to ask all my questions in a single thread. So with that, I've been going through bazz's tutorials (very helpful) but there's a few things I don't yet understand.

Yoshi's doc describes $2118 as the video port data register, what is written to the location set in the address register $2116. However, what is the purpose of register $2119? Assuming these registers are word-size, does this simply allow me to write two words to VRAM?
Re: SNES Beginner Questions
by on (#161905)
$2118 and $2119 are each one byte, together they make up the one "word" VRAM data port. One 16-bit STA to $2118 writes the lo byte to $2118 and the hi to $2119. Depending on how you have $2115 set up, this may or may not be what you want.

Happy to answer any random questions you may have!
Re: SNES Beginner Questions
by on (#161907)
Khaz wrote:
$2118 and $2119 are each one byte, together they make up the one "word" VRAM data port. One 16-bit STA to $2118 writes the lo byte to $2118 and the hi to $2119. Depending on how you have $2115 set up, this may or may not be what you want.

Happy to answer any random questions you may have!


Ah, the registers are a byte each. I thought they were a word each; makes much more sense. I presume the same is for $2116 when setting the address register? Thanks!
Re: SNES Beginner Questions
by on (#161918)
Yeah, the SNES is a funny kind of 16-bit system. The CPU data bus access is 8-bit (backward compatibility with 6502, you see), and as a result all addresses reference a byte, except in VRAM which goes by words. So setting $2116-17 to $3000 targets a byte offset of $6000 in VRAM, but setting $2181-83 to $003000 just targets an offset of $003000 in WRAM (also available at $7E3000 on the A bus).
Re: SNES Beginner Questions
by on (#161987)
So, it appears I've run into another thing that looks off. I'm currently in the NMI-VBLANK portion of bazz's tutorials. I've used this code to set my tilemap to $0400:

Code:
    lda #$04
    sta $2107


This translates to 0000 0100, and according to the documentation for register $2107, this is word "1" of the Tile Map, beginning at $0400 in VRAM.

bsnes, however, appears to show a VRAM location of $0800 for my tilemap. To illustrate:

Image

Note the tilemap data located at $0800 according to the VRAM debugger. Yet, in my code, I consistently use $0400 as a VRAM write address, and it works just as if it was located at $0400 (the palette is swapped on the tile, as you can see the high byte in the debugger).

Am I missing some reason this is at $0800 in the debugger instead of the expected $0400?
Re: SNES Beginner Questions
by on (#161988)
The word address is $0400. Writes to VMADD ($2116) set an address in 2-byte words.

The byte address is $0800. The memory viewer is presumably showing byte addresses. If it were instead showing word addresses for consistency with VMADD, the numbers down the left side would look like this:

Code:
0200
0208
0210
0218
...
03F8
0400
0408 (cut off)


You might want to contact support to learn the rationale behind showing byte addresses in the video memory viewer.
Re: SNES Beginner Questions
by on (#162208)
Ah, makes sense. The memory map is even 16 bytes across which should've been a dead giveaway.

As far as writing to the tilemap goes, I've noticed a quirk. I'm testing a few subroutines that would write to the BG tilemap, and the tilemap doesn't seem to accept any writes when register $2100 is set to enable the screen. I assume all tilemap writes must be done when the screen is set to off? As far as games' technical design go, does this mean that normally you would write most of your tilemap "upfront", or is normal design to simply enable/disable the screen whenever a write is done and write your background in "segments" (e.g. scrolling past what would fit in the tilemap)? Does it normally go in vblank instead? I would imagine enabling/disabling causes flicker or other performance issues...just making sure before I get stuck in a bad practice.
Re: SNES Beginner Questions
by on (#162212)
You can't interact with VRAM while the screen is being drawn. That leaves you two options:

1. Turn the screen off with $2100
2. Leave screen on and wait until vBlank

Depending on what you're trying to do you may need to do #1 for a moment, but generally speaking all your normal VRAM writing is done during vblank.
Re: SNES Beginner Questions
by on (#162224)
The time you'd want to turn off the screen is when you'd want to send more tile data than you can during vblank. You can only send enough tile data for about 3 64x64 sized sprites in one frame, so that's why you may need it.
Re: SNES Beginner Questions
by on (#162271)
Espozo wrote:
The time you'd want to turn off the screen is when you'd want to send more tile data than you can during vblank. You can only send enough tile data for about 3 64x64 sized sprites in one frame, so that's why you may need it.


I have the handy-dandy chart from the SNES wiki showing me how many cycles each instruction uses; generally, how many cycles do I have on an NTSC console during Vblank?
Re: SNES Beginner Questions
by on (#162272)
I haven't tested how long "post-render" is on the Super NES. (On the NES, it's time between the end of picture and the vblank NMI. But "pre-render" appears to be one line given the way scrolling works (where the top line is hidden). Assuming that pre-render and post-render are both 1 line, this means that in 224-line mode on an NTSC system, vblank is 262 - 1 (pre-render) - 224 (picture) - 1 (post-render) = 36 scanlines long. Each scanline is 341 dots long, minus ten for DRAM refresh, and DMA copy takes two dots per byte. So (341 - 10) * 36 / 2 = 5958 bytes. Compare this to a 128x16 pixel strip of sprite cels, which is 1024 bytes, and you'll end up being able to send about five such strips plus the 544 bytes of OAM.
Re: SNES Beginner Questions
by on (#162707)
Looks like I'm having an issue writing to the tilemap...

I noticed this issue after observing screen corruption while trying to create a font routine. To setup, I use PPU screenmode 2 and scroll the layer down enough to display the entirety of the tile. As a test, I have a vblank manually triggered, and then I write to BG1 tilemap address to display tiles onscreen. Prior to writing to the tilemap address, I set the address register to $0400 and begin writing tilemap indicies required to display text. When I display, say, three tiles...it works perfectly:

Image

I modified this simple routine to print ten of the same tile to the tilemap. Nothing else changes except the amount of tiles. I get this:

Image

Tilemap entry #4 is scrolled up by itself, and the tenth one is way off into the distance. Testing it with my font character set gave even stranger results: the third, fourth, and fifth entries would end up way down at the bottom, while the rest display normally. The memory in the tilemap looks fine:

Image

I reasoned there is some timing issue I'm missing...I placed this whole routine in the NMI vblank handler, but I get identical results. So not sure what I'm doing wrong or where my understanding of how the tilemap works is breaking down (I reasoned that the X,Y coordinates of a tile in a BG could be computed based on a presumption that the tilemap is a continuous array of word-size entries).

If you want to see the code responsible, you can check here.
Re: SNES Beginner Questions
by on (#162710)
ashterix wrote:
Looks like I'm having an issue writing to the tilemap...

I noticed this issue after observing screen corruption while trying to create a font routine. To setup, I use PPU screenmode 2 and scroll the layer down enough to display the entirety of the tile. As a test, I have a vblank manually triggered, and then I write to BG1 tilemap address to display tiles onscreen. Prior to writing to the tilemap address, I set the address register to $0400 and begin writing tilemap indicies required to display text. When I display, say, three tiles...it works perfectly:

Image

I modified this simple routine to print ten of the same tile to the tilemap. Nothing else changes except the amount of tiles. I get this:

Image

Tilemap entry #4 is scrolled up by itself, and the tenth one is way off into the distance. Testing it with my font character set gave even stranger results: the third, fourth, and fifth entries would end up way down at the bottom, while the rest display normally. The memory in the tilemap looks fine:

Image

I reasoned there is some timing issue I'm missing...I placed this whole routine in the NMI vblank handler, but I get identical results. So not sure what I'm doing wrong or where my understanding of how the tilemap works is breaking down (I reasoned that the X,Y coordinates of a tile in a BG could be computed based on a presumption that the tilemap is a continuous array of word-size entries).

If you want to see the code responsible, you can check here.


Why are you using mode 2? Mode 2 is an offset-per-tile mode, meaning each column of the screen has its own scroll values, fetched from a table in VRAM. This scroll offset table is controlled by BG3's registers. It looks like you've set BG3 to use the same RAM as BG1, meaning the same data is being interpreted as both the tilemap and the scroll offset table. If you just want to display a simple 4bpp tilemap, use mode 1.
Re: SNES Beginner Questions
by on (#162729)
Looks like I misunderstood the screen modes in that case. Working good now!

Image
Re: SNES Beginner Questions
by on (#167084)
ashterix wrote:
... I've been going through bazz's tutorials (very helpful) but there's a few things I don't yet understand.


Thanks for using my tutorials! I'm glad you found them very helpful, but I would like to add that they are on a public wiki, so if you realized anything that could be better, please feel free to make your own contribution by modifying the content!