NES Emulation: Mapper 34, and Nametable Mirroring

I downloaded the homebrew NES games from the 2020/2021 NesDev competition. If you’re a fan of playing homebrew games, you should definitely pick these up and enjoy some of the current NES homebrew scene.

I was interested in checking if my NES emulator could play all of the games. Most of them booted up fine, which was a great start. But I came across Arkade Rush, which uses mapper 34, which I hadn’t implemented, and checking NesCartDB showed that there are only a couple of officially licensed games that use this mapper, so it’s definitely not common.

Also - and I’m not sure how this happened - but the mapper actually consists of two different cartridge boards: BNROM and NINA-001.

And they aren’t the same.

BNROM

  1. PRG-ROM bank size of 32KB
  2. Uses 8KB of CHR-RAM (not ROM) and is not bank switched

The PRG-ROM bank is switched by writing data to any address on the CPU’s bus from address 0x8000 to 0xFFFF. Officially, only the two least-significant bits of this byte are used to determine which bank to use, giving a total potential PRG size of 128KB, however many emulators will use the full 8 bits, allowing up to 8MB of ROM for homebrew games to take advantage of.

Arkade Rush is a BNROM game, as is Deadly Towers.

BNROM is a popular choice for homebrew developers, as it can be used as part of mapper 28, which is a multicart that can bundle different games with different mappers (including BNROM) together. In NesDev homebrew competitions, often all of the entries are bundled together into a giant multicart using mapper 28.

Arkade Rush

NINA-001

  1. PRG-ROM bank size of 32KB
  2. PRG-RAM size of 8KB, not battery backed (so it doesn’t persist between console restarts)
  3. Two CHR-ROM banks of 4KB
  4. Nametable mirroring is hardwired to horizontal

The PRG-ROM bank is switched by writing data to address 0x7FFD on the CPU’s bus. Only the least significant bit of this byte is used for bank switching, giving a total potential PRG size of 64KB.

The two CHR-ROM banks, at 0x0000 and 0x1000 on the PPU’s bus are switched by writing data to 0x7FFE and 0x7FFF, respectively. The low nybble is used to determine which bank to use, giving a total potential CHR size of 64KB inside the cartridge.

Interestingly, because the addresses of these registers overlaps with the PRG-RAM address space (0x6000 to 0x7FFF on the CPU’s bus), writing to these registers also results in the PRG-RAM address being written to with the same value.

Impossible Mission II is a NINA-001 game.

Impossible Mission II

So if the two boards are so different, how does an emulator tell the difference between them? The iNES header tells you if the cartridge uses RAM or ROM for the CHR (a zero for the 6th byte means it uses RAM), so if the cartridge is using 8KB of RAM for CHR, it’s a BNROM game, otherwise it’s a NINA-001 game.

Nametable Mirroring

After basic implementation of the mapper, I had Arkade Rush, which uses this mapper, and it specifically uses the BNROM board, and I had Deadly Towers, which also uses the BNROM board, however there was an issue with the graphics. Deadly Towers started up like this:

Deadly Towers faulty start-up screen

It should start with a black screen, from which the title scrolls down, rather than starting with the title screen already there, and… then scrolling the same title screen down.

But Arkade Rush started up fine, and seemed to play as expected.

It seemed like a nametable problem, due to the graphics all looking correct, but it repeated the same graphics as it scrolled down. This sounded like it was using vertical mirroring, but wanted horizontal mirroring because of the vertical scrolling.

After statically setting the mirroring to horizontal in code, Deadly Towers would start-up correctly, validating my suspicion. But then Arkade Rush would have issues with its title screen:

Arkade Rush faulty start-up screen

A little more digging into what else affects nametable mirroring and I found this little bit in the iNES file specification for flag 6, which is the seventh byte in the header:

76543210
||||||||
|||||||+- Mirroring: 0: horizontal (vertical arrangement) (CIRAM A10 = PPU A11)
|||||||              1: vertical (horizontal arrangement) (CIRAM A10 = PPU A10)
||||||+-- 1: Cartridge contains battery-backed PRG RAM ($6000-7FFF) or other persistent memory
|||||+--- 1: 512-byte trainer at $7000-$71FF (stored before PRG data)
||||+---- 1: Ignore mirroring control or above mirroring bit; instead provide four-screen VRAM
++++----- Lower nybble of mapper number

Specifically, bit 3, which says Ignore mirroring control or above mirroring bit; instead provide four-screen VRAM. My iNES parser was always using bit 0 to determine which nametable mirroring to use.

Honoring this “ignore” bit - thus setting the game to use four-screen nametable mirroring - fixed the issue.

Not a super complicated mapper to implement, but it lead to some fun discoveries.

Resources

  1. BNROM on NesDev Wiki
  2. NINA-001 on NesDev Wiki
  3. Nametable Mirroring on NesDev Wiki, including games that use various mirroring modes
  4. PPU Scrolling by Rafael Bagmanov (bugzmanov), has excellent examples of nametable mirroring
  5. NES Architecture by Rodrigo Copetti, contains a section on nametable mirroring

This is the first of a few posts about less commonly implemented mappers for the NES, because in my experience, they often lead to more obscure bits of knowledge about NES emulation that don’t pop up when implementing the more common mappers for the more popular games that most hobbyist emulator developers are interested in.