R-Type NeoNeo Geo portR-Type Plus
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.

The port is not downloadable. This page shows a technique and its results. It does not distribute ROMs, game data, artwork, sound, the port's binaries or its source code, and no files are sent on request. R-Type is the property of its rights holders; see the legal note.
New: Boss Rush. The bosses back to back, every ship fully armed, against a run timer, with its own ranking saved in the cabinet's backup memory. See it below; it also plays on the arcade original.

In motion

Direct captures of the port's video and sound at the console's native 320×224, scaled 4× with no filtering; the sound is the Neo Geo's YM2610 playing the port's FM driver. The play is scripted: a run is replayed from power-on with new steering every 90 frames until it goes through without a lost ship, then captured.

100 s Two-player co-op with the weighted view camera: a 2P start from the title, the fly-in with the 1P and 2P tags, the beam gauge split between the players, stage 1 up to its green walker, then stage 3 from its fly-in to the warship. Watch the window lean toward the enemies while both ships stay in view.

71 s Armour mode, one player, with the weighted view camera: ARMOUR picked on the start screen, then stage 1 where a hit takes the gauge from green to yellow without ending the game, and stage 2's cave where two more take it to red, the worms on the last ship.

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. With two players the beam gauge is split: player 1's charge fills from the left in the original's blue, player 2's from the right in crimson.
  • 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.

Armour mode

A second rule set for the cabinet, picked on the start screen with up or down next to the stage select (the CREDIT line reads CLASSIC or ARMOUR; the choice stays until changed). The original's rule is unforgiving: one hit, one ship, back to the checkpoint. In Armour a hit with more than one ship left costs a ship and one level of the Force's laser, and the game goes on where it is: the ship blinks for two seconds, cannot be hit and passes through terrain, then play resumes. The last ship's hit is the original's death, with its game over, continue and checkpoint. In co-op each player has their own ships.

What changes on screen

  • The ship icons give way to a gauge in the beam gauge's style: one cell per starting ship, green with three ships, yellow with two, red with the last. The cell a hit took blinks for the two seconds.
  • A hit flashes the shield burst over the ship and plays the original's bonus cue instead of the death explosion.
  • The Force keeps its speed and bits; at laser level 1 a hit takes the Force itself.
  • Player 2's gauge mirrors player 1's from the right side of the HUD.

How it stays the original's code

The hit routine is hooked at one address: with ships to spare it decrements the ship count and the laser level, sets the original's own "just in" state that it uses after a fly-in (blink, no hits, terrain pass-through) for 120 frames, and returns before the death path. Everything else, including what happens when the grace runs out inside a wall, is the original's behaviour.

Start screen with STAGE 1 ARMOUR on the credit line
The start screen: STAGE 1, ARMOUR.
Stage 1 with a green three-cell gauge in the HUD
Three ships: the gauge is green.
The ship hit, a shield burst over it
A hit: the shield burst, the game goes on.
Two cells left, the gauge yellow
Two ships left: yellow, the lost cell blinking.
One cell left, the gauge red, the ship blinking
The last ship: red.
The stage 1 boss fought on the last ship
The stage 1 boss on the last ship.

Boss Rush

The third mode on the start screen, after CLASSIC and ARMOUR: the seven bosses one after another, every ship fully armed, against the clock. The rules are the arcade version's, played by the same translated program.

The run

  • Every new ship, a respawn, a join or a rejoin included, starts with the Force at full power on its front, the blue reflecting laser, two bits, homing missiles and a speed-up.
  • Each fight begins a few seconds before its boss, so its entrance and music play; a lost ship restarts there, and a continue too.
  • After a boss falls, the game's own stage clear plays, then the next fight begins. Stage 3 is left out: its boss is the giant warship itself, which starts with the stage and cannot be entered halfway.
  • Classic rules otherwise: one hit, one ship; GAME OVER and the continue as the original's. One or two players.

The timer

The HUD's score row shows the run's time, mm:ss.cc, where the high score sits in a normal game. It counts the Neo Geo's own frames, real time: when the game slows down in a busy fight, that counts too. It runs through deaths, pauses from GAME OVER through the continue countdown, holds from a boss's fall to the next fight, and stops at the last boss, its total on screen through the ending.

The ranking

When a run ends, a panel lists each boss's time, the total, the bosses beaten and the continues used, and the name entry follows. Runs rank by bosses beaten, then fewer continues, then time, in a table of their own: Boss Rush scores stay out of the game's ranking and high score. The attract mode shows both tables in turn; a two-player run is marked 2P in player 2's crimson.

Both tables survive power-off in the MVS's backup RAM. The BIOS keeps nothing of a game's work RAM there, so the port writes its own block in the cabinet's save slot: two copies, each with a sequence number and a checksum, so a power cut in the middle of a write leaves the other one.

Boss Rush: the stage 1 boss fought with the full loadout, the run timer at 00:29.77 in the HUD
Boss 1, fully armed: the timer where HI usually is.
Boss Rush: the stage 2 boss, the timer at 01:16.06
Boss 2, a minute and a quarter in.
The run's panel: each stage's time, the total, bosses and continues, then the name entry
The run's end: each boss's time, the total, then the initials.
The Boss Rush ranking table: bosses, continues, time and name for ten places
Boss Rush's own table, in the attract mode.
The Boss Rush table with a new entry highlighted at No. 4 and marked 2P in crimson
A two-player run ranked fourth: highlighted, marked 2P.

Fitting the arcade's screen

The M72 draws 384×256 pixels at 55 Hz; the Neo Geo shows 320×224 at 59.19 Hz. Keeping the arcade's picture on the smaller screen was the one place where the port could not simply replace hardware, and every option cost something. The answer, in two steps: a window that keeps the ship whole, then one that also looks where the enemies are.

What was tried and rejected

  • Shrinking the picture. The Neo Geo can shrink sprites, but it does so by dropping fixed pixel columns, one in sixteen at best, and every column sprite of the tile layers would need its own width. Thin vertical details vanish and the ship ends up narrower than on the cabinet, whose pixels are already narrower than the Neo Geo's. Rejected: the pixels stay 1:1.
  • A fixed window. The first builds showed a fixed 320×208 window of the 384×240 playfield, 32 pixels in from each side and 24 lines down. Game logic, spawns and hitboxes stayed in the arcade's coordinates, so nothing was lost, but the player's ship can fly to x 13 and line 5 of that playfield: at the back it was cut in half, at the top it vanished, and enemies the arcade spawns in plain view sat in the hidden strip on the right and shot "from nowhere".

The camera

The window now slides over the playfield to follow the ship, within limits: 16 pixels either way sideways and the full 32 lines vertically, easing in and out at up to three quarters of a pixel per frame. At rest it is the original fixed window, so the game looks the same until the ship pushes an edge. At the back the ship is whole bar four pixels, at the top and bottom it is whole; the price is that the hidden strip moves to the far side while the player is retreating. Only a playing ship moves it: fly-ins, deaths, the attract demo, the text screens and a stage clear (from the boss's fall, so STAGE CLEARED is centred) stay at rest, and in co-op the window follows whichever ship is furthest back and highest.

In the program this is two words, the window's offset, folded into the tile layers' scroll values and subtracted from each sprite's position in the VBlank drawing. The original's code never sees it, and the HUD band on the fix layer does not move.

Where it looks

Following the ship alone still left the window pointing at empty space while enemies sat in the hidden strip. So within the range the ship allows, the window now settles where it shows the most of the original's enemy list: each enemy counts 3 per visible pixel, each bullet 1, the starfield and explosions nothing. The score of every candidate position is summed in one pass over the 94 object records, and the target only moves when another position shows two thirds of an enemy more, so the window repositions every few seconds rather than drifting. It glides there: its speed builds up by a thirty-second of a pixel each frame to three quarters of a pixel, and it brakes in time to stop on the target. Only when the ship has moved past its margin faster than that does the window follow it at up to four pixels a frame, which keeps the ship whole on a fast retreat.

The rule was chosen by measurement: a scripted run of the arcade original was recorded with its object list every frame and replayed through candidate rules offline. Against following the ship, the weighted window leaves about a third fewer enemies fully out of view on stage 1 and 2 and a sixth fewer over the whole game, with the same number of direction changes. Which of the original's object handlers are enemies, bullets, explosions or invisible controllers is data in a text file, profiled from a play-through of all eight stages, and a model in the test suite recomputes the camera's target from the same records after every frame and compares.

The arcade frame with two outlined windows, then the fixed window with the ship cut at the left edge, then the camera window with the ship whole
Top: one arcade frame, 384×256, with the fixed window in red and the camera's in blue, the ship at the back. Middle: the fixed window, the ship cut in half. Bottom: the camera window.
The same comparison with the ship at the top of the playfield, hidden by the fixed window and shown by the camera
The ship at the top of the playfield: hidden entirely by the fixed window, shown by the camera.

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
Window320×208 of the arcade's 384×240 playfield at 1:1, bounded by the ship (24 px / 12 lines from its edges) over 64 pixels of travel sideways and 32 lines vertically, aimed at the most enemy pixels, one pixel per frame; the targeting costs about 5 of the 264 scanlines a frame
Tested onMAME 0.289 and a JAMMA arcade cabinet (libretro MAME 0.258)

Screenshots

Native 320×224 frames, 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 scores live, the beam gauge split between the players.
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: the green walker ahead, both ships behind it
Stage 1's green walker: the window has leaned ahead toward it while keeping both ships.
STAGE 1 CLEARED and the stage score, centred, the ship below
Stage clear: the window back at rest, the text centred.
Stage 1 boss rising
The stage 1 boss rises.
Stage 1 boss fight
Boss fight with both ships.
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 worms, further along
Stage 2, further in: the worms, on the last Armour ship.
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. A camera run pushes the ship to every edge and checks the window's stops, its one-pixel-per-frame motion, that the ship is drawn at the back, and that it rests after a death and through the attract demo. 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 arcade cabinet. It uses only documented Neo Geo features; no flash cart or custom hardware is involved.

Packaging mock-ups

These are concept mock-ups, not products. No box, cartridge or label was ever made, and none will be. The images were generated with an AI image model from the port's own screenshots (the title logo, the two ships, the stage 1 boss) to imagine what a home-console release might have looked like, in the style of late-1980s airbrushed arcade flyers. The cartridge shell was drawn from a public-domain photograph of a Neo Geo AES cartridge (Evan-Amos, Wikimedia Commons). Any resemblance to real packaging of the period is the point of the exercise, not a claim.

Mock-up of an AES-style box front showing two R-9 ships facing the stage 1 boss
Mock-up: a snap-case box front, the two ships against the stage 1 boss.
Mock-up of a black AES-style cartridge with the R-Type logo and a gold NEO mark on its label
Mock-up: the cartridge, label from the port's title screen.
Mock-up of the box and cartridge staged together
Mock-up: box and cartridge staged together.