Introducing the Å-machine

The Å-machine is a virtual machine for delivering interactive stories. It is designed for stories implemented in the Dialog programming language.

As the name suggests, the Å-machine is inspired by the Z-machine. The letter Å (pronounced [ɔː], like the English word “awe”) follows Z in the Swedish alphabet. In international contexts, å can be transcribed into aa, as in “The Aa-machine”.

The Dialog compiler can produce Å-machine story files starting with version 0g/01. The filename ending is .aastory. Support for the widely used and historically important Z-machine remains, and will not go away. But stories compiled for the Å-machine look better on the web, and are smaller and potentially faster on vintage hardware (the latter claim is unsubstantiated at the moment, but it has been an important design principle).

In a sense, the Å-machine is to Dialog what Glulx is to Inform 7. It eliminates the tight restrictions on story size, and extends the basic functionality with a carefully balanced set of new features. But the Å-machine is designed to run the same stories on everything from 8-bit systems to modern web browsers. Data structures and encodings are economical, and the overall word size has not increased. Large stories are supported, but small stories still have a very compact binary representation.

Compared to the Z-machine and Glulx, the Å-machine operates at a higher level of abstraction. This improves performance on vintage hardware, both by making story files smaller, which improves loading times, and by allowing larger chunks of computation to be implemented as native machine code. The downside is that the virtual machine is more tightly coupled to the idiosyncracies of a particular high-level language, in this case Dialog.

Currently, only a single Å-machine interpreter exists. It is implemented in pure javascript, and must be combined with a frontend that handles all input and output. Two frontends are provided: A web frontend based on jquery, for publishing stories online, and a Node.js frontend for running automated tests. A tool, aambundle, can convert an .aastory file into a web-friendly directory structure, including story and interpreter, ready for deployment on a server.

Release 3 of Tethered runs on the Å-machine.

This Cloak of Darkness implementation illustrates how to enhance touchscreen play with clickable links.

Browser compatibility is hard, and this first release already comes with a couple of known problems. Safari on iOS doesn’t adjust the window size when the on-screen keyboard appears, so for now only half the screen real estate gets used. Internet Explorer cannot save the game state or a transcript. Chromium on mobile devices somehow picks a slightly different font size for the paragraph that contains the input field. And I haven’t been able to get any screen readers to work.

I hope to address all of these issues eventually. Suggestions are welcome!

11 Likes

You’re free to take your own approach of course, but I wonder just how much would need to be added to Glk to implement your Å-machine features in GlkOte. Being able to leverage existing frameworks would help with compatibility, while extending Glk would let other authoring systems use the new features.

One thought for performance: no idea how big a change this would be, but if the opcodes which wait for interactivity (input, save/restore) could set a callback register and then stop processing rather than waiting, then that could allow for a lot of improvements. It’s not a pattern that Inform or Z-code/Glulx really support well, but maybe it could be with Dialog’s predicate design. Or it could be just as messy as with Inform. :slight_smile:

3 Likes

Hi Linus,

You’re efforts are nothing short of marvelous. You’ve written the foundation for an extensive IF language employing sensible design decisions.

You now go on to write a VM that sheds legacy limitations having an eye toward modern standards at the presentation level. Full CSS support? “Uh, yeah, nice.” It’s not a stretch, I believe that you’re a better software architect and developer than the vast majority even on your bad days.

I played ‘Tethered’ on the Å-machine through your Javascript interpreter. The ‘basic’ presentation looked inviting and unassuming; the introductory scenes pulled me in. I finished your game in roughly three hours. The game’s length is perfect, a kind of quality “beer and pretzels” experience as they say in American English parlance.

By the way, here’s what I liked most (no spoilers):

  • In two points in the game I knew I had to perform fairly complicated tasks. “Oh, boy,” I thought, “here we go” but it wasn’t like that. By the first action I took you automatically completed the task for me without giving away the challenge

  • Your clever use of foreshadowing

  • Multiple ways to figure out just what the heck is going on

Also, I find myself amused thinking I found a bug in your game–I didn’t. :slight_smile:

I suppose the following may be interpreted as feature requests but I know you’re busy (see "good design decisions above) so I suppose I’m simply asking for your thoughts:

  1. The concept of travel connectors in Dialog.

Travel connectors are neat as they provide the author all kinds of hooks for conditioned narratives at each point of travel including, " describeDeparture, describeArrival, TravelMessage, etc. I kind of think of travel connectors as ways to author the “sticks” between locations found on most IF maps.

  1. XHTML links/Javascript Hooks

Dialog, in my opinion, would be greatly served by an ability to call Javascript routines from directly within the Dialog code and receive objects back from said routines. This may range from a simple button to a MongoDB query to current GPS coordinates

  1. Ability to split project into multiple makefiles, i.e. one file per room

I commend you on your efforts (and so nice, too!). What are your thoughts?

4 Likes

I don’t think Dialog has any precise equivalent to describeDeparture and describeArrival, but that is partly because it doesn’t yet have a very sophisticated set of predicates dealing with NPCs (which I think they are mostly for). It does however have a pretty straightforward equivalent to TravelMessage: (narrate leaving $ $). So, suppose you had a room (#room) with a description like this:

You are in a clearing at the bottom of a cliff. Steep steps lead north up the cliff. A broad path, almost obscured by leaves, goes west.

You could then have “travel messages” like this:

(narrate leaving #room #north)
     With great effort your pick your way up the steep track.

(narrate leaving #room #west)
      The leaves rustle under your feet as you walk along the path.

The other concept in Dialog’s standard library which resembles TADS’ travel connector is (door $). Doors work not just for typical open-or-close-doors, but for any sort of “connection” which can block access.

3 Likes

This is a nice suggestion. The javascript engine has an i/o interface, and it could indeed be possible to implement a GlkOte frontend. I’ve been meaning to write an interpreter in C, once the Å-machine specification feels stable enough, and for that I would definitely use Glk. I’ll look into GlkOte as a potential long-term option. Still, right now I think it’s probably less trouble to fix the outstanding issues with the current javascript frontend than to make a new one.

The short answer is that it would probably be equally messy. I’m not sure how it would improve performance. I know parchment does just-in-time transpilation; is that what you had in mind? I don’t think that technique would have a comparable impact on Dialog code, since there isn’t a one-to-one correspondence between predicate queries and function calls.

Thank you very much for the praise! It makes me warm and happy inside.

Thanks also for the nice feedback on Tethered!

I’ll note, for the record, that “full CSS support” may be a bit of an overstatement. All CSS properties are indeed forwarded to the web browser, but only if the player decides to use a javascript interpreter. The Å-machine will eventually run on 8-bit hardware, presumably without much CSS support at all, which is why the Dialog manual specifies that all style tags are hints, and can be ignored by interpreters.

I concur with @PaulS that (narrate leaving $ $) provides some of the functionality. There’s also (narrate failing to leave $ $). I also concur that more thorough NPC handling would have to be in place before travel connectors could be supported to a wider extent.

But I fully agree that the liminal spaces between rooms are an important part of parser game geography, and deserve attention both at the story and library level.

I am wary of this. All web-related functionality (currently CSS and hyperlinks) is optional for interpreters, and the manual makes a point of telling the author not to rely on it. This is to ensure that Dialog games will remain playable for a long time, as Inform stories have been (and still are). Platforms and features come and go. Javascript changes all the time, and browser compatibility is frankly a mess. Hence the need for a simple, consistent, well-specified interface to carry the actual story, with an optional layer of bells and whistles and graphical presentation on top. As soon as there is a way for the actual game logic to escape into the presentation layer, then forwards and backwards compatibility is compromised. Would you trust a Vorple game to be playable in ten years? Twenty? What about a plain Z-machine game? And if story code has direct access to network communication, that opens the door for online-only games, and those are even more ephemeral.

This would have to be balanced against the need for a strict ordering of rule definitions. There is currently no limit on the number of source code files in a project, but it is critically important to always provide their filenames in the same order on the commandline. The rule definitions in earlier files supersede those in later files. Therefore, in my opinion, it seems sensible for authors to restrict themselves to one file of story code, one file of library code, and possibly one or two small configuration files that go at the very top, controlling global options (e.g. is this a debug build, or a pre-release for testers) that the story code can react to. But there is no technical limitation.

If you are thinking of incremental builds, I’m afraid performance would suffer too much. The full source code is required in order to determine which predicates are dynamic, which can be invoked using multi-queries, which are known to fail or succeed, what words might refer to which objects, and so on, and all of this information is crucial for generating sufficiently fast and compact Z-code (or Å-code for that matter).

All in all, your suggestions have been food for thought. I have attempted to clarify the reasoning behind some of my design choices. But Dialog is still in beta, and a lot could happen before it stabilizes. That evolution will certainly be influenced by thoughtful comments such as yours.

2 Likes

Yeah I was thinking in terms of a JITting interpreter. But also from my experience with Emscripten - which is less necessary seeing as you’ve already provided a JS terp. So it probably won’t actually be an issue.

Å-machine release 0.2 contains the following changes:

Javascript engine

  • Bugfix: Runtime error handler can now use undo.

Web frontend

  • Improved screen reader support.
  • Now possible to save gamestate and transcript in Internet Explorer.
  • Text selection now works, for copy-paste.
  • Support for logging to a remote server.
  • Progress bars are displayed correctly.
  • Simplified the HTML wrapper by moving most of the initial document structure to javascript.

The specification is still at version 0.1.

2 Likes

Will A-machine support html type hyperlinks to external resources? (now or in future releases)

For example a link to a photograph or additional optional narrative?

Thank you,
Jeff

Yes, I have some plans for this. But it needs to be designed carefully, to blend in well with the rest of the language, and to encourage (as far as possible) self-contained games that can be archived.

1 Like

Very good news. My use for limited external links involving photographs and possibly audio as a post lesson clarification in an educational based project.

Thank you,
v/r
Jeff

The Å-machine toolkit version 0.2.1 has been released. This includes version 0.2 of the specification, and updated versions of the Å-machine toolchain and official Javascript interpreter.

From now on, the version number has three parts. The first two are equal to the specification version, and the third gets incremented when the tools are improved without changing the specification.

Changes introduced in version 0.2 of the specification:

  • Support for external resources (e.g. embedded graphics and external link targets).

  • Ability to check at runtime whether the interpreter supports quitting.

Changes introduced in version 0.2.1 of the web interpreter:

  • Support for embedded images, downloadable feelies, and external links.

  • Added “restart” and “save story file” menu items.

  • Don’t move focus to the input element if the last command was clicked. Only do it if the last command was typed.

Changes introduced in version 0.2.1 of the Node.js interpreter:

  • Slight modification to the word-wrapping code, to ensure compatibility with dgdebug and dumbfrotz.
4 Likes

The Dialog system is amazing. Its features keep moving toward things that will find most useful. In this case, the availability of external links will be most useful in education applications that I have in mind for special needs students.

Thank you,
v/r
Jeff

If there is a Jesus, he must be Linus.

Version 0.2.2 fixes a bug where external restart (e.g. from the menu in the web interpreter) didn’t work properly.

1 Like

I’m happy to announce the Å-machine toolkit version 0.3.1.

It fixes a couple of minor bugs in the Javascript interpreter, but the big news is:

Commodore 64 interpreter

I have claimed that the Å-machine is designed to perform well on vintage systems. Now it’s time to deliver on that promise.

This release contains an interpreter written in generic 6502 assembly language, with a frontend for the Commodore 64 (equipped with a 1541-compatible floppy drive). A RAM Expansion Unit (REU) is required for the UNDO command, but is otherwise optional. The presence of a large (at least 256 kB) REU will also improve performance dramatically.

Creating a Commodore 64 version of an Å-machine game is easy. As before, the Å-machine toolkit contains a command-line tool called aambundle, that can read an .aastory file and create a directory with a self-contained web player. With the option -t c64, it will create a disk image for the C64 instead.

The performance on a stock system (C64 + 1541) is competitive. The first few commands tend to be slow (about 10–15 seconds), as the engine retrieves data on demand from disk. Once the system is warmed up, the typical response time before output starts to appear is about 1–3 seconds. Of course, printing a large chunk of text also takes time, especially if new content has to be loaded from disk. But that is all happening while you read, so it is not as frustrating as the initial wait.

With a REU, response times are typically less than a second.

I obtained these figures by running the Å-machine versions of Tethered and Pas De Deux. When the same games are compiled for the Z-machine, and played on a C64 with a REU (using Zeugma), they respond to commands in about 3–7 seconds. Without a REU (using Ozmoo), the response time is 1–3 minutes (!) because of the increased disk access. (I would have used the same interpreter for the benchmark, but Zeugma needs a REU by design, and Ozmoo crashed on these games in REU mode.) Part of the reason for the speed boost is that Å-machine storyfiles are only about half the size of their Z-machine counterparts. Each of these games now fits comfortably on a single disk side, along with the interpreter.

To put this in context, the original Hitchhiker’s Guide for the C64 (using Infocom’s own interpreter, and paging from disk) has response times of around 1–5 seconds. With a modern interpreter (e.g. Ozmoo with a REU), this can be reduced to half a second. So Dialog games are now on par with classic Infocom games in terms of performance, despite the fact that Dialog is a high-level, rule-based language with a complex in-game parser and standard library, more in the spirit of Inform 7 than ZIL.

In the long run, I hope this will open the gates for lots of modern, narrative-driven text games for the Commodore 64!

Updates to the specification

The original Å-machine specification document was written in tandem with the compiler and the Javascript interpreter, and went through several rounds of editing. As I wrote the 6502 interpreter, I went back to the specification document with fresh eyes, and discovered several errors. Another person, who I suspect is working on an interpreter of their own, also reported some. All of these errors have now been fixed, a couple of corner cases have been clarified, and the input tokenization process is described in detail. The document version number has been incremented to 0.3.

New option in the web player

The web player menu now contains a checkbox called “Always re-focus”. When this mode is enabled, the browser will move focus to the input text field after every move, regardless of whether the move was typed or clicked. This can be desirable (according to taste) when playing on a desktop computer. By default, the mode is disabled, and focus does not move when clicking on a link. This makes sense on mobile devices, where focusing on a text field will often bring up an on-screen keyboard that cuts the amount of visible text in half.

8 Likes

6 Likes

Get version 0.4.1 of the Å-machine toolkit! The archive contains version 0.4 of the specification, and updated versions of the Å-machine toolchain and interpreters. This is a companion release to Dialog version 0j/01.

Release notes:

  • Several improvements and clarifications in the specification document, including an attempt at formalizing the output model.

  • Spans, i.e. inline text segments with style attributes.

  • Support for hyperlinks where the target text is determined from the displayed text.

  • Clear Links opcode to transform old hyperlinks into plain text.

  • Ability to check whether a value is a dictionary word that doesn’t appear in the game dictionary (i.e. is represented internally by a list of characters).

  • Ability to split and join dictionary words.

  • Inhibit whitespace around certain stop characters when they are printed as a value.

  • New runtime error: Invalid output state.

  • Several new opcodes and opcode variants to reduce bytecode footprint.

  • Better text string encoding.

  • The javascript engine now handles extremely long input lines gracefully.

2 Likes

Version 0.4.2 of the Å-machine toolkit fixes two bugs that caused the various interpreters and tools to hang on very small story files. Thanks @Juhana!

2 Likes

Here’s version 0.4.3 of the Å-machine toolkit. There are two new features in the web interpreter:

  • Progress is automatically saved in local web storage. You can close the tab or browser, and come back later to find the game as you left it. Try it right now with Tethered or Pas De Deux!

  • Clicking/tapping anywhere in the main text area (except on a link) moves focus to the input field. Tapping on the status bar takes focus away, which is useful for hiding the on-screen keyboard on a mobile device. Selecting text for copy-paste should work as before.

6 Likes