Add files via upload
This commit is contained in:
parent
f159220bef
commit
a2ab02f9d9
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue