09. Scrolling

Scrolling means moving the background around. It does not affect sprites.

The NES PPU has 1 scroll register, 2005. You write to it twice, first the X scroll, and then the Y scroll. This is another thing that needs to happen during v-blank, and is handled automatically by neslib. neslib has this function scroll(x,y), you pass it the shift amounts. Adding to X scroll, shifts the screen left. Adding to the Y scroll shifts the screen up.

But, I decided that I didn’t like the way it handled Y scrolling. Y scrolling is a bit odd anyway, since values 0-$ef are real positions, and $f0 – ff are treated as negative values, and not what you want. neslib subtracts $f0 if the Y value is > $ef, and assumes that you are going to manage the maximum at $1df.

So, long story short, I do things differently than everyone else using C. I made 2 functions called set_scroll_x(x) and set_scroll_y(y). You can pass the set_scroll_y any int value, and the high byte will tell you which nametable you are in. Even means top, odd means bottom. If you have 2 collision maps, you know even = use the first one, odd = use the second one. Simple. Well, not perfect.

Our code still has to adjust each change in Y to skip over the $f0-ff region of the byte, because our screen is only 240 pixels high. Luckily, I wrote some functions to do this for us.

add_scroll_y(add,old y) to add to the y scroll.

sub_scroll_y(add, old y) to subtract from the y scroll.

Each returns a value, which will have to be passed to set_scroll_y(), to change the screen scroll. But before we show that, lets talk about mirroring. Examples…

Old scroll_y = 0xef, add 5. Returns 0x104, new scroll_y

scroll_y = add_scroll_y(5, scroll_y);

Old scroll_y = 0xef, add 0x15. Returns 0x114, new scroll_y.

scroll_y = add_scroll_y(0x15, scroll_y);

— or subtract

Old scroll_y = 0x104, subtract 5. Returns 0xef, new scroll_y

scroll_y = sub_scroll_y(5, scroll_y);

Again, skipping over the 0xf0 – 0xff invalid Y scroll values.


Horizontal scrolling (Vertical mirroring)

Remember from the intro page, I said that the NES only has enough VRAM for 2 nametables. If you set it to Vertical mirroring — the mirroring is set in the ines header in crt0.s, which is actually a linker symbol “NES_MIRRORING” found in the .cfg file. On a real cartridge they would have soldered one of the connections to permanently set it to H or V mirroring.

So with vertical mirroring the nametables are arranged like this.

0 1

0 1

With nametables 2 and 3 being copies of 0 and 1.

This is good for sideways scrolling. If you never change the Y scroll, you can scroll to the right, and have plenty of blank space just off screen to make changes to the nametable that the user can’t see. Here’s some example code of Vertical mirroring. You can freely scroll around, and see that the lower 2 nametables are just a copy of the top 2.

The numbers on the screen are sprites.






Vertical scrolling (Horizontal mirroring)

is basically the same, except the right 2 nametables are copies of the left 2

0 0
2 2

This is good for vertical scrolling. You can keep the X scroll static, and scroll up or down, and you have plenty of room to make changes just off screen, above or below where the user can see.





There is also 4 screen mode, which almost zero games used. It required a special mapper with an extra RAM chip in the cartridge. Gauntlet for example, and Rad Racer II.

This would be good for all direction scrolling. Most games just used 2 screen, and had glitchy tiles at the edges. Old TVs tended to cut off the edges, so it usually wasn’t too bad. A modern TV / monitor, might show all pixels… PAL TVs do also. Let’s not worry about that now.


2 thoughts on “09. Scrolling

  1. I think there may be a bug in the nesdoug set_scroll_y. It seems to result in garbage being added to the top of the screen as the scroll amount nears the point of rolling back to 0.

    It can be seen even with the -1 case you use in your examples:


    I captured a short clip of it here (watch the top of the screen): https://imgur.com/a/MUAW965

    This does not happen with neslib “scroll()”.

    Liked by 1 person

    1. This is a known issue. The top line is actually the attribute table data being transmitted as tile data. I figured that 1 line of faulty graphics would be small enough to be ignored, and many actual NES games do the same thing, expecting that this would be hidden in the overscan region of the 1980s TVs.

      You could set_scroll_y() to 0x1ef to fix it (or using the scroll() function, y = 0x1df, since that manages scroll >= 0xf0 by subtracting 0xf0).

      Liked by 1 person

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s