Instead I'd introduce some sort of random failure elsewhere in the game, so it superficially looks like it's working, but isn't any fun to play...
This isn't mentioned in the article, since one of the anti-piracy/-emulation techniques I didn't discover at the time of writing it (due to my dump being an overdump) is that many of the games do this. They screw up input so it either boots but you can't play it at all, or input is unplayably slow. It detects it by having an interesting memory mirroring quirk in the cartridges that no other GBA carts have.
This is one effect I discovered when I was working on getting around the anti-piracy effects of an arcade game[1][2] in order to run it on similar hardware. I think it is a more clever copy defeat mechanism than stopping the game from booting.
Mirrored memory is a side effect of unconnected lines on the address bus thus making the content of those bits irrelevant. Code can take advantage of this to run faster or put tag values into addresses.
What does this have to do with speed?
On a GBA, VRAM is faster than ordinary RAM. Programs can do well to use it for tight inner loops.
Almost all programs use IWRAM for this. It's one of the things it's for. It's as fast if not faster than VRAM (depends on if the VRAM is being accessed by the PPU at the time).
Using STM (store multiple) to DMA registers? Again, go faster.
In retrospect, yeah this might actually be the case.
Save type masquerading might be code that helps when running on a development kit, but I admit that I can't think of what use it might have.
There are a handful of GBA games that lie about save type as anti-piracy and refuse to save or even boot if it finds the wrong one. It's also really the only anti-piracy technique that any other GBA games actually use, since it's quite effective against flash carts.
You generally need to mask potentially-tagged pointers before dereferencing them. (Ab)using virtual memory or unconnected lines lets you skip the mask, eliminating the cost in any code that's otherwise unconcerned about the tags. This in turn may let you save a byte or register here and there (and thus save memory bandwith / potentially spill fewer registers, maybe saving some performance).
GCs may abuse pointer tagging for keeping track of what they scaned. Ruby's VALUE type is pointer sized, and will point to Ruby objects such as strings and symbols - but it can also directly represent e.g. a 31-bit integer value ('Fixnum') on 32-bit systems without needing to be dereferenced, and without needing to consume a separate type field.
Also used in early revisions of the Amiga. (BCPL or Amiga Basic used pointer tags for something like garbage collect or frame pointers or something IIRC.) Mac had something called 32 bit clean which I imagined could have been pointer tagging.)
"32 bit clean" meant that the ROM didn't use pointer tagging; otherwise it was using the top 8 bits for its own purposes and wouldn't run on machines which used the full 32-bit address line.
I tried writing an ARM -> JS recompiler, but my initial tests showed that the slow games tended to re-copy the portions of code that were slow back into their working RAM, causing me to invalidate the recompiled code. Maybe I could get clever and detect when I don't need to invalidate the recompiled code, but I have a feeling that that would actually slow it down overall. Haven't tried this yet, though, so who can say?
What I did observe is that the games that DON'T invalidate the recompiled code tended to run ~20% slower, so I'm not really sure what I was doing wrong there. It mostly seems to be that the code my recompiler is outputting is less well optimized by the JIT, and not that the recompilation is too slow. It's also possible that my recompilation is screwing over the garbage collector, as I have a somewhat verbose intermediate representation that I created in the hopes that I could optimize the recompiled code. I don't have much experience with compilers though, and again, I never got around to this.
I've been pondering pushing the branch that I did this work on, but I was hoping to wait until I got it to perform better before pushing it.
I can definitely see about moving some of the controls around. When I was testing it, I just had a lot of "wasted space" around the edges, so I figured I could use that for the controls and instructions. I guess I should rethink that.
You're correct that I'm scanning for the save type. Auto-detection is something I want to get in at some point, but I haven't quite gotten around to it yet. I was considering using a database for the save types as well, but I didn't know if such a database existed. I was just planning on pre-computing it by the same way I'm currently detecting it, which as you say has problems.
(I will use 'save-code' to mean the SRAM_V123 strings embedded in ROM images, and 'save-type' to mean the actual non-volatile storage configuration the game expects)
So, there's a couple of ways you can go:
- you can go the full auto-detection path. EEPROM access and size is easy to detect, I think it's fairly reliable to detect SRAM access, but unfortunately you can't dynamically detect Flash configuration - the first question the game asks the hardware is 'which Flash storage protocol do you want' and there's no way for the emulator to figure out how to answer that question ahead of time.
- You can go the full static-detection path, based on scanning for save-codes. This is pointless, because you can't determine whether the game wants 6 or 14-bit EEPROM data, and you still can't determine the Flash storage protocol. Also, there are games that contain two different save-codes but only work with one save-type, and of course there's Top Gun which contains all the different save-codes, but will ignore the A button at the main-menu if it finds any any non-volatile storage present.
- You can go full database-mode. This is what bsnes intends to do eventually, but it's kind of hampered by the fact there is no (known) complete database of save types.
- You can go a hybrid system. VBA does something like this: if the game is listed in vba-over.ini, use that save-type. Otherwise, guess based on the save-code: SRAM is easy, EEPROM starts in an 'unknown size' mode and automatically configures itself as soon as the game pokes at the emulated hardware, Flash picks some reasonable defaults and lets the user change them if they don't work. This obviously gives you really good compatibility but is horribly complicated to build.
The database I have at the moment only contains known-good configuration for the ROM images known to have two-or-more save-codes, plus one or two other games mentioned by users on the bsnes forums. It occurs to me that if you had some kind of UI where users could change the emulated storage configuration until it worked, you could have some kind of 'submit configuration' button, and the database might be built up that way.
This isn't mentioned in the article, since one of the anti-piracy/-emulation techniques I didn't discover at the time of writing it (due to my dump being an overdump) is that many of the games do this. They screw up input so it either boots but you can't play it at all, or input is unplayably slow. It detects it by having an interesting memory mirroring quirk in the cartridges that no other GBA carts have.