Thanks John, I'd pretty much came to the same conclusion. That means I need to disable interrupts to safely do a fetch_and_add. Since I'm in a function I'm wondering if I can used the delayed return, i.e.:
asm static int atomic_fetch_and_add(atomic_t *a, int i)
{
.optimize_iasm off;
MOVE.W Y0, X0; // Save 'i', 1 cycle
RTSD; // Delayed return, 5 cycles
MOVE.W X:(R2), Y0; // Store value to return, 1 cycle
ADD.W X:(R2), X0; // Add atomic value to 'i', 2 cycles
MOVE.W X0, X:(R2); // Store back in atomic value, 1 cycle
}
Since interrupts are disabled for three delayed instructions after the RTSD nothing can interrupt and they will execute as an atomic block.
I'm using assembly because if I'm providing an atomic library I want to be sure that the operation is definitely atomic and it's in a function so that I can turn optimization off for that block without impacting the calling code (and I'm assuming it'll effectively work as a compiler barrier to prevent the compiler re-ordering the code on either side of the call).
Are my assumptions correct, is that always going to work atomically and is there any better way to do it?
It certainly appears to work and given the debugger can't step through the delayed instructions I assume that nothing else can interrupt them either.
I think disabling interrupts for 4 cycles is acceptable, if it ever isn't then I'll have to look at implementing a more complex system with separate storage per interrupt level or something like that.