Why not use an inline assembly statement to do what you want?
See the inline assembler cookbook
A simple (readable) multiline example from there (a very short delay) which can go right into your AVR-GCC program:
asm volatile("nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t" ::);
Since you seem to know your assembly, you should be good ready to rock with inline assembly as well.
Answer from Windell Oskay on Stack ExchangeWhy not use an inline assembly statement to do what you want?
See the inline assembler cookbook
A simple (readable) multiline example from there (a very short delay) which can go right into your AVR-GCC program:
asm volatile("nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t" ::);
Since you seem to know your assembly, you should be good ready to rock with inline assembly as well.
I think that GCC's naked attribute does what you want. It skips generating any function header or footer, so you need to do your own reti.
void INT0_vect(void) __attribute__ ((signal, naked));
void INT0_vect(void)
{
TCCR1B = 0x09;
asm volatile("reti");
}
FreeRTOS uses this scheme for context switching on AVR, they explicitly store all registers. You may only want a subset:
#define portSAVE_CONTEXT() \ asm volatile ( "push r0 \n\t" \ "in r0, __SREG__ \n\t" \ "cli \n\t" \ "push r0 \n\t" \ "push r1 \n\t" \ "clr r1 \n\t" \ "push r2 \n\t" \ "push r3 \n\t" \ "push r4 \n\t" \ "push r5 \n\t" \ "push r6 \n\t" \ "push r7 \n\t" \ "push r8 \n\t" \ "push r9 \n\t" \ "push r10 \n\t" \ "push r11 \n\t" \ "push r12 \n\t" \ "push r13 \n\t" \ "push r14 \n\t" \ "push r15 \n\t" \ "push r16 \n\t" \ "push r17 \n\t" \ "push r18 \n\t" \ "push r19 \n\t" \ "push r20 \n\t" \ "push r21 \n\t" \ "push r22 \n\t" \ "push r23 \n\t" \ "push r24 \n\t" \ "push r25 \n\t" \ "push r26 \n\t" \ "push r27 \n\t" \ "push r28 \n\t" \ "push r29 \n\t" \ "push r30 \n\t" \ "push r31 \n\t" \ "lds r26, pxCurrentTCB \n\t" \ "lds r27, pxCurrentTCB + 1 \n\t" \ "in r0, 0x3d \n\t" \ "st x+, r0 \n\t" \ "in r0, 0x3e \n\t" \ "st x+, r0 \n\t" \ ); #define portRESTORE_CONTEXT() \ asm volatile ( "lds r26, pxCurrentTCB \n\t" \ "lds r27, pxCurrentTCB + 1 \n\t" \ "ld r28, x+ \n\t" \ "out __SP_L__, r28 \n\t" \ "ld r29, x+ \n\t" \ "out __SP_H__, r29 \n\t" \ "pop r31 \n\t" \ "pop r30 \n\t" \ "pop r29 \n\t" \ "pop r28 \n\t" \ "pop r27 \n\t" \ "pop r26 \n\t" \ "pop r25 \n\t" \ "pop r24 \n\t" \ "pop r23 \n\t" \ "pop r22 \n\t" \ "pop r21 \n\t" \ "pop r20 \n\t" \ "pop r19 \n\t" \ "pop r18 \n\t" \ "pop r17 \n\t" \ "pop r16 \n\t" \ "pop r15 \n\t" \ "pop r14 \n\t" \ "pop r13 \n\t" \ "pop r12 \n\t" \ "pop r11 \n\t" \ "pop r10 \n\t" \ "pop r9 \n\t" \ "pop r8 \n\t" \ "pop r7 \n\t" \ "pop r6 \n\t" \ "pop r5 \n\t" \ "pop r4 \n\t" \ "pop r3 \n\t" \ "pop r2 \n\t" \ "pop r1 \n\t" \ "pop r0 \n\t" \ "out __SREG__, r0 \n\t" \ "pop r0 \n\t" \ );
nop is an assembly instruction that does nothing--well as close to nothing as you can do and still execute a machine instruction, which means (probably) a REALLY tiny bit of time goes by (which can have limited value in certain realtime applications.
In this case, the statement asm("nop"); makes no semantic difference to the program. The only reason I can think that it might be present is to "force" the compiler to NOT collapse the code paths, making the machine structure of the switch statement visible if you look at the object code or disassemble the machine code or view it in a debugger.
Since nobody mentioned it, nop can also be useful to "yield" during a critical section, i.e. allow other interrupts to occur so as to reduce interrupt latency caused by the critical section.
This is typically useful in embedded applications without operating systems where you often have to poll variables and/or status registers during a critical section.
On many RISC processors (ARM, AVR), the instruction immediately following interrupt unmasking will still be masked, so if you just put sei/cli close together, you won't allow any interrupt to occur.
Each nop added between sei and cli allows one more interrupt to occur before the critical section resumes.