From your source code, it looks like you're expecting to be able to find individual songs as standalone assembly listings for a 'master' CPU (such as your 8086) that you can execute to play a song on a sound chip. Outside of very small examples, that's not a very useful way to use a sound chip, since unless the song code is designed like a coroutine, you won't be able to do anything else while the song is playing. (1)
(An earlier version of this answer misread your source code, apologies.)
In a game, the more common usage is to have a music playback routine separate from the song data, called at regular intervals to interpret a data-dense music binary format, to send instructions to the sound chip on every 'tick'. This routine is often referred to as a 'tracker', 'player', 'monitor' or 'playroutine' (2). This is what I think you're looking for.
Short answer:
For an example of a complete game written in Z80 assembly for the Sega Master System that has a music player routine and binary music format, you can look at the source to Gravity Beam: Master Gaiden (MIT licensed). Disclaimer: I wrote it. :)
You can listen to the music on YouTube.
The music binary blobs are stored in musicmodule_*.bin
, and the playback routine is in audio.z80asm
. My routines are heavily commented, so you'll be able to see a typical way to control a SN76489 sound chip from within a game running on a Z80. (3)
Long answer, and why your question isn't straightforward to answer:
In a game, music playback would be controlled by a music library on the master CPU, with its own state machine in memory wrapping the hardware state machine of the underlying sound chipset. The role of the music library would be to abstract the sound chip and present simpler routines for the game code to use. It would hold all of the state regarding the current song, current song position, volume, etc. The music library would have entry points like: music_select_song:
, music_begin_playback:
, music_pause:
, music_set_volume:
. The musical data itself lives in a separate binary blob.
The game would advance the music playback at regular intervals (4) by calling some music_advance:
routine that would move some internal pointer along the current music track in memory, read the next bytes of data, identify what notes had been triggered or silenced, or what track parameters must be altered, and alter the state of the music chipset appropriately through OUT
or memory mapped I/O. (5) If this sounds like the role of a MIDI player, well, that's because that's exactly what a MIDI player does. :)
On retro systems, music binary format standards were uncommon, and ROM and RAM space were at a premium so formats and trackers and players were often bespoke - written by the musicians themselves to easily expose whatever functionality they felt they needed, like Chris Hülsbeck & Peter Thierolf's TFMX format. Player routines could even add additional functions that the underlying hardware didn't have, which would have to be simulated by the routine as it proceeded through the song - things like pitch slides, rapid arpeggios (like the characteristic 'bubbling' SID sound) and warping/flanging from duty-cycle tricks. These would either be calculated in real time by the play routine (producing a pseudo-instrument sometimes known as a 'patch' or 'programme') or baked into a list of instructions by the editor (so effects like echo and reverb would be achieved by the editor simply adding additional quieter notes automatically into the note list).
In Gravity Beam: Master Gaiden, the binary music format is heavily packed to save ROM space, and the music player routine must analyse the bytes of the song to determine the delay until the next event, the track to alter, the pitch, and the patch to use (by itself the SN76489 doesn't have ADSR envelopes, so fading notes in and out must either be in-song instructions or part of the patch).
And every system is different, and ports of the same game across different 8-bit platforms might be written (quickly) by completely different people, the underlying music code could be totally different.
In the modern day, it's much easier to write something that would play an unmodified MIDI file on any system by interpreting the data in real-time, or translate a MIDI file off-line in advance to a more appropriate format for a given system. With better computers and communication, there are much more transparent standards and cooler tools and editors and off-line patch simulators for things like the YM2612.
So, anything is possible!
To be able to answer your question, you'd need to give us an example of what sound chip you're interested in. Then, we'd be able to identify tracker programs to edit common music formats for that chip, and suggest playback routines to go with it. Software like GoatTracker for the C64 (and its SID) and ProTracker for the Amiga (and its Paula) are editors that come with an assembly language playback routine you can use in your projects to play their songs. An example of a commercial music library used at the time would be Sega GEMS for the Mega Drive.
If you really want to make a standalone music player of the kind you describe (uninterruptible, no graphics ability, etc.), then you'll still need to tell us what system and what sound chip it ought to target. Some tracker editors can output a standalone playable listing like the one you want, like the BASIC-headered self-loading and self-executing SID files described in pjc50's answer. :)
- Unless you're asking for 'all systems' and want a catalogue-like list of systems together with their most-common editor+playback pairs?
- An example of a situation where sound code and data would be interleaved is if you had very limited CPU and controlled some aspect of the chipset directly with asm commands to produce some effect. E.g. playing a digitised speech on a 48K Spectrum like in Ghostbusters. In Ghostbusters everything freezes while the shout is played, so I don't doubt there's a
play_ghostbusters_speech:
routine in there somewhere with amplitudes and delays all mixed up together.
- A 'tracker' more commonly refers to the editor for making tracker songs (ProTracker, OctaMED, GoatTracker etc.), but the playing routine is often called a 'tracker' too. A monitor is a term for a resident ROM/RAM program that lives in the background and can be invoked to describe the current state of a program. Since a music player lives in the background, it is occasionally called a monitor. SoundMon by Brian Postma is music player/editor on the Amiga found in games like Exodus 3010.
- Incidentally, since the Sord M5 / CGL M5 has the same sound chip and processor, I've had the same routine running on an M5 to play the same music (with a little accomodation for the different timing).
- This can also be called by an interrupt built into the system hardware, like a Master System VBlank interrupt, or a configurable timer.
- If you'd like to see what SN76489 'instructions' look like, you can see The SN76489 datasheet or the programming guide at SMS POWER.