NESHawk: make game genie compare cheats work

This commit is contained in:
alyosha-tas 2020-05-12 11:17:10 -04:00 committed by nattthebear
parent ff4662efe8
commit c53bda9235
5 changed files with 171 additions and 22 deletions

View File

@ -1,4 +1,5 @@
using BizHawk.Emulation.Common;
using System;
namespace BizHawk.Client.Common
{
@ -176,6 +177,20 @@ namespace BizHawk.Client.Common
break;
}
}
// This will take effect only for NES, and will pulse the cheat with compare option directly to the core
// Only works for byte cheats currently
if (_watch.Size == WatchSize.Byte && _watch.Domain.Name == "System Bus")
{
if (Compare.HasValue)
{
_watch.Domain.SendCheatToCore((int)Address.Value, (byte)Value, Compare.Value, (int)ComparisonType);
}
else
{
_watch.Domain.SendCheatToCore((int)Address.Value, (byte)Value, -1, 0);
}
}
}
}

View File

@ -162,5 +162,7 @@ namespace BizHawk.Emulation.Common
for (var i = 0; i < values.Length; i++, start += 4)
values[i] = PeekUshort(start, bigEndian);
}
public virtual void SendCheatToCore(int addr, byte value, int compare, int compare_type) { }
}
}

View File

@ -334,4 +334,105 @@ namespace BizHawk.Emulation.Common
_monitor = monitor;
}
}
public class MemoryDomainDelegateSysBusNES : MemoryDomain
{
private Action<long, byte> _poke;
// TODO: use an array of Ranges
private Action<Range<long>, byte[]> _bulkPeekByte { get; set; }
private Action<Range<long>, bool, ushort[]> _bulkPeekUshort { get; set; }
private Action<Range<long>, bool, uint[]> _bulkPeekUint { get; set; }
public Func<long, byte> Peek { get; set; }
public Action<long, byte> Poke
{
get => _poke;
set
{
_poke = value;
Writable = value != null;
}
}
private Action<int, byte, int, int> sendcheattocore { get; set; }
public override byte PeekByte(long addr)
{
return Peek(addr);
}
public override void PokeByte(long addr, byte val)
{
_poke?.Invoke(addr, val);
}
public override void BulkPeekByte(Range<long> addresses, byte[] values)
{
if (_bulkPeekByte != null)
{
_bulkPeekByte.Invoke(addresses, values);
}
else
{
base.BulkPeekByte(addresses, values);
}
}
public override void BulkPeekUshort(Range<long> addresses, bool bigEndian, ushort[] values)
{
if (_bulkPeekUshort != null)
{
_bulkPeekUshort.Invoke(addresses, EndianType == Endian.Big, values);
}
else
{
base.BulkPeekUshort(addresses, EndianType == Endian.Big, values);
}
}
public override void BulkPeekUint(Range<long> addresses, bool bigEndian, uint[] values)
{
if (_bulkPeekUint != null)
{
_bulkPeekUint.Invoke(addresses, EndianType == Endian.Big, values);
}
else
{
base.BulkPeekUint(addresses, EndianType == Endian.Big, values);
}
}
public override void SendCheatToCore(int addr, byte value, int compare, int comparetype)
{
if (sendcheattocore != null)
{
sendcheattocore.Invoke(addr, value, compare, comparetype);
}
else
{
base.SendCheatToCore(addr, value, compare, comparetype);
}
}
public MemoryDomainDelegateSysBusNES(
string name,
long size,
Endian endian,
Func<long, byte> peek,
Action<long, byte> poke,
int wordSize,
Action<int, byte, int, int> nescheatpoke = null)
{
Name = name;
EndianType = endian;
Size = size;
Peek = peek;
_poke = poke;
Writable = poke != null;
WordSize = wordSize;
sendcheattocore = nescheatpoke;
}
}
}

View File

@ -48,8 +48,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
// cheat addr index tracker
// disables all cheats each frame
public int[] cheat_indexes = new int[0x10000];
public byte[] cheat_active = new byte[0x10000];
public int[] cheat_addresses = new int[0x1000];
public byte[] cheat_value = new byte[0x1000];
public int[] cheat_compare_val = new int[0x1000];
public int[] cheat_compare_type = new int[0x1000];
public int num_cheats;
// new input system
@ -848,7 +850,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
else
{
// apply a cheat to non-writable memory
ApplyCheat(addr, value, null);
ApplyCheat(addr, value);
}
}
@ -903,6 +905,25 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
{
// easy optimization, since rom reads are so common, move this up (reordering the rest of these else ifs is not easy)
ret = Board.ReadPrg(addr - 0x8000);
// handle cheats, currently all cheats are of game genie style only
if (num_cheats != 0)
{
for (int i = 0; i < num_cheats; i++)
{
if (cheat_addresses[i] == addr)
{
if (cheat_compare_type[i] == 0)
{
ret = cheat_value[i];
}
else if ((cheat_compare_type[i] == 1) && ((int)ret == cheat_compare_val[i]))
{
ret = cheat_value[i];
}
}
}
}
}
else if (addr < 0x0800)
{
@ -929,19 +950,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
ret = Board.ReadWram(addr - 0x6000);
}
// handle cheats (currently cheats can only freeze read only areas)
// there is no way to distinguish between a memory poke and a memory freeze
if (num_cheats !=0)
{
for (int i = 0; i < num_cheats; i++)
{
if(cheat_indexes[i] == addr)
{
ret = cheat_active[addr];
}
}
}
if (MemoryCallbacks.HasReads)
{
uint flags = (uint)(MemoryCallbackFlags.CPUZero | MemoryCallbackFlags.AccessRead);
@ -952,13 +960,32 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
return ret;
}
public void ApplyCheat(int addr, byte value, byte? compare)
public void ApplyCheat(int addr, byte value)
{
if (addr <= 0xFFFF)
{
cheat_indexes[num_cheats] = addr;
cheat_active[addr] = value;
num_cheats++;
cheat_addresses[num_cheats] = addr;
cheat_value[num_cheats] = value;
// there is no compare here
cheat_compare_val[num_cheats] = -1;
cheat_compare_type[num_cheats] = 0;
if (num_cheats < 0x1000) { num_cheats++; }
}
}
public void ApplyCompareCheat(int addr, byte value, int compare, int comparetype)
{
if (addr <= 0xFFFF)
{
cheat_addresses[num_cheats] = addr;
cheat_value[num_cheats] = value;
cheat_compare_val[num_cheats] = compare;
cheat_compare_type[num_cheats] = comparetype;
if (num_cheats < 0x1000) { num_cheats++; }
}
}

View File

@ -12,8 +12,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
{
var domains = new List<MemoryDomain>();
var RAM = new MemoryDomainByteArray("RAM", MemoryDomain.Endian.Little, ram, true, 1);
var SystemBus = new MemoryDomainDelegate("System Bus", 0x10000, MemoryDomain.Endian.Little,
addr => PeekMemory((ushort)addr), (addr, value) => ApplySystemBusPoke((int)addr, value), 1);
// System bus gets it's own class in order to send compare values to cheats
var SystemBus = new MemoryDomainDelegateSysBusNES("System Bus", 0x10000, MemoryDomain.Endian.Little,
addr => PeekMemory((ushort)addr), (addr, value) => ApplySystemBusPoke((int)addr, value), 1,
(addr, value, compare, comparetype) => ApplyCompareCheat(addr, value, compare, comparetype));
var PPUBus = new MemoryDomainDelegate("PPU Bus", 0x4000, MemoryDomain.Endian.Little,
addr => ppu.ppubus_peek((int)addr), (addr, value) => ppu.ppubus_write((int)addr, value), 1);
var CIRAMdomain = new MemoryDomainByteArray("CIRAM (nametables)", MemoryDomain.Endian.Little, CIRAM, true, 1);