Ozmoo - A Z-machine interpreter for the Commodore 64


#21

Agreed. Also note that this flag disappeared in version 4. I bet the people who designed the Z-machine did their best from the start, but learnt a lot from creating games using the Z-machine and so they created several new versions of the Z-machine over the years, fixing mistakes and adding new features.


(Linus Åkesson) #22

Hmm. I mean, yeah, I agree with the two of you that it’s inelegant. But I think it’s a slight category error to refer to this as a slight category error.

Infocom made all these games, and this system. Then the system was reverse-engineered, and through that process we (the community) reached a common understanding of what the various parts of the system were supposed to be doing. The role of the interpreter is this, the role of the game is that. But if it turns out that our understanding of the system is slightly askew, then surely the error is in our understanding. Otherwise it’s like claiming that the universe is wrong to be relativistic, since Newton’s laws are so much simpler.

It’s not that hard to come up with a use case for it: A game might want to supply custom messages for save and restore actions. After a successful save, a game might print “Shiver me timbers! Yer progress be saved. Now replace the game disk into the ol’ floppy bay, and hit ye return, old salt!”. But if the game is distributed on a two-sided disk, with the swap-on-demand data on side B, it should perhaps print “Now replace the game disk, keel-side up, into the ol’ floppy bay” instead. Hence the need for a header bit, so the game can distinguish between the two cases without breaking character.


(Linus Åkesson) #23

Less fanciful rationale: We know that some v3 games would fit on a single Atari disk, and some would need two disks. It might not be possible to distinguish between end-of-file and load error. So there needs to be some way for the interpreter to know whether it’s loading a single-disk game or a multi-disk game. For a multi-disk game, loading is supposed to stop exactly at the resident-memory boundary (where we prompt for disk two). This could be solved by having two versions of the interpreter (one for small games and one for large games), but that leads to double-maintenance and a risk for mix-ups. It’s much easier to have a single Atari interpreter, and then to include the unmodified game file if it fits on the same disk, otherwise split it and set a flag in the file that we’re splitting.


(Andrew Plotkin) #24

We’ve been implementing the Z-machine and writing games for it for more than twice as long as Infocom did.

We can say they were wrong if we want to. :slight_smile:

I don’t know how the Atari disk layout works, but it would be just as sensible – probably more sensible – to include a block of interpreter config information which is not part of the Z-code file.


(Linus Åkesson) #25

By modern standards and sensibilities, I agree. But the world of microcomputer software was different back then. I looked up the Atari disk format. Each disk comprises 720 sectors, and each sector is 128 bytes (of which up to 125 are actual file data). Adding an extra configuration file is going to eat up a whopping 128 bytes, whereas setting an otherwise unused bit in the header has zero overhead. For a game with a size that’s already close to the limit, that extra sector might push it into a two-disk game, doubling the mastering costs.

Of course, it would have been possible to solve this with zero overhead in a number of ways. Encoding it in the filename comes to mind. But again, perhaps the people at Infocom viewed the z-file header in a different light than we do now. Perhaps they thought of the header as precisely such a config file.


(Piergiorgio d'errico) #26

perhaps the “different light” is that the header is akin to the metadata of today ? (just awake, my apologies if I have wrote something stupid…)

Best regards from Italy,
dott. Piergiorgio.


#27

Coincidentally, this is exactly what Ozmoo does. It stores all config information on track 19, sector 0-1 on the boot disk. This includes data on:

  • Which disks are part of this game (2-4 disks) and what their names are
  • Which sectors on each disk are used for story data
  • The sector interleave factor used
  • Which parts of the story file are pre-loaded into memory when the game has loaded
  • Which parts of the story file are suggested for pre-loading before the game starts but the interpreter will need to load them itself, if it wants to
  • Build ID, so the interpreter can tell if a certain disk belongs to the same build.

(Linus Åkesson) #28

Here’s yet another angle. (I’m not trying to be contrarian, I just think this is an intriguing philosophical question that ties into IF system design and reverse-engineering.)

Consider a present-day Z-code interpreter that doesn’t support pictures or sounds, but is capable of loading both zblorb files and raw z-images. Once the game is up and running, this interpreter only needs to be concerned with the raw z-image. But when loading the game from a zblorb file, it needs to extract the z-image from inside it. So there are two different ways of loading the same game, depending on how the game data is organized on disk. Sound familiar?

How does the interpreter distinguish between the two cases (extracting from zblorb, vs. treating the entire file as a z-image)? If the ultimate goal is to have clear-cut categories, information about the container format (or lack thereof) needs to be stored outside the game file. This follows from the same argument that says that the Atari interpreter shouldn’t deduce the file format based on a header bit. For our present-day interpreter, the format information could be supplied as a command-line argument, or by the filename extension, for instance. But in practice, what any well-designed interpreter will do is to look at the header of the file. If the zblorb magic-number is there, use the zblorb loading routine. Otherwise, use the raw z-image loading routine.

Now, a possible counter-argument is that the zblorb header is sealed off from the game data. While it is stored together with the z-image in the same file, there is no way to access this data from within the running game, and because of this strict separation, there is no category violation. But the z-image header is part of an interface between the game and the interpreter, and interfaces are essentially contracts; they can have arbitrary rules. Suppose there’s a rule saying that the running z-code isn’t allowed to depend on the value of this flag. For instance, that the value of the flag is undefined once the game is running, except that in all current implementations it just happens to remain at its original value. Such a rule provides just the same strict separation, by design! But it’s impossible to deduce that such a rule exists, merely from reverse-engineering the existing implementations.


#29

The header of a Z-code file isn’t a file header at all. That’s why there is no magic number in there saying it’s a Z-code file. The entire file is a memory image of a computer, the Z-machine, and the header is the first part of that memory image.

Every single bit in the header is there as a means of communication between the Z-code program and the computer it runs on (the Z-machine), except for the bit we’re discussing here. The Z-code program couldn’t care less about whether it’s distributed on one or two disks, so there’s no need for communication regarding that fact. That’s why this information shouldn’t be in the Z-code header.


(Linus Åkesson) #30

And yet it moves.


#31

Project update:

We have put a lot of work into Ozmoo lately. Things that have happened since howtophil posted an archive with Infocom games built with Ozmoo:

  • Numerous bugs have been fixed
  • Printing routines can now sometimes put more text on a line than before
  • It is possible to build games larger than one 1541 disk, fitting them on two or three disks
  • Ozmoo has become quite a bit faster
  • Save/restore has been implemented
  • An option to embed a custom font with a game has been added
  • Support for custom alphabet tables
  • Support for custom character mappings, allowing for games with accented characters

There are still bugs. We think all z3 games should be fully playable. Borderzone has lots of problems and can’t really be played.

If you want to try some games using Ozmoo, you can download this little package, consisting of the z3 version of Curses (with a custom font) and z5 game The Temple (with the normal font):

microheaven.com/ozmoo/Ozmoo_samp … -12-03.zip

The project homepage is still at github.com/johanberntsson/ozmoo


#32

Nice!


#33

Ozmoo should soon be ready for release.

You can all help us focus our efforts by answering a very short poll:

goo.gl/forms/AvHABSapn4rTFDxU2

Thank you!


#34

Looking forward to it :slight_smile:


#35

We now consider Ozmoo to be ready for real-world use. Brief summary of what it is:

Ozmoo is a brand new Z-code interpreter for the Commodore 64. Ozmoo comes with a Ruby make script which bundles the interpreter with a game file and places the resulting game on one or more D64 disk images. The game can then be played on a real C64 with a 1541 disk drive or a replacement for the 1541, like an SD2IEC, 1541 Ultimate, Pi 1541 etc, or using a C64 emulator. Smaller games can be played on a system without a disk drive.

Ozmoo has the license GPL 2.0. This means anyone can use it for free, redistribute it, use it in games which they give away or sell etc. If you distribute a modified version of Ozmoo, you will need to publish the modifications. Ozmoo was written from scratch in 2018 and does not contain any of the interpreter code which was distributed with Infocom games and owned by Infocom.

Some things Ozmoo supports:

  • Z-code version 3, 4, 5 and 8.
  • Timed input, as used in Borderzone.
  • Named saves (Use a blank, formatted disk for saves, in the same drive as the game, or in a different drive)
  • Customizable colours.
  • Embedding a custom font (character set). One font is included with the distribution, but you can also supply your own font or, of course, use the system font.
  • The interpreter and as much of the story file as possible is stored in compressed form in a single file, which means loading is quick if the player has any kind of fastload cart.
  • The person bundling the game with Ozmoo can optimize which parts of the story are loaded with the initial file, to minimize disk reads at the beginning of the game.
  • A small game (story file < about 50 KB) can be stored as a single file which does not require a disk drive to play. Save/restore does require a disk drive.
  • A game up to about 170 KB can be stored on a single 1541 disk side, and played on a system with a single 1541 drive.
  • A game up to about 190 KB can be stored on two 1541 disk sides, and played on a system with a single 1541 drive.
  • Ozmoo fully supports using extended tracks, raising these limits by about 20 KB. However, not all emulators support extended tracks, and neither does any version of SD2IEC.
  • Larger games can be built and stored on two or three disk sides, but they will require a system with dual 1541 drives to play.
  • Custom alphabet table (typically used for non-English games)
  • Custom character mappings (Needed to play non-English games because accented characters don’t exist in PETSCII)

Please note: The Commodore 64 has a 1 MHz 8-bit processor and 64 KB of RAM. Games using a lot of CPU and/or running massive amounts of code on each turn will not be fast enough to be any fun. Infocom games are fast. Inform 5 and 6 games are usually fast enough. ZIL games should be fine. Inform 7 games aren’t really playable.

Go to Ozmoo’s homepage for more information and downloads: github.com/johanberntsson/ozmoo


#36

This is fantastic!


#37

Yes, it is!

I try to embed a font character set (cpm850-08) for French accents, but I don’t understand the positions of characters in the table.
In clairsys.fnt, the first characters are @, a, b

update
I found this:
https://www.c64-wiki.com/wiki/File:Zeichensatz-c64-poke1k.jpg
Is this the right table? But there are only 128 characters.

update
All right, I think I get it. I turned clairsys.fnt into a bitmap image to understand how it works.
http://auraes.free.fr/tmp/clairsys.png


#38

The program displays the printable characters.

[ Main i key; for (i=32 : i<=126 : i++) { print (char)i, " ", i, "^"; } for (i=155 : i<=251 : i++) { print (char)i, " ", i, "^"; } @read_char 1 ->key; ];ruby make.rb -P test.z5 x64 test.d64In DM4 p. 520: “Characters 155 to 251 are configurable using the directive Zcharacter.” But characters 156 to 159, seem to change the color of the characters.
And some characters, in lower table, are different from the DM4 lower ZSCII character set.


#39

It’s great to see someone is trying out the possibilities that Ozmoo opens up for non-English games.

The Z-machine uses ZSCII to encode characters. The C64 uses PETSCII. In order to create a Z-code interpreter on a C64, we need to create a mapping between these encodings. This is done in code in two places:

streams.asm: translate_zscii_to_petscii
text.asm: translate_petscii_to_zscii

One basic part of the mapping is taking into account that all letters, both lower case and upper case, move around between ZSCII and PETSCII. This happens in code in the subroutines mentioned above. What these subroutines also do, is look at the three mapping tables (character_translation_table_*) which are defined at the beginning of streams.asm. There is one table for codes that only need to be mapped for player input (PETSCII -> ZSCII), one for codes that need to be mapped both for input and output, and one for codes that need to be mapped only for text output (ZSCII -> PETSCII).

Now, you want to start using accented characters.

Since the C64’s character encoding (PETSCII) doesn’t have any accented characters at all, you need to replace some of the characters it does have with ones that you would like it to have, This is done by adding to the mapping tables AND replacing them in your custom character set. There is already (optional) code there to make it work for the Swedish characters. This code uses the standard substitutions which Commodore themselves used for the Swedish version of the C64:

] is replaced with å
[ is replaced with ä
£ is replaced with ö
(And similar codes for uppercase letters)

Since we’re using the same replacements that Commodore used, a game using this mod will work perfectly with a Swedish C64. When you type “ö” on the keyboard, you see “ö” on the screen, and it is encoded as a ZSCII “ö”.

In fact, while you are at it, you may want to map the capital letters to the corresponding lowercase letters for input (but to capital letters for output), or player input won’t be recognized if the player types a capital accented letter. I haven’t done it this way for the Swedish letters, since I didn’t think of it until now. Maybe skip this step for now, and try to get the basic stuff to work first.

You can PM me if you have further problems.

BTW: Which characters didn’t match in the lower table?


#40

BTW: This is the best tool I’ve found to edit C64 character sets: csdb.dk/release/?id=110 . It’s for Windows. It’s really old, but works fine under Windows 10.

And yes, the C64 only has 128 characters in a character set, plus 128 more which are inverted versions of the first 128.

The C64 character ROM has two character sets in it, each consisting of 256 characters:

1: Uppercase letters, digits, special characters (!#$%<>=., etc) and graphic characters.

2: Uppercase letters, lowercase letters, digits, special characters and (fewer) graphic characters.

Ozmoo always uses character set 2.

If you have a copy of a C64 character ROM, you can extract the second half (2048 bytes) save it, edit it if you like, save it as a file and use it as a font file with Ozmoo.