Forum: Mikrocontroller und Digitale Elektronik Fragen zum Z-Pointer


von Michael D. (etzen_michi)


Lesenswert?

Alles wird hinterfragt ^^.

Es geht hierbei um den Abschnitt "Ausgabe eines konstanten Textes" im 
Tutorial: http://www.mikrocontroller.net/articles/AVR-Tutorial:_LCD

Meine Fragen sind:


Funktioniert der Z-Pointer ähnlich wie der Stack?

Wie ich das sehe kann der Z-Pointer 16bit speichern in High und Low. ist 
das Richtig?

Kann man ein ASCII Zeichen den Code verändern? z.B. für ä ein $E1.

Warum wird hier text*2 in den Z-Pointer geladen?


Und nochmal eine Frage zum Stack:

Warum wird "out SPL, temp1" ein leeres Register in den Stack 
geschrieben?

von Falk B. (falk)


Lesenswert?

@  Michael Dierken (etzen_michi)

>Funktioniert der Z-Pointer ähnlich wie der Stack?

Naja, eher nicht.

>Wie ich das sehe kann der Z-Pointer 16bit speichern in High und Low. ist
>das Richtig?

Ja.

>Kann man ein ASCII Zeichen den Code verändern? z.B. für ä ein $E1.

???
Eher nicht.

>Warum wird hier text*2 in den Z-Pointer geladen?

http://www.mikrocontroller.net/articles/AVR-Tutorial:_Mehrfachverzweigung#Z-Pointer_leicht_verst.C3.A4ndlich

>Warum wird "out SPL, temp1" ein leeres Register in den Stack
>geschrieben?

Das ist nicht leer, bestenfalls Null.

MFG
Falk

von Hannes L. (hannes)


Lesenswert?

Michael Dierken schrieb:
> Alles wird hinterfragt ^^.
>
> Es geht hierbei um den Abschnitt "Ausgabe eines konstanten Textes" im
> Tutorial: http://www.mikrocontroller.net/articles/AVR-Tutorial:_LCD
>
> Meine Fragen sind:
>
>
> Funktioniert der Z-Pointer ähnlich wie der Stack?

Nein.

>
> Wie ich das sehe kann der Z-Pointer 16bit speichern in High und Low. ist
> das Richtig?

Ja, aber...

Denn der Z-Pointer ist das, was Du mit ihm machst. Er kann als zwei 
normale Register benutzt werden, aber auch als Pointer (Zeiger, 
Adresszeiger). Er kann auf SRAM zeigen, aber auch auf Flash, siehe dazu 
die Hilfetexte der Befehle, die den Z-Pointer benutzen. Eine 
Befehlsübersicht findest Du im Datenblatt unter Instruction summary.

>
> Kann man ein ASCII Zeichen den Code verändern? z.B. für ä ein $E1.

Das ist keine Frage des Z-Pointers, sondern der Schreibweise. Es gibt 
verschiedene Schreibweisen, die alle irgendwo in der Hilfe zum 
AVR-Studio erklärt werden, einfach mal ein bisschen in der Hilfe 
herumstöbern.

>
> Warum wird hier text*2 in den Z-Pointer geladen?

Weil der Befehl "lpm" den Flash byteweise adressiert, siehe Hilfe zu LPM 
und die Erklärung der Memories im Datenblatt.

>
>
> Und nochmal eine Frage zum Stack:
>
> Warum wird "out SPL, temp1" ein leeres Register in den Stack
> geschrieben?

Wieso ein leeres Register?
Man lädt doch zuvor mit "ldi temp1, low(ramend)" die Adresse des 
RAM-Endes in das Register, ehe man es in den Stackpointer schreibt.

...

von Michael D. (etzen_michi)


Lesenswert?

Eine weitere Frage:

In dem auf dem hingewiesenen Teil des Tutorials wird sowohl ZL als auch 
ZH mit text*2 geladen.

Wozu wird das ZH in diesem Falle verwendet?

Das ZL wird dazu verwendet das der Text ausgelesen werden kann, aber 
wozu ist das ZH da?

von Falk B. (falk)


Lesenswert?

Mensch, ZH und ZL gehören zusammen, das ist ein 16 Bit Pointer, verteilt 
auf zwei 8 Bit Register. RTFM!

von Michael D. (etzen_michi)


Lesenswert?

Mensch .. warum funktioniert das wenn ich ZH wechlasse?

Irgendwie verstehe ich das mit dem Z-Pointer und vorallem .db nicht ganz 
.. lese auch schon TFM aber i-wie komm ich da auchnicht weiter ....

von Michael D. (etzen_michi)


Lesenswert?

Ich will das nicht nur nachmachen können .. ich will das nachvollziehen 
können.

von Karl H. (kbuchegg)


Lesenswert?

Michael Dierken schrieb:
> Mensch .. warum funktioniert das wenn ich ZH wechlasse?

Weil höchst wahrscheinlich dein Programm kurz genug ist, dass der Text 
an einer Stelle im Speicher zu liegen kommt, an dem das höherweertige 
Byte der Adresse gleich 0 ist.

Du kannst natürlich auch die Sparbuchzinsen eines Sparbuchs ausrechnen, 
indem du immer nur Zahlen bis 100 berücksichtigst.
Bei einem Schüler mit einer Einlage von 25 Euro ist das kein Problem. Da 
kommt dann sogar das richtige raus.
Wenn du aber etwas mehr auf dem Sparbuch hast, und der Banker rechnet 
dir die Zinsen anstelle von 2568 nur von 68 Euro aus (weil nur kleiner 
100 in die Rechnung eingehen), dann wirst du zu Recht maulen.

>
> Irgendwie verstehe ich das mit dem Z-Pointer und vorallem .db nicht ganz

Ist doch ganz einfach.
Der Z-Pointer ist ein Zettel, auf dem steht: Der eigentliche Text (und 
da der nächste Buchstabe) ist in der Kommode, in Schublade 4 zu finden.

Der Befehl LPM geht jetzt her, liest den Zettel und benutzt die 
Information auf dem Zettel (Kommode, Schublade 4) um von genau dort, 
Kommode/Schublade 4, den Buchstaben zu holen.

Und wenn du dann auf den Zettel draufschreibst: Kommode / Schublafe 5, 
dann wird der LPM beim nächsten Zugriff eben zur Schublade 5 in der 
Kommode greifen um von dort den nächsten Buchstaben ins R0 zu laden.

von Michael D. (etzen_michi)


Angehängte Dateien:

Lesenswert?

Hoffe das jetzt keine Beschwerden kommen .. habe ich es so richtig
verstanden?
(Anhang, da es auf papier irgendwie einfach ist zu erklären)

(Habe nochmal mit 1/4 der Auflösung hochgeladen.)

von Michael D. (etzen_michi)


Lesenswert?

Daher das ich die Info bekomme habe dass das Bild wohl zu klein sein 
soll (da will man sich mal an die Upload Regeln halten) schriebe ich das 
hier nochmal so hin.

  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
+------------------------------------------------+
|                                                |
| H  i  e  r     s  t  e  h  t     d  a  s     P | 0
|                                                |
+------------------------------------------------+
|                                                |
| r  o  g  r  a  m  m                            | 1
|                                                |
+------------------------------------------------+
|                                                |
|                                                | 2
|                                                |
+------------------------------------------------+
|                                                |
| H  i  e  r     s  t  e  h  t     d  e  r     T | 3
|                                                |
+------------------------------------------------+
|                                                |
| e  x  t $00                                    | 4
|                                                |
+------------------------------------------------+
|                                                |
|                                                | 5
|                                                |
+------------------------------------------------+

1
ldi ZL, LOW(text*2)                     ; Lädt den Anfang von text (Byte H,
2
ldi ZH, High(text*2)                    ; Adresse 0x30) in den Z-Pointer
3
4
lpm temp1, Z+                           ; Kopiert das Byte (H) aus dem
5
                                        ; Z-Pointer in temp1
6
                                        ; Und erhöt den Z-Pointer um
7
                                        ; eins auf 0x31
8
9
cpi temp1, 0                            ; Schaut nach ob der Inhalt von
10
                                        ; temp1 = 0 ist uns setzt Z-Flag
11
                                        ; wenn dem so ist
12
13
text:
14
.db "Hier steht der Text", 0

Nach dem cpi kann man brne oder breq nutzen je nachdem ob ein Sprung 
ausgeführt werden soll sobald die kompletten Werte aus .db abgearbeitet 
wurden.


Wenn dies alles richtig ist, was ist wenn mit .db ein Wert gespeichert 
wird welcher ein Byte besitzt welches komplett auf 0 steht?

von bitte löschen (Gast)


Lesenswert?

Das hängt davon ab, was dem "cpi temp1,0" folgt. Beim Programm aus dem 
Tutorial würde die Schleife direkt am Anfang abgebrochen. Es ist ein 
bisschen wie die Frage: "Was passiert, wenn ich in Köln bin und nach 
Köln will?"

von Michael D. (etzen_michi)


Lesenswert?

Was hängt davon ab was dem "cpi temp1, 0" folgt?

Noch eine Frage:

Habe jetzt verstanden wozu er das *2 in ldi SPL, LOW(text*2) braucht ^^.

Aber da komtm ja gerne eine 9Bit lange adresse raus ... wie macht er 
dass? ...
Schaut er direkt vorm bearbeiten nach ob das obere oder untere Bit 
geladen werden muss?
Und speichert die dann direkt Bit 1-8ab?

Arbeitet er mit den nächsten 2Byte und hat somit das Bit7 des ersten 
Byte im Bit0 des zweiten Byte?

(so langsam fange ich an durch zu steigen .. zuindest hoffe ich das).

von bitte löschen (Gast)


Lesenswert?

Michael Dierken schrieb:
> Was hängt davon ab was dem "cpi temp1, 0" folgt?

Das war die Antwort auf Deine Frage ".. was ist wenn mit .db ein Wert 
gespeichert wird welcher ein Byte besitzt welches komplett auf 0 steht?"

Zu Deinem aktuellen Verständnis-Problem:
Michael Dierken schrieb:
> Aber da komtm ja gerne eine 9Bit lange adresse raus ... wie macht er
> dass? ...
Ich nehme mal an, mit "er" meinst Du Deinen Assembler.
"LOW" ist ein Makro, was aus einer 16-Bit Zahl die unteren 8 Bits 
herausholt, "HIGH" ist ein Makro, das die oberen 8 Bit herausholt.
LOW(0x1234) wird als 0x34 ausgewertet.
HIGH(0x1234) wird als 0x12 ausgewertet.
ZL und ZH bilden zusammen eine Adresse.

Zu Deinem Posting:
Du solltest Dir erst mal mit Dir selbst einig werden, ob Du die Bits von 
0 bis 7 oder von 1 bis 8 durchnummerierst. ("Und speichert die dann 
direkt Bit 1-8ab?" und "das Bit7 des ersten Byte im Bit0 des zweiten 
Byte?") Üblich ist 0 bis 7, trotzdem gibt es immer wieder 
Missverständnisse, z.B. bei der Beschriftung des Pollin Atmel Evaluation 
Boards.

Bitte schreibe etwas langsamer. Die Sekunden, die Du sparst, wenn Du 
Wörter wie "in", "auf" und "bei" einfach weg lässt, werden für alle 
anderen hier lästige Minuten, die sie darüber nachgrübeln, was Du wohl 
meinen magst. Leerzeichen sind auch nicht nur aus optischen Gründen da. 
Weniger lässig ist cooler. Auch wenn es im SMS-Zeitalter üblich geworden 
ist, im Telegrammstil zu kommunizieren, ist es in einem Forum, in dem 
technische Sachverhalte besprochen werden, fehl am Platze. Die "..." 
kannst Du gerne weglassen.
Flüchtigkeitsfehler, wie "ldi SPL, LOW(text*2)" (Statt "ldi ZL..") 
vermeidet man, wenn man seinen Text vor dem Abschicken nochmal 
durchliest.
Ich könnte noch einiges mehr dazu schreiben, aber ich fasse es mal wie 
folgt zusammen:

Hier sind viele Leute, die Dir ohne Bezahlung mit Deinen Problemen 
helfen. Sie investieren Zeit und geben sich Mühe mit Dir, obwohl sie 
Dich nicht einmal kennen.
Es sollte Dir eigentlich von selbst klar sein, dass Du diese Mühe 
dadurch honorieren solltest, dass Du Dir mit Deiner Frage mindestens 
gleich viel Mühe gibst!

von Michael D. (etzen_michi)


Lesenswert?

Tach ... Was die Leerzeichen angeht konnte ich jetzt leider nicht sehen, 
welche Stellen du meinst. Das gleiche gilt für auf, bei und in.
Lese mir auch den Text in der Regel immer noch einmal durch (Verwende 
ganz brav den "Vorschau" Button).

Ich muss zugeben das ich mit der Groß und kleinschreibung etwas gegeizt 
habe.

Das mit der Nummerierung 1-8 und 0-7 war absichtlich so.

Nehmen wir den Wert $47. Dieser hat 8Bit Länge.
Der wird nun *2 genommen wodurch ein Wert mit 9 Bit länge entsteht.
Hier rauf bezieht sich meine Frage.

Das LSB dieses 9Bit Stranges bestimmt soweit wie ich das verstanden habe 
ob die unteren 8 oder oberen 8Bit bei lpm temp1, Z+ in das Register 
kopiert werden.

Meine Frage ist nun wie er das macht, dass von den 8Bit nichts verloren 
geht, da er wenn ich mich nicht irre immer nur 8Bit abspeichern kann.

von bitte löschen (Gast)


Lesenswert?

Der Z-Pointer besteht aus 2 8 Bit-Registern.
Dein Beispielwert ist nun wenig geignet, da 0x47 * 2 0x8E ist, aber 
egal.
Das LSB einer Multiplikation mit 2 ist immer 0.

Michael Dierken schrieb:
> ..ob die unteren 8 oder oberen 8Bit bei lpm temp1, Z+ in das Register
> kopiert werden.

Welches Register? Bei der Operation sind 3 Register beteiligt, die alle 
verändert werden. Das Register temp1 erhält den Wert, der in der 
Speicherstelle steht, auf die der Z-Pointer zeigt. Der Z-Pointer, 
bestehend aus den Registern r30 und r31 (alias ZL und ZH) wird danach um 
1 erhöht, indem r30 um eins erhöht wird, und der Übertrag zu r31 addiert 
wird.

von spess53 (Gast)


Lesenswert?

Hi

>Nehmen wir den Wert $47. Dieser hat 8Bit Länge.

Wenn man führende Nullen weglässt, genaugenommen nur 7 Bit.

>Der wird nun *2 genommen wodurch ein Wert mit 9 Bit länge entsteht.
>Hier rauf bezieht sich meine Frage.

Auch nicht so richtig. $47 x 2 = $8E. Und das passt immer noch in 8 Bit. 
Erst bei Werten >7F ergibt sich bei der Verdopplung ein Übertrag.

>Meine Frage ist nun wie er das macht, dass von den 8Bit nichts verloren
>geht, da er wenn ich mich nicht irre immer nur 8Bit abspeichern kann.

Ich nehme mal an das bezieht sich auch auf LPM.

Bei z.B. ldi ZL, LOW(text*2) rechnet der Assembler erst text*2 aus, 
und lädt dann den Low-Teil davon nach ZL. Wo soll da etwas verloren 
gehen.

MfG Spess

von Michael D. (etzen_michi)


Lesenswert?

Beim schreiben der Antwort ist mir grad aufgefallen das ich da wohl doch 
noch einiges Falsch verstanden habe ... also nochmal:


Beim Befehl

ldi ZL, LOW(text*2) werden nur die Niedrigeren 8Bit der Adresse im 
Speicher für die Position der Daten text ins Register ZL geladen.

ldi ZH, HIGH(text*2) macht das gleiche mit den 8 höherwertigen Bits im 
Register ZH.

In beiden Fällen wird jeweils die Adresse *2 genommen, was aber bedeutet 
das wenn sie als MSB eine 1 hat diese Verloren geht.

Selbst wenn er diese 8Bit dann wieder durch 2teilen würde hätte er 
nichtmehr den richtigen Wert, da das damalige MSB doch nirgends mehr 
gespeichert ist und er an dieser Stelle eine 0 stzen würde.


Oder ist die Antwort von Spess so zu verstehen,
das er das sozusagen "ohne Ablegen" text*2 ausrechnet,

und dann sozusagen kurzzeitig mit 9Bit arbeitet indem er bei den 9Bit 
nur das LSB beachtet,

und das dann noch ohne extra Befehl wieder durch zwei teilt, wo der den 
Carry als MSB festlegt??


Oder wird das MSB beim übertrag automatisch als Bit0 in ZH geschrieben?

von spess53 (Gast)


Lesenswert?

Hi

Verstehe es doch mal, das wird einmal beim assemblieren vom 
Assembler ausgerechnet. Und der rechnet intern sogar mit 64 Bit.
Die Register bekommen nur das passende Stück zugewiesen.

Und wenn du das mal im Programm machen musst, wird der Übertrag durch 
das Carry-Flag berücksichtigt.
1
  ldi ZL,low(text)
2
  ldi ZH,High(text)
3
  lsl ZL
4
  rol ZH
5
6
oder
7
8
  ldi ZL,low(text)
9
  ldi ZH,High(text)
10
  add ZL,ZL
11
  adc ZH,ZH

MfG Spess

von Karl H. (kbuchegg)


Lesenswert?

Michael Dierken schrieb:

> ldi ZL, LOW(text*2) werden nur die Niedrigeren 8Bit der Adresse im
> Speicher für die Position der Daten text ins Register ZL geladen.
>
> ldi ZH, HIGH(text*2) macht das gleiche mit den 8 höherwertigen Bits im
> Register ZH.
>
> In beiden Fällen wird jeweils die Adresse *2 genommen, was aber bedeutet
> das wenn sie als MSB eine 1 hat diese Verloren geht.

Im Prinzip ja.
Nur hat eine Adresse auf dem AVR das nicht.
Eine Wort-Adresse!

Auf einem AVR sind im Assembler alle Adresse grundsätzlich immer fir 
Hälfte dessen, was du beim Byteweise durchzählen durch den Speicher 
erhalten würdest. Dies deshalb, weil AVR wortweise organisiert sind. 
Jeder Befehl hat ausnahmslos immer eine gerade Anzahl an Bytes (und 
beginnt damit zwangsweise immer an einer gerade Speicheradresse). Damit 
kann man das unterste Bit einsparen und dadurch sind alle Adressen 
grundsätzlich nur die Hälfte.
Ausnahme ist der LPM Befehl. Der will eine Byte-Adresse. Daher muss man 
das Verhalten des Assemblers ins Kalkül ziehen und selber wieder die 
Wort-Adresse verdoppeln um wieder eine Byte Adresse zu erhalten.

>
> Selbst wenn er diese 8Bit dann wieder durch 2teilen würde hätte er
> nichtmehr den richtigen Wert, da das damalige MSB doch nirgends mehr
> gespeichert ist und er an dieser Stelle eine 0 stzen würde.

Er hat den richtigen Wert, weil bei 64kByte Speicher die größtmögliche 
Wort-Adresse nun mal die Hälfte von diesen 64kByte ist.

> und dann sozusagen kurzzeitig mit 9Bit arbeitet indem er bei den 9Bit
> nur das LSB beachtet,

Was hast du immer mit deinen 9Bit

Eine Zahl wird verdoppelt und davon wahlweise das High-byte bze. das 
Low-Byte genommen. Wo ist denn da jetzt das gedankliche Problem?

> Oder wird das MSB beim übertrag automatisch als Bit0 in ZH geschrieben?

Das verdoppeln geschieht doch Bevor die Zerlegung in Low-Byte und 
High-byte gemacht wird. Das ist eine ganz normale 16-Bit Operation, die 
der Assembler da ausführt! Da sind doch Klammern rundum die der 
Assembler selbstverständlich berücksichtigt!

Wenn du willst, kannst du schreiben

   ldi  r18, (2*3)+5

und der Assembler rechnet den AUsdruck aus und erzeugt den Befehl also 
ob du geschrieben hättest

    ldi  r18, 25

machs doch nicht so kompliziert. Zusätzlich zu + - * / hast du halt noch 
die Operation "nimm das High Byte" bzw. "nimm das Low-Byte". Du 
verwendest ja auch Schiebeoperationen ganz normal

   out  portb, 1 << 5

Die Schiebeoperation wird ja auch vom Assembler durchgeführt und dann 
mit dem Ergebnis der Befehl für den AVR zusammengestellt.

   out  portb, $20

Und hier wird halt von einer 16 Bit Zahl das High Byte genommen

    ldi   r17, HIGH( $8923 )

der Assembler macht daraus

    ldi    r17, $89

und alles ist paletti.
Und bei dir ergebit sich halt jetzt die Zahl, in dem der Assembler 
vorher noch eine andere Zahl verdoppeln muss. Und diese andere Adresse 
ist 'zufällig' eine Adresse in den Speicher, die als Wort-Adresse 
angegeben ist.

Es ist doch keine Raketentechnik, wenn ein Assembler Ausdrücke berechnen 
kann und dann mit dem Ergebnis weiterarbeitet. Und ja: Der wertet seine 
Ausdrücke in 16 Bit aus.

von Michael Dierken (Gast)


Lesenswert?

Habe mir das nach ein paar Tagen Pause nochmal angeschaut und gehe mal 
davon aus das ich es jetzt verstanden habe. (Die Adresse wird sozusagen 
direkt von Compiler eringeschrieben und erst garnicht vom AVR 
rumgerechnet.)

Tut mir leid das ich euch so damit genervt habe ... aber ich will das 
nunmal nicht nur nachmachen sondern auch verstehen können.

Vielen Dank nochmal an alle die sich solche Mühe gemacht haben .. und 
das alles kostenfrei ^^.

Ich befürchte aber das ich im weiteren Laufe des Tutorials wohl nochmal 
ein oder zwei Fragen haben werde.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.