NESHawk: make game genie compare cheats work
This commit is contained in:
parent
ff4662efe8
commit
c53bda9235
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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) { }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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++; }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue