285 lines
7.1 KiB
C#
285 lines
7.1 KiB
C#
using BizHawk.Common;
|
|
|
|
namespace BizHawk
|
|
{
|
|
partial class Atari2600
|
|
{
|
|
class mDPC : MapperBase
|
|
{
|
|
private ulong totalCycles = 0;
|
|
private ulong elapsedCycles = 0;
|
|
private double FractionalClocks;
|
|
|
|
private int bank_4k = 0;
|
|
private IntBuffer Counters = new IntBuffer(8);
|
|
private ByteBuffer Flags = new ByteBuffer(8);
|
|
private IntBuffer Tops = new IntBuffer(8);
|
|
private IntBuffer Bottoms = new IntBuffer(8);
|
|
private ByteBuffer DisplayBank_2k = new ByteBuffer(2048);
|
|
private byte RandomNumber = 0;
|
|
|
|
private bool[] MusicMode = new bool[3]; //TOOD: savestates
|
|
|
|
public override byte PeekMemory(ushort addr)
|
|
{
|
|
return base.PeekMemory(addr); //TODO
|
|
}
|
|
|
|
public override void ClockCpu()
|
|
{
|
|
totalCycles++;
|
|
}
|
|
|
|
public override byte ReadMemory(ushort addr)
|
|
{
|
|
ClockRandomNumberGenerator();
|
|
addr &= 0x0FFF;
|
|
|
|
if (addr < 0x0040)
|
|
{
|
|
byte result = 0;
|
|
int index = addr & 0x07;
|
|
int function = (addr >> 3) & 0x07;
|
|
|
|
// Update flag register for selected data fetcher
|
|
if ((Counters[index] & 0x00ff) == Tops[index])
|
|
{
|
|
Flags[index] = 0xff;
|
|
}
|
|
else if ((Counters[index] & 0x00ff) == Bottoms[index])
|
|
{
|
|
Flags[index] = 0x00;
|
|
}
|
|
|
|
switch (function)
|
|
{
|
|
default:
|
|
result = 0;
|
|
break;
|
|
case 0x00:
|
|
if (index < 4)
|
|
{
|
|
result = RandomNumber;
|
|
}
|
|
else //it's a music read
|
|
{
|
|
byte[] MusicAmplitudes = {
|
|
0x00, 0x04, 0x05, 0x09, 0x06, 0x0a, 0x0b, 0x0f
|
|
};
|
|
|
|
//// Update the music data fetchers (counter & flag)
|
|
UpdateMusicModeDataFetchers();
|
|
|
|
byte i = 0;
|
|
if(MusicMode[0] && Flags[5] > 0)
|
|
{
|
|
i |= 0x01;
|
|
}
|
|
if(MusicMode[1] && Flags[6] > 0)
|
|
{
|
|
i |= 0x02;
|
|
}
|
|
if(MusicMode[2] && Flags[7] > 0)
|
|
{
|
|
i |= 0x04;
|
|
}
|
|
|
|
result = MusicAmplitudes[i];
|
|
}
|
|
break;
|
|
case 0x01:
|
|
result = DisplayBank_2k[2047 - Counters[index]];
|
|
break;
|
|
case 0x02:
|
|
result = DisplayBank_2k[2047 - (Counters[index] & Flags[index])];
|
|
break;
|
|
case 0x07:
|
|
result = Flags[index];
|
|
break;
|
|
}
|
|
|
|
// Clock the selected data fetcher's counter if needed
|
|
if ((index < 5) || ((index >= 5) && (!MusicMode[index - 5])))
|
|
{
|
|
Counters[index] = (Counters[index] - 1) & 0x07ff;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
else
|
|
{
|
|
Address(addr);
|
|
return core.rom[(bank_4k << 12) + addr];
|
|
}
|
|
}
|
|
|
|
public override void WriteMemory(ushort addr, byte value)
|
|
{
|
|
addr &= 0x0FFF;
|
|
|
|
// Clock the random number generator. This should be done for every
|
|
// cartridge access, however, we're only doing it for the DPC and
|
|
// hot-spot accesses to save time.
|
|
ClockRandomNumberGenerator();
|
|
|
|
if ((addr >= 0x0040) && (addr < 0x0080))
|
|
{
|
|
// Get the index of the data fetcher that's being accessed
|
|
int index = addr & 0x07;
|
|
int function = (addr >> 3) & 0x07;
|
|
|
|
switch (function)
|
|
{
|
|
case 0x00: // DFx top count
|
|
Tops[index] = value;
|
|
Flags[index] = 0x00;
|
|
break;
|
|
case 0x01: // DFx bottom count
|
|
Bottoms[index] = value;
|
|
break;
|
|
case 0x02: // DFx counter low
|
|
if ((index >= 5) && MusicMode[index - 5])
|
|
{
|
|
Counters[index] = (Counters[index] & 0x0700) | Tops[index]; // Data fetcher is in music mode so its low counter value should be loaded from the top register not the poked value
|
|
}
|
|
else
|
|
{
|
|
// Data fetcher is either not a music mode data fetcher or it
|
|
// isn't in music mode so it's low counter value should be loaded
|
|
// with the poked value
|
|
Counters[index] = (Counters[index] & 0x0700) | value;
|
|
}
|
|
break;
|
|
case 0x03: // DFx counter high
|
|
Counters[index] = ((value & 0x07) << 8) | (Counters[index] & 0x00ff);
|
|
|
|
// Execute special code for music mode data fetchers
|
|
if (index >= 5)
|
|
{
|
|
MusicMode[index - 5] = (value & 0x10) > 0 ? true : false;
|
|
|
|
// NOTE: We are not handling the clock source input for
|
|
// the music mode data fetchers. We're going to assume
|
|
// they always use the OSC input.
|
|
}
|
|
break;
|
|
case 0x06: // Random Number Generator Reset
|
|
RandomNumber = 1;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Address(addr);
|
|
}
|
|
return;
|
|
}
|
|
|
|
private void Address(ushort addr)
|
|
{
|
|
if (addr == 0x0FF8)
|
|
{
|
|
bank_4k = 0;
|
|
}
|
|
else if (addr == 0x0FF9)
|
|
{
|
|
bank_4k = 1;
|
|
}
|
|
}
|
|
|
|
public override void Dispose()
|
|
{
|
|
DisplayBank_2k.Dispose();
|
|
Counters.Dispose();
|
|
Flags.Dispose();
|
|
base.Dispose();
|
|
}
|
|
|
|
public override void SyncState(Serializer ser)
|
|
{
|
|
//TODO
|
|
base.SyncState(ser);
|
|
ser.Sync("bank_4k", ref bank_4k);
|
|
ser.Sync("DisplayBank_2k", ref DisplayBank_2k);
|
|
ser.Sync("Flags", ref Flags);
|
|
ser.Sync("Counters", ref Counters);
|
|
ser.Sync("RandomNumber", ref RandomNumber);
|
|
}
|
|
|
|
|
|
private void UpdateMusicModeDataFetchers()
|
|
{
|
|
// Calculate the number of cycles since the last update
|
|
//int cycles = mySystem->cycles() - mySystemCycles;
|
|
//mySystemCycles = mySystem->cycles();
|
|
ulong cycles = totalCycles - elapsedCycles;
|
|
elapsedCycles = totalCycles;
|
|
|
|
|
|
// Calculate the number of DPC OSC clocks since the last update
|
|
double clocks = ((20000.0 * cycles) / 1193191.66666667) + FractionalClocks;
|
|
int wholeClocks = (int)clocks;
|
|
FractionalClocks = clocks - (double)wholeClocks;
|
|
|
|
if (wholeClocks <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Let's update counters and flags of the music mode data fetchers
|
|
for (int x = 5; x <= 7; ++x)
|
|
{
|
|
// Update only if the data fetcher is in music mode
|
|
if (MusicMode[x - 5])
|
|
{
|
|
int top = Tops[x] + 1;
|
|
int newLow = Counters[x] & 0x00ff;
|
|
|
|
if (Tops[x] != 0)
|
|
{
|
|
newLow -= (wholeClocks % top);
|
|
if (newLow < 0)
|
|
{
|
|
newLow += top;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
newLow = 0;
|
|
}
|
|
|
|
// Update flag register for this data fetcher
|
|
if (newLow <= Bottoms[x])
|
|
{
|
|
Flags[x] = 0x00;
|
|
}
|
|
else if (newLow <= Tops[x])
|
|
{
|
|
Flags[x] = 0xff;
|
|
}
|
|
|
|
Counters[x] = (Counters[x] & 0x0700) | newLow;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ClockRandomNumberGenerator()
|
|
{
|
|
// Table for computing the input bit of the random number generator's
|
|
// shift register (it's the NOT of the EOR of four bits)
|
|
byte[] f = {
|
|
1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1
|
|
};
|
|
|
|
// Using bits 7, 5, 4, & 3 of the shift register compute the input
|
|
// bit for the shift register
|
|
byte bit = f[((RandomNumber >> 3) & 0x07) |
|
|
((RandomNumber & 0x80) > 0 ? 0x08 : 0x00)];
|
|
|
|
// Update the shift register
|
|
RandomNumber = (byte)(RandomNumber << 1 | bit);
|
|
}
|
|
}
|
|
}
|
|
}
|