01THREE A SIDE
Three picks, one on point.
Each side picks three in turn, and in six-player mode each player picks their own. No character twice in one team; the same character on both teams is fine.
X-Men vs. Street Fighter · CPS-2
Arcade X-Men vs. Street Fighter is two on two. This makes it three on three: pick three characters, tag either reserve in, chain one Hyper into the next with a Delayed Hyper Combo, and snap the other team's point out.
It's a patch for your own ROM, the Europe 961004 set (xmvsf), with a RetroArch core included. Two players, or six: one controller per character, P1, P2 and P3 against P4, P5 and P6. The original screen, or a wide arena at 512×224. And there's a two-player build for Fightcade, still a preview.
v1.1.1, preview. v1.1.1 fixes LK and MK pressed together in the two-player game while RetroArch's SOCD option is on, and the Mac launchers now bring RetroArch to the front; v1.1 added eighteen colours a fighter and HP+HK cycling through the whole team. It's still a preview: every run so far is scripted, with ordinary controller inputs, on the Linux core and its diagnostic build, apart from one Mac run of the two-player package. Not run yet: Windows PowerShell 5.1 on Windows, the other macOS runs, physical controllers, and the Fightcade client or online play. The third-tag fix for the reports on v1.0.0 hasn't been confirmed by the players who sent them, and the choppy snapback switch isn't changed yet. One refusal, a Team Hyper whose partner slot is still in use, hasn't come up in a run. The RetroArch packages need their included core: stock FinalBurn Neo, MAME and Fightcade can't run them, and there's no arcade-board version.
Before you play: Control card · What's been checked, and what's open · Where the colours come from · Package receipt · The core's source.
$NEW · start here

Each side picks three characters. One fights, the point; the other two wait as reserve A and reserve B, in the order you picked them. All three count for the timer, and a team is out when all three are down.

A Delayed Hyper Combo. During your Hyper, enter the next member's Hyper motion and finish it with MP+MK or MP+HK: they come in with their own Hyper and stay in. Each Hyper costs its own meter.

QCF+MP+MK or QCF+MP+HK, for one level. A clean hit forces the other team's point out and drags the reserve you named in, so you pick who you fight next.
Three a side, Delayed Hyper Combos, snapbacks, six controllers and the wide arena, in three minutes.
Three minutes, 1440p, with chapters. Every game shot is the v1.0.0 build in FinalBurn Neo with ordinary inputs, frame for frame at the board's 59.63 Hz; the numbers on the rails are the game's own RAM from each take. The board scenes are a drawing, with counts measured from the 68000 while the takes were recorded. The game's strobes are softened, and the video's description has the photosensitivity note. Music: Moment 37 (Less Power Mix) by morphus56k. Narration: an ElevenLabs voice. The video is a cut, not proof: the receipts are.
Ten pieces. Every clip says which build it shows and how it was recorded.
01THREE A SIDE
Each side picks three in turn, and in six-player mode each player picks their own. No character twice in one team; the same character on both teams is fine.
02TAGS
MP+MK brings in reserve A and MP+HK reserve B. HP+HK, the game's own tag, brings in the next one still alive: first to second, second to third, third to first. A reserve that's down keeps its place, and a request never swaps in the other one. Reserve order follows the original picks excluding the current point. Release MP, MK and HK before the next direct request; after an ambiguous MP+MK+HK, release all three.
03THE THIRD
When the point is knocked out, the partner the game keeps ready comes in if it's alive, as in the arcade game. If it isn't, the third member drops in instead.
04DELAYED HYPER COMBO
During your Hyper, enter the next member's Hyper motion and finish it with MP+MK or MP+HK. They come in with their own Hyper and stay in. Each Hyper costs its own meter, and each character joins a chain once. This is sequential; the separate QCF+HP+HK Team Hyper starts simultaneous attacks for two levels.
05SNAPBACK
QCF+MP+MK or QCF+MP+HK for one level, from neutral or cancelled from a ground normal that made contact. A clean hit forces the point out and the reserve you named in. Blocked or missed, the level is still spent.
06TEAM HYPER
QCF+HP+HK with two levels for both Hypers at once, and the Variable Counter while blocking, as the arcade game has them. The partner is your first living reserve.
v1.1 build · drawn from its colour table0718 COLOURS
Confirm with a button for colours 1 to 6, or with a punch and its kick together for 7 to 9. Hold Start with any of those nine for 10 to 18. If the other team already has that character in that colour, you get the next free one. Chun-Li keeps Start for her other outfit, so she has nine colours in each.
08ARCADE
Every CPU team up to Apocalypse has three members, and Apocalypse stays a solo boss. The partner battle after him stays as the game has it.
09WIDE ARENA
The WIDE launcher opens a 512×224 screen with 64 more columns on each side. Stages keep their ends and fighters their limits: you see more of each stage, and fighters can stand further apart.
10SIX PLAYERS
P1, P2 and P3 against P4, P5 and P6. The point's owner tags, starts DHCs and snapbacks; the incoming character is played by its own owner, and waiting players' buttons do nothing in combat.
Pick one, then the download for your computer. A RetroArch package holds the installer, the core and both screens; the Fightcade package holds an installer for Fightcade's own emulator.
P1 against P2, a team of three each, on one computer.
Needs: RetroArch (free) and your own clean xmvsf.zip (Europe 961004) with its key file
Download · Windows x86-64 · XMvsSF-3v3-DHC-v1.1.1-2P-windows-x86_64.zip · 4.1 MB · SHA-256 c221126706fc…Download · macOS Apple silicon · XMvsSF-3v3-DHC-v1.1.1-2P-macos-arm64.zip · 4.1 MB · SHA-256 3c9155facef8…Download · Linux x86-64 · XMvsSF-3v3-DHC-v1.1.1-2P-linux-x86_64.zip · 4.3 MB · SHA-256 b8b3bfd99fe2…Install it ↓Six controllers on one computer: P1, P2 and P3 against P4, P5 and P6.
Needs: RetroArch (free), your own clean xmvsf.zip (Europe 961004) with its key file, and six controllers
Download · Windows x86-64 · XMvsSF-3v3-DHC-v1.1.1-6P-windows-x86_64.zip · 4.1 MB · SHA-256 b9106213b53c…Download · macOS Apple silicon · XMvsSF-3v3-DHC-v1.1.1-6P-macos-arm64.zip · 4.1 MB · SHA-256 9035019560d1…Download · Linux x86-64 · XMvsSF-3v3-DHC-v1.1.1-6P-linux-x86_64.zip · 4.3 MB · SHA-256 c951c666e279…Install it ↓P1 against P2 on Fightcade's own FinalBurn Neo, with no extra core. A preview: not tried online yet.
Needs: Fightcade, your own clean xmvsf.zip (Europe 961004) with its key file, and an opponent with the same build code
Download · Windows, macOS, Linux one package · XMvsSF-3v3-DHC-Fightcade-v1.1.1-2P.zip · 284 KB · SHA-256 e8572f465583…Install it for Fightcade ↓Unzip the package, give the installer your xmvsf.zip, then start a launcher. The installer writes new files in the package folder and never changes your ZIP.
What you need: RetroArch (free), and your own clean X-Men vs. Street Fighter ZIP, xmvsf.zip: the Europe 961004 parent set that MAME, FinalBurn Neo and Fightcade use, with xmvsf.key inside. The packages hold no game data. The launchers start RetroArch with the package's own core and settings and leave your RetroArch configuration alone.
OriginalPLAY XMvsSF 3v3 2P (or 6P) runs the original 384×224 screen, at 4:3.
WidePLAY XMvsSF 3v3 2P WIDE (or 6P WIDE) runs the 512×224 arena at 16:9, from its own patched files in the package's system folder. Save states work within one screen.
One package for Windows, macOS and Linux. It builds a whole xmvsf.zip for Fightcade's ROMs folder from yours, and both players compare the build code it prints.
What you need: Fightcade, your own clean xmvsf.zip (the Europe 961004 set, with xmvsf.key inside), and on macOS or Linux, Python 3.8 or newer. The package holds no game data. Your opponent needs the same build: its code is f98f-bfea-1816-8d9a.
Going backIn Fightcade's ROMs folder, delete xmvsf.zip and rename xmvsf.zip.original back to xmvsf.zip. The US, Japan, Asia and Brazil versions share two of the changed files with xmvsf.zip and may fail their start-up check while this build is in place (not tested), so put the original back to play them. If your xmvsf.zip was a merged set holding those versions too, keep the original: the built file holds only the Europe 961004 set.
What's differentTwo players only, on the original 384×224 screen: the six-player mode, the wide arena and the miniature faces on the life bars need the RetroArch core. The rest is the same engine as the RetroArch builds: three a side, tags, KO hand-offs, Delayed Hyper Combos, snapbacks, Team Hyper, the Variable Counter, eighteen colours and CPU teams of three.
What was testedFightcade's emulator source (fightcade-fbneo c959501), run without its window, with scripted controller inputs and its states saved and loaded the way Fightcade's GGPO code does: every 61 frames the last 8 were loaded back and run again. On v1.0.0's build: 35 runs, 1,128,037 frames and 18,407 rollbacks, with no difference in the game state. Tags, KO hand-offs, Team Hyper, the Variable Counter, CPU third members, DHCs, snapbacks, colours, the HUD, continues and rematches pass there; an arcade run beat Apocalypse, but the test bot never won the partner battle after him. It boots and tags in MAME 0.264. On v1.1's build: five more runs with rollbacks on, for HP+HK cycling and every colour input, the Start ones included, and a match started from a state saved on the stock game, which the build now sets itself up from. Both times, the installer's PowerShell ran under PowerShell 7 on Linux and wrote the same file and build code as the Python installer.
Not tested yetThe Fightcade client itself, an online match between two machines, Fightcade's lobby with a changed xmvsf.zip, ranked matches, the Windows installer under Windows PowerShell 5.1 on Windows, the installer on a Mac, and physical controllers. Until those run, it's a preview.
| What you see | Why | What to do |
|---|---|---|
| NOT INSTALLED: ... not the Europe 961004 revision | This build needs the parent set xmvsf (Europe 961004). The US, Japan, Asia and Brazil versions have different program chips. | Use a clean xmvsf.zip of the Europe 961004 set, with xmvsf.key inside. |
| This ZIP is already patched | You gave the installer the built file, the one now in Fightcade's ROMs folder. | Give it your original, xmvsf.zip.original. |
| FinalBurn Neo warns about files with the wrong CRC | Three files are changed, so their CRCs differ from the stock set's. It's expected. | Misc > Options > Hide ROM Warnings in the emulator window. |
| The match desyncs | The two players don't have the same build, or one has the stock game. | Compare build codes: both need f98f-bfea-1816-8d9a. Give the installer the file in Fightcade's ROMs folder to see its code. |
| The game plays two on two | Fightcade loaded another xmvsf.zip, or the channel is for another version of the game. With v1.0.0, a stock per-game saved state could also skip the mod's startup; v1.1 sets itself up from one, which hasn't been tried in the Fightcade client yet. | Check the file in Fightcade/emulator/fbneo/ROMs with the installer, and play in a channel for the set xmvsf. Back up xmvsf_fbneo.fs and xmvsf_fbneo_ranked.fs if present in savestates, move the backups outside, then try a fresh boot. Keep them to restore stock. |
| Windows protected your PC | SmartScreen doesn't know the installer yet. | More info > Run anyway. The installer only writes into its own folder. |
RetroArch hands controllers to ports in the order it finds them. Each port is one player, and each player plays the character they pick.
| RetroPad | Arcade | Xbox pad |
|---|---|---|
| Y | Light punch | X |
| X | Medium punch | Y |
| L | Heavy punch | LB |
| B | Light kick | A |
| A | Medium kick | B |
| R | Heavy kick | RB |
| Start | Start | Start |
| Select | Coin | Back |
| What you see | Why | What to do |
|---|---|---|
| NOT INSTALLED: ... not the Europe 961004 revision | This release needs the parent set xmvsf (Europe 961004). The US, Japan, Asia and Brazil clones have different program chips. | Use a clean xmvsf.zip of the Europe 961004 set, the one MAME, FinalBurn Neo and Fightcade use. |
| xmvsf.key is missing | CPS-2 games need their key file, and your ZIP doesn't have it. | Use a current MAME or FinalBurn Neo set that includes xmvsf.key. |
| already exists and is not this release's file | A file from another version or a hand-made copy is where the installer writes. | Move the file it names (in roms or system) out of the folder, then install again. |
| RetroArch starts without the game | The launcher couldn't use a file or a setting, and it says which in its window. | Run the launcher again and read its window. If RetroArch is somewhere unusual, put the full path of its program on the first line of retroarch-path.txt. |
| RetroArch says the ROM is unknown, or the game plays two on two | RetroArch's own FinalBurn Neo core, or the files were started by hand without the package's settings. | Start the game with a PLAY launcher: it loads the included core and switches on the setting that reads the patched files. Don't rename or move them. |
| A punch and kick pair gives the first button's colour | The two buttons landed more than four frames apart, so the first one confirmed on its own. | Press the punch and its kick of the same strength at the same moment. For colours 10 to 18, hold Start before you press, or press it within those four frames. |
| The faces on the HUD show the LK colour | A core from v1.0.0 can't read v1.1's colour table. The fighters themselves still use the colour you picked. | Start the game with the v1.1 package's PLAY launcher, which loads the v1.1 core. |
| The wrong controller drives a character | RetroArch hands controllers to ports in the order it finds them. | F1 > Settings > Input > Port 1..6 Controls > Device Index. |
| macOS says the file can't be opened | Files from a browser are quarantined, and the .command files aren't signed. | Right-click the file > Open, then Open again. The launcher clears the quarantine from its own folder so RetroArch can load the core. |
| Windows protected your PC | SmartScreen doesn't know the installer yet. | More info > Run anyway. The installer only writes into its own folder. |
Only the included coreThe RetroArch packages need the core in the package: it adds the six-player input and the memory the three-member teams use. Stock FinalBurn Neo, MAME and Fightcade can't run those files, and there's no arcade board version. The Fightcade build is separate: it keeps the teams in memory FinalBurn Neo already has, for two players on the original screen.
By handThe installer is xmvsf3v3_patch.py (Python 3.8 or newer) on every system: python3 xmvsf3v3_patch.py /path/to/xmvsf.zip writes the same three files. The launchers start RetroArch with -L and the package's core, --appendconfig and the package's settings, which switch on FinalBurn Neo's “Allow patched romsets” and point it at the chosen screen's system folder, and roms/xmvsf6pd.zip.
Known limitsv1.1.1 fixes LK and MK pressed together in the two-player game while RetroArch's SOCD option is on, and the Mac launchers now bring RetroArch to the front; v1.1 added eighteen colours a fighter and HP+HK cycling through the whole team. It's still a preview: every run so far is scripted, with ordinary controller inputs, on the Linux core and its diagnostic build, apart from one Mac run of the two-player package. Not run yet: Windows PowerShell 5.1 on Windows, the other macOS runs, physical controllers, and the Fightcade client or online play. The third-tag fix for the reports on v1.0.0 hasn't been confirmed by the players who sent them, and the choppy snapback switch isn't changed yet. One refusal, a Team Hyper whose partner slot is still in use, hasn't come up in a run. The RetroArch packages need their included core: stock FinalBurn Neo, MAME and Fightcade can't run them, and there's no arcade-board version.
One line and the link. Copy it, or post it straight to X.
ready to post
XMvsSF 3v3 + DHC v1.1 for arcade X-Men vs. Street Fighter: three characters a side, tags to either reserve, Delayed Hyper Combos, snapbacks, eighteen colours and six players, in a wide arena if you like, plus a two-player build for Fightcade. A free patch for your own ROM.
Related: Cross Fever, four players in XMvsSF, MSHvsSF and MVC1 · MSH TAG, tag teams for Marvel Super Heroes · XCOTA TAG, tag teams for X-Men: Children of the Atom.
The current download is v1.1.1, still a preview. It fixes v1.1's RetroArch core: with its SOCD option on, LK and MK pressed on the same frame didn't both reach the two-player game. The Mac launchers also bring RetroArch to the front now. v1.0.0 and its r2 guide revision are withdrawn: v1.1 replaces them. The footage below shows v1.0.0, apart from the colour still. The preview checks list what has been checked and what is still open, including the snapback's choppy switch, which v1.1 doesn't change yet.
In the Delayed Hyper Combo post I wrote down the limit I couldn't route around in arcade Marvel vs. Capcom. Its teams are two fighters, so a Hyper can hand off once. MvC2's teams are three, so its chain goes three Hypers deep. The most MvC1 could get was MvC2's choice of Hyper, never its chain, and I left the choice out of v8.
X-Men vs. Street Fighter (1996) is where the Vs. series' tag teams started: two characters a side, HP+HK to swap, and a knocked-out point handing over to its partner. XMvsSF 3v3 + DHC gives each side a third character. With three, a Delayed Hyper Combo goes up to three Hypers deep, and the motion you enter picks which Hyper the next character comes in with. That's the chain and the choice, in the 1996 game.
The rest of the three-a-side game comes with it. MP+MK and MP+HK tag in the reserve you name, HP+HK (the arcade game's own tag) cycles through the whole team, and a snapback forces the other team's point out and drags in the reserve you pick. The arcade game's Team Hyper and Variable Counter keep their commands and costs.
It's a patch for your own ROM, the Europe 961004 set the emulators call xmvsf.
It runs in a RetroArch core that comes in the download, for two players or six,
one controller per character, on the original screen or in a 512×224 arena. The
trailer shows all of it in three minutes. There's also a two-player
build for Fightcade that needs no extra core, still a preview.
Below is how it works, and what hasn't been tested yet.
On this page: three a side · six controllers · Hyper into Hyper · snapbacks · the HUD · 18 colours · arcade mode · the wide arena · where the patch lives · what got tested · Fightcade
X-Men vs. Street Fighter calls its two-character Hyper the Variable Combination, and the move that brings the partner in out of a block the Variable Counter. I call the first one the Team Hyper here, like the rest of the page. DHC is MvC2's name, Delayed Hyper Combo, the same one the MvC1 patch uses, and the snapback comes from MvC2 too.
There are no assists, and nothing is rebalanced: damage, meter gain, the meter's cap, red-life recovery and every character's moves are the arcade game's. There is no air snapback, no timed Duo and no new character.
MvC2 made the Vs. series three a side, and its team play is built on that. A DHC chain is as long as the team. A snapback decides which of the other team's two reserves you fight next; against two a side there's only ever one.
X-Men vs. Street Fighter already has the tag-team pieces the later games built on: the HP+HK tag, the KO hand-off, the Team Hyper and the Variable Counter. Cross Fever put it on one board for four people, two on two, with MVC1's Duo. This is a different patch, and Cross Fever's XMvsSF builds stay as they are. Three characters a side makes six in a match, so the six-player version gives each one a player of its own.
I wanted three a side without turning it into a different game. The arcade game's own moves keep their inputs and their costs, and every new move in a fight uses MP+MK or MP+HK.


X-Men vs. Street Fighter runs four fighters: each side's point and partner, in
slots of $400 bytes at $FF4000, $FF4400, $FF4800 and $FF4C00. The
game's own variables start right after them, at $FF5000, so there's no room
for a fifth slot. Three a side doesn't need one: a side never has more than two
characters on screen, and the partner slot already holds whoever a tag or a KO
brings in next.
The code counts to two in more places than the slots. Here are three of them, decrypted (the CPS-2 encrypts its program; these are the instructions the 68000, the board's processor, actually runs):
; setting up a fighter: which of the side's two picks?
00FFFA 4A2E 0220 tst.b $220(a6) ; pick 0, or not?
00FFFE 6702 beq.s $010002 ; 0: keep the first
010000 C141 exg d0,d1 ; not 0: take the second
; a knocked-out point: anyone to send in?
00D588 206E 0224 movea.l $224(a6),a0 ; the other slot
00D58C 4A68 0210 tst.w $210(a0) ; its life
00D590 6710 beq.s $00D5A2 ; 0: nobody comes in
; a helper whose owner's slot changed character
010358 206E 0054 movea.l $54(a6),a0 ; its owner's slot
01035C 302E 0052 move.w $52(a6),d0 ; who made it
010360 B068 0052 cmp.w $52(a0),d0 ; still there?
010364 6718 beq.s $01037E
010366 2068 0224 movea.l $224(a0),a0 ; no: the other slot
01036A 2D48 0054 move.l a0,$54(a6) ; re-own it thereAt $FFFA, setting up a fighter, the game asks one question about which pick it
is: zero, or not zero. Zero gets the side's first character and anything else
the second, so writing a 2 there doesn't get you a third character; it gets you
the second one again. The patch replaces those three instructions with a lookup
that can return any of the three.
At $D588, in one of the game's KO paths, a knocked-out point looks at exactly
one fighter, the other slot on its side. If that one's life is zero, nobody
comes in. The patch uses this path for the third member, as the next section
shows.
At $010358, a helper object (the kind Cyclops, M. Bison and Sabretooth use)
checks that its owner's slot still holds the character that made it. If it
doesn't, the object decides the owner must be in the other slot, because with
two a side there's nowhere else, and moves itself there. With three a side, the
other slot can hold a different character altogether, and the object would end
up belonging to them.
So the third member lives outside the game's own RAM. The included core adds 64
KiB at $810000, which the CPS-2 board doesn't have, and each side gets a
64-byte team record, at $810100 and $810140: a 16-byte header and 16 bytes
per member, with its character, its life, its colour, its red-life ceiling and
its recovery countdown. A record isn't a frozen copy of a fighter. When the
third member is needed, the patch writes those fields into the partner slot,
runs the game's own setup on it, and lets the stock tag or KO entry bring it in,
the way any partner comes in.
It only moves into the slot once nothing still refers to the character there. The patch first walks the game's object lists, and if a live object still points at the slot's resident, the request is refused. It isn't queued, it isn't redirected to the other reserve, and nothing is deleted to make room.
The game's own RAM gets no new fields. The patch reads and writes only fields the game itself uses, and keeps the rest in the core's memory. A third member waiting off-slot recovers red life at the same pace as the partner on the bench. Each fight starts by clearing the team records and building all three members fresh from the picks, and a continue starts from new picks. Character select makes a third pass through the same grid and refuses a character the team already has, Chun-Li's alternate identity included.
When both characters in a side's slots are down and the third is alive, the
patch sends the knocked-out point down a path the stock code already has for a
delayed replacement: wait 60 frames, then swap. On the way, that path spawns an
object that shows the fallen fighter lying there, blinking, until it's removed.
The project calls it the KO ghost: object type $50, subtype 2 (move.w #2,d0
at $D5AA, then the spawner at $4BF54). The arcade game doesn't use this path
for an ordinary KO. In a stock MAME run with Chun-Li knocked out and Wolverine
alive, the swap comes from another routine and no ghost is ever created.
Earlier KO tests passed. A full arcade run found the problem in its fifth fight,
when Chun-Li went down: from frame 15,277 the game ran at about a twenty-first
of its speed. The main loop's counter ($FF501C) moved once every 21 frames or
so, and the 68000 was always inside the sprite builder.
Subtype 2 finds its state handlers through a table the code reads relative to
the program counter (its address is counted from the instruction that reads it),
at $4C0FE: 0008 002C 0128 012E. The first two entries are right. The last
two are subtype 0's offsets. From subtype 0's own table at $4C05C they point
at $4C184 and $4C18A, "advance" and "retire". Added to $4C0FE instead,
they land at $4C226 and $4C22C, in the middle of the routine for a different
object, a type-$54 mirror image.
So once the ghost reached state 4, it ran the mirror's code. That code took two
words of the ROM, at $4C042 and $4C046, as the ghost's position ($6934,
$7C21: nowhere near the stage), and it stepped Chun-Li's lying-down animation
with the step routine for objects instead of the one for fighters. The two lay
out their records differently. The object stepper read a field of her frame as
the address of the next one and got $80800000; from there the frame pointer
became $FFFFFFFF, and the sprite builder looped on a garbage count every
frame.
The fix is one hook, a jump the patch writes over a few of the game's own instructions, where subtype 2 picks its state. States 0 and 2 go where they always went. States 4 and 6 go to the advance and retire that the other ghosts, and this ghost's own shadow, already use. It still lies there for 60 frames and blinks, and then it's gone.
Here is the same test, with the same inputs up to the ghost's state 4, on the build before the fix and on the build with it. The numbers are main-loop passes in each stretch of 240 frames after the ghost appears; 240 is full speed, one pass a frame.
| After the ghost appears | Before the fix | With the hook |
|---|---|---|
| 1st 240 frames | 240 | 240 |
| 2nd 240 frames | 136 | 240 |
| 3rd 240 frames | 11 | 240 |
| 4th 240 frames | 13 | 240 |
| 5th 240 frames | 11 | 240 |
The arcade game reads two players. The included core adds a read-only window at
$808000 with six: each player's four directions, six buttons, Start and Coin,
72 switches in all, plus what was pressed and released since the last sample and
a history 16 samples deep. A capability word at $80804C says what the running
core adds, and the ROM checks it before it uses any of it.
The core only carries the buttons. The ROM decides who controls what: the left
team's picks belong to P1, P2 and P3 in pick order, the right team's to P4, P5
and P6 (owner = 3 × side + pick, counting from zero), and that never changes
during a match. The point's owner is the one who can tag, start a DHC or a
snapback, or call the Team Hyper. The incoming character answers to its own
player, and a waiting player's buttons reach nothing in combat: one test presses
chords on every bench and checks that no request, swap or input changes. In the
menus, P1 and P4 do what the arcade's P1 and P2 do.
One six-player test failed on its fifth tag. At frame 4,261 a fresh MP+HK went in, and instead of a tag the fighter threw a normal attack. The tag check paired two things: the input the game had last stored in the fighter, and the core's current sample number. Each is right on its own, but they can come from different samples. The game's work for one frame can run across the boundary between two of the core's frames, and the new HUD code had moved where that boundary fell. The check had already filed sample 4,261 with nothing held, so the new press looked like a repeat and was thrown away.
The fix gives each fighter slot its own counter, four of them at
$81020C–$81021B, bumped exactly when the game stores a new input sample in
that fighter. The tag check uses that counter instead of the core's. The same
4,503 frames of scripted input that failed now start the tag at 4,261, and
holding the chord doesn't start a second one.
The patched game uses memory and inputs no CPS-2 board has: the six-player
window at $808000 and 64 KiB of RAM at $810000. The core in the package adds
both, and the expanded view for the wide arena. Stock FinalBurn Neo, MAME and
Fightcade don't, so they can't run it, and there's no version for the arcade
board or for online play.
That core is an unofficial build of FinalBurn Neo's libretro port, and the FinalBurn Neo team doesn't support it: please report problems with it here, not to them. Its complete source is the xmvsf3v3-v1.1.1 release of its public fork, under FinalBurn Neo's licence. I've also offered the driver to FinalBurn Neo itself, as four sets of its own, in pull request #2798; until it's merged, play with the package's core.

A DHC starts during your Hyper. You enter the next character's Hyper motion and finish it with MP+MK for reserve A or MP+HK for reserve B. Your character leaves, the next one comes in doing that Hyper, and stays in. The motion is read relative to the way your character faces, since the incoming one isn't on screen yet.
The motion picks the Hyper, the way it does in MvC2. A few of the table's rows:
| Incoming | Motion + MP+MK or MP+HK | Their Hyper |
|---|---|---|
| Wolverine | QCF | Berserker Barrage X |
| Wolverine | DP | Weapon X |
| Cyclops | QCF | Mega Optic Blast |
| Cyclops | QCB | Super Optic Blast |
| Akuma | QCB | Messatsu Gou Hadou |
| Akuma | RDP | Messatsu Gou Shoryu |
| Zangief | QCF | Final Atomic Buster (ends the chain) |
The motion isn't always the Hyper's own command. Zangief's Final Atomic Buster is a 360 on its own and a quarter circle in a chain. Every character's motions are on the control card in each package.
In the chain test, Ryu's Shinkuu Hadouken goes into Ken's Shoryureppa (QCF + MP+MK), Ken's goes into Chun-Li's Kikoushou (QCF + MP+HK, with Chun-Li brought in from off-slot), and a request back to Ryu is refused because he's already been in that chain. Each Hyper spent one level. A chain goes A to B to C, never back to A.
Each step of a motion, and the button press after it, gets 14 input samples. The
game's own motion reader gives each step 12 to 17 frames, drawn from its random
number generator at $1608. The DHC reader never touches the generator, so it
can't change the random numbers the rest of the game gets; 14 is the game's own
button window, at $13D2C. Its clock counts the core's input samples, so the
game's Turbo speed, which can run more than one update per frame, doesn't
shorten it. When more than one motion finishes, the latest one wins, and on the
same sample the longer one does (a half circle over a quarter circle).
The window opens on the first frame after the Hyper's freeze on which it has an attack box, on the fighter or on an object carrying its character, and closes 8 frames after the last one. The Hyper's startup before that and its recovery after are refused, and so are a chord without a motion, the ambiguous MP+MK+HK and a request without enough meter. A refused request spends nothing, swaps nothing, and never turns into a tag or a snapback.
The switch itself is stock. The outgoing character leaves through the game's
tag-out and the incoming one enters through its tag-in. A hook at the end of the
tag-in ($11DB0) puts the new character where the outgoing one stood, on the
ground, and starts the Hyper you picked through that character's own Hyper
start. So its own code runs the Hyper: it pays that Hyper's cost, freezes the
screen and shows its own cut-in.
Throw Hypers end a chain: Zangief's, and Akuma's Shun Goku Satsu. Akuma's Tenma Gou Zankuu can't be a target, because it's an air Hyper and an incoming character starts on the ground, and neither can Rogue's absorbed Hyper, which depends on what she's absorbed; the tag swap leaves that absorbed state behind in the partner slot.
In v8 of the MvC1 patch, the partner comes in on its own team Hyper whatever you typed, and a two-fighter team has room for one handoff. Here the motion you type picks the Hyper, and a three-fighter team has room for two.

MvC2's manual (the PS2 one, page 11) describes the snapback as a motion plus a
partner button, for one level. Here it's QCF + MP+MK to name the other team's
reserve A, or QCF + MP+HK for reserve B. It starts from neutral on the ground,
or cancelled from a ground normal that made contact, under the game's own rule
for cancelling a normal into a special move ($12442).
Each team has its own snapback, so both can start one in the same frame. The level is spent at startup, blocked or missed included. A clean hit on the live point marks it to leave, and the named reserve is forced in once its slot can be reused, under the same check as a tag. A lethal hit is an ordinary KO. Against a team's last character a snapback is just a strike, as in MvC2, and naming a reserve that's already down is refused while the other is alive. It works on airborne victims too.
The stage darkens for a moment. In the vertical-blank interrupt, where the game hands its palette to the video hardware, the patch turns the stage layers' brightness down to 3 of 15, lets the hardware take that copy, and puts every original colour back before the interrupt returns. The game's own palette never keeps a darkened colour, and the fighters and the HUD, which draw with other rows, keep theirs.
An early snapback test passed for the wrong reason. It read the meter at
+$212, which is the gauge's fraction, not its level: one whiffed heavy punch
made it 8, and the test was satisfied with zero levels. The level is the byte at
+$214, where the game's own spender at $168E6 takes it from. That report is
kept, marked invalid as evidence, and every snapback test since earns a full
level with ordinary whiffed normals before it snaps.
A later build had a real bug. If the snapback's strike was hit out of its
startup, the patch went on holding the player's input for a strike that was no
longer happening, for 61 frames. Now the snapback checks right after the game's
own collision code, and once the game has put the fighter in hitstun (its action
becomes $36), the snapback ends. The level stays spent. The same 5,102 frames
of scripted input that showed the bug run clean on the fix.

Each side has three portraits and three thin life bars. The bars step outward along the timer's hexagon, 14 pixels apart, the game's own row spacing. There are no player numbers in a fight; P1 to P6 appear on character select and the versus screen. When a tag or a KO changes the point, its portrait, name and bar move to the top row, the other living member follows, and a knocked-out one goes to the bottom, its bar draining white over the gray track. Displayed life eases toward the real life one unit per game tick, and what's drawn never changes the real life.
The faces are the game's own 48×32 portraits, trimmed and fitted into 18×12, in
36 graphics tiles the original ROM leaves blank ($1FF00 to $1FF23). The
builder checks that they're blank before it writes, and that no other graphics
byte changes. Their colours were harder. In a fight the game uses all 32 sprite
palette rows, so there's no row to lend six more faces. The included core
recognizes the exact tile, position and attribute of a miniature and draws it
with that character's colours straight from the ROM: the game's own two, or one
of the patch's sixteen, unpacked from its colour table the same way the game
does it. A core that can't says so in the capability word, and the
ROM leaves the miniatures out.
The three-member HUD made the sprite list longer, and that uncovered a race in
the game's own code. The game builds each frame's sprite list in one of two
banks while the video hardware shows the other. When a list is done, the game
flips a pending-bank word ($FFFA9A), and the next vertical-blank interrupt
applies the flip and clears a busy flag ($FFFA90) that keeps a new list from
starting. A long list can still be building when that interrupt comes. The
interrupt clears the busy flag anyway, the list finishes and flips, and a second
list can start in the same bank before the next interrupt applies the flip. The
screen then gets the start of the new list and the end of the old one.
The saved frame looks normal. The test reads the sprite list the core actually
latched, and it found 254 records with seven miniature portraits instead of six:
two identical Ryu heads, one new and one old, drawn on the same spot. The mix
happened to be invisible, and the list was still wrong. The fix is a hook at
$1DCBA, where the game flips the bank, and 16 bytes of new code:
eori.w #$FFFF,$7A9A(a5) ; the game's own bank flip
st.b $7A90(a5) ; busy until the interrupt shows it
jmp $01DCC0 ; back to the gameNo interrupt is masked, and nothing else changes.
One more HUD failure turned out to be the test's fault. The core draws a frame,
then lets the 68000 finish its vertical blank, and a palette upload in that tail
can change the colours after the picture is drawn. The test read the palette
after the frame and compared it with a picture drawn with the palette from
before: 2,165 wrong pixels, a blue of #2277FF where the palette now said
#3388FF. The fix was in the test. It captures all 128 palette banks right
before each frame and accepts one whole palette, before or after, never a mix
and never a tolerance.
You confirm a pick with one of nine inputs: a button, or a punch and its kick together. Holding Start doubles each of them, so there are eighteen colours a fighter:
| Colours | Input |
|---|---|
| 1 to 6 | LP, LK, MP, HP, MK, HK, as the arcade game reads them |
| 7 to 9 | LP+LK, MP+MK, HP+HK |
| 10 to 15 | Start held with LP, MP, HP, LK, MK or HK |
| 16 to 18 | Start held with LP+LK, MP+MK or HP+HK |

Colours 1 and 2 are the game's own. Colours 3 to 6 are the ones v1.0.0 added: MvC2's colours where my Marvel vs. Capcom 2 palette files had them, derived ones for the rest, the same button-per-colour idea as six MvC2 colors.
Colours 7 to 18 are mostly palettes the palmod community made and shared, in
the PalMod gallery and in the MvC2
Discord's #palmodding channel, alongside Capcom's own console colours (the
XMvsSF Saturn and PlayStation bonus colours, MSHvSF's and MvC1's). Each was
chosen to look different from the fighter's other colours. Only where no
palette like that was left, for M. Bison's last four and the second Chun-Li
outfit's last ten, are there recolours made for this release. Every
package names the source of every colour in licenses/PALETTE-CREDITS.txt.
Colours stay with the character through tags, forced entries and rematches.
The HUD's faces use all eighteen in the included core. A core from v1.0.0 shows colours 3 to 18 there in the fighter's LK colour, since it can't read the new table. The big portraits, on Hypers, character select and the VS and win screens, show the LK colour for colours 3 to 18 in every core, as they did for 3 to 6 before.
A colour is three rows of 16 colour words, 96 bytes. Colours 1 and 2 are already in the game. The sixteen new ones, stored whole for every fighter, would take 27,648 bytes, and the blank space set aside for them is 12,288.
Capcom hit the same wall on the Super NES. Street Fighter Alpha 2 and Star Ocean carry the S-DD1 chip, which decompresses graphics on their way from the cartridge to video memory. CPS-2 has no such chip, so here the game's own 68000 does the unpacking.
A new colour rarely changes every pen of a fighter's body row, so the table keeps only the pens that differ from the fighter's own LK colour (or LP, whichever is closer), as 12-bit colours packed two to three bytes. An index points at each colour, and a checksum stamps the table. All sixteen fit in 6,964 bytes, almost four times smaller. zlib comes to 6,994 bytes on the same colours and LZMA to 6,361, but to reach one colour, both have to unpack everything stored before it, into as much as 27,648 bytes of RAM the game doesn't have, with a decoder far bigger than this one. This format unpacks one colour on its own.
The first time a fighter's colour is needed, the game unpacks it into one of eight slots in the core's extra RAM, about 480 instructions on average. After that it copies it from there, as it copies a stock palette. At most six fighters and two character-select figures are on screen, so a colour in use is never pushed out. The slots are part of the savestate, and a table with a different checksum empties them first.
Nothing is lost on the way, and four separate checks say so:
CPU teams have three members too, and the whole arcade mode runs to the end. Two complete arcade runs found problems that only show up late in it.
After Apocalypse, the arcade game has one more fight: the two characters on your
team at that point, one on one. The game takes them from the pair in the slots
when Apocalypse goes down ($F5B4 sets a flag at $FF5083) and skips character
select. An early build seeded that fight with the team's third pick as well,
which gave a side a duplicate reserve that a tag or a KO could have brought in.
Now the patch leaves that fight completely stock. While $FF5083 is set it
seeds no team records, and every hook behaves as the arcade game does.
In a six-player run, the player's point, Ken, was knocked out, and time ran out before Cammy came in. Counting all three members' life, the player won on time, 144 to 77, but the patch only kept a winner's three members if its point was alive, so the next fight, against Apocalypse, started with two. The arcade game keeps a winner's pair whatever its point's life, and now the patch keeps all three the same way.
The core can draw the CPS-2's layers 512 pixels wide. The game can't use that as it is: it culls sprites, clamps fighters, follows with the camera, streams in tiles and scrolls its parallax layers for a 384-pixel screen. The wide ROM hooks 43 places for it, all generated from one census of the original ROM that lists every site with its original bytes and its meaning, including the 58 calls that stream in tiles and the 60 places that move a parallax layer.
Relative to the camera, the arcade screen shows 64 to 448, and the wide arena 0 to 512 around the same centre. Everything that was tuned to the old edges keeps its distance from the new ones:
| Relative to the camera | Arcade screen | Wide arena |
|---|---|---|
| What's drawn | 64 to 448 | 0 to 512 |
| Fighter clamps | +85 and +427 | +21 and +491 |
| The camera starts to follow at | 144 and 367 | 80 and 431 |
| Incoming fighters start at | −16 and +528 | −80 and +592 |
The stage ends stay where they were. The camera's range shrinks by 64 at each end, so at the end of a stage the wide arena shows exactly the arcade game's outermost scenery, and the fighters' world limits don't move (21 to 747 on this stage, in both). What grows is how far apart two fighters can stand.

Two layers needed more than new numbers. Scroll 1, the layer of 8-pixel tiles,
is a 64-column map, exactly 512 pixels. A 512-pixel view that scrolls by less
than a tile needs 65 columns, and the game's 32-pixel streaming wrote over the
trailing edge, so stages 5, 9 and 10 showed black columns and repeated tiles.
The wide build keeps columns 64 to 127 in the core's memory at $814000, and
the core reads them for scroll 1 while a tag at $813FF8 (XS1X) says they're
valid.
The parallax layers had the opposite problem. A background that moves at a
fraction f of the camera's speed would show up to 64(1−f) pixels past
anything the arcade game ever shows at each end, and on stages 1, 3 and 5 that's
blank or unfinished art. Each layer's position gets 64(1−f)(1−2u) added, with
u the camera's place between its bounds (0 at the left end, 1 at the right),
so the layer spans exactly its native range.
The standard ROM carries none of this code. The wide hooks check the core's capability word every time, so a wide ROM on the arcade screen plays the arcade game, but their extra cycles still move things. Side by side with the standard ROM on the same inputs, the two made the same decisions for all 6,476 frames of the run, but the wide one's extra cycles shifted an occasional frame: at one round start, frame 3,146, a position differed by 3 pixels. So the two screens are two builds of the same source, each with its own launcher.
A hook overwrites a few of the game's instructions with a jump to new code, and the new code has to come back after the bytes it overwrote, never into them, because those bytes now hold the jump. Two wide stubs broke that rule, and a full arcade run in the wide arena hung in its first fight.
One replaced six bytes at $108A8 (addi.w #$55,d0 and move.w d0,d1, part of
a fighter clamp) but came back at $108AC, the last word of its own jump. That
word is the low half of the stub's address, $68C2, and $68C2 as an
instruction is bvc.b $10870, a branch the flags let through every time. The
68000 slid back into the clamp code, came around to the stub again, and looped
forever, writing memory as it went. That clamp only runs when a fighter has a
flag at +$30 set, which the stage tours never reached. The other stub, for
fighters entering from the right, jumped into bytes its left-side twin had
replaced, skipped a store, and left the entrant at its old position.
Both now come back after the bytes they replaced. The same mistake could hide
anywhere, so a new audit (audit_hook_returns.py) checks every branch in every
stub against every hook's replaced range. It finds nothing in the four v1.0.0
builds, and exactly the four branches of these two stubs in the builds before
the fix.
The program is seven 512 KB chips, $000000 to $37FFFF. The patch puts its
code and tables in regions that are blank in the original, all above $100000:
The CPS-2 encrypts its first megabyte. On the board, what the 68000 fetches as
instructions, and what it reads relative to the program counter, comes through
decrypted; a plain data read gets the encrypted bytes. The hooks have to sit in
the game's own code, so each one is written encrypted with the set's key. In the
two-player build, the KO ghost's hook reads 2B75 5F7E 91B7 54CF 002B ABFB to a
plain read and 4EF9 002F B3DE 4E71 4E71 4E71 to the CPU. The patch carries
those bytes already encrypted for the Europe 961004 set, which is why it takes
that set and no other: the clones have different program chips.
It matters for tables too. The KO ghost's state table is read relative to the
program counter, so 0008 002C 0128 012E is what the CPU gets there, not what a
plain read of the chip returns. The two PC-relative stock tables the patch's
code needs (one in the tag-in code at $11DC2, and the parallax rates at
$70690) it reads through the game's own instruction, or carries as constants.
The builder refuses a build if any byte it's about to replace differs from what
it expects, in the decrypted view. It also has to keep the game's boot test
happy. At power-on the game adds up one byte in every 4,095 of each program chip
and compares the seven sums with a table at $0000D0. If a changed byte lands
on one of those and the table stays as it was, the self-test fails. The builder
doesn't assume that rule. It finds it by reproducing the table from the clean
chips, then recomputes the sums after patching and rewrites the table (the first
chip's sum goes from $3FA0 to $3F51).
One rule runs under all of it. The game's interrupt handler, the code the board
runs once a frame whatever else is running, uses the 68000's A5 register as the
base of its work RAM, $FF8000, and never sets it; it trusts that A5 already
holds it. An early build borrowed A5 for a moment while it walked the object
lists, as the end marker of a list. An interrupt landed in that moment, and the
handler's move.w -$2CB0(a5),-$2C2E(a5), with A5 at $FF7080 instead of
$FF8000, wrote to $FF4452, the right point's character. Ken became Wolverine
at frame 5,814. The next build stopped borrowing A5, and the same 6,200 frames
of scripted input that broke the roster reach the third member with all six
characters intact.
Now no added instruction changes A5, and no interrupt is masked. A static audit
(audit_a5.py) follows every path from every hook and checks every save and
restore of A5: nothing in the four v1.0.0 builds. On the old build it finds the
three list-end loads into A5 that caused this.
A Team Hyper whose partner is still referenced by a live object is refused, and the code for it has been reviewed, but no run has reached it. In every attempt with ordinary inputs, the resident's projectile was gone 4 frames after the new point settled, before a request was possible. It stays unverified until a run shows it.
v1.0.0 needs its own core. The engine keeps its state in 64 KiB of RAM the core
adds at $810000, and it reads the six controllers and a sample clock through
a window the core adds at $808000. Fightcade's FinalBurn Neo has neither, so
the RetroArch packages can't run there.
The two-player game doesn't need six controllers, and its state fits in 16 KiB.
FinalBurn Neo, the emulator inside Fightcade, maps 16 KiB of RAM at
$660000–$663FFF for every CPS-2 game and keeps it in its save states, and
MAME maps it too. X-Men vs. Street Fighter doesn't use it. On Fightcade's own
emulator code, through 454,271 frames of attract mode, three arcade runs to the
ending, continues and 119 versus matches, plus every test-menu item, the game
didn't read or write one byte of it. The only code that can is a bookkeeping module at
$07B53E–$07BF74, and the game switches that on only when the board reports
extra memory.
So the Fightcade build keeps everything there. It's the same layout as v1.0.0's,
10,432 bytes, moved from $810000 to $660000. One new hook, in the power-on
path after the game's RAM test, sets it up:
The instruction it replaces reads $804030, where bit 14 set means the board
has no extra memory. The new code writes two test values into the block and
reads them back, clears all 16 KiB, and writes XFC1 at $663FF0. Then it does
the original read with bit 14 forced on, so the bookkeeping module can't start
and share the block. If the RAM isn't there, the stamp isn't either, and every
hook keeps the game's own behaviour.
The core's sample clock goes too. DHC and snapback motions count their windows
in it, so the build keeps its own at $663FF4, which the game's VBlank
interrupt adds one to right after it reads the controls. The miniature faces
stay out, because the core colours them as it draws, and so do the six-player
mode and the wide arena. The rest is v1.0.0's engine: its 46 hooks plus this
one, 14,202 bytes of code in the same blank region, and three changed files.
I haven't played it in Fightcade yet. What ran is Fightcade's emulator source,
fightcade-fbneo at commit c959501, built without its window and fed scripted
inputs. Its states were saved and loaded the way Fightcade's GGPO code does a
rollback: every 61 frames, the last 8 were loaded back and run again, and the
whole state had to come out the same. Across 35 runs and 1,128,037 frames that's
18,407 rollbacks, and none of them differed. Every read and write of the block
came from the engine's code, nothing touched $808000, and no VBlank was lost
or counted twice in a fight. These two-player tests pass there: tags, KO
hand-offs, Team Hyper and the Variable Counter, CPU third members, DHC chains
and refusals, snapbacks with every character, six colours, the HUD, continues
and rematches. An arcade run beat Apocalypse, but the test bot never won the
partner battle after him, so the ending hasn't been reached on this build. It
also boots and tags in MAME 0.264. v1.1 adds five more runs there, for the HP+HK
cycle and every colour input with and without Start, with the same rollbacks
and the same result.
Two things you'll see. FinalBurn Neo warns on every start that three files have the wrong CRC: they're the changed ones, and Misc > Options > Hide ROM Warnings turns the message off. And FinalBurn Neo doesn't keep its converted colours and its sprite list in a save state, so the first frame it draws after a rollback can show colours or sprites from before it. The stock game does the same, and the game state isn't affected.
Since v1.1 the build also sets itself up from a saved state. When Fightcade has
savestates/xmvsf_fbneo.fs (or xmvsf_fbneo_ranked.fs), it starts a match by
loading it, and a state saved on the stock game brings the 16 KiB back as the
stock game left them, all zeros, with the CPU long past the boot hook. v1.0.0's
build played that match two on two. In v1.1 the VBlank hook runs the same setup
on the first frame the XFC1 stamp is missing. On Fightcade's emulator code, a
match started from a state saved on the stock game had the stamp on its second
frame and the three-pick character select after the coins and Start.
The installer builds a whole xmvsf.zip from yours and prints a build code,
f98f-bfea-1816-8d9a for v1.1; both players need the same one. Its
PowerShell ran under PowerShell 7 on Linux and wrote the same file as the Python
installer. Not run yet: the Fightcade client, an online match between two
machines, Fightcade's lobby with a changed xmvsf.zip, ranked matches, the
installer under Windows PowerShell 5.1 on Windows and on a Mac, and physical
controllers. Until they run, it's a preview.
I set the goal and the rules: three a side with the arcade game's own tag, Team Hyper and Variable Counter kept, MP+MK and MP+HK for everything new, DHCs where the motion picks the Hyper, snapbacks, eighteen colours that fit with the palmod community's palettes ahead of generated ones, two players or six, and the wide arena. AI agents mapped the engine, wrote the payload, the hooks, the core changes, the installers and the test harness, picked colours 7 to 18 by that rule, ran the tests, and drafted this post. v1.0.0 is candidate 44, the Fightcade build is candidate 45, and v1.1, its Fightcade build included, is candidate 53. The failed candidates and their reports are kept next to the ones that pass, and the gates that haven't run say "open", not "passed".
This is an unaffiliated fan modification. X-Men vs. Street Fighter belongs to Capcom and the X-Men characters to Marvel. The download is patch data; you bring your own game.
Community input · Next release
Choose up to 3 priorities. No account needed.
Feedback guides the roadmap. Votes do not confirm bugs or commit a change to the next release.
Copies public poll choices and counts for your own workflow. Private notes are excluded. Copying sends nothing to another service.