AY

Common notes

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.

File format

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.

Header structure

OffsetLength in bytesNameDescription
+04FileIDFile identifier 'ZXAY'
+44TypeIDData type in container:
'EMUL' – Z80 CPU emulation;
'AMAD' – analog of FXM;
'ST11' – ST1 container.
+81FileVersionFile 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).
+91PlayerVersionRequired 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.
+102PSpecialPlayerThis 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.
+122PAuthorPointer to null-terminated string with author name, only one for whole AY-file.
+142PMiscSame, but to string with miscellaneous information.
+161NumOfSongsNumber of tunes in file (decreased by 1).
+171FirstSongTune number, which must be played first (decreased by 1 too).
+182PSongsStructureRelative 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.

Song structure

OffsetLength in bytesNameDescription
+02PSongNameRelative offset to null-terminated string with name of corresponding tune.
+22PSongDataOffset to record "Song data".

All above is rightly for any AY-files. Further each data type is described.

'EMUL' type

Record "Song data" for 'EMUL' type has next structure:

OffsetLength in bytesNameDescription
+01AChanAmiga’s channel number for emulating AY channel A.
+11BChanAmiga’s channel number for emulating AY channel B.
+21CChanAmiga’s channel number for emulating AY channel C.
+31NoiseAmiga’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.
+42SongLengthSong duration in 1/50 of second. If zero, then length is unknown (infinitely).
+62FadeLengthDuration of fade after end of song in 1/50 of second.
+81HiRegValues of high halves of all Z80 common registers (AF, AF’, HL, HL', DE, DE', BC, BC', IX and IY).
+91LoRegValues of low halves of all Z80 common registers including flag registers.
+102PPointsPointer to "Pointers" record.
+122PAddressesPointer to "Data blocks" record.

"Pointers" record consists of stack, initialization and playing routines addresses for the tune:

OffsetLength in bytesNameDescription
+02StackValue of SP register before starting emulation.
+22INITInitialization subprogram address in Z80 memory. Can be zero (see further).
+42INTERRUPTPlaying subprogram address (calls 50 times per second). Can be zero (see further).

"Data blocks" record contain sequences of three-word values groups:

OffsetLength in bytesNameDescription
+02Address1First block address in Z80 memory.
+22Length1Length of first block in bytes.
+62Offset1Relative offset to begin of 1st block in the AY-file.
+82Address2Second block address in Z80 memory.
+102Length2Length of 2nd block in bytes.
+122Offset2Relative offset to begin of 2nd block in the AY-file.
and so on until Address = 0 will met.

Minimum required integrity control

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):

  1. If Address + Length > 65536 then decrease Length to make it 65536.
  2. If CurrentPositionInFile + Offset + Length > FileSize then decrease Length too.

Player requirements

And now about that how player of version 3 must to play AY-files of 'EMUL' type.

  1. Fill range #0000-#00FF with #C9 byte value.
  2. Fill range #0100-#3FFF with #FF byte value.
  3. Fill range #4000-#FFFF with #00 byte value.
  4. Place at #0038 address #FB byte.
  5. If INIT for this song is equal to zero, then place first block loading address at first CALL instruction (see next 6 and 7 steps).
  6. If INTERRUPT for the song is equal to zero, then place at zero address next player:
            DI
            CALL INIT
    LOOP:   IM 2
            EI
            HALT
            JR LOOP
    
  7. If INTERRUPT is not equal to zero, then use another player:
            DI
            CALL INIT
    LOOP:   IM 1
            EI
            HALT
            CALL INTERRUPT
            JR LOOP
    
  8. Load all blocks for the song.
  9. Load to low halves of Z80 common registers (including flag registers) LoReg value.
  10. Load to high halves of Z80 common registers HiReg value.
  11. Load into I register value 3 (this player version).
  12. Load to SP register Stack value.
  13. Load to PC zero value.
  14. Disable interrupts and set IM0 mode.
  15. Reset AY sound chip.
  16. Run Z80 emulation.

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.

'AMAD' type

Record "Song data" for 'AMAD' type has next structure:

OffsetLength in bytesNameDescription
+02AllocAddressAllocation address of data block in Spectrum memory.
+21AndsixThe 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.
+31LoopsNumber of loops.
+42LoopLenLength of one loop in interrupts (VBI).
+62FadeOffsetPrecise fade specification (unused in Ay_Emul).
+82FadeLenHow long to fade (unused in Ay_Emul).
+101AChanSame as for 'EMUL' type.
+111BChanSame as for 'EMUL' type.
+121CChanSame as for 'EMUL' type.
+131NoiseSame as for 'EMUL' type.
+14???ZXDataOriginal data block from ZX Spectrum program. See in FXM-format description.

'ST11' type

Record "Song data" for 'ST11' type has next structure:

OffsetLength in bytesNameDescription
+01AChanSame as for 'EMUL' type.
+11BChanSame as for 'EMUL' type.
+21CChanSame as for 'EMUL' type.
+31NoiseSame as for 'EMUL' type.
+44UnknownHere (0,0,0,50) sequence usually, but (0,0,0,255) in one file.
+8???ZXDataOriginal data block from ZX Spectrum program. See in ST1-format description.

Further evolution: how you can help?

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.