Warum denkt der AVR Assembler Linker, dass meine Datentabelle an der falschen Adresse ist?

MCU: ATmega1284
Programmierer: JTAGICE3
IDE: Atmel Studio 7.0.2397
Sprache: AVR Assembler

Ich habe eine konstante Datentabelle im Flash-Speicher. Der Linker glaubt, dass er sich an der Adresse 0x0090 befindet. Wenn ich also Assembleranweisungen schreibe, um zu versuchen, die Adresse der Tabelle zu laden, laden sie 0x0090. Das Problem ist, dass sich die Tabelle tatsächlich an der Adresse 0x0120 befindet. Wenn ich also versuche, auf die Tabelle zuzugreifen, bekomme ich nur Müll.

Warum glaubt der Linker, dass sich die Datentabelle an einer anderen Adresse befindet als dort, wo sie tatsächlich programmiert wird?

Hier ist die Definition der Tabelle.

.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

Ich möchte mit der AVR ELPM-Anweisung wie folgt auf die Tabelle zugreifen ...

  ;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:

Das Problem ist, dass ich beim Versuch, das Z-Register mit der Adresse von lcd_init_table zu laden, den Wert 0x0090 erhalte. Die Zuordnungsdatei bestätigt, dass 0x0090 dort ist, wo der Linker denkt, dass sich die Tabelle befindet.

CSEG lcd_init_table 00000090

Das Problem ist, dass sich der Tisch nicht wirklich dort befindet. Die Hex-Datei zeigt, dass sie sich tatsächlich bei 0x0120 befindet. Das Programmieren der MCU und das Betrachten des Flashs mit dem Speicherfenster bestätigt, dass die Tabelle wirklich auf 0x0120 steht.

Geben Sie hier die Bildbeschreibung ein

Wenn ich die Adresse basierend auf dem Speicherort in der Hex-Datei fest codiere, funktioniert der Code wie beabsichtigt. Das Problem ist, dass ich das nicht wirklich tun kann, da sich die Tabelle möglicherweise bewegt, wenn ich Code hinzufüge/entferne. Ich könnte die Tabelle mit einer .org-Direktive an einer eindeutigen Adresse lokalisieren, aber das ist nur ein Pflaster und ich würde das eigentliche Problem gerne lösen.

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

Die Einstellungen des Geräteprogrammierers sind wie folgt.

Geben Sie hier die Bildbeschreibung ein

Die Compiler-Einstellung ist wie folgt.Geben Sie hier die Bildbeschreibung ein

Antworten (1)

0120h ist 0090h multipliziert mit 2.

In AVR ist Z ein Register, also ist es byteadressiert (also was über Z adressiert wird), während die Tabelle im Speicher wortadressiert ist (16-Bit). Das Multiplizieren von Z mit 2 löst das Problem.

Das scheint die Antwort zu sein. Obwohl der AVR-Flash als Wörter angeordnet ist, verwenden Anweisungen wie ELPM immer noch die Byte-Adressierung, um darauf zuzugreifen. Ich dachte (fälschlicherweise), dass der Linker mir auch Byte-Adressen für die Symbole gab.