Einführung Indirektes Adressieren
Auf YouTube mit Erklärungen ansehen..
Im Browser ohne Ton durchklicken... (Mit Pfeiltasten steuern) |
Atmel-RISC-8Bit Speicherkonzept am Beispiel des ATtiny2313
Der Speicher ist gemäß der Harvard-Architektur in einen Programm- und einen Datenspeicher unterteilt. Der Programmspeicher ist wortweise (16 Bit) organisiert, d.h. Den 2 KiByte Flash-Speicher stehen nur 1 Ki Adressen $000..$3FF gegenüber. Der Datenspeicher ist byteweise organisiert:
Anmerkung: Das EEPROM (ohne Abbildung) wird über einen besonderen Mechanismus angesprochen, es gibt spezielle Befehle.. Wichtig: Zur Vereinfachung und zum Einsparen von Programmspeicher werden den Registern und I/O Adressen besondere Kurz-Zugriffe zugeordnet. Beispiel: PORTB hat innerhalb des I/O Blocks die Adresse $18, bezogen auf den geammten Datenbereich die Adresse $38. Praktisch werden die symbolischen Namen wie R16 oder PIND verwendet, somit muss man sich um die genauen Adressen keinen Kopf machen, sie sind in der jeweiligen *.inc Datei vordefiniert. |
Direkte Adressierung im RAM-Bereich
Register Direkt, einfach | Register Direkt, mit zwei Registern |
---|---|
Beispiel: inc R16 ; erhöhe Register 16 um eins Man beachte die Anzahl der Bits für die Kodierung des Operanden. |
Beispiel: add R16, R17 ; R16 <- R16 + R17 Die Befehle sind meist genau 16 Bit lang. |
I/O Direkt | Daten Direkt |
Beispiel: in R16, PIND ; Lade PIND in Register 16 6Bit -> 64 Port-Adressen! |
Beispiel: lds R16, 0x60 ; Lade Inhalt des ersten SRAM-Bytes in R16 Dieser Befehl braucht zwei Wörter Platz wegen der 16 Bit SRAM Adresse (es gibt auch grössere Controller) |
Indirekte Daten-Adressierung im RAM-Bereich (SRAM)
Die Adresse des Operanden steht nicht direkt im Befehl sondern in Register (bzw. in zwei Registern, da ein 8Bit-Register für 16Bit-Adresse zu wenig ist).
Wozu ist das praktisch? Betrachte folgende Gedankenreise, C wird in Assembler umgesetzt wir spielen Compiler:
Wir möchten das SRAM als Datenspeicher nutzen und es beginne mit Adresse $60 = 0x60 char a = 5; Der Compiler merkt sich in einer Tabelle den Bezeichner (engl. Identifier) a und legt fortan für ihn die Adresse $60 fest, d.h. der Compiler assoziiert mit a die Adresse $60. Somit übersetzt er: ldi R16, 5 sts $60, R16 Hierbei handelt es sich noch um direkte Adressierung. |
|
Nun möchten wir zusätzlich ein char-Array mit dem Text "ABI" erzeugen: char s[] = "ABI"; Der Text passt nun nicht mehr in ein Byte, er ist vielmehr eine Folge von Buchstaben, die mit '\0', der 0 terminiert ist, s zeigt also auf einen Speicherbereich mit mehreren Bytes: Worin besteht nun der Unterschied? Wie kann man auf das 'B' zugreifen? Wenn s so wie a wäre könnte man nur auf das 'A' zugreifen, daher brauchen wir eine andere Konstruktion: s muss variabler werden, z.B. ein Register: |
Also assoziieren wir mit s ein Register, da unser 8Bit-Controller nur 8Bit-Register besitzt, von der Architektur aber mehr als 256 (Daten)RAM-Adressen vorgesehen sind benötigen wir zwei Register für den Zeiger auf die Adresse.
Der so adressierte Speicher wird nun nicht mehr direkt sondern indirekt über Register angesprochen. Die Entwickler des AVR haben dafür drei Registerpaare vorgesehen: (X=R27,R26; Y=R29,R28; Z=R31,R30)
|
Daten Indirekt | Mögliche Realisierung von char s[] = "ABI"; |
---|---|
In das Register X alias R27 und R26 muss $61 geladen werden:
ldi R27,0 ldi R26, $61 Oder vornehmer: ldi R27, high($61) ; ist 0 ldi R26, low($61) ; ist $61 Nun noch die Daten reinschaufeln: ldi R16, 'A' st X, R16 So, nun noch das X um 1 erhöhen für das 'B', das würde nun aber aufwändig werden, erst das R26, bedenke den möglichen Überlauf, das Carry-Bit und dann das R27... Viel zu umständlich dafür gibt es schon bessere Befehle: |
Daten Indirekt mit Inkrement und Dekrement
Daten Indirekt mit Post-Inkrement | Daten Indirekt mit Prä-Dekrement |
---|---|
Das bessere Daten reinschaufeln:
ldi R16, 'A' st X+, R16 ldi R16, 'B' st X+, R16 usw... |
Gibt es in allen 4 Variationen Prä-Post-Inkrement-Dekrement..
Schau in Deine Formelsammlung RTFM! |
Daten Indirekt mit Displacement
Daten Indirekt mit Displacement | |
---|---|
Bei komplexeren Datenstrukturen z.B. Listen von Records bzw. Objekten wäre es praktisch zu der Basisadresse des Objekts (Zeiger, Pointer auf Recotd bzw. Objekt) ein bestimmtes Feld bzw. Attribut ansprechen zu können. Der Versatz der Feld- bzw. Attributadresse wird auch als "Displacement" bezeichnet. Beachte: Displacement hat nur 6Bit -> 0 .. 64 Beispiel für Array of Integer mit 16Bit: int a,b[] = {1000,2000,3000}; // a zeigt auf $60, b zeigt auf $80 char i = 1; // i ist R20 a = b[i]; Die Adresse b[i] lässt sich mit Basisadresse b berechnen: b[i] = b + i*2, somit die Zuweisung a = b[i]: ldi R29, high($80) ldi R28, low($80) lsl R20; nach links schieben ist i*2 nehmen add R28, R20; b+ i*2 da es keinen Überlauf gibt kann man sich Rest sparen ld R16, Y; hol das low-Byte sts $60, R16 ld R16, Y+1; hol das high-Byte sts $61, R16 |
Zugriff auf Programmspeicher
Programm-Speicher-Zugriff auf Konstanten | |
---|---|
Im Programm-Speicher können Konstanten abgelegt werden mit der Assembler-Direktive .db. Mit dem LPM-Befehl kann ein Byte aus dem Programmspeicher via Z-Register in ein Register kopiert werden. Da der Programmspeicher 16 Bit organisiert ist wird mit dem LSB des Z-Registers zwichen dem High- und Low-Byte einer Programm-Speicher-Adresse unterschieden.
ldi R31, high(kzeiger) ; High-Byte in Z ldi R30, low(kzeiger) ; Low-Byte in Z lpm R16, Z+ ; lade erste Konstante ; usw. kzeiger: .db "ABI",0 ; die Konstante |
Programm-Speicher-Zugriff mit Post-Inkrement | |
---|---|
Siehe oben. |
Direkte Sprünge auf Programm-Adressen mit JMP und CALL | |
---|---|
Long Jump und Long Call, sind für grosse Sprünge sinnvoll 22-Bit Adressen. |
Indirekte Sprünge
Indirekte Sprünge auf Programm-Adressen mit IJMP und ICALL | |
---|---|
Praktisch für Case-Anweisungen, bzw. Sprung-Tabellen. |
Relative Sprünge auf Programm-Adressen mit RJMP und RCALL | |
---|---|
Relativer Sprung |