Very cool! This reminds me of ARDI Executor [1] - a piece of (discontinued) commercial software first released in 1990 that took the same API-level reimplementation approach used here. And it did so jaw-droppingly fast considering that it was running on 90s PC hardware. As a little kid using it to play a few Mac games on my Windows PC, it was genuinely inspiring to me to see that this was possible at a time when I was first learning how to code. :)
It was discontinued in 2005, but the developers subsequently open sourced it and put the code on GitHub [2] a couple years later.
Bonus: ARDI, the startup that created Executor, was very briefly featured in Bob Cringely's 1996 documentary Triumph of the Nerds as an example of a prototypical mid-90s Silicon Valley startup.
This is pretty neat. I have been spending the past few months adding an ARM64 JIT to Basilisk II (https://github.com/rcarmo/macemu) and totally appreciate what's involved (I'm currently stuck patching a Quadra ROM to bypass NuBus hardware detection...)
Will definitely give it a try, since I would _love_ to have a Classic Mac environment with some modern creature comforts (like file sharing) in tiny machines.
I am amazed that 1980's software works on binary API compatibility rather than relying on API quirks like timing, memory alignment quirks, memory layout from specific allocator behaviour, etc.
It only takes one unintentional reliance on an implementation detail to make an application not run on another OS implementation...
The Mac classic was about as pure as you could get from an architectural point of view.
A 1 bit framebuffer and a CPU gets you most of what the machine can do.
Most of the quirk abuse of 8-bit machines came from features that were provided with limitations. Sprites, but only 8 of them, colours but only 2 in any 8x8 cell. Multicolour but only in one of two palettes and you'll hate both.
Almost all of the hacks were to get around the limitations of the features.
I don't know if the decision apple made was specifically with future machines in mind. It certainly would have been a headache to make new machines 5 generations down the track if the first one had player missile graphics.
They mostly relied on OS/Toolbox implementation quirks though, not hardware implementation quirks, because applications that relied on the latter wouldn’t run on the Macintosh XL and that mattered to certain market segments. (Like some people using spreadsheets, who were willing to trade CPU speed for screen size.) Similarly anything that tried to use floppy copy protection tricks wouldn’t work due to the different system design, so that wasn’t common among applications.
So even things that wrote directly to the framebuffer would ask the OS for the address and bounds rather than hardcode them, copy protection would be implemented using license keys (crypto/hashes, not dongles) rather than weird track layouts on floppies, etc. It led to good enough forward compatibility that the substantial architectural changes in the Macintosh II were possible, and things just improved from there.
Out of curiosity, what app are you thinking of? Of all of types of software used with classic Mac OS (INITs, CDEVs, FKEYs, Desk Accessories, Drivers, etc.), apps would be the least likely to rely on implemention quirks.
I can't imagine how fast this is compared to the original hardware that ran it. I remember using a Mac 512k with a single floppy drive (no hard drive support) and doing the insert-floppy-dance. Computers were far more mechanical then.
It would be fun to have a "slow it down" feature that also has the various floppy read/write noises paired with it. Bonus points for different generations of hardware and having the OG HD noises to pair with those too!
There was a show HN retro HW project somewhat recently that included sound emulation on board. Maybe that author is reading this, but their sound emulation was probably my favorite part (not to disregard the actual hard parts! I just found it charming)
"Fond" memories of playing King's Quest IV as a little kid on my parents' Apple IIe. You had to swap in a new 5.25" floppy almost every time you walked to another screen. I was fascinated by the game but my god was it tedious to constantly flip and swap the disks around. Google says it came on 8 double sided disks, I could have sworn it was a couple dozen.
The game I played for hours on the Apple ][+ was "FantasyLand 2041". It came on six double sided disks, and I was bummed to find out after quite a lot of game time that disk six was corrupted. I then found out many years later that it wasn't corrupted, the game wasn't ever finished. I then further discovered that John Bell, who produced that and other popular games (Sand of Mars, Beneath the Pyramids, House of Usher), is utterly batty and has written a few "the government is hiding UFOs from us" books.
I'd like to see something like Carbon for old apps so that they boot in modern window frames (without the missing Tahoe corners) and can save to files.
Efforts like this could be a nice way to get old apps to run "natively" on current hardware, as there are a ton of them out there which are perfectly good for work, but which cannot run today.
Advanced Mac Substitute uses a factored approach. 68K emulation happens in the back end, which is a collection of processes connected by Unix-domain sockets, portable to basically any POSIX system.
The front end deals with displaying graphics and forwarding user input to the back end. Working front ends include SDL2 (by a contributor), X11, Linux fbdev, VNC, and five different macOS APIs (Cocoa/OpenGL, CGL, AGL, Quartz, and QuickDraw).
The front and back ends communicate using FIFOs and shared memory. I'm aware that certain platforms will require refactoring all of this to run in a single process. If Emscripten is one of them, then it won't be as simple as you suggest.
In any case, if I were the one doing the port, I might write a bare-bones front end just for this purpose, possibly using the fbdev one as a starting point.
This triggered flashbacks. I'm not sure if I'm remembering correctly, but I think we sometimes also used used Pascal, and it was optional for some toolboxes. It's been a long time though so I could be mistaken. That might have been pre-Mac? But good times, though. Boy, is the world a different place.
The original Mac system software was written in Pascal and most Mac toolbox calls took Pascal-style (prefixed by length) rather than C-style (terminated with null character) strings. But you could write application code in either language keeping this caveat in mind.
It was actually mostly written in assembly, but used Pascal calling conventions and structure layouts since that was expected to be the primary language for application developers. As it had been for Lisa, as it was for “large” applications on Apple II, and as was the case for much of the rest of the microcomputer and minicomputer industry and even the nascent workstation industry (eg Apollo).
It was the Lisa system software that was mostly implemented in Pascal and some blamed this for its largeness and its performance. Compilers and linkers weren’t great back then; most compiler code generation was pretty rigid, and most linkers didn’t even coalesce identical string literals across compilation unit boundaries!
Lisa Workshop C introduced the “pascal” keyword for function declarations and definitions to indicate they used Pascal calling conventions, and otherwise followed Lisa Pascal structure layout rules, so as to minimize the overhead of interoperating with the OS. (I’m not sure whether it introduced the “\p” Pascal string literal convention too or if that came later with Stanford or THINK Lightspeed C.)
I'm guessing they reimplemented the toolbox at the TRAP level (most MacOS calls at the time were accessed through the 68K TRAP instruction).
So, rather than emulating hardware to run native ROMs, they "simply" reimplemented the ROMs.
A friend of mine did this at another level. He basically rewrote the bulk of the toolbox as a C library so that the company, who had a Mac application, could port it to run on a PC, while sharing the source code.
This was before Windows, and it worked! Launched it from DOS, takes over the entire screen. He didn't copy the Mac look and feel. Instead he used OpenLook for his gadgets and what not (since it was, you know, "open").
But he rewrote the bulk of it: QuickDraw, Event Manager, Memory Manager, Window Manager, etc. Just ate it like an elephant. I don't think his regions were as clever as the Mac. Pretty sure he just stuck with rectangles.
Correction: 68K Mac OS calls were A-line traps — in other words, they had opcodes of the form `$Axxx`. To the processor, they're unimplemented instructions that each take an exception through the same vector. The exception handler is the Mac OS trap dispatcher.
`TRAP` is a different instruction, with opcodes `$4E4x`. Each one gets its own exception vector.
It's not just trap calls, though — sometimes applications write directly to the sound buffer or use hardware page flipping.
As I recall MacOS system calls were done through invalid instructions which would cause the CPU to "trap" (raise an interrupt). Giving rise to the question Mac extension writers asked of each other: "How many traps did you patch?"
Hm, doesn't seem to work. Let's try the X11 version:
make ams-x11
./build.pl -i exhibit graft skif minivx xv68k freemountd interact-x11
...
T=0.275s ERROR: OpenDF is unimplemented
Nope, it seems to be missing something. OpenDF? All I find is this: https://github.com/PrjEnt/OpenDF, a long-abandoned project which seems to be a more compact version of another abandoned thing.
The FSSpec calls added in System 7 are mostly new interfaces to existing File Manager functionality. There's an actual high-level `OpenDF()` call, which is like `FSOpen()` except that it won't try to open a driver when the name begins with `.`.
Some applications call `OpenDF()` without checking its availability, but fall back to `FSOpen()` or equivalent if `OpenDF()` returns `paramErr`, which is what the parent is witnessing. See `68k/modules/ams-fs/Files.cc` in the `metamage_1` repo.
If the error message is confusing people, maybe it's time to implement `OpenDF()` for real.
Strange it errors out on given that I assume the thing does run elsewhere. The X11 version sometimes shows the opening screen but any attempt at interaction leads to the mentioned error. The VLC version shows the error directly.
Very cool! This reminds me of ARDI Executor [1] - a piece of (discontinued) commercial software first released in 1990 that took the same API-level reimplementation approach used here. And it did so jaw-droppingly fast considering that it was running on 90s PC hardware. As a little kid using it to play a few Mac games on my Windows PC, it was genuinely inspiring to me to see that this was possible at a time when I was first learning how to code. :)
It was discontinued in 2005, but the developers subsequently open sourced it and put the code on GitHub [2] a couple years later.
[1] https://en.wikipedia.org/wiki/Executor_(software)
[2] https://github.com/ctm/executor
Bonus: ARDI, the startup that created Executor, was very briefly featured in Bob Cringely's 1996 documentary Triumph of the Nerds as an example of a prototypical mid-90s Silicon Valley startup.
This is pretty neat. I have been spending the past few months adding an ARM64 JIT to Basilisk II (https://github.com/rcarmo/macemu) and totally appreciate what's involved (I'm currently stuck patching a Quadra ROM to bypass NuBus hardware detection...)
Will definitely give it a try, since I would _love_ to have a Classic Mac environment with some modern creature comforts (like file sharing) in tiny machines.
I am amazed that 1980's software works on binary API compatibility rather than relying on API quirks like timing, memory alignment quirks, memory layout from specific allocator behaviour, etc.
It only takes one unintentional reliance on an implementation detail to make an application not run on another OS implementation...
The Mac classic was about as pure as you could get from an architectural point of view.
A 1 bit framebuffer and a CPU gets you most of what the machine can do.
Most of the quirk abuse of 8-bit machines came from features that were provided with limitations. Sprites, but only 8 of them, colours but only 2 in any 8x8 cell. Multicolour but only in one of two palettes and you'll hate both.
Almost all of the hacks were to get around the limitations of the features.
I don't know if the decision apple made was specifically with future machines in mind. It certainly would have been a headache to make new machines 5 generations down the track if the first one had player missile graphics.
There were plenty of apps that relied on implementation quirks.
They mostly relied on OS/Toolbox implementation quirks though, not hardware implementation quirks, because applications that relied on the latter wouldn’t run on the Macintosh XL and that mattered to certain market segments. (Like some people using spreadsheets, who were willing to trade CPU speed for screen size.) Similarly anything that tried to use floppy copy protection tricks wouldn’t work due to the different system design, so that wasn’t common among applications.
So even things that wrote directly to the framebuffer would ask the OS for the address and bounds rather than hardcode them, copy protection would be implemented using license keys (crypto/hashes, not dongles) rather than weird track layouts on floppies, etc. It led to good enough forward compatibility that the substantial architectural changes in the Macintosh II were possible, and things just improved from there.
3 replies →
Out of curiosity, what app are you thinking of? Of all of types of software used with classic Mac OS (INITs, CDEVs, FKEYs, Desk Accessories, Drivers, etc.), apps would be the least likely to rely on implemention quirks.
1 reply →
I can't imagine how fast this is compared to the original hardware that ran it. I remember using a Mac 512k with a single floppy drive (no hard drive support) and doing the insert-floppy-dance. Computers were far more mechanical then.
It would be fun to have a "slow it down" feature that also has the various floppy read/write noises paired with it. Bonus points for different generations of hardware and having the OG HD noises to pair with those too!
There was a show HN retro HW project somewhat recently that included sound emulation on board. Maybe that author is reading this, but their sound emulation was probably my favorite part (not to disregard the actual hard parts! I just found it charming)
"Fond" memories of playing King's Quest IV as a little kid on my parents' Apple IIe. You had to swap in a new 5.25" floppy almost every time you walked to another screen. I was fascinated by the game but my god was it tedious to constantly flip and swap the disks around. Google says it came on 8 double sided disks, I could have sworn it was a couple dozen.
The game I played for hours on the Apple ][+ was "FantasyLand 2041". It came on six double sided disks, and I was bummed to find out after quite a lot of game time that disk six was corrupted. I then found out many years later that it wasn't corrupted, the game wasn't ever finished. I then further discovered that John Bell, who produced that and other popular games (Sand of Mars, Beneath the Pyramids, House of Usher), is utterly batty and has written a few "the government is hiding UFOs from us" books.
how does it compare to executor? https://en.wikipedia.org/wiki/Executor_(software)
I'd like to see something like Carbon for old apps so that they boot in modern window frames (without the missing Tahoe corners) and can save to files.
Advanced Mac Substitute stores documents and preference files in sandboxed host directories.
For example, check out the MacPaint demo:
https://www.v68k.org/advanced-mac-substitute/demo/MacPaint-A...
If you were to double-click the Hello document in macOS' Finder, it would launch and open in MacPaint.app.
This is exactly the sort of project that can serve as the basis for such a system.
Efforts like this could be a nice way to get old apps to run "natively" on current hardware, as there are a ton of them out there which are perfectly good for work, but which cannot run today.
I'd be glad of being able to run Macromedia Freehand/MX thus.
Wine for classic Mac OS? Amazing. Well done.
Sounds like Wine + FEX
So has this beaten MACE to the finish line? Or are the goals different? https://mace.home.blog
But will it run Dark Castle??
Many hours were wasted on that game.
Still wondering why the main character looks like Sammy from Scooby Doo
and yes:
https://github.com/jjuran/metamage_1/commit/30cb0e260d5ff478...
Hell, I’d go straight for Beyond Dark Castle… it really took the series to a whole different level.
Very cool. If it's built on SDL2, it should be straightforward to make it run in the browser via Emscripten, right?
Advanced Mac Substitute uses a factored approach. 68K emulation happens in the back end, which is a collection of processes connected by Unix-domain sockets, portable to basically any POSIX system.
The front end deals with displaying graphics and forwarding user input to the back end. Working front ends include SDL2 (by a contributor), X11, Linux fbdev, VNC, and five different macOS APIs (Cocoa/OpenGL, CGL, AGL, Quartz, and QuickDraw).
The front and back ends communicate using FIFOs and shared memory. I'm aware that certain platforms will require refactoring all of this to run in a single process. If Emscripten is one of them, then it won't be as simple as you suggest.
In any case, if I were the one doing the port, I might write a bare-bones front end just for this purpose, possibly using the fbdev one as a starting point.
related discussion https://news.ycombinator.com/item?id=40338443
This triggered flashbacks. I'm not sure if I'm remembering correctly, but I think we sometimes also used used Pascal, and it was optional for some toolboxes. It's been a long time though so I could be mistaken. That might have been pre-Mac? But good times, though. Boy, is the world a different place.
The original Mac system software was written in Pascal and most Mac toolbox calls took Pascal-style (prefixed by length) rather than C-style (terminated with null character) strings. But you could write application code in either language keeping this caveat in mind.
It was actually mostly written in assembly, but used Pascal calling conventions and structure layouts since that was expected to be the primary language for application developers. As it had been for Lisa, as it was for “large” applications on Apple II, and as was the case for much of the rest of the microcomputer and minicomputer industry and even the nascent workstation industry (eg Apollo).
It was the Lisa system software that was mostly implemented in Pascal and some blamed this for its largeness and its performance. Compilers and linkers weren’t great back then; most compiler code generation was pretty rigid, and most linkers didn’t even coalesce identical string literals across compilation unit boundaries!
Lisa Workshop C introduced the “pascal” keyword for function declarations and definitions to indicate they used Pascal calling conventions, and otherwise followed Lisa Pascal structure layout rules, so as to minimize the overhead of interoperating with the OS. (I’m not sure whether it introduced the “\p” Pascal string literal convention too or if that came later with Stanford or THINK Lightspeed C.)
1 reply →
This is quite the feat. I’d love to know more about the process to make this, the motivation, how much time was spent, etc.
I'm guessing they reimplemented the toolbox at the TRAP level (most MacOS calls at the time were accessed through the 68K TRAP instruction).
So, rather than emulating hardware to run native ROMs, they "simply" reimplemented the ROMs.
A friend of mine did this at another level. He basically rewrote the bulk of the toolbox as a C library so that the company, who had a Mac application, could port it to run on a PC, while sharing the source code.
This was before Windows, and it worked! Launched it from DOS, takes over the entire screen. He didn't copy the Mac look and feel. Instead he used OpenLook for his gadgets and what not (since it was, you know, "open").
But he rewrote the bulk of it: QuickDraw, Event Manager, Memory Manager, Window Manager, etc. Just ate it like an elephant. I don't think his regions were as clever as the Mac. Pretty sure he just stuck with rectangles.
Correction: 68K Mac OS calls were A-line traps — in other words, they had opcodes of the form `$Axxx`. To the processor, they're unimplemented instructions that each take an exception through the same vector. The exception handler is the Mac OS trap dispatcher.
`TRAP` is a different instruction, with opcodes `$4E4x`. Each one gets its own exception vector.
It's not just trap calls, though — sometimes applications write directly to the sound buffer or use hardware page flipping.
As I recall MacOS system calls were done through invalid instructions which would cause the CPU to "trap" (raise an interrupt). Giving rise to the question Mac extension writers asked of each other: "How many traps did you patch?"
My earliest recollection of what motivated me is a desire to resurrect The Fool's Errand.
The irony is not lost on me. :-)
Executor from autc04 was fine until I tried to open Nethack 3.6.7 for m68k Macs. It crashed, but it was a fun exercise.
AMS might work better, but it's a pain to setup.
Hm, doesn't seem to work. Let's try the X11 version:
Nope, it seems to be missing something. OpenDF? All I find is this: https://github.com/PrjEnt/OpenDF, a long-abandoned project which seems to be a more compact version of another abandoned thing.
I think they mean FSpOpenDF (https://dev.os9.ca/techpubs/mac/Files/Files-53.html#HEADING5...), a (relatively) late addition to the Mac API.
The FSSpec calls added in System 7 are mostly new interfaces to existing File Manager functionality. There's an actual high-level `OpenDF()` call, which is like `FSOpen()` except that it won't try to open a driver when the name begins with `.`.
Some applications call `OpenDF()` without checking its availability, but fall back to `FSOpen()` or equivalent if `OpenDF()` returns `paramErr`, which is what the parent is witnessing. See `68k/modules/ams-fs/Files.cc` in the `metamage_1` repo.
If the error message is confusing people, maybe it's time to implement `OpenDF()` for real.
2 replies →
Ah, yes, the two front ends I haven't touched in years. This should be fun. :-)
You clipped the part that said "Starting VNC server on 127.0.0.1:5900". Did you try connecting a VNC client?
OpenDF is a MacOS toolbox call (which is apparently not implemented)
Strange it errors out on given that I assume the thing does run elsewhere. The X11 version sometimes shows the opening screen but any attempt at interaction leads to the mentioned error. The VLC version shows the error directly.
2 replies →