Romless NES program format

Preliminary version: details are not finalized, and may change after discussion period.

A romless NES program is one which runs from RAM rather than ROM. Before being run, its data is loaded into the various RAM areas in the NES and cartridge. The program doesn't care how the data is loaded, allowing it to be stored in more than one format. The preferred format is a standard iNES file, allowing them to be run on anything. Most importantly, programs can be uploaded from a PC to a NES via a serial link cable, allowing quick development and testing on a low-cost cartridge.

For full details, see the specification.

PC upload isn't finished yet. It's working, but needs a few bugs fixed and documentation to be completed.



Normally, running programs on a NES requires a programmable cartridge of considerable cost, and removal of the cartridge each time the program is modified. The romless format was created to solve these problems.

Romless programs run from RAM in the NES and cartridge. To get them there, a small bootloader runs on the NES and receives data via a serial cable connected to the PC and writes it to RAM. The bootloader gives the PC full control over the NES.

Many cartridges are suitable for running romless programs. The only requirement is that they have a bootloader on their ROM:

Different cartridges have different RAM capacities and mapper chips. Romless programs can be written so that they only require a minimum of cartridge features, and thus work on multiple cartridge types. For example, a program can be made to run from the RAM inside the NES so that it works even if the cartridge doesn't have WRAM.

Romless programs are normally stored as standard iNES files. They include a built-in loader so that they work on any emulator or programmable NES cartridge, without any need to connect to a PC. In this way, they act like normal NES programs and don't require anything special to run.

NMI and IRQ interrupts can be used as long as the cartridge has interrupt handlers in ROM that forward them to the romless program's own vectors held in RAM. Special features of the cartridge can also be used, for example changing the mirroring mode on MMC1 and MMC3, or using the MMC3's scanline interrupt.

The romless format offers

Writing a program

Download examples and support files for the ca65, asm6, nesasm, and wla-dx assemblers:

Before a romless program is run, its data is loaded into the various memory areas: RAM, WRAM, CHR RAM, nametables, and palette. Zero-page and most of the stack are cleared to 0, as well as the CPU, APU, and PPU registers. A second set of NMI, reset, and IRQ vectors is kept in RAM at $7FA-$7FF, and loaded as a part of loading RAM. The program is started with a JMP ($7FC) instruction.

A program's source code generally follows this template:

 MAPPER = 1, 4, etc.  or omit if no mapper needed
 V_MIRRORING = 0 or 1, or omit if it doesn't matter
 NO_INTERRUPTS = 1 if nmi and irq aren't defined

 .include "" ; required

reset: ; required

 BEGIN_WRAM    ; $2000 bytes

 BEGIN_CHARS   ; $2000 bytes

 BEGIN_SCREENS ; $800 bytes (only $400 if also using BEGIN_SCREEN2)

 BEGIN_SCREEN2 ; $400 bytes

 BEGIN_PALETTE ; $20 bytes

 END_ROMLESS ; required

Most sections optional: Everything above is optional, except those marked as required. Don't put an empty section if it's not needed, because that will cause the program to require that hardware present when running.

Little else needed: The .include "" sets almost everything up, including iNES header, reserved areas, loader code, main vectors and vectors in RAM.

Code can go in WRAM: To put code in WRAM at $6000, move it after BEGIN_WRAM.

Unused bytes cleared: Any unused bytes of a section are filled with 0, except palette, which is filled with $0F (black).

Assembler differences

Assembler Commands to assemble program
ca65 program.s
ld65 -C romless.cfg -o program.nes program.o
asm6 program.s
nesasm program.s
wla -o program.s program.o
wlalink linkfile program.o
Assembler Specifics
  • Use wromless.cfg to automatically put code at $6000. This puts the CODE and RODATA segments in WRAM, and BSS in RAM from $200-$600.
  • The normal romless.cfg puts CODE and RODATA in RAM at $200-$600. ZEROPAGE and BSS are in 0-$1FF.
  • The sections have segments of the same names, so you can freely switch segments.
  • A section can only be begun once; multiple uses will give an error. Sections can be omitted of course.
  • Avoid using a section more than once, because the assembler will start overwriting the previous data without any warning.
  • A space or tab must be at the beginning of every line except a constant, label, or comment.
  • Avoid using a section more than once, because the assembler will start overwriting the previous data.
  • You must use .define FOO 123 instead of FOO = 123.
  • A space or tab must be at the beginning of every line except a label or comment. If left out when using one of the BEGIN_ macros, you'll get no warning, just data put in the wrong section.

Minimizing requirements

A romless program can run on different hardware configurations, depending on what sections it defines. For example, a program that has its code in WRAM requires a cartridge that has WRAM, and thus can't run on a UNROM cartridge. To make a program runnable on the widest possible range of cartridges people might have, a program can indicate that it doesn't need a particular feature. The following features can be indicated as not used:

Feature To indicate non-use Allows running on Uses
CHR RAM omit CHARS section CHR ROM sound-only program
WRAM omit WRAM section cart without WRAM small program, picture display
H/V mirroring don't define V_MIRRORING mapper with fixed mirroring graphics without scrolling
Mapper don't define MAPPER UNROM, MMC1, MMC3 fixed mirroring, no special features
Interrupts define NO_INTERRUPTS cart without interrupt forwarding simple demos, music


Contact Shay Green