Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   for Schleife in ASM (https://www.delphipraxis.net/73133-schleife-asm.html)

Luckie 12. Jul 2006 23:45


for Schleife in ASM
 
Liste der Anhänge anzeigen (Anzahl: 1)
Nehmen wir mal folgenden Code:
Delphi-Quellcode:
var
  ar               : array[0..5] of integer = (0, 1, 2, 3, 4, 5);

  i, r             : integer;
begin
  r := 0;
  for i := 0 to 5 do
  begin
    inc(r, ar[i]);
  end;
  Writeln(r);
Egal was er macht, es ist jedenfalls eine Schleife, die wegen der Optimierung rückwärts läuft.

Der produzierte ASM Code sieht aus, wie im Anhang (CPU Fenster).

da steht jetzt:
Code:
dec edx
jnz -$08
jnz bedeutet, "Spring, wenn das Zero Flag nicht null ist." Aber das Zero Flag ist die ganze Zeit null. Kann man auch im Screenshot sehen. Erst wenn edx mit der Zählvariablen 0 erreicht hat, wird das Zero Flag auf 1 gesetzt und er springt nicht mehr. Der Code verhält sich also genau gegenteilig wie er eigentlich sollte. Oder habe ich da jetzt ein Verständnisproblem?

Muetze1 12. Jul 2006 23:51

Re: for Schleife ein ASM
 
JNZ = jump if not ZF

Die bedeutet, springe, wenn das Zero Flag (ZF) im Flags register nicht gesetzt ist. Wenn also ZF = 0, dann ist es nicht gesetzt. Es geht hier nicht um die 0 oder 1 welches ZF annehmen kann, sondern das Z steht für das Zero Flag. Und dieses ist nunmal 1, wenn eine vorherige mathematische Operation eine 0 ergeben hat. Mit anderen Worten: So lange EDX nicht 0 erreicht hat (was das ZF setzen würde), springe relativ 8 Bytes zurück (springe wenn ZF nicht gesetzt, jump if not ZF)

Luckie 12. Jul 2006 23:54

Re: for Schleife ein ASM
 
Hm. Dann habe ich das doch nur falsch verstanden. In meinem ASM Buch ist das aber auch etwas blöd erklärt. Puh ich dachte schon meine CPU wäre kaputt. :mrgreen:

Luckie 13. Jul 2006 00:01

Re: for Schleife ein ASM
 
Nachtrag:
Zitat:

An dieser Stelle wird die Zählvariable dekrementiert mit dec. dec hat die Eigenschaft, dass wenn das dekrementierte Register null wird das Zero Flag (ZF) zusetzen. Dies wird ausgenutzt, um mittels des Sprungbefehls jnz ("springe, wenn das Zero Flag (ZF) im Flags register nicht gesetzt ist") zu entscheiden, ob gesprungen werden muss oder nicht. Ist das Zero Flag nicht gesetzt (null), wird gesprungem und zwar in diesem Fall an eine Adresse 8 Byte zurück: $-08, was Adresse 00402561 wäre, und wie man sieht, ist das unser Schleifenrumpf. Ansonsten wird nicht gesprungen und im Code weitergemacht.

Und das, die Überprüfung eines Flags im Flagregister, ist einfachher und schneller als das Vergleichen zweier Register auf Gleichheit / Ungleichheit. Und das müsste man machen, wenn die Schleife vorwärts läuft, denn dann muss man ja die Zählvariable immer mit dem Endwert vergleichen, was bedeuten würde, dass man er mit dem ASM-Befehl cmp zwei Register vergleicht und erst dann ein Flag im Flagregister auswerten kann.
Stimmt der Text so?

Muetze1 13. Jul 2006 00:13

Re: for Schleife ein ASM
 
Ok, ansonsten nochwas:

jz = jump zero
jnz = jump not zero

Springe, wenn 0
Springe, wenn nicht 0

So kann man es sich vllt. auch leichter merken. Grundlegend hat es dein Assemblerbuch dann aber wirklich schlecht übersetzt (?) bzw. erklärt.

/EDIT: Ja, der Text stimmt so. Du hast nur einmal "register" klein geschrieben (wie zitiert). Ansonsten stimmt es so. Es klingt etwas verwirrend und man muss es zweimal lesen. Wenn das ein Tutorial werden soll, dann müsste man den ersten Teil vllt. nochmal anders formulieren um ihn zu entschärfen, weil beim ersten lesen verwirrt einen diese umgekehrte Logik recht stark.

Ansonsten noch ein Tipp: Als Argument kann man auch gleich nochmal die Opcodegrösse für CMP mit JE/JNE im Vergleich zu JNZ anführen. Ansonsten wäre auch noch allgemein anzumerken, dass sehr viele Operationen die Flags beeinflussen und viele Operationen vor allem das ZF mit setzen bzw. löschen. Dazu gibt es in den Unterlagen von Intel und AMD gute Tabellen. So z.B. bei AMD im Dokument #24594 AMD64 Architecture Programmer's Manual, Volume 3, General-Purpose and System Instructions auf Seite 178 (Dokument Seite 146) die Dokumentation der bedingten Sprünge und im Anhang E (Appendix E) findest du eine Tabelle wo aufgelistet wird, welches Flag im EFLAGS Register von welcher Instruktion beeinflusst wird (Seite 517, Dokument Seite 485).

Luckie 13. Jul 2006 00:17

Re: for Schleife in ASM
 
Ich habe es etwas umformuliert:
Zitat:

An dieser Stelle wird die Zählvariable dekrementiert mit dec. dec hat die Eigenschaft, dass wenn das dekrementierte Register null wird das Zero Flag (ZF) zusetzen. Dies wird ausgenutzt, um mittels des Sprungbefehls jnz ("springe, wenn das Zero Flag (ZF) im Flagsregister nicht gesetzt ist") zu entscheiden, ob gesprungen werden muss oder nicht. Ist das Zero Flag nicht gesetzt (null), wird gesprungen und zwar in diesem Fall an eine Adresse 8 Byte zurück: $-08, was Adresse 00402561 wäre, und wie man sieht, ist das unser Schleifenrumpf. Ansonsten wird nicht gesprungen und im Code weitergemacht.

Und das, die Überprüfung eines Flags im Flagregister, ist einfacher und damit schneller als das Vergleichen zweier Register auf Gleichheit / Ungleichheit. Und das müsste man machen, wenn die Schleife vorwärts läuft. In diesem Fall muss man die Zählvariable immer mit dem Endwert vergleichen, was bedeuten würde, dass man er mit dem ASM-Befehl cmp zwei Register vergleicht und erst dann ein Flag im Flagregister auswerten kann.

Muetze1 13. Jul 2006 00:19

Re: for Schleife in ASM
 
Jo, so lässt es sich einfach und verständlich in einem Zuge lesen. Ist nicht mehr so verwirrend wie zuvor. :thumb:

Luckie 13. Jul 2006 00:20

Re: for Schleife in ASM
 
OK, dann kommt es jetzt auf meine HP. ;)

http://www.michael-puff.de/Developer...ackwards.shtml

Muetze1 13. Jul 2006 00:26

Re: for Schleife in ASM
 
Doch noch was, da ich nicht genauen Artikel kenne, als Hinweis:

Zitat:

Zitat von Luckie
... wird gesprungen und zwar in diesem Fall an eine Adresse 8 Byte zurück: $-08, was Adresse 00402561 wäre, und wie man sieht, ist das unser Schleifenrumpf.

Die Instruktion steht an Adresse 0x00402567 und du springst 0x08 Bytes zurück und kommst auf 0x00402561 anstatt laut "Adam Riese" auf 0x0040255F. Daher die Frage ob du vorher schon darauf eingegangen bist, dass der Instruktion Pointer genutzt wird beim Sprung um die 8 Bytes ab zu ziehen und da er die Instruktion gerade abarbeitet, steht dieser schon auf der nächsten, also 0x00402569 und somit zieht er von dieser 8 Bytes ab.

Ich denke dass kann bei einer einzeln hier so gefallenen Äusserung ohne Erklärung zu Verwirrungen führen, da viele einfach die Instruktion suchen und von dem Offset subtrahieren, welcher links an dieser steht...

/EDIT: Was im Artikel nicht geklärt wird, bzw. worauf nicht hingewiesen wird...

Luckie 13. Jul 2006 00:31

Re: for Schleife in ASM
 
Wie bitte? Das habe ich jetzt, galube ich, nicht ganz verstanden. Es wird nicht acht Byte vom Offset zurückgesprumgen, sondern acht Byte vom Instruktionszeiger? Ist das gemeint?

Ich habe es jetzt so
Zitat:

in diesem Fall 8 Byte zurück: -$08, was Adresse $00402561 beim Instruktionszeiger entspricht, da der Instruktionszeiger schon bei $00402569 steht, und wie man sieht, ist das unser Schleifenrumpf
umformuliert.

Muetze1 13. Jul 2006 00:41

Re: for Schleife in ASM
 
Wenn Leute deine Erklärung lesen und schauen wollen wie er springt, dann suchen sie das JNZ -$08 und das steht am Offset 0x00402567 (siehe Anhang, dein Bild verschandelt, bezugnehmend auf oberen blauen Rahmen). Wenn man nun von dieser Adresse die gesprungenen 8 Bytes abzieht, dann kommt man aber auf eine falsche Adresse, und zwar auf 0x0040255F. An der Stelle steht man in mitten einer Instruktion und der Leser versteht die Welt nicht mehr. Daher vllt. darauf hinweisen, dass der Offset des relativen Sprungs auf die Adresse 0x00402569 (siehe Anhang, bezugnehmend auf unteren blauen Kasten) addiert (durch das Vorzeichen des Sprung-Offsets von -8 verringert sich die Adresse) und man erhält die von dir beschriebene Adresse 0x00402561 und damit die richtige Position.

Nun klarer? Das mit dem Instruktion Zeiger brauchste nicht erwähnen, weise nur darauf hin, dass die Leser bitte die 8 Bytes nicht von 0x00402567 abziehen (wo der Sprungbefehl laut Screenshot steht) sondern von der Adresse 0x00402569.

/EDIT: Jetzt hast du es erneut umformuliert und es ist ok. Aber ansonsten, vllt. findest du eine klein wenig einfachere Beschreibung, wenn du den Begriff des Instruktionszeigers erst gar nicht einführst und einfach nur auf die richtige Adresse zum subtrahieren hinweist, wie du es ja grundsätzlich schon tust. Da der Begriff des Instruktionszeigers nie erklärt oder eingeführt wird, verwirrt er die Leute mehr, da sie keine Informationen für diesen neuen Begriff bekommen...

(Man merkt, dass ich mal Chefredakteur war, oder? *g* (keine Angst, nur eine Schülerzeitung))

Luckie 13. Jul 2006 00:46

Re: for Schleife in ASM
 
Jetzt aber:
Zitat:

in diesem Fall 8 Byte zurück: -$08, was Adresse $00402561 ist, ausgehend von Adresse $00402569
;)

Muetze1 13. Jul 2006 00:48

Re: for Schleife in ASM
 
:thumb: Super, kurz, knapp, verständlich.

(Falls nun noch einer nachfragt warum die andere Adresse, kannst du ihm es jederzeit erklären...)

Und Gute Nacht (ich sollte schon seit min. 2 Stunden im Bett liegen...)

Luckie 13. Jul 2006 00:50

Re: for Schleife in ASM
 
Zitat:

Zitat von Muetze1
Und Gute Nacht (ich sollte schon seit min. 2 Stunden im Bett liegen...)

Ich auch. :?

jbg 13. Jul 2006 06:23

Re: for Schleife in ASM
 
Zitat:

Das interessante aber passiert in dem markiertren Block darunter
Das Interessanteste hast du aber nicht markiert, und zwar die Zeile
Delphi-Quellcode:
add eax,$04
Denn mit dieser Zeile bewegt sich das Array vorwärts, womit die Abarbeitung der Schleife trotz der rückwärtslaufenden Zählvariable, vorwärts geht.


Alle Zeitangaben in WEZ +1. Es ist jetzt 13:54 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz