Under the microscope: Taxi 2 (Dreamcast, PlayStation)
The second best "Taxi 2" game for Dreamcast
In this edition, I’m examining the Dreamcast and PlayStation versions of Taxi 2: Le Jeu, two video game adaptations of the French film Taxi 2. Although they have the same name and similar missions, they’re from different developers and have distinct codebases.
I found previously unreported cheat codes for both the Dreamcast and PlayStation versions.
Dreamcast: Unlock everything, re-lock everything, and play individual missions.
PlayStation: Unlock everything.
Details are below…
The Dreamcast version
To enter codes in this game, hold Start and press A at the main menu:
To unlock everything, enter this sequence:
X, Y, Left, Right,
X, Left, X, Right,
X, Y, Up, Down,
X, Up, X, Down,
X, Y, X, Y
Press Start+A when you’re finished. The Mode Arcade menu item will now be available if you got it right. You’ll be able to select from any of the Mode Mission tracks and from any of the Mode Arcade tracks:
To re-lock everything (why would you?), press Start+A again and enter this sequence:
X, Y, X, Y
You can also enter these codes to play individual missions:
And these codes start you in a particular Arcade mode challenge:
The PlayStation version
In the PlayStation version you can unlock everything by entering this sequence at the mode select screen:
Square, Triangle, Circle, R1, R2, L1, L2, Square
As in the Dreamcast version, all of the challenges in both Mission mode and Arcade mode will become available.
Technical details
Taxi 2 is one of the Windows CE Dreamcast games. That means it has a regular Windows .exe file that can be analyzed thoroughly by tools like Ghidra, which tends to make analysis easier. But it also means that the virtual memory addresses in the game binary don’t match what you’ll see in an emulator, which tends to make analysis more difficult.
On balance, the Windows CE factor wound up making things quite a bit harder. It took me a few minutes to locate the cheat code for the PlayStation version, and a few hours to figure out the Dreamcast version. Part of my problem was not knowing that you could press Start+A to enter a code. In my defense, this isn’t mentioned in the manual:
I got my bearings like this:
The function at
00020900
makes reference to the fileDEBUT.CMD
. I remembered enough of my high school French class to be able to associate that with the beginning part of the game.That same function has classic “menu screen” logic. There’s a chain of if conditions that represent button presses. If the Down/Up button pattern is detected, a cursor index gets incremented/decremented.
In the menu screen logic, there’s a part that will skip over cursor index 01 unless a certain condition is met. This is a match for Arcade mode being greyed out until you unlock it.
That condition is based on whether the value at
0005d4fc
is nonzero. Tracing writes to that address led to the cheat handling code.
The function at 0001fd28
does the cheat code handling. It translates each of your button presses into a number and then stores that number in a buffer. When you leave the code entry screen, it checks the contents of that buffer against a list of known sequences. The mapping is:
01
= X02
= Y03
= Left04
= Right05
= Up06
= Down
I got this mapping by watching the buffer fill up as I entered buttons.
The list of known sequences starts at 00053404
. That’s the “unlock everything” sequence. It’s encoded in a structure like this:
00053404 10 # Length is 0x10, or 16.
00053405 01 # 1st button is X.
00053406 02 # 2nd button is Y.
00053407 03 # 3rd button is Left.
00053408 04 # 4th button is Right...
I wrote this script to interpret the structures and print out the codes.
For the PlayStation game, the first thing I looked at got me to the function that handles the cheat codes (it’s at 80024ea0
). I took memory snapshots before and after changing the mode select screen cursor, then traced writes to the address that holds the menu cursor’s value.
That function has logic like this (Ghidra’s decompilation with my labels):
if (p1_pressed_01 != 0) {
switch(button_counter_01) {
case 0:
if (p1_pressed_01 == 0x100) {
button_counter_01 = 1;
}
break;
case 1:
if (p1_pressed_01 != 0x400) goto switchD_800251d0_caseD_8;
button_counter_01 = 2;
break;
case 2:
if (p1_pressed_01 != 0x200) goto switchD_800251d0_caseD_8;
button_counter_01 = 3;
break;
case 3:
if (p1_pressed_01 != 0x40) goto switchD_800251d0_caseD_8;
button_counter_01 = 4;
break;
case 4:
if (p1_pressed_01 != 0x2000) goto switchD_800251d0_caseD_8;
button_counter_01 = 5;
break;
case 5:
if (p1_pressed_01 != 0x20) goto switchD_800251d0_caseD_8;
button_counter_01 = 6;
break;
case 6:
if (p1_pressed_01 != 0x1000) goto switchD_800251d0_caseD_8;
button_counter_01 = 7;
break;
case 7:
if (p1_pressed_01 != 0x100) goto switchD_800251d0_caseD_8;
button_counter_01 = 8;
break;
default:
button_counter_01 = 0;
This is a button mapping that’s peculiar to this game, but I was able to puzzle it out by watching the value I’m calling button_counter_01
change in the debugger.
Outro
One bit of trivia: according to Sega Retro, Taxi 2 was the only game for a Sega system to be exclusively released in France. Will it be the only France-exclusive game that I cover on this site? We shall see…
I’ll be back next week with more retro game reverse engineering articles. Subscribe to Rings of Saturn to get the next post as soon as it’s available:

Interesting article. Thanks.