Zitat von
SebE:
Da ich aber kein Assembler kann, weis ich absolut nicht, was der macht, vor allem das FWAIT.
FWAIT (auch bekannt als WAIT) sorgt dafür, dass die CPU wartet, bis die FPU ihre aktuelle Operation beendet hat. Vor dem Pentium oder dem 80486 war die FPU ein externer, optionaler Koprozessor und musste daher mit dem Hauptprozessor synchronisiert werden. Wenn nämlich mehrere Prozessoren auf denselben Speicher zugreifen, kann es zu sogenannten Wettlaufsituation kommen. Das erste FWAIT im Trunc-Quelltext ist so ein Fall. Es trennt einen FPU- und einen CPU-Befehl, die dieselbe Speicherzelle manipulieren. Der CPU-Befehl soll natürlich erst dann ausgeführt werden, wenn der FPU-Befehl fertig ist.
Heutzutage ist die FPU in der CPU integriert. Wenn ich micht nicht irre braucht man deshalb solche FWAITs nicht mehr wirklich. Aber Borland will den Code wohl kompatibel zu älteren Prozessoren halten.
Außerdem sorgt FWAIT noch dafür, dass anliegende FPU Exceptions sofort behandelt werden und das ist auch auf modernen Prozessoren noch relevant. Denn wenn eine FPU
Exception auftritt, wie z.B. eine Division durch Null, dann passiert erst mal gar nichts. Das Programm könnte munter weiterlaufen, bis zum nächsten FPU Befehl, der auf anliegende Exceptions prüft.
Zitat:
Und warum POP't man die Register ECX, EAX, EDX am Ende, wenn man doch gar nicht's gePUSH't hat?
Auch warum man am Anfang 12 Bytes vorrutscht, versteh ich nicht.
Das hängt beides zusammen.
Der erste Befehl reserviert 12 Bytes auf dem Stack, quasi lokale Variablen. Nämlich 2 Bytes, um das aktuelle FPU Control Word zu sichern (save) und später zu restaurieren, 2 Bytes zum Basteln des neuen Control Words (scratch) und schließlich 8 Bytes für das Ergebnis.
Zitat:
Und bei den Funktionen FNSTCW und FLDCW hört's ganz auf.
FNSTCW = STore Control Word. Alle FPU Befehlsnamen fangen mit "F" an und das "N" hier bedeutet, dass anliegende FPU Exceptions nicht behandelt (also ignoriert) werden.
FLDCW = LoaD Control Word.
Das Control Word ist ein 16-Bit-Wort, das gewisse Einstellungen der FPU enthält, unter anderem wie gerundert wird und mit welcher Präzision gearbeitet wird.
Zitat:
Wäre schön, wenn sich jemand finden würde, der den Code erklären kann.
Ich hoffe, das habe ich gerade einigermaßen gut getan.
Falls nicht, hier alles nochmal zusammenhängend:
Delphi-Quellcode:
procedure _TRUNC;
asm
// Eingabe: Extended-Wert, der gerundet werden soll, in FST(0)
// Ausgabe: Int64-Wert in EDX:EAX
// Platz für lokale/temporäre Variablen auf dem Stack reservieren
SUB ESP,12
// FPU Control Word auf reservierten Stackplatz sichern
FNSTCW [ESP].Word
// save
// und noch eine zweite Kopie sichern
FNSTCW [ESP+2].Word
// scratch
// warten, bis FPU mit obigem Befehl fertig ist
FWAIT
// bestimmte Bits der 2. Stack-Kopie des Control Words auf 1 setzen, sodass FPU auf 0
// abrundet (also Nachkommastellen abschneidet) und mit voller Präzision rechnet
OR [ESP+2].Word, $0F00
// trunc toward zero, full precision
// modifizierte Stack-Kopie des Control Words in FPU laden und somit aktivieren
FLDCW [ESP+2].Word
// jetzt kommt die eigentliche Arbeit ;)
// Gleitkommawert, der sich gerade in ST(0) befindet (also unser Parameter) als
// Integer auf den Stack nach [ESP+4] speichern und dabei gemäß des aktuellen
// Control Words runden (truncate!)
// Außerdem wird der FPU-Registerstack "gePOPpt", aber das tut hier nichts zur Sache
FISTP qword ptr [ESP+4]
// wenn eine FPU Exception aufgetreten ist, dann jetzt sofort behandeln
// Es könnte ja versucht worden sein, einen Wert zu runden, der so groß (oder so klein)
// ist, dass er nicht mehr als Int64 dargestellt werden kann. Wenn das der Fall war,
// wollen wir an dieser Stelle informiert werden und nicht erst unabsehbar später.
FWAIT
// ursprüngliches FPU Control Word wieder restaurieren (zurück in die FPU laden)
FLDCW [ESP].Word
// ursprüngliches und modifiziertes Control Word vom Stack entfernen
// die Werte sind jetzt unwichtig, man könnte genausogut ADD ESP,4 schreiben, aber
// der Befehl POP ECX ist kleiner und ECX darf gefahrlos verändert werden
POP ECX
// gerundetes Ergebnis vom Stack entfernen und in die Register EDX:EAX laden
POP EAX
POP EDX
end;