BizHawk/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Sid.Voice.cs

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];
}
}
}
}