Writing a new ROM

Every ROM needs a little code for it to function properly.

In this example we set up a template that includes the ideal minimum code required for an executable ROM. This typically includes setting up things such as the Stack, Interrupts, main program loop, and for demonstration, a zero-page value that increments over time.

Feel free to copy this code directly into your IDE or text editor.

include "64cube.inc"

;zero-page values
ENUM $0
  counter db 1
ENDE

;called once at start
  org $200
  sei
  ldx #$ff
  txs

  lda #$0
  sta VIDEO

  _setw IRQ, VBLANK_IRQ
  cli

;constant loop
Main:
  jmp Main

;called once per frame
IRQ:
  inc counter
  rti

Give your new program a name. This can be anything so long as it has a .s assembly file extension. Then run your new ROM as follows:

./minicube newrom.s

You should now see a collection of pixels, with the one at the top left flickering, changing colour. These pixels are a visual representation of your program code in memory.

The ROM will automatically compile to a complete .bin without dependencies that can be loaded separately, and may be distributed as desired. Loading the compiled ROM is done as so:

./minicube newrom.bin

Breakdown of the ROM Start Template

In this section we'll look at what the template does.

You can write this in any text editing program, but it is best to use an IDE that supports 6502 syntax highlighting and grammar, such as VIM or Atom.

The first thing we should do is include the header file, which contains useful aliases and macros.

include "64cube.inc"

Next is typically the block that enumerates any values for the zero-page. You can think of these as variables. Here we define a byte value that will be used as a counter.

ENUM $0
  counter db 1
ENDE

We must now specify the origin of the ROM program and set the Interrupt. Any instructions here are called once when the program starts. Setting the Stack Pointer should also be done here:

  org $200
  sei
  ldx #$ff
  txs

0x0200 must be the start address for all programs.

Now to set the VIDEO register. To do this we load the Accumulator lda with the high-byte of the intended frame-buffer memory page.

  lda #$0
  sta VIDEO

TheVIDEOregister can be set to any 4k page within0x0000—0xF000

It is good practice to set this to at least0x1000however in this example it is at the beginning of memory and the zero-page, so we can see something happening. If not set the VIDEO register will default to 0x0000

Next we set the vector for the VBLANK Interrupt. We can optionally use the _setw macro here simply because it is easier to work with. See more about Macros here.

  _setw IRQ, VBLANK_IRQ
  cli

Now we have the main program loop. At least one loop must exist so that the ROM does not simply end. Usually anything in it would be called constantly, but for now it does nothing.

Main:
  jmp Main

We are giving this loop the Main label but it can have any unique name.

Lastly is the VBLANK Interrupt loop. The IRQ fires once per frame. Here we are only doing something basic, incrementing a timer, the counter value we defined earlier.

IRQ:
  inc counter
  rti

And that's it!

For reference, this is the absolute minimum required for a working ROM.

  org $200
  sei

Main:
  jmp Main

Last updated