Some questions that pop up every now and then across several community channels – whether it’s the Facebook group, the NESmakers forum or Discord – are about the banks a NESmaker project use. How many banks are there, what’s (by default) in which bank, and what is this bankswitching thing all about? Let me try and explain them.
Mappers
First, let me tell a bit about mappers. Every NES cartridge uses its own mapper. In a nutshell, a mapper is a memory configuration which tells the console which chips and features are available on the cartridge. Among other things, this is what allows bankswitching. The NES can hold a total of 32KB of program data (basically the game’s code) and 8KB of character data (the graphical background and sprite data). A cartridge can hold more data though, stored in 32KB banks. The chosen mapper defines how many banks there are. In code, you can then load the appropriate bank into NES memory. Some common mappers are:
- Mapper 0 (aka NROM, or “no mapper”), holds 32KB of program data and 8KB of character data. Banks are not swappable. Example game: Super Mario Bros.
- Mapper 1 (aka MMC1 or SxROM), allows bankswitching of the lower, upper or full program bank, and the character bank. Example game: the Legend of Zelda.
- Mapper 2 (aka UxROM) – allows bankswitching of the lower bank (there is no CHR swapping). Example game: Mega Man
- Mapper 4 (aka MMC3, among other names) – can use various PRG and CHR switching setups. Example games are Super Mario Bros. 2 and 3.
There are many more mappers, a full list of which can be found on the nesdev wiki.
NESmaker projects use Mapper 30, or UNROM-512. PCB boards that use this mapper was introduced by RetroUSB for the homebrew community, and since been replicated by InfiniteNesLives. A mapper 30 cartridge holds 32 banks with 16KB of program data each. These banks are mapped to the lower and upper bank of the NES console: the lower 16KB can be swapped, whereas the upper 16KB are fixed. I will explain the concept of bankswitching in more detail in a future Deep Dive post.
So, what’s in the banks?
As a NES developer, you’re pretty much free in what you’d like to put in which bank. NESmaker has a default structure as to what goes in which bank though. That’ll save beginning NESmaker users a lot of headache, as you won’t have to think about how to bankswitch where and when, without breaking stuff. I would suggest you go and take a look at the demo.txt file that’s generated in the GameEngineData folder when exporting your game, as the entire blueprint of the game’s code can be found there.
By default, the 32 banks are populated as follows:
Bank $00 | Overworld screen data* for screens (0,0) to (1,15) |
---|---|
Bank $01 | Overworld screen data, (2,0) to (3,15) |
Bank $02 | Overworld screen data, (4,0) to (5,15) |
Bank $03 | Overworld screen data, (6,0) to (7,15) |
Bank $04 | Overworld screen data, (8,0) to (9,15) |
Bank $05 | Overworld screen data, (10,0) to (11,15) |
Bank $06 | Overworld screen data, (12,0) to (13,15) |
Bank $07 | Overworld screen data, (14,0) to (15,15) |
Bank $08 | Underworld screen data, (0,0) to (1,15) |
Bank $09 | Underworld screen data, (2,0) to (3,15) |
Bank $0A | Underworld screen data, (4,0) to (5,15) |
Bank $0B | Underworld screen data, (6,0) to (7,15) |
Bank $0C | Underworld screen data, (8,0) to (9,15) |
Bank $0D | Underworld screen data, (10,0) to (11,15) |
Bank $0E | Underworld screen data, (12,0) to (13,15) |
Bank $0F | Underworld screen data, (14,0) to (15,15) |
Bank $10 | Background and path graphics (CHR data) |
Bank $11 | Background and path graphics (CHR data) |
Bank $12 | Background and path graphics (CHR data) |
Bank $13 | Monster graphics (CHR data) |
Bank $14 | Monster graphics (CHR data) |
Bank $15 | Game object graphics (CHR data) |
Bank $16 | Text groups, palette pointers, nametable pointers, attribute tables, collision tables, palette data, graphics pointers, object behavior pointers, object status pointers, text strings |
Bank $17 | Object behaviors |
Bank $18 | Object reactions, various subroutines** |
Bank $19 | Text entries |
Bank $1A | “HELLO WORLD” – unused in game, can be considered an empty bank |
Bank $1B | GGsound engine, music and sound effects |
Bank $1C | Object info & lookup table data***, timer end scripts, object action subroutine, bounding box handler, sprite drawing routine, timer update routines, object collision routine, object hurt routines, sprite address tables |
Bank $1D | Empty |
Bank $1E | Hud tiles (CHR data) |
Bank $1F | Aka the static/fixed bank: game initialization / reset script, main game loop, input handling, graphics loading routines, screen updates, data loading routines, postscreen loading script, screen trigger handling, text drawing routines, physics handler, active object handler, object creation, object subroutines, AI routines, AI lookup tables, interrupt vectors |
* Screen data = (meta)tile layout, collision data, screen info, object/monster info and attribute data.
** Sprite pre- and post-draw, collision handling, state update, object update, position update, AI reactions, aimed physics, hud updates, edge reactions, text box drawing mechanics.
*** Animation speed, behavior, action type, end action, vulnerability, max speed, size, bounding box, worth, speed variant, strength/defense, health, flags, accelleration, jump speed)
There’s an article about NESmaker banks on the NESmaker wiki that does it much more justice than this list does, so I’d encourage you to go there and read up on the basic bank structure of a NESmaker game.