commodore64: implement some of the waveform calculation algorithms documented in libsidplayfp

This commit is contained in:
saxxonpike 2012-11-14 18:15:52 +00:00
parent 631212ca4f
commit 5926918f8b
4 changed files with 389 additions and 0 deletions

View File

@ -95,7 +95,10 @@
<Compile Include="Computers\Commodore64\PRGFile.cs" />
<Compile Include="Computers\Commodore64\MemBus.cs" />
<Compile Include="Computers\Commodore64\Sid.cs" />
<Compile Include="Computers\Commodore64\SidEnvelopeGenerator.cs" />
<Compile Include="Computers\Commodore64\SidSoundProvider.cs" />
<Compile Include="Computers\Commodore64\SidWaveformCalculator.cs" />
<Compile Include="Computers\Commodore64\SidWaveformGenerator.cs" />
<Compile Include="Computers\Commodore64\Via.cs" />
<Compile Include="Computers\Commodore64\VicII.cs" />
<Compile Include="Computers\Commodore64\VicIIBackgroundGenerator.cs" />

View File

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BizHawk.Emulation.Computers.Commodore64
{
class EnvelopeGenerator
{
}
}

View File

@ -0,0 +1,133 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BizHawk.Emulation.Computers.Commodore64
{
class SidWaveformCalculator
{
struct CombinedWaveformConfig
{
public float bias;
public float pulseStrength;
public float topBit;
public float distance;
public float stMix;
public CombinedWaveformConfig(float newBias, float newPS, float newtB, float newDistance, float newStMix)
{
bias = newBias;
pulseStrength = newPS;
topBit = newtB;
distance = newDistance;
stMix = newStMix;
}
}
private static CombinedWaveformConfig[] cfgArray = new CombinedWaveformConfig[]
{
new CombinedWaveformConfig(0.880815f, 0.0f, 0.0f, 0.3279614f, 0.5999545f),
new CombinedWaveformConfig(0.8924618f, 2.014781f, 1.003332f, 0.02992322f, 0.0f),
new CombinedWaveformConfig(0.8646501f, 1.712586f, 1.137704f, 0.02845423f, 0.0f),
new CombinedWaveformConfig(0.9527834f, 1.794777f, 0.0f, 0.09806272f, 0.7752482f)
};
public short[][] BuildTable()
{
short[][] wftable = new short[8][];
for (int i = 0; i < 8; i++)
wftable[i] = new short[4096];
for (int accumulator = 0; accumulator < 1 << 24; accumulator += 1 << 12)
{
int idx = (accumulator >> 12);
wftable[0][idx] = 0xFFF;
wftable[1][idx] = (short)((accumulator & 0x800000) == 0 ? idx << 1 : (idx ^ 0xFFF) << 1);
wftable[2][idx] = (short)idx;
wftable[3][idx] = CalculateCombinedWaveForm(cfgArray[0], 3, accumulator);
wftable[4][idx] = 0xFFF;
wftable[5][idx] = CalculateCombinedWaveForm(cfgArray[1], 5, accumulator);
wftable[6][idx] = CalculateCombinedWaveForm(cfgArray[2], 6, accumulator);
wftable[7][idx] = CalculateCombinedWaveForm(cfgArray[3], 7, accumulator);
}
return wftable;
}
private short CalculateCombinedWaveForm(CombinedWaveformConfig config, int waveform, int accumulator)
{
float[] o = new float[12];
for (int i = 0; i < 12; i++)
{
o[i] = (accumulator >> 12 & (1 << i)) != 0 ? 1.0f : 0.0f;
}
if ((waveform & 3) == 1)
{
bool top = (accumulator & 0x800000) != 0;
for (int i = 11; i > 0; i--)
{
o[i] = top ? 1.0f - o[i - 1] : o[i - 1];
}
}
if ((waveform & 3) == 3)
{
o[0] *= config.stMix;
for (int i = 1; i < 12; i++)
{
o[i] = o[i - 1] * (1.0f - config.stMix) + o[i] * config.stMix;
}
}
o[11] *= config.topBit;
if (waveform == 3 || waveform > 4)
{
float[] distanceTable = new float[12 * 2 + 1];
for (int i = 0; i <= 12; i++)
{
distanceTable[12 + i] = distanceTable[12 - i] = 1.0f / (1.0f + i * i * config.distance);
}
float[] tmp = new float[12];
for (int i = 0; i < 12; i++)
{
float avg = 0.0f;
float n = 0.0f;
for (int j = 0; j < 12; j++)
{
float weight = distanceTable[i - j + 12];
avg += o[j] * weight;
n += weight;
}
if (waveform > 4)
{
float weight = distanceTable[i - 12 + 12];
avg += config.pulseStrength * weight;
n += weight;
}
tmp[i] = (o[i] + avg / n) * 0.5f;
}
for (int i = 0; i < 12; i++)
{
o[i] = tmp[i];
}
}
int value = 0;
for (int i = 0; i < 12; i++)
{
if (o[i] - config.bias > 0.0f)
{
value |= 1 << i;
}
}
return (short)value;
}
}
}

View File

@ -0,0 +1,242 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BizHawk.Emulation.Computers.Commodore64
{
class WaveformGenerator
{
private int accumulator;
private short[] dac = new short[4096];
private int floatingOutputTtl;
private int freq;
private short[][] modelWave;
private bool msbRising;
private int noiseOutput;
private int noNoise;
private int noNoiseOrNoiseOutput;
private int noPulse;
private int pulseOutput;
private int pw;
private int ringMsbMask;
private int shiftPipeline;
private int shiftRegister;
private int shiftRegisterReset;
private bool sync;
private bool test;
private short[] wave;
private int waveform;
private int waveformOutput;
public WaveformGenerator()
{
}
public void Clock()
{
if (test)
{
if (shiftRegisterReset != 0 && --shiftRegisterReset == 0)
{
ResetShiftRegister();
}
pulseOutput = 0xFFF;
}
else
{
int accumulatorNext = (accumulator + freq) & 0xFFFFFF;
int accumulatorBitsSet = ~accumulator & accumulatorNext;
accumulator = accumulatorNext;
msbRising = (accumulatorBitsSet & 0x800000) != 0;
if ((accumulatorBitsSet & 0x080000) != 0)
{
shiftPipeline = 2;
}
else if (shiftPipeline != 0 && --shiftPipeline == 0)
{
ClockShiftRegister();
}
}
}
private void ClockShiftRegister()
{
int bit0 = ((shiftRegister >> 22) ^ (shiftRegister >> 17)) & 0x1;
shiftRegister = ((shiftRegister << 1) | bit0) & 0x7FFFFF;
SetNoiseOutput();
}
public short Output(WaveformGenerator ringModulator)
{
if (waveform != 0)
{
int ix = (accumulator ^ (ringModulator.accumulator & ringMsbMask)) >> 12;
waveformOutput = wave[ix] & (noPulse | pulseOutput) & noNoiseOrNoiseOutput;
if (waveform > 0x8)
{
WriteShiftRegister();
}
}
else
{
if (floatingOutputTtl != 0 && --floatingOutputTtl == 0)
{
waveformOutput = 0;
}
}
pulseOutput = ((accumulator >> 12) >= pw) ? 0xFFF : 0x000;
return dac[waveformOutput];
}
public int ReadAccumulator()
{
return accumulator;
}
public int ReadFreq()
{
return freq;
}
public byte ReadOsc()
{
return (byte)(waveformOutput >> 4);
}
public bool ReadSync()
{
return sync;
}
public bool ReadTest()
{
return test;
}
public void Reset()
{
accumulator = 0;
freq = 0;
pw = 0;
msbRising = false;
waveform = 0;
test = false;
sync = false;
wave = modelWave[0];
ringMsbMask = 0;
noNoise = 0xFFF;
noPulse = 0xFFF;
pulseOutput = 0xFFF;
ResetShiftRegister();
shiftPipeline = 0;
waveformOutput = 0;
floatingOutputTtl = 0;
}
private void ResetShiftRegister()
{
shiftRegister = 0x7FFFFF;
shiftRegisterReset = 0;
SetNoiseOutput();
}
private void SetNoiseOutput()
{
noiseOutput =
((shiftRegister & 0x100000) >> 9) |
((shiftRegister & 0x040000) >> 8) |
((shiftRegister & 0x004000) >> 5) |
((shiftRegister & 0x000800) >> 3) |
((shiftRegister & 0x000200) >> 2) |
((shiftRegister & 0x000020) << 1) |
((shiftRegister & 0x000004) << 3) |
((shiftRegister & 0x000001) << 4);
noNoiseOrNoiseOutput = noNoise | noiseOutput;
}
public void Synchronize(WaveformGenerator syncDest, WaveformGenerator syncSource)
{
if (msbRising && syncDest.sync && !(sync && syncSource.msbRising))
{
syncDest.accumulator = 0;
}
}
public void WriteControl(byte control)
{
int waveformPrev = waveform;
bool testPrev = test;
waveform = (control >> 4) & 0x0F;
test = (control & 0x08) != 0;
sync = (control & 0x02) != 0;
wave = modelWave[waveform & 0x7];
ringMsbMask = ((~control >> 5) & (control >> 2) & 0x1) << 23;
noNoise = (waveform & 0x8) != 0 ? 0x000 : 0xFFF;
noNoiseOrNoiseOutput = noNoise | noiseOutput;
noPulse = (waveform & 0x4) != 0 ? 0x000 : 0xFFF;
if (!testPrev && test)
{
accumulator = 0;
shiftPipeline = 0;
shiftRegisterReset = 0x8000;
}
else if (testPrev && !test)
{
int bit0 = (~shiftRegister >> 17) & 0x1;
shiftRegister = ((shiftRegister << 1) | bit0) & 0x7FFFFF;
SetNoiseOutput();
}
if (waveform == 0 && waveformPrev != 0)
{
floatingOutputTtl = 0x28000;
}
}
public void WriteFreqLo(byte freqLo)
{
freq &= 0xFF00;
freq |= freqLo;
}
public void WriteFreqHi(byte freqHi)
{
freq &= 0x00FF;
freq |= (int)(freqHi << 8) & 0xFF00;
}
public void WritePWLo(byte pwLo)
{
pw &= 0x0F00;
pw |= pwLo;
}
public void WritePWHi(byte pwHi)
{
pw &= 0x00FF;
pw |= (int)(pwHi << 8) & 0x0F00;
}
private void WriteShiftRegister()
{
shiftRegister &=
~((1 << 20) | (1 << 18) | (1 << 14) | (1 << 11) |
(1 << 9) | (1 << 5) | (1 << 2) | (1 << 0)) |
((waveformOutput & 0x800) << 9) |
((waveformOutput & 0x400) << 8) |
((waveformOutput & 0x200) << 5) |
((waveformOutput & 0x100) << 3) |
((waveformOutput & 0x080) << 2) |
((waveformOutput & 0x040) >> 1) |
((waveformOutput & 0x020) >> 3) |
((waveformOutput & 0x010) >> 4);
noiseOutput &= waveformOutput;
noNoiseOrNoiseOutput = noNoise | noiseOutput;
}
}
}