AYmake V0.0 by James McKay (14/07/1998) ======================================= Purpose ------- The purpose of AYmake is to allow the creation of new AY files. When I say "AY" files I mean the "ZXAMEMUL" format originally used by DeliAY (add-on part of the DeliTracker) on the Amiga. Here they were usually called "EMUL.*" but, for the sake of DOS 8.3 filenames, I decided to call them "*.AY". On the Amiga you had to use an assembler to make AY files, the result being big endian words and ghastly 16-bit relative pointers. Fine for space saving but annoying (and limiting) otherwise. AY files are specifically for the playing of "128K music" using the AY-3-8912 soundchip at I/O addresses 49149 and 65533. According to standards it *CANNOT* be used for playing 128K samples or any 48K (internal speaker) sound. This is to maintain compatibility with the original player on the Amiga, however AYmake will allow you to make songs which have these in them since AYplay V0.2 can handle them. In addition, AY files can also play Amstrad CPC tunes (using the CPC ports), but AYplay is not very good at playing these! AYmake resembles "make" in concept but it looks like an INI file (as I used the x128 INI file code to save time). Usage ----- C:\> AYMAKE GAME This will take GAME.INI and create GAME.AY providing that the INI file has no mistakes in it. About AY Files -------------- AY files basically contain a block of Z80 code, some register/memory information and some text for the player to display. The Memory Model That An AY File Lives In ----------------------------------------- AYplay emulates a Spectrum 128K (in 48K mode) with the following exceptions: * Only the soundchip ports are emulated (49149 and 65533). * Additionally, the Amstrad CPC ports are emulated. * There is no ROM software. * Interrupts do not work normally, they are completely controlled by AYplay. * All 64K memory is RAM. * No screen or contended memory. * Memory is preset with certain values (see below). * AYplay V0.2 also supports port 254 (internal speaker). Memory Model ------------ 0 : : call INIT : 205,0,0 3 : : ld hl,INTERRUPT : 33,0,0 6 : : ld a,h : 124 7 : : or l : 181 8 : : jr nz,OK : 32,8 10: : ld a,i : 237,87 12: : ld h,a : 103 13: : dec l : 45 14: : ld e,(hl) : 94 15: : inc hl : 35 16: : ld d,(hl) : 86 17: : ex de,hl : 235 18: OK : ld (HERE+1),hl : 34,23,0 21: LOOP : halt : 118 22: HERE : call ???? : 205,0,0 25: : jr LOOP : 24,250 27: : ret : 201 28- 55 : 255 56 : 201 57-16383 : 255 16384-65535 : 000 Exception : If a word read is done at 65535 then it will always "see" 243 at address 0 (65536), so that JR 65524 can be done (for "I=255" situations). Address 27 may be used as a dummy INIT routine. When you play a song the PC is set to 0 and this code is executed. So, INIT and INTERRUPT are supplied by you. If INIT is 0 then the start address of the code block is called instead. If INTERRUPT is 0 then the Z80 code works out where the interrupt should be, based on the value of the I register after the INIT routine has been called. The "halt" causes an immediate interrupt, due to it's different implementation. The end result is that a song is played! This is a very strange system. You don't need to understand any of this, which is just as well... What You Have To Do ------------------- Firstly you must have hacked out a song player into a raw file and know exactly where it should reside in memory, what the registers should be and the INIT and INTERRUPT addresses. You have to make an INI file, this is a (sort of) script file. Firstly the Main Header: Creator ="Song Author" ; The guy who wrote the tune(s). Misc ="Game Name (C) 1988" ; For example... StartSong =0 ; Number from 0-255 Fortunately you don't need StartSong since AYmake allows an automatic way of deciding this. To signify a new song do: NewSong=This And to show that this is the startsong have this on the next line: StartSong=This Much easier! Song Fields: SongName="Level 3" AReg=0 Stack=49151 JpAddr=49152 IntAddr=49155 CodeStartAddr=49152 CodeLength=AUTO CodeName=GAME.BIN "Areg" (8-bits) is the value of every high byte of every register, before the INIT routine is called. (eg. A, B, D, H, A', B', D', H', IXH, IYH). The low bytes are set to 0. "Stack" is the stack pointer. "JpAddr" is the INIT routine, if you set this to 0 then the "CodeStartAddress". is called instead. "IntAddr" is the INTERRUPT routine, if you set this to 0 then it is calculated from the value of the I register after the INIT routine. "CodeStartAddr" is the start address of the code... "CodeLength" is the length of the code block, can be a number, but is better to set to AUTO. "CodeName" is the filename of the raw data block. You can also do this: JpAddr=DUMMY IntAddr=DUMMY In order to prevent one of them from taking place (don't do both!). Timesavers: All of the Song Fields can have "All" preceeding them to make them effective starting from the next "NewSong=This" and all the songs after that. eg: AllSongName="I am too lazy to name the songs." AllCodeStartAddr=32768 AllCodeLength=AUTO AllCodeName=GAMECODE.BIN Note : When a "NewSong=This" is read, the parser latches all "All" values onto the new song, but you can still change individual fields by using the non "All" fields to change them. For example, if you had one block of code which played multiple songs: AllStack=32767 AllJpAddr=32768 AllIntAddr=32771 AllCodeStartAddr=32768 AllCodeLength=AUTO AllCodeName=MULTI.BIN NewSong=This SongName="Song 1." Areg=0 NewSong=This SongName="Song 2." Areg=1 You can also use hex numbers (for all numeric fields) in the following ways: JpAddr=#8000 AllStack=$C000 Areg=0x7F You can also add comments with ";" ; This script file written on 19/11/2090. The easiest course of action is to just modify the example INI file supplied. Hacking Tunes ------------- I can only say a little on this subject since I am not an expert on it. To try this you must understand the Z80, know the I/O ports involved and have a suitable setup for extracting the code into raw files, possibly an emulator with a built in disassembler and raw code saving options. In all cases you must be able to read the code of the routines that you find, in order to know how where and much audio data must also be taken. 48K Tunes (the kind that stop the on-screen action): ---------------------------------------------------- While the tune is playing enter your disassembler and go back along the stack pointer. You should find a single CALL which starts and plays the tune to completion. When making an AY file of it, set the "JpAddr" to the CALL value and set the "IntAddr" to DUMMY (27). Unfortunately, the tune may use the BEEPER routine from the Spectrum ROM, which isn't in AYplay, so you would have to include it yourself (more work) by adding a bit to the INIT routine. 128K Tunes: ----------- This is more difficult, you are looking for two CALLs - the INIT and the INTERRUPT. The INTERRUPT routine will probably live in the IM2 handler. (First byte of IM2 is stored at wordpeek(wordpeek((Ireg<<8)|255)) ). While in IM2 look for a routine which OUTs to 49149 and 65533. Trace back (without leaving IM2) and try to find one single CALL - this will be your INTERRUPT value. To test it, you could try CALLing it in a loop to see if it makes a wibbly noise (because it's being CALLed too frequently, but it is advancing the tune to the next note). The INIT will probably access common memory locations with the INTERRUPT and may be close to the INTERRUPT routine, but it's a bit of a hit and miss affair. The INIT routine may also be called with the A reg (or other registers) set to certain values, possibly indicating multiple tunes played from the one routine... Remember that there is only a flat 64K in AYplay, no page flipping can occur. Amstrad CPC Tunes: ------------------ Similar to 128K tunes, but you may have to do substantial modifications to the song player. For example, it may use IM1 (CALL 56) to play the songs, it may also use some I/O ports that aren't emulated by AYplay. In general, a lot of work will be required. Other Tunes: ------------ The original DeliAY allowed you to write custom playing routines in 68000 code, nothing like that is available here. Maintaining Compatibility With The Amiga DeliAY ----------------------------------------------- * Only make tunes that use the AY-3-8912 and only simple tunes, not samples. * Do not make 48K tunes. * Avoid using address 56 for dummy routines, use "DUMMY" (27) instead. Limitations ----------- * Due to the 16-bit relative pointers used in the file format, you cannot make an AY file which is very big (the biggest jump you can have is 32767). * Only 256 tunes per file (maximum), which is unlikely to be a problem considering the other limitations mentioned. * AYmake may display a "Very Serious Error" if you try to put too much in one file, experimentation is recommended. If you were making the AY file manually then you could probably squeeze more in, but this is tricky! There are other fields in the AY file format, but I've chosen not to include them in AYmake, as they are non-essential. The generated AY file is of version 0 and req_version 0. Bye!