Show HN: Resurrecting Infocom's Unix Z-Machine with Cosmopolitan
christopherdrum.github.ioI recently brought Infocom's original UNIX z-machine source code back to life on modern systems. The modified source code, instructions on usage, a build of the z-machine, and examples of embedded game executables are available.
There is also a detailed write-up about the state of the original source code, the porting process, and the invaluable role Justine Tunney's Cosmpolitan project played in bringing the Zork trilogy (and more) to Windows/Mac/Linux/bsd for arm/x86 machines over the course of a lazy Sunday.
Funny coincidental timing - I just played HHGTTG a couple of times last night for the first time in thirty+ years or so.
I first played the game, way back - prior to having read the novel. It was mind-blowing. I had no idea what was going on but I got a real sense that it could be pretty funny if I did. So I read the novel eventually, and it was as good as I expected. I don't think I'd gone back to play the game until this week, though.
It had never occurred to me that someone might have the game be their first HHGTTG experience over some other media. For me, the BBC television series was my gateway into the world, then the game (I was already hooked on Infocom by then), then the books.
HHGTTG is a .z3 game and is compatible with this project's build of the Infocom source, should you get the urge to play again in another 30 years.
BBC tv or radio? Seems odd now to think back to a fixed time radio broadcast but I loved the radio show
TV series, absolutely; I've actually never heard the radio program. It aired on PBS in my area along with Monty Python, The Young Ones, and other shows from "across the pond."
They aren't misremembering, there was a tv series.
The most interesting part is that of all the Hitchhikers' media, only the original radio series and their published scripts are in complete agreement with each other on all details of world-building.
In case anyone needs to do the sort of "fix up scattered declarations and move them to headers", you really want to use:
(last I checked, Clang's implementation of this was buggy and half-useless, but I'll admit I stopped using Clang)I recommend this for well-maintained code too, just as another way to keep the code smells away. For a lot of projects this will only require adding a bit of `static` that you were too lazy to write.
I suggest -Wall -Werror for code hygiene.
I recommend against global -Werror, instead use -Werror= with each individual option to make it much easier to deal with compiler upgrades that change warnings.
For well-maintained code, -Werror=all is expected, but switching to it is not trivial in a nasty codebase. It's still worth doing, but it's a whole project.
-Werror=extra is also a good idea once your codebase is clean for all of the above, but you will need to learn to write suppressions, and some of those are compiler-specific.
-Werror=format=2 (requires adding attributes) and -Werror=unused (weird version-dependent interactions with other warnings, so better enable it explicitly) are next, then there's a long tail of obscure warning options that should be considered; many are useful but others should be explicitly disregarded (one particular question of interest that stick out: how hard is it to suppress/restore this within a macro).
Past the trivial, Clang's warnings tend to suck (and they often refuse patches even for parity); GCC's are more useful due to the strength of history. It's still worth using Clang just for the fact that it uses different logic for triggering warnings (and also different warnings available; -Weverything is great for discoverability); just be aware that `__has_warning` isn't very useful (what we really need is `__has_warning_that_actually_works_correctly`, which requires hard-coding known-bug-free compiler versions).
Cool project! But one nitpick.
K&R C has no concept of THEN. That's a peculiarity of the ZIP source code, defined as a pure no-op:
https://github.com/ChristopherDrum/pez/blob/main/zip/infocom...Oh wow, thank you for the clarification. I completely didn't even consider to audit for that in the ZIP source (though the ALL CAPS maybe should have been my hint). I'll update the post with this and another small thing another player found.
It's Bournegol [1]!
[1]: https://oldhome.schmorp.de/marc/bournegol.html
Its interesting to see how developers get into a mindset, based on their life experience.
What we're seeing here is 40 year old code compile and run with minimal effort. Largely because the C language has respected backwards compatibility.
Yes, there were breaking changes along the way, but they were trivial to resolve in hours. And (more interesting) the author believed that to be true and so persevered.
I saw this recently as well. I run a lot of programs on Windows. Many are 32 bit, written in the 90s for Windows 95, or 98. They all still run. Microsoft bends over backwards to keep things compatible.
I also get to write a lot of web API clients. I tell customers that it won't work forever. Web APIs are constantly changing. The service (especially Google) will change something soon to break it.
The people building those APIs are expecting only currently-maintained apps to run. They have no concept (or experience) of 40 or 30 year old code "just running". Crumbs, if you get 5 years out an API client you're doing well.
Yeah, the "porting" part was pretty trivial, all things considered. Any difficulty was really only as a consequence of certain patterns being unfamiliar or evolved. Once those patterns were identified, it was only "roll up your sleeves and do the work" to get it going again.
Working on this kind of got me re-thinking my approach to my personal projects again. What can I do today to best ensure my own code will be this easy to run in 30 years? As a corollary to that, what can I build today that will be interesting enough to want to run again in 30 years?
I think part of that is because those old Windows apps were distributed and forgotten about. There was no update procedure for many of them, and if there was it was just "buy a new disk" or at most "check the website and download a new EXE". Now that people have always-on internet connections, they expect to be able to get things instantly, and companies then expect to be able to push their updates instantly, and other companies expect that they can push their own update if someone else breaks their API, and so on and so forth until everyone expects everyone to have "the latest version" of everything.
It's the era of constant updates. I find it pretty annoying.
... all presuposing of course that the authors of that code are still around, and still want to make the effort of updating it.
In the enterprise world lots of software is more than a decade old, and no one is prepared to "update" it. The mindset in that space is for things to have longevity.
It's more of a mindset I think. Microsoft is clearly very focused on keeping things running. Apple (as a contrast example) is happy to change architecture, remove 32 bit support, and so on.
I agree it's a mindset, I just mean that that mindset has kind of coevolved with a bunch of other stuff.
Even Microsoft seems to be slipping. It seems like these days the (unstoppable!) Windows updates may actually change functionality and UI, which was much less common in the past. And of course the new versions of Windows are more aggressive about updating. They still maintain backwards compatibility for running third-party programs, but I feel like their mindset used to include more stability in their own software as well.
NTVDM is still out there from or based on NT source and still works too - 16 bit apps generally run as well. Microsoft chooses to not support them. last week I ran down the rabbit hole of how this works and how someone ported it to x64.
ReactOS' one it's far better than MS's one.
i think ucsd p-system vm predates z-machine by a couple of years (re OP's origin of vm use comment)
The TV show, in it's absurdity, pointed me to the radio show, which I adored, which led me to read all the books, so when the game came out we laughingly struggled with it, for months, until the sales guy at egghead gave us just a few hints...
A dacade later, ztools was able to read it's dictionary, and people would ask what it was all about... We distributed libraries on bbses, Napster, all manner or ways to get the media out so people could laugh... And most of them found their way back to the Adams books and recordings. We never distributed the books, unless the 2nd half was deleted... ( Waldens, which was beside Egghead, would to though spurts where Adams books would make the best sellers lists, and fade, and enjoy multiple resurgences. )
Go and buy the books (Douglas Adams) and use them for gifts.
Interesting to know, thanks. My intention with that comment was in pondering about vms distributed commercially in the home market, which I don't think I made clear enough in the post. :/
What's remarkable about Infocom's z-machine is the level of sophistication and polish vs the intended application, maybe unsurprising coming from MIT graduates with access to a PDP-10 as a development platform. Otherwise the use of virtual machines was, maybe not common, but not unusual.
* TinyBasic (1975) was specified (and sometimes implemented) as a VM https://en.wikipedia.org/wiki/Tiny_BASIC
* Apple Pascal (1979) was a UCSD Pascal system. https://en.wikipedia.org/wiki/Apple_Pascal
* The COSMAC VIP computer/console's (1977) games were programmed in CHIP-8, a VM. https://en.wikipedia.org/wiki/CHIP-8
* Scott Adams' text adventures (1978+) used an application-specific VM. https://en.wikipedia.org/wiki/Adventure_International
* Wozniak's SWEET16 contained in Apple II Integer Basic (1977) https://en.wikipedia.org/wiki/SWEET16
* If you count Forth as a VM, it was pretty common. https://en.wikipedia.org/wiki/Forth_(programming_language)
You must have done little research to remember those. I knew all but two. (COmSAC and Sweet16).
I wonder if the wikipedia articles are lucky enough to be good...
Blank and Berez were definitely thinking about p-machines when they designed the Z machine, and there is a hat tip in the 1980 Creative Computing article describing its inner workings.
[1]: https://mud.co.uk/richard/htflpism.htm
And the founders were AFAIK mostly looking at games as a testbed for bigger and better things—a mindset that unfortunately led to the Cornerstone database.
There were a bunch of minicomputer and the Unix operating systems that would arguable have been better than Microsoft’s entries. But it just wasn’t in the DNA of those companies to sell a consumer-priced operating systems.
Absolutely amazing. Good work. It downloaded and ran on Arch with no problem.
Fantastic to hear! Thanks for the kind words and for checking it out.
This is somewhat tangential to the precise topic but since it doesn’t come up often: if you’re into this genre I recommend a game called Vespers. Really well done and thick atmosphere.
My absolute favorite text adventure in recent years is Lost Pig. Alas, it is not compatible with the UNIX port this post is about, but it is a delightful game with a unique comedic voice.
Vespers is great. I also recommend the classic Lovecraftian adventure, Anchorhead.
(1) The UCSD p-Machine was contemporaneous and made it feasible to compile Pascal for machines like the 6502 which were terrible targets for conventional compilers. It was atrocious in every way, particularly atrociously slow. At the time many CS professors with Microsoft BASIC being the dominant teaching languages but UCSD plus the inability to do systems programming in ISO Pascal made a generation of young programmers hate Pascal with passion.
(2) Scott Adams made an adventure interpreter was was similar but less sophisticated than the z-machine but similarly portable, in fact you could write a Scott Adams interpreter in BASIC [1] but he also wrote interpreters in assembly for the most popular platforms
https://6502disassembly.com/a2-scott-adams/interp.html
(3) Infocom developed the games on a PDP-10 machine which might be the last general purpose computer that survived with other than the 8-bit byte. (Though it had bitwise addressing which helped it cope with the industry going in that direction).
[1] Notably if you tried to write an adventure game in straight up BASIC you'd struggle. It's not like you could look up a verb in a hashtable and dispatch to subroutine. You'd be writing incredibly long if-then-else ladders nested inside if-then-else ladders which is bad enough in a civilized language.
The Infocom games were also super-tight on memory. There’s a presentation by Dave Liebling I think at some game developers conference on YouTube someplace that goes into this among other things.
Game Developers Conference Classic Gaming Postmortem: Zork (2014)
https://www.gdcvault.com/play/1020612/Classic-Game-Postmorte...https://archive.org/details/GDC2014Lebling
Thanks. I used to run into Dave and other Infocom folks at Steve Meretzky’s Oscar parties but since Steve decamped to CA I may run into a few CA locals once in a great while if I happen to be out there at the right time for some reason.
Here was the 1980 explanation
https://archive.org/details/creativecomputing-1980-07/page/n...
As a side-note to the Scott Adams interpreter and the struggle of writing in BASIC, it should be noted that Scott Adams's original work was in fact in BASIC. He published that source code in BYTE Magazine, should anyone wish to poke around and see what was required to get a game and parser working.
https://archive.org/details/byte-magazine-1980-12/page/n193/...
Incredibly long if-then ladders, or some kind of little assembly routine to let you GOTO a value instead of a constant...
https://www.atarimagazines.com/compute/issue67/331_1_Compute...
The UCSD system was indeed astonishingly, unusably slow. When I got to try it in high school computer lab, in the 80s, I was like "Did whoever ported it to this particular computer just totally fuck it up? WTF?!"
An Infocom adventure on a machine with 16k RAM also had frequent pauses to fetch from floppy, but it was much more tolerable.
Re verb lookups in Basic: you could use DATA statements and READ in a FOR loop for lookup. I don't know what was typical but that's what I recall from some examples.
What is going on here?
[~/Downloads] $ chmod +x zork*
[~/Downloads] $ ./zork1
Cannot open assembly './zork1': File does not contain a valid CIL image.
You probably have WINE or something installed. We have a workaround:
For more help/information on the various gotchas and things that can go wrong running Actually Portable Executable (APE) binaries, please see this reference Mozilla put together: https://github.com/Mozilla-Ocho/llamafile/?tab=readme-ov-fil...No wine... but something something .NET : mono
Oh. Then the workaround should still work I think. Did it help you?
Do you have some sort of binfmt_misc setup for Windows/.NET executables? You might be able to get it to work by running `sh ./zork1` instead.
Yay, this works :-)
West of House You are standing in an open field west of a white house, with a boarded front door. There is a small mailbox here.
Wonderful, thanks for playing!
What setup are you running it on? That will help with troubleshooting. Or, if you prefer, feel free to file an issue on the project repo and we can hash it out there.
https://github.com/ChristopherDrum/pez/issues
I run Arch Linux on an AMD Ryzen 5. I have mono installed, not sure why, i guess it is a depenency for sdrsharp or the like. However, the sh ./zork1 did the trick :-)
Great! Glad to hear we have a simple solution for this issue. It is interesting to note your trouble, because another player in this post is running Arch and says it worked flawlessly. I guess Mono gets in the way a little.