Files
pico-loader/arm9/source/patches/arm7/cheats/CheatEnginePatchCode.s

456 lines
9.5 KiB
ArmAsm

.cpu arm7tdmi
.section "patch_cheatengine", "ax"
.syntax unified
// 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
.arm
.global cheatengine_entry_arm
.type cheatengine_entry_arm, %function
cheatengine_entry_arm:
adr r12, cheatengine_entry + 1
bx r12
.thumb
.global cheatengine_entry_thumb_replace
.type cheatengine_entry_thumb_replace, %function
cheatengine_entry_thumb_replace:
push {r4, r5, lr} // r5 is a dummy
ldr r0, [r0]
adr r4, cheatengine_entry_thumb_replace_return
cmp r0, #0
mov pc, r1
.balign 4
cheatengine_entry_thumb_replace_return:
pop {r4}
nop
.global cheatengine_entry
.type cheatengine_entry, %function
cheatengine_entry:
pop {r0, r1}
mov lr, r1
push {r4, r5, lr}
// increment 16-bit counter for C5
adr r1, c5counter
ldrh r0, [r1]
adds r0, #1
strh r0, [r1]
ldr r4, cheatengine_cheatsPtr
ldr r5, [r4, #4] // pload_cheats_t::numberOfCheats
adds r4, #8
entry_cheats_loop:
subs r5, #1
bmi entry_end
movs r0, r4
bl cheatengine_runCheat
ldmia r4!, {r0} // r0 = length of cheat in bytes (pload_cheat_t::length)
adds r4, r0
b entry_cheats_loop
entry_end:
pop {r4, r5}
pop {r3}
bx r3
runCheat_end:
pop {r4, r5, r6} // r8, r9, r10
mov r8, r4
mov r9, r5
mov r10, r6
pop {r4, r5, r6, r7, pc}
cheatengine_runCheat:
push {r4, r5, r6, r7, lr}
mov r4, r8
mov r5, r9
mov r6, r10
push {r4, r5, r6} // r8, r9, r10
ldmia r0!, {r1} // 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
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
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 E 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: // IF (count & b.l) == b.h
ldr r3, c5counter
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
cmp r3, #0xD
bhs opcode_DX_invalid
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)
opcode_D0: // ENDIF
lsrs r7, r7, #1
opcode_DX_invalid:
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
lsls r1, r1, #24
lsrs r1, r1, #22
cmp r1, #(9 << 2) // if data op >= 9, ignore
bhs opcode_D4_invalid
add pc, r1
nop
opcode_D4_0: // datareg += b
adds r6, r2
opcode_D4_invalid:
b runCheat_opcode_loop
opcode_D4_1: // datareg |= b
orrs r6, r2
b runCheat_opcode_loop
opcode_D4_2: // datareg &= b
ands r6, r2
b runCheat_opcode_loop
opcode_D4_3: // datareg ^= b
eors r6, r2
b runCheat_opcode_loop
opcode_D4_4: // datareg >>= b
lsls r6, r6, r2
b runCheat_opcode_loop
opcode_D4_5: // datareg <<= b
lsrs r6, r6, r2
b runCheat_opcode_loop
opcode_D4_6: // datareg = ROR(datareg, b)
rors r6, r2
b runCheat_opcode_loop
opcode_D4_7: // (s32)datareg >>= b
asrs r6, r6, r2
b runCheat_opcode_loop
opcode_D4_8: // datareg *= b
muls r6, r2
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
b runCheat_opcode_loop
opcode_EX: // copy b param bytes to address a+offset
push {r0}
// pad length to multiple of 8
adds r3, r2, #7
lsrs r3, r3, #3
lsls r3, r3, #3
adds r0, r3
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
blo FX_byte_loop
ldmia r3!, {r0}
stmia r1!, {r0}
subs r2, #4
b FX_word_loop
FX_byte_loop:
cmp r2, #1
blo 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
c5counter:
.word 0
.global cheatengine_cheatsPtr
cheatengine_cheatsPtr:
.word 0
.pool
.end