Jump to content

Zsound ZSM format update - request for feedback


ZeroByte
 Share

Recommended Posts

While interacting with the Furnace community about integrating a ZSM export function into the tracker, the specification for the ZSM format has been requested. While sprucing up the docs, I realized that the current loop and PCM offset pointers are unnecessarily confusing.

They were originally designed to be in the same format as the ZSM player routine's HiRam pointers: 2-byte memory location followed by 1-byte bank number. If a ZSM is loaded to $A000 then simply adding $A000 to the header's values will yield direct pointers to the exact memory locations of the loop point and PCM data tables. However, in practice I didn't want to require that ZSM files be loaded into "Bank-aligned" locations, so the player has to manipulate the data anyway.

So, I think it makes sense to convert these two values into just being 24-bit raw byte offset values. Having said this, my question is this: Does it make sense for these offsets to be relative to start-of-file (including the header bytes) or start-of-data?

Personally, I think relative to start-of-file makes the most sense (this would exclude a 2-byte PRG header, which I plan to eliminate once it becomes possible to fully utilize the headerless load feature from the BASIC "prompt"). Using start-of-file means that the player simply needs to add the load-to address to the value and convert the result into ADDR:BANK format.

TL/DR: Should the loop/pcm byte offsets in the header be relative to start-of-data or start-of-file? (my vote = start-of-file)

Link to comment
Share on other sites

And the calculation is:
Location = (Load_Addr + size) & $1FFF | $A000
Bank = (Load_Addr + size) >> 13 + load_bank

I'm thinking I may use one of the 5 reserved bytes in the header as "precalculated" status for the loop/pcm offsets, and store the results of these calcs back over the header in RAM at runtime so it only has to do this calc once.

  • Like 1
Link to comment
Share on other sites

I've gone with start-of-file for the offsets in the headers. This made the format specification much easier to follow. For now, the engine converts bytes into the direct memory locations every time a song is started, but in the future I will make it modify the copy in memory so that it only needs to happen once. The main sticking point is that I need to come up with a rock-solid, reliable way to verify that this transformation has already been done. I think the best thing is to use the final "reserved" header byte as a "done flag" byte - specifying that a ZSM file MUST have a zero here, but what happens when a file has a non-zero value there anyway? Crash/random behavior? I'm also considering providing a built-in load routine but the number of arguments it would require is fairly large, but would allow me to control the headerless loading behavior / guarantee that data is freshly-loaded and just overwrite the modified pointer fields with their in-memory versions. But obviously nothing would stop programs from just loading a ZSM and playing it w/o using the LOAD feature....

Another idea is to provide an init-song function that performs this task, and specify that this needs to be called on songs after they're loaded. It would speed up the start-song function a bit (saving about 1500 cycles already, not counting the work that will be done in future when PCM track support is done), and songs with FM are typically front-loaded with patch loads which are VERY slow due to the nature of the YM2151 itself, so saving a few raster lines is a good thing.

Anyway, anyone who has downloaded and used Zsound needs to update their copy and re-generate all ZSM files. This is likely to be a very rare occurrence, as the format itself seems sufficient for the future.

  • Like 1
Link to comment
Share on other sites

Okay - after some editing and proofreading, I've whittled down the ZSM format specification document to what I will consider the next version (1).

I'd like to invite any criticism, nitpicks, insights, etc from the community before I finalize this into the code base and tools.

Your feedback can be on anything - the document itself being readable/understandable, or comments about the format itself. In particular, if anyone's got any insight or would like to participate in working out the format of the event synchronization data channel, I'm open for suggestions.

https://github.com/ZeroByteOrg/zsound/blob/dc572353af55a48778a302db721e6f289eac7834/doc/ZSM Format Specification.md

Link to comment
Share on other sites

  • Super Administrators
On 5/16/2022 at 8:37 PM, Ed Minchau said:

Why little endian?  VERA and the 65c02 use big endian. Not saying it shouldn't be that way, just curious about the reasoning.

VERA and the 65x CPUs use Little Endian. Least Significant Byte is first. 

 

Link to comment
Share on other sites

On 5/17/2022 at 1:59 AM, Ed Minchau said:

Sonofagun, I've been using those terms backwards. Good thing it doesn't come up often. 

I use this trick to remember it: Little Endian - the "Little" byte comes before the "End" byte. For the Big Endian the "Big" byte comes before the "End" byte.

Of course that doesn't stop me from making mistakes. 🤐 

Link to comment
Share on other sites

On 5/17/2022 at 7:50 AM, Edmond D said:

I use this trick to remember it: Little Endian - the "Little" byte comes before the "End" byte. For the Big Endian the "Big" byte comes before the "End" byte.

Of course that doesn't stop me from making mistakes. 🤐 

Just makes me wanna slap the guy who deliberately came up with these terms. I mean, the obvious interpretation is that the big one would be at the end for big endian.

It's like the guy who decided "flammable" and "inflammable" should mean the same thing. English. What a language. 

  • Haha 1
Link to comment
Share on other sites

On 5/17/2022 at 10:52 AM, Ed Minchau said:

Just makes me wanna slap the guy who deliberately came up with these terms. I mean, the obvious interpretation is that the big one would be at the end for big endian.

It's like the guy who decided "flammable" and "inflammable" should mean the same thing. English. What a language. 

It's from Gulliver's Travels. The two Lilliputian factions at war were the big endians and the little endians. The dispute was over which end of a soft-boiled egg was the correct one to crack into.

Thus a little endian starts at the little end, and a big endian starts at the big end.

That's how the terminology works for byte order. You start eating at the big or little end of the value.

  • Like 2
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...

Important Information

Please review our Terms of Use