Der TT-Coprozessor in TURBO C

Da freut man sich, daß TURBO/PURE C den Coprozessor des TT unterstützt. Der Compiler macht seine Sache auch wunderschön. Doch nun die schlechte Nachricht: Es gibt da einen Spielverderber, nämlich die Standardbibliothek. Und ohne sie läuft halt wenig. Da bleibt einem nichts anderes, als Ärmel hochkrempeln und sich selber ans Werk machen.

Als ich zum ersten Mal den Speedup durch die FPU testen wollte, schrieb ich ein kleines Programm in TURBO C, das ja eine Option für 68882-Unterstützung besitzt. Doch leider erschienen sehr seltsame Zahlen auf dem Bildschirm. Nach etwas Rätselraten und anschließender Analyse des erzeugten Assembler-Codes war der Schuldige entlarvt: Der vom Compiler produzierte Code verwendet FPU-Befehle und übergibt auch die Parameter in FPU-Registern. Die Funktionen in der Standardbibliothek TCFLTLIB.LIB erwarten aber ihre Parameter auf dem Stack und rechnen alles „zu Fuß“ aus. Einen Coprozessor kennen sie zwar schon, können ihn aber nur über Speicheradressen ansprechen (wie es beim 68000 nötig ist), was auf dem TT nicht mehr geht. Fazit: Die FPU wird nur für die Grundrechenarten benutzt, und man kann nicht einmal einen Sinus ausrechnen.

Viel Einfaches

Die Lösung des Dilemmas war auch sofort klar: Selbst ist der Mann! Die meisten benötigten Funktionen sind Gott sei Dank direkt durch FPU-Befehle realisierbar. Dementsprechend findet man im Quelltext (Listing 1) viele Zweizeiler: FPU-Befehl und RTS. Etwas mehr Aufwand erfordern die Rundungsfunktionen ceil(), floor(), round() und trunc(). Da man nicht davon ausgehen darf, daß sich die FPU in einem bestimmten Rundungsmodus befindet, muß man den gerade gesetzten Zwischenspeichern, bevor man das Gewünschte einstellt. Den Rest erledigt dann der Befehl FINT. Für trunc() kann man sich das Ganze allerdings sparen, da es einen Befehl. FINTRZ gibt, der immer den „to zero“-Modus verwendet.

Bei frexp() tritt das Problem auf, daß laut TURBO-C-Konvention die gelieferte Mantisse im Bereich von 0.5 bis 1.0 liegen soll, der Befehl FGETMAN jedoch eine zwischen 1.0 und 2.0 erzeugt. Man muß also nachkorrigieren, sprich die Mantisse halbieren und dementsprechend den Exponenten um Eins erhöhen. Der hierbei verwendete Befehl FSCALE #x,FPy multipliziert FPy mit 2^x, was schneller ist als ein FDIV.

Bild 1: Das Packed-Decimal-String-Format

Nur Tipparbeit

Es gibt jedoch auch Funktionen, die nicht direkt in die FPU eingebaut sind. Hier wären asinh(), acosh() und die Potenzierung zu nennen. Die beiden Area-Funktionen müssen über ihre Definition berechnet werden, die Formeln dazu stehen im Listing. Bei der Potenzierung nach b^e = exp(e*ln(b) hat man folgendes Problem: Mathematisch ist die Potenz zu einer negativen Basis nur definiert, wenn der Exponent ganzzahlig ist. Das Programm muß also gegebenenfalls mit dem Betrag der Basis rechnen und bei ungeradem Exponenten das Ergebnis wieder negativ machen. Getestet wird, indem man den Exponenten in ein Integer wandelt und anschließend mit dem Fließkommawert vergleicht. Will man eine Undefinierte Potenz berechnen, wird der Logarithmus einer negativen Zahl genommen, was korrekt E_DOMAIN zur Folge hat.

Neue Funktionen

Die FPU hat sogar noch ein paar Funktionen mehr als die TURBO-C-Bibliothek bietet. In NEWMATH.LIB sind auch diese für C nutzbar gemacht. Hierunter fallen log2() (Zweierlogarithmus) sowie exp2() und exp10() als Umkehrfunktionen zu den Logarithmen. Auch kann der 68882 Sinus und Cosinus simultan berechnen. Die C-Funktion dazu ist sincos(), die den Sinus als Ergebnis liefert, der Cosinus wird über Zeiger zurückgegeben.

Komplett geändert wurde die Fehlerbehandlung. Da es äußerst unpraktisch ist, nach jeder Operation die Variable errno zu testen, gibt es in NEWMATH.LIB die Funktionen fclrerr() und ferr(). Die beiden Funktionen machen das Exception Accrued Byte der FPU zugänglich, indem sich diese alle Fehler merkt, bis sie ausdrücklich gelöscht werden. Man macht also vor einer Reihe von Operationen fclrerr() und kann nach deren Ausführung mit ferr() testen, ob bei irgendeiner davon ein Fehler auftrat. Die Rückgabewerte von ferr() sind in NEWMATH.H definiert und selbsterklärend. Es können auch Kombinationen der einzelnen Codes auftreten.

Die FPU kann als Ergebnisse auch Unendlich (z.B. ln 0) oder NAN („Not A Number“ = mathematisch sinnlos, z.B ln -1) zurückliefern. Solche Werte kann man mit NEWMATH.LIB durch die Funktion ftest() einfach erkennen. Es wird 1 geliefert, wenn das Argument ein NAN war, und 2 für Unendlich. Bei „normalen“ Zahlen erhält man 0. Aus dem Vorzeichen des Rückgabewerts erkennt man das Vorzeichen des Parameters (es gibt + und - Unendlich/NAN!).

Die Funktion fpumode(), die in der alten Bibliothek dazu diente, den Coprozessor ein- und auszuschalten, wird in NEWMATH.LIB verwendet, um die Rechengenauigkeit und den Rundungsmodus der FPU einzustellen. Der 68882 rechnet intern mit etwas mehr Genauigkeit, als nach außen sichtbar ist. Nach jedem Rechenschritt wird dann das Ergebnis auf die eingestellte Präzision zurechtgestutzt, wobei der eingestellte Rundungsmodus angewendet wird.

Bild 2: Funktionsweise des UNPK-Befehls

Der dicke Brocken

So weit, so gut: Jetzt können wir rechnen. Doch die Ergebnisse auf den Bildschirm bringen, ist wieder ein anderes Kapitel. Denn printfl) aus TCSTDLIB.LIB beherrscht sowieso keine Fließkommazah-len, und mit dem printf() aus TCFLTLIB.LIB hat man dieselben Schwierigkeiten wie mit allen anderen Routinen darin. Daher gibt es in NEWMATH.LIB auch eine Funktion zur Ausgabe von floats. Es wäre mir jedoch wesentlich zuviel Arbeit geworden, ganz printf() mit seinem riesigen Funktionsumfang neu zu schreiben. Meine Routine printfl() gibt sich dagegen ganz bescheiden aus, aber man sieht immerhin etwas Vernünftiges auf dem Bildschirm. Es wurde auf alle Formatierungs-Features verzichtet, nur Zahlen, die größer als 99999 oder kleiner als 0.01 sind, werden im Exponentialformat ausgegeben, alle anderen ohne Exponent. Schließlich sieht „10“ schöner aus als „1.0e+1“. '

Die Hauptarbeit dabei - das Umwandeln von Binär- in Dezimaldarstellung - können wir getrost der FPU überlassen. Sie kennt nämlich auch ein BCD-Format für Fließkommazahlen, das sog. Packed-Decimal-String-Format. Dabei entsprechen je 4 Bit einer Ziffer. Eine kurze Beschreibung dieses Formats finden Sie in Bild 1, für Genaueres möchte ich auf Literatur verweisen [1,2]. Die Umwandlung von BCD in ASCII erleichtert sich ebenfalls durch den 68030-Befehl UNPK („unpack“), dessen Funktionsweise man in Bild 2 dargestellt findet.

printfl() muß also eigentlich nur noch unnötige Nullen am rechten Rand streichen und entscheiden, ob Darstellung mit oder ohne Exponent angewandt werden soll. Natürlich werden auch NANs und Unendlich korrekt ausgegeben. Aufgrund der vielen Abfragen, die sich ergeben, wird die Routine leider etwas unübersichtlich und lang. Ich hoffe jedoch, daß sie mit Hilfe des Kommentars einigermaßen verständlich ist, und will hier im Text nicht ausführlich auf die Programmierung eingehen.

Ebenso wie bei printf() gibt es auch bei printfK) Varianten, die die Zahl statt auf den Bildschirm in ein File oder einen String ausgeben. Die Benutzung von flprintfl() und sprintfl() ist genau analog zu den Varianten von printfl). Realisiert werden sie durch einen Zeiger auf die Ausgaberoutine für printfl(), der je nach Einsprungpunkt auf den entsprechenden Geräte-Handler gesetzt wird. printfl() gibt genauso wie printf() die Anzahl der ausgegebenen Zeichen zurück.

Installation

Um NEWMATH.LIB zu installieren, übersetzen Sie den Quelltext von Listing 1 mit dem MAS-Assembler des TURBO-C-Pakets, benennen die entstehende .O-Datei in NEWMATH.LIB um und kopieren sie ins \LIB-Verzeichnis des Compilers. Sie wird dann anstelle von TCFLTLIB.LIB in der Projektdatei angegeben. Die für NEWMATH.LIB benötigten Prototypen und Definitionen finden sich in Listing 2. NEWMATH.H wird anstelle des alten MATH.H verwendet.

Damit kann man dann den Coprozessor des TT auch von C aus richtig nutzen, wenn auch der Overhead der Unterprogrammaufrufe etwas Zeit kostet. Praktischer wäre es natürlich, wenn der Compiler selbst z.B. den Befehl FSIN einsetzen würde, wenn die Funktion sin() im Programmtext steht. Aber das wird wohl ein frommer Wunsch bleiben. Vielleicht findet sich auch jemand, der sich die Mühe macht, ein vollständiges printfl() mit FPU-Unterstützung zu schreiben.

Literatur:

[1] Steve Williams: „68030 Assembly Language Reference“, Addison-Wesley, 1989, ISBN 0-201-08876-2

[2] Jochen Fischer: „Assembler ohne Grenzen“, ST-Computer 12/91, S. 100 ff.

; ----------------------------------------------
;                    NEWMATH.S
;
; Mathematische Bibliothek für TURBO/PURE C,
; die die FPU 68882 des TT unterstützt
;
;                by Roman Hodek
;           (c) 1992 MAXON Computer
; ----------------------------------------------

; Labels, die nach außen bekannt sein sollen;

EXPORT printf1,sprintf1,fprintf1 
EXPORT fabs,ceil,floor,round,trunc,fmod 
EXPORT exp,exp10,exp2,log,log10,log2 
EXPORT frexp,ldexp,modf 
EXPORT pow,pow10,sqrt
EXPORT sin,cos,tan,sincos,asin,acos,atan,atan2 
EXPORT sinh,cosh,tanh,asinh,acosh,atanh 
EXPORT ftest,fclrerr,ferr,fpumode

; für printfl benötigt:

IMPORT _StdOutF,fputc

fabs:   fabs.x      fp0,fp0
        rts

ceil:   fmove       fpcr,d0         ; Rounding mode:
        move.l      d0,d1
        and.b       #%11001111,d0
        or.b        #%00110000,d0   ; to + infty
        fmove       d0,fper
        fint.x      fp0,fp0
        fmove       d1,fper
        rts

floor:  fmove       fpcr,d0         ; Rounding mode:
        move.l      d0,d1
        and.b       #%11001111,d0
        or.b        #%00100000,d0   ; to - infty
        fmove       d0,fpcr
        fint.x      fp0,fp0
        fmove       d1,fpcr
        rts

round:  fmove       fpcr,d0         ; Rounding mode:
        move.l      d0,d1
        and.b       #%11001111,d0
        or.b        #%00000000,d0   ; to nearest
        fmove       d0,fpcr
        fint.x      fp0,fp0
        fmove       d1,fpcr
        rts

trunc:  fintrz.x    fp0,fp0         ; Rounding mode:
        rts                         ; to zero

fmod:   frem.x      fp1,fp0
        rts

exp:    fetox.x     fp0,fp0
        rts

exp10:  ftentox.x   fp0,fp0
        rts
exp2:   ftwotox.x   fp0,fp0
        rts
log:    flogn.x     fp0,fp0
        rts
log10:  flog10.x    fp0,fp0
        rts
log2:   flog2.x     fp0,fp0
        rts


; fgetman liefert eine Mantisse zwischen 1.0 
; und 2.0; frexp() soll aber Werte zwischen 
; 0.5 und 1.0 zurückgeben ==>
; man /= 2 und exp += 1

frexp:  fgetexp.x   fp0,fp1
        fgetman.x   fp0,fp0 
        fmove.w     fp0,(a0) 
        addq.w      #1,(a0) 
        fscale.w    #-1,fp0 
        rts

ldexp:  fscale.w    (a0),fp0
        rts

modf:   fintrz.x    fp0,fp1         ; FP1 <- Integerteil
        fmove.x     fp1,(a0)        ; Nachkommateil = 
        fsub.x      fp0,fp1         ; x - int(x) 
        rts

; Potenzberechnung: a^b = exp(b*ln(a))
; Ist für negative Basis nur bei ganzzahligen 
; Exponenten definiert!

pow:    ftst.x      fp0             ; Basis == 0 ->
        fbeq        pow_end         ; Ergebnis 0

        fmove.l     fp1,d0
        fcmp.l      d0,fp1
        fbne        pow_frac
                                    ; ganzzahl. Exp.: 
        ftst.x      fp0             ; Basis >= 0 ->
        fbge        pow_frac        ; normale Behandl.

        fabs.x      fp0             ; Basis < 0 :
        flogn.x     fp0,fp0         ; Betrag nehmen
        fmul.x      fp1,fp0
        fetox.x     fp0,fp0
        btst        #0,d0           ; und wenn Exponent
        beq         pow_end         ; ungerade:
        fneg.x      fp0             ; Ergebnis negativ
        bra         pow_end

pow_frac:flogn.x    fp0,fp0         ; ergibt bei negat.
        fmul.x      fp1,fp0         ; Exponenten
        fetox.x     fp0,fp0         ; E_DOMAIN

pow_end:rts

pow10:  fmove.w     d0,fp0
        ftentox.x   fp0,fp0
        rts

sqrt:   fsqrt.x     fp0,fp0
        rts

sin:    fsin.x      fp0,fp0
        rts
cos:    fcos.x      fp0,fp0
        rts
tan:    ftan.x      fp0,fp0
        rts
sincos: fsincos.x   fp0,fp1:fp0
        fmove.x     fp1,(a0)
        rts
asin:   fasin.x     fp0,fp0
        rts
acos:   facos.x     fp0,fp0
        rts
atan:   fatan.x     fp0,fp0
        rts

; atan2(y,x) berechnet den Arcustangens des 
; Bruches y/x. Die Funktion ist vor allem zu 
; Berechnungen des Arguments komplexer Zahlen 
; gedacht und liefert Ergebnisse -pi .. pi 
; 0 im Nenner wird korrekt gehandhabt und pi/2 
; zurückgegeben.

atan2:  ftst.x      fp1             ; x == 0 => Erg.
        fbeq        atan2_pol       ; +-pi/2

        moveq       #0,d0           ; sonst Korrektur:
        fbgt        atan2_fac       ; x>0 : 0*pi
        moveq       #1,d0           ; x<0, y>=0: +1*pi
        ftst.x      fp0             ;  " , y< 0: -1*pi
        fbge        atan2_fac
        neg.w       d0

atan2_fac:
        fdiv.x      fp1,fp0         ; fp0 <- atan(y/x)
        fatan.x     fp0
        fmovecr.x   #0,fp1          ; pi * Korrektur
        fmul.w      d0,fp1
        fadd.x      fp1,fp0         ; dazuaddieren
        rts

atan2_pol:                          ; Behandlung der
        fmove.x     fp0,fp1         ; Polstellen x == 0 
        fabs.x      fp0
        fdiv.x      fp1,fp0         ; fp0 <- sign< y )
        fmovecr.x   #0,fp1
        fmul.x      fp1,fp0         ; * pi
        fscale.w    #-1,fp0         ; / 2
        rts

sinh:   fsinh.x     fp0,fp0
        rts
cosh:   fcosh.x     fp0,fp0
        rts
tanh:   ftanh.x     fp0,fp0
        rts
asinh:  fmove.x     fp0,fp1         ; asinh(x) =
        fmul.x      fp1,fp1         ; ln(x+sqrt(x^2+1))
        fadd.w      #1,fp1
        fsqrt.x     fp1
        fadd.x      fp1,fp0
        flogn.x     fp0
        rts
acosh:  fmove.x     fp0,fp1         ; acosh(x) =
        fmul.x      fp1,fp1         ; ln(x+sqrt(x^2-1))
        fsub.w      #1,fp1
        fsqrt.x     fp1
        fadd.x      fp1,fp0
        flogn.x     fp0
        rts
atanh:  fatanh.x    fp0,fp0
        rts

; -----------------------------------------------------
; neue Verwaltungsfunktionen

; ftest testet sein Argument, ob es Unendlich 
; oder ein NAN ist. Rückgabe:
; +-1 => +-NAN ; +-2 => +-Unendlich
; 0 => "normale" Zahl

ftest:  ftst.x      fp0             ; FP0 testen
        fmove.l     fpsr,d0         ; Condition Codes
        swap        d0              ; holen
        lsr.w       #8,d0
        btst        #3,d0           ; N-Flag -> d1
        sne         d1
        and.l       #3,d0           ; X- und NAN-Flags:
        tst.b       d1              ; 2 = Unendl.
        beq         ftest_e         ; 1 = NAN
        neg.l       d0              ; ev. negieren
ftest_e:rts

; fclrerr löscht früher bemerkte Fehler

fclrerr:fmove.l     fpsr,d0         ; Accrued Excep-
        and.l       #$ffffff00,d0   ; tion-Byte
        fmove.l     d0,fpsr         ; löschen
        rts

; ferr testet, ob seit dem letzten fclrerr Fehler 
; aufgetreten sind. Rückgabe: s. Headerdatei

ferr:   fmove.l     fpsr,d0         ; Accrued Exception
        and.l       #$ff,d0         ; Byte lesen
        lsr.l       #3,d0
        rts

; fpumode setzt Rechengenauigkeit und Rundungs-
; modus der FPU

fpumode:fmove.l     fper,d2

        tst.w       d0              ; wenn d0 = prec.
        bmi         fpm_01          ; != -1 :
        lsl.w       #6,d0           ; ins FPCR schreiben
        and.b       #%00111111,d2
        or.b        d0,d2

fpm_01: tst.w       d1              ; wenn d1 = r.mode
        bmi         fpm_02          ; != -1 :
        lsl.w       #4,d0
        and.b       #%11001111,d2   ; ins FPCR
        or.b        d0,d2

fpm_02: fmove.l     d2,fpcr         ; FPCR zurückschr.
        rts


; -----------------------------------------------------
; [fs]printfl zur Ausgabe von Fließkommazahlen 
; f,s: selbe Bedeutung wie bei printf 
; Ausgabeformat: Werte > 99999 und < 0.01 
; werden mit Exponent gedruckt, sonst ohne 
; Rückgabewert: Anzahl der ausgegebenen Zeichen

saving  reg         d4-d7/a3-a5     ; zu rettende Register

packed  = -12                       ; lokale Variable
buffer  = packed - 16

; Einsprungpunkte je nach Ausgabegerät:
; a5 = Ausgabehandler 
; a4 = Zeiger auf String/File

printf1:
        link        a6,#buffer
        movem.l     #saving,-(sp)
        lea         put_scr(pc),a5
        bra         _printfl

sprintfl:
        link        a6,#buffer
        movem.l     #saving,-(sp)
        lea         put_str(pc),a5
        move.l      a0,a4
        bra         _printfl

fprintfl:
        link        a6,#buffer
        movem.l     #saving,-(sp)
        lea         put_file(pc),a5
        move.l      a0,a4

_printfl:ftst.x     fp0             ; Null abfangen
        fbne        pf_nnull
        moveq       #'0',d0
        jsr         (a5)
        bra         pf_end

        ; Zahl ins Packed-Decimal-Format 
pf_nnull:fmove.p    fp0,packed(a6){#17}

        btst        #5,packed(a6)   ; NAN oder
        bne         pf_nan          ; Unendl. ?

        ; mit oder ohne Exponent ?
        moveq       #0,d5           ; d5 = Kommapos.
        sf          d4              ; d4 = Exp.flag
        move.b      packed+2(a6),d0
        and.b       #$f0,d0
        bne         pf_mitex
        move.w      packed(a6),d0
        move.w      d0,d1
        and.w       #$0ff0,d1       ; |Exp.| > 9 ->
        bne         pf_mitex        ; mit Exp. ausg.
        and.w       #$f,d0
        btst        #6,packed(a6)   ; Vorzeichenbit
        beq         pf_01
        neg.w       d0              ; d0 = Exponent
pf_01:
        cmp.w       #-2,d0          ; -2<=Exp.<=5 =>
        blt         pf_mitex
        cmp.w       #5,d0
        bgt         pf_mitex
        st          d4              ; keinen Exp.!
        move.w      d0,d5           ; d5 = Komma-
                                    ; stelle

pf_mitex:moveq      #0,d7           ; d7 = Zeichen-
                                    ;      zähler
        btst        #7,packed(a6)   ; wenn negativ,
        beq         pf_02           ; ' - ' ausgeben

        moveq       #'-',d0
        jsr         (a5)

pf_02:
        tst.w       d5              ; wenn Exp.flag
        bpl         pf_start
        moveq       #'0',d0         ; Exp. == -1 ->
        jsr         (a5)            ; "0" ausg.
        moveq       #'.',d0         ; Exp. == -2 ->
        jsr         (a5)            ; "0.0” ausg.
        cmp.w       #-1,d5
        beq         pf_start
        moveq       #'0',d0
        jsr         (a5)

pf_start:move.b     packed+3(a6),d0 ; erste Ziffer
        and.b       #$0f,d0         ; ausgeben
        or.b        #$30,d0
        jsr         (a5)
        lea         packed+12(a6),a0 ; restliche
        lea         buffer+16(a6),a1 ; Ziffern
        moveq       #7,d0           ; entpacken

pf_03:  unpk        -(a0),-(a1),#$3030
        dbra        d0,pf_03
        lea         buffer+16(a6),a0 ; Nullen
        lea         -16(a0),a1      ; rechts weg-

pf_04:  cmp.b       #$30,-(a0)      ; streichen
        bne         pf_05
        clr.b       (a0)
        cmp.l       a0,a1
        bne         pf_04

pf_05:
        lea         buffer(a6),a3   ; Nachkommateil
        addq.w      #1,d5           ; ausgeben
        moveq       #15,d6

pf_loop:cmp.w       #1,d5           ; Abbruch, wenn
        bgt         pf_06           ; schon Komma
        tst.b       (a3)            ; und Nullbyte
        beq         pf_exp

pf_06:  subq.w      #1,d5
        bne         pf_07           ; Kommaz. == 0
        moveq       #'.',d0         ; => '.'' ausgeb.
        jsr         (a5)
pf_07:  move.b      (a3)+,d0        ; Ziffer ausgeb.
        or.b        #$30,d0
        jsr         (a5)
        dbra        d6,pf_loop
                                    ; Exponent ausg.
pf_exp: tst.b       d4              ; nur wenn d4
        bne         pf_end          ; != 0

        move.w      packed(a6),d0   ; Exp. ext ra-
        and.w       #$fff,d0        ; hieren
        move.w      packed+2(a6),d1
        and.w       #$f000,d1       ; Erweiterungs-
        or.w        d1,d0           ; stelle!

        lea         buffer(a6),a3
        unpk        d0,d1,#$3030    ; Exp. entpacken
        move.w      d1,2(a3)
        lsr.w       #8,d0
        unpk        d0,d1,#$3030
        move.w      d1,(a3)

        moveq       #'e',d0         ; 'e'-Zeichen
        jsr         (a5)
        moveq       #'+',d0         ; Vorzeichen
        btst        #6,packed(a6)   ; ausgeben
        beq         pf_08
        moveq       #'-',d0
pf_08:  jsr         (a5)

        moveq       #3, d6
pf_09:  subq.w      #1,d6
        cmp.b       #$30,(a3)+      ; führende 0en
        beq         pf_09           ; überspringen
        subq.l      #1,a3
        addq.w      #1,d6

pf_10:  move.b      (a3)+,d0        ; und Exponent
        jsr         (a5)            ; ausgeben
        dbra        d6,pf_10
        bra         pf_end

pf_nan: tst.l       packed+4(a6)    ; Unterscheidung
        bne         pf_na           ; NAN oder Unen.
        tst.l       packed+8(a6)
        bne         pf_na
        move.b      packed+3(a6),d0
        and.b       #15,d0
        bne         pf_na

        moveq       #'+',d0         ; +- Unendlich
        btst        #7,packed(a6)
        beq         pf_11
        moveq       #'-',d0
pf_11:  jsr         (a5)
        move.b      #$df,d0         ; Unendlich-
        jsr         (a5)            ; Zeichen
        bra         pf_end          ; ausgeben

pf_na:  moveq       #'N',d0         ; "NAN" ausgeben
        jsr         (a5)
        moveq       #'A',d0
        jsr         (a5)
        moveq       #'N',d0
        jsr         (a5)

pf_end: cmp.l       #put_str,a5     ; bei Ausg. in.
        bne         pf_12           ; String \0 an-
        clr.b       (a4)            ; hängen

pf_12:  move.w      d7,d0           ; Rückgabewert
        movem.l     (sp)+,#saving
        unlk        a6
        rts

; Ausgabehandler für Strings, Files und STDOUT

put_str:move.b      d0,(a4)+
        addq.w      #1,d7
        rts

put_scr:lea         _StdOutF,a0
        bsr         fputc
        addq.w      #1,d7
        rts

put_file:move.l     a4,a0
        bsr         fputc
        addq.w      #1,d7
        rts

Listing 1: NEWMATH.S: Quelltext der Bibliothek NEWMATH.LIB


/* NEWMATH.H
   (c) 1992 MAXON Computer
   Headerdatei für die FPU-Bibliothek
   NEWMATH.LIB

   Änderungen gegenüber dem alten MATH.H:
   - ftoa ersetzt durch [fs]printfl
   - andere Fehlerbehandlung : fclrerr, ferr
   - neue Fkt.: sincos, log2, exp10, exp2
   - ftest zum Testen von Unendlich und NAN's
   - fpumode jetzt zum Setzen von Rechengenauigkeit und Rundungsmodus
*/

#if !defined( __MATH )
#define __MATH

#define HUGE_VAL    1.189731495357231765E+4932

#define M_E         2.71828182845904523536
#define M_LOG2E     1.44269504088896340736
#define M_LOG10E    0.434294481903251827651
#define M_LN2       0.693147180559945309417
#define M_LN10      2.30258509299404568402
#define M_PI        3.14159265358979323846
#define M_PI_2      1.57079632679489661923
#define M_PI_4      0.785398163397448309116
#define M_1_PI      0.318309886183790671538
#define M_2_PI      0.636619772367581343076
#define M_1_SQRTPI  0.564189583547756286948
#define M_2_SQRTPI  1.12837916709551257390
#define M_SQRT2     1.41421356237309504880
#define M_SQRT_2    0.707106781186547524401

int     printfl( double x );
int     fprintfl( FILE *file, double x );
int     sprintfl( char *str, double x );

double  fabs( double x );
double  ceil( double x );
double  floor( double x );
double  round( double x );
double  trunc( double x );
double  fmod( double x, double y );

double  exp( double x ); 
double  exp10( double x ); 
double  exp2( double x ); 
double  log( double x ); 
double  log10( double x ); 
double  log2( double x );

double frexp( double x, int *nptr ); 
double ldexp( double x, int n ); 
double modf( double x, double *nptr );

double  pow( double x, double y ); 
double  pow10( int i ); 
double  sqrt( double x );

double  cos( double x ); 
double  sin( double x ); 
double  tan( double x ); 
double  sincos( double x, double *c );

double  acos( double x );
double  asin( double x );
double  atan( double x );
double  atan2( double x, double y );
double  cosh( double x );
double  sinh( double x );
double  tanh( double x );

double  acosh( double x ); 
double  asinh( double x ); 
double  atanh( double x );

double  atof( const char *digStr );

int     ftest( double x );
void    fclrerr( void ); 
int     ferr( void );

/* Rückgabewerte von ferr() */

#define E_INEXACT   1 
#define E_SING      2
#define E_OVERFLOW  4 
#define E_UNDERFLOW 8 
#define E_DOMAIN    16

void    fpumode( int prec, int round );

/* Präzisionen für fpumode() */

#define P_EXTENDED  0 
#define P_SINGLE    1 
#define P_DOUBLE    2

/* Rundungsmodi für fpumode() */

#define RM_NEAREST  0 
#define RM_ZERO     1 
#define RM_MIFTY    2 
#define RM_PIFTY    3

#endif

Listing 2: NEWMATH.H: Header-Datei zu NEWMATH.LIB


Roman Hodek
Aus: ST-Computer 04 / 1992, Seite 120

Links

Copyright-Bestimmungen: siehe Über diese Seite