I know this is an older topic (sorry), but just wanted to clarify and figured someone might find this via search.
PPSSPP should detect code modifications (and it worked on the PSP also), but only in certain situations (the way games usually do it.)
Beware the instruction cache. Your change on the PSP will only work if the modified instruction is not in the cache or gets purged out before the next run. The data cache (Dcache) is separate. You *should* use sceKernelIcacheInvalidateRange to write proper code.
You can see here:https://github.com/hrydgard/pspautotests/blob/master/tests/cpu/vfpu/vregs.cpp#L28
(in this case, I'm dynamically generating code for a test run, so I flush the dcache to make sure my write is in RAM, and flush the icache to make sure the new instructions are seen from RAM.)
Even if you can't call sceKernelIcacheInvalidateRange, there's actually a `cache` instruction. Operation 8 on the PSP's processor will zap the icache for a cache line (I assume it's what that syscall does anyway.) `cache 8,0x1234(at)` should do the trick.
If you do this, PPSSPP should detect the modification and everything should be fine.
Without that, PPSSPP still tries to detect code modifications, but not exhaustively. First, it won't detect a partial write (i.e. sb, sh, swl, swr type writes), only a full word write. Second, the write needs to touch the start of the basic block. This is a limitation currently for performance reasons, so like I said it may change (there's a couple games that fall into annoying gaps...)
Just to explain the basic block part, here's some example MIPS we might want to modify:
08004000 12200003 beq s1,zero,0x08004010
08004004 00000000 nop
08004008 3C021234 lui v0,0x1234
0800400C 34445678 ori a0,v0,0x5678
Let's say that we wanted to change this to load 0x1234567C instead of 0x12345678. A tempting way might be to just write the byte at 0800400F, but PPSSPP definitely won't detect that.
But even writing a full word there won't work. You'd have to write to 08004008 as well, because that starts a "basic block". Basically, a new basic block starts after every possible branch, and at every branch target. It's a compiler thing. PPSSPP only checks the first instruction of each basic block.
In practice, if you can't clear cache for some reason (but really, do that please), the next best thing to do is what games typically do: overwrite the entire function. Swapping in a new function will work fine. Replacing the first instruction with a jump to your code will also work.