mirror of
https://github.com/LNH-team/pico-loader.git
synced 2026-06-02 01:06:50 +02:00
Initial work on cheat engine
This commit is contained in:
398
arm9/source/patches/arm7/cheats/CheatEnginePatchCode.s
Normal file
398
arm9/source/patches/arm7/cheats/CheatEnginePatchCode.s
Normal file
@@ -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
|
||||
Reference in New Issue
Block a user