Context switching is one of the core component of parallel processing. I was trying to implement one such. Storing the SP to data space is something noteworthy.
I wanted to save my main stack pointer into the RAM - data space. For that I initialized a buffer to store the stack pointer and a pointer variable to point to the buffer.
uint8_t buffer[2]; /* or uint16_t buffer */
uint8_t *ptr;
and my main have ptr = &buffer[0];
here is my savecontext asm
"lds r26, ptr \n\t" \
"lds r27, ptr + 1 \n\t" \
"in r0, 0x3d \n\t" \
"st x+, r0 \n\t" \
"in r0, 0x3e \n\t" \
"st x+, r0 \n\t" \
and in the restore context macro I have
"lds r26, ptr \n\t" \
"lds r27, ptr + 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" \
The disassemble code has
Save Context ()
ec: a0 91 24 01 lds r26, 0x0124
f0: b0 91 25 01 lds r27, 0x0125
f4: 0d b6 in r0, 0x3d ; 61
f6: 0d 92 st X+, r0
f8: 0e b6 in r0, 0x3e ; 62
fa: 0d 92 st X+, r0
I wanted to save my main stack pointer into the RAM - data space. For that I initialized a buffer to store the stack pointer and a pointer variable to point to the buffer.
uint8_t buffer[2]; /* or uint16_t buffer */
uint8_t *ptr;
and my main have ptr = &buffer[0];
here is my savecontext asm
"lds r26, ptr \n\t" \
"lds r27, ptr + 1 \n\t" \
"in r0, 0x3d \n\t" \
"st x+, r0 \n\t" \
"in r0, 0x3e \n\t" \
"st x+, r0 \n\t" \
and in the restore context macro I have
"lds r26, ptr \n\t" \
"lds r27, ptr + 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" \
The disassemble code has
Save Context ()
ec: a0 91 24 01 lds r26, 0x0124
f0: b0 91 25 01 lds r27, 0x0125
f4: 0d b6 in r0, 0x3d ; 61
f6: 0d 92 st X+, r0
f8: 0e b6 in r0, 0x3e ; 62
fa: 0d 92 st X+, r0
Restore Context ()
100: a0 91 24 01 lds r26, 0x0124
104: b0 91 25 01 lds r27, 0x0125
108: cd 91 ld r28, X+
10a: cd bf out 0x3d, r28 ; 61
10c: dd 91 ld r29, X+
10e: de bf out 0x3e, r29 ; 62
Atmega328P has the following memory spec.
32KB of flash
2KB of RAM i.e. 2048 bytes (0x100 - 0x8FF). That means two registers are required to store an address (i.e. why we have buffer[2] )
uint8_t buffer[2];
uint8_t *ptr;
ptr = &buffer[0];
which initialize location 0x02F3 as buffer[0] and location 0x02F4 as buffer[1] and
location 0x124 and location 0x125 as ptr ( because of 10 bit wide address we need two locations to store one address ) and ptr will have addreess of buffer[0]
location 0x0124 contains 0xF3 and 0x0125 contains 0x02 ( little endian style check Figure 1 )
f4: 0d b6 in r0, 0x3d ; 61
f6: 0d 92 st X+, r0
i.e. to X pointed address ( X pointer has 0x02F3 ) we store content of 0x3D which is SPL and then to the next address i.e. 0x02F4 we store SPH
Consider, I have a stack pointer 0x04FC which I wanted to store into the memory.
uint8_t buffer[2];
uint8_t *ptr;
ptr = &buffer[0];
which initialize location 0x02F3 as buffer[0] and location 0x02F4 as buffer[1] and
location 0x124 and location 0x125 as ptr ( because of 10 bit wide address we need two locations to store one address ) and ptr will have addreess of buffer[0]
location 0x0124 contains 0xF3 and 0x0125 contains 0x02 ( little endian style check Figure 1 )
![]() |
Figure 1 |
Now lets see the disassembled code.
lds r26, 0x0124
lds r27, 0x0125
which loads address of buffer[0] into X pointer which is 16 bit
so the X pointer has 0x02F3
Then we have
f6: 0d 92 st X+, r0
10c: dd 91 ld r29, X+
10e: de bf out 0x3e, r29 ; 62
Now that the stack pointer "0x04FC" is copied to ram 0x02F3 and 0x02F4 in little endian style.
Endianess is very important here. As we can see in the X pointer 0x1A has 0xFC, 0x1B contains 0x04 ( Figure 1 )