You can disable interrupts when modifying locking primitives, and save/restore the CPU context when interrupts are entered/exited. You can also minimize the amount of changes that any interrupt handler can make within the interrupt context.
It's not really that bad, and plenty of microcontrollers lack atomic operations.
If you disable the interrupts, does that mean any incoming events get ignored, or simply queued for later? If the former, that sounds like you risk missing something important.
Queued for later.
Obviously with a certain max depth, most probably of 1, meaning you're still missing repeated interrupts.
Sure, although I think "missing" has negative connotations that don't actually apply to this. When unmasked, your interrupt handler usually just runs until there is no more work to do, which is (again, usually) discoverable. Your device driver is still awoken promptly if the interrupt arrived during a critical section.
The critical section is typically very small, usually just modifying a single variable. Interrupts can't happen faster than the CPU frequency anyway, so you won't miss much.
You just guard those sequences by disabling and reenabling interrupts around them. It's a very common pattern in systems not designed for SMP.
Only communicate in one direction. I write, you read. I can also provide a large coherent set of data so long as you dont read it until I mark it as ready. There are many other ways too.
Obviously interrupt code can't clobber registers like that, any shared register the ISR needs to share with ordinary code must be saved/restored on entry/exit somehow.
I think GP is talking about MMIO or some device register rather than the CPU’s register file.
You disable interrupts for the three instructions it takes to emulate the atomic instruction. Which is barely slower than the atomic instruction would be anyway, because both have to read and write RAM, which is the slow part whether it's one micro-sequenced instruction or several standard ones.
You implement higher-level abstractions like mutexes. On a single core device this could be as simple as masking interrupts. You can use the mutexes to avoid the described race.