Cheats: Use safe memory access routines

This commit is contained in:
Connor McLaughlin 2021-09-10 15:53:15 +10:00
parent 8dcd68b0a8
commit e12474ac91

View file

@ -14,8 +14,9 @@
#include <cctype> #include <cctype>
#include <iomanip> #include <iomanip>
#include <sstream> #include <sstream>
#include <type_traits>
Log_SetChannel(Cheats); Log_SetChannel(Cheats);
static std::array<u32, 256> cht_register; //Used for D7 ,51 & 52 cheat types static std::array<u32, 256> cht_register; // Used for D7 ,51 & 52 cheat types
using KeyValuePairVector = std::vector<std::pair<std::string, std::string>>; using KeyValuePairVector = std::vector<std::pair<std::string, std::string>>;
@ -39,66 +40,34 @@ static bool IsValidScanAddress(PhysicalMemoryAddress address)
} }
template<typename T> template<typename T>
static T DoMemoryRead(PhysicalMemoryAddress address) static T DoMemoryRead(VirtualMemoryAddress address)
{ {
using UnsignedType = typename std::make_unsigned_t<T>;
static_assert(std::is_same_v<UnsignedType, u8> || std::is_same_v<UnsignedType, u16> ||
std::is_same_v<UnsignedType, u32>);
T result; T result;
if constexpr (std::is_same_v<UnsignedType, u8>)
if ((address & CPU::DCACHE_LOCATION_MASK) == CPU::DCACHE_LOCATION && return CPU::SafeReadMemoryByte(address, &result) ? result : static_cast<T>(0);
(address & CPU::DCACHE_OFFSET_MASK) < CPU::DCACHE_SIZE) else if constexpr (std::is_same_v<UnsignedType, u16>)
{ return CPU::SafeReadMemoryHalfWord(address, &result) ? result : static_cast<T>(0);
std::memcpy(&result, &CPU::g_state.dcache[address & CPU::DCACHE_OFFSET_MASK], sizeof(result)); else // if constexpr (std::is_same_v<UnsignedType, u32>)
return result; return CPU::SafeReadMemoryWord(address, &result) ? result : static_cast<T>(0);
}
address &= CPU::PHYSICAL_MEMORY_ADDRESS_MASK;
if (address < Bus::RAM_MIRROR_END)
{
if (Bus::g_ram != NULL)
std::memcpy(&result, &Bus::g_ram[address & Bus::g_ram_mask], sizeof(result));
else
return 0;
return result;
}
if (address >= Bus::BIOS_BASE && address < (Bus::BIOS_BASE + Bus::BIOS_SIZE))
{
std::memcpy(&result, &Bus::g_bios[address & Bus::BIOS_MASK], sizeof(result));
return result;
}
result = static_cast<T>(0);
return result;
} }
template<typename T> template<typename T>
static void DoMemoryWrite(PhysicalMemoryAddress address, T value) static void DoMemoryWrite(PhysicalMemoryAddress address, T value)
{ {
if ((address & CPU::DCACHE_LOCATION_MASK) == CPU::DCACHE_LOCATION && using UnsignedType = typename std::make_unsigned_t<T>;
(address & CPU::DCACHE_OFFSET_MASK) < CPU::DCACHE_SIZE) static_assert(std::is_same_v<UnsignedType, u8> || std::is_same_v<UnsignedType, u16> ||
{ std::is_same_v<UnsignedType, u32>);
std::memcpy(&CPU::g_state.dcache[address & CPU::DCACHE_OFFSET_MASK], &value, sizeof(value));
return;
}
address &= CPU::PHYSICAL_MEMORY_ADDRESS_MASK; if constexpr (std::is_same_v<UnsignedType, u8>)
CPU::SafeWriteMemoryByte(address, value);
if (address < Bus::RAM_MIRROR_END) else if constexpr (std::is_same_v<UnsignedType, u16>)
{ CPU::SafeWriteMemoryHalfWord(address, value);
// Only invalidate code when it changes. else // if constexpr (std::is_same_v<UnsignedType, u32>)
T old_value; CPU::SafeWriteMemoryWord(address, value);
std::memcpy(&old_value, &Bus::g_ram[address & Bus::g_ram_mask], sizeof(old_value));
if (old_value != value)
{
std::memcpy(&Bus::g_ram[address & Bus::g_ram_mask], &value, sizeof(value));
const u32 code_page_index = Bus::GetRAMCodePageIndex(address & Bus::g_ram_mask);
if (Bus::IsRAMCodePage(code_page_index))
CPU::CodeCache::InvalidateBlocksWithPageIndex(code_page_index);
}
return;
}
} }
static u32 GetControllerButtonBits() static u32 GetControllerButtonBits()
@ -1287,9 +1256,9 @@ void CheatCode::Apply() const
const u16 value2 = Truncate16((inst.value32 & 0xFFFF0000u) >> 16); const u16 value2 = Truncate16((inst.value32 & 0xFFFF0000u) >> 16);
const u16 value = DoMemoryRead<u16>(inst.address); const u16 value = DoMemoryRead<u16>(inst.address);
if (value==value1) if (value == value1)
DoMemoryWrite<u16>(inst.address, value2); DoMemoryWrite<u16>(inst.address, value2);
else if (value==value2) else if (value == value2)
DoMemoryWrite<u16>(inst.address, value1); DoMemoryWrite<u16>(inst.address, value1);
index++; index++;
} }
@ -1535,7 +1504,8 @@ void CheatCode::Apply() const
break; break;
case 0x43: // Write the u16 from cht_register[cht_reg_no2] to cht_register[cht_reg_no1] case 0x43: // Write the u16 from cht_register[cht_reg_no2] to cht_register[cht_reg_no1]
// and add the u16 from the address field to it // and add the u16 from the address field to it
cht_register[cht_reg_no1] = Truncate16(cht_register[cht_reg_no2] & 0xFFFFu) + Truncate16(poke_value & 0xFFFFu); cht_register[cht_reg_no1] =
Truncate16(cht_register[cht_reg_no2] & 0xFFFFu) + Truncate16(poke_value & 0xFFFFu);
break; break;
case 0x44: // Write the u16 from the value stored in cht_register[cht_reg_no2] + poke_value to the address case 0x44: // Write the u16 from the value stored in cht_register[cht_reg_no2] + poke_value to the address
// stored in cht_register[cht_reg_no1] // stored in cht_register[cht_reg_no1]
@ -1655,16 +1625,16 @@ void CheatCode::Apply() const
break; break;
case InstructionCode::ExtMultiConditionals: // F6 case InstructionCode::ExtMultiConditionals: // F6
{ {
//Ensure any else if or else that are hit outside the if context are skipped // Ensure any else if or else that are hit outside the if context are skipped
if ( (inst.value32 & 0xFFFFFF00u) != 0x1F000000) if ((inst.value32 & 0xFFFFFF00u) != 0x1F000000)
{ {
activate_codes = false; activate_codes = false;
break; break;
} }
for (;;) for (;;)
{ {
const u8 totalConds = Truncate8(instructions[index-1].value32 & 0x000000FFu); const u8 totalConds = Truncate8(instructions[index - 1].value32 & 0x000000FFu);
const u8 conditionType = Truncate8(instructions[index-1].address & 0x000000FFu); const u8 conditionType = Truncate8(instructions[index - 1].address & 0x000000FFu);
bool conditions_check; bool conditions_check;
@ -1676,61 +1646,73 @@ void CheatCode::Apply() const
{ {
switch (instructions[index].code) switch (instructions[index].code)
{ {
case InstructionCode::CompareEqual16: //D0 case InstructionCode::CompareEqual16: // D0
conditions_check &= (DoMemoryRead<u16>(instructions[index].address) == instructions[index].value16); conditions_check &=
(DoMemoryRead<u16>(instructions[index].address) == instructions[index].value16);
break; break;
case InstructionCode::CompareNotEqual16: //D1 case InstructionCode::CompareNotEqual16: // D1
conditions_check &= (DoMemoryRead<u16>(instructions[index].address) != instructions[index].value16); conditions_check &=
(DoMemoryRead<u16>(instructions[index].address) != instructions[index].value16);
break; break;
case InstructionCode::CompareLess16: //D2 case InstructionCode::CompareLess16: // D2
conditions_check &= (DoMemoryRead<u16>(instructions[index].address) < instructions[index].value16); conditions_check &=
(DoMemoryRead<u16>(instructions[index].address) < instructions[index].value16);
break; break;
case InstructionCode::CompareGreater16: //D3 case InstructionCode::CompareGreater16: // D3
conditions_check &= (DoMemoryRead<u16>(instructions[index].address) > instructions[index].value16); conditions_check &=
(DoMemoryRead<u16>(instructions[index].address) > instructions[index].value16);
break; break;
case InstructionCode::CompareEqual8: //E0 case InstructionCode::CompareEqual8: // E0
conditions_check &= (DoMemoryRead<u8>(instructions[index].address) == instructions[index].value8); conditions_check &= (DoMemoryRead<u8>(instructions[index].address) == instructions[index].value8);
break; break;
case InstructionCode::CompareNotEqual8: //E1 case InstructionCode::CompareNotEqual8: // E1
conditions_check &= (DoMemoryRead<u8>(instructions[index].address) != instructions[index].value8); conditions_check &= (DoMemoryRead<u8>(instructions[index].address) != instructions[index].value8);
break; break;
case InstructionCode::CompareLess8: //E2 case InstructionCode::CompareLess8: // E2
conditions_check &= (DoMemoryRead<u8>(instructions[index].address) < instructions[index].value8); conditions_check &= (DoMemoryRead<u8>(instructions[index].address) < instructions[index].value8);
break; break;
case InstructionCode::CompareGreater8: //E3 case InstructionCode::CompareGreater8: // E3
conditions_check &= (DoMemoryRead<u8>(instructions[index].address) > instructions[index].value8); conditions_check &= (DoMemoryRead<u8>(instructions[index].address) > instructions[index].value8);
break; break;
case InstructionCode::ExtCompareEqual32: //A0 case InstructionCode::ExtCompareEqual32: // A0
conditions_check &= (DoMemoryRead<u32>(instructions[index].address) == instructions[index].value32); conditions_check &=
(DoMemoryRead<u32>(instructions[index].address) == instructions[index].value32);
break; break;
case InstructionCode::ExtCompareNotEqual32: //A1 case InstructionCode::ExtCompareNotEqual32: // A1
conditions_check &= (DoMemoryRead<u32>(instructions[index].address) != instructions[index].value32); conditions_check &=
(DoMemoryRead<u32>(instructions[index].address) != instructions[index].value32);
break; break;
case InstructionCode::ExtCompareLess32: //A2 case InstructionCode::ExtCompareLess32: // A2
conditions_check &= (DoMemoryRead<u32>(instructions[index].address) < instructions[index].value32); conditions_check &=
(DoMemoryRead<u32>(instructions[index].address) < instructions[index].value32);
break; break;
case InstructionCode::ExtCompareGreater32: //A3 case InstructionCode::ExtCompareGreater32: // A3
conditions_check &= (DoMemoryRead<u32>(instructions[index].address) > instructions[index].value32); conditions_check &=
(DoMemoryRead<u32>(instructions[index].address) > instructions[index].value32);
break; break;
case InstructionCode::ExtCompareBitsSet8: //E4 Internal to F6 case InstructionCode::ExtCompareBitsSet8: // E4 Internal to F6
conditions_check &= (instructions[index].value8 == (DoMemoryRead<u8>(instructions[index].address) & instructions[index].value8)); conditions_check &=
(instructions[index].value8 ==
(DoMemoryRead<u8>(instructions[index].address) & instructions[index].value8));
break; break;
case InstructionCode::ExtCompareBitsClear8: //E5 Internal to F6 case InstructionCode::ExtCompareBitsClear8: // E5 Internal to F6
conditions_check &= ((DoMemoryRead<u8>(instructions[index].address) & instructions[index].value8) == 0); conditions_check &=
((DoMemoryRead<u8>(instructions[index].address) & instructions[index].value8) == 0);
break; break;
case InstructionCode::ExtBitCompareButtons: // D7 case InstructionCode::ExtBitCompareButtons: // D7
{ {
const u32 frame_compare_value = instructions[index].address & 0xFFFFu; const u32 frame_compare_value = instructions[index].address & 0xFFFFu;
const u8 cht_reg_no = Truncate8((instructions[index].value32 & 0xFF000000u) >>24); const u8 cht_reg_no = Truncate8((instructions[index].value32 & 0xFF000000u) >> 24);
const bool bit_comparison_type = ((instructions[index].address & 0x100000u) >>20); const bool bit_comparison_type = ((instructions[index].address & 0x100000u) >> 20);
const u8 frame_comparison = Truncate8((instructions[index].address & 0xF0000u) >>16); const u8 frame_comparison = Truncate8((instructions[index].address & 0xF0000u) >> 16);
const u32 check_value = (instructions[index].value32 & 0xFFFFFFu); const u32 check_value = (instructions[index].value32 & 0xFFFFFFu);
const u32 value1 = GetControllerButtonBits(); const u32 value1 = GetControllerButtonBits();
const u32 value2 = GetControllerAnalogBits(); const u32 value2 = GetControllerAnalogBits();
u32 value = value1 | value2; u32 value = value1 | value2;
if ((bit_comparison_type == false && check_value == (value & check_value))//Check Bits are set if ((bit_comparison_type == false && check_value == (value & check_value)) // Check Bits are set
|| (bit_comparison_type == true && check_value != (value & check_value))) //Check Bits are clear ||
(bit_comparison_type == true && check_value != (value & check_value))) // Check Bits are clear
{ {
cht_register[cht_reg_no] += 1; cht_register[cht_reg_no] += 1;
switch (frame_comparison) switch (frame_comparison)
@ -1776,61 +1758,73 @@ void CheatCode::Apply() const
{ {
switch (instructions[index].code) switch (instructions[index].code)
{ {
case InstructionCode::CompareEqual16: //D0 case InstructionCode::CompareEqual16: // D0
conditions_check |= (DoMemoryRead<u16>(instructions[index].address) == instructions[index].value16); conditions_check |=
(DoMemoryRead<u16>(instructions[index].address) == instructions[index].value16);
break; break;
case InstructionCode::CompareNotEqual16: //D1 case InstructionCode::CompareNotEqual16: // D1
conditions_check |= (DoMemoryRead<u16>(instructions[index].address) != instructions[index].value16); conditions_check |=
(DoMemoryRead<u16>(instructions[index].address) != instructions[index].value16);
break; break;
case InstructionCode::CompareLess16: //D2 case InstructionCode::CompareLess16: // D2
conditions_check |= (DoMemoryRead<u16>(instructions[index].address) < instructions[index].value16); conditions_check |=
(DoMemoryRead<u16>(instructions[index].address) < instructions[index].value16);
break; break;
case InstructionCode::CompareGreater16: //D3 case InstructionCode::CompareGreater16: // D3
conditions_check |= (DoMemoryRead<u16>(instructions[index].address) > instructions[index].value16); conditions_check |=
(DoMemoryRead<u16>(instructions[index].address) > instructions[index].value16);
break; break;
case InstructionCode::CompareEqual8: //E0 case InstructionCode::CompareEqual8: // E0
conditions_check |= (DoMemoryRead<u8>(instructions[index].address) == instructions[index].value8); conditions_check |= (DoMemoryRead<u8>(instructions[index].address) == instructions[index].value8);
break; break;
case InstructionCode::CompareNotEqual8: //E1 case InstructionCode::CompareNotEqual8: // E1
conditions_check |= (DoMemoryRead<u8>(instructions[index].address) != instructions[index].value8); conditions_check |= (DoMemoryRead<u8>(instructions[index].address) != instructions[index].value8);
break; break;
case InstructionCode::CompareLess8: //E2 case InstructionCode::CompareLess8: // E2
conditions_check |= (DoMemoryRead<u8>(instructions[index].address) < instructions[index].value8); conditions_check |= (DoMemoryRead<u8>(instructions[index].address) < instructions[index].value8);
break; break;
case InstructionCode::CompareGreater8: //E3 case InstructionCode::CompareGreater8: // E3
conditions_check |= (DoMemoryRead<u8>(instructions[index].address) > instructions[index].value8); conditions_check |= (DoMemoryRead<u8>(instructions[index].address) > instructions[index].value8);
break; break;
case InstructionCode::ExtCompareEqual32: //A0 case InstructionCode::ExtCompareEqual32: // A0
conditions_check |= (DoMemoryRead<u32>(instructions[index].address) == instructions[index].value32); conditions_check |=
(DoMemoryRead<u32>(instructions[index].address) == instructions[index].value32);
break; break;
case InstructionCode::ExtCompareNotEqual32: //A1 case InstructionCode::ExtCompareNotEqual32: // A1
conditions_check |= (DoMemoryRead<u32>(instructions[index].address) != instructions[index].value32); conditions_check |=
(DoMemoryRead<u32>(instructions[index].address) != instructions[index].value32);
break; break;
case InstructionCode::ExtCompareLess32: //A2 case InstructionCode::ExtCompareLess32: // A2
conditions_check |= (DoMemoryRead<u32>(instructions[index].address) < instructions[index].value32); conditions_check |=
(DoMemoryRead<u32>(instructions[index].address) < instructions[index].value32);
break; break;
case InstructionCode::ExtCompareGreater32: //A3 case InstructionCode::ExtCompareGreater32: // A3
conditions_check |= (DoMemoryRead<u32>(instructions[index].address) > instructions[index].value32); conditions_check |=
(DoMemoryRead<u32>(instructions[index].address) > instructions[index].value32);
break; break;
case InstructionCode::ExtCompareBitsSet8: //E4 Internal to F6 case InstructionCode::ExtCompareBitsSet8: // E4 Internal to F6
conditions_check |= (instructions[index].value8 == (DoMemoryRead<u8>(instructions[index].address) & instructions[index].value8)); conditions_check |=
(instructions[index].value8 ==
(DoMemoryRead<u8>(instructions[index].address) & instructions[index].value8));
break; break;
case InstructionCode::ExtCompareBitsClear8: //E5 Internal to F6 case InstructionCode::ExtCompareBitsClear8: // E5 Internal to F6
conditions_check |= ((DoMemoryRead<u8>(instructions[index].address) & instructions[index].value8) == 0); conditions_check |=
((DoMemoryRead<u8>(instructions[index].address) & instructions[index].value8) == 0);
break; break;
case InstructionCode::ExtBitCompareButtons: // D7 case InstructionCode::ExtBitCompareButtons: // D7
{ {
const u32 frame_compare_value = instructions[index].address & 0xFFFFu; const u32 frame_compare_value = instructions[index].address & 0xFFFFu;
const u8 cht_reg_no = Truncate8((instructions[index].value32 & 0xFF000000u) >>24); const u8 cht_reg_no = Truncate8((instructions[index].value32 & 0xFF000000u) >> 24);
const bool bit_comparison_type = ((instructions[index].address & 0x100000u) >>20); const bool bit_comparison_type = ((instructions[index].address & 0x100000u) >> 20);
const u8 frame_comparison = Truncate8((instructions[index].address & 0xF0000u) >>16); const u8 frame_comparison = Truncate8((instructions[index].address & 0xF0000u) >> 16);
const u32 check_value = (instructions[index].value32 & 0xFFFFFFu); const u32 check_value = (instructions[index].value32 & 0xFFFFFFu);
const u32 value1 = GetControllerButtonBits(); const u32 value1 = GetControllerButtonBits();
const u32 value2 = GetControllerAnalogBits(); const u32 value2 = GetControllerAnalogBits();
u32 value = value1 | value2; u32 value = value1 | value2;
if ((bit_comparison_type == false && check_value == (value & check_value))//Check Bits are set if ((bit_comparison_type == false && check_value == (value & check_value)) // Check Bits are set
|| (bit_comparison_type == true && check_value != (value & check_value))) //Check Bits are clear ||
(bit_comparison_type == true && check_value != (value & check_value))) // Check Bits are clear
{ {
cht_register[cht_reg_no] += 1; cht_register[cht_reg_no] += 1;
switch (frame_comparison) switch (frame_comparison)
@ -1873,13 +1867,13 @@ void CheatCode::Apply() const
Log_ErrorPrintf("Incomplete multi conditional instruction"); Log_ErrorPrintf("Incomplete multi conditional instruction");
return; return;
} }
if ( conditions_check == true) if (conditions_check == true)
{ {
activate_codes = true; activate_codes = true;
break; break;
} }
else else
{//parse through to 00000000 FFFF and peek if next line is a F6 type associated with a ELSE { // parse through to 00000000 FFFF and peek if next line is a F6 type associated with a ELSE
activate_codes = false; activate_codes = false;
// skip to the next separator (00000000 FFFF), or end // skip to the next separator (00000000 FFFF), or end
constexpr u64 separator_value = UINT64_C(0x000000000000FFFF); constexpr u64 separator_value = UINT64_C(0x000000000000FFFF);
@ -1888,7 +1882,7 @@ void CheatCode::Apply() const
while (index < count) while (index < count)
{ {
const u64 bits = instructions[index++].bits; const u64 bits = instructions[index++].bits;
if (bits == separator_value ) if (bits == separator_value)
{ {
const u64 bits_ahead = instructions[index].bits; const u64 bits_ahead = instructions[index].bits;
if ((bits_ahead & 0xFFFFFF00u) == elseif_value) if ((bits_ahead & 0xFFFFFF00u) == elseif_value)
@ -1897,7 +1891,7 @@ void CheatCode::Apply() const
} }
if ((bits_ahead & 0xFFFF0000u) == else_value) if ((bits_ahead & 0xFFFF0000u) == else_value)
{ {
//index++; // index++;
activate_codes = true; activate_codes = true;
break; break;
} }
@ -1906,12 +1900,12 @@ void CheatCode::Apply() const
} }
if ((bits & 0xFFFFFF00u) == elseif_value) if ((bits & 0xFFFFFF00u) == elseif_value)
{ {
//index--; // index--;
break; break;
} }
if ((bits & 0xFFFFFFFFu) == else_value) if ((bits & 0xFFFFFFFFu) == else_value)
{ {
//index++; // index++;
activate_codes = true; activate_codes = true;
break; break;
} }
@ -1950,18 +1944,18 @@ void CheatCode::Apply() const
index++; index++;
bool activate_codes; bool activate_codes;
const u32 frame_compare_value = inst.address & 0xFFFFu; const u32 frame_compare_value = inst.address & 0xFFFFu;
const u8 cht_reg_no = Truncate8((inst.value32 & 0xFF000000u)>>24); const u8 cht_reg_no = Truncate8((inst.value32 & 0xFF000000u) >> 24);
const bool bit_comparison_type = ((inst.address & 0x100000u) >>20); const bool bit_comparison_type = ((inst.address & 0x100000u) >> 20);
const u8 frame_comparison = Truncate8((inst.address & 0xF0000u) >>16); const u8 frame_comparison = Truncate8((inst.address & 0xF0000u) >> 16);
const u32 check_value = (inst.value32 & 0xFFFFFFu); const u32 check_value = (inst.value32 & 0xFFFFFFu);
const u32 value1 = GetControllerButtonBits(); const u32 value1 = GetControllerButtonBits();
const u32 value2 = GetControllerAnalogBits(); const u32 value2 = GetControllerAnalogBits();
u32 value = value1 | value2; u32 value = value1 | value2;
if ((bit_comparison_type==false && check_value==(value & check_value))//Check Bits are set if ((bit_comparison_type == false && check_value == (value & check_value)) // Check Bits are set
|| (bit_comparison_type==true && check_value!=(value & check_value))) //Check Bits are clear || (bit_comparison_type == true && check_value != (value & check_value))) // Check Bits are clear
{ {
cht_register[cht_reg_no]+=1; cht_register[cht_reg_no] += 1;
switch (frame_comparison) switch (frame_comparison)
{ {
case 0x0: // No comparison on frame count, just do it case 0x0: // No comparison on frame count, just do it
@ -1986,7 +1980,7 @@ void CheatCode::Apply() const
} }
else else
{ {
cht_register[cht_reg_no]=0; cht_register[cht_reg_no] = 0;
activate_codes = false; activate_codes = false;
} }
@ -2008,7 +2002,6 @@ void CheatCode::Apply() const
} }
break; break;
case InstructionCode::ExtCheatRegistersCompare: // 52 case InstructionCode::ExtCheatRegistersCompare: // 52
{ {
index++; index++;
@ -2627,8 +2620,6 @@ void CheatCode::Apply() const
} }
break; break;
case InstructionCode::MemoryCopy: case InstructionCode::MemoryCopy:
{ {
if ((index + 1) >= instructions.size()) if ((index + 1) >= instructions.size())
@ -3076,8 +3067,6 @@ void MemoryScan::Result::UpdateValue(MemoryAccessSize size, bool is_signed)
break; break;
} }
value_changed = (value != old_value); value_changed = (value != old_value);
} }