co do _sfr_io_addr:
- głównie chodzi o to, że standardowa przestrzeń IO ma 64 komórki (dostępne przez instrukcje in/out), ale dostępne również w przestrzeni pamięci ram (przesunięcie +0x20).. w nowszych/większych procesorach te 64 komórki może być za mało - wtedy dodatkowe komórki są pod wyższymi adresami w przestrzeni pamięci, ale oznacza to, że są nie osiągalne przez in/out. Tak więc do każdego rejestru można odwoływać się przez dostęp do pamięci (polecenia ld/st - zawsze 2 cykle) lub do przestrzeni io (in/out - zawsze 1 cykl).. można np wpisywać wartość do rejestrów tak:
>> st PORTA, r24
ale jeśli zalezy na optymalizacji kodu i rejestr jest dostępny przez in/out, to można go zapisać tak:
>> out _SFR_IO_ADDR(PORTA), r24
zastosowanie _SFR_IO_ADDR jest po to, żeby przesunąć wartość podawaną do out o 0x20 w dół.. równoważny zapis (aczkolwiek nie polecam - czytelność):
>> out PORTA-0x20, r24
Co do kodu i przerwań - jakkolwiek kod C może być skompilowany do poziomu asemblera, wiązanie wektorów przerwań odbywa się na poziomie linkera (wiązanie z plikiem objektowym układu). Najważniejsze nagłówki i deklaracje są w <avr/io.h> i <avr/interrupt.h> - można je includować tak samo do kodu C jak i do kodu asemblera.
W C wektory przerwań deklaruje się przeważnie tak:
#include <avr/io.h>
#include <avr/interrupt.h>
SIGNAL(SIG_INTERRUPT1/* lub inne przerwanie*/)
{
/* tu kod */
}
a równie dobrze można z poziomu asemblera:
#include <avr/io.h>
#include <avr/interrupt.h>
.global SIG_INTERRUPT1
SIG_INTERRUPT1: /* lub inne przerwanie*/
/* tu kod */
reti
Taka funkcja będzie bezpośrednio wywoływana przez skok z tablicy wektorów przerwań. Nie wolno tutaj zapomnieć o odkładaniu rejestrów i zachowywaniu rejestru stanu.. ale co tu dużo mówić...