Pointer (sPtr, dPtr, ePtr)

Pointer sind spezielle intrinsische Variablen. Intrinsisch bedeutet, dass der Wert den sie speichern als eine Adresse auf einen Speicherbereich interpretiert wird. Stringvariablen und MemoryBlocks sind ebenfalls intrinsische Variablen.

Pointern kommt jedoch eine besondere Bedeutung zu: Sie können wie Word-Variablen mit Werten belegt werden und man kann mit ihnen Rechnungen durchführen. Weiterhin sind die Objektfunktionen des MemoryBlocks darauf anwendbar, also beispielsweise p.ByteValue(0) = 0x77 usw.

Da der Controller über drei unterschiedliche Speichersegmente verfügt, wurden auch drei verschiedene Pointer-Typen als neuer Datentyp implementiert. Dies sind:

  • sptr: Pointer auf Adressen im Arbeitsspeicher (sram segment)
  • dptr: Pointer auf Adressen im Flash (data segment)
  • eptr: Pointer auf Adressen im Eeprom (eeprom segment)

Mit Pointern ist es also möglich, auf eine beliebige Adresse, z.Bsp. innerhalb eines MemoryBlocks oder in einer Flash-Tabelle mit den Objektfunktionen zuzugreifen:

Beispiel im Datensegment (Direktzugriff):

dim pd as dptr
 
pd = table.Addr
print "pd = 0x";hex(pd);" ";34;pd.StringValue(2,12);34
 
do
loop
 
data table
  .dw myfunc1
  .db "Menu Nr. 1  "
  .dw myfunc2
  .db "Menu Nr. 2  "
  .dw myfunc3
  .db "Menu Nr. 3  "
enddata

Das Ganze ist vergleichbar mit den Zugriffen über das Sram, Flash oder Eeprom-Interface, bei denen man im Unterschied zu den Pointern absolute Adressangaben vornehmen muss (also nicht zwangsläufig etwas super neues). Bei Pointern ist aber eine weitere Besonderheit vorhanden: Die Möglichkeit zum SuperImposing mit Strukturen.

SuperImpose

SuperImposing ist das „darüberlegen“ einer Strukturdeklaration auf einen beliebigen Speicherbereich. D.h. wenn man bspw. eine Struktur in Form eines Menüelements deklariert hat, lässt sich mit Pointern diese Strukturdeklaration auf den Speicherbereich abbilden/maskieren und kann dann die Elemente der Struktur elegant lesen. Das SuperImposing/Maskieren von Strukturdeklarationen auf Speicherbereiche ist auf die Verwendung mit Pointern beschränkt.

Syntax

Die Syntax ist denkbar einfach, wobei jedoch bestimmte Vorgaben einzuhalten sind. Bei einer Strukturdeklaration handelt es sich um eine virtuelle Definition, welche dem Compiler mitteilt wie der Zugriff erfolgen soll:

mypointer.<StrukturName>[( Index )].<StrukturElement>

Ist das Strukturelement eine weitere Struktur, verschachtelt sich der Aufruf entsprechend tiefer.

Index ist ein optionaler Konstantenwert bzw. ab Version 2013.r5 ein Ausdruck (z.B. Variable), der den Startoffset auf Basis der Gesamten Strukturgröße ergibt. D.h. ist die Struktur mitsamt seiner Elemente 4 Bytes groß, würde Index = 2 zu einer Startadresse + 8 führen, ab der die Struktur über den Speicherbereich gelegt wird.

Anwendung

Strukturdeklaration:

struct ENTRY
  word  fnAddr
  string	text[12]
endstruct

Pointer für Flash-Segment dimensionieren:

dim pd as dptr

Pointer mit Adresse der Tabelle belegen:

pd = table.Addr

Indirekter Zugriff über Strukturdeklaration, die ab der im Pointer abgelegten Adresse über den Speicherbereich gelegt wird (superimpose). Bei Angabe eines Arrayindex bei einer Struktur, wird der Offset automatisch berechnet:

print "fnAddr = 0x";hex(pd.ENTRY(0).fnAddr);", menu text = ";34;pd.ENTRY(0).text;34
print "fnAddr = 0x";hex(pd.ENTRY(1).fnAddr);", menu text = ";34;pd.ENTRY(1).text;34
print "fnAddr = 0x";hex(pd.ENTRY(2).fnAddr);", menu text = ";34;pd.ENTRY(2).text;34

Die Tabelle im Flash:

data table
  .dw myfunc1
  .db "Menu Nr. 1  "
  .dw myfunc2
  .db "Menu Nr. 2  "
  .dw myfunc3
  .db "Menu Nr. 3  "
enddata

Beispielprogramm

pointer.luna
const F_CPU=20000000
avr.device = atmega328p
avr.clock  = F_CPU
avr.stack = 64
 
uart.baud = 19200
uart.recv.enable
uart.send.enable
 
struct ENTRY
  word  	fnAddr
  string	text[12]
endstruct
 
dim a as byte
dim ps as sptr
dim pd as dptr
dim m as MemoryBlock
 
print 12;"pointer and superimpose example"
print
 
m.New(64)
m.WordValue(0)	= 0xaabb
m.CString(2)	= "Entry Nr. 1 "
m.WordValue(14)	= 0xccdd
m.CString(16)	= "Entry Nr. 2 "
m.WordValue(28)	= 0xeeff
m.CString(30)	= "Entry Nr. 3 "
 
ps = m.Ptr				'Speicheradresse der MemoryBlock-Daten
pd = table.Addr			'Speicheradresse der Tabelle im Flash
 
'direkt über Objektfunktionen
print "(sram)  ";34;ps.StringValue(2,12);34
print "(flash) ";34;pd.StringValue(2,12);34
print
 
'SuperImpose
'
'SuperImposing ist das "darüberlegen" einer Strukturdeklaration auf einen beliebigen Speicherbereich.
'D.h. wenn man bspw. eine Struktur in Form eines Menüelements deklariert hat, lässt sich mit Pointern
'diese Strukturdeklaration auf den Speicherbereich abbilden und kann dann die Elemente der
'Struktur elegant lesen. Bei Angabe eines Arrayindex (Konstante) bei einer Struktur, wird der
'Offset automatisch berechnet.
 
print "sram:"
print "fnAddr = 0x";hex(ps.ENTRY(0).fnAddr);", menu text = ";34;ps.ENTRY(0).text;34
print "fnAddr = 0x";hex(ps.ENTRY(1).fnAddr);", menu text = ";34;ps.ENTRY(1).text;34
print "fnAddr = 0x";hex(ps.ENTRY(2).fnAddr);", menu text = ";34;ps.ENTRY(2).text;34
print
print "flash:"
print "fnAddr = 0x";hex(pd.ENTRY(0).fnAddr);", menu text = ";34;pd.ENTRY(0).text;34
print "fnAddr = 0x";hex(pd.ENTRY(1).fnAddr);", menu text = ";34;pd.ENTRY(1).text;34
print "fnAddr = 0x";hex(pd.ENTRY(2).fnAddr);", menu text = ";34;pd.ENTRY(2).text;34
print
 
print "Aufruf per Icall aus der Tabelle:"
icall pd.ENTRY(0).fnAddr
icall pd.ENTRY(1).fnAddr
icall pd.ENTRY(2).fnAddr
 
print
print "ready"
 
do
loop
 
procedure myfunc1()
  print "myfunc 1"
endproc
procedure myfunc2()
  print "myfunc 2"
endproc
procedure myfunc3()
  print "myfunc 3"
endproc
 
'Wichtiger Hinweis:
'Adressen von Labels, Objekten oder Methoden liegen im Flash immer an einer Word-Grenze, sie werden
'im Flash-Objekt daher auch als Word-basierte Adresse abgelegt. Möchte man mit der Byte-Adresse arbeiten,
'muss man den Wert wie im Assembler mit 2 multiplizieren.
data table
  ENTRY { myfunc1, "Menu Nr. 1" }
  ENTRY { myfunc2, "Menu Nr. 2" }
  ENTRY { myfunc3, "Menu Nr. 3" }
enddata