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
- Tile size: 16×16 pixels, 4 bits per pixel, 128 bytes per tile.
- Storage: the C-ROMs ship in numbered pairs — C1/C2, C3/C4, C5/C6 and so on.
- Odd chip (C1, C3, C5…) carries bitplanes 0 and 1.
- Even chip (C2, C4, C6…) carries bitplanes 2 and 3.
- Transparent pen is index 0, not 15.
- Bit order: bit 0 — the least significant bit — is the
leftmost pixel. This is the opposite of CPS1, and it is the single easiest thing to get
backwards.
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:
- Plane order.
bp0, bp2, bp1, bp3 here, bp0, bp1, bp2, bp3 there — palette indices come out scrambled.
- Bit order. LSB is the left pixel here, MSB there — every group of eight pixels comes out mirrored.
- Block order. The right half of the tile is stored first: bytes 0–63 are the right 8×16, bytes 64–127 the left.
- Transparent pen. Index 0 here, index 15 on CPS1.
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.
-
Sixteen colours per tile, and index 0 is transparent. That is fifteen
usable colours, not sixteen — a constraint worth designing around from the first
sketch rather than discovering at import time.
-
Everything visible is a sprite. There are no scroll layers in the usual
sense — backgrounds are built out of sprites too, which is why a
“background” and a “character” are the same kind of object to the
hardware, and why each band can scroll at its own rate without a dedicated layer.
-
A sprite is a vertical strip, and they are rationed by line. One sprite
is 16 pixels wide and 1 to 32 tiles tall, so a character costs several chained together.
381 can be displayed per frame, but the binding limit is per scanline: 96
sprites, and transparent pixels count. A full-width background band eats 20 of
those 96 before a single character is drawn. Parallax on this machine is free in
shape, not in budget — it is the constraint you will hit first.