2012-11-27 05:11:40 +00:00
|
|
|
|
using System;
|
|
|
|
|
|
|
|
|
|
namespace BizHawk.Emulation.Computers.Commodore64.MOS
|
|
|
|
|
{
|
2012-12-05 21:07:51 +00:00
|
|
|
|
public abstract partial class Sid
|
2012-11-27 05:11:40 +00:00
|
|
|
|
{
|
|
|
|
|
// ------------------------------------
|
|
|
|
|
|
2013-08-19 03:42:40 +00:00
|
|
|
|
protected class Envelope
|
2012-11-27 05:11:40 +00:00
|
|
|
|
{
|
2013-08-19 03:42:40 +00:00
|
|
|
|
protected const int stateAttack = 0;
|
|
|
|
|
protected const int stateDecay = 1;
|
|
|
|
|
protected const int stateRelease = 2;
|
|
|
|
|
|
|
|
|
|
protected int attack;
|
|
|
|
|
protected int decay;
|
|
|
|
|
protected bool delay;
|
|
|
|
|
protected int envCounter;
|
|
|
|
|
protected int expCounter;
|
|
|
|
|
protected int expPeriod;
|
|
|
|
|
protected bool freeze;
|
|
|
|
|
protected int lfsr;
|
|
|
|
|
protected bool gate;
|
|
|
|
|
protected int rate;
|
|
|
|
|
protected int release;
|
|
|
|
|
protected int state;
|
|
|
|
|
protected int sustain;
|
|
|
|
|
|
|
|
|
|
protected static int[] adsrTable = new int[]
|
2012-11-30 06:41:02 +00:00
|
|
|
|
{
|
|
|
|
|
0x7F00, 0x0006, 0x003C, 0x0330,
|
|
|
|
|
0x20C0, 0x6755, 0x3800, 0x500E,
|
|
|
|
|
0x1212, 0x0222, 0x1848, 0x59B8,
|
|
|
|
|
0x3840, 0x77E2, 0x7625, 0x0A93
|
|
|
|
|
};
|
|
|
|
|
|
2013-08-19 03:42:40 +00:00
|
|
|
|
protected static int[] expCounterTable = new int[]
|
2012-11-30 06:41:02 +00:00
|
|
|
|
{
|
|
|
|
|
0xFF, 0x5D, 0x36, 0x1A, 0x0E, 0x06, 0x00
|
|
|
|
|
};
|
|
|
|
|
|
2013-08-19 03:42:40 +00:00
|
|
|
|
protected static int[] expPeriodTable = new int[]
|
2012-11-30 06:41:02 +00:00
|
|
|
|
{
|
|
|
|
|
0x01, 0x02, 0x04, 0x08, 0x10, 0x1E, 0x01
|
|
|
|
|
};
|
|
|
|
|
|
2013-08-19 03:42:40 +00:00
|
|
|
|
protected static int[] sustainTable = new int[]
|
2012-11-30 06:41:02 +00:00
|
|
|
|
{
|
|
|
|
|
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
|
|
|
|
|
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
public Envelope()
|
|
|
|
|
{
|
|
|
|
|
HardReset();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void ExecutePhase2()
|
|
|
|
|
{
|
2012-12-05 21:07:51 +00:00
|
|
|
|
|
2012-11-30 06:41:02 +00:00
|
|
|
|
{
|
2012-12-05 21:07:51 +00:00
|
|
|
|
if (!delay)
|
|
|
|
|
{
|
|
|
|
|
envCounter--;
|
|
|
|
|
delay = true;
|
|
|
|
|
UpdateExpCounter();
|
|
|
|
|
}
|
2012-12-03 04:19:45 +00:00
|
|
|
|
|
2012-12-05 21:07:51 +00:00
|
|
|
|
if (lfsr != rate)
|
|
|
|
|
{
|
2012-12-11 06:27:00 +00:00
|
|
|
|
int feedback = ((lfsr >> 14) ^ (lfsr >> 13)) & 0x1;
|
2012-12-05 21:07:51 +00:00
|
|
|
|
lfsr = ((lfsr << 1) & 0x7FFF) | feedback;
|
2012-12-03 04:19:45 +00:00
|
|
|
|
return;
|
2012-12-05 21:07:51 +00:00
|
|
|
|
}
|
|
|
|
|
lfsr = 0x7FFF;
|
2012-12-03 04:19:45 +00:00
|
|
|
|
|
2012-12-05 21:07:51 +00:00
|
|
|
|
if (state == stateAttack || ++expCounter == expPeriod)
|
2012-11-30 06:41:02 +00:00
|
|
|
|
{
|
2012-12-05 21:07:51 +00:00
|
|
|
|
expCounter = 0;
|
|
|
|
|
if (freeze)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
switch (state)
|
|
|
|
|
{
|
|
|
|
|
case stateAttack:
|
|
|
|
|
envCounter++;
|
|
|
|
|
if (envCounter == 0xFF)
|
|
|
|
|
{
|
|
|
|
|
state = stateDecay;
|
|
|
|
|
rate = adsrTable[decay];
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case stateDecay:
|
|
|
|
|
if (envCounter == sustainTable[sustain])
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (expPeriod != 1)
|
|
|
|
|
{
|
|
|
|
|
delay = false;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
envCounter--;
|
|
|
|
|
break;
|
|
|
|
|
case stateRelease:
|
|
|
|
|
if (expPeriod != 1)
|
|
|
|
|
{
|
|
|
|
|
delay = false;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
envCounter--;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
envCounter &= 0xFF;
|
|
|
|
|
UpdateExpCounter();
|
2012-11-30 06:41:02 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void HardReset()
|
|
|
|
|
{
|
|
|
|
|
attack = 0;
|
|
|
|
|
decay = 0;
|
|
|
|
|
delay = true;
|
|
|
|
|
envCounter = 0;
|
|
|
|
|
expCounter = 0;
|
|
|
|
|
expPeriod = expPeriodTable[0];
|
|
|
|
|
freeze = false;
|
|
|
|
|
gate = false;
|
|
|
|
|
lfsr = 0x7FFF;
|
|
|
|
|
rate = adsrTable[release];
|
|
|
|
|
release = 0;
|
|
|
|
|
state = stateRelease;
|
|
|
|
|
sustain = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void UpdateExpCounter()
|
|
|
|
|
{
|
2012-12-05 21:07:51 +00:00
|
|
|
|
|
2012-11-30 06:41:02 +00:00
|
|
|
|
{
|
2012-12-11 06:27:00 +00:00
|
|
|
|
for (int i = 0; i < 7; i++)
|
2012-12-05 21:07:51 +00:00
|
|
|
|
{
|
|
|
|
|
if (envCounter == expCounterTable[i])
|
|
|
|
|
expPeriod = expPeriodTable[i];
|
|
|
|
|
}
|
|
|
|
|
if (envCounter == 0)
|
|
|
|
|
freeze = true;
|
2012-11-30 06:41:02 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ------------------------------------
|
|
|
|
|
|
2012-12-11 06:27:00 +00:00
|
|
|
|
public int Attack
|
2012-11-30 06:41:02 +00:00
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return attack;
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
attack = (value & 0xF);
|
|
|
|
|
if (state == stateAttack)
|
|
|
|
|
rate = adsrTable[attack];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-11 06:27:00 +00:00
|
|
|
|
public int Decay
|
2012-11-30 06:41:02 +00:00
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return decay;
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
decay = (value & 0xF);
|
|
|
|
|
if (state == stateDecay)
|
|
|
|
|
rate = adsrTable[decay];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool Gate
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return gate;
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
bool nextGate = value;
|
|
|
|
|
if (nextGate && !gate)
|
|
|
|
|
{
|
|
|
|
|
state = stateAttack;
|
|
|
|
|
rate = adsrTable[attack];
|
|
|
|
|
delay = true;
|
|
|
|
|
freeze = false;
|
|
|
|
|
}
|
|
|
|
|
else if (!nextGate && gate)
|
|
|
|
|
{
|
|
|
|
|
state = stateRelease;
|
|
|
|
|
rate = adsrTable[release];
|
|
|
|
|
}
|
|
|
|
|
gate = nextGate;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-11 06:27:00 +00:00
|
|
|
|
public int Level
|
2012-11-30 06:41:02 +00:00
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return envCounter;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-11 06:27:00 +00:00
|
|
|
|
public int Release
|
2012-11-30 06:41:02 +00:00
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return release;
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
release = (value & 0xF);
|
|
|
|
|
if (state == stateRelease)
|
|
|
|
|
rate = adsrTable[release];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-11 06:27:00 +00:00
|
|
|
|
public int Sustain
|
2012-11-30 06:41:02 +00:00
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return sustain;
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
sustain = (value & 0xF);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ------------------------------------
|
2012-12-03 08:38:12 +00:00
|
|
|
|
|
|
|
|
|
public void SyncState(Serializer ser)
|
|
|
|
|
{
|
2013-08-19 03:42:40 +00:00
|
|
|
|
Sync.SyncObject(ser, this);
|
|
|
|
|
}
|
2012-12-03 08:38:12 +00:00
|
|
|
|
|
|
|
|
|
// ------------------------------------
|
2012-11-27 05:11:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-08-19 03:42:40 +00:00
|
|
|
|
protected class Voice
|
2012-11-27 05:11:40 +00:00
|
|
|
|
{
|
2013-08-19 03:42:40 +00:00
|
|
|
|
protected int accBits;
|
|
|
|
|
protected int accNext;
|
|
|
|
|
protected int accumulator;
|
|
|
|
|
protected bool controlTestPrev;
|
|
|
|
|
protected int controlWavePrev;
|
|
|
|
|
protected int delay;
|
|
|
|
|
protected int floatOutputTTL;
|
|
|
|
|
protected int frequency;
|
|
|
|
|
protected bool msbRising;
|
|
|
|
|
protected int noise;
|
|
|
|
|
protected int noNoise;
|
|
|
|
|
protected int noNoiseOrNoise;
|
|
|
|
|
protected int noPulse;
|
|
|
|
|
protected int output;
|
|
|
|
|
protected int pulse;
|
|
|
|
|
protected int pulseWidth;
|
|
|
|
|
protected bool ringMod;
|
|
|
|
|
protected int ringMsbMask;
|
|
|
|
|
protected int shiftRegister;
|
|
|
|
|
protected int shiftRegisterReset;
|
|
|
|
|
protected bool sync;
|
|
|
|
|
protected bool test;
|
|
|
|
|
protected int[] wave;
|
|
|
|
|
protected int waveform;
|
|
|
|
|
protected int waveformIndex;
|
|
|
|
|
protected int[][] waveTable;
|
2012-11-30 06:41:02 +00:00
|
|
|
|
|
2012-12-11 06:27:00 +00:00
|
|
|
|
public Voice(int[][] newWaveTable)
|
2012-11-30 06:41:02 +00:00
|
|
|
|
{
|
2012-11-30 21:12:23 +00:00
|
|
|
|
waveTable = newWaveTable;
|
2012-11-30 06:41:02 +00:00
|
|
|
|
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()
|
|
|
|
|
{
|
2012-12-05 21:07:51 +00:00
|
|
|
|
|
2012-11-30 06:41:02 +00:00
|
|
|
|
{
|
2012-12-05 21:07:51 +00:00
|
|
|
|
if (test)
|
2012-11-30 06:41:02 +00:00
|
|
|
|
{
|
2012-12-05 21:07:51 +00:00
|
|
|
|
if (shiftRegisterReset != 0 && --shiftRegisterReset == 0)
|
|
|
|
|
{
|
|
|
|
|
ResetShiftReg();
|
|
|
|
|
}
|
|
|
|
|
pulse = 0xFFF;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2013-08-17 23:15:31 +00:00
|
|
|
|
accNext = (accumulator + frequency) & 0xFFFFFF;
|
|
|
|
|
accBits = ~accumulator & accNext;
|
2012-12-05 21:07:51 +00:00
|
|
|
|
accumulator = accNext;
|
|
|
|
|
msbRising = ((accBits & 0x800000) != 0);
|
|
|
|
|
|
|
|
|
|
if ((accBits & 0x080000) != 0)
|
|
|
|
|
delay = 2;
|
|
|
|
|
else if (delay != 0 && --delay == 0)
|
|
|
|
|
ClockShiftReg();
|
2012-11-30 06:41:02 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ------------------------------------
|
|
|
|
|
|
|
|
|
|
private void ClockShiftReg()
|
|
|
|
|
{
|
2012-12-05 21:07:51 +00:00
|
|
|
|
|
|
|
|
|
{
|
2013-08-17 23:15:31 +00:00
|
|
|
|
shiftRegister = ((shiftRegister << 1) |
|
|
|
|
|
(((shiftRegister >> 22) ^ (shiftRegister >> 17)) & 0x1)
|
|
|
|
|
) & 0x7FFFFF;
|
2012-12-05 21:07:51 +00:00
|
|
|
|
SetNoise();
|
|
|
|
|
}
|
2012-11-30 06:41:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void ResetShiftReg()
|
|
|
|
|
{
|
2012-12-05 21:07:51 +00:00
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
shiftRegister = 0x7FFFFF;
|
|
|
|
|
shiftRegisterReset = 0;
|
|
|
|
|
SetNoise();
|
|
|
|
|
}
|
2012-11-30 06:41:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void SetNoise()
|
|
|
|
|
{
|
2012-12-05 21:07:51 +00:00
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
2012-11-30 06:41:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void WriteShiftReg()
|
|
|
|
|
{
|
2012-12-05 21:07:51 +00:00
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
2012-11-30 06:41:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ------------------------------------
|
|
|
|
|
|
2012-12-11 06:27:00 +00:00
|
|
|
|
public int Control
|
2012-11-30 06:41:02 +00:00
|
|
|
|
{
|
|
|
|
|
set
|
|
|
|
|
{
|
2013-08-17 23:15:31 +00:00
|
|
|
|
controlWavePrev = waveform;
|
|
|
|
|
controlTestPrev = test;
|
2012-11-30 06:41:02 +00:00
|
|
|
|
|
|
|
|
|
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;
|
2013-08-14 05:05:17 +00:00
|
|
|
|
noNoise = ((waveform & 0x8) != 0) ? 0x000 : 0xFFF;
|
2012-11-30 06:41:02 +00:00
|
|
|
|
noNoiseOrNoise = noNoise | noise;
|
2013-08-14 05:05:17 +00:00
|
|
|
|
noPulse = ((waveform & 0x4) != 0) ? 0x000 : 0xFFF;
|
2012-11-30 06:41:02 +00:00
|
|
|
|
|
2013-08-17 23:15:31 +00:00
|
|
|
|
if (!controlTestPrev && test)
|
2012-11-30 06:41:02 +00:00
|
|
|
|
{
|
|
|
|
|
accumulator = 0;
|
|
|
|
|
delay = 0;
|
|
|
|
|
shiftRegisterReset = 0x8000;
|
|
|
|
|
}
|
2013-08-17 23:15:31 +00:00
|
|
|
|
else if (controlTestPrev && !test)
|
2012-11-30 06:41:02 +00:00
|
|
|
|
{
|
2013-08-17 23:15:31 +00:00
|
|
|
|
shiftRegister = ((shiftRegister << 1) |
|
|
|
|
|
((~shiftRegister >> 17) & 0x1)
|
|
|
|
|
) & 0x7FFFFF;
|
2012-11-30 06:41:02 +00:00
|
|
|
|
SetNoise();
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-17 23:15:31 +00:00
|
|
|
|
if (waveform == 0 && controlWavePrev != 0)
|
2012-11-30 06:41:02 +00:00
|
|
|
|
floatOutputTTL = 0x28000;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-11 06:27:00 +00:00
|
|
|
|
public int Frequency
|
2012-11-30 06:41:02 +00:00
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return frequency;
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
frequency = value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-11 06:27:00 +00:00
|
|
|
|
public int FrequencyLo
|
2012-11-30 06:41:02 +00:00
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return (frequency & 0xFF);
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
frequency &= 0xFF00;
|
|
|
|
|
frequency |= value & 0x00FF;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-11 06:27:00 +00:00
|
|
|
|
public int FrequencyHi
|
2012-11-30 06:41:02 +00:00
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return (frequency >> 8);
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
frequency &= 0x00FF;
|
|
|
|
|
frequency |= (value & 0x00FF) << 8;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-11 06:27:00 +00:00
|
|
|
|
public int Oscillator
|
2012-11-30 06:41:02 +00:00
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return output;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-11 06:27:00 +00:00
|
|
|
|
public int Output(Voice ringModSource)
|
2012-11-30 06:41:02 +00:00
|
|
|
|
{
|
2012-12-05 21:07:51 +00:00
|
|
|
|
|
2012-11-30 06:41:02 +00:00
|
|
|
|
{
|
2012-12-05 21:07:51 +00:00
|
|
|
|
if (waveform != 0)
|
|
|
|
|
{
|
2013-08-17 23:15:31 +00:00
|
|
|
|
waveformIndex = (accumulator ^ (ringModSource.accumulator & ringMsbMask)) >> 12;
|
|
|
|
|
output = wave[waveformIndex] & (noPulse | pulse) & noNoiseOrNoise;
|
2012-12-05 21:07:51 +00:00
|
|
|
|
if (waveform > 8)
|
|
|
|
|
WriteShiftReg();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (floatOutputTTL != 0 && --floatOutputTTL == 0)
|
|
|
|
|
output = 0x000;
|
|
|
|
|
}
|
2013-08-14 05:05:17 +00:00
|
|
|
|
pulse = ((accumulator >> 12) >= pulseWidth) ? 0xFFF : 0x000;
|
2012-12-05 21:07:51 +00:00
|
|
|
|
return output;
|
2012-11-30 06:41:02 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-11 06:27:00 +00:00
|
|
|
|
public int PulseWidth
|
2012-11-30 06:41:02 +00:00
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return pulseWidth;
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
pulseWidth = value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-11 06:27:00 +00:00
|
|
|
|
public int PulseWidthLo
|
2012-11-30 06:41:02 +00:00
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return (pulseWidth & 0xFF);
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
2012-12-03 06:08:12 +00:00
|
|
|
|
pulseWidth &= 0x0F00;
|
2012-11-30 06:41:02 +00:00
|
|
|
|
pulseWidth |= value & 0x00FF;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-11 06:27:00 +00:00
|
|
|
|
public int PulseWidthHi
|
2012-11-30 06:41:02 +00:00
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return (pulseWidth >> 8);
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
pulseWidth &= 0x00FF;
|
2012-12-03 06:08:12 +00:00
|
|
|
|
pulseWidth |= (value & 0x000F) << 8;
|
2012-11-30 06:41:02 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool RingMod
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return ringMod;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool Sync
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return sync;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Synchronize(Voice target, Voice source)
|
|
|
|
|
{
|
|
|
|
|
if (msbRising && target.sync && !(sync && source.msbRising))
|
|
|
|
|
target.accumulator = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool Test
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return test;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-11 06:27:00 +00:00
|
|
|
|
public int Waveform
|
2012-11-30 06:41:02 +00:00
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return waveform;
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-12-03 08:38:12 +00:00
|
|
|
|
|
|
|
|
|
// ------------------------------------
|
|
|
|
|
|
|
|
|
|
public void SyncState(Serializer ser)
|
|
|
|
|
{
|
2013-08-19 03:42:40 +00:00
|
|
|
|
BizHawk.Emulation.Computers.Commodore64.Sync.SyncObject(ser, this);
|
2012-12-03 08:38:12 +00:00
|
|
|
|
|
|
|
|
|
if (ser.IsReader)
|
|
|
|
|
wave = waveTable[waveform];
|
|
|
|
|
}
|
2012-11-30 06:41:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ------------------------------------
|
|
|
|
|
|
2012-12-10 22:51:02 +00:00
|
|
|
|
public Sound.Utilities.SpeexResampler resampler;
|
|
|
|
|
|
2013-08-19 03:42:40 +00:00
|
|
|
|
protected static int[] syncNextTable = new int[] { 1, 2, 0 };
|
|
|
|
|
protected static int[] syncPrevTable = new int[] { 2, 0, 1 };
|
|
|
|
|
|
|
|
|
|
protected int cachedCycles;
|
|
|
|
|
protected bool disableVoice3;
|
|
|
|
|
protected int[] envelopeOutput;
|
|
|
|
|
protected Envelope[] envelopes;
|
|
|
|
|
protected bool[] filterEnable;
|
|
|
|
|
protected int filterFrequency;
|
|
|
|
|
protected int filterResonance;
|
|
|
|
|
protected bool filterSelectBandPass;
|
|
|
|
|
protected bool filterSelectLoPass;
|
|
|
|
|
protected bool filterSelectHiPass;
|
|
|
|
|
protected int mixer;
|
|
|
|
|
protected int potCounter;
|
|
|
|
|
protected int potX;
|
|
|
|
|
protected int potY;
|
|
|
|
|
protected short sample;
|
|
|
|
|
protected int[] voiceOutput;
|
|
|
|
|
protected Voice[] voices;
|
|
|
|
|
protected int volume;
|
|
|
|
|
protected int[][] waveformTable;
|
2012-11-30 06:41:02 +00:00
|
|
|
|
|
2012-12-05 21:07:51 +00:00
|
|
|
|
public Func<byte> ReadPotX;
|
|
|
|
|
public Func<byte> ReadPotY;
|
|
|
|
|
|
2012-12-11 06:27:00 +00:00
|
|
|
|
public Sid(int[][] newWaveformTable, int newSampleRate, Region newRegion)
|
2012-11-30 06:41:02 +00:00
|
|
|
|
{
|
2012-12-10 22:51:02 +00:00
|
|
|
|
uint cyclesPerSec = 0;
|
|
|
|
|
|
2012-12-03 04:19:45 +00:00
|
|
|
|
switch (newRegion)
|
|
|
|
|
{
|
2012-12-11 06:27:00 +00:00
|
|
|
|
case Region.NTSC: cyclesPerSec = 14318181 / 14; break;
|
|
|
|
|
case Region.PAL: cyclesPerSec = 17734472 / 18; break;
|
2012-12-03 04:19:45 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-11-30 06:41:02 +00:00
|
|
|
|
waveformTable = newWaveformTable;
|
|
|
|
|
|
|
|
|
|
envelopes = new Envelope[3];
|
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
|
|
|
envelopes[i] = new Envelope();
|
2012-12-11 06:27:00 +00:00
|
|
|
|
envelopeOutput = new int[3];
|
2012-11-30 06:41:02 +00:00
|
|
|
|
|
|
|
|
|
voices = new Voice[3];
|
|
|
|
|
for (int i = 0; i < 3; i++)
|
2012-11-30 21:12:23 +00:00
|
|
|
|
voices[i] = new Voice(newWaveformTable);
|
2012-12-11 06:27:00 +00:00
|
|
|
|
voiceOutput = new int[3];
|
2012-11-30 06:41:02 +00:00
|
|
|
|
|
|
|
|
|
filterEnable = new bool[3];
|
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
|
|
|
filterEnable[i] = false;
|
2012-12-08 16:05:00 +00:00
|
|
|
|
|
|
|
|
|
resampler = new Sound.Utilities.SpeexResampler(0, cyclesPerSec, 44100, cyclesPerSec, 44100, null, null);
|
2012-11-27 05:11:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-12-10 22:51:02 +00:00
|
|
|
|
public void Dispose()
|
|
|
|
|
{
|
|
|
|
|
if (resampler != null)
|
|
|
|
|
{
|
|
|
|
|
resampler.Dispose();
|
|
|
|
|
resampler = null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-27 05:11:40 +00:00
|
|
|
|
// ------------------------------------
|
|
|
|
|
|
|
|
|
|
public void HardReset()
|
|
|
|
|
{
|
2012-11-30 06:41:02 +00:00
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
|
|
|
{
|
|
|
|
|
envelopes[i].HardReset();
|
|
|
|
|
voices[i].HardReset();
|
|
|
|
|
}
|
|
|
|
|
potCounter = 0;
|
|
|
|
|
potX = 0;
|
|
|
|
|
potY = 0;
|
2012-11-27 05:11:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ------------------------------------
|
|
|
|
|
|
2013-08-17 23:15:31 +00:00
|
|
|
|
public void ExecutePhase2()
|
2012-11-27 05:11:40 +00:00
|
|
|
|
{
|
2013-08-17 23:15:31 +00:00
|
|
|
|
cachedCycles++;
|
|
|
|
|
|
|
|
|
|
// potentiometer values refresh every 512 cycles
|
|
|
|
|
if (potCounter == 0)
|
|
|
|
|
{
|
|
|
|
|
potCounter = 512;
|
|
|
|
|
potX = ReadPotX();
|
|
|
|
|
potY = ReadPotY();
|
|
|
|
|
Flush(); //this is here unrelated to the pots, just to keep the buffer somewhat loaded
|
|
|
|
|
}
|
|
|
|
|
potCounter--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Flush()
|
|
|
|
|
{
|
|
|
|
|
while (cachedCycles > 0)
|
|
|
|
|
{
|
|
|
|
|
// process voices and envelopes
|
|
|
|
|
voices[0].ExecutePhase2();
|
|
|
|
|
voices[1].ExecutePhase2();
|
|
|
|
|
voices[2].ExecutePhase2();
|
|
|
|
|
envelopes[0].ExecutePhase2();
|
|
|
|
|
envelopes[1].ExecutePhase2();
|
|
|
|
|
envelopes[2].ExecutePhase2();
|
|
|
|
|
|
|
|
|
|
// process sync
|
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
|
|
|
voices[i].Synchronize(voices[syncNextTable[i]], voices[syncPrevTable[i]]);
|
|
|
|
|
|
|
|
|
|
// get output
|
|
|
|
|
voiceOutput[0] = voices[0].Output(voices[2]);
|
|
|
|
|
voiceOutput[1] = voices[1].Output(voices[0]);
|
|
|
|
|
voiceOutput[2] = voices[2].Output(voices[1]);
|
|
|
|
|
envelopeOutput[0] = envelopes[0].Level;
|
|
|
|
|
envelopeOutput[1] = envelopes[1].Level;
|
|
|
|
|
envelopeOutput[2] = envelopes[2].Level;
|
|
|
|
|
|
|
|
|
|
mixer = ((voiceOutput[0] * envelopeOutput[0]) >> 7);
|
|
|
|
|
mixer += ((voiceOutput[1] * envelopeOutput[1]) >> 7);
|
|
|
|
|
mixer += ((voiceOutput[2] * envelopeOutput[2]) >> 7);
|
|
|
|
|
mixer = (mixer * volume) >> 4;
|
|
|
|
|
|
|
|
|
|
sample = (short)mixer;
|
|
|
|
|
resampler.EnqueueSample(sample, sample);
|
|
|
|
|
cachedCycles--;
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-11-27 05:11:40 +00:00
|
|
|
|
|
|
|
|
|
// ------------------------------------
|
|
|
|
|
|
|
|
|
|
public byte Peek(int addr)
|
|
|
|
|
{
|
2013-08-14 05:05:17 +00:00
|
|
|
|
return ReadRegister((addr & 0x1F));
|
2012-11-27 05:11:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Poke(int addr, byte val)
|
|
|
|
|
{
|
2013-08-14 05:05:17 +00:00
|
|
|
|
WriteRegister((addr & 0x1F), val);
|
2012-11-27 05:11:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-08-14 05:05:17 +00:00
|
|
|
|
public byte Read(int addr)
|
2012-11-27 05:11:40 +00:00
|
|
|
|
{
|
2012-12-02 23:57:10 +00:00
|
|
|
|
addr &= 0x1F;
|
2012-11-30 21:12:23 +00:00
|
|
|
|
byte result = 0x00;
|
2012-11-30 06:41:02 +00:00
|
|
|
|
switch (addr)
|
|
|
|
|
{
|
|
|
|
|
case 0x19:
|
|
|
|
|
case 0x1A:
|
|
|
|
|
case 0x1B:
|
|
|
|
|
case 0x1C:
|
2013-08-17 23:15:31 +00:00
|
|
|
|
Flush();
|
2012-11-30 06:41:02 +00:00
|
|
|
|
result = ReadRegister(addr);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-14 05:05:17 +00:00
|
|
|
|
private byte ReadRegister(int addr)
|
2012-11-30 06:41:02 +00:00
|
|
|
|
{
|
2012-11-30 21:12:23 +00:00
|
|
|
|
byte result = 0x00;
|
2012-11-30 06:41:02 +00:00
|
|
|
|
|
|
|
|
|
switch (addr)
|
|
|
|
|
{
|
|
|
|
|
case 0x00: result = (byte)voices[0].FrequencyLo; break;
|
|
|
|
|
case 0x01: result = (byte)voices[0].FrequencyHi; break;
|
|
|
|
|
case 0x02: result = (byte)voices[0].PulseWidthLo; break;
|
|
|
|
|
case 0x03: result = (byte)voices[0].PulseWidthHi; break;
|
|
|
|
|
case 0x04:
|
|
|
|
|
result = (byte)(
|
|
|
|
|
(envelopes[0].Gate ? 0x01 : 0x00) |
|
|
|
|
|
(voices[0].Sync ? 0x02 : 0x00) |
|
|
|
|
|
(voices[0].RingMod ? 0x04 : 0x00) |
|
|
|
|
|
(voices[0].Test ? 0x08 : 0x00) |
|
|
|
|
|
(byte)(voices[0].Waveform << 4)
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
case 0x05:
|
|
|
|
|
result = (byte)(
|
|
|
|
|
(envelopes[0].Attack << 4) |
|
|
|
|
|
(envelopes[0].Decay)
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
case 0x06:
|
|
|
|
|
result = (byte)(
|
|
|
|
|
(envelopes[0].Sustain << 4) |
|
|
|
|
|
(envelopes[0].Release)
|
|
|
|
|
);
|
|
|
|
|
break;
|
2012-12-02 23:57:10 +00:00
|
|
|
|
case 0x07: result = (byte)voices[1].FrequencyLo; break;
|
|
|
|
|
case 0x08: result = (byte)voices[1].FrequencyHi; break;
|
|
|
|
|
case 0x09: result = (byte)voices[1].PulseWidthLo; break;
|
|
|
|
|
case 0x0A: result = (byte)voices[1].PulseWidthHi; break;
|
2012-11-30 06:41:02 +00:00
|
|
|
|
case 0x0B:
|
|
|
|
|
result = (byte)(
|
2012-12-02 23:57:10 +00:00
|
|
|
|
(envelopes[1].Gate ? 0x01 : 0x00) |
|
|
|
|
|
(voices[1].Sync ? 0x02 : 0x00) |
|
|
|
|
|
(voices[1].RingMod ? 0x04 : 0x00) |
|
|
|
|
|
(voices[1].Test ? 0x08 : 0x00) |
|
|
|
|
|
(byte)(voices[1].Waveform << 4)
|
2012-11-30 06:41:02 +00:00
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
case 0x0C:
|
|
|
|
|
result = (byte)(
|
2012-12-02 23:57:10 +00:00
|
|
|
|
(envelopes[1].Attack << 4) |
|
|
|
|
|
(envelopes[1].Decay)
|
2012-11-30 06:41:02 +00:00
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
case 0x0D:
|
|
|
|
|
result = (byte)(
|
2012-12-02 23:57:10 +00:00
|
|
|
|
(envelopes[1].Sustain << 4) |
|
|
|
|
|
(envelopes[1].Release)
|
2012-11-30 06:41:02 +00:00
|
|
|
|
);
|
|
|
|
|
break;
|
2012-12-02 23:57:10 +00:00
|
|
|
|
case 0x0E: result = (byte)voices[2].FrequencyLo; break;
|
|
|
|
|
case 0x0F: result = (byte)voices[2].FrequencyHi; break;
|
|
|
|
|
case 0x10: result = (byte)voices[2].PulseWidthLo; break;
|
|
|
|
|
case 0x11: result = (byte)voices[2].PulseWidthHi; break;
|
2012-11-30 06:41:02 +00:00
|
|
|
|
case 0x12:
|
|
|
|
|
result = (byte)(
|
2012-12-02 23:57:10 +00:00
|
|
|
|
(envelopes[2].Gate ? 0x01 : 0x00) |
|
|
|
|
|
(voices[2].Sync ? 0x02 : 0x00) |
|
|
|
|
|
(voices[2].RingMod ? 0x04 : 0x00) |
|
|
|
|
|
(voices[2].Test ? 0x08 : 0x00) |
|
|
|
|
|
(byte)(voices[2].Waveform << 4)
|
2012-11-30 06:41:02 +00:00
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
case 0x13:
|
|
|
|
|
result = (byte)(
|
2012-12-02 23:57:10 +00:00
|
|
|
|
(envelopes[2].Attack << 4) |
|
|
|
|
|
(envelopes[2].Decay)
|
2012-11-30 06:41:02 +00:00
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
case 0x14:
|
|
|
|
|
result = (byte)(
|
2012-12-02 23:57:10 +00:00
|
|
|
|
(envelopes[2].Sustain << 4) |
|
|
|
|
|
(envelopes[2].Release)
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
case 0x15: result = (byte)(filterFrequency & 0x7); break;
|
|
|
|
|
case 0x16: result = (byte)((filterFrequency >> 3) & 0xFF); break;
|
|
|
|
|
case 0x17:
|
|
|
|
|
result = (byte)(
|
|
|
|
|
(filterEnable[0] ? 0x01 : 0x00) |
|
|
|
|
|
(filterEnable[1] ? 0x02 : 0x00) |
|
|
|
|
|
(filterEnable[2] ? 0x04 : 0x00) |
|
|
|
|
|
(byte)(filterResonance << 4)
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
case 0x18:
|
|
|
|
|
result = (byte)(
|
|
|
|
|
(byte)volume |
|
|
|
|
|
(filterSelectLoPass ? 0x10 : 0x00) |
|
|
|
|
|
(filterSelectBandPass ? 0x20 : 0x00) |
|
|
|
|
|
(filterSelectHiPass ? 0x40 : 0x00) |
|
|
|
|
|
(disableVoice3 ? 0x80 : 0x00)
|
2012-11-30 06:41:02 +00:00
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
case 0x19: result = (byte)potX; break;
|
|
|
|
|
case 0x1A: result = (byte)potY; break;
|
2012-12-03 04:19:45 +00:00
|
|
|
|
case 0x1B: result = (byte)(voiceOutput[2] >> 4); break;
|
|
|
|
|
case 0x1C: result = (byte)(envelopeOutput[2]); break;
|
2012-11-30 06:41:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
2012-11-27 05:11:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-08-14 05:05:17 +00:00
|
|
|
|
public void Write(int addr, byte val)
|
2012-11-27 05:11:40 +00:00
|
|
|
|
{
|
2012-12-02 23:57:10 +00:00
|
|
|
|
addr &= 0x1F;
|
|
|
|
|
switch (addr)
|
|
|
|
|
{
|
|
|
|
|
case 0x19:
|
|
|
|
|
case 0x1A:
|
|
|
|
|
case 0x1B:
|
|
|
|
|
case 0x1C:
|
|
|
|
|
case 0x1D:
|
|
|
|
|
case 0x1E:
|
|
|
|
|
case 0x1F:
|
|
|
|
|
// can't write to these
|
|
|
|
|
break;
|
|
|
|
|
default:
|
2013-08-17 23:15:31 +00:00
|
|
|
|
Flush();
|
2012-12-02 23:57:10 +00:00
|
|
|
|
WriteRegister(addr, val);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2012-11-27 05:11:40 +00:00
|
|
|
|
}
|
2012-11-30 06:41:02 +00:00
|
|
|
|
|
2013-08-14 05:05:17 +00:00
|
|
|
|
private void WriteRegister(int addr, byte val)
|
2012-11-30 06:41:02 +00:00
|
|
|
|
{
|
2012-12-03 04:19:45 +00:00
|
|
|
|
switch (addr)
|
|
|
|
|
{
|
|
|
|
|
case 0x00: voices[0].FrequencyLo = val; break;
|
|
|
|
|
case 0x01: voices[0].FrequencyHi = val; break;
|
|
|
|
|
case 0x02: voices[0].PulseWidthLo = val; break;
|
|
|
|
|
case 0x03: voices[0].PulseWidthHi = val; break;
|
|
|
|
|
case 0x04: voices[0].Control = val; envelopes[0].Gate = ((val & 0x01) != 0); break;
|
2012-12-11 06:27:00 +00:00
|
|
|
|
case 0x05: envelopes[0].Attack = (val >> 4); envelopes[0].Decay = (val & 0xF); break;
|
|
|
|
|
case 0x06: envelopes[0].Sustain = (val >> 4); envelopes[0].Release = (val & 0xF); break;
|
2012-12-03 04:19:45 +00:00
|
|
|
|
case 0x07: voices[1].FrequencyLo = val; break;
|
|
|
|
|
case 0x08: voices[1].FrequencyHi = val; break;
|
|
|
|
|
case 0x09: voices[1].PulseWidthLo = val; break;
|
|
|
|
|
case 0x0A: voices[1].PulseWidthHi = val; break;
|
|
|
|
|
case 0x0B: voices[1].Control = val; envelopes[1].Gate = ((val & 0x01) != 0); break;
|
2012-12-11 06:27:00 +00:00
|
|
|
|
case 0x0C: envelopes[1].Attack = (val >> 4); envelopes[1].Decay = (val & 0xF); break;
|
|
|
|
|
case 0x0D: envelopes[1].Sustain = (val >> 4); envelopes[1].Release = (val & 0xF); break;
|
2012-12-03 04:19:45 +00:00
|
|
|
|
case 0x0E: voices[2].FrequencyLo = val; break;
|
|
|
|
|
case 0x0F: voices[2].FrequencyHi = val; break;
|
|
|
|
|
case 0x10: voices[2].PulseWidthLo = val; break;
|
|
|
|
|
case 0x11: voices[2].PulseWidthHi = val; break;
|
|
|
|
|
case 0x12: voices[2].Control = val; envelopes[2].Gate = ((val & 0x01) != 0); break;
|
2012-12-11 06:27:00 +00:00
|
|
|
|
case 0x13: envelopes[2].Attack = (val >> 4); envelopes[2].Decay = (val & 0xF); break;
|
|
|
|
|
case 0x14: envelopes[2].Sustain = (val >> 4); envelopes[2].Release = (val & 0xF); break;
|
|
|
|
|
case 0x15: filterFrequency &= 0x3FF; filterFrequency |= (val & 0x7); break;
|
|
|
|
|
case 0x16: filterFrequency &= 0x7; filterFrequency |= val << 3; break;
|
2012-12-03 04:19:45 +00:00
|
|
|
|
case 0x17:
|
|
|
|
|
filterEnable[0] = ((val & 0x1) != 0);
|
|
|
|
|
filterEnable[1] = ((val & 0x2) != 0);
|
|
|
|
|
filterEnable[2] = ((val & 0x4) != 0);
|
2012-12-11 06:27:00 +00:00
|
|
|
|
filterResonance = val >> 4;
|
2012-12-03 04:19:45 +00:00
|
|
|
|
break;
|
|
|
|
|
case 0x18:
|
2012-12-11 06:27:00 +00:00
|
|
|
|
volume = (val & 0xF);
|
2012-12-03 04:19:45 +00:00
|
|
|
|
filterSelectLoPass = ((val & 0x10) != 0);
|
|
|
|
|
filterSelectBandPass = ((val & 0x20) != 0);
|
|
|
|
|
filterSelectHiPass = ((val & 0x40) != 0);
|
|
|
|
|
disableVoice3 = ((val & 0x40) != 0);
|
|
|
|
|
break;
|
|
|
|
|
case 0x19:
|
|
|
|
|
potX = val;
|
|
|
|
|
break;
|
|
|
|
|
case 0x1A:
|
|
|
|
|
potY = val;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2012-11-30 06:41:02 +00:00
|
|
|
|
}
|
2012-12-03 08:38:12 +00:00
|
|
|
|
|
|
|
|
|
// ----------------------------------
|
|
|
|
|
|
2013-08-19 03:42:40 +00:00
|
|
|
|
public void SyncState(Serializer ser)
|
2012-12-03 08:38:12 +00:00
|
|
|
|
{
|
2013-08-19 03:42:40 +00:00
|
|
|
|
Sync.SyncObject(ser, this);
|
|
|
|
|
ser.BeginSection("env0");
|
2012-12-03 08:38:12 +00:00
|
|
|
|
envelopes[0].SyncState(ser);
|
|
|
|
|
ser.EndSection();
|
|
|
|
|
ser.BeginSection("wav0");
|
|
|
|
|
voices[0].SyncState(ser);
|
|
|
|
|
ser.EndSection();
|
|
|
|
|
ser.BeginSection("env1");
|
|
|
|
|
envelopes[1].SyncState(ser);
|
|
|
|
|
ser.EndSection();
|
|
|
|
|
ser.BeginSection("wav1");
|
|
|
|
|
voices[1].SyncState(ser);
|
|
|
|
|
ser.EndSection();
|
|
|
|
|
ser.BeginSection("env2");
|
|
|
|
|
envelopes[2].SyncState(ser);
|
|
|
|
|
ser.EndSection();
|
|
|
|
|
ser.BeginSection("wav2");
|
|
|
|
|
voices[2].SyncState(ser);
|
|
|
|
|
ser.EndSection();
|
|
|
|
|
}
|
2012-11-27 05:11:40 +00:00
|
|
|
|
}
|
|
|
|
|
}
|