The format is native to ZX Spectrum and Amstrad CPC, so it can be played both in emulators and on real ZX Spectrum and Amstrad CPC computers without using special players.
AY-file format is designed by Patrik Rak for his DeliAY plug-in to DeliTracker (Amiga computer). James McKay gave second life to this format (AYPlay and AYMake utilities) on PC. But AYMake used a limited number of AY-format features.
Next description I wrote after year of working with AY-files, but I could understand all only after discussion with Patrik Rak.
So, format is designed on Amiga with MC68000 processor. Due to this fact, all word values are in Motorola order (first byte is high) and must be aligned to two-byte offsets from AY-file beginning.
Second important thing is all pointers in the format are signed and relative (i.e. are offsets from current position in file to pointed data). Range of the offsets is from -32768 to +32767. Therefore can be situations of AY-file creation impossibility for some specific cases.
AY-file is sequence of records. They order is not fixed, but first record must be header.
Offset | Length in bytes | Name | Description |
---|---|---|---|
+0 | 4 | FileID | File identifier 'ZXAY' |
+4 | 4 | TypeID | Data type in container: 'EMUL' – Z80 CPU emulation; 'AMAD' – analog of FXM; 'ST11' – ST1 container. |
+8 | 1 | FileVersion | File version, you can use this field as free as you want, however, format author recommends to numerate versions in turn (first version (release) is 0, second is 1 and so on). |
+9 | 1 | PlayerVersion | Required player version for playing. Only three versions are exists now: 0 – use if you do not know what player version you need; 1 – initial player version; 2 – first 256 byte of Z80 memory is filled with 0xC9 value (RET instruction); 3 – last version for now; full Z80 emulation plus beeper port emulation; see its full description further. |
+10 | 2 | PSpecialPlayer | This is for AY-files which contain player in MC68000 machine codes. As Patrik Rak is saying, only one file of this kind is existing, so, can be simply ignored. |
+12 | 2 | PAuthor | Pointer to null-terminated string with author name, only one for whole AY-file. |
+14 | 2 | PMisc | Same, but to string with miscellaneous information. |
+16 | 1 | NumOfSongs | Number of tunes in file (decreased by 1). |
+17 | 1 | FirstSong | Tune number, which must be played first (decreased by 1 too). |
+18 | 2 | PSongsStructure | Relative pointer to "Song structure" record. |
Size of header is 20 bytes.
Last offset of header points to records "Song structure" (one record per tune, the records follows one after another). So, all EMUL, AMAD, ST11 and any other type can contain more than one tune in AY-file.
Offset | Length in bytes | Name | Description |
---|---|---|---|
+0 | 2 | PSongName | Relative offset to null-terminated string with name of corresponding tune. |
+2 | 2 | PSongData | Offset to record "Song data". |
All above is rightly for any AY-files. Further each data type is described.
Offset | Length in bytes | Name | Description |
---|---|---|---|
+0 | 1 | AChan | Amiga’s channel number for emulating AY channel A. |
+1 | 1 | BChan | Amiga’s channel number for emulating AY channel B. |
+2 | 1 | CChan | Amiga’s channel number for emulating AY channel C. |
+3 | 1 | Noise | Amiga’s channel number for emulating AY noise. Typically these four bytes are 0, 1, 2, 3. You also can use these four numbers in any order. |
+4 | 2 | SongLength | Song duration in 1/50 of second. If zero, then length is unknown (infinitely). |
+6 | 2 | FadeLength | Duration of fade after end of song in 1/50 of second. |
+8 | 1 | HiReg | Values of high halves of all Z80 common registers (AF, AF’, HL, HL', DE, DE', BC, BC', IX and IY). |
+9 | 1 | LoReg | Values of low halves of all Z80 common registers including flag registers. |
+10 | 2 | PPoints | Pointer to "Pointers" record. |
+12 | 2 | PAddresses | Pointer to "Data blocks" record. |
Offset | Length in bytes | Name | Description |
---|---|---|---|
+0 | 2 | Stack | Value of SP register before starting emulation. |
+2 | 2 | INIT | Initialization subprogram address in Z80 memory. Can be zero (see further). |
+4 | 2 | INTERRUPT | Playing subprogram address (calls 50 times per second). Can be zero (see further). |
Offset | Length in bytes | Name | Description |
---|---|---|---|
+0 | 2 | Address1 | First block address in Z80 memory. |
+2 | 2 | Length1 | Length of first block in bytes. |
+6 | 2 | Offset1 | Relative offset to begin of 1st block in the AY-file. |
+8 | 2 | Address2 | Second block address in Z80 memory. |
+10 | 2 | Length2 | Length of 2nd block in bytes. |
+12 | 2 | Offset2 | Relative offset to begin of 2nd block in the AY-file. |
and so on until Address = 0 will met. |
Follow next rules during loading blocks, if you want that your own AY-files player will be playing most now existing AY-files (these rules enable to play corrupted AY-files even):
And now about that how player of version 3 must to play AY-files of 'EMUL' type.
DI CALL INIT LOOP: IM 2 EI HALT JR LOOP
DI CALL INIT LOOP: IM 1 EI HALT CALL INTERRUPT JR LOOP
You can see that blocks can overwrite standard player even. You can use this possibility if you need to run non-standard player routine. In this case you can place it at INIT address or at #0001 address even. Data blocks can be loaded at any Z80 memory address except 0.
Ay_Emul works as player of version 3.
Offset | Length in bytes | Name | Description |
---|---|---|---|
+0 | 2 | AllocAddress | Allocation address of data block in Spectrum memory. |
+2 | 1 | Andsix | The parameter must either 31 or 15. It is used for correction result of addition current noise value and parameter of 8D command (see FXM description). I.e. some players use 5 bits of noise register, and some only 4 ones. |
+3 | 1 | Loops | Number of loops. |
+4 | 2 | LoopLen | Length of one loop in interrupts (VBI). |
+6 | 2 | FadeOffset | Precise fade specification (unused in Ay_Emul). |
+8 | 2 | FadeLen | How long to fade (unused in Ay_Emul). |
+10 | 1 | AChan | Same as for 'EMUL' type. |
+11 | 1 | BChan | Same as for 'EMUL' type. |
+12 | 1 | CChan | Same as for 'EMUL' type. |
+13 | 1 | Noise | Same as for 'EMUL' type. |
+14 | ??? | ZXData | Original data block from ZX Spectrum program. See in FXM-format description. |
Offset | Length in bytes | Name | Description |
---|---|---|---|
+0 | 1 | AChan | Same as for 'EMUL' type. |
+1 | 1 | BChan | Same as for 'EMUL' type. |
+2 | 1 | CChan | Same as for 'EMUL' type. |
+3 | 1 | Noise | Same as for 'EMUL' type. |
+4 | 4 | Unknown | Here (0,0,0,50) sequence usually, but (0,0,0,255) in one file. |
+8 | ??? | ZXData | Original data block from ZX Spectrum program. See in ST1-format description. |
And now about flaws of AY-format and about AY-files support realization in Ay_Emul.
First flaw is specification allows to set SongLength field to “infinite playing” value (zero). The AY emulator has no this term, so files with zero SongLength would be played 15000 interrupts instead of infinite.
Second flaw is vagueness with term "1/50 of second".
If it is interrupt period, then it must to depend of two values: Z80 frequency and number of TStates between interrupts (maybe only for Russian ZX Spectrum clones). Anyway, interrupt period is different in different Spectrum models. So, specification standardizes no one of described parameters. Ay_Emul uses 3494400 Hz for Z80 frequency and 69888 for TStates between interrupts (i.e. 69888/3494400 = 1/50 of second).
But if the music is played without synchronization with interruptions, then the lack of data on the processor frequency is already critical: if the person creating the AY-file measures the duration and translates it into the specified units for one CPU frequency, and the end user's player plays on another, then the file either will not be played to the right moment, or will be played further more than necessary.
Third flaw is in that no able to load blocks at #0000 address. This problem can be decided if first block (and only first block) can to have zero address. But this decision is not compatible with other players and therefore not supported in Ay_Emul too.
I can to add that AY-format implies only 48K-memory model. But the most part of Spectrum music can be adapted to 48K limitations.
AY-format developing on PC was occurred through efforts of James McKay. He had added beeper supporting. But his AYMake does not support information about time length, so, big number of AY-files were appeared in Internet without duration set.
Project AY team (format enthusiasts with led by bcass) rebuilt many existing AY-files and added time information in many of them, and also built many new AY-files.
Patrik Rak made PC DeliPlayer plug-in for AY-files, and we can listen to PC realization of AY-player from AY-format author now.
Tim Farrell made a SpecAY player for the real ZX Spectrum, as a result, it is possible to listen to most of the collection on native hardware.
Well, and with other things in AY-format you can to meet by yourself, if you'll want to rip AY-music from Speccy programs. You can find my utilities with source codes for splitting and creating new AY-files (AYMakeR, aysplitr, ay2sna) on official site of Ay_Emul.
The duration of the sound will help determine the utility YMTOOL.EXE from Arnaud Carre: it is enough to convert an AY-file to YM6, setting the duration to 10–30 minutes, and the utility will help you find the body of the cycle.