Okay, so Z-machine story files are officially big-endian. I wrote my interpreter to have a “word” class which deals with this, and it’s fine… but I wonder what the old 8-bit platforms did and/or newer interpreters like Ozmoo do?
The “right” way to solve it would be to produce native endianness when the story is generated for that platform. But that defeats the purpose of having the same story file work everywhere.
But there also isn’t really enough information in the Z-machine format to properly byte-swap all of the data at load time either? I mean, you can do parts of it, like immediates in instructions, but there’s nothing preventing somebody from accessing an array of words as an array of bytes?
(Even immediates in instructions is tough, if any game uses any of the old 6502 tricks like overlapping two instructions to avoid a double branch)
It’s pretty much impossible to alter the story file to match the endianness of the target platform, either at compile-time or when bundling the story with an interpreter for a certain platform. Neither program can know for sure if the numbers will be operated on as 8-bit or 16-bit numbers, or a mix of the two.
While writing Ozmoo, I found that there was never an instance where it would have been an advantage if the story file would have used a little-endian representation of 16-bit numbers. Since the 6502 has no instructions to do anything at all with 16-bit numbers (read, write, copy, perform arithmetic operations…), it’s just as fast and simple to perform operations on big-endian numbers.
Of course, the 6502 can use 16-bit numbers to describe addresses in its 16-bit address-space, but that doesn’t matter here.
If I was to write an interpreter in a high-level language like C, it would be a different story. These languages are generally too slow to be of any use for this task on 6502-based machines though.
(There may be a few places in the code where the MEGA65 and Commander X16 versions of Ozmoo could have benefited from 16-bit operations, if the story file had used little-endian numbers, e.g. the MEGA65 CPU can increase or decrease a 16-bit number, but these platforms are fast enough that it gets less important, and I don’t think it would have made a noticeable difference.)
Ah, good point. I was thinking any indexed memory access would go through zero page, but since there are no 16 bit load/stores anyway, you just do the byte swap when generating the zero page address.
We typically use indirect-indexed addressing ( Indirect-indexed addressing - C64-Wiki ) - you make sure a zeropage vector points to the first byte, load the value, store it where you need it to go, increase index register y, load the next value, store it. It’s just as easy to do for big-endian or small-endian numbers.
If the code benefits from reading out the lowbyte first, you can set the index one higher from the start, read that value, decrease the index register and read the next value. We do this in some instances as well.
Endianness is probably only relevant for the stack. On non-8bit machines you can store the stack in its native endianness. You only need to byte swap it when saving to Quetzal. (Though for autosaves you could leave it in native.)
I believe there’s a note in Infocom’s early specs that one of the bits in Flags 1 would have marked the endianness of the story file, so you could generate big-endian story files for some platforms and little-endian files for others. But this was never actually implemented because it would be a colossal headache for the compiler.
Found it! (Thanks to this thread, which had me looking at the original Z-specs again recently.) In Infocom’s Z3 spec, page 16, bit 0 of Flags 1 (which they call “the mode byte”) is the “byte-swap” bit: “0 means words are high-order byte first, 1 means words are low-order byte first”. All surviving story files have this bit set to 0, meaning big-endian.
In the modern spec, bit 0 of Flags 1 is unused in Z3. And in Infocom’s Z4 and Z5 spec, page 39, this bit is redefined as “upper window available” in Z4 and “color available” in Z5. (The modern spec defines it as “color available” for both Z4 and Z5.)