16-Bit-Schleifen mit 8051

OP #5132212
Lesenswert?

Hallo zusammen!

Ich benötige zum Auslesen eines Speicherbereichs eine 16-Bit-Schleife, 
die aus den Registern R0 (Highbyte) und R1 (Lowbyte) bestehen soll.

Der Befehl "DJNZ Loop" scheint gewisse Probleme mit dem Überlauf zu 
haben, wenn der Startwert "00h" ist. Da wird dann 00h zu FFh und die 
Schleife wird 255 mal durchlaufen obwohl eigentlich Null mal durchlaufen 
werden müsste.

Und wie sieht es eigentlich aus mit den Werten 255/256 ?
Wenn der 16-Bit-Zähler auf 0100h steht, müsste der da die Schleife 256 
mal durchlaufen oder 255 mal ? Wie würde ich die 256 erreichen wenn das 
Lowbyte der Schleife nur maximal 255 mal durchlaufen wird ?

Weiss da jemand etwas dazu ?
Moderator (Firma: Titel) Persönliche Seite #5132222
Lesenswert?

H-G S. schrieb:
> Wenn der 16-Bit-Zähler auf 0100h steht, müsste der da die Schleife 256
> mal durchlaufen oder 255 mal ?
Reine Definitionssache.
Gegenfrage dazu: wie oft müsste dein Zähler die Schleife durchlaufen, 
wenn da z.B. der Wert 0004h drin steht?

Mit der Antwort auf diese Frage lässt sich dann leicht sagen, wie oft er 
die Schleife durchlaufen muss, wenn 256 = 0100h drin steht...

> Weiss da jemand etwas dazu ?
Bitte nicht Plenken!
Gast #5132281
Lesenswert?

Ralf G. schrieb:
> Das ist kein Problem, das ist genauso (wie der Name schon sagt)
> dokumentiert!

Und noch dazu ist es logisch:
Decrement: 00h -> ffh
Jump not zero: ja, ist ja nicht 0

Das Missverständnis liegt nicht daran, dass der Prozessor nicht logisch 
denken kann, sondern der Programmierer.

H-G S. schrieb:
> obwohl eigentlich Null mal durchlaufen
> werden müsste.

Noch ein kapitaler Denkfehler: 1mal wird die Schleife IN JEDEM FALL 
ausgeführt, DJNZ kommt ja erst am Schluss.

Es gibt noch sooo viel zu lernen...

Georg
OP #5132366
Lesenswert?

Ich werde die alte Holzhammer-Methode verwenden:    :-)

- Den 16-Bit-Zähler laden mit Wertebereich 0001h-XXXXh.
- Am Schleifenende eine volle 16-Bit-Subtraktion (minus 0001h) 
durchführen.
- Wenn beide Bytes des Zählers 0 sind dann Schleife verlassen.




Das Beispielprogramm von Peter Danegger zählt scheinbar durch Null, das 
ist auch nicht so günstig scheint mir - da kommt wieder das 
255/256-Problem hoch. Doch die Vergleichs-Befehle scheinen nützlich - 
die habe ich komplett vergessen!


Und hört auf mich als dumm hinzustellen  :-)
Ich weiss genau was der DJNZ-Befehl macht und dass die Schleife 
mindestens einmal durchlaufen wird.
Gast #5132503
Lesenswert?

H-G S. schrieb:
> Ich weiss genau was der DJNZ-Befehl macht und dass die Schleife
> mindestens einmal durchlaufen wird.

H-G S. schrieb:
> obwohl eigentlich Null mal durchlaufen
> werden müsste.

Ganz offensichtlich weisst du das nicht. Als Anfänger dumm sein ist 
durchaus möglich, dumm bleiben wollen und jeden anmeckern, der auf 
Fehler aufmerksam macht, ist aber keine gute Idee.

Georg
Gast #5132616
Lesenswert?

Sinnvoll ist der Befehl (djnz) ja auch am ENDE eines Stücks Code. Wo 
zum Anfang gesprungen wird, wenn noch nicht auf 0 dekrementiert ist.

Wenn er einmal durchlaufen werden soll, stellt man zu Anfang 1 ein.
Wenn er zweimal durchlaufen werden soll, 2.


Bei DJNZ ist die Variable auch kaum als Indexzähler geeignet, so dass 
ein Decrement am Anfang blöd wäre.

Und wenn man mehr als 255 Bytes kopieren will, man also ggf. eine äußere 
Schleife braucht, ist es auch schöner, wenn man mit Startwert 0 dann 
auch 256 Durchläufe am Stück hinkriegt. Bei 0 Durchläufen wird der ganze 
kladeradatsch garnicht aufgerufen.
Persönliche Seite #5133337
Lesenswert?

H-G S. schrieb:
> Hallo zusammen!
>
> Ich benötige zum Auslesen eines Speicherbereichs eine 16-Bit-Schleife,
> die aus den Registern R0 (Highbyte) und R1 (Lowbyte) bestehen soll.
>
> Der Befehl "DJNZ Loop" scheint gewisse Probleme mit dem Überlauf zu
> haben, wenn der Startwert "00h" ist. Da wird dann 00h zu FFh und die
> Schleife wird 255 mal durchlaufen obwohl eigentlich Null mal durchlaufen
> werden müsste.
>
> Und wie sieht es eigentlich aus mit den Werten 255/256 ?
> Wenn der 16-Bit-Zähler auf 0100h steht, müsste der da die Schleife 256
> mal durchlaufen oder 255 mal ? Wie würde ich die 256 erreichen wenn das
> Lowbyte der Schleife nur maximal 255 mal durchlaufen wird ?
>
> Weiss da jemand etwas dazu ?

Lass dich nicht drausbringen.

Wenn ein Register R1 mit dem Wert s1 = 1...255 oder 0 initialisiert ist, 
kannst du beim 8051 mit DJNZ zunächst eine Schleife genau 1...256 mal 
durchlaufen. Das gilt auch für andere 8-bit-µC mit DJNZ.

Also:
1
    MOV  R1,#s1
2
loop1:
3
    Befehlsfolge
4
    DJNZ R1,loop1
Der Wert Null ist bei DJNZ also äqivalent mit 256 Schleifendurchgängen.
Bei anderen µC nimmt man statt DJNZ einfach zwei Befehle, nämlich DEC 
und BRNE (je nach µC).

Falls man mehr als 256 Schleifendurchgänge für eine Befehlsfolge 
benötigt, nimmt man entweder zwei verschachtelte Schleifen, also:
1
    MOV  R0,#s2
2
loop2:
3
    MOV  R1,#s1
4
loop1:
5
    Befehlsfolge
6
    DJNZ R1,loop1
7
    DJNZ R0,loop2
Die Zahl der Schleifendurchgänge ist dann s1 * s2, reicht also von 1 bis 
2^16.

Oder falls sich die Zahl der Schleifendurchgänge nicht als Produkt von 
zwei 8-Bit-Werten (jeweils 1...256) darstellen lässt, kommt man an einer 
16-Bit-Arithmetik für den Schleifenzähler nicht vorbei, die aber kaum 
aufwendiger ist.
#5133410
Lesenswert?

Eberhard H. schrieb:
> Oder falls sich die Zahl der Schleifendurchgänge nicht als Produkt von
> zwei 8-Bit-Werten (jeweils 1...256) darstellen lässt, kommt man an einer
> 16-Bit-Arithmetik für den Schleifenzähler nicht vorbei,

Das stimmt nicht ganz!
Wenn man dein Beispiel etwas umstellt:

Eberhard H. schrieb:
>    MOV  R0,#s2
>    MOV  R1,#s1
> loop1:
>     Befehlsfolge
>     DJNZ R1,loop1
>     DJNZ R0,loop1

Jetzt wird zuerst die "Befehlsfolge" s1-mal durchlaufen und danach (s2 
mal 256).
So kann man jeden Wert von 1 bis 65536 erreichen.
Du hast aber Recht, es läuft auf eine einfache 16-Bit Arithmetik hinaus.
Persönliche Seite #5133458
Lesenswert?

Route 6. schrieb:
> Eberhard H. schrieb:
>> Oder falls sich die Zahl der Schleifendurchgänge nicht als Produkt von
>> zwei 8-Bit-Werten (jeweils 1...256) darstellen lässt, kommt man an einer
>> 16-Bit-Arithmetik für den Schleifenzähler nicht vorbei,
>
> Das stimmt nicht ganz!
> Wenn man dein Beispiel etwas umstellt:
>
> Eberhard H. schrieb:
>>    MOV  R0,#s2
>>    MOV  R1,#s1
>> loop1:
>>     Befehlsfolge
>>     DJNZ R1,loop1
>>     DJNZ R0,loop1
>
> Jetzt wird zuerst die "Befehlsfolge" s1-mal durchlaufen und danach (s2
> mal 256).
> So kann man jeden Wert von 1 bis 65536 erreichen.

Sehr wohl stimmt mein Programm, und zwar in Unterschied zu deiner 
Korrektur ganz allgemein für alle s1, s2 = 1 bis 255 und Null:
1
    MOV  R0,#s2
2
loop2:
3
    MOV  R1,#s1
4
loop1:
5
    Befehlsfolge
6
    DJNZ R1,loop1
7
    DJNZ R0,loop2

Dein Sonderfall gilt nur für s1 = 0, denn er verwendet die Tatsache, 
dass R1 nach DJNZ R0,loop1 bereits Null ist.

Dieser Sonderfall s1 = 0 ist bei meiner allgemeinen Version wegen MOV 
R1,#s1 enthalten und somit lassen sich mit s1 = s2 = 0 auch 2^16 = 65536 
Durchläufe erreichen.

Falls s1 <> 0, wird bei dir die innere Schleife mit DJNZ R1,loop1 nur 
einmal richtig durchlaufen (nämlich s1 mal, danach immer 256 mal), da R1 
nicht neu geladen wird.

Die Gesamtzahl der Durchläufe ist bei deiner Version also (s2-1)*256 + 
s1, was vielleicht für andere Schleifenzahlen praktisch ist, die nicht 
per s1*s2 dargestellt werden können.
Beitrag #5133496 wurde von einem Moderator gelöscht.
OP #5133513
Lesenswert?

Die aktuelle Schleifenversion ist auf dem angehängten Scan - den 
Kontrast konnte ich nicht verbessern ...


Es wäre natürlich gut, wenn Eberhard H.´s  Schleife funktionieren würde.
Das würde ein paar Programmzeilen sparen.


Edit: aber vielleicht gibt es Probleme mit dem kurzen/relativen 
Rücksprung, da in der Schleife recht viele Befehle stehen werden.
Angehängte Dateien:
Persönliche Seite #5133536
Lesenswert?

H-G S. schrieb:
> Die aktuelle Schleifenversion ist auf dem angehängten Scan - den
> Kontrast konnte ich nicht verbessern ...

Du musst nach der Befehlsfolge erst 1 subtrahieren und dann 
abfragen/springen (sinngemäß wie bei DJNZ) und nicht umgekehrt, denn 
sonst stimmt die Zahl der Durchläufe nicht wie erwartet.

>
> Es wäre natürlich gut, wenn Eberhard H.´s  Schleife funktionieren würde.

Sie funktioniert wie beschrieben.

>
> Edit: aber vielleicht gibt es Probleme mit dem kurzen/relativen
> Rücksprung, da in der Schleife recht viele Befehle stehen werden.

Das ist dann ein 8051-Problem.
Gast #5133565
Lesenswert?

H-G S. schrieb:
> aber vielleicht gibt es Probleme mit dem kurzen/relativen
> Rücksprung, da in der Schleife recht viele Befehle stehen werden.

1. Es gibt auch lange Sprünge
2. Es gibt Unterprogramme

Vielleicht wäre es doch ganz gut, erst mal die einfachsten Grundlagen zu 
lernen.

Georg
OP #5133624
Lesenswert?

Eberhard H. schrieb:
> Du musst nach der Befehlsfolge erst 1 subtrahieren und dann
> abfragen/springen (sinngemäß wie bei DJNZ) und nicht umgekehrt, denn
> sonst stimmt die Zahl der Durchläufe nicht wie erwartet.

Stimmt!
Erst kommt die Subtraktion von 0001h, dann die Abfrage ob der Zähler auf 
0000h steht.
Danke dir!

Edit: Der Wertebereich der Schleife geht ja von 0001h bis xxxxh, also 
größer 0000h als Starwert. Daher kann sofort 0001h subtrahiert werden 
ohne einen Überlauf zu provozieren.

Peter D. schrieb:
> Warum so umständlich?
> Ist Dir diese Lösung zu einfach:
> Beitrag "Re: 16-Bit-Schleifen mit 8051"

Die ist mit im Moment zu relativ wegen der kurzen Sprungdistanz ... mal 
schauen wieviele Byte in der Schleifenmitte stehen dann kann ich sie mir 
nochmal anschauen.
Persönliche Seite #5133631
Lesenswert?

Peter D. schrieb:
> H-G S. schrieb:
>> Die aktuelle Schleifenversion ist auf dem angehängten Scan
>
> Warum so umständlich?
> Ist Dir diese Lösung zu einfach:
> Beitrag "Re: 16-Bit-Schleifen mit 8051"

Ich bin zwar kein 8051-Programmierer, aber wie willst du damit 2^16 
Durchläufe von "Code" erreichen?
1
weiter:
2
        nop             ; Code
3
anfang:                 ; hier Einsprung in die Funktion
4
        dec     r0
5
        cjne    r0, #0ffh, weiter
6
        dec     r1
7
        cjne    r1, #0ffh, weiter
8
        ret

2^16 Durchläufe klappen damit nur, wenn man in "weiter" statt in 
"anfang" einspringt und r0, r1 vorher mit 0ffh lädt. Ansonsten hat man 
wenigstens einen Durchlauf vergeigt.

Oder man vergleicht mit 0 statt mit 0ffh und löscht r0 und r1 vor dem 
Einsprung in "weiter".

Das wäre dann die "lange" Version von Route 66, die man mit zwei 
verschiedenen Rücksprungadressen und erneutem Laden von r0 wiederum 
verallgemeinern kann.

Der geringste Aufwand sind vermutlich zwei verschachtelte DJNZ-Schleifen 
mit den Anfangswerten Null und bei Bedarf eine zu lange Befehlsfolge 
("Code") in ein Unterprogramm zu verlagern.

Edit: Sehe gerade, dass CJNE auch nur einen relativen Sprung mit 8 Bit 
schafft, also ist es keine "lange" Version und benötigt sogar noch 2 
Bytes mehr (2x DEC) als mit DJNZ.
OP #5134242
Lesenswert?

Peter D. schrieb:
> Hä?
> R0, R1 sind doch Register. Der Abbruchtest ist immer gleich, auch bei
> Deinem Code.

Ups ... ich dachte die ffh seien Platzhalter für den Start-/Zählwert.
Doch es sind Überlauf-Testwerte. Der Zählwert steht ja in R0/R1.

Sieht sehr kompakt aus die Methode - sollte man sich merken und mal 
durchrechnen.

Die 256 als Wert scheint ja wichtig weil die 1. Bitstelle im High-Wort 
den Wert 256 hat und somit muss wohl die innere Schleife wirklich 256 
mal durchlaufen werden   :-)

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren