310 lines
7.0 KiB
C#
310 lines
7.0 KiB
C#
using BizHawk.Common;
|
|
|
|
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
|
|
{
|
|
public sealed partial class Sid
|
|
{
|
|
private sealed class Voice
|
|
{
|
|
private int _accBits;
|
|
private int _accNext;
|
|
private int _accumulator;
|
|
private bool _controlTestPrev;
|
|
private int _controlWavePrev;
|
|
private int _delay;
|
|
private int _floatOutputTtl;
|
|
private int _frequency;
|
|
private bool _msbRising;
|
|
private int _noise;
|
|
private int _noNoise;
|
|
private int _noNoiseOrNoise;
|
|
private int _noPulse;
|
|
private int _output;
|
|
private int _pulse;
|
|
private int _pulseWidth;
|
|
private bool _ringMod;
|
|
private int _ringMsbMask;
|
|
private int _shiftRegister;
|
|
private int _shiftRegisterReset;
|
|
private bool _sync;
|
|
private bool _test;
|
|
private int[] _wave;
|
|
private int _waveform;
|
|
private int _waveformIndex;
|
|
private readonly int[][] _waveTable;
|
|
|
|
public Voice(int[][] newWaveTable)
|
|
{
|
|
_waveTable = newWaveTable;
|
|
HardReset();
|
|
}
|
|
|
|
public void HardReset()
|
|
{
|
|
_accumulator = 0;
|
|
_delay = 0;
|
|
_floatOutputTtl = 0;
|
|
_frequency = 0;
|
|
_msbRising = false;
|
|
_noNoise = 0xFFF;
|
|
_noPulse = 0xFFF;
|
|
_output = 0x000;
|
|
_pulse = 0xFFF;
|
|
_pulseWidth = 0;
|
|
_ringMsbMask = 0;
|
|
_sync = false;
|
|
_test = false;
|
|
_wave = _waveTable[0];
|
|
_waveform = 0;
|
|
|
|
ResetShiftReg();
|
|
}
|
|
|
|
public void ExecutePhase2()
|
|
{
|
|
|
|
{
|
|
if (_test)
|
|
{
|
|
if (_shiftRegisterReset != 0 && --_shiftRegisterReset == 0)
|
|
{
|
|
ResetShiftReg();
|
|
}
|
|
_pulse = 0xFFF;
|
|
}
|
|
else
|
|
{
|
|
_accNext = (_accumulator + _frequency) & 0xFFFFFF;
|
|
_accBits = ~_accumulator & _accNext;
|
|
_accumulator = _accNext;
|
|
_msbRising = (_accBits & 0x800000) != 0;
|
|
|
|
if ((_accBits & 0x080000) != 0)
|
|
_delay = 2;
|
|
else if (_delay != 0 && --_delay == 0)
|
|
ClockShiftReg();
|
|
}
|
|
}
|
|
}
|
|
|
|
// ------------------------------------
|
|
|
|
private void ClockShiftReg()
|
|
{
|
|
|
|
{
|
|
_shiftRegister = ((_shiftRegister << 1) |
|
|
(((_shiftRegister >> 22) ^ (_shiftRegister >> 17)) & 0x1)
|
|
) & 0x7FFFFF;
|
|
SetNoise();
|
|
}
|
|
}
|
|
|
|
private void ResetShiftReg()
|
|
{
|
|
_shiftRegister = 0x7FFFFF;
|
|
_shiftRegisterReset = 0;
|
|
SetNoise();
|
|
}
|
|
|
|
private void SetNoise()
|
|
{
|
|
|
|
{
|
|
_noise =
|
|
((_shiftRegister & 0x100000) >> 9) |
|
|
((_shiftRegister & 0x040000) >> 8) |
|
|
((_shiftRegister & 0x004000) >> 5) |
|
|
((_shiftRegister & 0x000800) >> 3) |
|
|
((_shiftRegister & 0x000200) >> 2) |
|
|
((_shiftRegister & 0x000020) << 1) |
|
|
((_shiftRegister & 0x000004) << 3) |
|
|
((_shiftRegister & 0x000001) << 4);
|
|
_noNoiseOrNoise = _noNoise | _noise;
|
|
}
|
|
}
|
|
|
|
private void WriteShiftReg()
|
|
{
|
|
_output &=
|
|
0xBB5DA |
|
|
((_output & 0x800) << 9) |
|
|
((_output & 0x400) << 8) |
|
|
((_output & 0x200) << 5) |
|
|
((_output & 0x100) << 3) |
|
|
((_output & 0x040) >> 1) |
|
|
((_output & 0x020) >> 3) |
|
|
((_output & 0x010) >> 4);
|
|
_noise &= _output;
|
|
_noNoiseOrNoise = _noNoise | _noise;
|
|
}
|
|
|
|
// ------------------------------------
|
|
|
|
public int Control
|
|
{
|
|
set
|
|
{
|
|
_controlWavePrev = _waveform;
|
|
_controlTestPrev = _test;
|
|
|
|
_sync = (value & 0x02) != 0;
|
|
_ringMod = (value & 0x04) != 0;
|
|
_test = (value & 0x08) != 0;
|
|
_waveform = (value >> 4) & 0x0F;
|
|
_wave = _waveTable[_waveform & 0x07];
|
|
_ringMsbMask = ((~value >> 5) & (value >> 2) & 0x1) << 23;
|
|
_noNoise = (_waveform & 0x8) != 0 ? 0x000 : 0xFFF;
|
|
_noNoiseOrNoise = _noNoise | _noise;
|
|
_noPulse = (_waveform & 0x4) != 0 ? 0x000 : 0xFFF;
|
|
|
|
if (!_controlTestPrev && _test)
|
|
{
|
|
_accumulator = 0;
|
|
_delay = 0;
|
|
_shiftRegisterReset = 0x8000;
|
|
}
|
|
else if (_controlTestPrev && !_test)
|
|
{
|
|
_shiftRegister = ((_shiftRegister << 1) |
|
|
((~_shiftRegister >> 17) & 0x1)
|
|
) & 0x7FFFFF;
|
|
SetNoise();
|
|
}
|
|
|
|
if (_waveform == 0 && _controlWavePrev != 0)
|
|
_floatOutputTtl = 0x28000;
|
|
}
|
|
}
|
|
|
|
public int FrequencyLo
|
|
{
|
|
get
|
|
{
|
|
return _frequency & 0xFF;
|
|
}
|
|
set
|
|
{
|
|
_frequency &= 0xFF00;
|
|
_frequency |= value & 0x00FF;
|
|
}
|
|
}
|
|
|
|
public int FrequencyHi
|
|
{
|
|
get
|
|
{
|
|
return _frequency >> 8;
|
|
}
|
|
set
|
|
{
|
|
_frequency &= 0x00FF;
|
|
_frequency |= (value & 0x00FF) << 8;
|
|
}
|
|
}
|
|
|
|
public int Output(Voice ringModSource)
|
|
{
|
|
|
|
{
|
|
if (_waveform != 0)
|
|
{
|
|
_waveformIndex = (_accumulator ^ (ringModSource._accumulator & _ringMsbMask)) >> 12;
|
|
_output = _wave[_waveformIndex] & (_noPulse | _pulse) & _noNoiseOrNoise;
|
|
if (_waveform > 8)
|
|
{
|
|
WriteShiftReg();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (_floatOutputTtl != 0 && --_floatOutputTtl == 0)
|
|
{
|
|
_output = 0x000;
|
|
}
|
|
}
|
|
|
|
_pulse = _accumulator >> 12 >= _pulseWidth ? 0xFFF : 0x000;
|
|
return _output;
|
|
}
|
|
}
|
|
|
|
public int PulseWidthLo
|
|
{
|
|
get
|
|
{
|
|
return _pulseWidth & 0xFF;
|
|
}
|
|
|
|
set
|
|
{
|
|
_pulseWidth &= 0x0F00;
|
|
_pulseWidth |= value & 0x00FF;
|
|
}
|
|
}
|
|
|
|
public int PulseWidthHi
|
|
{
|
|
get
|
|
{
|
|
return _pulseWidth >> 8;
|
|
}
|
|
|
|
set
|
|
{
|
|
_pulseWidth &= 0x00FF;
|
|
_pulseWidth |= (value & 0x000F) << 8;
|
|
}
|
|
}
|
|
|
|
public bool RingMod => _ringMod;
|
|
|
|
public bool Sync => _sync;
|
|
|
|
public void Synchronize(Voice target, Voice source)
|
|
{
|
|
if (_msbRising && target._sync && !(_sync && source._msbRising))
|
|
{
|
|
target._accumulator = 0;
|
|
}
|
|
}
|
|
|
|
public bool Test => _test;
|
|
|
|
public int Waveform => _waveform;
|
|
|
|
// ------------------------------------
|
|
public void SyncState(Serializer ser)
|
|
{
|
|
ser.Sync("_accBits", ref _accBits);
|
|
ser.Sync("_accNext", ref _accNext);
|
|
ser.Sync("_accumulator", ref _accumulator);
|
|
ser.Sync("_controlTestPrev", ref _controlTestPrev);
|
|
ser.Sync("_controlWavePrev", ref _controlWavePrev);
|
|
ser.Sync("_delay", ref _delay);
|
|
ser.Sync("_floatOutputTtl", ref _floatOutputTtl);
|
|
ser.Sync("_frequency", ref _frequency);
|
|
ser.Sync("_msbRising", ref _msbRising);
|
|
ser.Sync("_noise", ref _noise);
|
|
ser.Sync("_noNoise", ref _noNoise);
|
|
ser.Sync("_noNoiseOrNoise", ref _noNoiseOrNoise);
|
|
ser.Sync("_noPulse", ref _noPulse);
|
|
ser.Sync("_output", ref _output);
|
|
ser.Sync("_pulse", ref _pulse);
|
|
ser.Sync("_pulseWidth", ref _pulseWidth);
|
|
ser.Sync("_ringMod", ref _ringMod);
|
|
ser.Sync("_ringMsbMask", ref _ringMsbMask);
|
|
ser.Sync("_shiftRegister", ref _shiftRegister);
|
|
ser.Sync("_shiftRegisterReset", ref _shiftRegisterReset);
|
|
ser.Sync("_sync", ref _sync);
|
|
ser.Sync("_test", ref _test);
|
|
ser.Sync("_waveform", ref _waveform);
|
|
ser.Sync("_waveformIndex", ref _waveformIndex);
|
|
|
|
_wave = _waveTable[_waveform & 0x07];
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|