An Introduction to v68k
As a compulsive programmer who looks back on pre-NeXT Mac systems with unusual fondness, I suppose it was inevitable that I would begin working on a 68K emulator. And given my penchant for reinventing the proverbial wheel, it was predictable that I would write my own — it’s called v68k.
As usual, I had my reasons. First, I was avoiding a dependency on complex build systems — it had to be buildable outside of Unix. Second, it had to be platform-independent — failing on 64-bit or requiring a JIT implementation was a dealbreaker. Finally, the emulation should be implemented not as a complete executable, but as a library with no global state, supporting multiple disparate usage scenarios — mechanism, not policy. This was important, because I have several policies in mind.
The most obvious use of a 68K emulator is to run 68K Mac applications. Mini vMac and Basilisk II are two examples of programs that, provided a Macintosh ROM and a disk image with Mac system software, will boot Mac OS in a window. They differ in both features and approach: Mini vMac began with original-form-factor Macs like the Mac Plus and emulating not just the 68000 processor but much of the other hardware, allowing the system-supplied device drivers to just work. By contrast, Basilisk II support focuses on the Mac II (which introduced color and Ethernet to the Mac line) and later and works by replacing Mac OS drivers with its own, which although frequently better performing also pose new risks of instability. Mini vMac tends to be more robust, but Basilisk II is more useful.
However, v68k isn’t intended to compete with either of these programs (for the time being, at least). While simulating hardware and patching drivers are two ways of emulating a Mac OS system, they’re not the only way to run the applications themselves. Mac programs call into the system software with special instructions (called 'traps') that the processor treats as unimplemented opcodes and handles by taking an exception. Each of these 16-bit opcodes starts with hex digit 'A' (e.g. A9F4 for _ExitToShell) and are therefore known as 'A-traps'. Mac OS installs an exception handler which looks up the function address in a table and calls it. One v68k host program I’m developing will install its own native versions of these calls. When an emulated Mac application calls the _NewWindow trap, rather than running the ROM or system implementation of _NewWindow (requiring emulation), the emulator will run its own code instead. On classic Mac OS or OS X, it’s even possible to use the native NewWindow() function, but Cocoa and cross-platform frameworks are also options.
The benefits of this approach are threefold: The most noticeable is that the application runs as a contemporary of native applications — with access to the full screen and native menus, windows, and controls — as well as access to the native file system and native memory management. This is in stark contrast to Mini vMac with a Mac Plus ROM, which gives you a 512x342 black and white screen and 4 MB of RAM, and as the only form of file transfer, disk images — a sort of virtualized sneakernet.
More important, perhaps, is that replacing the ROM and system software (instead of running them in emulation) means that no Apple code is required. While ROM images can be found if you know where to look, concerns over copyright infringement and the spectre of piracy allegations are likely to have a chilling effect. While it’s convenient that Apple’s extreme disinterest in pre-NeXT technology apparently extends to copyright enforcement, depending on such fortuitous circumstances is unwise, as illustrated by the recent takedown of MegaUpload.
Finally, the processing cost of running the operating system in emulation is avoided. Not only does this mean that many fewer native instructions are required to perform operations, but infinitely fewer are needed to do nothing at all — when the application calls _WaitNextEvent, the entire emulator process can sleep, instead of repeatedly emulating the instructions in Mac OS' idle code path.
So that’s the first piece of the puzzle of why I wrote a 68k emulator and a promise of what’s to come. But if it can’t emulate Mac apps yet, then what can it do? And how does emulated code talk to the real world? In the next issue, we’ll limit ourselves to running code only and take a look at standard I/O, filters, unit tests, and untrusted code. Stay optimized!