A Month of Reconstructing ZZT

By: Dr. Dos
Date: April 17, 2020

Well, it's been just over a month now since Asie surprised all of us with The Reconstruction of ZZT, a recreation of ZZT's lost source code. ZZT and Zeta are now MIT licensed and the sky is the limit. Let's take a look at the new discoveries, solved mysteries, cracking bugs, new features, requested features, investigations into other versions of ZZT, and of course the current forks in development to get an idea of just what's been going on lately.

New Discoveries

Honestly, I did it in no small part because I wanted to give people a readable reference for ZZT behavior — Asie

One thing having source code available means is that no matter how esoteric the behavior, you can find an answer for why. The reconstructed source provides details on previously ambiguous aspects of ZZT and has even revealed a few new ones.

Command line Switches

Until the release of the reconstruction nobody knew that ZZT has some options that can be passed at the command line.

ZZT.EXE /R forces ZZT to run its configuration. In ZZT v3.2 this always happens anyway, but the switch is still parsed. In ZZT v3.0 this is a much easier option than deleting the ZZT.CFG file entirely.

ZZT.EXE /T adjusts the method ZZT uses for timing! By default ZZT uses the system time interrupt 21h for its timing. If this argument is passed it will instead use the timer interrupt 1Ch. If you're running ZZT on a system with a malfunctioning real-time clock, this could be of use?

Unusual Stat Limits

ZZT has a MAX_STAT value of 150. Everything in the engine respects that limit but one element wants to be a rebel: Duplicators. Thanks to some better bounds checking when spawning something with a stat there isn't actually a bug here, but duplicators make their own (duplicate) check using a stat limit of 174.

Why the discrepancy? Who knows! Stranger still, ongoing attempts at reconstructing Super ZZT have revealed that its duplicators still have the same check. This time the comparison assumes a stat limit of 152. Super ZZT also has a reduced stat limit of 128 which rather oddly means that both ZZT and Super ZZT have a check that's off by 24.

Shared Code

ZZT is a rather efficient little program that was capable of running on some very low-end machines quite well even in its earliest days. One thing you notice after years of working with it are the obvious instances where generic code gets to handle multiple scenarios.

Most obvious is that if you name a board $Hello or !label;Hyperlink and bring up the board list in the editor, the board titles are parsed with the same text formatting they'd get as if they were lines of ZZT-OOP! The fact that hyperlinks and labels work in board titles and the board list is the basis of the notorious Super Lock.

The reconstruction has let us confirm that the code handling drawing torchlight and handling bomb explosions is also shared. If your modification wants to increase the radius of torchlight, it's going to increase the blast radius of bombs as well unless the two are decoupled.

Mysteries Solved And New Mysteries

The Length of Silence

Sometimes ZZTers have wanted to mask sound effects. Some earlier fan-hacks remove various sounds entirely. Probably the most common example is to mute duplicators on boards that duplicate them onto a player. These boards will have duplicators chugging along until it's time to let them duplicate onto a player and force a board transition. They get pretty noisy though! Lucky for us ZZT's #play command overrides default sounds. A solution is to have an object play a bunch of silence with #play xxxxxxxxxxx to play a bunch of rests. This gets put in an audio buffer which carries on to the next board. To my knowledge nobody ever bothered calculating documenting-in-a still-readily-available-manner the length of one cycle's worth of silence, enough to quiet the built-ins but allow sound to resume immediately. This one's on me, such information is unsurprisingly contained in a release of #play

Asie helpfully explained that it's as simple as a single 32nd note.

#play tx /i#restart

This code will provide the minimum amount of silence needed!

Well... almost. If your silence lasts for a single cycle you need to make sure the object producing it is before anything that makes noise in the stat order, and the player will always be ahead of it, so player shots will still make noise with this solution.

Limits

An early question for Asie after the project was revealed was if there was any reason for the stat limit to be 150. Obviously it's a nice round number, but people were curious why not 200? Or 255? Or 1500 to fill a board entirely?

Turbo Pascal (at the time) had a hard limit of 64K for statically allocated data, meaning it would be possible to run out of space when creating a larger stat array.

The Skipped Element

Anybody who's taken a look at ZZT's file format (or even manually changed a tile's element in KevEdit) has come across the list of elements and noticed "Element 46". There's this one element that goes unused. Cut content? The editor cursor? Yet another text color?

Nah, it's just undefined. Asie's speculation is that it was originally white-on-black text that got moved to the end of the list. Since the "white text" ZZT produces is white-on-black rather than white-on-dark-white, it makes sense for Element 46 to be "black text", which would fit the white-on-dark-background-color format all the other text characters use.

Kryptonite Text

At the end of the element list in the file format are the various colors of text. In the earlier days of hex-editing ZZT worlds, there was an obvious interest in seeing what would happen if you changed element IDs to a larger number than any of the known elements. Sure enough, you get more text colors!

screen9
This text is actually blinking. Also #54 is visible in the dark??

(Err... don't ask why the colors are all shifted I took this from an existing example since otherwise I'd have to hex-edit some fresh text.)

A pretty sensible theory (that turned out to be correct) is that after passing a certain threshold for element IDs, ZZT treats everything afterwards as text. The colors to use are calculated based on the element ID, and so going beyond the defined seven colors just continues this trend. What makes this text "Kryptonite" is the fact that touching it will usually crash ZZT.

Now we know why, and it's exactly as you'd guess. ZZT defines a default set of properties for an element and then creates information for all 53 used elements populating unique data for each element when it's there like a name or what key accesses it in the editor. If you go beyond this range, the element is drawn as text, but when something interacts with it, it doesn't even have those default values to point to and typically explodes.

ZZT's Audio System

Always impressive and always a mess, replicating ZZT's sound accurately has been a tremendous pain for ages. With the sound system exposed it's very much possible to port it elsewhere and get fully accurate notes for ZZT's own sound effects. Internally (in some cases at least), ZZT uses an entirely different format for defining sounds than the system used with #play. If you're looking for a project, getting a 100% accurate ZZM player would be a treat.

Why Can't You Push Torches?

You can push ammo. You can push gems. You can push keys. You can push scrolls, bombs, boulders, sliders, and creatures. You can't push torches.

I don't know, it always bugged me. My only guess was that torches are unique in that they are visible in dark rooms and don't have stats. I figured perhaps if you pushed a torch in the darkness the board might not update its display properly? Well, thanks to being able to easily modify properties I made ZZT with pushable torches and got to see what happened if you moved them around in the dark. The answer: nothing, they pushed just fine and displayed just fine. So now it bugs me even more.

Maximum Files

Years ago I said "to hell with it", and mass extracted every ZZT world in z2's /zgames/ directory into a single folder.

ZZT does not like this. This isn't all that surprising, but we know what the actual limit is now. Text windows are limited to 1024 entries. More than that is a bad time.

unkArg1

One of the last variables in the deconstruction to be given a proper name was an argument sent when calling functions related to touching elements. This has turned out to be the stat ID of the element doing the touching. In every single instance this is 0 as the player is the only element that can touch things. This raises the possibility that there was consideration for objects or other elements to be able to send touch messages as well, something that would have dramatically altered ZZT. It's speculation, of course but definitely fun to consider.

Instruction limits

Objects running ZZT-OOP are only allowed to execute 32 instructions per cycle. Some like movement commands immediately end the cycle after their execution, while others let code keep being processed on the same cycle. One interesting edge case is the discovery that following a hyperlink in text resets the count. Presumably this is so a series of links that travel back and forth between each other aren't suddenly cut off if the player spends too many actions navigating choices.

Understanding Bugs

Press ENTER to select this

Nagohkluh asked about a minor ZZT bug and was curious what its origins were. In some instances when displaying messages in a scroll window when the cursor is on a blank line of text the top of the scroll will display "Press ENTER to select this" as if the line was a hyperlink. After finding a reproducible instance of the bug Asie dug into it and found the cause

enter

Essentially when one scroll closes and a new one opens, the new one is written right over the old one in memory. A check for what to display in the top of the window uses the first character of the currently selected line, and if the line in the new scroll is blank (meaning length = 0), it will still try to read the first character, which will remain whatever the first character was of the old scroll. So if your third line starts with a "!" in the old scroll and the third line is blank in the new one, ZZT will see the "!" still and display the "Press ENTER" line accordingly!

While I know I've seen this bug in the wild a few times, I don't know how aware of it people were. I have seen several ZZT worlds use "$" for otherwise blank lines of text which would defeat the bug and result in a blank line as "$" activates the centered + white text mode.

Fixing Bugs

While plenty of odd emergent behavior has been used to enhance ZZT, some of its bugs have never been all that beneficial. Numerous fixes have already been implemented.

Some, such as fixing the broken message when picking up a black key or opening a black door is as simple as adding "Black" to the array of color names and increasing its size by one. Others, like... making them not give 256 gems are a little more involved.

Other fixes include #put not working in the bottom row. ZZT crashing when a scroll moves of uses #become.

Asie has a patch available that allows to either unpause the game by pressing "P" again or by merely attempting to move (rather than successfully moving). This fixes a common softlock when the player pauses while surrounded, or more likely, restores a saved game where they're surrounded and the game starts paused.

(Because it's possible to touch an object while paused and then move out of the way, the latter unpausing method can break using this as an exploit as well as breaking a puzzle in 2019's Yuki and the Space Show. I am very much on team "Press P again".)

Adding Features

Rewriting Memory

One limitation of ZZT that offers no benefit and has been the bane of many ZZTers is its memory allocation. ZZT runs entirely in conventional memory which restricts world sizes based on available resources. One of the first major overhauls Asie pulled off was adding support for EMS memory,

elise

Now massive worlds like the 400+ KB For Elise have some breathing room. The debug information in the above screenshot shows the game running with more than 400KB of conventional memory and 134 pages of expanded memory available. This memory change applies to the currently loaded board as well allowing for a board size limit closer to 65,000 bytes. Worlds using this system meanwhile, can be measured in megabytes.

New Cheats

A very approachable way to make changes to ZZT without a lot of knowledge of its code and/or Pascal is adding new cheats. While the extremely open nature of ZZT worlds means it's easy to adjust a game to your liking via editing, it can be a bit of a pain to drop what you're doing and break out the editor, possibly having to mess around with renaming save files to load them in KevEdit. Cheats are a fast way to make broad changes to a game experience. A few new cheats that others have coded in include:

Having Fun

Asie comes up with an alternative board transition style:

My first critical change was to fix the water to match the water found in DEMO.ZZT. (Fun fact, this actually breaks the ability to specify colored water in ZZT-OOP!)

Other Versions of ZZT

The obvious question after reconstructing ZZT v3.2 was if there'd be a reconstruction of Super ZZT or earlier releases of ZZT. Since the initial release of RoZ, Asie has been chipping away at Super ZZT and Mr. Alert, a former developer of z2 has taken up the challenge of cracking ZZT's earlier releases. But first, a helpful rough ordering of releases:

For the longest time, older versions of ZZT were considered novelties at best, but now they can be better understood. The purchase of a 5.25" floppy labeled "ZZT's Labyrinth" in 2018 caused a small uproar when it got us to finally notice ZZT 3.0's ability to skip the game configuration screen. Attempts were made at figuring out the specifics of its extended configuration file, but it's only now that we have a complete understanding as Mr. Alert has been reconstructing ZZT v3.0 along with discovering numerous other changes differentiating it from ZZT.

ZZT v3.0

This list is taken verbatim from Mr. Alert

Worth noting is the lack of 40-column mode support. This makes sense for ZZT v3.0 as it appears to be the version before Super ZZT's initial release. As the two engines share a decent chunk of code, ZZT v3.2 contains some references to Super ZZT features that go unused in the original.

zzt30mono
ZZT v3.0 in monochrome mode
zzt31mono
ZZT v3.1 in monochrome mode

"ZZT 3.0 also preserves the blinking attribute, while 3.1 does not."

zzt30herc
ZZT v3.0 on MDA
zzt31herc
ZZT v3.1 on MDA

Mr. Alert has also documented the changes for ZZT v3.1 as well!

ZZT v3.1 (Shareware)

This list is taken verbatim from Mr. Alert

There's nothing too cool here other than the discovery of a bugged ?-DARK cheat. Imported boards not clearing their exits was known, and actually gives 3.0 (and 2.0 which shares the bug) some useful functionality today. Asie used it when stitching together Outskirts of Zweldronia!, an incomplete world with a corrupt board known only to exist in exported board form. By importing the boards in the proper order with this version, the board connections were preserved instead of needing to be recreated.

ZZT's Revenge

Mr. Alert also pointed out that the version of ZZT v3.2 included with ZZT's Revenge actually has a single tiny difference from the normal v3.2 implying that the copy in Revenge predates it.

Wishlists

And of course, you aren't suddenly given complete control of the code-base without daydreaming a few feature requests. These are just a few of the ideas people have expressed interest in being added to ZZT:

Current Patches/Forks and Features

OpenZoo

OpenZoo is a project of Asie intended to collect various patches to ZZT's code-base, allow users to choose which patches to apply, and generate a version of ZZT with them. Currently it is just a collection of various patches with no interface for applying them.

Features:

Asie has in particular shown interest in creating a "ZZTRUN", a stripped down ZZT v3.2 that removes its editor and reduces memory and file size as a drop-in ZZT replacement for the modern ZZT use case of ZZT + Zeta + KevEdit that would also have the benefit of freeing up memory for playing ZZT worlds on DOS machines where the editor isn't of interest

Libzoo

Libzoo is Asie's re-implementation of the ZZT engine that plans to port it to a portable C library. The intent of which is that by porting in such a manner that it becomes possible to create front-end implementations of the ZZT engine and aid in its ability to run it on all sorts of hardware versus a more modern programming language focused on Windows/Mac/Linux implementations.

I want libzoo so I can port ZZT to more platforms and I want libzoo because some platforms can't just handle an identical ZZT interface: think phones, or TVs, or consoles — Asie

ZooAdvance

Asie has set ZZT free of MS-DOS and now has it running on... the Game Boy Advance...

ZooAdvance running Town

Okay, so it might not seem like the most relevant thing to port libzoo to, but it's a good example of what libzoo allows to be accomplished. The GBA hardware has a 240x160 resolution, 256KB of memory and a fairly limited number of buttons. It's not hardware that should directly be able to run ZZT, but Asie has been making good progress on it as low-end target for what ZZT can manage to run on. It's so tiny!

Zoo64

While Asie takes the long term approach of getting ZZT on everything under the sun, GreaseMonkey has gone with the alternative (and just as valid) goal of "ZZT on modern machines". His own project, Zoo64, takes the approach of compiling ZZT with a modern version of FreePascal.

The Linux Reconstruction of ZZT

GreaseMonkey isn't the only one interested in getting ZZT working with FreePascal. Over on z2's forums, a user known as The Mysterious KM has shared The Linux Reconstruction of ZZT. This is definitely the attempt that's farthest along currently. A few features (sound, mouse, and joystick support) have been temporarily cut to focus on getting ZZT running in a terminal window first and foremost. However, depsite those omissions (of which only audio is missed), the Linux Reconstruction has made excellent progress, with success in running complex ZZT programs such as the Mandelbrot renderer in Preposterous Machines!

Mr. Alert's Demo Branch

While not a full-fledged port, Mr. Alert has been working on adding demo recording/playback functionality to ZZT. This would be a boon to potential speedrunners and those wishing to create a Tool-Assisted speedrun.

This has of course lead to realizations like how adjusting game speed adjusts how many cycles messages are displayed to ensure there's time to read them. This can desync demos thanks to how messages use stats!

Dos's Branch and Patches and It's Just a Big Mess Sorry

Features:

I couldn't not get in on this of course. I mean, you already saw my amazingly improved water. I've yet to commit anything serious and have been waffling between focusing on making patches for OpenZoo or committing to my own branch aimed towards my own personal goals of making ZZT friendlier for those who have never used it as well as making it something that can benefit me when doing streams of ZZT worlds or capturing screenshots for articles.

But nonetheless, I've done some experiments here and there with it.

The (Marginal) Silliness of ZZT

RT-55J has a fork that exists just to be more of a playground for experimental features just to see what can be done to ZZT's gameplay called The (Marginal) Silliness of ZZT. It's not intended for serious use, but it is definitely showing off a lot of new functionality that a game could base itself around.

Features:

FreeZZT

Zzo38, has also been working on a fork that has a bit of a variety in new features and fixes and is the first fork to play with the idea of extending ZZT enough to change its world format, focusing on creating new possibilities while maintaining support for classic worlds as well. FreeZZT is the most ambitious fork yet, and as its develops it will be interesting to see what comes of it! Zzo plans to focus solely on DOS support with Zeta and minimize ZZT's memory footprint as much as possible resulting in a lot of rarely used features being stripped away.

Features:

Honestly I absolutely love having timestamps for saves. That is such a good idea. Zzo also has a list of some in-development features available as well.

All in all, it's been an extremely productive month! There's a lot of knowledge being shared in the Worlds of ZZT Discord which has a channel dedicated to the reconstruction and things built from it. For those interested in making their own modifications, ZZT's code-base is surprisingly straightforward, and those of us with experience in programming in non-Pascal languages have been able to jump right in. Experimentation is highly encouraged as we all figure out what the future of ZZT as engine might be like where limits are gracefully lifted and what will or won't catch on and be used when making ZZT worlds.

====== A Worlds of ZZT Production ======

The Worlds of ZZT project is committed to the preservation of ZZT and its history.

This article was produced thanks to supporters on Patreon.

Support Worlds of ZZT on Patreon!
Article directory
Main page