NES Emulation: After Burner, Nametable Mapping, and Expansion Slots

After Burner was one of my favorite games growing up, and although it was After Burner II on the Mega Drive that I originally fell in love with, the NES version is still good fun and worth playing.

It was published by Sunsoft, and as such, it was distributed on a Sunsoft cartridge board, the Sunsoft-4. It was the only US game released on this board.

  1. Two PRG-ROM banks of 16KB, first bank is switchable, and the second bank is fixed to the last bank
  2. 8KB of optional PRG-RAM
  3. Four 2KB CHR-ROM banks
  4. Two optional 1KB CHR-ROM banks, mapped to the nametable addresses
  5. Nametable mirroring is programmatically controlled

The first PRG-ROM bank is switched by writing to any address on the CPU’s bus from 0xf000 to 0xffff. The least-significant 4 bits are used to switch the bank, and the 5th bit is used to enable PRG-RAM.

7  bit  0
---- ----
...E BBBB
   | ||||
   | ++++- Select 16 KiB PRG banked into $8000-$BFFF
   +------ 1:Enable PRG RAM = WRAM +CS2

The four CHR-ROM banks are switched by writing to the address ranges 0x8000 - 0x8fff, 0x9000 - 0x9fff, 0xa000 - 0xafff, and 0xb000 to 0xbfff, respectively, with all 8 bits available for bank switching.

So far, all of this is relatively straight-forward, being incredibly similar to most other mappers. And with all this, After Burner boots up with working audio, and is even playable, but very clearly without any background graphics rendering, because I haven’t implemented the nametable mapping yet.

In my emulator, it’s mapping every tile in the background to tile 0, which is a blank tile in the pattern table.

After Burner with no nametable mirroring

Nametable Mapping

The two 1KB CHR banks, mapped to the nametable addresses, are switched by writing to registers in the address ranges 0xc000 - 0xcfff, and 0xd000 - 0xdfff, respectively. However, only 7 of the 8 bits of these values are used to switch banks, and the most-significant bit is always set to 1. This has the practical consequence for game developers that the nametable banks must exist in the final 128KB of CHR-ROM.

The last register that we’re interested in, which exists in the address range 0xe000 - 0xefff, controls the nametable mirroring mode with the two least-significant bits, and it also controls whether or not the nametable addresses on the PPU’s bus are controlled by the mapper by setting bit 5.


7654 3210
   |   ||
   |   ++- Mirroring
   |       0: vertical (0101); 1: horizontal (0011);
   |       2: 1-screen (0000); 3: 1-screen (1111)
   +------ Chip select for PPU $2000-$2FFF (nametables):
           0 for CIRAM or 1 for CHR ROM

As far as I know, the original Sunsoft-4 cartridge board only supported setting vertical and horizontal mirroring, however as there are extra bits available, these extra bits are often used in emulators to give homebrew developers more functionality.

This gives the cartridge board a unique ability; when nametable data is being retrieved from the PPU bus in the address ranges 0x2000 - 0x2fff, instead of using the existing RAM chip inside the NES, called CIRAM, it will instead map these addresses to CHR-ROM on the cartridge.

Why would you want to do this? I can think of two reasons.

The first reason is that the address space that maps the nametables allows for addressing of up to 4KB of data, however the internal CIRAM chip in the NES only holds 2KB of data, so nametable mirroring is necessary to allow addressing of all 4KB of address space.

But if you wanted to map more than 2KB of data into the 4KB of address space, the NES allows cartridges to map the nametable address space to CHR-ROM on the cartridge, so now a game could have four nametables with different background data in each one.

However, the Sunsoft-4 only allows mapping two 1KB banks for the nametables though, so there’s still only 2KB of data mapped to the 4KB of address space… so that’s not it.

The second reason is to allow very rapid changing of the background, like when the fighter jet does a full flip; the pattern tables are being updated as you rotate to give the angled appearance, and the nametables are being swapped aswell to map these angular-looking tiles to parts of the background. This would otherwise be too slow to do while maintaining a fast, responsive game.

There are two banks which can be configured for the nametables, and in the context of the nametable mirroring modes, the first bank is always mapped to the lower nametable, and the second bank to the higher nametable.

For example, in vertical mirroring, often used for games that scroll in the horizontal direction, the nametables are mirrored like so:


0x2000    0x2400
  +---------+---------+
  |         |         |
  |    A    |    B    |
  |         |         |
  +---------+---------+
  |         |         |
  |    A    |    B    |
  |         |         |
  +---------+---------+
0x2800    0x2c00

For games using that use this function on the Sunsoft-4 board, the two left nametables (at 0x2000 and 0x2800) will be mapped to the first nametable bank in CHR-ROM, and the two nametables on the right (at 0x2400 and 0x2c00) will be mapped to the second bank.

A similar mapping is necessary for horizontal mirroring - which maps the top nametables to the first bank, and the bottom nametables to the second bank - and for single screen mirroring - which maps all four nametables to either the first or second bank.

After Burner with nametable mirroring

This is the first mapper that I’ve come across that maps the address ranges for the nametable data.

Expansion Slots

Nantettatte!! Baseball is a game that was released in Japan for the Famicom that used this mapper, and they modified the cartridge board slightly to include a licensing chip, and also an expansion slot on the cartridge, so that you could insert mini cartridges which contained data for baseball teams. I presume this was so new baseball team data (like player names) could be released each baseball season without having to re-release the entire game cartridge again.

In order to read data from the expansion cartridge (referred to as external ROM) rather than the primary cartridge (internal ROM), the register which controls the first bank of PRG-ROM (0xf000 to 0xffff) works slightly differently than above.


7  bit  0
---- ----
...E RBBB
   | ||||
   | |+++- Select 16 KiB PRG banked into $8000-$BFFF
   | +---- 1: select internal ROM
   |       0: select external ROM
   +------ 1:Enable PRG RAM = WRAM +CS2
           0:Enable licensing verification

As before, this only controls the first bank of PRG-ROM, and whether it points to internal or external ROM. The second bank of PRG-ROM always points to the last bank of internal ROM.

Nantettattee!! Baseball

I chose not to add support for this game, at least for now, but it’s actually a lot of fun to play.

It’s interesting to see how these particular expansion cartridges were supported in the NES/Famicom.

Resources

  1. Mapper 68 on NesDev Wiki
  2. Nametable Mirroring on NesDev Wiki
  3. Sunsoft Mapper Info by goroh