It’s time to take down the future of law enforcement. Following on from my discovery that RoboCop arcade has bootleg protection, see here , I decided it would be fun to crack it.
TL;DR – RoboCop uses a separate HuC6280 CPU as it’s copy protection. The CPU is a variant of the very common 6502 and could be found in the PC Engine console. I’m assuming this was chosen because it wasn’t something you could get off the shelf, like a 68000 or a 6502, so would make replica PCBs a lot more difficult.
So far I’d discovered some wait loops, an obfuscated jump table execution and two functions which dumped a large chunk of data to the sub CPU and read it back later. That last bit I was unsure of but rather than digg…
It’s time to take down the future of law enforcement. Following on from my discovery that RoboCop arcade has bootleg protection, see here , I decided it would be fun to crack it.
TL;DR – RoboCop uses a separate HuC6280 CPU as it’s copy protection. The CPU is a variant of the very common 6502 and could be found in the PC Engine console. I’m assuming this was chosen because it wasn’t something you could get off the shelf, like a 68000 or a 6502, so would make replica PCBs a lot more difficult.
So far I’d discovered some wait loops, an obfuscated jump table execution and two functions which dumped a large chunk of data to the sub CPU and read it back later. That last bit I was unsure of but rather than digging too deep, I decided just to patch it out everything I’d found so far and see how the game reacted.
The two CPUs have a shared RAM chip totalling 4kb in size. On the 68000 it’s mapped to address $180000. Thankfully within my disassembly I’ve managed to map that out and found all references to that address space. Based on the references I labelled up the addresses with my best guesses.

Now it should just be a case of patching out anything which points to this memory space.
Startup
To kick things off, the 68000 clears the shared RAM and fills it with a payload. Once the data is in place, it fires a signal to the HuC6280 that it needs to process it and awaits confirmation. If there’s no HuC6280 on your bootleg PCB, then this will get stuck looping forever.

To patch it, we simply put an RTS instruction at $1c8 which ensures calls to this function is disabled. Just for good measure I also apply RTS to the ClearProtectRam and FillProtectRam functions in case there’s some obfuscated call hiding somewhere else.
Hunting for further references to IO_ProtectReq1 I find another busy wait. This one occurs if the 68000 is reset or it hits an exception. It’s a slightly different value being passed it which I think might force the HuC6280 to reset or similar.

Another simple patch. Just apply NOP instructions over the move, cmp and bne instructions.
The Funky Jump Table
The initial discovery which lead me down this rabbit whole in the first place. It’s described in more detail in the previous post but essentially the jump table contains a bunch of bad addresses and it’s not in the correct execution order.

The HuC6280 provides the required jump indexes in the correct order which means the game runs correctly and avoids hitting one of the bad addresses.
I had already pulled out this number list previously so I know which functions to call and it what order. To patch this out, I dump the following code at $3eee in the ROM in place of the jump table execution function. Thankfully it managed to fit.

The Data Dump
As previously discovered, the game dumps a large amount of data onto the HuC6280 and then reads it back later.

I’ve no idea what this is doing but rather than dig deep now I figured I would apply RTS patches to both functions and then run a test. I’ll be able to confirm the rest of the patches are working and hopefully this will reveal itself.
At first glance everything seemed to be working great, but sure enough, RoboCop played it’s trump card.
The enemy bullets are flying straight through RoboCop! What the?!?!
It seems like the game is “off loading” the enemy bullet to player collision detection to the HuC6280. This would have presented a very complicated challenge to would-be bootleggers back in the day. Not only would they need to understand 68000, they would need to port code between two completely different processors, one of which isn’t available to the public.
The problem now is that essential game logic only exists on the HuC6280. If we patch it out, the bullets don’t work. The only way around this is to write a chunk of new code from the ground up. Essentially a complete re-implementation of some game logic from one CPU architecture to another.
8-Bit Bullets
To solve this I will need to get a complete understanding of how the game logic stuck inside the HuC6280 operates. This presents a unique challenge for me. While I’ve been writing and disassembling 68000 code for a very long time, my experience with the 6502 and 8-bit CPUs is very lacking. I’ve done a little Z80 but usual run back to my 16-bit safe zone. On top of that, this isn’t really a 6502 either. There are new instructions, addressing modes and an MMU here. Unfortunately that puts my current disassembler out of the picture. However the MAME debugger is pretty dam good and allows you to apply comments to each instruction. This should hopefully be enough to mark up what’s going on.
To kick things off I go for easy pickings, understanding what data is being passed over by the 68000. After a bit of digging and cross referencing this is what I came up with.

It looks like most of it relates to stuff you would need for collision checks. Player position, their current life level, a hit box ID and so on. The loop at the end of this function copies the entire enemy bullet array. What is missing is box data. We may well know the player or bullet position, but we don’t know how big their bounding boxes are.
Next up I need a basic understanding of the enemy bullet structure. A quick cross reference later and I find the function which instantiates them.

The loop at the bottom shows that the first byte is some flag to say it’s active or a certain type of bullet and that the structure is $28 bytes long. There’s some weird compare at the end which makes no sense and raises some suspicion, but let’s crack on.
A great way to understand the data is to just look at it while in action. In MAME you can point a memory debugger window at an address and look at it while the game is running. So I point it to the EnemyShot array and set the window width to the size of the structure. You can see each bullets activity in real time and even step each frame. From this it’s quite easy to see things like X/Y position, status flags switching on and off and the odd counter running down.
At this point I’ve exhausted all the avenues 68000 side so it’s time to start poking around in the HuC6280. I’ll abridge this a bit because it was very involved, but after a lot of debug stepping and checking values in memory I manage to get a solid understanding of the main collision check loop.

The two big take aways here is that the player and bullet bounding box data is inside the HuC6280. My disassembler does comes in handy here, while it’s not good with the code, it is great at dealing with memory points and data. We’ll need this for our new code, so I extract it all and set it to one side.

The thing we really need to know is what happens after a collision is detected. For each bullet it calls the collision check subroutine which returns 1 in the A register if there was a hit against the player.

There are a number of additional checks against some bullet structure values to ensure it doesn’t set a bullet as hit if it already done so. If everything passes, it changes some bullet states, looks up how many hit points it’s worth and reduces the players life bar accordingly.
The last step is understanding the internals of the collision check itself and.. well I’m going to gloss over it a little because it’s a lot of instructions. But suffice to say it’s a fairly standard AABB check, X and Y have offset and size applied to the position of either the player or the bullet.
Now I have enough information to build a 68000 version. The overall structure of this was very quick to put together with all the information freshly gathered but it did require a bunch of side-by-side debug step comparisons to get it correct. It’s really important to make sure the numbers all come out the same compared to the original. What we don’t want is the original gameplay to be affected in anyway.

The full source code is available below, build with vasm.
The Final Patch
With the new collision detection code built I now need to inject it somewhere in the ROM. Thankfully there is a lot of filler space near the end of the first 128kb block. The code ended up being fully PC relative, i.e. could be stored anywhere in the ROM, but I inject it at address $1e600.
The two last functions to patch are ProtectStoreVals and ProtectReadVals. The first of which copies all the data and triggers the HuC6280 to perform the collision detections. The effect 68000 side is zero so I can just disable this one with an RTS. The latter however pulls all the new data back into the 68000. Essentially a call to ProtectReadVals is really a call to EnemyBulletToPlayerCollisionCheck, catchy name I know. There is only one call to this but just to be sure the first instruction is patched with JMP $1e600 to call our new collision detection routine.
The Result
I’d be lying if I said it worked first time, of course it didn’t, but the core of it was pretty good and it just needed a few tweaks to get it right.
The below video shows the game running while looking at the HuC6280 sub CPU running. As you can see, it’s literally stuck in a holding pattern doing nothing.
Before I sign off, we just need to add a little 1337 to the release. While it would be fun to make a crack intro for the DataEast hardware, I would rather get back to the task of disassembling the rest of the game ROM and seeing how feasible it might be to port it to a different 68000 based platform. So instead I spray a little graffiti on the score board and make a run for it!

So, do you own a broken HuC6280 sitting on a DataEast RoboCop arcade board? Do you want to bring that board back to life? Well then grab our fine IPS patch release down below.
Honestly, if this brings even one board back to life, I’ll be so happy.
Till next time!