From ac27f4451cc6a0ca31e7897f0f814e6de88f17bc Mon Sep 17 00:00:00 2001 From: adelikat Date: Mon, 7 Apr 2014 01:33:45 +0000 Subject: [PATCH] Atari 2600 - major progress on the DPC mapper, Pitfall II is now playable, but the mapper sound channel is still quite bad, also lacks things like savestate support --- .../Consoles/Atari/2600/Mappers/mDPC.cs | 302 ++++++++++-------- 1 file changed, 165 insertions(+), 137 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/2600/Mappers/mDPC.cs b/BizHawk.Emulation.Cores/Consoles/Atari/2600/Mappers/mDPC.cs index f2a5ffbcd7..704c25928b 100644 --- a/BizHawk.Emulation.Cores/Consoles/Atari/2600/Mappers/mDPC.cs +++ b/BizHawk.Emulation.Cores/Consoles/Atari/2600/Mappers/mDPC.cs @@ -1,4 +1,5 @@ -using BizHawk.Common; +using System.Linq; +using BizHawk.Common; namespace BizHawk.Emulation.Cores.Atari.Atari2600 { @@ -224,228 +225,273 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 */ internal class mDPC : MapperBase { - private ulong totalCycles; - private ulong elapsedCycles; - private double FractionalClocks; + // TODO: HardReset + // TODO: Savestates + // TODO: Dispose + // TODO: sound channel doesn't work + private readonly IntBuffer _counters = new IntBuffer(8); + private readonly ByteBuffer _tops = new ByteBuffer(8); + private readonly ByteBuffer _flags = new ByteBuffer(8); + private readonly ByteBuffer _bottoms = new ByteBuffer(8); + private readonly bool[] _myMusicMode = new bool[3]; - private int bank_4k; - 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; + private int _bank4K; + private byte _myRandomNumber; - private bool[] MusicMode = new bool[3]; // TODO: savestates + // TOD: something simpler here, don't need both + private int _mySystemCycles; + private int _systemCycles; - public override byte PeekMemory(ushort addr) + // Fractional DPC music OSC clocks unused during the last update + private double _myFractionalClocks; + + // TODO: refactor to not do this mess + private byte[] __mydisp; + private byte[] _myDisplayImage { - return base.PeekMemory(addr); //TODO + get + { + return __mydisp ?? (__mydisp = Core.Rom.Skip(8192).Take(2048).ToArray()); + } + } + + public override void SyncState(Serializer ser) + { + base.SyncState(ser); + ser.Sync("bank_4k", ref _bank4K); + } + + public override void HardReset() + { + _bank4K = 0; + base.HardReset(); } public override void ClockCpu() { - totalCycles++; + _systemCycles++; } - public override byte ReadMemory(ushort addr) + private byte ReadMem(ushort addr, bool peek) { - ClockRandomNumberGenerator(); - addr &= 0x0FFF; - - if (addr < 0x0040) + if (addr < 0x1000) { - byte result = 0; - int index = addr & 0x07; - int function = (addr >> 3) & 0x07; + return base.ReadMemory(addr); + } + + if (!peek) + { + Address(addr); + ClockRandomNumberGenerator(); + } + + if (addr < 0x1040) + { + byte result; + + // Get the index of the data fetcher that's being accessed + var index = addr & 0x07; + var function = (addr >> 3) & 0x07; // Update flag register for selected data fetcher - if ((Counters[index] & 0x00ff) == Tops[index]) + if ((_counters[index] & 0x00ff) == _tops[index]) { - Flags[index] = 0xff; + _flags[index] = 0xff; } - else if ((Counters[index] & 0x00ff) == Bottoms[index]) + else if ((_counters[index] & 0x00ff) == _bottoms[index]) { - Flags[index] = 0x00; + _flags[index] = 0x00; } switch (function) { - default: - result = 0; - break; case 0x00: if (index < 4) { - result = RandomNumber; + result = _myRandomNumber; } - else // it's a music read + else // No, it's a music read { - byte[] MusicAmplitudes = { - 0x00, 0x04, 0x05, 0x09, 0x06, 0x0a, 0x0b, 0x0f + var musicAmplitudes = new byte[] { + 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) + if (_myMusicMode[0] && _flags[5] > 0) { i |= 0x01; } - if (MusicMode[1] && Flags[6] > 0) + if (_myMusicMode[1] && _flags[6] > 0) { i |= 0x02; } - if (MusicMode[2] && Flags[7] > 0) + if (_myMusicMode[2] && _flags[7] > 0) { i |= 0x04; } - result = MusicAmplitudes[i]; + result = musicAmplitudes[i]; } break; + + // DFx display data read case 0x01: - result = DisplayBank_2k[2047 - Counters[index]]; + result = _myDisplayImage[2047 - _counters[index]]; break; + + // DFx display data read AND'd w/flag case 0x02: - result = DisplayBank_2k[2047 - (Counters[index] & Flags[index])]; + result = (byte)(_myDisplayImage[2047 - _counters[index]] & _flags[index]); break; + + // DFx flag case 0x07: - result = Flags[index]; + result = _flags[index]; + break; + + default: + result = 0; break; } // Clock the selected data fetcher's counter if needed - if ((index < 5) || ((index >= 5) && (!MusicMode[index - 5]))) + if ((index < 5) || ((index >= 5) && (!_myMusicMode[index - 5]))) { - Counters[index] = (Counters[index] - 1) & 0x07ff; + _counters[index] = (_counters[index] - 1) & 0x07ff; } return result; } - - Address(addr); - return Core.Rom[(bank_4k << 12) + addr]; + + return Core.Rom[(_bank4K << 12) + (addr & 0xFFF)]; + } + + public override byte ReadMemory(ushort addr) + { + return ReadMem(addr, false); + } + + public override byte PeekMemory(ushort addr) + { + return ReadMem(addr, true); } public override void WriteMemory(ushort addr, byte value) { - addr &= 0x0FFF; + if (addr < 0x1000) + { + base.WriteMemory(addr, value); + return; + } - // 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. + Address(addr); ClockRandomNumberGenerator(); - if ((addr >= 0x0040) && (addr < 0x0080)) + if (addr >= 0x1040 && addr < 0x1080) { - // Get the index of the data fetcher that's being accessed - int index = addr & 0x07; - int function = (addr >> 3) & 0x07; + var index = addr & 0x07; + var function = (addr >> 3) & 0x07; switch (function) { - case 0x00: // DFx top count - Tops[index] = value; - Flags[index] = 0x00; + // DFx top count + case 0x00: + _tops[index] = value; + _flags[index] = 0x00; break; - case 0x01: // DFx bottom count - Bottoms[index] = value; + + // DFx bottom count + case 0x01: + _bottoms[index] = value; break; - case 0x02: // DFx counter low - if ((index >= 5) && MusicMode[index - 5]) + + // DFx counter low + case 0x02: + if ((index >= 5) && _myMusicMode[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 + // Data fetcher is in music mode so its low counter value + // should be loaded from the top register not the poked value + _counters[index] = (_counters[index] & 0x0700) | + _tops[index]; } 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; + _counters[index] = (_counters[index] & 0x0700) | value; } + break; - case 0x03: // DFx counter high - Counters[index] = ((value & 0x07) << 8) | (Counters[index] & 0x00ff); + + // DFx counter high + case 0x03: + _counters[index] = (ushort)(((value & 0x07) << 8) | + (_counters[index] & 0x00ff)); // Execute special code for music mode data fetchers if (index >= 5) { - MusicMode[index - 5] = (value & 0x10) > 0; - + _myMusicMode[index - 5] = (value & 0x10) > 0; + // 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; + + // Random Number Generator Reset + case 0x06: + _myRandomNumber = 1; + break; } } - else - { - Address(addr); - } - - return; } private void Address(ushort addr) { - if (addr == 0x0FF8) + if (addr == 0x1FF8) { - bank_4k = 0; + _bank4K = 0; } - else if (addr == 0x0FF9) + else if (addr == 0x1FF9) { - bank_4k = 1; + _bank4K = 1; } } - public override void Dispose() + private void ClockRandomNumberGenerator() { - DisplayBank_2k.Dispose(); - Counters.Dispose(); - Flags.Dispose(); - base.Dispose(); - } + // 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 }; - 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); - } + // Using bits 7, 5, 4, & 3 of the shift register compute the input + // bit for the shift register + var bit = f[((_myRandomNumber >> 3) & 0x07) | + (((_myRandomNumber & 0x80) > 0) ? 0x08 : 0x00)]; - public override void HardReset() - { - // TODO - base.HardReset(); + // Update the shift register + _myRandomNumber = (byte)((_myRandomNumber << 1) | bit); } 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; - + var cycles = _systemCycles - _mySystemCycles; + _mySystemCycles = _systemCycles; // 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; + var clocks = ((20000.0 * cycles) / 1193191.66666667) + _myFractionalClocks; + var wholeClocks = (int)clocks; + _myFractionalClocks = clocks - wholeClocks; if (wholeClocks <= 0) { @@ -453,17 +499,17 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 } // Let's update counters and flags of the music mode data fetchers - for (int x = 5; x <= 7; ++x) + for (var x = 5; x <= 7; ++x) { // Update only if the data fetcher is in music mode - if (MusicMode[x - 5]) + if (_myMusicMode[x - 5]) { - int top = Tops[x] + 1; - int newLow = Counters[x] & 0x00ff; + var top = _tops[x] + 1; + var newLow = _counters[x] & 0x00ff; - if (Tops[x] != 0) + if (_tops[x] != 0) { - newLow -= (wholeClocks % top); + newLow -= wholeClocks % top; if (newLow < 0) { newLow += top; @@ -475,36 +521,18 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 } // Update flag register for this data fetcher - if (newLow <= Bottoms[x]) + if (newLow <= _bottoms[x]) { - Flags[x] = 0x00; + _flags[x] = 0x00; } - else if (newLow <= Tops[x]) + else if (newLow <= _tops[x]) { - Flags[x] = 0xff; + _flags[x] = 0xff; } - Counters[x] = (Counters[x] & 0x0700) | newLow; + _counters[x] = (_counters[x] & 0x0700) | (ushort)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); - } } }