Scrolling backgrounds - Falmouth-Games-Academy/comp310-wiki GitHub Wiki
Before reading up on scrolling backgrounds one should first become familiar with how backgrounds work using the Picture Processing Unit here Backgrounds. PPU scrolling is the method of moving the displayed portion of the screen in order to display a much larger area than the standard 256x240 pixel screen. If you look at games like Super Mario Bros and similar platformer games you can see they are many screens wide. A tutorial on how to program scrolling backgrounds can be found here [3].
Normally, a program writes to two PPU registers to set the scroll position in its NMI (Non-maskable interrupt) handler. The first thing you do is find the X and Y coordinates of the upper left corner of the visible area that will be seen by the camera. After that you write the X coordinate to PPUSSCROLL ($2005), then you write the Y coordinate to the PPUSCROLL. After you have defined the the coordinates you then write the starting page (high order bit of X and Y) to bits 0 and 1 of PPUCTRL ($2000) [1].
The scroll position written to PPUSCROLL is applied at the end of vertical blanking, which happens just before rendering begins, therefore these writes need to occur before the end of the vblank [1] [4]. Also, because of writes to the PPUADDR ($2006) register, this can overwrite the scroll position. The two writes to PPUSCROLL need to happed after any updates to VRAM using PPUADDR [1].
if ((v & 0x001F) == 31) // if coarse X == 31
v &= ~0x001F // coarse X = 0
v ^= 0x0400 // switch horizontal nametable
else
v += 1 // increment coarse X
if ((v & 0x7000) != 0x7000) // if fine Y < 7
v += 0x1000 // increment fine Y
else
v &= ~0x7000 // fine Y = 0
int y = (v & 0x03E0) >> 5 // let y = coarse Y
if (y == 29)
y = 0 // coarse Y = 0
v ^= 0x0800 // switch vertical nametable
else if (y == 31)
y = 0 // coarse Y = 0, nametable not switched
else<br/>
y += 1 // increment coarse Y
v = (v & ~0x03E0) | (y << 5) // put coarse Y back into v
Two common errors that can occur when implementing scrolling backgrounds are:
- A delay in the scroll being applied. This tends to happen when the non-maskable interrupt takes too long and so the PPUSCROLL is not set before the end of vblank. It is advised to stick to a maximum of 64 bytes per nametable per NMI. Otherwise too much data is being processed [1].
- Using PPUADDR before PPUSCROLL. As they use the same register they will overwrite each other. If PPUADDR is used after PPUSCROLL, you must then use PPUSCROLL again [1].
Vertical Blank (VBlank) is the period when the electron gun in CRT Televisions moves between the bottom right pixel of the screen to the top left after it has finished 'printing' the image to the screen [4]. As stated in source [1] and confirmed in source [4] any changes to the PPU's nametables and/or pallettes must be done during Vblank. Any exception to this are called raster effects [4], background scrolling is one such raster effect. The reason for this is to allow for things like HUD which must remain static while the background is scrolling.
In most single player games only one scroll is needed. This is relatively simple to do on the NES as you only need to write to register $2000 once and register $2005 twice per vblank to get a scrolling effect. The low two bits of $2000 select which of the four nametables to use. The first write to $2005 specifies the X scroll, in pixels. The second write to $2005 specifies the Y scroll, in pixels [1].
This works as the writing to $2005 uses a toggle to specify which write is taking place either the X or Y locations. If the status of the toggle is unknown then it can and should be reset by reading from $2002 before the first write to $2005 [1].
It is possible to do X/Y Split scrolling however it will require 4 writes to registers: Nametable number << 2 (that is: $00, $04, $08, or $0C) to $2006 Y to $2005 X to $2005 Low byte of nametable address to $2006, which is ((Y & $F8) << 2) | (X >> 3) [1].
The last two writes should occur during horizontal blanking to avoid any visual errors [1].