Contestaré esta pregunta específicamente para los controladores AVR que mencionaste. El principio básico también es válido para muchas otras arquitecturas de 8 bits.
Los AVR son núcleos de 8 bits. Esto significa que tienen registros de 8 bits. Sin embargo, 8 bits no son suficientes para acceder a una cantidad de memoria utilizable. Por lo tanto, el núcleo AVR puede usar un conjunto específico de registros combinados como registros de puntero de 16 bits. Los registros r30 y r31 (también alias como ZL y ZH) son un ejemplo de esto. Juntos forman el Z Pointer.
En la asamblea, leer un byte en la dirección 0x1234 se vería así:
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 familia AVR tiene 3 pares de registros que pueden usarse para esto. Están específicamente diseñados en hardware para permitir tales operaciones.
Al programar en un lenguaje de nivel superior como C, el compilador maneja estas cosas.
Nota: Algunos AVR incluso admiten tamaños de memoria mayores que 64k. Estos controladores tienen un registro de función especial en el que se escriben bits adicionales de la dirección antes del acceso. Por lo tanto, la dirección consta de los siguientes bits (MSB a LSB):
Registro de funciones especiales (generalmente 1 byte), ZH (8 bits), ZL (8 bits).