by andete. Original documents available at:

Volume levels

I finally got to do some 'real' YM2413 reverse engineering. Though I started simple: I looked at the 4-bit volume register of the YM2413 channels (located in bits 3-0 of registers 0x30-0x38).

I wrote a program that plays a sine wave (the exact shape is less important here) for all 16 volume settings. I captured those signals (see analyze/volume/volume{0-15}.raw) converted them to YM2413 levels (see analyze/volume/volume{0-15}.out) and looked at the maximum amplitude of the signal.

That gives 16 pictures that look like this (this picture shows vol=0):

As expected for volume=0 (the maximum volume setting), the signal spans the full 9-bit range going from 0-511. ... I should start using signed YM2413 signal values, in that case the signal goes from -255 to +255 (including both -0 and +0, more on these two zeros below).

Mostly because it looks nice, I (manually) copy-pasted one sine-period of each volume setting into a single picture:

Here's a table showing volume-setting versus maximum-amplitude:


As already mentioned for vol=0 we get a maximum amplitude of 255. The YM2413 datasheet states that each volume step changes the volume by -3dB (thus multiplying the amplitude by a factor sqrt(0.5) ~= 0.707). And that's also what we see in this table.

The table shows the same maximum amplitude for volumes 14 and 15. Let's take a closer look:

In both cases the signal (only) takes on 4 distinct values: -1, -0, +0 and +1 (more on -0 and +0 below). But for vol=14 (the louder of the two) the signal is more often +/-1 than for vol=15, so on average it's further away from zero. Or in other words after quantization both signals use the same set of (only 4) values, but the signal for vol=14 has higher energy than the one for vol=15.

This shows that only looking at the maximum amplitude doesn't tell the whole story. I'll have to investigate this in more detail later. Probably after I've figured out the exact shape of the sine wave (it's not a perfect sine wave, instead it's a discretized version stored in ROM).

It's also interesting to know that setting vol=15 does not fully mute the channel (although -45dB is already very quite).

The pictures above show YM2413 levels, it's also interesting to look at the raw signal (I mean the ADC values not yet converted to YM2413 levels). The following image shows the raw signal for vol=12.

As explained in earlier posts there's relatively little noise near the center levels, so we can very clearly see the distinct YM2413 levels. But what is strange in this image is the non-uniform distance between the different levels (y-axis). For most levels this distance is (very roughly) about 100 ADC units, but for the center levels the distance is only about 50 units.

The two levels encircled in red are what I've been calling -0 and +0. If we make these two levels coincide (so move -0 a bit up and +0 a bit down), we get a much nicer image (though this transformation doesn't yet make the inter-level-distance completely uniform). And this confirms my guess that -0 and +0 should (ideally) be the same level.

One thing I already noticed a while ago but didn't mention yet in these posts is that the rising side of the sine wave (but not the falling side) shows some anomalies. These are shown in the next image:

So instead of monotonically rising, at regular intervals, the signal jumps up for one sample, then jumps two levels down for a while and then goes back to the original level. My current best explanation is that this is caused by the modulator part of the YM2413 instrument settings. These are the settings used in this image:

carrier0010000 0 15150015

So apparently, even though mod.TL=63, the frequency modulation is not fully suppressed. I tried setting mod.AR=0, but that didn't make any difference.

What did help was setting mod.WF=1 (use a clipped sine wave for the modulator, so for half a period the signal is zero) and setting car.ML=1 (make the carrier move twice as fast as the modulator). The combined effect is that we now have alternating 'clean' periods and periods with anomalies.

Sine-table resolution

Above I mentioned we need to know the exact shape of the sine wave to be able to exactly calculate the volume levels (because we could only measure amplitudes up to integer YM2413 levels). So let's make a start in investigating that sine wave table. One (easy) aspect is to determine the number of entries in the table.

For all the earlier measurements I used an arbitrary YM2413 channel frequency setting: R#0x10 = 0x61 R#0x20 = 0x12 this sets fnum=97 and block=1 (that results in a frequency of 37Hz assuming the YM2413 is clocked at 3.5MHz, but the actual value doesn't matter here). In the following experiments I've set the frequency to nice round powers-of-2 (IIRC I used fnum=32,64 and block=0,1 but again the exact values don't matter much).

Because we're using such low channel frequencies the YM2413 output remains constant for a number of consecutive samples before it moves to the next entry in the internal sine wave table. We could already see this in the earlier experiments: there the output remained constant for either 5 or 6 samples. Now when using powers-of-2 each 'flat' segment in the output also lasts for an exact power-of-2 number of samples (e.g. exactly 4, 8 or 16 samples).

Next I looked at the duration of one full sine period. When dividing that period-length by the segment-length, I found that this ratio is always exactly 1024. This means the YM2413 has 1024 entries in its sine wave table (and as expected this is also a power-of-2).

Actually a sine wave has some symmetry properties that allow to only store 1/4 of the wave in the table (the other parts can be reconstructed by flipping the x- and/or y-axis). So the ROM only needs 256 entries.

This result matches with other reverse engineering results of OPL2 and OPL3 chips. Those chips also divide the sine wave in 1024 steps. See this article for more details:

The YM2413 (OPLL) is derived from the YM3812 (OPL2), OPLL is a cheaper variant of OPL2 ('L' for light-weight??). So it's possible that, next to the number of entries, also the actual values in the tables match. But it's also possible the values are stored with less precision. That's something I should investigate later.

Return to top