MASM32-SDK (Windows Konsolapplikationen):
Gleitkommazahl und Mathematischer Coprozessor (Einführung FPU Teil 1)

Es soll ein Programm entwickelt werden, das aus einem Radius die Kreisfläche ermittelt. Das geschieht mit der Formel
Radius * Radius * Kreiszahl = Kreisfläche. Gerechnet wird in zwei Schritten: Radius * Kreiszahl = Kreisumfang
Radius * Kreisumfang= Kreisfläche.

Die Kreiszahl ist keine Ganzzahl, sondern sie hat eine Wert von etwa 3,1425926534... Aber auch der Radius muss nicht unbedingt eine Ganzzahl sein. Er kann z.B 25,4 mm betragen. Solche Zahlen werden als Gleitkommazahlen bezeichnet. Intern sind sie aus drei Bestandteilen zusammengesetzt:

  • Vorzeichen,
  • Position des Dezimalkommas bzw. des Dezimalpunktes,
  • Ziffernfolge ohne das Dezimalkomma. Es wurde entfernt.
Gleitkommazahlen belegen im Speicher 4 Bytes oder - bei sogenannter doppelter Genauigkeit - 8 Bytes:

Beispiel: Definieren von Gleitkommazahlen

dd und dq entscheiden über die Länge der internen Darstellung. Enthält die darzustellende Zahl einen Dezimalpunkt, entsteht eine Gleitkommadarstellung (Real). Fehlt der Dezimalpunkt, entsteht eine Ganzzahldarstellung (Integer). Ein Vorzeichen wird im werthöchsten Bit der internen Darstellung gekennzeichnet. Im Beispiel ist dies Bit immer 0, denn es kommen nur positive Zahlen vor.
 00000044 00000001		dd1     dd      1       ;Ganzzahl 1
 00000048 3F800000		dd10    dd      1.0     ;Gleitkommazahl 1
 0000004C 3F8CCCCD		dd11    dd      1.1     ;Gleitkommazahl 1,1
 00000050 3F99999A		dd12    dd      1.2     ;Gleitkommazahl 1,1
;Es folgen Zahlendefinitionen mit 8 Bytes Speicherbelegung
 00000054			dq1     dq      1       ;Ganzzahl 1
	   0000000000000001
 0000005C			dq10    dq      1.0     ;Gleitkommazahl 1
	   3FF0000000000000
 00000064			dq11    dq      1.1     ;Gleitkommazahl 1,1
	   3FF199999999999A
 0000006C			dq12    dq      1.2     ;Gleitkommazahl 1,2
	   3FF3333333333333

Außerdem existiert noch eine 10 Byte lange Darstellung von Gleitkommazahlen. Sie basiert auf binär codierter Dezimalzahlendarstellung, d.h. die Inhalte der beteiligten Halbbytes sind auf die binäre Darstellung der Ziffern von 0 bis 9 beschränkt.

Insgesamt können Gleitkommazahlen im Speicher mittels

  • real4, dword oder dd,
  • real8, qword oder dq,
  • real10 tbyte oder dt
deklariert werden. Bei Nichtverwendung der mit „real” beginnenden Deklarationsschlüsselworten wird jedoch eine Ganzzahl erzeugt, sofern kein Dezimalpunkt bei der Wertangabe vorhanden ist.

Das Rechnen mit Gleitkommazahlen überlässt man zweckmäßigerweise dem mathematischen Coprozessor. Er ist ab der CPU 80486 Bestandteil der CPU. Allerdings benötigt er ein eigenes Programmierverfahren.

Bei Verwendung des MASM32-SDKs wird die Programmierung des mathematischen Coprozessors (FPU wie floating point unit) wesentlich erleichtert: Es steht eine geeignete Hilfsbibliothek zur Verfügung. Sie ermöglicht die Nutzung der FPU, ohne dass man allzu tief in deren Besonderheiten einsteigen muss.


Das folgende Assemblerprogramm nutzt die Hilfsbibliothek, um die Kreisfläche zu errechnen. Der einzige Schönheitsfehler ist, dass sich der Radius nicht über die Tastatur eingeben lässt. Aber das Programm zeigt, wie man ASCII-Strings passend für die Verarbeitung durch die FPU aufbereiten kann. Außerdem zeigt es, wie man das von der FPU ermittelte Ergebnis auf dem Bildschirm anzeigen kann.
Bei dem folgenden Programm handelt es sich um ein Übungsprogramm. Somit fehlt die Eingabe des Kreisradius per Tastatur. Wie dies im Prinzip gemacht wird, geht aus einer vorherigen Webseite hervor.
;Beispiel Errechnen der Kreisflaeche.
;Es handelt sich um eine Ueberarbeitung eines Beispiels
;aus der Hilfe des MASM32-SDKs. Ziel der Ueberarbeitung
;war es, ein wesentlich unkomplizierteres und somit
;leichter verstaendliches Beispiel zu schaffen.
;Es wird zunaechst ein Kreisumfang aus Radius * Phi und anschliessend
;die zugehoerige Kreisflaeche aus Umfang * Radius errechnet.
;Die ermittelte Kreisflaeche wird mit einer Messagebox dargestellt.

  .486
  .model flat, stdcall
  option casemap :none   ; case sensitive

; -------------------------------------------
.XLIST
  include \masm32\include\windows.inc
  include \masm32\include\user32.inc
  include \masm32\include\kernel32.inc
  include \masm32\include\Fpu.inc

  includelib \masm32\lib\user32.lib
  includelib \masm32\lib\kernel32.lib
  includelib \masm32\lib\Fpu.lib
.LIST
; -------------------------------------------
.data
radius        dt    0
phi           dt    0           ;unbenutzter Speicherplatz
radius_inp    db    "2",0
phi_inp       db    "3.141592654",0
result        db    25 dup(0)
MsgBoxCaption db    "Kreisflaeche",0

.code

start:
;konvertiere radius_inp in REAL10 und speichere es als radius
    invoke FpuAtoFL, ADDR radius_inp, ADDR radius, DEST_MEM

;convert phi_inp in REAL10 und speichere es in der FPU
    invoke FpuAtoFL, ADDR phi_inp, 0, DEST_FPU

;Multipliziere radius mal phi. dies ergibt den umfang. er steht in der fpu
    invoke FpuMul, 0, ADDR radius, 0, SRC1_FPU or SRC2_REAL or DEST_FPU

;multiply radius mal umfang. dies ergibt die flaeche. sie steht in der fpu
    invoke FpuMul, 0, ADDR radius, 0, SRC1_FPU or SRC2_REAL or DEST_FPU

;konvertiere das ergebnis (die flaeche) in ASCII mit 4 nachkommstellen
    invoke FpuFLtoA, 0, 4, ADDR result, SRC1_FPU or SRC2_DIMM
    invoke MessageBox, 0, ADDR result, ADDR MsgBoxCaption, MB_OK

    invoke ExitProcess, 0
end start
Ergebnisanzeige Kreisfläche berechenen Bild links: Die Ergebnisanzeige.
Berechnet wurde die Fläche eines Kreises mit dem Radius 2.

Interpretation des Programmes

  • invoke FpuAtoFL, ADDR radius_inp, ADDR radius, DEST_MEM: Konvertiert die ASCII-Zahl in radius_inp in einer Gleitkommazahl. Die Gleitkommazahl steht anschließend im Speicher (DEST_MEM) im Feld radius.
  • invoke FpuAtoFL, ADDR phi_inp, 0, DEST_FPU: Konvertiert die in ASCII angegebene Kreiszahl 3.14... in eine Gleitkommazahl. Die Gleitkommazahl steht anschließend in der obersten Stackposition der FPU (DEST_FPU). Es gibt übrigens einen speziellen Befehl, der die Kreiszahl in die oberste Stackposition der FPU einträgt. Er heißt fldpi und hat keine Operanden.
  • invoke FpuMul, 0, ADDR radius, 0, SRC1_FPU or SRC2_REAL or DEST_FPU: Multipliziert die im Speicherfeld radius hinterlegte Gleitkommazahl mit der an oberster Stackposition der FPU stehenden Gleitkommazahl. Das Ergebnis steht anschließend in der obersten Stackposition der FPU (DEST_FPU).
    Die Beschreibung des Befehls ist etwas verwirrend:
     Src1 × Src2 → Dest
    FpuMul (
    lpSrc1    // pointer to, or value of, 1st parameter
    lpSrc2    // pointer to, or value of, 2nd parameter
    lpDest    // pointer to destination of result
    uID       // ID flags for sources and destination
    )
    • lpSrc1: This either points to an 80-bit REAL number or to a DWORD integer, or is an immediate DWORD integer or one of the FPU constants FPU_PI or FPU_NAPIER (see FPU Constants for description), all according to the content of uID. (This parameter is ignored if uID indicates that the value is already on the FPU.) Der hier angegebene Parameterwert ist 0. Er wird ignoriert.
    • lpSrc2: This either points to an 80-bit REAL number or to a DWORD integer, or is an immediate DWORD integer or one of the FPU constants FPU_PI or FPU_NAPIER, all according to the content of uID. (This parameter is ignored if uID indicates that the value is already on the FPU.) Der hier angegebene Parameterwert ist ADDR radius.
    • lpDest: This points to a TBYTE where the 80-bit REAL result of the multiplication will be returned. The result can instead be retained on the FPU according to the content of uID. (This parameter is ignored if uID indicates that the result should remain on the FPU.) Der hier angegebene Parameterwert ist 0. Er wird ignoriert.
    • uID: Only one of the SRC1_? flags must be OR'ed with only one of the SRC2_? flags and OR'ed with one of the DEST_? flags. (The DEST flag does not need to be OR’ed if the destination is in memory; that is the default destination.) Der hier angegebene Parameterwert ist SRC1_FPU or SRC2_REAL or DEST_FPU. Demnach ist
      der erste Wert bereits im Stack der FPU,
      der zweite Wert eine REAL10-Zahl die nicht im Stack der FPU steht
      und das Ergebnis der Multiplikation soll im Stack der FPU abgelegt werden.
    uID Flag     Meaning
    SRC1_FPU     Src1 is already on the FPU
    SRC1_REAL    Src1 is a pointer to an 80-bit REAL number
    SRC1_DMEM    Src1 is a pointer to a 32-bit signed integer
    SRC1_DIMM    Src1 is a 32-bit signed integer
    SRC1_CONST   Src1 is one of the special FPU constants
    SRC2_FPU     Src2 is already on the FPU
    SRC2_REAL    Src2 is a pointer to an 80-bit REAL number
    SRC2_DMEM    Src2 is a pointer to a 32-bit signed integer
    SRC2_DIMM    Src2 is a 32-bit signed integer
    SRC2_CONST   Src2 is one of the special FPU constants
    DEST_FPU     The result remains on the FPU
    DEST_MEM     lpDest is a pointer to a TBYTE
                 (this is the default and does not need to be indicated)
    

Linkhinweis
SIMPLY FPU by Raymond Filiatreault ist eine gut verständliche Beschreibung der FPU-Programmierung. Der Text ist in amerikanischem Englisch.
Letztes Upload: 24.03.2023 um 11:35:14 • Impressum und Datenschutzerklärung