## Nametable scrolling

Discuss emulation of the Nintendo Entertainment System and Famicom.

Moderator: Moderators

yaazz
Posts: 24
Joined: Sat Oct 10, 2009 9:18 am

### Nametable scrolling

Would someone be able to explain to me how the nametables scroll?
For instance, scrolling between nametable 0 and nametable 1.

I am reading http://spark.ofloo.net/~qeed/nes_emu.txt and it makes sense how the PPU draws a tile, but how does it know to scroll?

qeed
Posts: 61
Joined: Tue Jun 17, 2008 11:51 am
Look at the ppu.reg[6] increments

Code: Select all

``````if ((ppu.reg[6] & 0x1F) == 0x1F)
ppu.reg[6] ^= 0x41F;
else
++ppu.reg[6];
``````
The 0x1F represents the scanline of tiles, when it hits 32 tiles, the bit is switched thats the name table switch
From loopy's docs
2000 write:
t:xxxxABxxxxxxxxxx=d:xxxxxxAB
0x41F in binary is 0b10000011111 so it switches the name tables.

and then this

Code: Select all

``````						if ((ppu.reg[6] & 0x7000) == 0x7000)
{
tmp = ppu.reg[6] & 0x3E0;
//reset tile y offset 12 - 14 in addr
ppu.reg[6] &= 0xFFF;
switch (tmp)
{
//29, flip bit 11
case 0x3A0:
ppu.reg[6] ^= 0xBA0;
break;
case 0x3E0: //31, back to 0
ppu.reg[6] ^= 0x3E0;
break;
default: //inc y scroll if not reached
ppu.reg[6] += 0x20;
}
}
else //inc fine y
ppu.reg[6] += 0x1000;
``````
when used in conjunction it takes care of name table scrolling. The second one is the fine y, as it goes to 29 which is really 30 since we start from 0, it means that we used up the 32x30 tiles of the name table and so it flips the other bit of name table.

yaazz
Posts: 24
Joined: Sat Oct 10, 2009 9:18 am
The second one is the fine y

What is "the second one" referring to

also, i assume from your name and the website that you wrote that document? It's been a big help I appreciate it

qeed
Posts: 61
Joined: Tue Jun 17, 2008 11:51 am
The upper bit of the name table, since the name table is 2 bit wide. (0 = \$2000; 1 = \$2400; 2 = \$2800; 3 = \$2C00) and thanks, I really should put my name in there people always asked me if i wrote that. If you have any suggestions on how to improve it, please tell me. Though the doc was just alot of compilation from pre existing info, I did make sure to give credit to who told me those things.

yaazz
Posts: 24
Joined: Sat Oct 10, 2009 9:18 am
Hmm still not getting it
I am not quite sure what is meant by fineY and fineX

What is the point of this if statement?
((ppu.reg[6] & 0x7000) == 0x7000)

qeed
Posts: 61
Joined: Tue Jun 17, 2008 11:51 am
look at the else following the if statement. It has to do with how the tiles are stored in the NES. Each Tile are 8x8 in the NES, when you are decoding the tiles so when the NES is getting the pattern address to get the tiles to decode it basically does this:

pat_addr = (tile << 4) | (ppu.reg[6] >> 12) |

the ppu.reg[6] >> 12 represents the Y Scroll, so it goes from 0 to 0x7000 each representing 0..7 because
0x0000 >> 12 = 0
0x1000 >> 12 = 1
0x2000 >> 12 = 2

and so on. Since the NES tile are 8x8 (meaning 8 in height) that statement if the NES has finished reading. Let me give you an example:

Tile = tile = ppu.p_nt[(ppu.reg[6] & 0xC00) >> 10]\
[ppu.reg[6] & 0x3FF];

pat_addr = (tile << 4) | (ppu.reg[6] >> 12) |

Let say the tile is 0 and the background address written through \$2001 is 0, then it means that the NES will get the CHR data from address 0,1,2,3... so on till 7. If the tile is 1 then 17,18,19,20...so on again in a multiple of 8, because the each NES tiles represent 8x8 pixels. So the if statement tests if the Y scroll has reached 8, if it did, then it knows its time to move on to the 8 in height location for the new tiles. If not, then it increments the ppu.reg[6] to allow the tiles to get the second scanline of tiles, the third scanline of tiles... so on until 8 scanlines are reached. Since we are going from 0, that is why it ended at 0x7000, for y scroll to represent 8 total scanlines. The tile is shifted by 4 because the NES represents the lower 2 bit of CHR data using bitplanes. So Byte 0 stores the lower bits of the CHR data, and Byte 7 store the upper 1 bit and so on..making it a total of 2 bit for each pixel. The other 2 upper bits is gotten through the attribute table.

yaazz
Posts: 24
Joined: Sat Oct 10, 2009 9:18 am
so when ppu_cycles = 0, where are we supposed to start fetching the first tile at?

Also, how does 0x2005 come into play

yaazz
Posts: 24
Joined: Sat Oct 10, 2009 9:18 am
NVM I figured that out, you have a variable called render_addr to handle that

Can you tell me what the ATTR_Shift variable is supposed to do?

qeed
Posts: 61
Joined: Tue Jun 17, 2008 11:51 am
the attribute shift variable is supposed to shift the attribute by the correct amount so it can properly get the attribute bits for a certain bits. Hmm, it seems I haven't clarified much in my writing, thanks for asking these questions. I'll work on a non-code explanation of the logic in the doc, since that seems to help you more.

yaazz
Posts: 24
Joined: Sat Oct 10, 2009 9:18 am
Can you explain what the difference between Render_Addr and Reg 2006 is in your code? You seem to use them interchangeably

qeed
Posts: 61
Joined: Tue Jun 17, 2008 11:51 am
render_addr is just a variable that shows what the NES is reading at the moment. I followed the idea for nintendulator to show what the NES is reading. For actual logic and such, you don't really need render_addr. You just need to make the proper decoding. (Though I am not exactly sure if that is really what the NES is reading in hardware). I just set it to whatever that was read last.

yaazz
Posts: 24
Joined: Sat Oct 10, 2009 9:18 am
ok makes sense I think
Can you explain this a little better
if ((ppu.reg[6] & 0x7000) == 0x7000)
{
tmp = ppu.reg[6] & 0x3E0;
//reset tile y offset 12 - 14 in addr
ppu.reg[6] &= 0xFFF;
switch (tmp)
{
//29, flip bit 11
case 0x3A0:
ppu.reg[6] ^= 0xBA0;
break;

case 0x3E0: //31, back to 0
ppu.reg[6] ^= 0x3E0;
break;

default: //inc y scroll if not reached
ppu.reg[6] += 0x20;

}
}
else //inc fine y
ppu.reg[6] += 0x1000;

I understand the Y Scroll but what is the point of the Y Offset?

qeed
Posts: 61
Joined: Tue Jun 17, 2008 11:51 am

Code: Select all

``````ppu.reg[6] += 0x20;
``````
Remember that the tile is gotten by doing

Code: Select all

``````tile = ppu.p_nt[(ppu.reg[6] & 0xC00) >> 10]\
[ppu.reg[6] & 0x3FF];
``````
So basically the ppu.reg[6] += 0x20 is done at the end of the scanline to move to the next 32 batch of tiles. Since 32 tiles represents 256 pixels which is one scanline.

So to sum up the Y Scroll ( ppu.reg[6] += 0x20; )
increment the pointer so it can fetch the tiles for the next 8 scanline since one tile handles up to 8 scanlines.. It increments by 0x20 (32) every time because as the scanline is progressing, the X scroll is going up by 1 up until 32 and then resets, so it needs some more bits to get the other tiles, which is basically what the Y Scroll is. It keep track of what scanline you are on. The Y Offset (Fine Y)

Code: Select all

``````else //inc fine y
ppu.reg[6] += 0x1000;
``````

What this does is part of the tiles in the scanline. Since the tiles are 8x8 pixels EACH, and

Code: Select all

``````pat_addr = (tile << 4) | (ppu.reg[6] >> 12) |
``````
You scan see from there that the tiles can only go up in multiples of 16. So basically what the Y Offset is is it allows the PPU to access it in smaller increments. Think of it as

tile * 16 + y_offset where y_offset is incremented up until 8 (Which is 8 pixels high, the size of ONE tile)

Hopefully that helps, if not, I am out of ways of explaining it
Last edited by qeed on Wed Nov 11, 2009 4:14 pm, edited 1 time in total.

yaazz
Posts: 24
Joined: Sat Oct 10, 2009 9:18 am
omg i cant belive i didnt figure that out myself. Each tile is 8*8 so it gets drawn 8 times.... Duh

thanks for the help im gonn have video running tonight i know it

yaazz
Posts: 24
Joined: Sat Oct 10, 2009 9:18 am
Turns out one day was gonna be a few days, forgot about a few assignments I had to do

Anyways, I got some output now!
No Pallette or Attributes yet, just Pattern Table data.

I just thought I would post up this lol worthy screenshot.
Anyone know the reason why the Super mario bros logo isnt showing up?