home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 614a.lha / MultiPlayer_v1.17 / Programmers / Formats.doc < prev    next >
Text File  |  1992-02-14  |  28KB  |  547 lines

  1.                  General Module Format (GMOD) Description
  2.                        Copyright (C) 1991 Bryan Ford
  3.                             All Rights Reserved
  4.                           Revision date 27-Jul-91
  5.  
  6.  
  7.  
  8.     The GMOD format is a format which can interface any music module with
  9. embedded player code with any program that wants to play the music.  It has
  10. many powerful features, but is designed to be as simple as you want it to
  11. be.  The 'least common denominator' support is extremely small, so the
  12. module can be small and quick.  In many cases, all that is needed to write
  13. a GMOD module is to tack on a short constant header before a normal module.
  14.  
  15.     If you still have doubts, I'll back my words with an offer:  If you are
  16. the author of a music-maker program (or any program that creates music
  17. modules), I will personally write whatever code is necessary to extend your
  18. program to load and save GMOD modules as well as your own format.  Just
  19. contact me on the network, telephone, or by mail.  (My addresses are at the
  20. end of this document.) I'm tired of zillions of incompatible module formats
  21. around:  the Soundtracker format is not powerful enough as a standard, but
  22. every alternate module format requires its own nonstandard player.  The
  23. GMOD format, then, is my proposed solution to this problem, and I'm willing
  24. to back it up.
  25.  
  26.     If GMOD catches on, we may start to see music maker programs that write
  27. full-featured GMOD modules that are not only playable with most any simple
  28. player program, but also support many specialized features such as external
  29. control (volume, speed, fast-forward, rewind, etc.), real-time sound
  30. effects added to the music (for games or other sound effects), and whatever
  31. else you can imagine.
  32.  
  33.     This document is intended especially for distribution with my music
  34. player MultiPlayer (which of course supports this format).  Wherever
  35. MultiPlayer goes, this document goes.  However, since the GMOD format isn't
  36. really tied to MultiPlayer, MultiPlayer doesn't have to go wherever this
  37. document goes.  You may also distribute this document apart from
  38. MultiPlayer, put it on public bulletin board systems, or even include it
  39. along with commercial programs if you are so inclined, as long as it
  40. remains unchanged.  (If you have suggestions or additions to this format,
  41. contact me and I'll make every effort to accomodate your needs as quickly
  42. as possible.)
  43.  
  44.     At the end of this document are described two other module formats,
  45. XMOD and AMOD, which are very simple module formats that I used before the
  46. GMOD format was developed.  GMOD replaces both previous formats, so please
  47. don't write programs that create XMOD or AMOD modules.  Although
  48. MultiPlayer can still play them, and you may still create them if you want
  49. something extremely simple and quick, for general purpose use you should
  50. always use GMODs.
  51.  
  52.     In this document, when I refer to the 'player', I am referring to the
  53. program that uses the module (the caller), such as MultiPlayer, not the
  54. actual player code in the module itself.  I refer to the module, both
  55. specific music-playing code and song data, as just the 'module'.
  56.  
  57.  
  58.  
  59.                            Format Specification
  60.                            ~~~~~~~~~~~~~~~~~~~~
  61.  
  62.     Now for the actual definition.  A GMOD file always begins with four
  63. specific longwords, then a jump table with an arbitrary length, then the
  64. rest of the file can be anything you want.  The format follows:
  65.  
  66. module+$00      'GMOD' ($474D4F44)
  67. module+$04      4-byte ID of program that created this module
  68. module+$08      Memory address where this module MUST be loaded, 0 if relocatable
  69. module+$0c      Offset of end of jump table (=numentries*4+$10)
  70. module+$10      Start of jump table (arbitrary length)
  71.  
  72.     The first longword in the file is simply used to identify GMOD modules.
  73.  
  74.     The ID of the creator (module+$04) is used to identify the program that
  75. created this module.  It can be ignored by any program that simply wants to
  76. play the module.  A music composer program might check this longword when
  77. trying to re-load a module to make sure the module is the correct type.
  78. When selecting an ID for your program to use, think of it as an IFF chunk
  79. ID:  Try to select something that is somewhat readable ASCII, but is
  80. unlikely to collide with some other program.  (For example, don't use
  81. 'SONG' or 'TRAK' or something simple like that.)
  82.  
  83.     The load address is the absolute location in memory where the module
  84. must be loaded.  Although hopefully programmers will have enough sense not
  85. to create non-relocatable modules in the future, the feature is there for
  86. current module types that use absolute addresses in the code and/or data.
  87. In the MultiPlayer implementation using this feature is not dangerous to
  88. the system and will never cause innocent memory to be stomped on, but it
  89. may cause the module to be unable to load even when there's plenty of
  90. memory.  If the required memory area is already occupied (even if it's just
  91. a few bytes somewhere in the required range), MultiPlayer will refuse to
  92. load the module.  Therefore, this feature should never be used except when
  93. absolutely necessary, since these modules may not be able to load even when
  94. there is plenty memory available.  MultiPlayer properly allocates the
  95. needed memory with AllocAbs, rather than just overwriting memory as many
  96. European demos do, so multitasking will never cause the module to get
  97. overwritten.  I wish this feature wasn't necessary, but sometimes we just
  98. have to clean up other people's messes, don't we?
  99.  
  100.     The offset of the end of the jump table is the offset (from the start
  101. of the module) pointing to just past the last valid jump table entry.  It
  102. can be calculated using the equation (numvecs*4)+16, where numvecs is the
  103. number of vectors in the jump table.  Any vectors beyond this offset are
  104. assumed by the player (caller) to be 'do-nothing' routines - i.e.  just an
  105. RTS.
  106.  
  107.     The actual jump table entries are usually branches or PC-relative jump
  108. instructions, but can be any code that fits in four bytes.  In particular,
  109. if a function simply wants to return a constant value (such as
  110. GetNumSongs), a MOVEQ and an RTS could fit right into the jump table.  The
  111. player must not try to interpret the jump table entries (and by no means
  112. modify it!).  The one exception to this rule is that the player may look
  113. into the jump table to see if the first word of the entrypoint is $4E75
  114. (RTS), in which case the player knows that the entry is not used.  This
  115. way, the player can avoid bogging down the system with unused interrupts
  116. and such.
  117.  
  118.     The calling conventions are always standard Amiga conventions:  The
  119. routines must save registers D2-D7 and A2-A6, and must return with an RTS.
  120. Parameters are passed in various registers and returned in D0, the same way
  121. as standard Amiga libraries.
  122.  
  123.     Note that, since any of the entries may be omitted, special care must
  124. be taken by the caller when calling the module.  First, before calling a
  125. particular entrypoint, the offset must be tested against the MaxVecOfs in
  126. the module.  If the offset is greater than OR EQUAL to MaxVecOfs, the
  127. routine does not exist, and behaves the same way as if the entrypoint was a
  128. do-nothing entrypoint (first word is RTS).  Second, when the caller is
  129. expecting a return code of some kind in D0, before calling the module it
  130. must initialize D0 to some default value, or an invalid value it can
  131. recognize, in case the routine is a do-nothing or nonexistent entrypoint.
  132. For example, before calling the GetNumSongs function, D0 should be
  133. initialized to 1.  If GetNumSongs doesn't exist or does nothing, the
  134. default value of one song will be assumed.
  135.  
  136.     I will not specify whether or not operating system routines (such as
  137. Exec's memory allocation routines) may be called from within the module.
  138. For a general-purpose player program like MultiPlayer, this should be
  139. allowed.  However, I expect that the majority of music modules for a long
  140. time to come will stay away from the operating system in order to support
  141. games and demos and other programs that take over the machine.  If the
  142. operating system is to be used normally, it is always the caller's
  143. (player's) responsibility to allocate the audio.device's channels and make
  144. sure the module fits into the multitasking system.  All the module needs to
  145. do is stick to the standard audio hardware registers (and audio DMA
  146. control) and keep its nose out of forbidden areas.  Modules should generally
  147. not need to muck around in interrupts, since the format defines some very
  148. general-purpose interrupt routines, but I won't forbid it either.  If a module
  149. wants to do its own interrupt processing, then all the module's interrupt
  150. entrypoints (VBlank50, VBlank60, and TimerTick) should be do-nothing, and
  151. the module should properly allocate the interrupts using system calls.
  152.  
  153.     The currently defined jump table entries are described below:
  154.  
  155.  
  156. module+$10      InitMusic
  157.  
  158.     Called to initialize the module.  This is generally the first routine
  159. called by a player program after loading the module.  It should be called
  160. only once, and the module may be started and stopped as many times as
  161. needed after a single call to InitMusic.  D0 must be set to NULL before
  162. calling the routine, and the initialization routine should return NULL in
  163. D0 if the initialization succeeded, or a pointer to a string (in D0)
  164. describing the error if the initialization failed.  No other routines in
  165. the module may be called before initialization.  InitMusic should not mess
  166. with the audio hardware or start playing music, as the audio channels may
  167. not be allocated by this time.  This entrypoint would generally be used to
  168. perform any relocation or other one-time initialization of the module, or
  169. for allocating memory or other resources through the operating system if
  170. necessary.
  171.  
  172.  
  173. module+$14      StartMusic
  174.  
  175.     Called to start playing the music.  If the module contains several
  176. songs, then D0 will hold the song number (0..n-1) to start playing,
  177. otherwise D0 will be 0.  StartMusic always starts a given song at the
  178. beginning.  To pause and restart in the middle of a song, use PauseMusic
  179. and RestartMusic.
  180.  
  181.     Any given module is always guaranteed to have a song number 0.  Before
  182. calling StartMusic with a song number greater than zero, it must call
  183. GetNumSongs to make sure that song actually exists.  There is not
  184. necessarily any protection in the module against playing songs that do not
  185. exist.
  186.  
  187.     The StartMusic entrypoint may be called again after StopMusic to
  188. restart the music after it was stopped.  This allows the module to be
  189. 're-used' without reloading.  Also, it allows switching songs in the module
  190. if the module contains more than one song.  However, StartMusic should
  191. never be called after another StartMusic, without a StopMusic in between.
  192.  
  193.  
  194. module+$18      StopMusic
  195.  
  196.     Called to stop playing the music.  Often all this does is turn off
  197. audio DMA and return.  StopMusic may be called any number of times, with or
  198. without a corresponding StartMusic call.
  199.  
  200.     Note that no interrupt entrypoints in the module are called by the
  201. player anytime except when the music is actually playing, i.e.  after
  202. StartMusic and before StopMusic.  Therefore, the player program should
  203. enable audio interrupts AFTER StartMusic, and disable interrupts BEFORE
  204. StopMusic.
  205.  
  206.  
  207. module+$1c      EndMusic
  208.  
  209.     Called to shut down the module and prepare to be unloaded.  The caller
  210. MUST call this entry before unloading the module, even if the call to
  211. InitMusic failed.  If the music was playing, StopMusic must be called
  212. before calling EndMusic.  This may be called more than once successively,
  213. but no other routines in the module may be called after EndMusic has been
  214. called.  A module may not be re-used after EndMusic.  EndMusic should not
  215. mess with audio hardware or DMA registers - that is done in StopMusic.
  216. This entrypoint is provided mainly to free any allocated memory or do other
  217. operating-system related cleanup.  As such, for most current modules this
  218. entrypoint would do nothing.
  219.  
  220.  
  221. module+$20      Reserved (was PauseMusic, now obsolete)
  222.  
  223.  
  224. module+$24      ContinueMusic
  225.  
  226.     After the player has called StopMusic (but before it calls EndMusic),
  227. it can call ContinueMusic to try and continue the music at the position
  228. where it was stopped.  The player calls this function with D0=0, and if
  229. the module is able to restart the music, it should return D0=1; otherwise
  230. it should leave D0 at 0.  The player must ONLY start sending interrupts
  231. again if the module returned nonzero in D0 - otherwise, the module
  232. doesn't support this feature and turning interrupts on at this point
  233. might cause trouble.  The player can always call StartMusic again to
  234. restart the module at the beginning.
  235.  
  236.  
  237. module+$28      VBlank50
  238. module+$2c      VBlank60
  239.  
  240.     The player program must call whichever of these vectors are supplied,
  241. at the appropriate frequency.  The VBlank50 routine will be called 50 times
  242. per second, the VBlank60 routine will be called 60 times per second.  The
  243. module may use either one of these routines (not both).  If the player
  244. calls these routines from a vertical blank interrupt (as is usually the
  245. case, but not necessarily), it is its responsibility to adjust the
  246. frequency appropriately.  Note that a more general timing system is also
  247. available, described below.
  248.  
  249.  
  250. module+$30      Channel0
  251. module+$34      Channel1
  252. module+$38      Channel2
  253. module+$3c      Channel3
  254.  
  255.     These four entrypoints, if supplied, are called by the player whenever
  256. the corresponding audio channels are reloaded.  They correspond to Exec's
  257. AUD0-AUD3 interrupts (levels 7-10), CPU level 4.
  258.  
  259.  
  260. module+$40      GetNumSongs
  261.  
  262.     Some music modules have more than one song in the same module.  This is
  263. useful when you want several separate musical scores that all use the same
  264. set of instruments, for memory efficiency.  If this entrypoint is supplied,
  265. the player program may call this routine to find out how many different
  266. songs are contained in this module.  The routine must be called with D0 = 1
  267. (the default number of songs), and the module should return the actual
  268. number of songs in D0.  Once the player knows how many songs are in the
  269. module, it may select the song to play in the call to StartMusic.
  270.  
  271.  
  272. module+$44      GetSongName
  273.  
  274.     If a module contains several songs, GetSongName provides a method for
  275. the player to find the name of each song.  This may be simply displayed to
  276. the user while the song is playing, or a list of songs may be built for the
  277. user to select from by name.  This routine is called with D0 holding NULL
  278. and D1 holding the song number (which must be in the proper range).  If the
  279. module supplies this routine, it must return a pointer to the song name in
  280. D0.  Otherwise, D0 will remain NULL and the song will remain nameless.
  281.  
  282.  
  283. module+$48      GetSongAuthor
  284.  
  285.     This routine works the same way as GetSongName, except the name of the
  286. song's author is returned.  If all the songs in the module are by the same
  287. person (or if the module contains only one song), D1 can be ignored.
  288.  
  289.  
  290. module+$4c      GetFrequency
  291. module+$50      TimerTick
  292.  
  293.     These routines can be used by modules for more general-purpose timing,
  294. or for odd timing values not based on the vertical blank interrupt.  If the
  295. module uses this method of timing, both of these functions must be
  296. implemented, and neither VBlank50 or VBlank60 may be.  The GetFrequency
  297. function must simply return the number of ticks per second the module
  298. requires in d0.  The TimerTick function will then be called (probably from
  299. an interrupt) at the specified frequency while the module is playing, just
  300. like the VBlank functions.
  301.  
  302.  
  303. module+$54      GetMakerName
  304.  
  305.     This entrypoint must be called with D0=0, and if it is implemented,
  306. must return the name of the program that was used to create this module.
  307. Although music maker programs should use the ID field at offset 4 to
  308. recognize specific module types, this string can be used by player programs
  309. and such to identify the module's origin for the user.
  310.  
  311.  
  312. module+$58      Hook
  313.  
  314.     This function is called by the player with D0=0, D1=hook flags, and A0
  315. pointing to a standard Hook structure as defined in the 2.0 header files.
  316. (This does not mean that modules or players that use this feature must run
  317. on 2.0 - this structure is just used for convenience.) The hook flags work
  318. very much like Intuition's IDCMP flags and tell the module what kinds of
  319. events the player wants to hear about.  If the module supports this call,
  320. it must save the pointer to the Hook (it will remain valid until after
  321. EndMusic is called) and return in D0 the hook flags indicate what hooks the
  322. module supports.
  323.  
  324.     During playing, the module will call the player's Hook for the events
  325. that the player specified (in D1 in the Hook call) AND the module supports
  326. (returned in D0 from the Hook call).  Standard Hook calling conventions
  327. must be used:  A0 points to the Hook structure that the player originally
  328. passed to the module, and A1 points to an appropriate 'message' or parameter
  329. packet (see below).  The 'object' passed in A2 is currently unused, but may
  330. be used in future message types.
  331.  
  332.     The message that the module passes to the player's Hook function depends
  333. on the type of event.  The first longword of the message always contains
  334. a single Hook Flags bit set, indicating the type of event (just like IDCMP
  335. messages).  Note that the message is NOT a standard Exec message.  After
  336. the first longword, more data can be passed depending on the event.
  337.  
  338.     The player may call the module's Hook entrypoint more than once.
  339. However, the Hook pointer passed to the module must ALWAYS be the same!
  340. Therefore, re-calling this entrypoint can only be used to change the
  341. 'active' event bits (ala ModifyIDCMP()), not to change the Hook pointer
  342. itself.
  343.  
  344.     Note that the module's entrypoints are not necessarily reentrant, so
  345. the player's Hook function must NEVER call any of the module's entrypoints,
  346. or make any calls that might indirectly cause one of the entrypoints to be
  347. called.
  348.  
  349.     Currently the following event types are defined:
  350.  
  351.  
  352. BITDEF  GMODH,REPEAT,0
  353.  
  354.     If supported and requested, the module calls the Hook function as
  355. soon as the music is about to wrap around and repeat itself.  If the
  356. player's intent is to stop the module when it repeats, it must set a flag
  357. or signal a parent Task - it must not directly call the module's StopMusic
  358. entrypoints (see above).  The message passed to the player's Hook must, of
  359. course, have the first longword set to GMODHF_REPEAT (1<<0), but other than
  360. that the message is unused.
  361.  
  362.  
  363. BITDEF  GMODH,SEQUENCE,1
  364.  
  365.     If supported and requested, the module calls the player's Hook whenever
  366. the music's sequence number changes.  This event type is specifically
  367. designed for music formats with Soundtracker-like sequences:  if the music
  368. format doesn't use such sequences, then this event type should be left
  369. unimplemented by the module.  The sequence number starts at 0 and goes to a
  370. maximum of length-1 (length is the number of sequences in the module).  The
  371. message passed to the player's Hook must have GMODHF_SEQUENCE (1<<1) in the
  372. first longword, the new sequence number in the second longword (offset 4),
  373. and the total number of sequences in the third longword (offset 8).  The
  374. module should also call this Hook once during the PlayMusic call, or very
  375. soon afterwards, so the player knows immediately how long the module is.
  376.  
  377.  
  378. module+$5c      Jump
  379.  
  380.     Instructs the module to jump to a particular position within the
  381. current song.  This is extremely module-dependent.  The player passes D0=0
  382. and the position to jump to (a longword) in D1.  For Soundtracker-like
  383. modules, this position is probably the sequence number.  For other modules,
  384. it may be a time value or whatever.  If the module successfully jumps to
  385. the requested position (it should do range checking and such first), it
  386. returns D0=1, otherwise it leaves or sets D0=0.  This entrypoint exists to
  387. allow users and programmers to do nifty tricks with specific modules - the
  388. player can't and shouldn't interpret or use this entrypoint by itself.
  389.  
  390.  
  391. module+$60      SetVolume
  392.  
  393.     Gives the module a new master volume.  If the module supports this
  394. feature, the master volume set by this entrypoint must be independent of
  395. all internal volume effects and such.  In other words, all internal volume
  396. controls must be relative to this volume.  The new master volume should be
  397. made effective immediately - not on the next note or internal volume change.
  398.  
  399.     This entrypoint is passed two volume parameters: the left-side volume
  400. in D0, and the right-side volume in D1.  Each parameter ranges from $0
  401. (minimum) to $100 (maximum).  To get a custom-chip-style volume (0-64),
  402. just shift this value right two bits.  To scale an internal volume, just
  403. multiply the two volumes and shift right 8 bits.
  404.  
  405.  
  406. module+$64      GetScroll
  407.  
  408.     This entrypoint exists for the single purpose of allowing music
  409. composers to include all their useless ramblings in their modules for
  410. everyone else to wade through.  It must be called with D0=0, and if it
  411. exists, it will return in D0 a pointer to one (probably LONG)
  412. null-terminated string, which is the entire scroll text.  The only control
  413. codes it may contain are newlines (ASCII 10) and tabs (ASCII 9).  This
  414. makes it easy to create a scroll text simply from a text file.  The player
  415. displaying the scroll text will probably just ignore these control codes.
  416. (Of course, the player wouldn't have to implement a "traditional" scroller
  417. - it can display the text however it wants.)
  418.  
  419.  
  420.     I have many other possible entrypoints and hooks in mind, but haven't
  421. gotten them standardized yet (much less implemented in MultiPlayer), so
  422. I'll leave them out for now.  If there's something you want, drop me a line
  423. and I'll go ahead and add it to the specification, whether or not
  424. MultiPlayer can support it.  (It probably can.)  The important thing is to
  425. keep the interface powerful, easy to use, and standardized.
  426.  
  427.     To use GMOD modules in your own programs, such as games or demos, all
  428. you have to do is call the appropriate entrypoints at the appropriate
  429. times.  If your program will only be using one module, or a limited number
  430. of modules all made with the same program, you can make assumptions about
  431. the module, i.e., which interrupt timing routine to call, etc.  A minimum
  432. implementation might be as simple as two calls:  one to the initialization
  433. code, and one during each VBlank interrupt.  (Many modules do nothing more
  434. than turn audio DMA off in their exit code.) Of course, if you want to be
  435. compatible with more modules, you'll have to put in a little more effort.
  436.  
  437.     If you are the author of a music-composer program, I hope you will
  438. seriously consider supporting GMOD modules.  As I said at the top of this
  439. document, I'll do everything I can to make life easy for you.  A minimal
  440. implementation can be done very quickly and with very little overhead.
  441. Modules with built-in players are becoming more and more popular, and I
  442. believe a standard, extensible caller interface could benefit both
  443. musicians and programmers.
  444.  
  445.  
  446.  
  447.                         The XMOD format (obsolete)
  448.                         ~~~~~~~~~~~~~~~~~~~~~~~~~~
  449.  
  450.     Besides the standard module formats that MultiPlayer understands, there
  451. is also a new format you can use to turn just about anything into a
  452. multitasking module.  Basically, you go into the program you want to rip
  453. the music out of with some kind of debugger (I use S.I.M.), find the player
  454. and module (with luck, they'll be right next to each other), and add an
  455. XMOD header at the beginning so that MultiPlayer (or another player that
  456. supports XMOD format) knows what to do with it.
  457.     The XMOD format is very simple:
  458.  
  459. module+$00      'XMOD'
  460. module+$04      InitEntry
  461. module+$08      PlayEntry
  462. module+$0c      StopEntry
  463.  
  464.     When MultiPlayer loads a module and finds an 'XMOD' in the beginning of
  465. it, it first calls module+$04 to let the player initialize itself and the
  466. module, then it calls module+$08 50 times per second (the normal vertical
  467. blanking interrupt speed on PAL systems), and finally calls module+$0c when
  468. the user stops the module.  The player must be PC-relative, or it must do
  469. its own relocation in the initialization code.  The entire file is always
  470. loaded into chip memory in one continuous block.
  471.     All three entrypoints must use standard Amiga calling conventions and
  472. save registers D2-D7/A2-A6.  This includes the PlayEntry - it may not
  473. modify A5/A6 even though it's normally called from an interrupt routine.
  474. They may use D0-D1/A0-A1 without restoring them.  No parameters are passed
  475. to any of the entries, and no return codes are checked for.  All three
  476. routines must return with an RTS, not with an RTE.
  477.     If you need to debug an XMOD module (i.e.  it doesn't work the first
  478. time), you can just set the first instruction at module+$04 to 'illegal'
  479. ($4AFC), activate a good debugger, and load the module from MultiPlayer.
  480. As soon as MultiPlayer calls the module's init routine, it should kick into
  481. the debugger.
  482.  
  483.  
  484.  
  485.                         The AMOD format (obsolete)
  486.                         ~~~~~~~~~~~~~~~~~~~~~~~~~~
  487.  
  488.     The AMOD format is another special-purpose format MultiPlayer
  489. understands.  It is almost identical to the XMOD format, except in one
  490. respect:  AMOD modules always get loaded at a specific address.  For those
  491. few oddball music composer/player programs that like to write modules with
  492. lots of absolute addresses in them, this format may be the only good way to
  493. make the modules multitask.
  494.     The AMOD format is a simple extension to the XMOD format:
  495.  
  496. address+$00     'AMOD'
  497. address+$04     InitEntry
  498. address+$08     PlayEntry
  499. address+$0c     StopEntry
  500. address+$10     Load address
  501.  
  502.     When MultiPlayer encounters an AMOD module, it allocates memory for the
  503. module starting at the load address specified in the module.  The load
  504. address points to the location where the 'AMOD' identifier should go.  The
  505. module's code does not have to be PC-relative, since the load address is
  506. known.  Other than that, an AMOD module is loaded and executed the same way
  507. as an XMOD module.
  508.     If the required memory area is already occupied (even if it's just a
  509. few bytes somewhere in the required range), the module will not load.
  510. Therefore, the AMOD format should never be used except when absolutely
  511. necessary, since these modules may not be able to load even when there is
  512. enough memory available.  The memory does get allocated (the space is not
  513. just stomped on as with most European demos), so it will not cause the
  514. machine to crash; it just might not load sometimes when it should be able
  515. to.  I wish this module type wasn't necessary, but sometimes we just have
  516. to clean up somebody else's messes, don't we?
  517.  
  518.  
  519.  
  520.                               Contact Address
  521.                               ~~~~~~~~~~~~~~~
  522.  
  523.     I tend to move around a great deal, so mail sent directly to me
  524. sometimes has a hard time catching up.  If you want mail to reach me (it
  525. may take a while, but it WILL reach me), send it to this address:
  526.  
  527.         Bryan Ford
  528.         8749 Alta Hills Circle
  529.         Sandy, UT 84093
  530.  
  531.     I can be reached more quickly (for the time being anyway) on the phone
  532. or through one of the electronic mail addresses below:
  533.  
  534.         (801) 585-4619
  535.         bryan.ford@m.cc.utah.edu
  536.         baf0863@cc.utah.edu
  537.         baf0863@utahcca.bitnet
  538.  
  539.     If you want to get something to me through the mail more quickly, FIRST
  540. call or E-mail me to make sure I'm still here, then send it to this
  541. address:
  542.  
  543.         Bryan Ford
  544.         27104 Ballif Hall
  545.         University of Utah
  546.         Salt Lake City, UT 84112
  547.