Transplanting the Mac’s Central Processor: Gary Davidian’s 68000 Emulator (2020)
computerhistory.org
computerhistory.org
The hardware prototypes like the one in the picture are remarkable as well. Imagine four "Macintosh" systems that look just like regular 68K Macs of that time, booting regular 68K Mac OS and application software, but they all have different CPU architectures (68K, 29K, 88K, PPC.)
(Presumably these architectures all had direct support for the 68K's big-endian byte order, unlike, say, x86, though Apple also famously built an x86/PC port of classic Mac OS.)
68K -> PPC -> x86 -> ARM
The remarkable thing is that the new machines in emulation mode are usually comparable to or faster than the older machines.
The central reason is that most programmers make no use of the immense capabilities that modern processors provide. For example, using AVX2 or AVX-512, you can do do magic in accelerating some algorithms on x86. Software that makes use of these will likely be quite slow on (Apple's) ARM processors.
This is why Apple’s emulator is so fast: it doesn’t emulate any of the system libraries, it uses the native ones instead.
https://developer.apple.com/library/archive/documentation/ma...
but it was very hard to get right. You have to know the right calling convention for everything, and there are some problems with floating point precision and other things I forget the details of.
But it seems weird in any case to me to give Apple a lot of credit for this, because haven't we gotten to the point where VMs have eaten the (software) world? Everything runs on an advanced emulator, it's just the processor never even existed.
Presumably Mac OS on PowerPC got faster as more of it was ported to native code; it's always nice when OS updates make your machine faster rather than slower.
But your point is interesting; as you note, Connectix's Speed Doubler apparently included a dynamic/just-in-time binary translator - I wonder what Davidian thinks about it? (Of course Rosetta 2 for M1 does on-demand static translation.) I'd guess that Davidian's emulator may have used an interpretation approach for consistent instruction timing, which may have been required for timing-sensitive device drivers to work properly, and also for running out of a static ROM with zero RAM overhead. Dynamic translation can eat a lot of RAM, especially if you have to keep the original code around (e.g. for code page memory accesses.)
Connectix famously continued its emulation game and ultimately won a pyrrhic victory after being sued by Sony, resulting in PlayStation/game console emulation being declared legal (hooray!) but also killing the company and its PS1 emulator product (which may have resurfaced as Sony's own PS1 emulator for the PSP/PSVita/PSTV/PS3, though the PS Classic uses PCSX.)
My understanding is that the ROM is mostly OpenFirmware (which is forth bytecode), the nanokernel (aka the 68k emulator) and a mostly unmodified 68k toolbox.
Some further details are at: https://orangejuiceliberationfront.com/universal-procedure-p...
At that point, the translator has to know precisely where the jump might go, and it becomes a total mess. Function pointers get encoded as data all the time, and you'd need to properly translate every address. Simple programs might not run into it, but telling users "sorry, we only support translating programs too simple for you to care about running" is not a good experience.
On the other hand, dynamic recompilation lets you easily hook indirect function calls to translate whatever new function is accessed on demand, and lets you reach really high compatibility.
It's not even possible on x86 with only static jumps, because you can jump into the middle of an instruction and turn it into a different one.
"If an executable contains only Intel instructions, macOS automatically launches Rosetta and begins the translation process. When translation finishes, the system launches the translated executable in place of the original."
https://developer.apple.com/documentation/apple-silicon/abou...
Interestingly enough, you can't mix different flavors of code, for example plugins written for different architectures, the way you could in some previous emulators.