mirror of
https://github.com/LNH-team/pico-loader.git
synced 2026-04-12 22:52:40 +02:00
456 lines
9.5 KiB
ArmAsm
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 |