Add files via upload

This commit is contained in:
alyosha-tas 2017-05-24 19:37:44 -04:00 committed by GitHub
parent f159220bef
commit a2ab02f9d9
4 changed files with 639 additions and 0 deletions

View File

@ -0,0 +1,334 @@
using System;
using System.Numerics;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
{
// Emulates the TIA
public partial class TIA : ISoundProvider
{
public byte BusState;
private bool _doTicks;
private byte _hsyncCnt;
private int _capChargeStart;
private bool _capCharging;
public int AudioClocks; // not savestated
private readonly Audio[] AUD = { new Audio(), 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];
public void Reset()
{
_hsyncCnt = 0;
_capChargeStart = 0;
_capCharging = false;
AudioClocks = 0;
_doTicks = false;
}
// Execute TIA cycles
public void Execute(int cycles)
{
// do the audio sampling
if (_hsyncCnt == 36 || _hsyncCnt == 148)
{
LocalAudioCycles[AudioClocks] += (short)(AUD[0].Cycle() / 2);
LocalAudioCycles[AudioClocks] += (short)(AUD[1].Cycle() / 2);
AudioClocks++;
}
}
public byte ReadMemory(ushort addr, bool peek)
{
var maskedAddr = (ushort)(addr & 0x000F);
byte coll = 0;
int mask = 0;
if (maskedAddr == 0x00) // CXM0P
{
}
if (maskedAddr == 0x01) // CXM1P
{
}
if (maskedAddr == 0x02) // CXP0FB
{
}
if (maskedAddr == 0x03) // CXP1FB
{
}
if (maskedAddr == 0x04) // CXM0FB
{
}
if (maskedAddr == 0x05) // CXM1FB
{
}
if (maskedAddr == 0x06) // CXBLPF
{
}
if (maskedAddr == 0x07) // CXPPMM
{
}
// inputs 0-3 are measured by a charging capacitor, these inputs are used with the paddles and the keyboard
// Changing the hard coded value will change the paddle position. The range seems to be roughly 0-56000 according to values from stella
// 6105 roughly centers the paddle in Breakout
if (maskedAddr == 0x08) // INPT0
{
}
if (maskedAddr == 0x09) // INPT1
{
}
if (maskedAddr == 0x0A) // INPT2
{
}
if (maskedAddr == 0x0B) // INPT3
{
}
if (maskedAddr == 0x0C) // INPT4
{
}
if (maskedAddr == 0x0D) // INPT5
{
}
// some bits of the databus will be undriven when a read call is made. Our goal here is to sort out what
// happens to the undriven pins. Most of the time, they will be in whatever state they were when previously
// assigned in some other bus access, so let's go with that.
coll += (byte)(mask & BusState);
if (!peek)
{
BusState = coll;
}
return coll;
}
public void WriteMemory(ushort addr, byte value, bool poke)
{
var maskedAddr = (ushort)(addr & 0x3f);
if (!poke)
{
BusState = value;
}
if (maskedAddr == 0x00) // VSYNC
{
}
else if (maskedAddr == 0x01) // VBLANK
{
}
else if (maskedAddr == 0x02) // WSYNC
{
}
else if (maskedAddr == 0x04) // NUSIZ0
{
}
else if (maskedAddr == 0x05) // NUSIZ1
{
}
else if (maskedAddr == 0x06) // COLUP0
{
}
else if (maskedAddr == 0x07) // COLUP1
{
}
else if (maskedAddr == 0x08) // COLUPF
{
}
else if (maskedAddr == 0x09) // COLUBK
{
}
else if (maskedAddr == 0x0A) // CTRLPF
{
}
else if (maskedAddr == 0x0B) // REFP0
{
}
else if (maskedAddr == 0x0C) // REFP1
{
}
else if (maskedAddr == 0x0D) // PF0
{
}
else if (maskedAddr == 0x0E) // PF1
{
}
else if (maskedAddr == 0x0F) // PF2
{
}
else if (maskedAddr == 0x10) // RESP0
{
}
else if (maskedAddr == 0x11) // RESP1
{
}
else if (maskedAddr == 0x12) // RESM0
{
}
else if (maskedAddr == 0x13) // RESM1
{
}
else if (maskedAddr == 0x14) // RESBL
{
}
else if (maskedAddr == 0x15) // AUDC0
{
AUD[0].AUDC = (byte)(value & 15);
}
else if (maskedAddr == 0x16) // AUDC1
{
AUD[1].AUDC = (byte)(value & 15);
}
else if (maskedAddr == 0x17) // AUDF0
{
AUD[0].AUDF = (byte)((value & 31) + 1);
}
else if (maskedAddr == 0x18) // AUDF1
{
AUD[1].AUDF = (byte)((value & 31) + 1);
}
else if (maskedAddr == 0x19) // AUDV0
{
AUD[0].AUDV = (byte)(value & 15);
}
else if (maskedAddr == 0x1A) // AUDV1
{
AUD[1].AUDV = (byte)(value & 15);
}
else if (maskedAddr == 0x1B) // GRP0
{
}
else if (maskedAddr == 0x1C) // GRP1
{
}
else if (maskedAddr == 0x1D) // ENAM0
{
}
else if (maskedAddr == 0x1E) // ENAM1
{
}
else if (maskedAddr == 0x1F) // ENABL
{
}
else if (maskedAddr == 0x20) // HMP0
{
}
else if (maskedAddr == 0x21) // HMP1
{
}
else if (maskedAddr == 0x22) // HMM0
{
}
else if (maskedAddr == 0x23) // HMM1
{
}
else if (maskedAddr == 0x24) // HMBL
{
}
else if (maskedAddr == 0x25) // VDELP0
{
}
else if (maskedAddr == 0x26) // VDELP1
{
}
else if (maskedAddr == 0x27) // VDELBL
{
}
else if (maskedAddr == 0x28) // RESMP0
{
}
else if (maskedAddr == 0x29) // RESMP1
{
}
else if (maskedAddr == 0x2A) // HMOVE
{
}
else if (maskedAddr == 0x2B) // HMCLR
{
}
else if (maskedAddr == 0x2C) // CXCLR
{
}
}
private enum AudioRegister : byte
{
AUDC, AUDF, AUDV
}
private int _frameStartCycles, _frameEndCycles;
}
}

View File

@ -0,0 +1,212 @@
using System;
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
{
public partial class TIA
{
public class Audio
{
// noise/division control
public byte AUDC = 0;
// frequency divider
public byte AUDF = 1;
// volume
public byte AUDV = 0;
// 2 state counter
private bool sr1 = true;
// 4 bit shift register
private int sr4 = 0x0f;
// 5 bit shift register
private int sr5 = 0x1f;
// 9 bit shift register
private int sr9 = 0x1ff;
// 3 state counter
private int sr3 = 2;
// counter based off AUDF
private byte freqcnt;
// latched audio value
private bool on = true;
private bool Run3()
{
sr3++;
if (sr3 == 3)
{
sr3 = 0;
return true;
}
return false;
}
private bool Run4()
{
bool ret = (sr4 & 1) != 0;
bool c = ((sr4 & 1) != 0) ^ ((sr4 & 2) != 0);
sr4 = (sr4 >> 1) | (c ? 8 : 0);
return ret;
}
private bool Run5()
{
bool ret = (sr5 & 1) != 0;
bool c = ((sr5 & 1) != 0) ^ ((sr5 & 4) != 0);
sr5 = (sr5 >> 1) | (c ? 16 : 0);
return ret;
}
private bool One4()
{
bool ret = (sr4 & 1) != 0;
sr4 = (sr4 >> 1) | 8;
return ret;
}
private bool One5()
{
bool ret = (sr5 & 1) != 0;
sr5 = (sr5 >> 1) | 16;
return ret;
}
private bool Run1()
{
sr1 = !sr1;
return !sr1;
}
private bool Run9()
{
bool ret = (sr9 & 1) != 0;
bool c = ((sr9 & 1) != 0) ^ ((sr9 & 16) != 0);
sr9 = (sr9 >> 1) | (c ? 256 : 0);
return ret;
}
/// <summary>
/// call me approx 31k times a second
/// </summary>
/// <returns>16 bit audio sample</returns>
public short Cycle()
{
if (++freqcnt >= AUDF)
{
freqcnt = 0;
switch (AUDC)
{
case 0x00:
case 0x0b:
// Both have a 1 s
One5();
on = One4();
break;
case 0x01:
// Both run, but the 5 bit is ignored
on = Run4();
//Run5();
break;
case 0x02:
if ((sr5 & 0x0f) == 0 || (sr5 & 0x0f) == 0x0f)
{
on = Run4();
}
Run5();
break;
case 0x03:
if (Run5())
{
on = Run4();
}
break;
case 0x04:
Run5();
One4();
on = Run1();
break;
case 0x05:
One5();
Run4();
on = Run1();
break;
case 0x06:
case 0x0a:
Run5();
if ((sr5 & 0x0f) == 0)
{
on = false;
}
else if ((sr5 & 0x0f) == 0x0f)
{
on = true;
}
break;
case 0x07:
case 0x09:
on = Run5();
break;
case 0x08:
on = Run9();
break;
case 0x0c:
case 0x0d:
if (Run3())
{
on = Run1();
}
break;
case 0x0e:
if (Run3())
{
goto case 0x06;
}
break;
case 0x0f:
if (Run3())
{
goto case 0x07;
}
break;
}
}
return (short)(on ? AUDV * 1092 : 0);
}
public void SyncState(Serializer ser)
{
ser.Sync("AUDC", ref AUDC);
ser.Sync("AUDF", ref AUDF);
ser.Sync("AUDV", ref AUDV);
ser.Sync("sr1", ref sr1);
ser.Sync("sr3", ref sr3);
ser.Sync("sr4", ref sr4);
ser.Sync("sr5", ref sr5);
ser.Sync("sr9", ref sr9);
ser.Sync("freqcnt", ref freqcnt);
ser.Sync("on", ref on);
}
}
}
}

View File

@ -0,0 +1,65 @@
using System;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
{
public partial class TIA : ISoundProvider
{
public bool CanProvideAsync => false;
public void SetSyncMode(SyncSoundMode mode)
{
if (mode != SyncSoundMode.Sync)
{
throw new InvalidOperationException("Only Sync mode is supported.");
}
}
public SyncSoundMode SyncMode => SyncSoundMode.Sync;
public void GetSamplesSync(out short[] samples, out int nsamp)
{
short[] ret = new short[_spf * 2];
GetSamples(ret);
samples = ret;
nsamp = _spf;
}
public void GetSamplesAsync(short[] samples)
{
throw new NotSupportedException("Async is not available");
}
public void DiscardSamples()
{
AudioClocks = 0;
}
private readonly int _spf;
// Exposing this as GetSamplesAsync would allow this to provide async sound
// However, it does nothing special for async sound so I don't see a point
private void GetSamples(short[] samples)
{
if (AudioClocks > 0)
{
var samples31Khz = new short[AudioClocks]; // mono
for (int i = 0; i < AudioClocks; i++)
{
samples31Khz[i] = LocalAudioCycles[i];
LocalAudioCycles[i] = 0;
}
// convert from 31khz to 44khz
for (var i = 0; i < samples.Length / 2; i++)
{
samples[i * 2] = samples31Khz[(int)(((double)samples31Khz.Length / (double)(samples.Length / 2)) * i)];
samples[(i * 2) + 1] = samples[i * 2];
}
}
AudioClocks = 0;
}
}
}

View File

@ -0,0 +1,28 @@
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
{
public partial class TIA
{
public void SyncState(Serializer ser)
{
ser.BeginSection("TIA");
ser.Sync("hsyncCnt", ref _hsyncCnt);
// add everything to the state
ser.Sync("Bus_State", ref BusState);
ser.Sync("Ticks", ref _doTicks);
// some of these things weren't in the state because they weren't needed if
// states were always taken at frame boundaries
ser.Sync("capChargeStart", ref _capChargeStart);
ser.Sync("capCharging", ref _capCharging);
ser.Sync("AudioClocks", ref AudioClocks);
ser.Sync("FrameStartCycles", ref _frameStartCycles);
ser.Sync("FrameEndCycles", ref _frameEndCycles);
}
}
}