using System;
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Atari.Atari2600
{
public partial class TIA
{
public class Audio
{
// noise/division control
public byte AUDC_L = 0;
public byte AUDC_R = 0;
// frequency divider
public byte AUDF_L = 1;
public byte AUDF_R = 1;
// volume
public byte AUDV_L = 0;
public byte AUDV_R = 0;
// 2 state counter
private bool sr1_L = true;
private bool sr1_R = true;
// 4 bit shift register
private int sr4_L = 0x0f;
private int sr4_R = 0x0f;
// 5 bit shift register
private int sr5_L = 0x1f;
private int sr5_R = 0x1f;
// 9 bit shift register
private int sr9_L = 0x1ff;
private int sr9_R = 0x1ff;
// 3 state counter
private int sr3_L = 2;
private int sr3_R = 2;
// counter based off AUDF
private byte freqcnt_L;
private byte freqcnt_R;
// latched audio value
private bool on_L = true;
private bool on_R = true;
private bool Run3_L()
{
sr3_L++;
if (sr3_L == 3)
{
sr3_L = 0;
return true;
}
return false;
}
private bool Run4_L()
{
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_L()
{
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_L()
{
bool ret = (sr4_L & 1) != 0;
sr4_L = (sr4_L >> 1) | 8;
return ret;
}
private bool One5_L()
{
bool ret = (sr5_L & 1) != 0;
sr5_L = (sr5_L >> 1) | 16;
return ret;
}
private bool Run1_L()
{
sr1_L = !sr1_L;
return !sr1_L;
}
private bool Run9_L()
{
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;
}
///
/// call me approx 31k times a second
///
/// 16 bit audio sample
public short Cycle_L()
{
if (++freqcnt_L >= AUDF_L)
{
freqcnt_L = 0;
switch (AUDC_L)
{
case 0x00:
case 0x0b:
// Both have a 1 s
One5_L();
on_L = One4_L();
break;
case 0x01:
// Both run, but the 5 bit is ignored
on_L = Run4_L();
//Run5();
break;
case 0x02:
if ((sr5_L & 0x0f) == 0 || (sr5_L & 0x0f) == 0x0f)
{
on_L = Run4_L();
}
Run5_L();
break;
case 0x03:
if (Run5_L())
{
on_L = Run4_L();
}
break;
case 0x04:
Run5_L();
One4_L();
on_L = Run1_L();
break;
case 0x05:
One5_L();
Run4_L();
on_L = Run1_L();
break;
case 0x06:
case 0x0a:
Run5_L();
if ((sr5_L & 0x0f) == 0)
{
on_L = false;
}
else if ((sr5_L & 0x0f) == 0x0f)
{
on_L = true;
}
break;
case 0x07:
case 0x09:
on_L = Run5_L();
break;
case 0x08:
on_L = Run9_L();
break;
case 0x0c:
case 0x0d:
if (Run3_L())
{
on_L = Run1_L();
}
break;
case 0x0e:
if (Run3_L())
{
goto case 0x06;
}
break;
case 0x0f:
// poly5 output to div 6
if (Run5_L())
{
if (Run3_L())
{
on_L = Run1_L();
}
}
break;
}
}
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;
}
///
/// call me approx 31k times a second
///
/// 16 bit audio sample
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:
// poly5 output to div 6
if (Run5_R())
{
if (Run3_R())
{
on_R = Run1_R();
}
}
break;
}
}
return (short)(on_R ? AUDV_R * 1092 : 0);
}
public void SyncState(Serializer ser)
{
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);
}
}
}
}