RLE-decompression library nes-rle-decompress 0.1.0 released

In my NES game project, I wanted to insert pre-drawn background screens (such as for the title screen), but given a background screen is 1,024 bytes including the nametable attributes, I needed to find some way to compress the nametable data for each background screen to be smaller.

I wrote a small RLE-decompression algorithm for NES 6502 assembly and have uploaded it to GitHub to see if it proves useful for anyone else. It is licensed under the Unlicense License (effectively, the software is released into the public domain).

The assembly code is written using asm6f syntax.

The project also includes a small Python script which compresses a 1024-byte nametable data file so you can then use the decompression code.

Limitations#

  • Currently we only encode the count using positive integers. This means that files that don’t have long runs of identical bytes won’t compress as well, or may even end up much larger than the original file. I have a version of the code in progress which uses negative numbers to specify that the next n bytes should be copied exactly rather than repeated. This should save some space, but the code isn’t yet working.

  • Assumes your code is using vertical mirroring. The macro LoadRLEScreen asks for a nametable to write to, with 0 = $2000 and 1 = $2400. The code could be tweaked easily enough to work with horizontal mirroring.

Compressing the nametable data file#

compress_rle.py is a Python script which will take a 1024-byte nametable data file and RLE-compress it. In my project, my uncompressed nametable data files have .bin extensions and the compressed nametable data files have .rle extensions:

> python compress_rle.py --input bg_title_screen.bin --output bg_title_screen.rle
Input file: bg_title_screen.bin
Output file: bg_title_screen.rle
Input file size (bytes): 1024
Output file size (bytes): 516
Compression (%): 49.6

>

Usage in 6502 assembly#

First, define a 16-bit variable named pointer in RAM:

.enum $0000
pointer .dsb 2
.ende

If you don’t already have constants declared with the PPU addresses, do so:

; PPU addresses
PpuCtrl			= $2000
PpuMask			= $2001
PpuStatus		= $2002
OamAddr			= $2003
OamData			= $2004
PpuScroll		= $2005
PpuAddr			= $2006
PpuData			= $2007
OamDma			= $4014

Later in code, I define different locations including RLE-compressed nametables. For example:

bg_title_screen:
  .incbin "src/assets/bg_title_screen.rle"

The macro LoadRLEScreen allows you to specify the 16-bit address in PRG-ROM where the RLE-compressed nametable data is located, as well as the nametable it should be loaded into. LoadRLEScreen clobbers A, X, and Y.

; load into nametable 0
LoadRLEScreen bg_title_screen, $00

; load into nametable 1
LoadRLEScreen bg_title_screen, $01

NOTE: This assumes you’re doing vertical mirroring. I may extend the code so as to take the nametable memory address you’re writing to, hence allowing this to work with horizontal mirroring.