diff --git a/BizHawk.Emulation/Consoles/Atari/2600/Mappers/mDPC.cs b/BizHawk.Emulation/Consoles/Atari/2600/Mappers/mDPC.cs index 73afe99f48..584429e9b6 100644 --- a/BizHawk.Emulation/Consoles/Atari/2600/Mappers/mDPC.cs +++ b/BizHawk.Emulation/Consoles/Atari/2600/Mappers/mDPC.cs @@ -12,7 +12,7 @@ namespace BizHawk { int bank_4k = 0; IntBuffer Counters = new IntBuffer(8); - IntBuffer Flags = new IntBuffer(8); + ByteBuffer Flags = new ByteBuffer(8); IntBuffer Tops = new IntBuffer(8); IntBuffer Bottoms = new IntBuffer(8); ByteBuffer DisplayBank_2k = new ByteBuffer(2048); @@ -43,42 +43,39 @@ namespace BizHawk switch (function) { default: + result = 0; + break; + case 0x00: if (index < 4) { result = RandomNumber; } - else + else //it's a music read { - result = 0;//TODO - - - //byte[8] musicAmplitudes = { - // 0x00, 0x04, 0x05, 0x09, 0x06, 0x0a, 0x0b, 0x0f - //}; + byte[] MusicAmplitudes = { + 0x00, 0x04, 0x05, 0x09, 0x06, 0x0a, 0x0b, 0x0f + }; //// Update the music data fetchers (counter & flag) - //UpdateMusicModeDataFetchers(); + UpdateMusicModeDataFetchers(); - //byte i = 0; - //if(MusicMode[0] && Flags[5]) - //{ - // i |= 0x01; - //} - //if(MusicMode[1] && Flags[6]) - //{ - // i |= 0x02; - //} - //if(MusicMode[2] && Flags[7]) - //{ - // i |= 0x04; - //} + 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]; + result = MusicAmplitudes[i]; } break; - case 0x00: - result = 0; //TODO - break; case 0x01: result = DisplayBank_2k[2047 - Counters[index]]; break; @@ -86,9 +83,16 @@ namespace BizHawk 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 @@ -105,11 +109,70 @@ namespace BizHawk public override void WriteMemory(ushort addr, byte value) { - Address(addr); if (addr < 0x1000) { base.WriteMemory(addr, 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; + default: + break; + } + } + else + { + Address(addr); + } + return; } private void Address(ushort addr) @@ -134,6 +197,7 @@ namespace BizHawk public override void SyncState(Serializer ser) { + //TODO base.SyncState(ser); ser.Sync("bank_4k", ref bank_4k); ser.Sync("DisplayBank_2k", ref DisplayBank_2k); @@ -142,9 +206,75 @@ namespace BizHawk ser.Sync("RandomNumber", ref RandomNumber); } + private double FractionalClocks; private void UpdateMusicModeDataFetchers() { - //TODO + // Calculate the number of cycles since the last update + //int cycles = mySystem->cycles() - mySystemCycles; + //mySystemCycles = mySystem->cycles(); + int cycles = 0; //TODO: need to get cycles! + // 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); } } }