“Holy Grail” Bugs in Emulation, Part 2
mgba.io
mgba.io
One way to get "perfect" emulation is to completely reverse-engineer the system down to the transistor level, wherein the timing relationships between all the components are revealed. I believe this has been done only for a very small number of game consoles, of which I'm aware of are the Atari VCS/2600[1] and NES[2]. Even a GameBoy hasn't been analysed to this level, much less a GBA or DS.
Also, this: https://trixter.oldskool.org/2015/04/07/8088-mph-we-break-al...
[1] http://blog.visual6502.org/2014/10/atari-2600-simulation.htm...
After all, the Nintendo hand held line may well be closer to the NES and C64 than a modern PC or smartphone.
A lovely example of a bug in a game that may as well be intention is a use after free memory bug in the DOS version of SimCity. This went uncaught during development because DOS didn't have the checks in place as modern OSs do. But trying to run it on Windows and you get an error.
So what Microsoft did in the name of backwards compatibility and user friendliness was to program and exception for the SimCity binary.
Similarly the stability of Win9x was kinda compromised by how the cmd window had virtually all the capabilities of DOS. This to the point of being able to poke various BIOS memory areas.
It is about how certain games behaves in ways that may or may not be bugs in a strict sense, but still go outside the expected environment set down by the hardware documentation while still functioning on the actual hardware.
So be it cmd in Windows, or Dosbox, if one want to run the unmodified SimCity binary from back then it will have to allow it to access ram after it was freed. Because when it worked back then, but do not work now, where is the incorrect behavior (or bug if you will) actually located?
Edit : related article : https://arstechnica.com/gaming/2011/08/accuracy-takes-power-...
What do you gain with transistor level that you don't alreaady get with RTL level?
For Infinity (https://news.ycombinator.com/item?id=12291850) we employed a number of tricks, notably changing video parameters mid-render. For example, when walking around on the overworld, the screen looks tilted. This was done by adjusting the camera's X offset after each scanline.
Before this experience, I would have thought all an emulator needs to do is emulate the CPU, and render video frames as fast as possible based on video memory. Instead, the GPU needs to be fully emulated too, drawing pixels from top to bottom and left to right, at exactly the rate expected of real hardware and in sync with the CPU.
Speaking of hardware tricks, one neat thing we discovered during development was that if a cartridge isn't present, then reading from the ROM memory area returned zeros. This was possible to do if your code was running from RAM and could survive a cartridge being taken out while it was running. This only worked with Game Boy Color (and not original Game Boy) because Nintendo removed the safety lock from the power switch. There is actually code in Infinity to do something with this, although it is unfinished. Just another ridiculous thing emulator authors would have to support somehow. :)
And i am not surprised that a non-cartridge would read zeros. I suspect the GB(C) was set up somewhat similar to the C64, where the cartridge connector on the back was basically wired to certain RAM pins on CPU.
Do they just have roughly comparable computational power--or do they actually share some chips?
The NES is a MOS 6502, the GBC has a weird chip by Sharp that hybridizes a Zilog Z80 and an Intel 8080 clocked about double in Game Boy mode and quadruple in GBC mode. The GBC also has sixteen times more RAM, eight times more VRAM, supports bigger carts (up to a megabit), and twice the palettized colors on screen, albeit with marginally fewer sprites.
Huge difference in hardware--the primary reason the GBC is looked at as being "NES-like" is because of the lateness of its release and the comparative jump to the GBA, which is a recognizably modern ARM environment.
The NES had a CPU based on the MOS Technology 6502 running at around 1MHz, 2KB of RAM, 2 screens of video tile memory, and 2 sprite layers, with support for 64 sprites, max of 8 per line.
The GBC had a CPU loosely based on a Z80 running at about 4 or 8MHz, 32KB of RAM, smaller resolution, more colors available (and more displayable on-screen at once), fewer sprites (40), but more displayable on one line (10).
Emphasis on "loosely". It's a weird thing.
https://realboyemulator.wordpress.com/2013/01/01/the-nintend...
But as best i understand it, the basic conceptual design of the GB/GBC and the NES is pretty much the same.
Neither device do much if anything without a cartridge inserted. This unlike say a modern game console that have quite elaborate firmware that loads even without any game media present.
The GB/GBC boot ROM doesn't do as much as even, say, the GBA, but if you're talking "conceptual" about firmware, we're talking about a pretty unbroken line to the current day. (Modern game consoles also have operating systems, but those aren't firmware.)
For something as critical as a memory chip going missing I would have expected either an abrupt halt due to inconsistent circuitry, or a halt on the next read to the ROM's addressable memory. That the CPU instructions would gracefully fall back to interpreting memory as zeros was unexpected to me. Although I don't know much about electronics.
Do you think this would work on other consoles, such as the NES? Given how finicky the NES is about the cartridge loading mechanism, I would think that cart removal while a game is running would cause the system to go into that panic mode with the power light flashing.
It's a bit like the situation with C, where some code depends on a specific outcome of undefined behavior.
With this, I don't mean that the programmer of the game is to blame. If you are developing for a single system, it is reasonable to use some of the properties of this system. In this specific situation, it might even be of interest for Nintendo, since it makes it harder to pirate games.
If one would like to avoid a situation like this, it seems best to develop for an emulator with unreliable properties (the principle of fail-fast: crash and let the developer know that he makes an assumption that is not guaranteed to hold), but which implements the ISA faithfully.
This means that if a game that worked on actual hardware is not working in emulation, the emulator is no behaving accurately enough compared to the hardware.
You are missing the point of an emulator: to correctly emulate hardware. Off-label use of the hardware was standard in that era of game development. Failure to support it is an emulator bug.
> it seems best to develop for an emulator with unreliable properties
Those emulators generally did not exist during the commercial lifespan of the system, at least not in any real quantity, and mean giving up on the advantages of the actual behavior of the hardware. Correctness is not as important as performance and fluidity in a console game.