Je répondrai à cette question spécifiquement pour les contrôleurs AVR que vous avez mentionnés. Le principe de base est également valable pour de nombreuses autres architectures 8 bits.
Les AVR sont des cœurs 8 bits. Cela signifie qu'ils ont des registres 8 bits. Cependant, 8 bits ne suffisent pas pour accéder à une quantité de mémoire utilisable. Par conséquent, le noyau AVR est capable d'utiliser un ensemble spécifique de registres combinés en tant que registres de pointeurs 16 bits. Les registres r30 et r31 (également alias ZL et ZH) en sont un exemple. Ensemble, ils forment le pointeur Z.
Dans l'assembly, la lecture d'un octet à l'adresse 0x1234 ressemblerait à ceci:
ldi ZL, 0x34 ; Load r30 (ZL) with low byte of address
ldi ZH, 0x12 ; Load r31 (ZH) with high byte of address
ld r16, Z ; Load byte to r16
La famille AVR dispose de 3 paires de registres qui peuvent être utilisées à cet effet. Ils sont spécifiquement conçus en matériel pour permettre de telles opérations.
Lors de la programmation dans un langage de niveau supérieur comme C, le compilateur gère ce genre de choses.
Remarque: Certains AVR prennent même en charge des tailles de mémoire supérieures à 64 Ko. Ces contrôleurs ont un registre de fonctions spéciales dans lequel des bits supplémentaires de l'adresse sont écrits avant l'accès. L'adresse est donc constituée des bits suivants (MSB à LSB):
Registre de fonctions spéciales (généralement 1 octet), ZH (8 bits), ZL (8 bits).