Some thoughts from my onjoing poking at Apple's late 90s un*x efforts.
The A/UX Mac environment is a reasonable implementation, with a few rough edges but overall understandable. In addition to normal unix processes, the kernel has support for 24 and 32 bit Mac processes. 24 is basically the same as 32, but does extra page table tomfoolery to make the process's memory map wrap at the 16MB mark. Mac processes get their userland memory set up to look basically like a Mac - RAM at low addresses (including the low-memory globals at page 0) and a chunk of RAM assigned as the pagebuffer, the hardware ROM mapped in where the ROM lives, etc. /mac/bin/startmac launches into one of these address maps and the system just runs entirely in userland, with modified System files making A/UX system calls instead of talking to the hardware. Any other Mac processes get the same RAM mapped in, so they run with the exact same backing store as every other Mac process. The only real wrinkle is the /dev/uinter0 kernel driver which implements the event manager so that these multiple Mac-personality processes can do cooperative multitasking and not stomp all over everybody's data by running at the same time on the same RAM. And there's a bit more complexity around reflecting A-traps back into the Mac userland and dealing with supervisor-mode instructions, but that's just mechanics.
I sorta assumed that MAE would be the same Mac environment but with the Event Manager folded back in (since no need to support multiple unix processes), wrapped in a m68k emulator. The mae process itself sets up the GUI, makes sure the user has a System Folder and whatever other host-side setup is required, makes some sysv shared memory to be the mac environment's RAM, then forks off an emulator process to be the Mac environment. The Mac environment uses the same A/UX syscalls it used to in A/UX, emulator translates them to host syscalls, job done.
But no.
MAE's heritage in A/UX is jut allthere. The
Running it as
Turns out
There's just *so* *much* running inside the Mac environment that should by rights be all on the unix side. I mean, I assume there were real engineering or organisation constraints that made it go this way, but looking at it as an implementation it's just baffling.
Aside: But wait, how can Xlib code in the virtual 68k pass pointers to the framebuffer to the X server? The guest code sees virtual 68k addresses, and the X server sees real host memory addresses. Well yes, but the emulated CPU runs with a 1:1 RAM mapping - address 0xdeadbeef in the 68k world is address 0xdeadbeef in the host world too. Which is fine, it helps the JIT go faster, but it means the Mac low-memory globals need to be mapped at process address 0. I suspect this is why MAE no longer works on modern Solaris - null-dereference protection preventing the required memory map from being set up.
Also aside: Huh? Mac code under emulation calls
The A/UX Mac environment is a reasonable implementation, with a few rough edges but overall understandable. In addition to normal unix processes, the kernel has support for 24 and 32 bit Mac processes. 24 is basically the same as 32, but does extra page table tomfoolery to make the process's memory map wrap at the 16MB mark. Mac processes get their userland memory set up to look basically like a Mac - RAM at low addresses (including the low-memory globals at page 0) and a chunk of RAM assigned as the pagebuffer, the hardware ROM mapped in where the ROM lives, etc. /mac/bin/startmac launches into one of these address maps and the system just runs entirely in userland, with modified System files making A/UX system calls instead of talking to the hardware. Any other Mac processes get the same RAM mapped in, so they run with the exact same backing store as every other Mac process. The only real wrinkle is the /dev/uinter0 kernel driver which implements the event manager so that these multiple Mac-personality processes can do cooperative multitasking and not stomp all over everybody's data by running at the same time on the same RAM. And there's a bit more complexity around reflecting A-traps back into the Mac userland and dealing with supervisor-mode instructions, but that's just mechanics.
I sorta assumed that MAE would be the same Mac environment but with the Event Manager folded back in (since no need to support multiple unix processes), wrapped in a m68k emulator. The mae process itself sets up the GUI, makes sure the user has a System Folder and whatever other host-side setup is required, makes some sysv shared memory to be the mac environment's RAM, then forks off an emulator process to be the Mac environment. The Mac environment uses the same A/UX syscalls it used to in A/UX, emulator translates them to host syscalls, job done.
But no.
MAE's heritage in A/UX is jut allthere. The
mae binary itself, if you rename it to midnight, is a synthetic 68k and an A/UX syscall translator for running A/UX binaries on solaris/hpux/whatever hosts. It's basically the model I was thinking of above - a synthetic CPU that catches guest syscalls and translates them to host syscalls.Running it as
mae it just sets up some RAM, loads lib/engine as if it was the ROM, and starts the synthetic CPU. So where does the GUI come from? How do we ensure the user has a System Folder? How does all this work?Turns out
lib/engine isn't just a Mac ROM. It's that plus "support code", and the support code is straight A/UX COFF binaries that do all that extra stuff. Where does the Mac's display window on the host come from? A/UX code linked with Xlib, running inside the cooperative Mac environment, connects to the host's X display server and talks X protocol to create a window shm-backed by the framebuffer RAM. How do we make sure the user has a system folder? A/UX code running in the Mac environment looks for it and if it's not there issues system("/bin/cp -r <srcpath> <destpath>"). Where does the rest of the GUI come from? Code running in the Mac environment calls a special other kind of service trap, not an A/UX syscall, that forks out the maed process (main GUI and some other stuff) as a child of the emulator. There's even code running inside the Mac that calls out to let the license manager know it's running.There's just *so* *much* running inside the Mac environment that should by rights be all on the unix side. I mean, I assume there were real engineering or organisation constraints that made it go this way, but looking at it as an implementation it's just baffling.
Aside: But wait, how can Xlib code in the virtual 68k pass pointers to the framebuffer to the X server? The guest code sees virtual 68k addresses, and the X server sees real host memory addresses. Well yes, but the emulated CPU runs with a 1:1 RAM mapping - address 0xdeadbeef in the 68k world is address 0xdeadbeef in the host world too. Which is fine, it helps the JIT go faster, but it means the Mac low-memory globals need to be mapped at process address 0. I suspect this is why MAE no longer works on modern Solaris - null-dereference protection preventing the required memory map from being set up.
Also aside: Huh? Mac code under emulation calls
system("/bin/cp -r <srcpath> <destpath>")? Yeah... A/UX libc system(3) first calls the host solaris fork(2), creating another full copy of the emulated Mac, which then runs more emulated A/UX code, before finally calling back into the host world for exec(2) and becoming the macd process.


