Skip to content

Case Study: NES to SNES Conversions

You are here:
Estimated reading time: 8 min

Case Study: NES to SNES Conversions

A practical guide about how games like “Little Medusa” was ported in record time.

Intro

First of all, the game is running and playing well on NES. All the bugs have been removed and it is playtested and even released. It is so popular that the extra power of some platforms (such as SNES) could benefit from its popularity and the extra power comes with the hardware.

The idea of porting is nothing new. In fact, when games were released by major software houses in the times of diversity (which we don’t really have any more now) there were several releases of the same game on different platforms. Sometimes these crossed console and computer generations and had something extra for the newer, faster, more powerful machines. 

Most the time the different platform releases were done by different teams. It was rare for a single team to work on two or more different architectures. CPU-s, graphical abilities, memory organization, custom chips were different enough, requiring people to specialize or to know multiple platforms inside out.

A very successful game, Impossible Mission ported from Commodore 64 (left) to SEGA Master System (right). The two systems have nothing in common.

The borders seemed to blur when the C programming language started to gain recognition. It was always there in the UNIX world from the 70-s but games only started to adapt it in the end of 80-s. Even then, it was used with care. The computer to generate assembly code for itself was nice but wasn’t that efficient at the beginning. It also took time for the compilers to produce the code and the first operating systems weren’t even forgiving when it came to debugging. 

Still this was a huge turning point. Games became more portable than before. For example a game written in 6502 assembly required a complete rewrite for Z80 assembly. The more complex and different nature of 68000 assembly also needed a total “from scratch” approach for x86 platforms such as 80286 or 80386. 

The game logic could be shifted almost without changes and only those parts dealing with the hardware had to be rewritten. This was unfortunately also the reason for a lot of “lazy ports” which were simply low quality when ported since the developers were satisfied with the bare minimum.

The next turning point was the birth of the cross-compilers. This was a new idea, carefully kept as secret for many software houses in the beginning. As the obvious difference between platforms came to front, compilers were written to produce code on the faster platform but for a different target platform.

Development time was shortened and also the target platform could have crashed without jeopardizing the source code.

NES to SNES

The NES to SNES porting process has a few requirements:

  • The game is coded in C
  • The cc65 compiler is used
  • Preferably Shiru’s neslib was used during development

The first point goes without saying: we intend to shift the game logic over, almost with no changes (more about the changes later).

The second point is critical: this is the only working C compiler for SNES.

The third point makes porting much faster: neslib has useful abstraction functions which can be reused.

This is made possible with Lauri’s sneslib. This is a port of neslib to the SNES. It does not provide all the functions (does more in some areas) but it is almost a drop-in replacement.

The following details the differences between the NES and SNES and also gives some examples how it is solved with sneslib.

Memory architecture

NES has a 8-bit CPU and can access 64K memory at a time. To overcome this limitation, many companies used bank switching AKA mappers. A popular mapper called MMC3 is used in most of the games. To understand what it does here is a simple analogy:

Imagine you have 8K of ROM where your game code and graphics resides. You ran out of this and would need more space. Of course, you can use 64K theoretically but you need even more than that. What would you do? You can’t just swap cartridges in the middle of the game – unless you can!

This is what MMC3 does (among other things) but this is its main profile. It swaps memory areas (called banks or pages) while the game is running. Of course there are more than one banks in the memory. Swapping the bank where the game is running will certainly cause a crash. This “swap” happens instantly. MMC3 tricks the CPU to read from different areas of its ROM. The CPU always thinks it accesses the same memory regions as before but in the meantime it changed. There’s nothing wrong with that.

Now imagine the same is applied to the PPU as well. You can have way more graphics than 64K

Here is what the NES looks like inside, memory-wise, from the CPU side:

  • 2K of RAM – in C programming it is used for variables.
  • ~48K of free area what a cartridge can claim for itself to be ROM or RAM. It’s a bit less ($4020-$ffff) and some areas are still special. – usually called as PRG

The rest of the 64K is used for PPU access or mirrors of the same 2K area.

The PPU has its own 16K VRAM, and can see this:

  • 2x4K of pattern RAM – this contains the tile graphics – also called as CHR
  • 2x2k of nametable RAM – this contains the tile maps 
  • 32 byte of palette entries. 

The missing area is mirrored similarly to the CPU.

Here is what the MMC3 can do for you:

Mapping of:

  • 2K CHR ROM into the VRAM from $0000 
  • 2K CHR ROM into the VRAM from $0800 
  • 1K CHR ROM into the VRAM from $1000 
  • 1K CHR ROM into the VRAM from $1400 
  • 1K CHR ROM into the VRAM from $1800 
  • 1K CHR ROM into the VRAM from $1c00
  • 8K PRG ROM into RAM from $8000
  • 8K PRG ROM into RAM from $a000

There is also an area from $C000 but it’s fixed. MMC3 also has the feature to reverse these features. It can swap the 2K and 1K bank mapping locations and also can provide similar swap for the PRG mappings.

Anyway, NES developers usually have to changes these banks often and jump from PRG bank to PRG bank. This complexity is hidden by the C compiler somewhat.

The SNES has no mapper. It does use banking but only because its CPU is a semi-16-bit one. It can operate in 8 or 16 (24) bit mode. It’s a more complex CPU but from the point of this guide it’s not important: it is mostly handled as it was a 8-bit CPU.

The SNES has more more memory in the LoROM and hiROM cartridge specification. A PRG bank is 32K big and can have as much as 4MB. It is only for PRG ROM.

It also has some more types of memory:

  • 128K WRAM – called work RAM. Variables and C runtime sits here
  • 64K VRAM – anything that’s graphics goes here
  • 64K SPU RAM – anything that has to do with sound goes here 

Just by compiling the NES game loop less than 32K it would run unchanged on the SNES.

Just as the NES programmers have to change PRG banks very often, the SNES game also has to change its PRG bank if it’s needed. This is supported by its CPU, not a mapper chip.

Comparison of the NES (top) and SNES (bottom) memory maps. Note the versatility and the size of the SNES map.

Background graphics

The NES can display 256×240 (256×224 on NTSC) pixels but it is usually measured by tiles or blocks. That’s 32×30.

The NES needs to support specific “mirrorings” if scrolling is needed. It’s either two screens next or on top of eachother. The MMC3 supports even 4-way mirroring (64×60) but that’s extra in most games, rarely used. 

The SNES on the other hand can display the same blocks (256×224 on NTSC) but it has 4-way mirroring by default. On top of that it supports video modes (the NES has only one) for multiple overlay backgrounds (with SNES terminology: BG-s) but this is not part of this guide.

There is also a nametable associated with it. It has to be placed into VRAM either when the screen is turned off or during the VBLANK interrupt – this is same as on the NES.

There is a habit for NES programmers to switch CHR banks to animate tiles and whole backgrounds. This may be a bit troublesome on SNES. See more in the DMA chapter.

<screenshot of Little Medusa BG data on NES and SNES>

Sprites

The NES can display 64 sprites but has limitations. It can have 8 sprites in each scanline only and sprites are 8×8 pixels. There is another mode with 8×16 sprites but it’s a nightmare to work with so it’s rarely used.

Conveniently one of the VRAM CHR areas is used for sprites and the another is for backgrounds. 

This is the reason the SNES sprites are relatively small or flickering: most of them are metasprites, made up from multiple 8×8 blocks.

The NES sprite are can be visualized as a 128×128 pixel area divided into 8×8 blocks.

The SNES can display much more, and two fixed sizes can be selected. It can do 8×8, 16×16, 32×32, 64×64. Here is the first secret of this guide: the sneslib uses 8×8 sprites and follows the NES code to assemble metasprites.

The SNES has twice the sprite area than the NES.  It is also a good coincidence for the sneslib.

Another advantage is the “more than 8” limit in a single scanline for sprites. It is more complicated how many sprites can be displayed under different circumstances but suffice to say a ported SNES game will flicker much later (or if at all) than a SNES game with this method.

In most cases all the NES sprite handling can stay in place, the neslib covers for the differences. We lose the advantage for the larger sprites but it can speed up the porting process a lot.

BG sizes are the same, should not be a problem for the artist. Metasprites though usually are “scrambled” in their 128×128 area. Even if this are could be converted to SNES format, improving them can be challenge.

There are a few solution such as:

  • Try not to scramble the NES sprite CHR so it can be touched up easily
  • Strip the metasprite system from the game and use native SNES sprites.

Also,  NES programmers tend to swap sprite CHR banks to expand the sprite frames. It is the same issue with BG-s.

Difference between the NES (left) and SNES (right) sprite organization. SNES have twice the sprite memory than NES and also can handle larger sprites than 8×8. On the top NES screenshot the sprites are scrambled to save space. They make up the metasprites.

On the bottom SNES screenshot the sprites were arranged visually recognizable blocks but still made with metasprites.

<screenshot of Little Medusa sprite CHR data from NES and SNES>

Colors

The NES has a fixed palette and can have 4 colors by attribute (16×16) blocks. Sprites can have 4 colors in total. This limitation is higher on the SNES: sprites can have 16 colors selectable of 8 palettes with 16 colors each. The BG-s can have several colors in different modes but the one used in the method (Mode 1) can have 16 colors per block, selected from 8 palettes of 16 colors each!

The NES graphics (converted from original) will work without a doubt. However it is advisable to redraw or recolor the originals to get advantage from the BGR palette of the SNES. 

The NES (left) and SNES (right) palette system. The NES colors are fixed. The SNES colors are BGR. Note the current (top left) selected entry and its BGR values. There is a visible “split” between the first 8 and the second 8 entry for BG and sprites respectively.

Sound

The NES sound chip can generate sounds and play some samples. The SNES has 64K space and all it can do is playing samples with some effects. All music and sfx has to be redone from scratch.

DMA

This is SNES-specific. Since there is no bank switching scheme which could be used for fast memory content replacement, SNES has to resort to its DMA engine. It’s pretty fast, can transfer about 5K of data per frame. 

Unfortunately it takes more time than just switching a bank on NES so redesign of the code (or game itself) may be needed sometimes.

Typical problems are sprites. If there are all four 1K banks switched per frame on NES, that’s 8K data transfer per frame on SNES. Time permitting either the sprite system have to be recoded to use DMA streaming or reorganized into the SNES sprite area (if they fit).

If you look at the SNES sprites topic you will notice there are only a few sprites stored in its area. That’s because the sprite frames are copied there with DMA during the game, it is not needed to keep all frames in VRAM.

Final thoughts

Best case scenario is when a NES game code will run with modifications made to the bank switching parts then dropping it into the sneslib environment. The metasprites, re-converted BG-s will function as they were on a large MMC3 SNES cartridge thanks to DMA.

In other cases, the DMA has to be carefully utilized to be used as a substitute for bank switching. In more problematic cases the sprite system has to be rewritten so it would fit the SNES capabilities.

Sound is always a case of re-making it either on SNES-GSS or SnesMOD. Currently there is no Famitracker to SNES converter and it wouldn’t be practical either.

Download Guide

The guide is available in PDF format and can be downloaded at the link below. Approximately 331kb

Was this article helpful?
Dislike 0
Views: 5
Back To Top