Index

Aufbau und Prinzipien der Arbeitsweise des Rechners

2. Untersuchung einfacher Programmstrukturen


[Zuweisungsoperationen] [Terme] [Verzweigungen] [Wiederholungen]

Wiederholungen

Im letzten Teil untersuchen wir, wie Schleifen auf Maschinenebene realisiert weden.

a) For-Schleife

Als Klickprozedur wird nachfolgende Zählschleife programmiert:

procedure TfoAnwendung.buRunClick(Sender: TObject);
var i, a: integer;
begin
  a := 1;
  for i := 1 to 10 do a := a + 1;
  lbAusgabe.Caption := IntToStr(a);
end;

Delphi übersetzt die Prozedur in folgenden Maschinencode:

BB01000000 mov ebx,$00000001 Variable a
B80A000000 mov eax,$0000000a Variable i
43 inc ebx Erhöhe a um 1
48 dec eax Erniedrige i um 1
75FC jnz -$04 Springe, falls nicht 0, 4 Byte zurück

Zunächst fallen einige Besonderheiten auf:

Die Anweisung a := a + 1 wird durch die effektive Anweisung inc (= increment) realisiert.
Der Schleifenzähler wird auf den Endwert initialisiert und dann erniedrigt. Auch hierbei kommt die effektive Anweisung dec (= decrement) zum Einsatz.
Der Sprung zum Anfang der Schleife erfolgt mit jnz -$04. Dabei wird der EIP um 4 erniedrigt, da FC ein negativer Wert, nämlich -4 darstellt. Wir wissen bereits, dass jnz das Zero-Flag auswertet. Wie aber wird dieses gesetzt? Es fehlt scheinbar eine Compare-Anweisung.
Hier kommt eine weitere Besonderheit von inc bzw dec
zum Tragen. Diese Anweisungen setzen je nach Ergebnis unter anderem das Zero-Flag (ZF) und das Parity-Flag (PF). Beim Einzelschrittmodus lässt sich das auch gut verfolgen. Delphi stellt im CPU-Fenster Veränderungen, die bei einer Anweisung auftreten, rot dar.
So kann tatsächlich der nötige Rücksprung ermittelt werden ohne eine weitere Compare-Anweisung. Jetzt wird auch klar, warum die Schleife rückwärts gezählt wird, da die Auswertung sonst nicht auf diese Weise erfolgen könnte.

b) Repeat-Schleife

Als nächstes betrachten wir die Repeat-Schleife:

procedure TfoAnwendung.buRunClick(Sender: TObject);
var a, b: integer;
begin
  a := 11;
  b := 2;
  repeat
    a := a - b;
  until a < b;
  lbAusgabe.Caption := IntToStr(a);
end;

Der Maschinencode, den Delphi hierzu erzeugt, sieht folgendermaßen aus:

BB0B000000 mov ebx,$0000000b Variable a
B802000000 mov eax,$00000002 Variable b
2BD8 sub ebx,eax Subtrahiere EBX - EAX (= a - b),
Ergebnis in EBX (= a)
3BC3 cmp eax,ebx Vergleiche EAX - EBX (= b - a)
7EFA jle -$06 Springe, falls kleiner oder gleich 0 um 6 Byte zurück

Zunächst wird der Schleifenkörper ausgeführt (sub-Anweisung).
Danach erfolgt ein compare von b - a. Die Abbruchbedingung a < b ergibt umgeformt b - a > 0. Da der Sprung jedoch als Wiederholung ausgeführt wird, nämlich zurück zum Schleifenbeginn, hat dieser für b - a <= 0 zu erfolgen. Das bedeutet bei compare müsste in diesem Fall SF oder ZF gesetzt sein: jle (= jump less or equal 0) führt aber genau in diesem Fall zur Sprungausführung. Es wird um 6 Byte zurückgesprungen, also zum Schleifenkörperbeginn (sub-Anweisung).

c) While-Schleife

Als letztes betrachten wir die While-Schleife:

procedure TfoAnwendung.buRunClick(Sender: TObject);
var a, b: integer;
begin
  a := 11;
  b := 2;
  while a >= b; do a := a - b;
  lbAusgabe.Caption := IntToStr(a);
end;

Der Maschinencode, den Delphi hierzu erzeugt, sieht folgendermaßen aus:

BB0B000000 mov ebx,$0000000b Variable a
B802000000 mov eax,$00000002 Variable b
3BC3 cmp eax,ebx Vergleiche EAX - EBX (= b - a)
7F06 jnle +$06 Springe, falls nicht kleiner oder gleich 0 um 6 Byte weiter
2BD8 sub ebx,eax Subtrahiere EBX - EAX (= a - b),
Ergebnis in EBX (= a)
3BC3 cmp eax,ebx Vergleiche EAX - EBX (= b - a)
7EFA jle -$06 Springe, falls kleiner oder gleich 0 um 6 Byte zurück

Die Abfrage a >= b wird umgeformt in b - a <= 0. Die Anweisung jnle (= jump not less or equal 0) führt also den Sprung aus, falls not (SF or ZF). Hieraus folgt SF = 0 und ZF = 0, d.h. der Vergleich b - a war größer 0. Man erkennt, dass der Sprung ausgeführt wird, wenn die Abfrage b - a <= 0 falsch ist, also die Schleife nicht ausgeführt werden darf. Das Sprungziel ist die nächste Anweisung nach dem Schleifenkörper. Es werden 6 Byte übersprungen. Dies sind die nächsten drei Anweisungen zu je 2 Byte. Damit ist die Ausführungsbedingung realisiert.
Man sollte nun annehmen, dass am Ende des Schleifenkörpers ein unbedingter Rücksprung zur Ausführungsbedingung erfolgt. Die Lösung sieht hier anders aus.
Nach einem nochmaligen Compare-Vergleich erfolgt dieses Mal der Sprung im entgegengesetzten Fall wie bei der Ausführungsbedingung: jle wird also ausgeführt, wenn der Schleifenkörper wiederholt werden muss. Es wird um 6 Byte nach vorne übersprungen, was zum Beginn des Schleifenkörpers führt (sub-Anweisung).
Damit ist der zweite Teil der Schleife eigentlich eine Schleife mit Wiederholbedingung am Schluss. Dies entspricht aber der Repeat-Schleife.

Die While-Schleife wird also als Repeat-Schleife mit zusätzlicher Eintrittsbedingung realisiert.

Die nachfolgenden Diagramme zeigen den Ablauf im Vergleich:
Ablauf bei der Repeat-Schleife Ablauf bei der While-Schleife


[Zuweisungsoperationen] [Terme] [Verzweigungen]

[Index] [Einführung] [Logische Schaltelemente - CPU]


Autor: Jürgen Dehmer
Letzte Änderung: 29.02.2004