Design Philosophy

A very specific philosophy governs this project. It is this philosophy that has shaped what Exodus has become thus far, and will continue to shape what it will become in the future. That philosophy can be broken down into the following key ideas:

1. Generic emulation

A system is simply the combination of a set of discrete components. A Motorola 68000 chip in a Mega Drive is the same as a 68000 in an Amiga, an arcade system, etc. One core should exist for a given device, which emulates all the features of that device, so that the device can then be used in any system, without it being aware of any external details of the system that uses it. Each core should serve to make its emulation accurate and "feature complete", whether a particular feature or level of accuracy is required in any particular target system or not.

2. Architecture before performance

A good, clean architecture for a core is critical to ensure readability of the code, which directly affects how maintainable that core will be, and also how well it serves to document the device it emulates. All cores must be free from assembly code. This is critical for portability and future preservation of the core. In addition, all cores should make use of object and class structures where appropriate, and the code itself should be well written and structured.

3. Clean code before performance

Using less lines doesn't necessarily make your code better. You might be able to sum up a complex operation on one line using 10 bit shifts, half a dozen AND operators, and a few OR's. Consider carefully first if the resulting code is the cleanest, most readable way to express that operation. In most cases, a cleaner way exists. Use the "Data" class where appropriate, rather than directly performing bit manipulation. Avoid "magic numbers". Where constants exist, define them all in one place, then refer to them by name in code. This not only serves to make code more readable, it also helps to avoid bugs when writing code, especially when performing bit manipulation.

4. Code as documentation

All cores should serve as a form of documentation on the device they emulate. To this end, the code must be well structured, readable, and extremely well commented. Cores need to be maintainable by future developers, not just the original author. Leave a 500 line comment if that's what you need to explain what something is doing. Where you use an external source of information to implement something, add it to the list of references for the project, so that other people can see why something is done a particular way. Someone should be able to understand everything they need to know about how your core works by reading through the code itself and the list of references. If they can't, you're missing a reference or a comment.

5. Technical accuracy

Every core should be as accurate as possible, and honestly report on its accuracy. If there is any point at which the real behaviour of the device is unclear, or where the core doesn't emulate a behaviour of the real device, it should be documented, so the limitations of a given core can be well understood to others, not just the original developer. Any "hacks", where something is written in a way that is known to be incorrect, just to get the correct apparent result in a given situation, should be avoided at all costs. These kind of hacks only serve to hide underlying problems, and make them harder to detect and debug later on. They ultimately hurt the goals of emulation and preservation rather than helping them.

6. Timing accuracy

Accurate emulation of timing is critical in a lot of systems. All cores should emulate the timing of the original hardware as much as is possible, in any case where that timing information can be observed by an external device. Cases where timing is known to not be respected, or where correct timing information is simply unknown, should be clearly documented.

7. Debugging support

It should be a goal of every emulation core to expose as many debugging features to the user as possible. Not only does this open the emulation platform up to people wanting to do development or testing, it also greatly assists in debugging the emulator itself, improving existing emulation cores, and developing new cores.

8. Performance after all of the above

Performance is important, but only in as much as it does not infringe on good architecture, readable code, generic implementation, or accuracy. Computers get faster. Compilers get better. Inefficient code can be optimized later. Ultimately, slow cores will run full speed one day. On the other hand, unmaintainable cores will languish and die, no matter how fast they are. We're trying to preserve systems for the coming decades, not the next 2 years. We need to write cores that will stand the test of time.

If you understand the above philosophy, you will understand why Exodus has been designed the way it has, and what sets it apart from other emulators.

If you wish to make a donation to show your appreciation for this project, you can do so here. Your donation may go towards the hosting costs of the website, or equipment or reference hardware to assist in the development of Exodus. It may also go towards a bunch of flowers for my beautiful wife, to say thanks for your support and patience all those nights I stayed up late working on this project.