Schnell-Kurs: FPU (Assembler)

Dies hier ist ein sehr kompakter Schnell-Kurs für Assembler für den FPU (Floating Point Unit).
Dabei werden die wichtigsten Eigenschaften zum Assembler-Programmieren erklärt.
In diesen Dokumenten sind grundsätzlich alle ASM-Funktionen und Register groß geschrieben, bei Variabeln und Namen wird nur der erste Buchstaben groß geschrieben.

FPU-Assembler ganz Kurz

WRITEME

Um Fließpunktmathematik zu ermöglichen, wurde ein spezieller Coprozessor, die Floating Point Unit (FPU), entwickelt, der diese Operationen beschleunigt. Mit diesem wurde auch ein Satz neuer Funktionen eingeführt. Ab 486 ist die FPU interner Bestandteil des CPU-Hauptprozessors; bei früheren CPU-Typen mußte die FPU zusätzlich als ein externer Prozessor in das System eingebaut werden.

Beschreibung-Kurz-FPU
OpCodes-FPU
Mathe-Trigonometrie

FPU-Register

Die FPU benutzt eigene Operationsregister, die wie ein Stack organisiert sind. Es gibt insgesamt 8 FPU-Register die ST genannt werden, und diese werden numeriert von ST0 bis ST7. Um also mit FPU-Registern rechnen zu können, müssen die Werte in diese Register geladen werden. Anders als bei den CPU-Registern aber werden die Werte dabei immer in den ST0 kopiert und alle Werte werden um ein Register nach hinten verschoben.
Die mathematischen Operationen benutzen fast immer ST0 und können zusätzlich ein beliebiges weiteres ST-Register mit einbeziehen. Eine Operation wie zB. ST4 = ST2 + ST0 ist so nicht möglich, nur zB. ST0 = ST0 + ST3, da das Resultat immer auf ST0 steht, und ST0 auch mit berechnet wird.

Im ASM-Code gibt es für einige Operationen Standardregister, wenn keine Parameter angegeben werden. Die Stack-Register/Parameter sollten hier in diesen Codes aber immer angegeben werden, um die Register eindeutig zu bezeichnen und den Code lesbarer zu machen, auch wenn dies nicht zwingend notwendig ist -ebenso, um Funktionen erkennen zu können, die keine Parameter besitzen, und dadurch nur auf ST0 anwendbar sind.
ST0 oder ST wird auch TOS (Top of Stack) genannt.

Direkter Austausch von Daten zwischen CPU und FPU Registern ist nicht möglich, Daten müssen über den Stack oder RAM ausgetauscht werden.
Leider haben auch die FPU-Flags keine direkte CPU Verbindung, also müssen auch diese über Stack bzw. RAM ausgetauscht werden zB. um mit Sprungbefehlen auf Vergleiche zu regieren, da die FPU auch keine Sprungbefehle kennt.

RegisterBeschreibung
ST(?)Ist die korrekte Schreibweise für die Parameter. ist nichts angegeben ist ST(0) meistens der Standard.
STbedeutet das es ST0 sein muß, da nur dieses erlaubt ist.
ST?Wird für die Stack Beschreibung der Einfachheit halber verwendet (also zB. ST(0) statt ST0).


? --> 0-7 Platzhalter für 8 FPU-Register

Datenformate

Es werden 3 verschiedene Genauigkeiten unterstützt. Die Information teilt sich in Mantisse Exponent und das letze Bit als Signet.
Der FPU rechten intern immer mit dem 80-Bit Datenformat, egal welches Datenformat geladen wurde. Andere Formate werden beim Laden in 80-Bit konvertiert und umgekehrt.

BitSigExponentMantisseVB-Format
321724Single
6411053Double
8011564(keines)

Kommutativgesetz

Operationen die nicht kommutativ (vertauschbar) sind, das heißt die Reihenfolge der Parameter eine Rolle spielt, müssen zusätzlich eine Funktion besitzen der die Möglichkeit bietet die Parameter umzukehren.
Für kommutative Operationen wie FADD FMUL gibt es keinen Reverse-Funktion da unnötig (A = A + B ist gleich wie A = B + A), für FSUB FDIV gibt es FSUBR FDIVR (weil A = A - B nicht gleich ist wie A = B - A).
Eine Funktion wäre so zu verstehen, FSUBRP -> Fpu SUB Reverse Pop

    FADD     ST, ST(1)   ; -- ST0 = ST0 + ST1
    FADD     ST(1), ST   ; -- ST1 = ST1 + ST0, Resultat ist identisch
 
    FSUB     ST, ST(1)   ; -- ST0 = ST0 - ST1
    FSUB     ST(1), ST   ; -- ST1 = ST1 - ST0, Resultat ist NICHT identisch
    FSUBR    ST, ST(1)   ; -- ST0 = ST1 - ST0, Reverse

Operation mit POP

Es ist möglich bei einer mathematischen Operation gleichzeitig ein Stack-Pop durchzuführen, der Code kann so optimiert werden.

Normalerweise werden Resultate auf ST gespeichert. Hier bleiben die Werte unverändert

    FADD     ST, ST(1)   ; -- ST0 = ST0 + ST1
    FADD     ST(1), ST   ; -- ST1 = ST1 + ST0, das ist auch möglich


Wird aber nach der Berechnung der Stack reduziert (durch POP), geht der erste Stack ST0 verloren, deshalb haben die Funktionen die ein POP mit einbeziehen keine ST, ST(1) Parameter-Reihenfolge, da es sinnlos wäre ein Wert in den ST zu speichern der durch den Pop dann verloren geht.

    FADDP    ST(1), ST   ; <- ST0 = ST1 + ST0, hier wird der Stack nach der Berechnung reduziert

FPU Varianten

Allgemein gelten folgende Schreibweisen

    ;- Normal
    FSUB     ST, ST(1)   ; -- ST0 = ST0 - ST1
    FSUB     ST(1), ST   ; -- ST1 = ST1 - ST0
 
    ;- Reverse
    FSUBR    ST, ST(1)   ; -- ST0 = ST1 - ST0
    FSUBR    ST(1), ST   ; -- ST1 = ST0 - ST1
 
 
    ;- POP, Normal
    FSUBP    ST(1), ST   ; <- ST0 = ST1 - ST0
 
    ;- POP, Reverse
    FSUBRP   ST(1), ST   ; <- ST0 = ST0 - ST1
 
 
    ;- Speicher
    FSUB     [Speicher]  ; -- ST0 = ST0 - [Speicher]
 
    ;- Speicher, Reverse
    FSUBR    [Speicher]  ; -- ST0 = [Speicher] - ST0
 
 
    ;- Integer
    FISUB    [Speicher]  ; -- ST0 = ST0 - [Speicher]
 
    ;- Integer, Reverse
    FISUBR   [Speicher]  ; -- ST0 = [Speicher] - ST0
 
 
    ;- FALSCH
    FSUB     ST(1), ST(2)       ; eine solche Operation ist nicht möglich, ein Operator muß ST0 sein
    FSUB     ST(1), [Speicher]  ; Speicher-Operationen können nur auf ST0 ausgeführt werden

FPU-Stack

Wie erwähnt sind die FPU Register Stack ähnlich aufgebaut, und so werden Werte über Push Pop ähnliche Funktionen in die Regsiter geladen.

Wieso eigentlich der FPU-Stack und nicht die Möglichkeit jedes Register einzeln anzusprechen was wesentlich mehr Möglichkeiten bieten würde?
Die Antwort liegt in der Hardware Konstruktion des Prozessors und in der Zeit in welcher FPU entwickelt wurde. Damals mußte man Platz sparen, und auch die Befelsabarbeitung ist schneller wenn die Funktionen kürzer sind. Also versuchte man mit möglichst wenig Platzverbrauch die FPU-Funktionen zu realisieren, das waren 3 Bits welche 8 Kombinationen erlauben. Weitere 8 Bist waren nicht mehr möglich da auch noch Informationen zur Funktion selbst kodiert werden müssen. Also löste man das über die Stack-Struktur und konnte so 8 Werte abbilden in dem man aber dennoch nur 1 Register exakt angeben kann und das zweite Register (das TOS), fest einbaut.

Werden zu viele Werte in die Register geladen, wird ein Stack-Overflow Fehler im FPU ausgelöst.

    FLD1                ; -> ST0 = 1    : Schreibt 1 in ST0
    FLD  ST(0)          ; -> ST0 = ST0  : Kopiert ST0 in ST0
    FADD ST(0), ST(0)   ; -- ST0 = ST0 + ST0
    FLD  Val1           ; -> ST0 = Val1 : Schreibt Val1 in ST0

Danach sieht der Inhalt der FPU-Register so aus:

ST0= Val1
ST1= 2
ST2= 1
ST3-ST7Leer


    FSTP Val2           ; <- Val2 = ST0 : Schreibt ST0 in Val2

Danach sieht der Inhalt der FPU-Register so aus:

ST0= 2
ST1= 1
ST2-ST7Leer

FPU Kommentar für Stack und Registerinhalt

Um den Code leserlich zu machen werden hier in den Asm-Codes die Stackbewegungen im Kommentar beschrieben, und der Inhalt aller Register nach dem durchführen der Operation aufgelistet.

ASM FPU Kommentar-Format
Stack-Bewegung
--Keine FPU-Stack Bewegung
->PUSH auf FPU-Stack
<-POP vom FPU-Stack
Resultate
ST? = Val1Es wurde eine Berechnung durchgeführt und gespeichert
ST?=Val1Es wurde ein Resultat unverändert beibehalten, und vielleicht auf ein anderes Register verschoben
Spezielle Operationen im Kommentar
<?>Vergleichen
<=>Vertauschen

FPU-Flag

Der FPU hat sein eigenes Flag-Register auf dem Informationen ausgegeben und Fehler angezeigt werden.

FlagBitBeschreibung
C008Bedingungscode-Bit 0
C109Bedingungscode-Bit 1
C210Bedingungscode-Bit 2
C314Bedingungscode-Bit 3
IE00Ungültigkeits-Status IE (invalid Operation exception)
DE01Unmaskiert-Status DE (denormalized operand exception)
ZE02Nulldivisions-Status ZE (Zero divide exception)
OE03Überlauf-Status OE (overflow exception)
UE04Unterlauf-Status UE (underflow exception)
PE05Genauigkeits-Status PE (precision exception)
B15Busy-Status B (busy)
IR07Interrupt-Status IR (interrupt request)
ST..11Stapelzeiger-Bit 0
ST..12Stapelzeiger-Bit 1
ST..13Stapelzeiger-Bit 2
-06(Reserviert)

Assembler/Schnell-Kurs_FPU.txt · Zuletzt geändert: 2010/06/15 19:13 von 129.187.209.151