Guide

How Neo Geo sprites are stored in the C-ROM

A Neo Geo sprite tile is 16×16 pixels, four bitplanes, 128 bytes. The awkward part is that those four bitplanes are not next to each other: they are split across a pair of C-ROM chips, two planes in each. That single detail is what breaks most tile-decoding code carried over from other arcade hardware.

The short version

Why the bitplanes are split across two chips

It is a bandwidth arrangement. The C-ROMs are 16-bit parts and the video hardware reads both chips of a pair at once, so a single fetch returns 32 bits — all four planes of one row of eight pixels.

Whether that costs you anything depends on how you hold the data. Kept as separate C1 and C2 files, a tile is split across two buffers. In the interleaved form — the one MAME uses internally, and the one inside a .neo — a tile is 128 contiguous bytes and the four planes of a row are four consecutive bytes, in the order bp0, bp2, bp1, bp3.

To read one pixel, take those four bytes, shift each down to the same bit position — leftmost pixel is bit 0 — and stack them: plane 0 at weight 1, plane 1 at weight 2, plane 2 at weight 4, plane 3 at weight 8. That gives the palette index, 0 to 15.

Why CPS1 code does not port over

Both machines put four planes of a row in four consecutive bytes, so the shape of the loop survives. Four smaller things do not, and each produces its own flavour of garbage:

We hit all four building both emulators. The Neo Geo decoder had to be written fresh rather than adapted, and getting shapes on screen with the wrong colours — or mirrored in eight-pixel strips — is the usual sign that one of these four is still wrong.

The fix layer is a different format again

The Neo Geo also has a fix layer — the 8×8 text and HUD layer — and it lives in the S-ROM under entirely different rules: 8×8 tiles, 4bpp, 32 bytes per tile, but stored linearly, in columns rather than in planes. Pixels go two to a byte, read top to bottom in 2-pixel-wide columns; the right half of the tile is stored before the left; and within each byte the low nibble is the left pixel. The reference documentation calls the column order “oddly mixed”, which is fair.

It is not simpler than the C-ROM, it is differently strange — and not interchangeable with it. If your tool reads sprites correctly and the HUD is scrambled, this is why.

What this means if you are drawing

Two things follow from the format, and both matter more than the byte layout does.

Not having to think about any of this

Sprixe Neo Studio reads and writes this format for you. You draw in native Neo Geo palettes, import from Aseprite, and the encoder packs the result into C-ROM — correct bitplane split, pen 0 transparent, no manual byte juggling. The format is worth understanding; it is not worth implementing twice.

Enter the beta Neo Geo palettes →