А можно узнать, зачем запоминать контекст в программе на ассемблере?! Что, все подпрограммы требуют по 16 регистров? Или это для прерываний, где экономят каждый такт (а если экономить не нужно, пишут на каком-нибудь AVR Бейсике)?
Совершенно не вижу необходимости.
Для прерываний я выделяю 3-4 регистра, один "верхний". И хватает. Не хватает - тогда push/pop.
Инициализация стека делаю макросом: "OUT2i SP,RAMEND" Макросы IN2 и OUT2 сами знают, в каком порядке грузить/читать старший/младший регистр, это важно для TCNT OCR и т.п.
Инициализация портов. "INITPORT PORTA, PULL, INP,PULL, INP, INP,OUTH,OUTL,OUTL"
Макрос INITPORT исходит из последовательной адресации PIN, DDR, PORT регистров. Для АВРок везде так. Собственно, вот он.
/// port = @0 DDR = @0 - 1 PIN = @0-2
/// define IN=00 PULL=01 OUTL=10 OUTH=11 ( 0,1,2,3) <1><2><3> used
/// bit 7 6 5 4 3 2 1 0
/// example: INITPORT PORTA, OUTL,OUTL,OUTL,OUTH, PULL, IN, IN, IN
/// @0 @1 @2 @3 @4 @5 @6 @7 @8
#define INP 0
#define PULL 1
#define OUTL 2
#define OUTH 3
#define PIN 2 /// for "portb-PIN" = pinb
#define DDR 1
#define PORT 0
.MACRO INITPORT /// def: INITPORT Id,K2,K2,K2,K2,K2,K2,K2,K2
ldi tmp2,(@1&1)<<7|(@2&1)<<6|(@3&1)<<5|(@4&1)<<4|(@5&1)<<3|(@6&1)<<2|(@7&1)<<1|(@8&1) /// will PORT
ldi tmp1,(@1&2)<<6|(@2&2)<<5|(@3&2)<<4|(@4&2)<<3|(@5&2)<<2|(@6&2)<<1|(@7&2)|(@8&2)>>1 /// will DDR
out @0-1,tmp1
out @0,tmp2 /// быстро.
.ENDMACRO
Если надо обратиться к PIN или DDR пишу "in portc-PIN", раньше вечно ошибался писал "in port"