commodore64: implement some of the waveform calculation algorithms documented in libsidplayfp
This commit is contained in:
parent
631212ca4f
commit
5926918f8b
|
@ -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" />
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Computers.Commodore64
|
||||
{
|
||||
class EnvelopeGenerator
|
||||
{
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue