¿Por qué el enlazador AVR Assembler cree que mi tabla de datos está en la dirección incorrecta?

MCU: ATmega1284
Programador: JTAGICE3
IDE: Atmel Studio 7.0.2397
Idioma: Ensamblador AVR

Tengo una tabla de datos constante en la memoria flash. El enlazador cree que está ubicado en la dirección 0x0090, por lo que cuando escribo las instrucciones de ensamblaje para intentar cargar la dirección de la tabla, cargan 0x0090. El problema es que la tabla en realidad se encuentra en la dirección 0x0120. Entonces, cuando intento acceder a la tabla, obtengo basura.

¿Por qué el enlazador piensa que la tabla de datos está ubicada en una dirección diferente de donde realmente se está programando?

Aquí está la definición de la tabla.

.cseg
.align 16
  lcd_init_table: .db \
  0xEF , 0x03, 0x03, 0x80, 0x02, \
  0xCF , 0x03, 0x00, 0xC1, 0x30, \
  0xED , 0x04, 0x64, 0x03, 0x12, 0x81, \
  0xE8 , 0x03, 0x85, 0x00, 0x78, \
  0xCB , 0x05, 0x39, 0x2C, 0x00, 0x34, 0x02, \
  0xF7 , 0x01, 0x20, \
  0xEA , 0x02, 0x00, 0x00, \
  0xC0 , 0x01, 0x23, \
  0xC1 , 0x01, 0x10, \
  0xC5 , 0x02, 0x3e, 0x28, \
  0xC7 , 0x01, 0x86, \
  0x36 , 0x01, 0x48, \
  0x37 , 0x01, 0x00, \
  0x3A , 0x01, 0x55, \
  0xB1 , 0x02, 0x00, 0x18, \
  0xB6 , 0x03, 0x08, 0x82, 0x27, \
  0xF2 , 0x01, 0x00, \
  0x26 , 0x01, 0x01, \
  0xE0 , 0x0F, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E , 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00, \
  0xE1 , 0x0F, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31 , 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F, \
  0x11 , 0x80, \
  0x29 , 0x80, \
  0x00 , 0x00

Quiero acceder a la tabla usando la instrucción AVR ELPM de la siguiente manera...

  ;Load Z register with table address 
  ldi zh, lcd_init_table >> 8
  ldi zl, lcd_init_table & 0xFF
  clr r16
  out RAMPZ, r16

  lcd_init_loop:
  elpm r16, Z+ ;load command
  tst  r16
  breq lcd_init_done
  call lcd_write_cmd ;write command to LCD
  elpm r16, Z+ ;load wait and num args
  mov  r17, r16
  andi r16, 0x7F ;extract num_args
  breq lcd_init_skip_args
  call lcd_write_bytes_from_mcu_rom ;write rom bytes to LCD
  lcd_init_skip_args:
  andi r17, 0x80   ;extract wait flag
  breq lcd_init_loop ;if wait flag is set...
  ldi  r16, 150 ;wait 150 ms
  call wait_ms 
  jmp  lcd_init_loop ;go back to init loop
  lcd_init_done:

El problema es que cuando trato de cargar el registro Z con la dirección de lcd_init_table obtengo el valor 0x0090. El archivo de mapa confirma que 0x0090 es donde el enlazador cree que se encuentra la tabla.

CSEG lcd_init_table 00000090

El problema es que la mesa no está realmente ubicada allí. El archivo hexadecimal muestra que en realidad se encuentra en 0x0120. Programar la MCU y mirar el flash usando la ventana de memoria confirma que la tabla realmente está en 0x0120.

ingrese la descripción de la imagen aquí

Si codifico la carga de la dirección en función de la ubicación en el archivo hexadecimal, entonces el código funciona según lo previsto. El problema es que realmente no puedo hacer eso, ya que la tabla podría moverse a medida que agrego o elimino código. Podría ubicar la tabla en una dirección explícita usando una directiva .org, pero eso es solo una curita y me gustaría resolver el problema real.

  ;Load Z register with table address 
  ldi zh, 0x01
  ldi zl, 0x20

La configuración del programador del dispositivo es la siguiente.

ingrese la descripción de la imagen aquí

La configuración del compilador es la siguiente.ingrese la descripción de la imagen aquí

Respuestas (1)

0120h es 0090h multiplicado por 2.

En AVR, Z es un registro, por lo que tiene una dirección de bytes (entonces, lo que se direcciona a través de Z), mientras que la tabla en la memoria tiene una dirección de palabra (16 bits). Así que multiplicar Z por 2 resolverá el problema.

Esa parece ser la respuesta. Aunque el flash AVR está organizado como palabras, las instrucciones como ELPM todavía usan el direccionamiento de bytes para acceder a él. Supuse (erróneamente) que el enlazador también me estaba dando direcciones de bytes para los símbolos.