I have had this same problem in the past. If I send a C2 ASM code, it works the first time, but if I try sending any other codes afterwards, while that same C2 code was active, then it froze. Also, when I modified the ASM code, recompiled it and re-sent it, it froze the game.
What I had to do to be able to modify the C2 ASM code and re-send it was go back to the disassembler and restore the original instruction to the ASM address, then I could send it again... Which was tedious.
The tedious process you describe is automated using GCT Code Undo. A line of the format "##undo_addr undo_val" will poke undo_addr with undo_val when codes are disabled and/or sent again. For instance, I have a code for RT4EAF which unlocks characters for leveling. It hooks
8009F828: 80010024 lwz r0,36(r1) and it's stored in my GCT tab as the following.
##
8009F828 80010024C209F828 00000003
2C030000 4082000C
38600001 7C7FF1AE
80010024 00000000
To assist with creating the undo line, I added a keyboard shortcut. Double click the C2 word of the code, so that e.g. C209F828 is highlighted. Then press ctrl+u and it will automatically read the word at the C2 address with a ba=80000000 and insert the appropriate undo line. You can also ctrl+d or ctrl+m to get to the disassembler/memview for the selected code word, again with ba=80000000.
---
Okay, so here's the deal with the C2 code bugs. An unpatched debugger does not automatically execute codes after they are uploaded to the code list. Immediately after codes are uploaded, it goes back to the game loop for one frame. I call this the "virgin frame" because the C2 code is a "virgin" code - the "back-branch"/very last word of the C2 has not been written to so it is still 00000000.
When a C2 code is executed, a branch over-writes the hook address,
and the back-branch of the C2 is written to branch to the hook address + 4 (i.e. the instruction after the hook). The first time a C2 code is applied, the hook address will be untouched during the virgin frame. Then, the code handler will execute the C2 code before the next frame, over-writing the hook address and the back-branch.
The second time a C2 code is applied, the back-branch is virginized so that it is 00000000 again. However, the hook address is still the branch from the first time the C2 was applied. During the virgin frame, the branch is taken to the C2 code, but the back-branch hasn't been written yet, so it crashes trying to execute the "instruction" 00000000. This is why Y.S. would use a bctrl back to the hook address + 4. But in order to actually disable or "unhook" the C2 you need to over-write the hook with the original instruction.
---
If you tried to do a "classic" pause before sending the codes, you would find out that the codes are executed before the classic pause is entered. Instead, I would set a breakpoint just before the codes are executed - this is the BPNext on the About tab. If you did a BPNext pause, and uploaded codes, then when it resumed it would immediately execute the code handler. This was the purpose of the Pause While Sending checkbox.
As of...some recent version, I can't remember which, one of the debugger patches will make sure the codes are executed immediately after they're uploaded, so you don't need Pause While Sending anymore. There's also some small details about patches that flush and invalidate branches and another that resets the debugger registers, but this is already a novel...