|
|
From Sonic Retro
SCHG:Music Hacking/Pointer and Header FormatFrom Sonic Retro
Pointer formatPointers, as they pertain to SMPS, are values used to locate data at any point in the file. In the context of music editing, pointers will be used to locate the start of each channel's notation data; in addition, they are also used to branch to certain portions of data within the actual data through the use of coordination flags. This section in particular will describe the format used in the song headers and within the branching flags--F6, F7, and F8, respectively. 68k SMPSHeadersPointers in the headers of a song or sound effect are 16-bits (two bytes) and work relative to the beginning of the song file. For example, when working with a standalone piece of SMPS data, if the voice pointer reads 0180, you would go to offset $180 within the file to locate the voice data. If you're working with a song inside a full compiled ROM, the process is similar--simply take the offset where the song data starts and add the voice pointer's value to calculate the offset of the data. So, for example, if your music is at offset $E8900 in the ROM, you'd take the voice pointer value--in this case, $180--and you'd add it to the offset in order to locate the data. In this particular instance, $E8A80 would be the resulting offset, and subsequently the location of the voice data in the song. This pointer format applies to all pointers within the header of a piece of SMPS data. Coordination FlagsIn contrast to Z80 SMPS, the pointer format in branching coordination flags differs slightly. As before, each pointer is two bytes, but this time pointers are relative to the start of the pointer--not the start of the song file. To clarify, if you were using the format of the F7 flag--F7 xx yy zzzz--the pointer would be relative to the offset in the file where zzzz is stored. Pointers in coordination flags use signed relative values to locate data both after and before the current location. To do this, there is an imposed limit of about $8000 bytes in either direction on how far one single flag can branch in the song. When dealing with these flags, a value of FFFF acts as zero; in other words, a pointer value of FFFF would branch to the current location--nowhere. To branch ahead of the current location, one would increase the value beyond FFFF to jump ahead. So, for example, if our pointer was 0298, we would be branching ahead $299 bytes from our current location. Assuming the current location is $E8000, this branch would be locating data at offset $E8299. A general formula you could use in order to calculate the length of positive branches quickly would be to increase a pointer value by $1--this would help you get into the mindset of locating data more adeptly. To branch before the current location, one would decrease the value beyond FFFF to jump back. So, for example, if our pointer was FFE8, we would be branching $17 bytes before the current location. Assuming our current location is, once again, $E8000, this branch would be locating data at offset $E7FE9. The easiest way to calculate the length of a negative branch would be to take the pointer value and subtract it from FFFF. In our previous example, subtracting FFE8 from FFFF gives a result of $17. 68k SMPS is unique in that each song is literally self-contained--a song can be stored at any offset within a ROM and the relative nature of the pointers will always locate data properly. This is a significant advantage over the Z80 variant of the engine, which requires all data to be at absolute offsets within a data bank. Points of note:
Z80 SMPSZ80 SMPS differs from its 68k counterpart in a few different ways. First and foremost, unlike 68k SMPS, the same pointer format is used in both the coordination flags and the headers. All pointers are stored in little-endian format due to the nature of the processor; in other words, the byte order is reversed. If a pointer reads 7880, you should mentally parse it as referring to bank offset 8078. Secondly, pointers are absolute instead of relative, but are only absolute in terms of the current Z80 data bank. Pointer values will always start with a value of 8000, regardless of whether or not the bank itself is stored at an offset that contains 8000 as part of the address; this is so the music can be properly located in the context of the Z80 bank. So, for example, if the current Z80 bank is at offset $70000, a pointer value of 7880 (which, when swapped, becomes 8078) would be locating data at offset $70078. When dealing with standalone Z80 SMPS files, it is required that you first make note of the starting offset of the song within its respective Z80 bank before you can properly calculate pointer values within the song. So, for example, if a song is located at offset $F0900 in the ROM, your starting offset for the song will be 8900. To calculate relative offsets within the file where data is stored, subtract the starting offset from your pointer value. So, for example, if our DAC pointer reads value 508E (which after swapping the bytes becomes 8E50), the relative offset of our DAC notation within the file will be offset $550, or $550 bytes from the start of the song file. This process is relatively simpler for the SMPS variant used in the SMS version of Sonic 2; since all music data is contained in bank 2 ($8000-$FFFF), simply convert the pointer to big endian and use that as your absolute address in the ROM. In addition to within Z80 SMPS data, this format is also used to locate data within a game's music pointer index. Sonic 2 Final/Mega Man: Wily WarsSonic 2 is a special case amongst games that use the Z80 SMPS engine in that its music is compressed to individual files in the saxman compression, which are subsequently parsed within Z80 RAM. In contrast to normal Z80 SMPS music, the only difference is that compressed music in Sonic 2 always has a starting offset of 1380. All operations regarding the calculation of offsets from pointers remains the same. It is, however, worth noting that not all music in Sonic 2 is compressed; exceptions are noted as such later on in the guide. Mega Man: Wily Wars stores songs uncompressed, but also has music copied to Z80 RAM. The starting offset for this game is always $1002. Header formatsMega Drive SMPSAll pointers in this section differ according to the game you are editing. Remember, relative pointers for 68k SMPS, and absolute little endian pointers for Z80 SMPS. The music header format listed is a universal format used in all incarnations of the SMPS engine except for Knuckles' Chaotix, which uses a slightly modified version to accommodate for the extra sound hardware (the PWM sound channels). SMPS main header
It is easy to convert between Sonic 2 and Sonic 3+ main tempo values: S2 main tempo = (256 - S3 main tempo) mod 256 S3 main tempo = (256 - S2 main tempo) mod 256 The conversion works for all values of main tempo other than 0 -- this value cannot be converted exactly between the two drivers. To convert between S1 and S2 main tempo can be done approximately) with the following formula: S2 main tempo = floor{ [ (S1 main tempo-1) * 256 + floor(S1 main tempo/2) ] / S1 main tempo } (where S1 main tempo of 0 should be plugged as 256). There is an error intrinsic to this approximation; this error is always less than half a frame out of every 256 frames, and the average absolute error for all main tempo values is about 0.239 frames out of every 256. The reverse conversion (from Sonic 2 to Sonic 1 tempos) is best done with a lookup table; here is the table for the more important S2 tempos (values with an asterisk give exact conversions; the others are the conversions of least error):
Then follows headers for FM and DAC channels, as many as are specified on byte $02 (offsets relative to header position in file). SMPS FM and DAC headers
The first channel specified is the DAC channel, regardless of whether or not bytes $02-$03 are zero or not. After those, there are the PSG channels: SMPS PSG headers
Only PSG3 can be made into a noise channel without bugs. To use a noise channel, you need to use 3 PSG channels and convert the third into a noise channel. For SFX, the header is quite different: Main SFX header
Unlike the case for music, SFX track headers are equal for all tracks; there is one header for each track specified on byte $03: SFX track headers
So you must specify the channel type on a track-by-track basis. If you are careful enough designing SFX, you can theoretically play (and hear) several of them at the same time as long as they do not share the same channels -- each SFX channel has a fixed location in z80 RAM and a fixed song track in RAM that it overrides on initialization. In any of the games, you can't safely use PSG3 ($C0) and the noise channel ($E0) at the same time at SFX initialization, as they will want to use the same block of RAM. Turning a channel to noise with coord. flags afterwards is safe, however you will always want to convert a PSG3 channel to noise or you will face many bugs. In Sonic & Knuckles, you will want to avoid using $E0 to define a noise channel, as it will be very buggy. 32X SMPSThe format for the SMPS music header is slightly different to accommodate for the 32x's extra sound hardware.
Master System/Game Gear SMPSSince the SMS only has 4 PSG channels (3 tone, 1 noise), the header format is changed to accomodate this.
Understanding the header is essential to editing music. This is the basis of basic music editing, such as pitch and tempo editing. Once you understand the pointers and the header, then you are ready to start learning more advanced things. Sega CD SMPS ("SMPS-PCM")Since SMPS-PCM runs on a CPU that can only access the Sega CD's PCM chip which hooks into a global sample bank (for each SMPS program on disc), the header is different:
|
