summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Guillen Fandos2021-05-05 02:20:00 +0200
committerDavid G. F2021-05-05 21:15:27 +0200
commit4fd456e1583a4c8686c8de87c2aeb1eb78125be1 (patch)
treea808a15f40df0f09226fc8e4a620b0f546b48729
parent52088a4d10af9a8c0e95b0eb168d4dfd0a13639f (diff)
downloadpicogpsp-4fd456e1583a4c8686c8de87c2aeb1eb78125be1.tar.gz
picogpsp-4fd456e1583a4c8686c8de87c2aeb1eb78125be1.tar.bz2
picogpsp-4fd456e1583a4c8686c8de87c2aeb1eb78125be1.zip
Adding Code Breaker cheat support
This works on both interpreter and dynarec. Tested in MIPS, ARM and x86, still needs some more testing, some edge cases can be buggy.
-rw-r--r--arm/arm_emit.h8
-rw-r--r--arm/arm_stub.S16
-rw-r--r--cheats.c531
-rw-r--r--cheats.h29
-rw-r--r--cpu.c8
-rw-r--r--cpu_threaded.c10
-rw-r--r--gba_memory.c4
-rw-r--r--libretro.c10
-rw-r--r--main.c4
-rw-r--r--psp/mips_emit.h7
-rw-r--r--psp/mips_stub.S13
-rw-r--r--x86/x86_emit.h6
12 files changed, 283 insertions, 363 deletions
diff --git a/arm/arm_emit.h b/arm/arm_emit.h
index 1432617..4368a80 100644
--- a/arm/arm_emit.h
+++ b/arm/arm_emit.h
@@ -31,6 +31,8 @@ u32 prepare_store_reg(u32 scratch_reg, u32 reg_index);
void generate_load_reg(u32 ireg, u32 reg_index);
void complete_store_reg(u32 scratch_reg, u32 reg_index);
void complete_store_reg_pc_no_flags(u32 scratch_reg, u32 reg_index);
+void thumb_cheat_hook();
+void arm_cheat_hook();
u32 arm_update_gba_arm(u32 pc);
u32 arm_update_gba_thumb(u32 pc);
@@ -1876,6 +1878,12 @@ u32 execute_store_cpsr_body(u32 _cpsr, u32 store_mask, u32 address)
generate_indirect_branch_cycle_update(dual_thumb); \
} \
+#define thumb_process_cheats() \
+ generate_function_call(thumb_cheat_hook);
+
+#define arm_process_cheats() \
+ generate_function_call(arm_cheat_hook);
+
#define thumb_swi() \
generate_swi_hle_handler(opcode & 0xFF, thumb); \
generate_function_call(execute_swi_thumb); \
diff --git a/arm/arm_stub.S b/arm/arm_stub.S
index 944d36a..222bb21 100644
--- a/arm/arm_stub.S
+++ b/arm/arm_stub.S
@@ -288,6 +288,22 @@ arm_update_gba_builder(idle_arm, arm, add)
arm_update_gba_builder(idle_thumb, thumb, add)
+@ Cheat hooks for master function
+@ This is called whenever PC == cheats-master-function
+@ Just calls the C function to process cheats
+
+#define cheat_hook_builder(mode) ;\
+defsymbl(mode##_cheat_hook) ;\
+ save_flags() ;\
+ store_registers_##mode() ;\
+ call_c_function(process_cheats) ;\
+ load_registers_##mode() ;\
+ restore_flags() ;\
+ bx lr ;\
+
+cheat_hook_builder(arm)
+cheat_hook_builder(thumb)
+
@ These are b stubs for performing indirect branches. They are not
@ linked to and don't return, instead they link elsewhere.
diff --git a/cheats.c b/cheats.c
index f3e79e4..1a37081 100644
--- a/cheats.c
+++ b/cheats.c
@@ -19,373 +19,230 @@
#include "common.h"
-cheat_type cheats[MAX_CHEATS];
-u32 num_cheats;
-
-void decrypt_gsa_code(u32 *address_ptr, u32 *value_ptr, cheat_variant_enum
- cheat_variant)
+typedef struct
{
- u32 i;
- u32 address = *address_ptr;
- u32 value = *value_ptr;
- u32 r = 0xc6ef3720;
-
- u32 seeds_v1[4] =
- {
- 0x09f4fbbd, 0x9681884a, 0x352027e9, 0xf3dee5a7
- };
- u32 seeds_v3[4] =
- {
- 0x7aa9648f, 0x7fae6994, 0xc0efaad5, 0x42712c57
- };
- u32 *seeds;
+ bool cheat_active;
+ struct {
+ u32 address;
+ u32 value;
+ } codes[MAX_CHEAT_CODES];
+ unsigned cheat_count;
+} cheat_type;
- if(cheat_variant == CHEAT_TYPE_GAMESHARK_V1)
- seeds = seeds_v1;
- else
- seeds = seeds_v3;
+cheat_type cheats[MAX_CHEATS];
+u32 max_cheat = 0;
+u32 cheat_master_hook = 0xffffffff;
- for(i = 0; i < 32; i++)
+static void update_hook_codebreaker(cheat_type *cheat)
+{
+ int i;
+ for(i = 0; i < cheat->cheat_count; i++)
{
- value -= ((address << 4) + seeds[2]) ^ (address + r) ^
- ((address >> 5) + seeds[3]);
- address -= ((value << 4) + seeds[0]) ^ (value + r) ^
- ((value >> 5) + seeds[1]);
- r -= 0x9e3779b9;
+ u32 code = cheat->codes[i].address;
+ u32 address = code & 0xfffffff;
+ u32 opcode = code >> 28;
+
+ if (opcode == 1)
+ {
+ u32 pcaddr = 0x08000000 | (address & 0x1ffffff);
+ #ifdef HAVE_DYNAREC
+ if (cheat_master_hook != pcaddr)
+ init_caches(); /* Flush caches to install hook */
+ #endif
+ cheat_master_hook = pcaddr;
+ return; /* Only support for one hook */
+ }
}
-
- *address_ptr = address;
- *value_ptr = value;
}
-void add_cheats(char *cheats_filename)
+static void process_cheat_codebreaker(cheat_type *cheat, u16 pad)
{
- FILE *cheats_file;
- char current_line[256];
- char *name_ptr;
- u32 *cheat_code_ptr;
- u32 address, value;
- u32 num_cheat_lines;
- u32 cheat_name_length;
- cheat_variant_enum current_cheat_variant;
-
- num_cheats = 0;
-
- cheats_file = fopen(cheats_filename, "rb");
-
- if(cheats_file)
+ int i;
+ unsigned j;
+ for(i = 0; i < cheat->cheat_count; i++)
{
- while(fgets(current_line, 256, cheats_file))
- {
- // Get the header line first
- name_ptr = strchr(current_line, ' ');
- if(name_ptr)
+ u32 code = cheat->codes[i].address;
+ u16 value = cheat->codes[i].value;
+ u32 address = code & 0xfffffff;
+ u32 opcode = code >> 28;
+
+ switch (opcode) {
+ case 0: /* Game CRC, ignored for now */
+ break;
+ case 1: /* Master code function */
+ break;
+ case 2: /* 16 bit OR */
+ write_memory16(address, read_memory16(address) | value);
+ break;
+ case 3: /* 8 bit write */
+ write_memory8(address, value);
+ break;
+ case 4: /* Slide code, writes a buffer with addr/value strides */
+ if (i + 1 < cheat->cheat_count)
{
- *name_ptr = 0;
- name_ptr++;
+ u16 count = cheat->codes[++i].address;
+ u16 vincr = cheat->codes[ i].address >> 16;
+ u16 aincr = cheat->codes[ i].value;
+ for (j = 0; j < count; j++)
+ {
+ write_memory16(address, value);
+ address += aincr;
+ value += vincr;
+ }
}
-
- if(!strcasecmp(current_line, "gameshark_v1") ||
- !strcasecmp(current_line, "gameshark_v2") ||
- !strcasecmp(current_line, "PAR_v1") ||
- !strcasecmp(current_line, "PAR_v2"))
+ break;
+ case 5: /* Super code: copies bytes to a buffer addr */
+ for (j = 0; j < value * 2 && i < cheat->cheat_count; j++)
{
- current_cheat_variant = CHEAT_TYPE_GAMESHARK_V1;
+ u8 bvalue, off = j % 6;
+ switch (off) {
+ case 0:
+ bvalue = cheat->codes[++i].address >> 24;
+ break;
+ case 1 ... 3:
+ bvalue = cheat->codes[i].address >> (24 - off*8);
+ break;
+ case 4 ... 5:
+ bvalue = cheat->codes[i].address >> (40 - off*8);
+ break;
+ };
+ write_memory8(address, bvalue);
+ address++;
}
- else
-
- if(!strcasecmp(current_line, "gameshark_v3") ||
- !strcasecmp(current_line, "PAR_v3"))
+ break;
+ case 6: /* 16 bit AND */
+ write_memory16(address, read_memory16(address) & value);
+ break;
+ case 7: /* Compare mem value and execute next cheat */
+ if (read_memory16(address) != value)
+ i++;
+ break;
+ case 8: /* 16 bit write */
+ write_memory16(address, value);
+ break;
+ case 10: /* Compare mem value and skip next cheat */
+ if (read_memory16(address) == value)
+ i++;
+ break;
+ case 11: /* Compare mem value and skip next cheat */
+ if (read_memory16(address) <= value)
+ i++;
+ break;
+ case 12: /* Compare mem value and skip next cheat */
+ if (read_memory16(address) >= value)
+ i++;
+ break;
+ case 13: /* Check button state and execute next cheat */
+ switch ((address >> 4) & 0xf) {
+ case 0:
+ if (((~pad) & 0x3ff) == value)
+ i++;
+ break;
+ case 1:
+ if ((pad & value) == value)
+ i++;
+ break;
+ case 2:
+ if ((pad & value) == 0)
+ i++;
+ break;
+ };
+ break;
+ case 14: /* Increase 16/32 bit memory value */
+ if (address & 1)
{
- current_cheat_variant = CHEAT_TYPE_GAMESHARK_V3;
+ u32 value32 = (u32)((s16)value); /* Sign extend to 32 bit */
+ address &= ~1U;
+ write_memory32(address, read_memory32(address) + value32);
}
else
{
- current_cheat_variant = CHEAT_TYPE_INVALID;
- }
-
- if(current_cheat_variant != CHEAT_TYPE_INVALID)
- {
- strncpy(cheats[num_cheats].cheat_name, name_ptr, CHEAT_NAME_LENGTH - 1);
- cheats[num_cheats].cheat_name[CHEAT_NAME_LENGTH - 1] = 0;
- cheat_name_length = strlen(cheats[num_cheats].cheat_name);
- if(cheat_name_length &&
- ((cheats[num_cheats].cheat_name[cheat_name_length - 1] == '\n') ||
- (cheats[num_cheats].cheat_name[cheat_name_length - 1] == '\r')))
- {
- cheats[num_cheats].cheat_name[cheat_name_length - 1] = 0;
- cheat_name_length--;
- }
-
- if(cheat_name_length &&
- cheats[num_cheats].cheat_name[cheat_name_length - 1] == '\r')
- {
- cheats[num_cheats].cheat_name[cheat_name_length - 1] = 0;
- }
-
- cheats[num_cheats].cheat_variant = current_cheat_variant;
- cheat_code_ptr = cheats[num_cheats].cheat_codes;
- num_cheat_lines = 0;
-
- while(fgets(current_line, 256, cheats_file))
- {
- if(strlen(current_line) < 3)
- break;
-
- sscanf(current_line, "%08x %08x", &address, &value);
-
- decrypt_gsa_code(&address, &value, current_cheat_variant);
-
- cheat_code_ptr[0] = address;
- cheat_code_ptr[1] = value;
-
- cheat_code_ptr += 2;
- num_cheat_lines++;
- }
-
- cheats[num_cheats].num_cheat_lines = num_cheat_lines;
-
- num_cheats++;
+ write_memory16(address, read_memory16(address) + value);
}
+ break;
+ case 15: /* Immediate and check and skip */
+ if ((read_memory16(address) & value) == 0)
+ i++;
+ break;
}
-
- fclose(cheats_file);
}
}
-void process_cheat_gs1(cheat_type *cheat)
+void process_cheats(void)
{
- u32 cheat_opcode;
- u32 *code_ptr = cheat->cheat_codes;
- u32 address, value;
- u32 i;
-
- for(i = 0; i < cheat->num_cheat_lines; i++)
- {
- address = code_ptr[0];
- value = code_ptr[1];
-
- code_ptr += 2;
+ u32 i;
- cheat_opcode = address >> 28;
- address &= 0xFFFFFFF;
-
- switch(cheat_opcode)
- {
- case 0x0:
- write_memory8(address, value);
- break;
+ for(i = 0; i <= max_cheat; i++)
+ {
+ if(!cheats[i].cheat_active)
+ continue;
- case 0x1:
- write_memory16(address, value);
- break;
-
- case 0x2:
- write_memory32(address, value);
- break;
-
- case 0x3:
- {
- u32 num_addresses = address & 0xFFFF;
- u32 address1, address2;
- u32 i2;
-
- for(i2 = 0; i2 < num_addresses; i2++)
- {
- address1 = code_ptr[0];
- address2 = code_ptr[1];
- code_ptr += 2;
- i++;
-
- write_memory32(address1, value);
- if(address2 != 0)
- write_memory32(address2, value);
- }
- break;
- }
-
- // ROM patch not supported yet
- case 0x6:
- break;
-
- // GS button down not supported yet
- case 0x8:
- break;
-
- // Reencryption (DEADFACE) not supported yet
- case 0xD:
- if(read_memory16(address) != (value & 0xFFFF))
- {
- code_ptr += 2;
- i++;
- }
- break;
-
- case 0xE:
- if(read_memory16(value & 0xFFFFFFF) != (address & 0xFFFF))
- {
- u32 skip = ((address >> 16) & 0x03);
- code_ptr += skip * 2;
- i += skip;
- }
- break;
-
- // Hook routine not supported yet (not important??)
- case 0x0F:
- break;
- }
- }
+ process_cheat_codebreaker(&cheats[i], 0x3ff ^ io_registers[REG_P1]);
+ }
}
-// These are especially incomplete.
-
-void process_cheat_gs3(cheat_type *cheat)
+void cheat_clear()
{
- u32 cheat_opcode;
- u32 *code_ptr = cheat->cheat_codes;
- u32 address, value;
- u32 i;
-
- for(i = 0; i < cheat->num_cheat_lines; i++)
- {
- address = code_ptr[0];
- value = code_ptr[1];
-
- code_ptr += 2;
-
- cheat_opcode = address >> 28;
- address &= 0xFFFFFFF;
-
- switch(cheat_opcode)
- {
- case 0x0:
- cheat_opcode = address >> 24;
- address = (address & 0xFFFFF) + ((address << 4) & 0xF000000);
-
- switch(cheat_opcode)
- {
- case 0x0:
- {
- u32 iterations = value >> 24;
- u32 i2;
-
- value &= 0xFF;
-
- for(i2 = 0; i2 <= iterations; i2++, address++)
- {
- write_memory8(address, value);
- }
- break;
- }
-
- case 0x2:
- {
- u32 iterations = value >> 16;
- u32 i2;
-
- value &= 0xFFFF;
-
- for(i2 = 0; i2 <= iterations; i2++, address += 2)
- {
- write_memory16(address, value);
- }
- break;
- }
-
- case 0x4:
- write_memory32(address, value);
- break;
- }
- break;
-
- case 0x4:
- cheat_opcode = address >> 24;
- address = (address & 0xFFFFF) + ((address << 4) & 0xF000000);
-
- switch(cheat_opcode)
- {
- case 0x0:
- address = read_memory32(address) + (value >> 24);
- write_memory8(address, value & 0xFF);
- break;
-
- case 0x2:
- address = read_memory32(address) + ((value >> 16) * 2);
- write_memory16(address, value & 0xFFFF);
- break;
-
- case 0x4:
- address = read_memory32(address);
- write_memory32(address, value);
- break;
-
- }
- break;
-
- case 0x8:
- cheat_opcode = address >> 24;
- address = (address & 0xFFFFF) + ((address << 4) & 0xF000000);
-
- switch(cheat_opcode)
- {
- case 0x0:
- value = (value & 0xFF) + read_memory8(address);
- write_memory8(address, value);
- break;
-
- case 0x2:
- value = (value & 0xFFFF) + read_memory16(address);
- write_memory16(address, value);
- break;
-
- case 0x4:
- value = value + read_memory32(address);
- write_memory32(address, value);
- break;
- }
- break;
-
- case 0xC:
- cheat_opcode = address >> 24;
- address = (address & 0xFFFFFF) + 0x4000000;
-
- switch(cheat_opcode)
- {
- case 0x6:
- write_memory16(address, value);
- break;
-
- case 0x7:
- write_memory32(address, value);
- break;
- }
- break;
- }
- }
+ int i;
+ for (i = 0; i < MAX_CHEATS; i++)
+ {
+ cheats[i].cheat_count = 0;
+ cheats[i].cheat_active = false;
+ }
+ cheat_master_hook = 0xffffffff;
}
-
-void process_cheats(void)
+void cheat_parse(unsigned index, const char *code)
{
- u32 i;
-
- for(i = 0; i < num_cheats; i++)
- {
- if(cheats[i].cheat_active)
- {
- switch(cheats[i].cheat_variant)
- {
- case CHEAT_TYPE_GAMESHARK_V1:
- process_cheat_gs1(cheats + i);
- break;
+ int pos = 0;
+ int codelen = strlen(code);
+ cheat_type *ch = &cheats[index];
+ char buf[1024];
+
+ if (index >= MAX_CHEATS)
+ return;
+ if (codelen >= sizeof(buf))
+ return;
+
+ memcpy(buf, code, codelen+1);
+
+ /* Init to a known good state */
+ ch->cheat_count = 0;
+ if (index > max_cheat)
+ max_cheat = index;
+
+ /* Replace all the non-hex chars to spaces */
+ for (pos = 0; pos < codelen; pos++)
+ if (!((buf[pos] >= '0' && buf[pos] <= '9') ||
+ (buf[pos] >= 'a' && buf[pos] <= 'f') ||
+ (buf[pos] >= 'A' && buf[pos] <= 'F')))
+ buf[pos] = ' ';
+
+ /* Try to parse as Code Breaker */
+ pos = 0;
+ while (pos < codelen)
+ {
+ u32 op1; u16 op2;
+ if (2 != sscanf(&buf[pos], "%08x %04hx", &op1, &op2))
+ break;
+ ch->codes[ch->cheat_count].address = op1;
+ ch->codes[ch->cheat_count++].value = op2;
+ pos += 13;
+ while (pos < codelen && buf[pos] == ' ')
+ pos++;
+ if (ch->cheat_count >= MAX_CHEAT_CODES)
+ break;
+ }
+
+ if (pos >= codelen)
+ {
+ /* All codes were parsed! Process hook here */
+ ch->cheat_active = true;
+ update_hook_codebreaker(ch);
+ return;
+ }
+
+ /* TODO parse other types here */
+}
- case CHEAT_TYPE_GAMESHARK_V3:
- process_cheat_gs3(cheats + i);
- break;
- default:
- break;
- }
- }
- }
-}
diff --git a/cheats.h b/cheats.h
index e25ad73..496df15 100644
--- a/cheats.h
+++ b/cheats.h
@@ -17,28 +17,17 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#define CHEAT_NAME_LENGTH 17
+#ifndef __GPSP_CHEATS_H__
+#define __GPSP_CHEATS_H__
-typedef enum
-{
- CHEAT_TYPE_GAMESHARK_V1,
- CHEAT_TYPE_GAMESHARK_V3,
- CHEAT_TYPE_INVALID
-} cheat_variant_enum;
-
-typedef struct
-{
- char cheat_name[CHEAT_NAME_LENGTH];
- u32 cheat_active;
- u32 cheat_codes[256];
- u32 num_cheat_lines;
- cheat_variant_enum cheat_variant;
-} cheat_type;
+#define MAX_CHEATS 20
+#define MAX_CHEAT_CODES 64
void process_cheats(void);
-void add_cheats(char *cheats_filename);
+void cheat_parse(unsigned index, const char *code);
+void cheat_clear();
+
+extern u32 cheat_master_hook;
-#define MAX_CHEATS 16
+#endif
-extern cheat_type cheats[MAX_CHEATS];
-extern u32 num_cheats;
diff --git a/cpu.c b/cpu.c
index badb9c2..5df8bb8 100644
--- a/cpu.c
+++ b/cpu.c
@@ -1679,6 +1679,10 @@ arm_loop:
collapse_flags();
cycles_per_instruction = global_cycles_per_instruction;
+ /* Process cheats if we are about to execute the cheat hook */
+ if (pc == cheat_master_hook)
+ process_cheats();
+
old_pc = pc;
/* Execute ARM instruction */
@@ -3294,6 +3298,10 @@ thumb_loop:
collapse_flags();
+ /* Process cheats if we are about to execute the cheat hook */
+ if (pc == cheat_master_hook)
+ process_cheats();
+
old_pc = pc;
/* Execute THUMB instruction */
diff --git a/cpu_threaded.c b/cpu_threaded.c
index a32b1b8..6874ae0 100644
--- a/cpu_threaded.c
+++ b/cpu_threaded.c
@@ -3303,6 +3303,11 @@ s32 translate_block_arm(u32 pc, translation_region_type
block_data[block_data_position].block_offset = translation_ptr;
arm_base_cycles();
+ if (pc == cheat_master_hook)
+ {
+ arm_process_cheats();
+ }
+
translate_arm_instruction();
block_data_position++;
@@ -3502,6 +3507,11 @@ s32 translate_block_thumb(u32 pc, translation_region_type
block_data[block_data_position].block_offset = translation_ptr;
thumb_base_cycles();
+ if (pc == cheat_master_hook)
+ {
+ thumb_process_cheats();
+ }
+
translate_thumb_instruction();
block_data_position++;
diff --git a/gba_memory.c b/gba_memory.c
index 8d3d39e..74d22c7 100644
--- a/gba_memory.c
+++ b/gba_memory.c
@@ -2380,7 +2380,6 @@ char gamepak_filename[512];
u32 load_gamepak(const struct retro_game_info* info, const char *name)
{
- char cheats_filename[256];
char *p;
s32 file_size = load_gamepak_raw(name);
@@ -2423,9 +2422,6 @@ u32 load_gamepak(const struct retro_game_info* info, const char *name)
if ((load_game_config_over(gamepak_title, gamepak_code, gamepak_maker)) == -1)
load_game_config(gamepak_title, gamepak_code, gamepak_maker);
- change_ext(gamepak_filename, cheats_filename, ".cht");
- add_cheats(cheats_filename);
-
return 0;
}
diff --git a/libretro.c b/libretro.c
index 21ca04f..40aec37 100644
--- a/libretro.c
+++ b/libretro.c
@@ -639,8 +639,16 @@ bool retro_unserialize(const void* data, size_t size)
void retro_cheat_reset(void)
{
+ cheat_clear();
+}
+
+void retro_cheat_set(unsigned index, bool enabled, const char* code)
+{
+ if (!enabled)
+ return;
+
+ cheat_parse(index, code);
}
-void retro_cheat_set(unsigned index, bool enabled, const char* code) {}
static void extract_directory(char* buf, const char* path, size_t size)
{
diff --git a/main.c b/main.c
index 759aa94..260edd5 100644
--- a/main.c
+++ b/main.c
@@ -230,7 +230,9 @@ u32 update_gba(void)
update_gbc_sound(cpu_ticks);
gbc_sound_update = 0;
- process_cheats();
+ /* If there's no cheat hook, run on vblank! */
+ if (cheat_master_hook == ~0U)
+ process_cheats();
vcount = 0;
// We completed a frame, tell the dynarec to exit to the main thread
diff --git a/psp/mips_emit.h b/psp/mips_emit.h
index 12685e8..174fee5 100644
--- a/psp/mips_emit.h
+++ b/psp/mips_emit.h
@@ -44,6 +44,7 @@ void mips_indirect_branch_dual(u32 address);
u32 execute_read_cpsr();
u32 execute_read_spsr();
void execute_swi(u32 pc);
+void mips_cheat_hook();
u32 execute_spsr_restore(u32 address);
void execute_store_cpsr(u32 new_cpsr, u32 store_mask);
@@ -2422,6 +2423,12 @@ u32 execute_store_cpsr_body(u32 _cpsr, u32 store_mask, u32 address)
generate_indirect_branch_cycle_update(dual); \
} \
+#define thumb_process_cheats() \
+ generate_function_call(mips_cheat_hook);
+
+#define arm_process_cheats() \
+ generate_function_call(mips_cheat_hook);
+
#ifdef TRACE_INSTRUCTIONS
void trace_instruction(u32 pc)
{
diff --git a/psp/mips_stub.S b/psp/mips_stub.S
index 08151db..786dc9e 100644
--- a/psp/mips_stub.S
+++ b/psp/mips_stub.S
@@ -44,6 +44,7 @@
.global init_emitter
.global mips_lookup_pc
.global smc_write
+.global mips_cheat_hook
.global write_io_epilogue
.global memory_map_read
@@ -256,6 +257,17 @@ mips_update_gba:
nop
+# Processes cheats whenever we hit the master PC
+mips_cheat_hook:
+ sw $ra, REG_SAVE2($16)
+ save_registers
+ cfncall process_cheats, 8
+ lw $ra, REG_SAVE2($16)
+ restore_registers
+ jr $ra
+ nop
+
+
# Loads the main context and returns to it.
# ARM regs must be saved before branching here
return_to_main:
@@ -649,6 +661,7 @@ fnptrs:
.long set_cpu_mode # 5
.long execute_spsr_restore_body # 6
.long execute_store_cpsr_body # 7
+ .long process_cheats # 8
#if !defined(HAVE_MMAP)
diff --git a/x86/x86_emit.h b/x86/x86_emit.h
index ef79110..45b663b 100644
--- a/x86/x86_emit.h
+++ b/x86/x86_emit.h
@@ -2236,6 +2236,12 @@ static void function_cc execute_swi(u32 pc)
generate_indirect_branch_cycle_update(dual); \
} \
+#define thumb_process_cheats() \
+ generate_function_call(process_cheats);
+
+#define arm_process_cheats() \
+ generate_function_call(process_cheats);
+
#define thumb_swi() \
generate_swi_hle_handler(opcode & 0xFF); \
generate_update_pc((pc + 2)); \