diff --git a/arm9/source/patches/arm7/cheats/CheatEnginePatchCode.s b/arm9/source/patches/arm7/cheats/CheatEnginePatchCode.s new file mode 100644 index 0000000..e993469 --- /dev/null +++ b/arm9/source/patches/arm7/cheats/CheatEnginePatchCode.s @@ -0,0 +1,398 @@ +.cpu arm7tdmi +.section "patch_cheatengine", "ax" +.syntax unified +.thumb + +// For reference on how Action Replay codes work for the DS +// - https://problemkaputt.de/gbatek-ds-cart-cheat-action-replay-ds.htm +// - https://github.com/melonDS-emu/melonDS/blob/master/src/AREngine.cpp + +.global cheatengine_entry +.type cheatengine_entry, %function +cheatengine_entry: + push {r4, lr} + ldr r4, cheatengine_cheatsPtr +entry_cheats_loop: + ldmia r4!, {r0} // r0 = cheat pointer + cmp r0, #0 + beq entry_end + bl cheatengine_runCheat + b entry_cheats_loop +entry_end: + pop {r4} + pop {r3} + bx r3 + +runCheat_end: + pop {r4, r5, r6, r7} // r8, r9, r10, r11 + mov r8, r4 + mov r9, r5 + mov r10, r6 + mov r11, r7 + pop {r4, r5, r6, r7, pc} + +cheatengine_runCheat: + push {r4, r5, r6, r7, lr} + mov r4, r8 + mov r5, r9 + mov r6, r10 + mov r7, r11 + push {r4, r5, r6, r7} // r8, r9, r10, r11 + ldr r1, [r0] // r1 = length of cheat code + adds r1, r0 + mov r8, r1 // r8 = end of cheat code + mov r9, r0 // r9 = loop start + movs r4, #0 // r4 = loop counter + movs r5, #0 // r5 = offset register + movs r6, #0 // r6 = data register + movs r7, #1 // r7 = condition stack + mov r10, r7 // r10 = loop condition stack backup + mov r11, r6 // r11 = c5count +runCheat_opcode_loop: + cmp r0, r8 + bhs runCheat_end + + ldmia r0!, {r1, r2} // r1, r2 = cheat code a, b + + // == condition check == + + lsrs r3, r1, #24 // r3 = op + cmp r3, #0xC5 // C5 has a special condition check + beq 1f + subs r3, #0xD0 + cmp r3, #2 + bls 1f // D0 - D2 are not condition checked + + lsrs r3, r7, #1 // check bottom bit of condition stack + bcs 1f // if set, the opcode is executed + + lsrs r3, r1, #24 // r3 = op + cmp r3, #0xE0 + bne runCheat_opcode_loop + + // opcode E0 has a dynamic length + adds r2, #7 + movs r3, #7 + bics r2, r3 // pad length to multiple of 8 + adds r0, r2 + + b runCheat_opcode_loop + +1: + // == execute the opcode == + lsrs r3, r1, #28 // r3 = top 4 bits of op + lsls r1, r1, #4 + lsrs r1, r1, #4 + lsls r3, r3, #1 + add r3, pc + ldrh r3, [r3, #2] + add pc, r3 + +top_4_bits_table: + .short (opcode_0X - top_4_bits_table - 2) + .short (opcode_1X - top_4_bits_table - 2) + .short (opcode_2X - top_4_bits_table - 2) + .short (opcode_3X - top_4_bits_table - 2) + .short (opcode_4X - top_4_bits_table - 2) + .short (opcode_5X - top_4_bits_table - 2) + .short (opcode_6X - top_4_bits_table - 2) + .short (opcode_7X - top_4_bits_table - 2) + .short (opcode_8X - top_4_bits_table - 2) + .short (opcode_9X - top_4_bits_table - 2) + .short (opcode_AX - top_4_bits_table - 2) + .short (opcode_BX - top_4_bits_table - 2) + .short (opcode_CX - top_4_bits_table - 2) + .short (opcode_DX - top_4_bits_table - 2) + .short (opcode_EX - top_4_bits_table - 2) + .short (opcode_FX - top_4_bits_table - 2) + +opcode_0X: // 32-bit write + str r2, [r1, r5] + b runCheat_opcode_loop + +opcode_1X: // 16-bit write + strh r2, [r1, r5] + b runCheat_opcode_loop + +opcode_2X: // 8-bit write + strb r2, [r1, r5] + b runCheat_opcode_loop + +opcode_3X: // IF b > u32[a] + bl prepare_opcode_3X_to_6X + bls runCheat_opcode_loop + adds r7, #1 + b runCheat_opcode_loop + +opcode_4X: // IF b < u32[a] + bl prepare_opcode_3X_to_6X + bhs runCheat_opcode_loop + adds r7, #1 + b runCheat_opcode_loop + +opcode_5X: // IF b == u32[a] + bl prepare_opcode_3X_to_6X + bne runCheat_opcode_loop + adds r7, #1 + b runCheat_opcode_loop + +opcode_6X: // IF b != u32[a] + bl prepare_opcode_3X_to_6X + beq runCheat_opcode_loop + adds r7, #1 + b runCheat_opcode_loop + +prepare_opcode_3X_to_6X: + cmp r1, #0 + bne 1f + movs r1, r5 +1: + ldr r1, [r1] + lsls r7, r7, #1 + cmp r2, r1 + bx lr + +opcode_7X: // IF b.l > ((~b.h) & u16[a]) + bl prepare_opcode_7X_to_AX + bls runCheat_opcode_loop + adds r7, #1 + b runCheat_opcode_loop + +opcode_8X: // IF b.l < ((~b.h) & u16[a]) + bl prepare_opcode_7X_to_AX + bhs runCheat_opcode_loop + adds r7, #1 + b runCheat_opcode_loop + +opcode_9X: // IF b.l == ((~b.h) & u16[a]) + bl prepare_opcode_7X_to_AX + bne runCheat_opcode_loop + adds r7, #1 + b runCheat_opcode_loop + +opcode_AX: // IF b.l != ((~b.h) & u16[a]) + bl prepare_opcode_7X_to_AX + beq runCheat_opcode_loop + adds r7, #1 + b runCheat_opcode_loop + +prepare_opcode_7X_to_AX: + cmp r1, #0 + bne 1f + movs r1, r5 +1: + ldrh r1, [r1] + lsls r7, r7, #1 + lsrs r3, r2, #16 + bics r1, r3 + lsls r2, r2, #16 + lsrs r2, r2, #16 + cmp r2, r1 + bx lr + +opcode_BX: // offset = u32[a + offset] + ldr r5, [r1, r5] + b runCheat_opcode_loop + +opcode_CX: + lsrs r3, r1, #24 // r3 = op + cmp r3, #0 + beq opcode_C0 + cmp r3, #4 + beq opcode_C4 + cmp r3, #5 + beq opcode_C5 + cmp r3, #6 + beq opcode_C6 + b runCheat_opcode_loop + +opcode_C0: // FOR 0..b + movs r4, r2 // loop count + mov r9, r0 // loop start + mov r10, r7 // condition stack backup + b runCheat_opcode_loop + +opcode_C4: // offset = pointer to C4000000 opcode + movs r5, r0 + subs r5, #8 + b runCheat_opcode_loop + +opcode_C5: // count++ / IF (count & b.l) == b.h + // c5count++ + mov r3, r11 + adds r3, #1 + mov r11, r3 + + // condition check for C5 + lsrs r1, r7, #1 + bcc 1f + + lsls r7, r7, #1 + + lsrs r1, r2, #16 // chk + + lsls r2, r2, #16 + lsrs r2, r2, #16 // mask + ands r3, r2 + + cmp r3, r1 + bne 1f + + adds r7, #1 + +1: + b runCheat_opcode_loop + +opcode_C6: // u32[b] = offset + str r5, [r2] + b runCheat_opcode_loop + +opcode_DX: + lsrs r3, r1, #24 + lsls r3, r3, #1 + add r3, pc + ldrh r3, [r3, #2] + add pc, r3 + +DX_table: + .short (opcode_D0 - DX_table - 2) + .short (opcode_D1 - DX_table - 2) + .short (opcode_D2 - DX_table - 2) + .short (opcode_D3 - DX_table - 2) + .short (opcode_D4 - DX_table - 2) + .short (opcode_D5 - DX_table - 2) + .short (opcode_D6 - DX_table - 2) + .short (opcode_D7 - DX_table - 2) + .short (opcode_D8 - DX_table - 2) + .short (opcode_D9 - DX_table - 2) + .short (opcode_DA - DX_table - 2) + .short (opcode_DB - DX_table - 2) + .short (opcode_DC - DX_table - 2) + .short (opcode_DD - DX_table - 2) + .short (opcode_DE - DX_table - 2) + .short (opcode_DF - DX_table - 2) + +opcode_D0: // ENDIF + lsrs r7, r7, #1 + b runCheat_opcode_loop + +opcode_D1: // NEXT + cmp r4, #0 + ble 1f + + subs r4, #1 + mov r0, r9 // jump to loop start + b runCheat_opcode_loop + +1: + mov r7, r10 // restore condition stack backup + b runCheat_opcode_loop + +opcode_D2: // NEXT+FLUSH + cmp r4, #0 + ble 1f + + subs r4, #1 + mov r0, r9 // jump to loop start + b runCheat_opcode_loop + +1: + movs r5, #0 + movs r6, #0 + movs r7, #1 + b runCheat_opcode_loop + +opcode_D3: // offset = b + movs r5, r2 + b runCheat_opcode_loop + +opcode_D4: // data op + b runCheat_opcode_loop + +opcode_D5: // datareg = b + movs r6, r2 + b runCheat_opcode_loop + +opcode_D6: // u32[b+offset] = datareg / offset += 4 + str r6, [r2, r5] + adds r5, #4 + b runCheat_opcode_loop + +opcode_D7: // u16[b+offset] = datareg / offset += 2 + strh r6, [r2, r5] + adds r5, #2 + b runCheat_opcode_loop + +opcode_D8: // u8[b+offset] = datareg / offset += 1 + strb r6, [r2, r5] + adds r5, #1 + b runCheat_opcode_loop + +opcode_D9: // datareg = u32[b+offset] + ldr r6, [r2, r5] + b runCheat_opcode_loop + +opcode_DA: // datareg = u16[b+offset] + ldrh r6, [r2, r5] + b runCheat_opcode_loop + +opcode_DB: // datareg = u8[b+offset] + ldrb r6, [r2, r5] + b runCheat_opcode_loop + +opcode_DC: // offset += b + adds r5, r2 + // fall through to b runCheat_opcode_loop + +opcode_DD: +opcode_DE: +opcode_DF: + b runCheat_opcode_loop + +opcode_EX: // copy b param bytes to address a+offset + push {r0} + + adds r0, r2 + adds r0, #7 + movs r3, #7 + bics r0, r3 // pad length to multiple of 8 + + pop {r3} + push {r0} + adds r1, r5 + b FX_word_loop + +opcode_FX: // copy b bytes from address offset to address a + push {r0} + movs r3, r5 +FX_word_loop: + cmp r2, #4 + bls FX_byte_loop + ldmia r3!, {r0} + stmia r1!, {r0} + subs r2, #4 + b FX_word_loop + +FX_byte_loop: + cmp r2, #1 + bls FX_end + ldrb r0, [r3] + adds r3, #1 + strb r0, [r1] + adds r1, #1 + subs r2, #1 + b FX_byte_loop + +FX_end: + pop {r0} + b runCheat_opcode_loop + +.balign 4 + +// Pointer to list of pointers to cheat codes. Last pointer should be nullptr to terminate the list. +.global cheatengine_cheatsPtr +cheatengine_cheatsPtr: + .word 0 + +.pool +.end \ No newline at end of file