How do emulators work and how are they written?
stackoverflow.com
stackoverflow.com
Are you still working on the book?
I can understand how others' significant edits would cause it to go CW, though.
200 per day, from upvotes only.
Then for extra credit we could rewrite the whole thing (or part of it) in another language. And/or get a guaranteed A if we could make the whole shebang self-hosting.
It was a really great way of understanding the basics of a system architecture.
As for the emulator, it was pretty primitive, but basically it loaded the compiled program code into memory, then would read an operation's worth of bytes, look the operation up in some kind of lookup table (or similar) that mapped the emulated system's instructions to local system instructions say, x86, and execute that operation.
Sometimes the mapping resulted in several operations on the host side the emulated system might execute in one.
eg. ADD R1, R2, R3
might add the contents of registers r2 and r3, then put the result into r1. In x86 it would be more like
ADD AX, BX
MOV CX, AX
or some such.(actually we did it a little higher level then that, but that's the idea).
So an emulator needs to provide all of the various registers and operations and such so that the code can execute, it likely has to have the ability emulate the memory architecture and translate I/O appropriately. Some older systems for example could only output to a printer terminal, or to a simple status display, on a modern system you have to translate calls to those output devices to equivalent ones on your system (like the screen).
More complex system may need to emulate several processors. The SNES and Amiga for example have a handful of chips that not only need to be emulated, but much of the software assumes a particular timing in the interaction of those components that can be very challenging to get right and keeping track of all that and running at reasonable performance can require fast hardware.
These days though, not many emulators use the simple execution mechanism I outlined above. Many of them have sophisticated code profiling and execution caches that can significantly speed up the emulation.
In these terms, emulating a system, or a VM (like Java) or some other runtime environment (like Javascript and the V8 runtime for example) isn't really all that different and there's huge crossover in the theoretical concepts between these areas.
The SO post already links to bsnes, which does an excellent job of balancing readability and accuracy. It's still a little hard to grok the code, but leagues easier than tackling something like SNES9x.
I would also recommend glancing through the vNES source code; it's much simpler than bsnes and is very easy to understand for the most part: http://www.thatsanderskid.com/programming/vnes/index.html
The important thing about the decapped chips is that any on-board ROM has been dumped (electrically, not visually) so that they can be emulated with an opcode interpreter, just like other emulators.
Static recompilation would be really difficult on systems like the SNES and GBA, which have two processor modes (8bit 6502 emulation/16bit 65816, and 16bit THUMB/32bit ARM respectively), so the width of any given instruction is dependent on the processor state. Dynamic recompilation would be faster than instruction-by-instruction interpretation, but full dynamic recompilation (with subroutine caching and all) would probably get pretty difficult to time (I could be wrong), and cycle accurate timing is a stated goal for bsnes (to the point where byuu had the enhancement chips like the CX4 decapped).
bsnes has taken the low-level route because a decent bit of the software for the SNES is really timing sensitive - even that black circle effect at the end of the level in Super Mario World is done by changing values between scanlines. For systems like N64 on the other hand, where the software is less timing sensitive, high level emulation techniques are more common, for the sake of speed increases.
Each 65816 opcode takes between 2 and 8 cycles to execute (more for the MVN and MVP opcodes) and there are rules about when interrupts can be asserted -- bsnes correctly emulates these processor details.
Also, I believe the bsnes properly takes into account the read and write states of the memory bus. This is also an intra-opcode level of detail.
AFAIK, some of the other CPU cores (esp. the DSP-n and Supergameboy cores) are straightforward opcode interpreters.