New F6 Cheat Type (IF,ELSE IF, ELSE structure)

Cheat Type F6 lets you perform cheats in IF / ELSE IF / ELSE constructs along 
with multiple conditions for each one.

It supports conditions  D0, D1, D2, D3, E0, E1, E2, E3, A0, A1, A2, A3, D7 and 
two new types that will only be available in the F6 conditions :- 
E4 & E5 which are bit comparisons on bytes (set and clear). 

Any of those types which are "block conditionals" like D7 will be treated as a 
single condition with the other conditions so you wont need to worry about 
extra 00000000 FFFFs just for them (infact you can drop them completely apart
from the last 00000000 FFFF, which you can also drop if you have no trailing
cheat lines after the end of the construct.


#IF / ELSE IF / ELSE Statement Example with Notes
80001000 00000001   Setup Test Condition 1
80001004 00000002   Setup Test Condition 2
80001008 00000003   Setup Test Condition 3
8000100C 00000004   Setup Test Condition 4

                    1F = IF, last two digits determine the number of conditions to check
                    F600000x  x = 0 All conditions must be true, x = 1 At least One condition must be true                  
F6000000 1F000004   Check the next 4 lines as the conditions for the IF loop to be taken

D0001000 00000001  )
D0001004 00000002  )Conditions
D0001008 00000003  )
D000100C 00000004  )

80001010 00005555    Poke(s)
00000000 0000FFFF  End of IF Statement

F6000000 E15E1F02  E15E1F = ELSE IF (same format as IF) - Can have multiple ELSE IF Blocks or none at all
D0001008 00000003  )
D000100C 00000005  )Conditions
80001010 00008888     Poke(s)
00000000 0000FFFF  End of ELSE IF Statement 

F6000000 E15E0000  E15E = ELSE (no need for conditions - this will poke if the others dont)
80001010 0000AAAA    Poke(s)
00000000 0000FFFF End of ELSE Statement



#Multiple Test Using Joypad Left Thumb Directions, Look at Address 1100 in Debugger
F6000000 1F000001  
D7020002 00100000
90001100 50205055
90001104 53534552 
90001108 20204445
9000110C 20202020
F6000000 E15E1F01
D7020002 00400000
90001100 4E574F44
90001104 45525020
90001108 44455353
9000110C 20202020
F6000000 E15E1F01
D7020002 00800000
90001100 5446454C
90001104 45525020
90001108 44455353
9000110C 20202020
F6000000 E15E1F01
D7020002 00200000
90001100 48474952
90001104 52502054
90001108 45535345
9000110C 20202044
F6000000 E15E0000
90001100 20202020
90001104 20202020
90001108 20202020
9000110C 20202020
00000000 0000FFFF
This commit is contained in:
PugsyMAME 2021-08-21 15:08:34 +01:00 committed by GitHub
parent abe9c0d438
commit 0ebb791323
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 290 additions and 10 deletions

View File

@ -54,7 +54,10 @@ static T DoMemoryRead(PhysicalMemoryAddress address)
if (address < Bus::RAM_MIRROR_END) 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; return result;
} }
@ -986,15 +989,15 @@ static bool IsConditionalInstruction(CheatCode::InstructionCode code)
{ {
switch (code) switch (code)
{ {
case CheatCode::InstructionCode::CompareEqual16: // D0 case CheatCode::InstructionCode::CompareEqual16: // D0
case CheatCode::InstructionCode::CompareNotEqual16: // D1 case CheatCode::InstructionCode::CompareNotEqual16: // D1
case CheatCode::InstructionCode::CompareLess16: // D2 case CheatCode::InstructionCode::CompareLess16: // D2
case CheatCode::InstructionCode::CompareGreater16: // D3 case CheatCode::InstructionCode::CompareGreater16: // D3
case CheatCode::InstructionCode::CompareEqual8: // E0 case CheatCode::InstructionCode::CompareEqual8: // E0
case CheatCode::InstructionCode::CompareNotEqual8: // E1 case CheatCode::InstructionCode::CompareNotEqual8: // E1
case CheatCode::InstructionCode::CompareLess8: // E2 case CheatCode::InstructionCode::CompareLess8: // E2
case CheatCode::InstructionCode::CompareGreater8: // E3 case CheatCode::InstructionCode::CompareGreater8: // E3
case CheatCode::InstructionCode::CompareButtons: // D4 case CheatCode::InstructionCode::CompareButtons: // D4
case CheatCode::InstructionCode::ExtCompareEqual32: // A0 case CheatCode::InstructionCode::ExtCompareEqual32: // A0
case CheatCode::InstructionCode::ExtCompareNotEqual32: // A1 case CheatCode::InstructionCode::ExtCompareNotEqual32: // A1
case CheatCode::InstructionCode::ExtCompareLess32: // A2 case CheatCode::InstructionCode::ExtCompareLess32: // A2
@ -1619,6 +1622,7 @@ void CheatCode::Apply() const
case InstructionCode::ExtSkipIfNotGreater8: // C4 case InstructionCode::ExtSkipIfNotGreater8: // C4
case InstructionCode::ExtSkipIfNotLess16: // C5 case InstructionCode::ExtSkipIfNotLess16: // C5
case InstructionCode::ExtSkipIfNotGreater16: // C6 case InstructionCode::ExtSkipIfNotGreater16: // C6
case InstructionCode::ExtMultiConditionals: // F6
{ {
index++; index++;
@ -1649,6 +1653,275 @@ void CheatCode::Apply() const
case InstructionCode::ExtSkipIfNotGreater16: // C6 case InstructionCode::ExtSkipIfNotGreater16: // C6
activate_codes = (DoMemoryRead<u16>(inst.address) > inst.value16); activate_codes = (DoMemoryRead<u16>(inst.address) > inst.value16);
break; 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<u16>(instructions[index].address) == instructions[index].value16);
break;
case InstructionCode::CompareNotEqual16: //D1
conditions_check &= (DoMemoryRead<u16>(instructions[index].address) != instructions[index].value16);
break;
case InstructionCode::CompareLess16: //D2
conditions_check &= (DoMemoryRead<u16>(instructions[index].address) < instructions[index].value16);
break;
case InstructionCode::CompareGreater16: //D3
conditions_check &= (DoMemoryRead<u16>(instructions[index].address) > instructions[index].value16);
break;
case InstructionCode::CompareEqual8: //E0
conditions_check &= (DoMemoryRead<u8>(instructions[index].address) == instructions[index].value8);
break;
case InstructionCode::CompareNotEqual8: //E1
conditions_check &= (DoMemoryRead<u8>(instructions[index].address) != instructions[index].value8);
break;
case InstructionCode::CompareLess8: //E2
conditions_check &= (DoMemoryRead<u8>(instructions[index].address) < instructions[index].value8);
break;
case InstructionCode::CompareGreater8: //E3
conditions_check &= (DoMemoryRead<u8>(instructions[index].address) > instructions[index].value8);
break;
case InstructionCode::ExtCompareEqual32: //A0
conditions_check &= (DoMemoryRead<u32>(instructions[index].address) == instructions[index].value32);
break;
case InstructionCode::ExtCompareNotEqual32: //A1
conditions_check &= (DoMemoryRead<u32>(instructions[index].address) != instructions[index].value32);
break;
case InstructionCode::ExtCompareLess32: //A2
conditions_check &= (DoMemoryRead<u32>(instructions[index].address) < instructions[index].value32);
break;
case InstructionCode::ExtCompareGreater32: //A3
conditions_check &= (DoMemoryRead<u32>(instructions[index].address) > instructions[index].value32);
break;
case InstructionCode::ExtCompareBitsSet8: //E4 Internal to F6
conditions_check &= (instructions[index].value8 == (DoMemoryRead<u8>(instructions[index].address) & instructions[index].value8));
break;
case InstructionCode::ExtCompareBitsClear8: //E5 Internal to F6
conditions_check &= ((DoMemoryRead<u8>(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<u16>(instructions[index].address) == instructions[index].value16);
break;
case InstructionCode::CompareNotEqual16: //D1
conditions_check |= (DoMemoryRead<u16>(instructions[index].address) != instructions[index].value16);
break;
case InstructionCode::CompareLess16: //D2
conditions_check |= (DoMemoryRead<u16>(instructions[index].address) < instructions[index].value16);
break;
case InstructionCode::CompareGreater16: //D3
conditions_check |= (DoMemoryRead<u16>(instructions[index].address) > instructions[index].value16);
break;
case InstructionCode::CompareEqual8: //E0
conditions_check |= (DoMemoryRead<u8>(instructions[index].address) == instructions[index].value8);
break;
case InstructionCode::CompareNotEqual8: //E1
conditions_check |= (DoMemoryRead<u8>(instructions[index].address) != instructions[index].value8);
break;
case InstructionCode::CompareLess8: //E2
conditions_check |= (DoMemoryRead<u8>(instructions[index].address) < instructions[index].value8);
break;
case InstructionCode::CompareGreater8: //E3
conditions_check |= (DoMemoryRead<u8>(instructions[index].address) > instructions[index].value8);
break;
case InstructionCode::ExtCompareEqual32: //A0
conditions_check |= (DoMemoryRead<u32>(instructions[index].address) == instructions[index].value32);
break;
case InstructionCode::ExtCompareNotEqual32: //A1
conditions_check |= (DoMemoryRead<u32>(instructions[index].address) != instructions[index].value32);
break;
case InstructionCode::ExtCompareLess32: //A2
conditions_check |= (DoMemoryRead<u32>(instructions[index].address) < instructions[index].value32);
break;
case InstructionCode::ExtCompareGreater32: //A3
conditions_check |= (DoMemoryRead<u32>(instructions[index].address) > instructions[index].value32);
break;
case InstructionCode::ExtCompareBitsSet8: //E4 Internal to F6
conditions_check |= (instructions[index].value8 == (DoMemoryRead<u8>(instructions[index].address) & instructions[index].value8));
break;
case InstructionCode::ExtCompareBitsClear8: //E5 Internal to F6
conditions_check |= ((DoMemoryRead<u8>(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: default:
activate_codes = false; activate_codes = false;
break; break;
@ -2465,6 +2738,7 @@ void CheatCode::ApplyOnDisable() const
case InstructionCode::ExtSkipIfNotGreater8: // C4 case InstructionCode::ExtSkipIfNotGreater8: // C4
case InstructionCode::ExtSkipIfNotLess16: // C5 case InstructionCode::ExtSkipIfNotLess16: // C5
case InstructionCode::ExtSkipIfNotGreater16: // C6 case InstructionCode::ExtSkipIfNotGreater16: // C6
case InstructionCode::ExtMultiConditionals: // F6
case InstructionCode::ExtCheatRegistersCompare: // 52 case InstructionCode::ExtCheatRegistersCompare: // 52
index++; index++;
break; break;
@ -2802,6 +3076,8 @@ void MemoryScan::Result::UpdateValue(MemoryAccessSize size, bool is_signed)
break; break;
} }
value_changed = (value != old_value); value_changed = (value != old_value);
} }

View File

@ -78,9 +78,13 @@ struct CheatCode
ExtSkipIfNotGreater8 = 0xC4, ExtSkipIfNotGreater8 = 0xC4,
ExtSkipIfNotLess16 = 0xC5, ExtSkipIfNotLess16 = 0xC5,
ExtSkipIfNotGreater16 = 0xC6, ExtSkipIfNotGreater16 = 0xC6,
ExtMultiConditionals = 0xF6,
ExtCheatRegisters = 0x51, ExtCheatRegisters = 0x51,
ExtCheatRegistersCompare = 0x52, ExtCheatRegistersCompare = 0x52,
ExtCompareBitsSet8 = 0xE4, //Only used inside ExtMultiConditionals
ExtCompareBitsClear8 = 0xE5, //Only used inside ExtMultiConditionals
}; };
union Instruction union Instruction