New Documentation: An authoritative reference on the YM2612

For anything related to sound (YM2612, PSG, Z80, PCM...)

Moderator: BigEvilCorporation

AamirM
Very interested
Posts: 472
Joined: Mon Feb 18, 2008 8:23 am
Contact:

Post by AamirM » Sat Jul 05, 2008 12:39 pm

Hi,

I was doing some SSG-EG stuff in Regen and just noted another thing that only Kega emulates. The sound test number 0xA in Asterix will sound different every time you play it. The difference is in the speed and amplitude of it. It seems these two factors depend on something.
King Of Chaos wrote:Aamir, if you need a tester with good ears, I'm your man. :wink:
Sure, I am still testing a few thing to make sure nothing is broken. I will put it up when I am done.

stay safe,

AamirM

Nemesis
Very interested
Posts: 791
Joined: Wed Nov 07, 2007 1:09 am
Location: Sydney, Australia

Post by Nemesis » Wed Jul 09, 2008 9:15 pm

Several things first up:

1. This is going to be a VERY long post

2. All this information relates specifically to tests I have performed on the YM2612. Although I may not always name the YM2612 specifically, it is implied that all this information has only been tested and verified on the YM2612. I'm going to make a number of references to the way things are implemented in MAME along the way, and specifically, some areas where the behaviour of MAME differs to the way the YM2612 behaves. The implementation in MAME is based on tests done on the YM2203 and YM2151. Note that where I point out that MAME emulates something incorrectly, that simply means the emulation in MAME doesn't match the behaviour of the YM2612. Someone with access to the YM2203 or YM2151 hardware would need to cross-check my findings with those chips to determine whether the MAME implementation is incorrect for all these chips, or whether the YM2612 differs from these chips on these points of behaviour.

3. I'm going to make a LOT of assertions in the following article about how the YM2612 carries out certain operations. I'm not always going to explain how I know these assertions to be true, or the tests I carried out to gather this information. In most cases, it is the combined result of a lot of tests, analysis of sampled results, and a lot of math. Providing a full description of all the tests I carried out would detract from the purpose of this article. Suffice to say, if I make an assertion, it is only because I have specifically tested and proven this aspect to be correct to my satisfaction, even if the information is available in the official documentation, or has been implemented a certain way by another emulator. If there is something I haven't tested and verified for myself, I will note it along the way. If you want to query a specific assertion I make, or you want more info about how I obtained certain results, just ask.

4. I'm going to be pointing out flaws in other people's emulators in this document. Please don't take it personally. Existing emulators are a reference for behaviour on the YM2612 as much as a document is, and I think it's important to be clear what is emulated accurately, and what is not.

5. Credit to Jarek Burczynski for his work on the MAME core. His implementation of the update process for the envelope generator in MAME is extremely accurate, and in the end my testing confirmed much of his implementation as being correct.

And now without further ado, here's the result of the last 2 weeks of my life. What you're about to see is a full breakdown of how the YM2612 envelope generator works, including a comprehensive look at the SSG-EG mode. There is not a single emulator in existence at this point in time that implements everything from the following article correctly, so emulator authors should find it informative.


YM2612: Clock sources
First of all, an overview of the clock. The YM2612 is fed by an external clock source. The clock governs the speed at which the YM2612 performs all of its operations. In order to correctly emulate the YM2612, all aspects of its emulation must ultimately be tied back to the external clock.

In the PAL Mega Drive, the external clock is 7610000Hz. Documentation tells us that Yamaha FM chips divide the external clock by the FM prescaler, to get the FM clock. In the case of the YM2612, the FM prescaler appears to be fixed at 6. Here's how you calculate the FM clock:

FM Clock = External Clock / FM Prescaler
FM Clock = External Clock / 6
FM Clock = 7610000 / 6 = 1268333.33r

The FM Clock ultimately determines the sample rate of the YM2612 output, or in other words, the number of actual changes to the output that occur in a given period of time. Here's how you calculate the output frequency of the YM2612:

Output Frequency = FM Clock / 24
Output Frequency = 1268333.33r / 24 = 52847.22Hz

Interestingly, that divider of 24 is 6*4, or 6 channels times 4 operators. Maybe that's significant, maybe it isn't. All I've done at this stage is to verify the output frequency.

This is where things get a little more interesting. The Envelope Generator does NOT update once for each sample the YM2612 produces, as you might initially expect it would. More than that, the envelope generator isn't even tied to the FM clock source. Testing has shown that the clock for the envelope generator is independent of the FM clock. Here is the formula for the rate at which the envelope generator updates:

EG Clock = External Clock / 351

This may seem a little odd. I agree. I have tested and re-tested my results however, and the numbers come back the same. The ratio of the frequency output to the envelope generator clock is 1:2.4375. This directly contradicts MAME, which has the output cycle at a ratio of 1:3, or in other words, the envelope generator updates once for every 3 FM clock pulses. Comparing the output from MAME with the output from the YM2612 shows that MAME is incorrect however. Getting this update cycle for the envelope generator correct is important, and SSG-EG mode in particular is very sensitive to this.


YM2612: Overview of the Envelope Generator
The only purpose of the envelope generator is to generate a single number for each operator. That number is the attenuation value, or in simple terms, the amount to reduce the volume of an operator by. As an attenuation value, a high number produces a low volume, and a low number produces a high volume. The envelope generator outputs its attenuation value as a 10-bit number, giving a range of 0x000-0x3FF/0-1024. This output number is a simple linear progression from 0db to 96db. Here is the weighting for each bit in the 10-bit output of the envelope generator:

Code: Select all

EG attenuation output bit weighting = 96 / 2^10
---------------------------------------------------------------------------------
|   9   |   8   |   7   |   6   |   5   |   4   |   3   |   2   |   1   |   0   |
|-------------------------------------------------------------------------------|
|   48  |   24  |   12  |   6   |   3   |  1.5  |  0.75 | 0.375 | 0.1875|0.09375|
---------------------------------------------------------------------------------
IE, a value of 0x20 is 3db, a value of 0x40 is 6db.

Ultimately, the value of the envelope generator comes in its ability to automatically adjust this 10-bit attenuation value over time, based on a set of parameters. Under normal operation (no SSG-EG), the envelope generator creates a basic ADSR envelope. For the sake of brevity, I'm going to assume everyone reading this knows the basics of how the ADSR envelope in the YM2612 works, as it's sufficiently explained in the available documentation.


YM2612: Decibels to samples
The 10-bit attenuation output of the envelope generator is a linear scale in decibels. The decibel scale itself is logarithmic. If the envelope generator produces a linear increase in values, IE, incrementing the 10-bit output value by 1 for each clock pulse, the final curve that is produced by the YM2612 would be an exponential curve, not a straight line. This conversion from db to sample values is handled by the DAC for the FM module, not the envelope generator. Don't confuse the FM module DAC with the DAC registers at 0x2A/0x2B in the YM2612, that's a different thing entirely.

The envelope generator doesn't know what a decibel is, or how it relates to the final samples the YM2612 produces. The only job of the envelope generator is to produce this 10-bit attenuation output. After all calculations have been performed by the envelope generator, and the final attenuation value is handed off to the DAC, it is only then that the conversion from decibels to actual sample values is performed, and it is at this point an otherwise linear progression becomes an exponential progression. This means when you're looking at sampled data and comparing it to the output of the envelope generator, you must always be comparing on the basis of decibels.

This is probably obvious to anyone who has a bit of a background in audio, but I'm mentioning this because this confused me initially when I started looking at sampled output.


YM2612: Inputs and internal data
The envelope generator receives the following register data:

Code: Select all

TL      Total Level     7 bits
SL      Sustain Level   4 bits
AR      Attack Rate     5 bits
DR      Decay Rate      5 bits
SR      Sustain Rate    5 bits
RR      Release Rate    4 bits
SSG-EG  SSG-EG Mode     4 bits
In addition, the YM2612 receives the following data for each operator, which will be discussed further later on:
-Notification about key-on and key-off events
-The current 4-bit key-scale value

For each operator, the envelope generator stores the following data:
-A register indicating the current phase being run in the ADSR envelope (Attack, Decay, Sustain, or Release)
-A 6-bit calculated Rate value
-The current 10-bit attenuation value
-A flag indicating the polarity of the output. When this is set, the attenuation output is inverted.

The envelope generator also keeps the following global data which is shared by all operators:
-A running cycle counter of unknown size (not really important), which has enough storage for at least 14 bits of data.


YM2612: Attack and Decay
The ASDR envelope fundamentally only has two states: Attack and Decay. In the Attack phase, the attenuation value decreases exponentially, until it reaches 0. In the decay phase, the attenuation increases in a linear fashion until the attenuation reaches max, at which time no sound is produced for that channel. Decay, Sustain, and Release are all components of the decay phase, and for calculation purposes, they are all identical. With comparable settings, the same output will be produced from all three phases. When it comes to calculating envelopes, the only thing that makes the Decay, Sustain, and Release phases different from each other is that they calculate their decay rates using values from different registers, so the user can provide a different rate of decay for each state. When they are all set with comparable settings, the final waveform will be the same, regardless of what SL is set to or when key-off occurs.


YM2612: Calculating Attenuation Values
So we know the envelope generator maintains a 10-bit attenuation value for each operator. This is increased in a linear fashion during the decay phase, and decreased in an exponential fashion during the attack phase, but how and when are these adjustments made? This is the core of the envelope generator. The process occurs in several key steps.

1. The Rate Calculation
The rate calculation is the first step in calculating attenuation values. The envelope generator performs the rate calculation once, as the operator enters a new phase of the ADSR envelope. It requires two pieces of data first:

R: The rate value supplied in the register for the state currently running (AR,DR,SR,RR)
Rks: The 4-bit Rate Key-Scaling value for the operator. Refer to page 29 in the YM2608 manual.

With those two pieces of data, you then use the following formula to calculate the 6-bit internal rate number:

Rate = (2*R) + Rks

Special cases:
-If R = 0, then Rate = 0, regardless of the value of Rks.
-Rate is capped at 63. If the calculation result is greater than 63, Rate=63.
-Note that the source registers for AR, DR, and SR are 5-bit, but RR is only 4-bit. In order to convert RR into a 5-bit value for use in this calculation, multiply it by 2, and add 1. In other words, you can think of RR as the upper 4 bits of a 5-bit rate value, with the LSB fixed to 1.

This Rate number doesn't give you anything directly, just another number, but it is used in the following stages to adjust the attenuation.

2. The Update Cycle
And here's the bit that's not documented. The rate calculation gives you a 6-bit rate value, but how is that actually applied to vary the 10-bit attenuation value over time? First of all, I'm going to throw a few tables at you:

Code: Select all

Table 1: Counter shift values
11   11   11   11     0-3    (0x00-0x03)
10   10   10   10     4-7    (0x04-0x07)
9    9    9    9      8-11   (0x08-0x0B)
8    8    8    8      12-15  (0x0C-0x0F)
7    7    7    7      16-19  (0x10-0x13)
6    6    6    6      20-23  (0x14-0x17)
5    5    5    5      24-27  (0x18-0x1B)
4    4    4    4      28-31  (0x1C-0x1F)
3    3    3    3      32-35  (0x20-0x23)
2    2    2    2      36-39  (0x24-0x27)
1    1    1    1      40-43  (0x28-0x2B)
0    0    0    0      44-47  (0x2C-0x2F)
0    0    0    0      48-51  (0x30-0x33)
0    0    0    0      52-55  (0x34-0x37)
0    0    0    0      56-59  (0x38-0x3B)
0    0    0    0      60-63  (0x3C-0x3F)

Code: Select all

Table 2: Attenuation increment values
*0,0,0,0,0,0,0,0  *0,0,0,0,0,0,0,0  *0,1,0,1,0,1,0,1  *0,1,0,1,0,1,0,1    0-3    (0x00-0x03)
 0,1,0,1,0,1,0,1  *0,1,0,1,0,1,0,1   0,1,1,1,0,1,1,1  *0,1,1,1,0,1,1,1    4-7    (0x04-0x07)
 0,1,0,1,0,1,0,1   0,1,0,1,1,1,0,1   0,1,1,1,0,1,1,1   0,1,1,1,1,1,1,1    8-11   (0x08-0x0B)
 0,1,0,1,0,1,0,1   0,1,0,1,1,1,0,1   0,1,1,1,0,1,1,1   0,1,1,1,1,1,1,1    12-15  (0x0C-0x0F)
 0,1,0,1,0,1,0,1   0,1,0,1,1,1,0,1   0,1,1,1,0,1,1,1   0,1,1,1,1,1,1,1    16-19  (0x10-0x13)
 0,1,0,1,0,1,0,1   0,1,0,1,1,1,0,1   0,1,1,1,0,1,1,1   0,1,1,1,1,1,1,1    20-23  (0x14-0x17)
 0,1,0,1,0,1,0,1   0,1,0,1,1,1,0,1   0,1,1,1,0,1,1,1   0,1,1,1,1,1,1,1    24-27  (0x18-0x1B)
 0,1,0,1,0,1,0,1   0,1,0,1,1,1,0,1   0,1,1,1,0,1,1,1   0,1,1,1,1,1,1,1    28-31  (0x1C-0x1F)
 0,1,0,1,0,1,0,1   0,1,0,1,1,1,0,1   0,1,1,1,0,1,1,1   0,1,1,1,1,1,1,1    32-35  (0x20-0x23)
 0,1,0,1,0,1,0,1   0,1,0,1,1,1,0,1   0,1,1,1,0,1,1,1   0,1,1,1,1,1,1,1    36-39  (0x24-0x27)
 0,1,0,1,0,1,0,1   0,1,0,1,1,1,0,1   0,1,1,1,0,1,1,1   0,1,1,1,1,1,1,1    40-43  (0x28-0x2B)
 0,1,0,1,0,1,0,1   0,1,0,1,1,1,0,1   0,1,1,1,0,1,1,1   0,1,1,1,1,1,1,1    44-47  (0x2C-0x2F)
 1,1,1,1,1,1,1,1   1,1,1,2,1,1,1,2   1,2,1,2,1,2,1,2   1,2,2,2,1,2,2,2    48-51  (0x30-0x33)
 2,2,2,2,2,2,2,2   2,2,2,4,2,2,2,4   2,4,2,4,2,4,2,4   2,4,4,4,2,4,4,4    52-55  (0x34-0x37)
 4,4,4,4,4,4,4,4   4,4,4,8,4,4,4,8   4,8,4,8,4,8,4,8   4,8,8,8,4,8,8,8    56-59  (0x38-0x3B)
 8,8,8,8,8,8,8,8   8,8,8,8,8,8,8,8   8,8,8,8,8,8,8,8   8,8,8,8,8,8,8,8    60-63  (0x3C-0x3F)
Think of this as a multidimentional array of [64][8]. For each of the possible 64 rate values, there are 8 integer values.

Both these tables are based on information provided in MAME, but I've independently tested most of these values against the YM2612 output. I did find a few inaccuracies in the attenuation increment values provided in MAME. The entries marked with an asterisk differ from the values provided in the MAME core (For the record, yes, rate values of 2 and 3, 4 and 5, 6 and 7 do produce an identical output. That's not an error).

Ok, so what do these tables do? The following pseudo C-like algorithm describes the basic process the envelope generator carries out each cycle.

Code: Select all

++globalCycleCounter
For each operator
    //Read the shift value from Table 1
    counterShiftValue = counterShiftTable[operator.rate]
    If (globalCycleCounter % (1 << counterShiftValue)) == 0
        //Check the current cycle we're up to (0-7)
        updateCycle = (globalCycleCounter >> counterShiftValue) & 0x07

        //Read the next attenuation increment value from Table 2
        attenuationIncrement = attenuationIncrementTable[operator.rate][updateCycle]

        //Update the attenuation
        //The attenuation update process happens here. More on that in the next section.
    Endif
Next operator
The use of the global cycle counter makes this seem a little more complex than it really is. Basically, the Rate calculation determines two things:

1. How many envelope generator cycles will pass between updates
Table 1 lists "Counter shift values" for each possible rate. If the value is 0, the attenuation will be updated every cycle of the envelope generator. If the value is 1, the attenuation will be updated every second cycle. If it's 2, updates will occur every 4 cycles, if it's 3, every 8, etc. At the max value of 11, the attenuation will only be updated once every 2048 cycles of the envelope generator. This allows extremely slow attack and decay.

2. How large the adjustments to the attenuation will be at each update
Table 2 gives an 8-cycle loop of adjustment values for the attenuation, for each possible rate. The YM2612 works through these 8 values in a continuous loop. Each time the attenuation value for an envelope is updated, it will use the next sequential value in that loop to adjust the attenuation. In some cases, the value is 0, meaning even though an update cycle is triggered, no adjustments are made to the envelope on that cycle. For the larger rate values, the adjustments are more extreme.

The way the attenuation update cycle has been setup allows the envelope generator to create envelopes that have an extremely long decay (up to 10223616 samples, or 193.5 seconds on a PAL Mega Drive, for a rate of 2), while still allowing high-resolution updates to envelopes that have an extremely short decay (as low as 312 samples for a rate of 63). The same is of course true of the attack phase.


I think it's important to show how the contents of these two internal tables can be determined by measuring the external output of the YM2612, so allow me to show you a screencap:
Image

You can clearly see the steps in the attenuation here, including the pattern of cycles where the attenuation is and is not modified. This wave was created in SSG-EG mode, so the changes in amplitude are larger than normal (more on that later), but you can observe the same effect without SSG-EG active. This particular wave was produced with a calculated rate value of 7. Looking at the table above, that gives it an update cycle pattern of 0,1,1,1,0,1,1,1. You can see that pattern coming through in the output. The counter shift values increase the length between the steps.

This waveform also demonstrates the fact that the update cycle is based on a global clock. You can see at the start of this wave that there is a short period where the wave is held at 0db, before the first drop occurs. This drop does not occur at the same spacing as the rest of the changes that follow it. This demonstrates that each operator doesn't have its own running cycle counter. When you key-on an operator on the real system, it's possible for it to start at any point in the 8-cycle pattern of steps, and regardless of how large the spacing between steps, the first change in attenuation may happen at any time from the moment the note is keyed on, depending on what the global counter was up to when key-on occurred.


YM2612: Updating and outputting attenuation values
In the previous section, some pseudocode was presented showing the update cycle for attenuation values in the envelope generator. The part on actually adjusting the attenuation was left out. The update process is different between attack and decay. The decay phase is simpler, so I'll present that one first.

The decay phase:

Code: Select all

operator.attenuation += attenuationIncrement
The decay phase is a simple linear progression. You add the increment value to the attenuation on each update.

The attack phase:

Code: Select all

operator.attenuation += attenuationIncrement * (((1024 - operator.attenuation) / 16) + 1)
The attack phase is a little more complex. The attack phase produces an inverted exponential curve in attenuation, from maximum to minimum. This formula was derived after a little experimentation. This formula is only half the implementation however. In an earlier section, I mentioned that the envelope generator stores a flag for each operator indicating whether the output is inverted. During the attack phase, this flag is set. This effectively does a binary not of the attenuation when it is output, so 0x000 becomes 0xFFF, 0x12F becomes 0xED0, etc.

For the output of attenuation values, here's the basic process:
-Take a copy of the current internal 10-bit attenuation number
-If the output invert flag is set, perform a binary NOT on the number
-Add TL to the number. TL is converted to a 10-bit attenuation value first. A limit is applied to prevent the result from overflowing.


YM2612: The ASDR envelope
This is a quick summary of how the envelope generator manages the ASDR envelope.

1. Key-on occurs.
-The envelope generator sets the operator to the attack phase
-The rate parameter for the attack phase is calculated
-The current attenuation value is cleared
-The inverted output flag is set
2. The attenuation is continually adjusted, until attenuation reaches 0
-The envelope generator switches the operator to the decay phase
-The rate parameter for the decay phase is calculated
3. The attenuation is continually adjusted, until the attenuation value reaches SL
-The envelope generator switches the operator to the sustain phase
-The rate parameter for the sustain phase is calculated
4. The attenuation is continually adjusted until it hits max

At any point, key-off may occur. If it does, the following immediately occurs.
-The envelope generator sets the operator to the release phase
-The rate parameter for the release phase is calculated
-The inverted output flag is cleared


YM2612: SSG-EG mode
Ahh yes, now we've reached that part that every emulator gets wrong: SSG-EG mode. Well, right into it. The key feature SSG-EG provides is the ability to repeat the decay and sustain phases of the ASDR envelope over and over again. First of all, here's the SSG-EG register, as it's presented in the YM2608 documentation:
Image

You'll see there are 8 patterns presented here, but there isn't any real indication given in the documentation of what these patterns mean, or how they affect the output waveform. What these patterns actually represent is a series of repetitions of the decay and sustain phases of the ASDR envelope, like so:
Image
Image

When the attenuation for the operator hits max, the envelope switches back to the attack phase. If you've got the attack rate set to 0x1F like the Yamaha documentation says you need to, you can effectively say the attenuation is reset to 0, and the envelope mode switches back to the decay phase. The next section will cover the interaction of SSG-EG with the attack phase.

Each repetition within these patterns is selected from one of 4 basic modes. These modes affect the way the decay and sustain phases proceed. Each mode shows how the amplitude changes in the decay phase over time. Here are what each of the 4 modes do:

1. Normal
Image
This mode represents the normal progression of the decay and sustain phases. The amplitude starts at the maximum level, and proceeds to the minimum level, or in other words, the attenuation increases from 0 to max. This is the way the decay and sustain phases would proceed without SSG-EG mode active.

2. Inverted
Image
The inverted output flag is set for the operator during this repetition. The calculation remains the same, but due to the inverted output, the 10-bit attenuation value will proceed from maximum to minimum, or in other words, rather than "decaying", the output will be increasing in volume.

3. Held
Image
The output is simply held at maximum attenuation (minimum volume).

4. Inverted Held
Image
An inverted version of the above. The output is held at minimum attenuation (maximum volume).

There's a better description of the 4 bits that set the SSG-EG mode than the one provided in the YM2608 manual.

Code: Select all

SSG-EG: $90-$9E
---------------------------------
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|---------------|---|---|---|---|
| /   /   /   / | E |ATT|ALT|HLD|
---------------------------------
E = Enable. Unless this bit is set, SSG-EG mode is inactive, and none of the other bits have any effect.
ATT = Attack. When this bit is set, the inverted output flag is initially set at the start of the first decay phase.
ALT = Alternate. The inverted output flag is toggled after each repetition, alternating between 1 and 0.
HLD = Hold. After the decay and sustain phases have completed one full pass, the output is held at the final level. Once hold mode begins, it doesn't end until key-off.

If you look at the 8 patterns shown above, you'll see how these bits combine to create the patterns. Note that when you combine the ALT bit with hold, the inverted output flag is toggled before hold mode begins.


There is a second effect that SSG-EG has on all envelope generator decay phases, including the release phase. When an SSG-EG mode is applied to an operator, each time the attenuation value is being updated, the increase is multiplied by a factor of 6. Note that the envelope generator does not update any quicker in SSG-EG mode. The update cycles remain the same, but the amount that is added to the attenuation value is increased by a factor of 6 in the YM2612.

So, for the sake of completeness, as per the update formulae for the attack and decay phases given above, the following is the update formula for all decay phases under SSG-EG mode.

Code: Select all

operator.attenuation += 6 * attenuationIncrement
Note that the rate of change for the attack phase is not affected by SSG-EG mode, so the calculation previously given for the attack phase still stands. There are however a lot of other quirks to mention regarding the attack phase and SSG-EG mode. The interaction of the attack phase and SSG-EG is the topic of the next section.


YM2612: SSG-EG and the Attack Phase
Officially, Yamaha say the attack rate must be set to 0x1F in order to use SSG-EG mode. You can consider all behaviour of the YM2612 to be officially undefined and unsupported when you combine SSG-EG mode with an attack rate less than 0x1F.

The effect of having an attack value less than 0x1F with SSG-EG enabled is that the attack phase will also be included in each repetition of the envelope. SSG-EG does a loop of the attack, decay, and sustain phases. Although the rate of change in the decay phases are increased by a factor of 6 when SSG-EG is applied, the rate of change for the attack phase is unaffected, and is evaluated in its normal fashion. Each repetition of the envelope triggered by SSG-EG will repeat the attack, decay, and sustain modes.

The reason Yamaha say not to use the attack phase when SSG-EG is enabled, is because it really does mess things up. There are two issues it creates. The first issue relates to the nature of the calculation being performed to create the attack phase. The attack phase is an inverted exponential curve. The attenuation starts at maximum, and advances very quickly at first, but gradually slows as it approaches minimum attenuation. SSG-EG allows the output to be inverted. This doesn't change the actual calculation that is being performed in order to calculate the attack curve however, it literally just inverts the bits being output. This creates an attack curve which makes several extremely large, obvious steps from 0db, which are the most audible changes, while all the slower gradual changes occur at the other end of the scale, where the change in attenuation is much less noticeable. Here's a screenshot of what you get with an inverted attack curve:
Image

The second issue with combining an attack phase with SSG-EG mode is a little more subtle, and a lot more serious. If you look in an earlier section where I present pseudocode for the update process for the attack phase, you'll see that I mention the attack phase uses the output inversion bit to invert the output. Both the decay and attack phases use an incrementing value to represent the attenuation value, but the attack phase uses this output inversion setting to negate the output, which creates a falling attenuation (rising volume), instead of a rising attenuation (falling volume). The problem is, SSG-EG mode uses the output inversion bit too, and they end up interfering with each other. Sometimes the beginning of the attack phase will override the changes made by SSG-EG mode, and sometimes it will be the other way around. The worst thing is, because the update timing of the attack cycle, like all envelope update cycles, is tied to the global cycle count for the envelope generator, the results are inconsistent. If you repeat a key-on for the operator without changing any settings, you might get quite a different result.

With a real attack phase and SSG-EG mode both active, the first two SSG-EG patterns always work correctly, as they don't affect the output inversion bit. Patterns which use the ATT bit to start the output inverted sometimes have their inversion overridden by the attack phase, so they will start non-inverted. Patterns which use the ALT bit are severely affected, as at the start of each repetition, including the first, there's a chance that it could go either way, so each successive repetition may either be inverted or not inverted. There's also often a cross-over period of one update cycle for the envelope phase, where the output inversion bit is set by SSG-EG, then overridden by the attack phase, creating a loud spike between each repetition.

Here's a screenshot showing the messed up stuff you can get with the ALT bit set:
Image

And here's a screenshot showing the noise spike you get between each cycle even when it behaves:
Image

Emulating all these quirks would require exact knowledge of when these flags are set and cleared. I may do more work in this area at a later date.


YM2612: Current emulator support for SSG-EG mode
I haven't checked what emulators do about emulating SSG-EG mode with an attack phase, but here are some notes about current emulator support for SSG-EG mode in the YM2612.

-MAME doesn't clear the inverted output flag on release, so release during an inverted output creates a constant tone. MAME also only multiplies the attenuation increase by a factor of 4 in the decay and sustain phases, not 6. Interestingly, MAME also does not adjust the rate of the release phase, which it should.
-Kega doesn't do the release phase at all when SSG-EG is active, and messes up held notes. It does get the timing of the decay phase almost perfect however.
-Gens doesn't clear the inverted output flag on release, so it has a constant tone problem like MAME does. Gens doesn't seem to increase the decay phase increment at all, so each repetition of the decay phase runs 6 times too slow.


YM2612: SSG-EG Test ROM
Finally, here's a quick test rom which uses all 8 SSG-EG patterns:
http://nemesis.hacking-cult.org/MegaDri ... ssg-eg.bin

Here's the source:
http://nemesis.hacking-cult.org/MegaDri ... ssg-eg.asm

And here's a sample of the output you get on a PAL MD1600 MegaDrive, recorded at 96KHz:
http://nemesis.hacking-cult.org/MegaDri ... md1600.rar



And on that note, I'm going to get some sleep.
Last edited by Nemesis on Thu Jul 10, 2008 11:41 am, edited 1 time in total.

HardWareMan
Very interested
Posts: 745
Joined: Sat Dec 15, 2007 7:49 am
Location: Kazakhstan, Pavlodar

Post by HardWareMan » Thu Jul 10, 2008 3:50 am

So, if we return to previous test with oscilloscope:
Image
we get that real single channel sample rate is:
Nemesis wrote:Output Frequency = FM Clock / 24
Output Frequency = 1268333.33r / 24 = 52847.22Hz
And every channel have silence gap, that is mean 52847,22Hz / 2 = 26423,61Hz. That is close to measured with oscilloscope, but not equival. We need to re-measure that very carefully....

Nemesis
Very interested
Posts: 791
Joined: Wed Nov 07, 2007 1:09 am
Location: Sydney, Australia

Post by Nemesis » Thu Jul 10, 2008 4:59 am

I don't have access to any equipment right now, but I did my own measurements of the output with an oscilloscope during this process, and the numbers I got back matched this calculation as I recall. I didn't record any of the results though. I'll be able to test this again in a day or two, so I'll measure the output frequency again and snag some results.

Eke
Very interested
Posts: 884
Joined: Wed Feb 28, 2007 2:57 pm
Contact:

Post by Eke » Thu Jul 10, 2008 10:04 am

wow, this is great and definitively the technical doc I was waiting for since long long time :shock:

It describes the internal mechanism pretty well ,and also help me a lot to better understand the MAME emulator implementation


just to clarify something, Shiru mentionned this in another thread
http://www.spritesmind.net/_GenDev/foru ... .php?t=231
Shiru wrote:Alone Coder already did some tests on YM2203, which was verified on YM2612 - so we already knew about most of other registers, but not about special mode on/off.

So, if we change registers after KEY ON but before KEY OFF:

- TL, MULT, DET changes sound properly and immediately
- AR, DR, SR, RR, Feedback changes does not affects on currently playing note, but affects properly after new KEY ON

I forgot about SL, RS, but generally seems like changing of any registers, which controls amplitude and frequency is affects immediately, and changing of any registers, which controls envelope parameters and algorithm is affects after new KEY ON.

Alone Coder did used fact about AR/DR/etc in his compiler of TFM music to preload some values by frame before corresponding KEY ON, that allow to reduce number of register writes per frame (by moving some of writes to prev. frames). That works fine both on TFM device and SMD.
So, according to you, as the env rates are calculated at the start of each phase, would this mean that register values are also latched at the same time and not all together on Key ON as mentionned above ?

btw, just a minor suggestion but, in your frequency calculation and approximations, you should use the the exact clock values:53693175/7 for NTSC and 53203424/7 for PAL ;) this would make 52781.17 Hz for pal

Shiru
Very interested
Posts: 786
Joined: Sat Apr 07, 2007 3:11 am
Location: Russia, Moscow
Contact:

Post by Shiru » Thu Jul 10, 2008 10:33 am

Shiru wrote:- AR, DR, SR, RR, Feedback changes does not affects on currently playing note, but affects properly after new KEY ON
I was wrong about Feedback, actually it changes sound immediately too.

Nemesis
Very interested
Posts: 791
Joined: Wed Nov 07, 2007 1:09 am
Location: Sydney, Australia

Post by Nemesis » Thu Jul 10, 2008 12:04 pm

So, according to you, as the env rates are calculated at the start of each phase, would this mean that register values are also latched at the same time and not all together on Key ON as mentionned above ?
Hmmm. To be honest, I didn't thoroughly test this particular aspect. I meant to double-check this point before I made this post, but I ran out of time. I was going to be doing far more extensive tests on when various register changes are applied at a later date.

I did get results that indicated rate values could be changed after key-on, but I can't recall if this was combined with CSM or SSG-EG mode at the time, which could potentially have affected the result. Consider this suspect until I can re-test.

Eke
Very interested
Posts: 884
Joined: Wed Feb 28, 2007 2:57 pm
Contact:

Post by Eke » Thu Jul 10, 2008 12:58 pm

For the record, the MAME core resets the shift & increment values immediately after that:

1) the register values for the according rates (AR,DR,SR,RR) are modified

2)Rks is modified (either KSR register is modified or frequency/block is modified)

I think the introduction of Batman&Robin is pretty sensitive to that: I needed to modify the set_ar_ksr function to make it sound correctly


PS: some other differences I notice between MAME core and your tests around enveloppe shift & increments:

1/ the MAME implementation use a dedicated enveloppe increment value when calculated Attack Rate is 62 or 63: it uses +16 instead of +8 in this special case.

2/ shift values are wrong when R = 0 (in the formula Rate = 2xR +Rks, Rate should be 0 when R=0), shift value of 0 is usead instead of 11.

3/ in the case of RR, Rate is not forced to 0 if RR = 0

4/ the current attenuation value is NOT cleared at KEY ON

TmEE co.(TM)
Very interested
Posts: 2440
Joined: Tue Dec 05, 2006 1:37 pm
Location: Estonia, Rapla City
Contact:

Post by TmEE co.(TM) » Thu Jul 10, 2008 3:18 pm

http://www.fileden.com/files/2008/4/21/ ... SSG-EG.rar

This is the recording of the SSG-EG test ROM from my MD2. I cut out the silence to get smaller file... it seems to work exactly like real YM2612 (but cleaner signal).
Mida sa loed ? Nagunii aru ei saa ;)
http://www.tmeeco.eu
Files of all broken links and images of mine are found here : http://www.tmeeco.eu/FileDen

Nemesis
Very interested
Posts: 791
Joined: Wed Nov 07, 2007 1:09 am
Location: Sydney, Australia

Post by Nemesis » Fri Jul 11, 2008 12:14 am

For the record, the MAME core resets the shift & increment values immediately after that:

1) the register values for the according rates (AR,DR,SR,RR) are modified

2)Rks is modified (either KSR register is modified or frequency/block is modified)

I think the introduction of Batman&Robin is pretty sensitive to that: I needed to modify the set_ar_ksr function to make it sound correctly
So that means rate changes are applied immediately in MAME? That could be correct. I'll run some tests on this ASAP.

1/ the MAME implementation use a dedicated enveloppe increment value when calculated Attack Rate is 62 or 63: it uses +16 instead of +8 in this special case.
That's their way of eliminating the attack phase. When Rate = 62 or 63 during attack, the attack phase is skipped. MAME use this calculation to immediately force the result to TL, but based on the behaviour I've observed with CSM and SSG-EG, I think it far more likely that the YM2612 doesn't even enter the attack phase, and skips straight to decay when the calculated attack rate is greater than 61. I probably should have written that in as a special case.
2/ shift values are wrong when R = 0 (in the formula Rate = 2xR +Rks, Rate should be 0 when R=0), shift value of 0 is usead instead of 11.
There's actually no way to know what the shift value is set to when Rate = 0, and it doesn't make a difference in the end, since no updates are made even when the counter does expire. I've got the shift value set to 11 in this case as it fits with the pattern, but it wouldn't make any difference if they set the shift counter to 0. Likewise, there's no way to know what increment values are used for a Rate of 1, since there's no way for this case to arise.
3/ in the case of RR, Rate is not forced to 0 if RR = 0
That's because RR is a 4-bit value. For the rate calculation, RR is treated as a 5-bit value with the LSB set to 1, and it's after this conversion that the check is made, so Rate can never equal 0 in the release phase.
4/ the current attenuation value is NOT cleared at KEY ON
Yeah, I think they might be right about that actually. That calls into question my assertion that the YM2612 uses an output invert bit for the attack phase. Some behaviour in SSG-EG mode suggests it does, but this suggests it doesn't. Well, rather than resetting the contents of the attenuation register, they could be doing a binary NOT at the start of the attack phase, to adjust the attenuation into a compatible number. That sounds most likely to me at this stage.

Stef
Very interested
Posts: 3131
Joined: Thu Nov 30, 2006 9:46 pm
Location: France - Sevres
Contact:

Post by Stef » Fri Jul 11, 2008 10:53 am

Very interesting technicals informations !
Thanks for spending that much time in testing then writing your founds.
It's exactly that type of informations we need for accurate emulation, i'm really motived to rewrite a new YM2612 core now :p

TmEE co.(TM)
Very interested
Posts: 2440
Joined: Tue Dec 05, 2006 1:37 pm
Location: Estonia, Rapla City
Contact:

Post by TmEE co.(TM) » Fri Jul 11, 2008 11:48 am

Go Stef !!! I like Gens, and it desperately needs better sound (with good DAC support so my sound engine would sound NICE) :)

Now that I know how SSG-EG works, I could have some cool instruments :)
Mida sa loed ? Nagunii aru ei saa ;)
http://www.tmeeco.eu
Files of all broken links and images of mine are found here : http://www.tmeeco.eu/FileDen

Eke
Very interested
Posts: 884
Joined: Wed Feb 28, 2007 2:57 pm
Contact:

Post by Eke » Fri Jul 11, 2008 6:14 pm

notaz wrote:
Eke wrote:what did you do exactly to fix the MAME core ?
I changed the start of refresh_fc_eg_slot() to this:

Code: Select all

INLINE void refresh_fc_eg_slot(FM_SLOT *SLOT, int fc, int kc)
{
        int ksr, fdt;

        /* (frequency) phase increment counter */
        fdt = fc+SLOT->DT[kc];
        /* detect overflow */
        if (fdt < 0) fdt += fn_table[0x7ff*2] >> 2;
        SLOT->Incr = fdt*SLOT->mul >> 1;

...
Note that this only works when sampling @ 44.1hHz, so it's probably wrong. Multiplier may have something to do with all this too.
side note: this requires the YM2612 to be emulated at the original frequency (i.e HQ YM2612 mode) to work properly

I implemented the Gens interpolation method (anyone know about other resampling algorithm ?) in MAME to get it working but doing this:

fc += SLOT->DT[kc];
if (fc < 0) fc = (UINT32)((double)ym2612.OPN.fn_table[0x7ff*2] / ym2612.OPN.ST.freqbase) + SLOT->DT[kc];
apparently make it work is both case (tested @48kHz)

AamirM
Very interested
Posts: 472
Joined: Mon Feb 18, 2008 8:23 am
Contact:

Post by AamirM » Fri Jul 18, 2008 2:14 pm

Hi,

Great work once again Nemesis! Thanks for all the work your doing.

stay safe,

AamirM

Arbee
Interested
Posts: 16
Joined: Sat Aug 02, 2008 5:35 pm
Location: USA
Contact:

Post by Arbee » Sat Aug 02, 2008 5:40 pm

Hi. I'm looking to fold all of these fixes into official MAME. I see TFM/MM has a modified MAME core with some fixes and the Flashback fix was posted here. If anyone else has been tinkering with a MAME-derived FM core based on these findings or other tests against real h/w I'd love to see your changes. Please PM me if you'd rather not post them inline.

Thanks in advance!

Post Reply