Technical demonstration · Neo Geo MVS · 2026

R-Type, the 1987 arcade program, running on Neo Geo hardware — with a two-player co-op mode it never had

A personal reverse-engineering and porting study by Pascal Peltriaux. The original game's NEC V30 code is translated, instruction by instruction, to the Neo Geo's Motorola 68000, and everything it sends to Irem's video hardware is redrawn with the Neo Geo's sprite engine. On top of that program runs a co-op mode: both players fly at once, on the original's own player code.

Nothing here is downloadable. This site shows a technique and its results. It does not distribute ROMs, game data, artwork, sound, binaries or source code, and no files are sent on request. R-Type is the property of its rights holders; see the legal note.

Captured from the emulated Neo Geo

Both clips are straight captures of the port running in MAME's Neo Geo MVS emulation at the console's native 320×224, scaled 4× with no filtering. The sound is the Neo Geo's own YM2610 playing the port's FM driver.

80 s Two-player co-op: a 2P start from the title, the fly-in with the 1P and 2P tags, the stage 1 boss, the stage 2 snake, and the stage 3 warship. Both ships were set invulnerable for the capture so the camera stays on the action.

45 s Power-on and attract mode: the Neo Geo BIOS boot, then the original's attract sequence (title, demo play, ranking) running through the translated code.

How it works

The arcade board is an Irem M72: a NEC V30 (an x86-compatible CPU) driving two tile layers, a sprite list and a palette RAM. The Neo Geo has a Motorola 68000, no tile layers at all, and 381 hardware sprites that can be chained into columns. The port keeps the original program and replaces its hardware.

1. Translate the program, don't rewrite it

A translator (Python, built on the Capstone disassembler) walks the original's code from its interrupt handler and main loop and emits 68000 assembly for every instruction it reaches: about 21,700 x86 instructions. The x86 flags live in the 68000's condition codes, with a liveness pass that saves and restores them only where a later instruction would need them. Segment registers, far calls, string instructions, BCD arithmetic and the V30's own extra instructions are all covered.

The translated code runs against a byte-exact copy of the original's memory: its program ROM image, 16 KB of work RAM, sprite RAM and palette RAM. Every 16-bit word is stored byte-swapped so that the big-endian 68000 reads the little-endian game's words as single moves.

2. Redraw what the game sends to its video chips

The original writes a 128-entry sprite list, two 64×64 tilemaps and a palette. On the Neo Geo:

  • Sprites become hardware sprites, one per 16-pixel column of each entry, placed exactly where the M72 would put them.
  • Each tile layer is 32 column sprites of 32 tiles each: the whole 512×512 tilemap sits in hardware, so scrolling only moves the columns. A Neo Geo tile is one 2×2 group of the original's 8×8 tiles, found in a table built from the stage maps and a recording of the original.
  • The HUD band (score, beam gauge) goes to the Neo Geo's fix layer.
  • Palettes map one to one, with merged palettes generated for tile groups that mix palette slots.

Translated code reports what it wrote, so changed tile groups are queued and redrawn in the next VBlank, and only then.

3. Keep the arcade's frame structure

As on the board, the original's start-up code runs once, its main loop then pops tasks from its own queue forever, and the Neo Geo VBlank interrupt runs the original's interrupt handler wherever the main loop happens to be. Long tasks such as screen fills and map streaming span several frames exactly as they did in the arcade. The game gets every Neo Geo frame (59.19 Hz against the original's 55 Hz), so it plays a little faster but scrolls evenly.

4. Hand-write only the hot spots

Translated code is slower than native code, so the few shared routines every object calls each frame were rewritten in 68000 by hand from a description of what they do: the hit tests (with a broad phase that rejects most candidates before the original's chain of tests), the sprite piece writers, the palette fader, the tilemap clears and the map block streamer. The interrupt handler's object loops are native too, with a per-slot cache of each object's handler address. Everything else is the translation.

Two players at once

The arcade game is single-player with alternating turns. The co-op mode here runs the original's own player code a second time each frame, with player 2's copy of the player objects (ship, Force, shots, beam) swapped in. Player 2 flies a crimson R-9; its shots hit and score for it, enemies can hit it, and the items it touches are its own.

What the mode adds

  • A 2P start from the title begins a co-op game. Player 2 can also join a running game with a credit.
  • A lost ship flies back in where the game is; the game only rewinds to a checkpoint when both players are out.
  • Both scores and ship counts on the HUD, player 2's in crimson; a blinking PRESS START while a player is out.
  • 1P and 2P tags over the ships at every fly-in, for three seconds.
  • Enemies that aim at "the player" are given the nearer ship.

How it stays the original's code

No player logic was rewritten. Each frame the handler runs player 1's code, then the port swaps in player 2's copy of the player objects and runs the same code again. Hooks at a handful of addresses make the original's "the player" mean the right ship: hit tests check both, items go to the one that touched them, and the HUD draws two scores.

Two frames: both ships flying in with 1P and 2P tags on stage 1 and stage 3
The fly-in on stages 1 and 3: player 1's ship in its usual colours, player 2's in crimson, each tagged.

Numbers

Translated instructionsabout 21,700 (everything reachable from the interrupt handler and the main loop)
Hand-written routines22 shared routines plus the interrupt's object loops; everything else is machine translation
Cartridge1 MB program ROM, no bank switching; the original's ROM image sits inside it
Frames run late42 of 5,160 in a profile run of attract mode and play (single player); 495 in co-op, mostly on the stage 2 snake
Interrupt handler81% of a frame in busy scenes; when four or more tasks are waiting, every other VBlank is left to the main loop so the game slows down instead of dropping tasks, as the original's 64-entry queue would
Frame rate59.19 Hz (every Neo Geo frame) against the original's 55 Hz
Tested onMAME 0.289 and a Recalbox RGB JAMMA cabinet (libretro MAME 0.258)

Screenshots

Native 320×224 frames from the emulated Neo Geo, shown pixel for pixel.

Neo Geo boot eye-catcher
Power-on: the console's own boot screen.
R-Type title screen with NEO under the logo
Title, with the port's NEO mark under the logo.
Attract mode demo play
Attract demo: the original's recorded inputs, replayed by the translated code.
Ranking table
Ranking table on the fix layer.
Start screen with credits
Start screen. A stage select was added here for testing.
Two ships on stage 1
Co-op on stage 1: both HUD scores live.
Two ships on stage 1 with the Force
Each player has a Force of their own.
Stage 1 base
Stage 1: the two tile layers as column sprites.
Stage 1 boss rising
The stage 1 boss rises.
Stage 1 boss fight
Boss fight with both ships.
Stage 1 boss explodes
Boss destroyed.
Stage cleared
Stage cleared.
Stage 2 cave
Stage 2: the organic cave.
Stage 2 snake
The snake, the heaviest scene for the 68000.
Stage 2 snake coiling
Snake segments share hardware sprites with everything else on the line.
Stage 2 snake with two ships
Two ships against the snake.
Stage 3 wave cannon beam
Stage 3: a charged beam.
Stage 3 warship bow
The warship's bow.
Stage 3 warship
Destroyed parts rewrite the background layer, as in the arcade.
Stage 3 warship, further along
Further along the hull.

How the translation is verified

Frame-exact comparison against the arcade

A harness records 300 frames of the original in MAME (its memory at one VBlank interrupt, then every port access per frame) and replays them through the translated code on the emulated Neo Geo. Each frame, 16-bit checksums of every 256-byte block of work RAM and both tilemaps are compared with the original's. Result: all 300 frames identical.

Checks on the hand-written routines

Each native routine keeps the original's exact memory, register and flag results, and is tested against its translated counterpart. The hit-test broad phase runs both ways in a check build and counts disagreements: none in 3,900 frames of play. A sprite check rebuilds the Neo Geo's sprite memory from a write tap and compares every object sprite against a model of the sprite list after every frame: 0 mismatches in about 715,000 checks.

Scripted play

Automated runs play attract mode and games in MAME, checking that nothing untranslated ran, that coins and starts worked, that fewer than 10% of frames ran late, and that music and effects played. Every stage was also recorded on its own to check the tile layers against the original's maps.

Real hardware path

The cartridge image runs under MAME's Neo Geo MVS emulation and on a JAMMA cabinet through Recalbox. It uses only documented Neo Geo features; no flash cart or custom hardware is involved.