A2600: Refactor audio and savestate it.

This commit is contained in:
alyosha-tas 2019-06-01 19:44:01 -04:00
parent c48d5be02a
commit e60896c1b7
3 changed files with 266 additions and 85 deletions

View File

@ -126,10 +126,9 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600
private HMoveData _hmove;
private BallData _ball;
private readonly Audio[] AUD = { new Audio(), new Audio() };
private readonly Audio AUD =new Audio();
// current audio register state used to sample correct positions in the scanline (clrclk 0 and 114)
////public byte[] current_audio_register = new byte[6];
public readonly short[] LocalAudioCycles = new short[2000];
private static int ReverseBits(int value, int bits)
@ -809,8 +808,8 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600
{
if (AudioClocks < 2000)
{
LocalAudioCycles[AudioClocks] += (short)(AUD[0].Cycle() / 2);
LocalAudioCycles[AudioClocks] += (short)(AUD[1].Cycle() / 2);
LocalAudioCycles[AudioClocks] += (short)(AUD.Cycle_L() / 2);
LocalAudioCycles[AudioClocks] += (short)(AUD.Cycle_R() / 2);
AudioClocks++;
}
}
@ -1258,27 +1257,27 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600
}
else if (maskedAddr == 0x15) // AUDC0
{
AUD[0].AUDC = (byte)(value & 15);
AUD.AUDC_L = (byte)(value & 15);
}
else if (maskedAddr == 0x16) // AUDC1
{
AUD[1].AUDC = (byte)(value & 15);
AUD.AUDC_R = (byte)(value & 15);
}
else if (maskedAddr == 0x17) // AUDF0
{
AUD[0].AUDF = (byte)((value & 31) + 1);
AUD.AUDF_L = (byte)((value & 31) + 1);
}
else if (maskedAddr == 0x18) // AUDF1
{
AUD[1].AUDF = (byte)((value & 31) + 1);
AUD.AUDF_R = (byte)((value & 31) + 1);
}
else if (maskedAddr == 0x19) // AUDV0
{
AUD[0].AUDV = (byte)(value & 15);
AUD.AUDV_L = (byte)(value & 15);
}
else if (maskedAddr == 0x1A) // AUDV1
{
AUD[1].AUDV = (byte)(value & 15);
AUD.AUDV_R = (byte)(value & 15);
}
else if (maskedAddr == 0x1B) // GRP0
{

View File

@ -8,88 +8,98 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600
public class Audio
{
// noise/division control
public byte AUDC = 0;
public byte AUDC_L = 0;
public byte AUDC_R = 0;
// frequency divider
public byte AUDF = 1;
public byte AUDF_L = 1;
public byte AUDF_R = 1;
// volume
public byte AUDV = 0;
public byte AUDV_L = 0;
public byte AUDV_R = 0;
// 2 state counter
private bool sr1 = true;
private bool sr1_L = true;
private bool sr1_R = true;
// 4 bit shift register
private int sr4 = 0x0f;
private int sr4_L = 0x0f;
private int sr4_R = 0x0f;
// 5 bit shift register
private int sr5 = 0x1f;
private int sr5_L = 0x1f;
private int sr5_R = 0x1f;
// 9 bit shift register
private int sr9 = 0x1ff;
private int sr9_L = 0x1ff;
private int sr9_R = 0x1ff;
// 3 state counter
private int sr3 = 2;
private int sr3_L = 2;
private int sr3_R = 2;
// counter based off AUDF
private byte freqcnt;
private byte freqcnt_L;
private byte freqcnt_R;
// latched audio value
private bool on = true;
private bool on_L = true;
private bool on_R = true;
private bool Run3()
private bool Run3_L()
{
sr3++;
if (sr3 == 3)
sr3_L++;
if (sr3_L == 3)
{
sr3 = 0;
sr3_L = 0;
return true;
}
return false;
}
private bool Run4()
private bool Run4_L()
{
bool ret = (sr4 & 1) != 0;
bool c = ((sr4 & 1) != 0) ^ ((sr4 & 2) != 0);
sr4 = (sr4 >> 1) | (c ? 8 : 0);
bool ret = (sr4_L & 1) != 0;
bool c = ((sr4_L & 1) != 0) ^ ((sr4_L & 2) != 0);
sr4_L = (sr4_L >> 1) | (c ? 8 : 0);
return ret;
}
private bool Run5()
private bool Run5_L()
{
bool ret = (sr5 & 1) != 0;
bool c = ((sr5 & 1) != 0) ^ ((sr5 & 4) != 0);
sr5 = (sr5 >> 1) | (c ? 16 : 0);
bool ret = (sr5_L & 1) != 0;
bool c = ((sr5_L & 1) != 0) ^ ((sr5_L & 4) != 0);
sr5_L = (sr5_L >> 1) | (c ? 16 : 0);
return ret;
}
private bool One4()
private bool One4_L()
{
bool ret = (sr4 & 1) != 0;
sr4 = (sr4 >> 1) | 8;
bool ret = (sr4_L & 1) != 0;
sr4_L = (sr4_L >> 1) | 8;
return ret;
}
private bool One5()
private bool One5_L()
{
bool ret = (sr5 & 1) != 0;
sr5 = (sr5 >> 1) | 16;
bool ret = (sr5_L & 1) != 0;
sr5_L = (sr5_L >> 1) | 16;
return ret;
}
private bool Run1()
private bool Run1_L()
{
sr1 = !sr1;
return !sr1;
sr1_L = !sr1_L;
return !sr1_L;
}
private bool Run9()
private bool Run9_L()
{
bool ret = (sr9 & 1) != 0;
bool c = ((sr9 & 1) != 0) ^ ((sr9 & 16) != 0);
sr9 = (sr9 >> 1) | (c ? 256 : 0);
bool ret = (sr9_L & 1) != 0;
bool c = ((sr9_L & 1) != 0) ^ ((sr9_L & 16) != 0);
sr9_L = (sr9_L >> 1) | (c ? 256 : 0);
return ret;
}
@ -97,92 +107,92 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600
/// call me approx 31k times a second
/// </summary>
/// <returns>16 bit audio sample</returns>
public short Cycle()
public short Cycle_L()
{
if (++freqcnt >= AUDF)
if (++freqcnt_L >= AUDF_L)
{
freqcnt = 0;
switch (AUDC)
freqcnt_L = 0;
switch (AUDC_L)
{
case 0x00:
case 0x0b:
// Both have a 1 s
One5();
on = One4();
One5_L();
on_L = One4_L();
break;
case 0x01:
// Both run, but the 5 bit is ignored
on = Run4();
on_L = Run4_L();
//Run5();
break;
case 0x02:
if ((sr5 & 0x0f) == 0 || (sr5 & 0x0f) == 0x0f)
if ((sr5_L & 0x0f) == 0 || (sr5_L & 0x0f) == 0x0f)
{
on = Run4();
on_L = Run4_L();
}
Run5();
Run5_L();
break;
case 0x03:
if (Run5())
if (Run5_L())
{
on = Run4();
on_L = Run4_L();
}
break;
case 0x04:
Run5();
One4();
on = Run1();
Run5_L();
One4_L();
on_L = Run1_L();
break;
case 0x05:
One5();
Run4();
on = Run1();
One5_L();
Run4_L();
on_L = Run1_L();
break;
case 0x06:
case 0x0a:
Run5();
if ((sr5 & 0x0f) == 0)
Run5_L();
if ((sr5_L & 0x0f) == 0)
{
on = false;
on_L = false;
}
else if ((sr5 & 0x0f) == 0x0f)
else if ((sr5_L & 0x0f) == 0x0f)
{
on = true;
on_L = true;
}
break;
case 0x07:
case 0x09:
on = Run5();
on_L = Run5_L();
break;
case 0x08:
on = Run9();
on_L = Run9_L();
break;
case 0x0c:
case 0x0d:
if (Run3())
if (Run3_L())
{
on = Run1();
on_L = Run1_L();
}
break;
case 0x0e:
if (Run3())
if (Run3_L())
{
goto case 0x06;
}
break;
case 0x0f:
if (Run3())
if (Run3_L())
{
goto case 0x07;
}
@ -191,21 +201,189 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600
}
}
return (short)(on ? AUDV * 1092 : 0);
return (short)(on_L ? AUDV_L * 1092 : 0);
}
private bool Run3_R()
{
sr3_R++;
if (sr3_R == 3)
{
sr3_R = 0;
return true;
}
return false;
}
private bool Run4_R()
{
bool ret = (sr4_R & 1) != 0;
bool c = ((sr4_R & 1) != 0) ^ ((sr4_R & 2) != 0);
sr4_R = (sr4_R >> 1) | (c ? 8 : 0);
return ret;
}
private bool Run5_R()
{
bool ret = (sr5_R & 1) != 0;
bool c = ((sr5_R & 1) != 0) ^ ((sr5_R & 4) != 0);
sr5_R = (sr5_R >> 1) | (c ? 16 : 0);
return ret;
}
private bool One4_R()
{
bool ret = (sr4_R & 1) != 0;
sr4_R = (sr4_R >> 1) | 8;
return ret;
}
private bool One5_R()
{
bool ret = (sr5_R & 1) != 0;
sr5_R = (sr5_R >> 1) | 16;
return ret;
}
private bool Run1_R()
{
sr1_R = !sr1_R;
return !sr1_R;
}
private bool Run9_R()
{
bool ret = (sr9_R & 1) != 0;
bool c = ((sr9_R & 1) != 0) ^ ((sr9_R & 16) != 0);
sr9_R = (sr9_R >> 1) | (c ? 256 : 0);
return ret;
}
/// <summary>
/// call me approx 31k times a second
/// </summary>
/// <returns>16 bit audio sample</returns>
public short Cycle_R()
{
if (++freqcnt_R >= AUDF_R)
{
freqcnt_R = 0;
switch (AUDC_R)
{
case 0x00:
case 0x0b:
// Both have a 1 s
One5_R();
on_R = One4_R();
break;
case 0x01:
// Both run, but the 5 bit is ignored
on_R = Run4_R();
//Run5();
break;
case 0x02:
if ((sr5_R & 0x0f) == 0 || (sr5_R & 0x0f) == 0x0f)
{
on_R = Run4_R();
}
Run5_R();
break;
case 0x03:
if (Run5_R())
{
on_R = Run4_R();
}
break;
case 0x04:
Run5_R();
One4_R();
on_R = Run1_R();
break;
case 0x05:
One5_R();
Run4_R();
on_R = Run1_R();
break;
case 0x06:
case 0x0a:
Run5_R();
if ((sr5_R & 0x0f) == 0)
{
on_R = false;
}
else if ((sr5_R & 0x0f) == 0x0f)
{
on_R = true;
}
break;
case 0x07:
case 0x09:
on_R = Run5_R();
break;
case 0x08:
on_R = Run9_R();
break;
case 0x0c:
case 0x0d:
if (Run3_R())
{
on_R = Run1_R();
}
break;
case 0x0e:
if (Run3_R())
{
goto case 0x06;
}
break;
case 0x0f:
if (Run3_R())
{
goto case 0x07;
}
break;
}
}
return (short)(on_R ? AUDV_R * 1092 : 0);
}
public void SyncState(Serializer ser)
{
ser.Sync(nameof(AUDC), ref AUDC);
ser.Sync(nameof(AUDF), ref AUDF);
ser.Sync(nameof(AUDV), ref AUDV);
ser.Sync(nameof(sr1), ref sr1);
ser.Sync(nameof(sr3), ref sr3);
ser.Sync(nameof(sr4), ref sr4);
ser.Sync(nameof(sr5), ref sr5);
ser.Sync(nameof(sr9), ref sr9);
ser.Sync(nameof(freqcnt), ref freqcnt);
ser.Sync(nameof(on), ref on);
ser.Sync(nameof(AUDC_L), ref AUDC_L);
ser.Sync(nameof(AUDF_L), ref AUDF_L);
ser.Sync(nameof(AUDV_L), ref AUDV_L);
ser.Sync(nameof(sr1_L), ref sr1_L);
ser.Sync(nameof(sr3_L), ref sr3_L);
ser.Sync(nameof(sr4_L), ref sr4_L);
ser.Sync(nameof(sr5_L), ref sr5_L);
ser.Sync(nameof(sr9_L), ref sr9_L);
ser.Sync(nameof(freqcnt_L), ref freqcnt_L);
ser.Sync(nameof(on_L), ref on_L);
ser.Sync(nameof(AUDC_R), ref AUDC_R);
ser.Sync(nameof(AUDF_R), ref AUDF_R);
ser.Sync(nameof(AUDV_R), ref AUDV_R);
ser.Sync(nameof(sr1_R), ref sr1_R);
ser.Sync(nameof(sr3_R), ref sr3_R);
ser.Sync(nameof(sr4_R), ref sr4_R);
ser.Sync(nameof(sr5_R), ref sr5_R);
ser.Sync(nameof(sr9_R), ref sr9_R);
ser.Sync(nameof(freqcnt_R), ref freqcnt_R);
ser.Sync(nameof(on_R), ref on_R);
}
}
}

View File

@ -72,6 +72,10 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600
ser.Sync(nameof(AudioClocks), ref AudioClocks);
ser.Sync(nameof(New_Frame), ref New_Frame);
ser.BeginSection("Audio");
AUD.SyncState(ser);
ser.EndSection();
ser.BeginSection("Player0");
_player0.SyncState(ser);
ser.EndSection();