diff --git a/src/core/cheats.cpp b/src/core/cheats.cpp index 1be10303d..40d69fb3c 100644 --- a/src/core/cheats.cpp +++ b/src/core/cheats.cpp @@ -54,7 +54,10 @@ static T DoMemoryRead(PhysicalMemoryAddress address) if (address < Bus::RAM_MIRROR_END) { - std::memcpy(&result, &Bus::g_ram[address & Bus::g_ram_mask], sizeof(result)); + if (Bus::g_ram != NULL) + std::memcpy(&result, &Bus::g_ram[address & Bus::g_ram_mask], sizeof(result)); + else + return 0; return result; } @@ -986,15 +989,15 @@ static bool IsConditionalInstruction(CheatCode::InstructionCode code) { switch (code) { - case CheatCode::InstructionCode::CompareEqual16: // D0 - case CheatCode::InstructionCode::CompareNotEqual16: // D1 - case CheatCode::InstructionCode::CompareLess16: // D2 - case CheatCode::InstructionCode::CompareGreater16: // D3 - case CheatCode::InstructionCode::CompareEqual8: // E0 - case CheatCode::InstructionCode::CompareNotEqual8: // E1 - case CheatCode::InstructionCode::CompareLess8: // E2 - case CheatCode::InstructionCode::CompareGreater8: // E3 - case CheatCode::InstructionCode::CompareButtons: // D4 + case CheatCode::InstructionCode::CompareEqual16: // D0 + case CheatCode::InstructionCode::CompareNotEqual16: // D1 + case CheatCode::InstructionCode::CompareLess16: // D2 + case CheatCode::InstructionCode::CompareGreater16: // D3 + case CheatCode::InstructionCode::CompareEqual8: // E0 + case CheatCode::InstructionCode::CompareNotEqual8: // E1 + case CheatCode::InstructionCode::CompareLess8: // E2 + case CheatCode::InstructionCode::CompareGreater8: // E3 + case CheatCode::InstructionCode::CompareButtons: // D4 case CheatCode::InstructionCode::ExtCompareEqual32: // A0 case CheatCode::InstructionCode::ExtCompareNotEqual32: // A1 case CheatCode::InstructionCode::ExtCompareLess32: // A2 @@ -1619,6 +1622,7 @@ void CheatCode::Apply() const case InstructionCode::ExtSkipIfNotGreater8: // C4 case InstructionCode::ExtSkipIfNotLess16: // C5 case InstructionCode::ExtSkipIfNotGreater16: // C6 + case InstructionCode::ExtMultiConditionals: // F6 { index++; @@ -1649,6 +1653,275 @@ void CheatCode::Apply() const case InstructionCode::ExtSkipIfNotGreater16: // C6 activate_codes = (DoMemoryRead(inst.address) > inst.value16); break; + case InstructionCode::ExtMultiConditionals: // F6 + { + //Ensure any else if or else that are hit outside the if context are skipped + if ( (inst.value32 & 0xFFFFFF00u) != 0x1F000000) + { + activate_codes = false; + break; + } + for (;;) + { + const u8 totalConds = Truncate8(instructions[index-1].value32 & 0x000000FFu); + const u8 conditionType = Truncate8(instructions[index-1].address & 0x000000FFu); + + bool conditions_check; + + if (conditionType == 0x00 && totalConds > 0) // AND + { + conditions_check = true; + + for (int i = 1; totalConds >= i; index++, i++) + { + switch (instructions[index].code) + { + case InstructionCode::CompareEqual16: //D0 + conditions_check &= (DoMemoryRead(instructions[index].address) == instructions[index].value16); + break; + case InstructionCode::CompareNotEqual16: //D1 + conditions_check &= (DoMemoryRead(instructions[index].address) != instructions[index].value16); + break; + case InstructionCode::CompareLess16: //D2 + conditions_check &= (DoMemoryRead(instructions[index].address) < instructions[index].value16); + break; + case InstructionCode::CompareGreater16: //D3 + conditions_check &= (DoMemoryRead(instructions[index].address) > instructions[index].value16); + break; + case InstructionCode::CompareEqual8: //E0 + conditions_check &= (DoMemoryRead(instructions[index].address) == instructions[index].value8); + break; + case InstructionCode::CompareNotEqual8: //E1 + conditions_check &= (DoMemoryRead(instructions[index].address) != instructions[index].value8); + break; + case InstructionCode::CompareLess8: //E2 + conditions_check &= (DoMemoryRead(instructions[index].address) < instructions[index].value8); + break; + case InstructionCode::CompareGreater8: //E3 + conditions_check &= (DoMemoryRead(instructions[index].address) > instructions[index].value8); + break; + case InstructionCode::ExtCompareEqual32: //A0 + conditions_check &= (DoMemoryRead(instructions[index].address) == instructions[index].value32); + break; + case InstructionCode::ExtCompareNotEqual32: //A1 + conditions_check &= (DoMemoryRead(instructions[index].address) != instructions[index].value32); + break; + case InstructionCode::ExtCompareLess32: //A2 + conditions_check &= (DoMemoryRead(instructions[index].address) < instructions[index].value32); + break; + case InstructionCode::ExtCompareGreater32: //A3 + conditions_check &= (DoMemoryRead(instructions[index].address) > instructions[index].value32); + break; + case InstructionCode::ExtCompareBitsSet8: //E4 Internal to F6 + conditions_check &= (instructions[index].value8 == (DoMemoryRead(instructions[index].address) & instructions[index].value8)); + break; + case InstructionCode::ExtCompareBitsClear8: //E5 Internal to F6 + conditions_check &= ((DoMemoryRead(instructions[index].address) & instructions[index].value8) == 0); + break; + case InstructionCode::ExtBitCompareButtons: // D7 + { + const u32 frame_compare_value = instructions[index].address & 0xFFFFu; + const u8 cht_reg_no = Truncate8((instructions[index].value32 & 0xFF000000u) >>24); + const bool bit_comparison_type = ((instructions[index].address & 0x100000u) >>20); + const u8 frame_comparison = Truncate8((instructions[index].address & 0xF0000u) >>16); + const u32 check_value = (instructions[index].value32 & 0xFFFFFFu); + const u32 value1 = GetControllerButtonBits(); + const u32 value2 = GetControllerAnalogBits(); + u32 value = value1 | value2; + + 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 + { + cht_register[cht_reg_no] += 1; + switch (frame_comparison) + { + case 0x0: // No comparison on frame count, just do it + conditions_check &= true; + break; + case 0x1: // Check if frame_compare_value == current count + conditions_check &= (cht_register[cht_reg_no] == frame_compare_value); + break; + case 0x2: // Check if frame_compare_value < current count + conditions_check &= (cht_register[cht_reg_no] < frame_compare_value); + break; + case 0x3: // Check if frame_compare_value > current count + conditions_check &= (cht_register[cht_reg_no] > frame_compare_value); + break; + case 0x4: // Check if frame_compare_value != current count + conditions_check &= (cht_register[cht_reg_no] != frame_compare_value); + break; + default: + conditions_check &= false; + break; + } + } + else + { + cht_register[cht_reg_no] = 0; + conditions_check &= false; + } + break; + } + default: + Log_ErrorPrintf("Incorrect conditional instruction (see chtdb.txt for supported instructions)"); + return; + } + } + } + else if (conditionType == 0x01 && totalConds > 0) // OR + { + conditions_check = false; + + for (int i = 1; totalConds >= i; index++, i++) + { + switch (instructions[index].code) + { + case InstructionCode::CompareEqual16: //D0 + conditions_check |= (DoMemoryRead(instructions[index].address) == instructions[index].value16); + break; + case InstructionCode::CompareNotEqual16: //D1 + conditions_check |= (DoMemoryRead(instructions[index].address) != instructions[index].value16); + break; + case InstructionCode::CompareLess16: //D2 + conditions_check |= (DoMemoryRead(instructions[index].address) < instructions[index].value16); + break; + case InstructionCode::CompareGreater16: //D3 + conditions_check |= (DoMemoryRead(instructions[index].address) > instructions[index].value16); + break; + case InstructionCode::CompareEqual8: //E0 + conditions_check |= (DoMemoryRead(instructions[index].address) == instructions[index].value8); + break; + case InstructionCode::CompareNotEqual8: //E1 + conditions_check |= (DoMemoryRead(instructions[index].address) != instructions[index].value8); + break; + case InstructionCode::CompareLess8: //E2 + conditions_check |= (DoMemoryRead(instructions[index].address) < instructions[index].value8); + break; + case InstructionCode::CompareGreater8: //E3 + conditions_check |= (DoMemoryRead(instructions[index].address) > instructions[index].value8); + break; + case InstructionCode::ExtCompareEqual32: //A0 + conditions_check |= (DoMemoryRead(instructions[index].address) == instructions[index].value32); + break; + case InstructionCode::ExtCompareNotEqual32: //A1 + conditions_check |= (DoMemoryRead(instructions[index].address) != instructions[index].value32); + break; + case InstructionCode::ExtCompareLess32: //A2 + conditions_check |= (DoMemoryRead(instructions[index].address) < instructions[index].value32); + break; + case InstructionCode::ExtCompareGreater32: //A3 + conditions_check |= (DoMemoryRead(instructions[index].address) > instructions[index].value32); + break; + case InstructionCode::ExtCompareBitsSet8: //E4 Internal to F6 + conditions_check |= (instructions[index].value8 == (DoMemoryRead(instructions[index].address) & instructions[index].value8)); + break; + case InstructionCode::ExtCompareBitsClear8: //E5 Internal to F6 + conditions_check |= ((DoMemoryRead(instructions[index].address) & instructions[index].value8) == 0); + break; + case InstructionCode::ExtBitCompareButtons: // D7 + { + const u32 frame_compare_value = instructions[index].address & 0xFFFFu; + const u8 cht_reg_no = Truncate8((instructions[index].value32 & 0xFF000000u) >>24); + const bool bit_comparison_type = ((instructions[index].address & 0x100000u) >>20); + const u8 frame_comparison = Truncate8((instructions[index].address & 0xF0000u) >>16); + const u32 check_value = (instructions[index].value32 & 0xFFFFFFu); + const u32 value1 = GetControllerButtonBits(); + const u32 value2 = GetControllerAnalogBits(); + u32 value = value1 | value2; + + 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 + { + cht_register[cht_reg_no] += 1; + switch (frame_comparison) + { + case 0x0: // No comparison on frame count, just do it + conditions_check |= true; + break; + case 0x1: // Check if frame_compare_value == current count + conditions_check |= (cht_register[cht_reg_no] == frame_compare_value); + break; + case 0x2: // Check if frame_compare_value < current count + conditions_check |= (cht_register[cht_reg_no] < frame_compare_value); + break; + case 0x3: // Check if frame_compare_value > current count + conditions_check |= (cht_register[cht_reg_no] > frame_compare_value); + break; + case 0x4: // Check if frame_compare_value != current count + conditions_check |= (cht_register[cht_reg_no] != frame_compare_value); + break; + default: + conditions_check |= false; + break; + } + } + else + { + cht_register[cht_reg_no] = 0; + conditions_check |= false; + } + break; + } + default: + Log_ErrorPrintf("Incorrect conditional instruction (see chtdb.txt for supported instructions)"); + return; + } + } + } + else + { + Log_ErrorPrintf("Incomplete multi conditional instruction"); + return; + } + if ( conditions_check == true) + { + activate_codes = true; + break; + } + else + {//parse through to 00000000 FFFF and peek if next line is a F6 type associated with a ELSE + activate_codes = false; + // skip to the next separator (00000000 FFFF), or end + constexpr u64 separator_value = UINT64_C(0x000000000000FFFF); + constexpr u64 else_value = UINT64_C(0x00000000E15E0000); + constexpr u64 elseif_value = UINT64_C(0x00000000E15E1F00); + while (index < count) + { + const u64 bits = instructions[index++].bits; + if (bits == separator_value ) + { + const u64 bits_ahead = instructions[index].bits; + if ((bits_ahead & 0xFFFFFF00u) == elseif_value) + { + break; + } + if ((bits_ahead & 0xFFFF0000u) == else_value) + { + //index++; + activate_codes = true; + break; + } + index--; + break; + } + if ((bits & 0xFFFFFF00u) == elseif_value) + { + //index--; + break; + } + if ((bits & 0xFFFFFFFFu) == else_value) + { + //index++; + activate_codes = true; + break; + } + } + if (activate_codes == true) + break; + } + } + break; + } default: activate_codes = false; break; @@ -2465,6 +2738,7 @@ void CheatCode::ApplyOnDisable() const case InstructionCode::ExtSkipIfNotGreater8: // C4 case InstructionCode::ExtSkipIfNotLess16: // C5 case InstructionCode::ExtSkipIfNotGreater16: // C6 + case InstructionCode::ExtMultiConditionals: // F6 case InstructionCode::ExtCheatRegistersCompare: // 52 index++; break; @@ -2802,6 +3076,8 @@ void MemoryScan::Result::UpdateValue(MemoryAccessSize size, bool is_signed) break; } + + value_changed = (value != old_value); } diff --git a/src/core/cheats.h b/src/core/cheats.h index f1963d176..30f87c1d4 100644 --- a/src/core/cheats.h +++ b/src/core/cheats.h @@ -78,9 +78,13 @@ struct CheatCode ExtSkipIfNotGreater8 = 0xC4, ExtSkipIfNotLess16 = 0xC5, ExtSkipIfNotGreater16 = 0xC6, + ExtMultiConditionals = 0xF6, ExtCheatRegisters = 0x51, ExtCheatRegistersCompare = 0x52, + + ExtCompareBitsSet8 = 0xE4, //Only used inside ExtMultiConditionals + ExtCompareBitsClear8 = 0xE5, //Only used inside ExtMultiConditionals }; union Instruction