MASM32-SDK (Windows Konsolapplikationen):
Arbeiten mit dem FPU-Stackregistern (Einführung FPU Teil 2)

Auf dieser Seite wird ein erweitertes Übungsbeispiel für den Umgang mit den FPU-Stackregistern erarbeitet. Ausgangspunkt ist die Ermittlung der Windungsanzahl einer einlagigen Zylinderspule für einen Schwingkreis für die Hochfrequenztechnik. Der technische Hintergrund steht in „Das Spulennomogramm”. Auf der gleichen Seite findet sich ein entsprechendes BASIC-Programm mit den mathematischen Formeln:
m = s + d / 2
f = (d / 2) ^ 2 * 3.14
w = SQR((l * 1000 * m) / (4 * 3.14 * f))

In der üblichen Infix-Schreibweise enthalten die verwendete mathematische Formeln Klammern, Bruchstriche und das Wurzelzeichen. Damit wird die Reihenfolge des Rechenvorgangs bestimmt. In der Assemblervariante hat man diese elegante Möglichkeit zur Bestimmung der Reihenfolge nicht. Hier muss der Programmierer die Reihenfolge der einzelnen Rechenschritte im Programmablauf festlegen: Er fungiert quasi als „Preprozessor”.

Die FPU-Stackregister
Der mathematische Coprozessor (FPU) verwendet für Gleitkommazahlen acht Register die als Kellerspeicher (Stack) organisiert sind. Sie heißen st(0) bis st(7). Alternativ kann das oberste Stackregister st(0) als st angesprochen werden.

Werte werden in das oberste FPU-Stackregister durch Befehle wie fld, fild und fbld eingetragen.

Werte aus dem obersten FPU-Stackregister lassen sich mit Befehlen wie fst, fstp, fist, fistp und fbstp auslesen. Die Befehle, die auf stp enden räumen dabei den Wert vom Stack ab. Die Befehle mit st am Ende speichern den Wert im Hauptspeicher ohne ihn vom Stack zu entfernen.

Die FPU speichert ihre Daten in ihren Registern immer im 80 bit langen Gleitkommaformat (Real). Beim Rechnen nutzt die FPU grundsätzlich mindestens 19 Dezimalstellen.

Eine Besonderheit stellt die Verwendung der Zahlendarstellung im „Packed Decimal-Formats” dar. Es handelt sich um eine Sonderform der Darstellung als „Binary Coded Decimal” (BCD). Dabei werden in jedem Halbbyte nur die binären Darstellungen der Ziffern von 0 bis 9 genutzt. Bei der FPU verwendet das Packed Decimal-Format 10 Bytes zur Darstellung von 18 Ziffern. Die so dargestellte Zahl ist immer ein Ganzzahl (Integer). Das höchste Bit des höchsten Bytes (Byte 19) ist das Vorzeichenbit: Ist es gesetzt, ist die Zahl negativ. Ist es nicht gesetzt, ist die Zahl positiv. Die übrigen 7 bits von Byte 19 werden ignoriert.

Die hexadezimale Darstellung der Zahl -65536 im „Packed Decimal-Formats” ist:
80000000000000065536h

Da bei Intel Daten im „little–endian-Format” gespeichert werden, würde dies im Speicherdump so aussehen:
36 55 06 00 00 00 00 00 00 80

Im Datenbereich des Speichers werden Zahlen des Packed Decimal-Formates mit dem Schlüsselwort tbyte deklariert.

Genau zwei Befehle beschäftigen sich mit der Übertragung von Daten zwischen dem Datenbereich des Hauptspeichers und dem obersten FPU-Stackregister:

  • FBLD quelle lädt das oberste FPU-Stackregister vom Datenbereich des Speichers
  • FBSTP ziel speichert den Inhalt des FPU-Stackregisters in den Datenbereich des Speichers und entfernt den Wert aus dem FPU-Stack.

Für Details sei auf die beiden Befehlsbeschreibungen in SIMPLY FPU by Raymond Filiatreault verwiesen.


Bei dem folgenden Programm handelt es sich um ein Übungsprogramm. Somit fehlt die Eingabe der in die Berechnung eingehenden Daten per Tastatur. Wie dies im Prinzip gemacht wird, geht aus dieser vorhergehenden Webseite hervor.
; Beispiel Berechnung einer einlagigen Zylinderspule
; Siehe dazu das BASIC-Programm auf: https://fredriks.de/dl8wa/d2.php?f=2
;
; moegliche Eingabedaten:
; l = Induktivität in uH
; d = Spulendurchmesser in cm
; s = Spulenlänge  in cm
; Zwischenwerte:
; r = Radius der Spule aus d/2
; m = s + r
; f = r ^ 2 * 3.14
; Ergebnis Windungszahl
; w = SQR((l * 1000 * m) / (4 * 3.14 * f))
; bzw. gekuerzt auf
; w = SQR((l * 250 * m) / (3.14 * f))
; -------------------------------------------
 .486
  .model flat, stdcall
  option casemap:none   ; case sensitive

; -------------------------------------------
.XLIST
  include \masm32\include\windows.inc
  include \masm32\macros\macros.asm       ; MASM support macros
  include \masm32\include\user32.inc
  include \masm32\include\masm32.inc
  include \masm32\include\kernel32.inc
  include \masm32\include\Fpu.inc
  include \masm32\include\msvcrt.inc

  includelib \masm32\lib\user32.lib
  includelib \masm32\lib\masm32.lib
  includelib \masm32\lib\kernel32.lib
  includelib \masm32\lib\Fpu.lib
  includelib \masm32\lib\msvcrt.lib

.LIST
; -------------------------------------------
.data
w       dt     0        ;Windungszahl
l       dt     0        ;Induktivität in uH
d       dt     0        ;Spulendurchmesser in cm
s       dt     0        ;Spulenlaenge in cm

;Eingabedaten
l_inp   db      "0.21",0
d_inp   db      "1",0
s_inp   db      "1",0
m       dt     0        ;Zwischenergebnis
f       dt     0        ;Zwischenergebnis
r       dt     0        ;Spulenradius in cm

;Zur Anzeige
w_txt   db      "Windungsanzahl: "
w_zahl  db      34 dup(0)
l_txt   db      13,10,"Induktivitaet (uH): "
l_zahl  db      34 dup(0)
d_txt   db      13,10,"Spulendurchmesser (cm): "
d_zahl  db      34 dup(0)
s_txt   db      13,10,"Spulenlaenge (cm): "
s_zahl  db      34 dup(0)

MsgBoxCaption db    "Spulendaten",0

.code
start:

;konvertiere d_inp in REAL10 und speichere es in der FPU
    invoke FpuAtoFL, ADDR d_inp, 0, DEST_FPU
;errechne Radius und speichere ihn in r
    invoke FpuDiv, 0, 2, ADDR r, SRC1_FPU or SRC2_DIMM or DEST_MEM
;konvertiere s_inp in REAL10 und speichere sie in der FPU
    invoke FpuAtoFL, ADDR s_inp, 0, DEST_FPU
;errechnet m = s + r und speichere m
    invoke FpuAdd, 0, ADDR r, ADDR m, SRC1_FPU or SRC2_REAL or DEST_MEM
;errechnet f = r ^ 2 * 3.14 und speichere in f
    invoke FpuMul, ADDR r, ADDR r, 0, SRC1_REAL or SRC2_REAL or DEST_FPU
    invoke FpuMul, 0, FPU_PI, ADDR f, SRC1_FPU or SRC2_CONST or DEST_MEM

;Ermittlung der Windungsanzahl bei vorgegebenen Induktivitaet
;errechne phi * f  und speichere Produkt in f
    invoke FpuMul, FPU_PI, ADDR f, ADDR f, SRC1_CONST or SRC2_REAL or DEST_MEM

;errechne l * 250 * m  und speichere Produkt in FPU
    invoke FpuAtoFL, ADDR l_inp, 0, DEST_FPU
    invoke FpuMul, 0, 250, 0, SRC1_FPU or SRC2_DIMM or DEST_FPU
    invoke FpuMul, 0, ADDR m, 0, SRC1_FPU or SRC2_REAL or DEST_FPU
;Division, dann Wurzel ziehen
    invoke FpuDiv, 0, ADDR f, 0, SRC1_FPU or SRC2_REAL or DEST_FPU
    invoke FpuSqrt, 0, 0, SRC1_FPU or DEST_FPU

;konvertiere windungsanzahl in ASCII mit 2 nachkommstellen
    invoke FpuFLtoA, 0, 2, ADDR w_zahl, SRC1_FPU or SRC2_DIMM or DEST_MEM

;Ausgabe der Spulendaten. l, d und s werden aus den Eingabedaten uebernommen
;szCopy quelle ziel
invoke szCopy, ADDR l_inp, ADDR l_zahl
invoke szCopy, ADDR d_inp, ADDR d_zahl
invoke szCopy, ADDR s_inp, ADDR s_zahl
invoke szCatStr, ADDR w_txt, ADDR l_txt
invoke szCatStr, ADDR w_txt, ADDR d_txt
invoke szCatStr, ADDR w_txt, ADDR s_txt

invoke MessageBox, 0, ADDR w_txt, ADDR MsgBoxCaption, MB_OK
invoke ExitProcess, 0
end start
Ergebnisanzeige Spule berechnen Bild links: Die Ergebnisanzeige.
Berechnet wurde lediglich die Anzahl der Windungen.

Bemerkungen zum Programm

Das Programm besteht fast ausschließlich aus Aufrufen der beim MASM32-SDK mitgelieferten FPU-Funktionen. Sie erleichtern die Programmierung der FPU. Allerdings müssen sie die beim Aufruf übergebenen Parameter auswerten. Deshalb benötigen sie mehr Rechenzeit als die individuelle Nutzung der entsprechenden FPU-Befehle.

SIMPLY FPU by Raymond Filiatreault ist eine sehr 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