Subversion Repositories pentevo

Rev

Blame | Last modification | View Log | Download | RSS feed | ?url?

; FLOAT.INC
;******************************************************************************
;* Gleitkommabibliothek fuer TLCS 900                                         *
;*                                                                            *
;* Originale fuer den 68000 aus mc, bis auf die Quadratwurzel aus c't         *
;* Portierung auf TLCS 900 von Alfred Arnold, Oktober 1993                    *
;*                                                                            *
;*  Routine  Funktion             Eingabe Ausgabe  Stack    Laenge  Zeit/14MHz *
;*                                                                            *
;*  fadd     Addition             XWA+XHL    XWA  12 Byte 194 Byte    60 us   *
;*  fsub     Subtraktion          XWA-XHL    XWA  12 Byte   7 Byte    65 us   *
;*  fmul     Multiplikation       XWA*XHL    XWA  20 Byte 218 Byte    70 us   *
;*  fdiv     Division             XWA/XHL    XWA  20 Byte 147 Byte   300 us   *
;*  fmul2    Mult. mit 2er-Potenz XWA*(2^BC) XWA   6 Byte  99 Byte    20 us   *
;*  fitof    Int-->Float          XWA        XWA   4 Byte  41 Byte    90 us   *
;*  fftoi    Float-->Int          XWA        XWA   2 Byte  72 Byte    20 us   *
;*  fsqrt    Quadratwurzel        XWA        XWA  16 Byte 192 Byte   220 us   *
;*  fftoa    Float-->ASCII        XWA   (XHL),BC ~38 Byte 228 Byte ~4500 us   *
;*  fatof    ASCII-->Float     (XHL),BC XWA,[BC] ~40 Byte 260 Byte ~2300 us   *
;*                                                                            *
;*  - Wollen Sie einzelne Routinen entfernen, so beachten Sie, dass fsub Teile *
;*    aus fadd und fdiv Teile aus fmul verwendet !                            *
;*  - Gleitkommaformat ist IEEE Single (32 Bit)                               *
;*  - Integerwerte bei fmul2, fitof und fftoi sind vorzeichenbehaftet         *
;*  - Der Prozessor muss sich im Maximum-Modus befinden                       *
;*  - Da die Routinen lokale Labels verwenden, ist mindestens AS 1.39 erfor-  *
;*    derlich                                                                 *
;*  - Die Ausfuehrungszeiten koennen je nach Operand streuen, insbesondere bei *
;*    den Konvertierungsfunktionen                                            *
;*  - MACROS.INC muss vorher eingebunden werden                               *
;******************************************************************************

;------------------------------------------------------------------------------
; gemeinsamer Anfang, Makros

shifta          macro   op,dest         ; Schieben, falls evtl. A>=16
                push    a               ; A wird zerschossen
                bit     4,a             ; Akku im Bereich 16..31 ?
                jr      z,smaller
                op      16,dest         ; dann einmal gross schieben
smaller:        push    f               ; Carry erhalten
                and     a,15            ; obere Bits plaetten
                jr      z,fertig
                pop     f               ; evtl. Rest verarbeiten
                op      a,dest
                jr      ende            ; Carry schon gut
fertig:         pop     f
ende:           pop     a               ; A zurueck
                endm

                section FloatLib

;------------------------------------------------------------------------------
; Konstanten

Ld10:           dd      ld(10)          ; Konversionskonstanten
One:            dd      1.0
Half:           dd      0.5
Ten:            dd      10.0
Tenth:          dd      3dcccccdh       ; =0.1, aber die Rundung auf manchen
                                        ; Systemen variiert (damit Test nicht
                                        ; scheitert)

Bias            equ     127
MaxExpo         equ     255
Comma           equ     '.'

;------------------------------------------------------------------------------
; Addition: XWA=XWA+XHL

                proc    fadd

                push    xbc             ; andere Register retten
                push    xde
                push    xhl

                ld      xde,xwa         ; Operand 1 nach XDE verschieben
                rlc     1,xde           ; Vorzeichen der Operanden nach Bit 0
                rlc     1,xhl
                ld      xbc,xde         ; Differenz bilden
                sub     xbc,xhl
                jr      nc,NoSwap       ; evtl. vertauschen, so dass
                ld      xwa,xhl         ; groessere in XDE
                ld      xhl,xde
                ld      xde,xwa
NoSwap:         ld      qa,e            ; Vorzeichen 1 ausmaskieren
                and     qa,1            ; (=Ergebnis Vorzeichen)
                bit     0,c             ; gleiches Vorzeichen ?
                jr      z,NoSub
                set     1,qa            ; dann Subtraktion vormerken

NoSub:          sub     xbc,xbc         ; XBC initialisieren
                rlc     8,xde           ; Exponent 1 rausholen
                ld      c,e
                or      e,e             ; falls <>0, implizite 1 einbauen
                scc     nz,e
                rrc     1,xde
                ld      e,0             ; Bit 0..7 wieder plaetten

                rlc     8,xhl           ; dito Exponent 2 extrahieren
                ld      qc,l
                or      l,l
                scc     nz,l
                rrc     1,xhl
                ld      l,0

; Zwischenstand:
;  - Mantissen linksbuendig inkl. impliziter Eins in XDE und XHL
;  - Exponent 1 in BC, Exponent 2 in QBC
;  - Ergebnisvorzeichen in QA, Bit 0
;  - Subtraktionsflag in QA, Bit 1

                ld      wa,bc           ; Exponentendifferenz berechnen
                sub     wa,qbc
                cp      wa,24           ; >24, d.h. Zahl 2 vernachlaessigbar gegen Zahl 1
                jr      gt,Round        ; ja, Ergebnis ist groessere Zahl
                shifta  srl,xhl         ; ansonsten Mantisse 2 entspr. anpassen

Add:            bit     1,qa            ; subtrahieren ?
                jr      nz,Subtract     ; ja-->
                add     xde,xhl         ; nein, Mantissen addieren
                jr      nc,Round        ; kein Ueberlauf, runden
                rr      1,xde           ; ansonsten Ueberlauf einschieben...
                inc     bc              ; ...und Exponent korrigieren
                jr      Round           ; normal weiter runden

Subtract:       sub     xde,xhl         ; Mantissen subtrahieren
                jr      z,Zero          ; falls Null, Gesamtergebnis 0
                jr      m,Round         ; fuehrende 1 noch da: zum Runden
Normalize:      or      bc,bc           ; Exponent bereits Null ?
                jr      z,Round         ; dann denormales Ergebnis
                dec     bc              ; ansonsten Mantisse eins rauf, Exponent
                sll     1,xde           ; eins runter
                jr      p,Normalize     ; solange, bis Eins auftaucht

Round:          add     xde,80h         ; Rundung auf Bit hinter Mantisse
                jr      nc,NoOver
                rr      1,xde           ; Bei Ueberlauf korrigieren
                inc     bc
NoOver:         ld      e,0             ; Mantissenrest plaetten
                or      xde,xde         ; insgesamt 0 ?
                jr      z,Zero          ; dann Ergebnis 0
                cp      bc,MaxExpo      ; Exponentenueberlauf ?
                jr      lt,NoEOver
                ld      bc,MaxExpo      ; ja: Unendlich: Exponent=Maximum
                sub     xde,xde         ;                Mantisse=0
                jr      Denormal

NoEOver:        or      bc,bc           ; Exponent 0 ?
                jr      z,Denormal      ; ja, denormal
                sll     1,xde           ; fuehrende Eins nicht speichern
Denormal:       ld      e,c             ; Exponenten einbauen
                rrc     8,xde           ; nach oben schieben
                rr      1,qa            ; Vorzeichen einbauen
                rr      1,xde

Zero:           ld      xwa,xde         ; Ergebnis in Akku

                pop     xhl             ; Register zurueck
                pop     xde
                pop     xbc

                ret

                endp

;------------------------------------------------------------------------------
; Subtraktion: XWA=XWA-XHL

                proc    fsub

                xor     qh,80h          ; Vorzeichen 2 drehen
                jp      fadd            ; ansonsten wie Addition

                endp

;------------------------------------------------------------------------------
; Multiplikation: XWA=XWA*XHL

                proc    fmul

                public  MulRound:Parent,MulZero:Parent,MulResult:Parent
                public  DivError:Parent

                push    xbc             ; Register retten
                push    xde
                push    xhl
                push    xix
                push    xiy

                ld      xiy,xwa         ; Op1 kopieren
                xor     xiy,xhl         ; Ergebnisvorzeichen bestimmen

                ex      wa,qwa          ; Registerhaelften Op1 vertauschen
                ld      xde,xwa         ; Op1 ab sofort in XDE
                and     de,7fh          ; Exponent und Vz. behandeln
                and     wa,7f80h        ; Exponent maskieren
                jr      z,Denorm1       ; gleich Null-->Op1 denormal
                set     7,de            ; ansonsten implizite Eins einbauen
                sub     wa,80h          ; Bias kompensieren
Denorm1:
                ex      hl,qhl          ; Op2 genauso behandeln
                ld      xbc,xhl
                and     hl,7fh
                and     bc,7f80h
                jr      z,Denorm2
                set     7,hl
                sub     bc,80h
Denorm2:
                add     bc,wa           ; Exponenten addieren
                srl     7,bc            ; richtig positionieren
                sub     bc,Bias-3       ; Bias-3 abziehen
                cp      bc,-24          ; totaler Unterlauf ?
                jr      lt,MulZero      ; dann Ergebnis 0

                ld      wa,de           ; beide oberen Haelften multiplizieren
                mul     xwa,hl
                ex      wa,qwa          ; Ergebnis in oberer Haelfte lagern
                ld      wa,de           ; obere Haelfte Op1 retten
                ex      de,qde          ; untere Haelfte Op1 holen
                ld      ix,hl           ; untere Haelfte Op1 * obere Op2
                mul     xix,de
                ex      hl,qhl          ; untere Op1 * untere Op2
                mul     xde,hl
                ex      de,qde          ; obere Op1 * untere Op2
                mul     xhl,wa

                ld      wa,de           ; Teile aufaddieren
                add     xwa,xix
                add     xwa,xhl
                jr      z,MulResult     ; Mantisse Null, Ergebnis Null
                jr      m,MulRound

                or      bc,bc           ; Exponent negativ ?
                jr      m,Unterlauf     ; ggfs. Unterlauf behandeln

Nor:            or      bc,bc           ; Exponent Null ?
                jr      z,MulRound      ; ja-->zum Runden
                rl      1,xde           ; nein, Mantisse eins nachschieben
                rl      1,xwa
                dec     bc              ; und Exponent runter
                or      xwa,xwa         ; fuehrende Eins da ?
                jr      p,Nor           ; nein, weiterschieben

MulRound:       add     xwa,80h         ; Rundung
                jr      nc,NoROver      ; dabei Ueberlauf ?
                rr      1,xwa           ; ja: Mantisse & Exponent korrigieren
                inc     bc
NoROver:        cp      bc,MaxExpo      ; Exponentenueberlauf ?
                jr      lt,NoEOver
DivError:       ld      bc,MaxExpo      ; dann unendlich einstellen
                sub     xwa,xwa
                jr      Denormal

NoEOver:        or      bc,bc           ; Exponent 0 ?
                jr      z,Denormal
                sll     1,xwa           ; fuehrende 1 loeschen

Denormal:       ld      a,c             ; Exponent einbauen
                rrc     8,xwa           ; hochschieben
                rl      1,xiy           ; Vorzeichen einbauen
                rr      1,xwa

MulResult:      pop     xiy
                pop     xix
                pop     xhl
                pop     xde
                pop     xbc

                ret

MulZero:        sub     xwa,xwa         ; Null erzeugen
                jr      MulResult

Unterlauf:      cp      bc,-24          ; totaler Unterlauf ?
                jr      le,MulZero      ; dann Null
                neg     bc              ; sonst umbauen
                ld      xde,xwa         ; dazu Akku freimachen
                sub     wa,wa           ; Endexponent
                ex      wa,bc           ; ist 0
                shifta  srl,xde         ; Mantisse herunterschieben
                ld      xwa,xde         ; Ergebnis zurueck nach XWA
                jr      MulRound        ; zurueck mit Exponent 0

                endp

;------------------------------------------------------------------------------
; Division: XWA=XWA/XHL

                proc    fdiv

                push    xbc             ; Register retten (muss gleich zu fmul sein)
                push    xde
                push    xhl
                push    xix
                push    xiy

                ld      xiy,xwa         ; Op1 kopieren
                xor     xiy,xhl         ; Ergebnisvorzeichen bestimmen

                ex      wa,qwa          ; Vorbehandlung wie bei fmul
                ld      xde,xwa
                and     de,7fh
                and     wa,7f80h
                jr      z,Denorm1
                set     7,de
                sub     wa,80h
Denorm1:
                ex      hl,qhl
                ld      xbc,xhl
                and     hl,7fh
                and     bc,7f80h
                jr      z,Denorm2
                set     7,hl
                sub     bc,80h
Denorm2:
                sub     wa,bc           ; Exponentendifferenz bilden
                ld      bc,wa           ; muс in BC liegen
                sra     7,bc            ; richtig positionieren
                add     bc,Bias         ; Bias addieren
                cp      bc,-24          ; totaler Unterlauf ?
                jr      lt,MulZero      ; ja, Ergebnis Null

                ex      hl,qhl          ; Format 0fff ... ffff 0000 0000
                or      xhl,xhl         ; Ergebnis unendlich ?
                jrl     z,DivError
                sll     7,xhl
                ex      de,qde          ; dito Divident
                or      xde,xde         ; falls Null, Ergebnis Null
                jrl     z,MulZero
                sll     7,xde

NormLoop:       bit     14,qhl          ; Divisor normalisiert ?
                jr      nz,Normal
                inc     bc              ; nein, Exponent RAUF (ist Ergebnisexponent)
                sll     1,xhl
                jr      NormLoop

Normal:         sub     xwa,xwa         ; Ergebnisquotient vorbesetzen
                add     bc,25           ; Exponent nicht groesser als 0

Loop:           ld      xix,xde         ; Divident zwischenspeichern
                sub     xde,xhl         ; probeweise abziehen
                ccf                     ; Carry drehen
                jr      c,IsOne         ; ungedrehter Carry=1: Divisor paсt
                ld      xde,xix         ; ansonsten zurueckkopieren
IsOne:          rl      1,xwa           ; Ergebnisbit einschieben
                sll     1,xde           ; Divident verschieben
                dec     bc              ; Exponent runter
                or      bc,bc
                jr      z,Denorm        ; falls Null, denormalisieren
                bit     8,qwa           ; fuehrende Eins da ?
                jr      z,Loop          ; nein, weiterrechnen

Denorm:         sll     7,xwa           ; Mantisse positionieren
                jrl     z,MulResult     ; Ergebnis 0 ?
                jrl     MulRound        ; ansonsten zum Runden

                endp

;-----------------------------------------------------------------------------
; Multiplikation mit Zweierpotenz: XWA=XWA*2^BC

                proc    fmul2

                push    bc              ; Register retten
                push    xde

                ld      xde,xwa         ; Vorzeichen merken
                sll     1,xwa           ; Vorzeichen rausschieben
                jr      z,Zero          ; falls Null, Ergebnis Null
                rlc     8,xwa           ; Exponent nach unten...
                sub     de,de           ; und in DE packen
                add     e,a
                jr      z,Denorm        ; falls denormalisiert..
                or      bc,bc           ; Multiplikation oder Division ?
                jr      m,Divide        ; (neg. Exponent=Division)

                add     de,bc           ; Exponent addieren
                cp      de,MaxExpo      ; Ueberlauf ?
                jr      ge,Over         ; ja, Ergebnis unendlich
Result:         ld      a,e             ; Ergebnisexponent einbauen
                rrc     8,xwa           ; Exponent nach oben
                rl      1,xde           ; Vorzeichen einschieben
                rr      1,xwa

Zero:           pop     xde             ; Register zurueck
                pop     bc
                ret

Over:           ld      de,MaxExpo      ; Ergebnis unendlich
                sub     xwa,xwa
                jr      Result

Divide:         add     de,bc           ; Exponentensumme bilden
                jr      gt,Result       ; >0, keine Sonderbehandlung
                scf                     ; ansonsten 1 explizit fБr
                rr      1,xwa           ; denormale Zahl machen
DDenorm:        or      de,de           ; Exponent=0 ?
                jr      z,Result        ; ja, Ergebnis einfach denormal
                srl     1,xwa           ; ansonsten weiter denormalisieren
                jr      z,Zero          ; dabei totaler Unterlauf->Null
                inc     de              ; Exponent korrigieren
                jr      DDenorm
DDDenorm:       add     de,bc           ; Exponentensumme bilden
                jr      DDenorm

Denorm:         or      bc,bc           ; Multiplikation oder Division ?
                jr      m,DDDenorm
                sub     a,a             ; alten Exponenten loeschen
Norm:           sll     1,xwa           ; normalisieren...
                jr      c,Stop          ; bis fuehrende Eins da
                dec     bc              ; oder 2. Exponent 0
                or      bc,bc
                jr      nz,Norm
                jr      Result          ; Multiplikator kompl. fuer Normalisierung draufgegangen
Stop:           add     de,bc           ; Rest addieren
                jr      Result          ; alles andere schon o.k.

                endp

;------------------------------------------------------------------------------
; LongInt-->Float : XWA-->XWA

                proc    fitof

                push    xbc             ; Register retten

                or      xwa,xwa         ; Null ?
                jr      z,Result        ; dann Ergebnis Null
                scc     m,qc            ; Vorzeichen nach QC, Bit 0
                jr      p,Positive
                cpl     wa              ; falls negativ,drehen
                cpl     qwa
                inc     xwa
Positive:       ld      bc,Bias+32      ; Exponent vorbesetzen
Shift:          dec     bc              ; Mantisse verschieben
                sll     1,xwa
                jr      nc,Shift
                ld      a,c             ; Exponent einbauen
                rrc     8,xwa           ; Exponent nach oben
                rr      1,qc            ; Vorzeichen einbauen
                rr      1,xwa

Result:         pop     xbc             ; Register zurueck
                ret

                endp

;------------------------------------------------------------------------------
; Float-->LongInt : XWA-->XWA

                proc    fftoi

                push    bc              ; Register retten

                rl      1,xwa           ; Vorzeichen in Carry
                scc     c,b             ; in B merken

                rlc     8,xwa           ; Exponent nach unten
                ld      c,a             ; in C legen
                sub     c,Bias          ; Bias abziehen

                jr      m,Zero          ; neg. Exponent -> Zahl<0 -> Ergebnis 0
                cp      c,31            ; Ueberlauf ?
                jr      ge,Over

                scf                     ; fuehrende Eins einschieben
                rr      1,xwa
                sub     a,a             ; Exponent loeschen

Shift:          srl     1,xwa           ; jetzt schieben, bis Ergebnis stimmt
                inc     c
                cp      c,31
                jr      ne,Shift

                srl     1,b             ; negieren ?
                jr      nc,Positive
                cpl     wa              ; ggfs. negieren
                cpl     qwa
                inc     xwa

Positive:       pop     bc              ; Register zurueck
                ret

Zero:           sub     xwa,xwa         ; Ergebnis 0
                jr      Positive

Over:           ld      xwa,7fffffffh   ; Ueberlauf: Maxint zurueckgeben
                srl     1,b             ; negativ ?
                jr      nc,Positive
                cpl     wa              ; ja, neg. Maximum zurueckgeben
                cpl     qwa
                jr      Positive

                endp

;------------------------------------------------------------------------------
; Quadratwurzel: XWA=SQRT(XWA)

                proc    fsqrt

                push    xbc             ; Register retten
                push    xde
                push    xhl
                push    xix

                ld      xix,xwa         ; Argument retten
                or      xix,xix         ; Zahl negativ ?
                jrl     m,DomainError   ; dann geht es nicht

                ex      ix,qix          ; MSW holen
                and     xix,7f80h       ; Exponent isolieren
                jrl     z,Zero          ; keine Behandlung denormaler Zahlen

                and     xwa,7fffffh     ; Mantisse isolieren
                sub     ix,7fh*80h      ; Bias vom Exponenten entfernen
                bit     7,ix            ; Exponent ungerade ?
                res     7,ix
                jr      z,EvenExp
                add     xwa,xwa         ; ja: Mantisse verdoppeln
                add     xwa,1000000h-800000h ; impl. Eins dazu
EvenExp:                                ; erste Iteration ohne impl. Eins
                sra     1,ix            ; Exponent/2 mit Vorzeichen
                add     ix,7fh*80h      ; Bias wieder dazu
                ex      ix,qix          ; neuen Exponenten in QIX aufheben
                sll     7,xwa           ; x ausrichten
                ld      xde,40000000h   ; xroot nach erster Iteration
                ld      xhl,10000000h   ; m2=2 << (MaxBit-1)
Loop10:         ld      xbc,xwa         ; xx2 = x
Loop11:         sub     xbc,xde         ; xx2 -= xroot
                srl     1,xde           ; xroot = xroot/2
                sub     xbc,xhl         ; x2 -= m2
                jr      m,DontSet1
                ld      xwa,xbc         ; x = xx2
                or      xde,xhl         ; xroot += m2
                srl     2,xhl           ; m2 = m2/4
                jr      nz,Loop11
                jr      WABCSame
DontSet1:       srl     2,xhl           ; m2 = m2/4
                jr      nz,Loop10       ; 15* abarbeiten
                                        ; Bit 22..8
                ld      xbc,xwa         ; 17. Iteration separat
WABCSame:       sub     xbc,xde
                rrc     1,xde           ; mitsamt Carry...
                ex      de,qde          ; auf neues Alignment umstellen
                sub     xbc,1           ; Carry von 0-$4000: x2 -= m2
                jr      m,DontSet7
                or      xbc,-40000000h  ; 0-$4000: x2 -= m2, Teil 2
                ld      xwa,xbc
                or      de,4000h        ; xroot += m2
DontSet7:       ex      wa,qwa          ; x auf neues Alignment umstellen
                ld      hl,1000h        ; m2 - obere Haelfte schon 0
Loop20:         ld      xbc,xwa         ; xx2 = x
Loop21:         sub     xbc,xde         ; xx2 -= xroot
                srl     1,xde           ; xroot = xroot/2
                sub     xbc,xhl         ; x2 -= m2
                jr      m,DontSet2
                ld      xwa,xbc         ; x = xx2
                or      xde,xhl         ; xroot += m2
                srl     2,xhl           ; m2 = m2/4
                jr      nz,Loop21
                jr      Finish
DontSet2:       srl     2,xhl           ; m2 = m2/4
                jr      nz,Loop20       ; 7* abarbeiten

Finish:         sub     xwa,xde         ; Aufrunden notwendig ?
                jr      ule,NoInc
                inc     xde             ; wenn ja, durchfuehren
NoInc:          res     7,qde           ; impl. Eins loeschen
                or      xde,xix
                ld      xwa,xde         ; Ergebnis in XWA
                jr      End

DomainError:    ld      xwa,0ffc00000h  ; -NAN zurueckgeben
                jr      End

Zero:           sub     xwa,xwa         ; Ergebnis 0

End:            pop     xix             ; Register zurueck
                pop     xhl
                pop     xde
                pop     xbc
                ret

                endp

;------------------------------------------------------------------------------
; Unterroutine Zehnerpotenz bilden: XWA=10.0^BC

                section fPot10          ; nicht mit proc, da private Funktion
                public  fPot10:Parent
fPot10:

                push    xbc             ; Register retten
                push    xhl

                ld      xwa,(One)       ; Ausgangspunkt fuers Multiplizieren
                ld      xhl,(Ten)       ; zu benutzende Potenz
                or      bc,bc           ; negative Potenz ?
                jr      p,IsPos
                ld      xhl,(Tenth)     ; dann eben Zehntel multiplizieren
                neg     bc              ; fuer Schleife immer positiv
IsPos:
                or      bc,bc           ; Noch weiter multiplizieren ?
                jr      z,End
                bit     0,bc            ; Restpotenz ungerade ?
                jr      z,IsEven
                call    fmul            ; ja: einzeln multiplizieren
IsEven:         srl     1,bc            ; naechste Stelle
                push    xwa             ; neue Potenz berechnen
                ld      xwa,xhl
                call    fmul            ; durch quadrieren
                ld      xhl,xwa
                pop     xwa
                jr      IsPos           ; weiter nach Einsen suchen

End:            pop     xhl             ; Register zurueck
                pop     xbc
                ret

                endsection

;------------------------------------------------------------------------------
; Unterroutine Zahl dezimal wandeln

                section fOutDec
                public  fOutDec:Parent
fOutDec:

                push    xwa             ; Register retten
                push    xbc
                push    de
                push    xhl

                bit     15,qwa          ; negativ ?
                jr      z,IsPos
                ld      (xix+),'-'      ; ja: vermerken...
                cpl     wa              ; ...und Zweierkomplement
                cpl     qwa
                inc     xwa
                jr      GoOn
IsPos:          bit     7,c             ; Pluszeichen ausgeben ?
                jr      nz,GoOn
                ld      (xix+),'+'
GoOn:           res     7,c             ; Plusflag loeschen
                ld      qbc,0           ; Nullflag und Zaehler loeschen

InLoop:         ld      xhl,0           ; Division vorbereiten
                ld      e,32            ; 32 Bit-Division
DivLoop:        sll     1,xwa           ; eins weiterschieben
                rl      1,xhl
                srl     1,xwa           ; fuer nachher
                sub     xhl,10          ; passt Divisor hinein ?
                jr      nc,DivOK
                add     xhl,10          ; nein, zuruecknehmen...
                scf                     ; im Ergebnis 0 einschieben
DivOK:          ccf                     ; neues Ergebnisbit
                rl      1,xwa           ; Ergebnis in XWA einschieben...
                djnz    e,DivLoop

                add     l,'0'           ; ASCII-Offset addieren
                bit     1,qb            ; schon im Nullbereich ?
                jr      z,NormVal
                ld      l,b             ; ja, dann gewuenschtes Leerzeichen
NormVal:        push    l               ; auf LIFO legen
                inc     qc              ; ein Zeichen mehr
                or      xwa,xwa         ; Quotient Null ?
                scc     z,qb
                jr      nz,InLoop       ; wenn nicht Null, auf jeden Fall weiter
                cp      c,qc            ; ansonsten nur, falls min. Stellenzahl
                jr      ugt,InLoop      ; noch nicht erreicht

OutLoop:        pop     a               ; jetzt Zeichen umgekehrt ablegen
                ld      (xix+),a
                djnz    qc,OutLoop

                pop     xhl             ; Register zurueck
                pop     de
                pop     xbc
                pop     xwa

                ret

                endsection

;------------------------------------------------------------------------------
; Gleitkomma nach ASCII wandeln:
; In:  Zahl in XWA
;      Zeiger auf Speicher in XHL
;      max. Anzahl Nachkommastellen in C
;      B/Bit 0 setzen, falls Mantissen-Pluszeichen unerwuenscht
;      B/Bit 1 setzen, falls Exponenten-Pluszeichen unerwuenscht
;      B/Bit 2..4 = Stellenzahl Exponent
;      B/Bit 5 setzen, falls Nullen am Ende der Mantisse unerwuenscht
; Out: Zahl abgelegter Zeichen (exkl. NUL am Ende) in BC
;      (XHL) = gebildeter String

                proc    fftoa

                push    xix             ; Register retten
                push    xhl
                push    de
                push    xbc
                push    xwa

                ld      xix,xhl         ; Zeiger auf Speicher kopieren
                ld      de,bc           ; Parameter sichern

                ld      xhl,xwa         ; Zahl auf die Zerlegebank bringen
                res     15,qwa          ; Vorzeichen hier nicht mehr gebraucht

                ld      c,'+'           ; Annahme positiv
                sll     1,xhl           ; Vorzeichen in Carry bringen
                jr      c,IsNeg         ; Minuszeichen immer erforderlich...
                bit     0,d             ; ...Pluszeichen optional
                jr      nz,NoMantSgn
                jr      WrMantSgn
IsNeg:          ld      c,'-'           ; ja
WrMantSgn:      ld      (xix+),c        ; Mantissenvorzeichen ablegen
NoMantSgn:
                ld      c,qh            ; Exponenten herausholen...
                extz    bc              ; ...auf 16 Bit erweitern...
                sll     8,xhl           ; ...und in Quelle loeschen

                cp      bc,MaxExpo      ; Sonderwert (INF/NAN) ?
                jrl     z,SpecialVals   ; ja-->

                or      bc,bc           ; Zahl denormal ?
                jr      nz,IsNormal     ; nein, normal weiter
                or      xhl,xhl         ; bei kompl. Null auch ueberspringen
                jr      z,IsNull
Normalize:      sll     1,xhl           ; ja: solange zaehlen, bis 1 erscheint
                jr      c,IsNormal
                dec     bc
                jr      Normalize
IsNormal:       sub     bc,Bias         ; Bias abziehen
IsNull:
                push    xwa             ; fuer folgendes Zahl retten
                ld      wa,bc           ; Zweierexponenten in Float wandeln
                exts    xwa
                call    fitof
                ld      xhl,(Ld10)      ; in Dezimalexponenten wandeln
                call    fdiv
                or      xwa,xwa         ; Zahl negativ ?
                jr      p,NoCorr
                ld      xhl,(One)       ; dann nocheinmal korrigieren wg.
                call    fsub            ; unterer Gaussklammer
NoCorr:         call    fftoi           ; Den Ausflug in Float beenden
                ld      qbc,wa          ; den Zehnerexponenten retten
                ld      bc,wa
                call    fPot10          ; von diesem Exponenten Zehnerpotenz
                ld      xhl,xwa         ; bilden
                pop     xwa             ; alte Zahl zurueck
                call    fdiv            ; Teilen: Ergebnis ist Zahl zwischen
Again:          ld      xhl,xwa         ; 1.0 und 9.9999..., diese retten
                call    fftoi           ; Vorkommastelle berechnen
                cp      a,10            ; doch etwas drueber ?
                jr      ult,NoRoundErr
                ld      xwa,xhl         ; ja, dann noch einmal zehnteln
                ld      xhl,(Tenth)
                call    fmul
                inc     qbc
                jr      Again
NoRoundErr:     add     a,'0'           ; diese nach ASCII wandeln...
                ld      (xix+),a        ; ...und ablegen
                sub     a,'0'           ; wieder rueckgaengig machen
                cp      e,0             ; gar keine Nachkommastellen ?
                jr      eq,NoComma
                ld      (xix+),Comma    ; Dezimalpunkt ausgeben
                call    fitof           ; in ganze Gleitkommazahl wandeln
                call    fsub            ; Differenz bilden
                chg     15,qwa          ; war verkehrtherum...
                ld      xhl,xwa         ; nach XHL verschieben, weil XWA gebraucht
                ld      c,e             ; Zehnerpotenz fuer Skalierung ausrechnen
                extz    bc              ; auf 16 Bit aufblasen
                call    fPot10          ; Skalierungswert berechnen
                call    fmul            ; hochmultiplizieren
                ld      xhl,(Half)      ; Rundung
                call    fadd
                call    fftoi           ; diese herausziehen
                ld      b,'0'           ; n-stellig mit Vornullen ausgeben
                ld      c,e
                set     7,c             ; kein Pluszeichen!
                call    fOutDec
                bit     5,d             ; Nullen am Ende abraeumen ?
                jr      nz,CleanZeros
NoComma:
                ld      a,d             ; falls Minimalstellenzahl Exponent=0
                and     a,00011100b     ; und Exponent=0, vergessen
                or      a,qb
                or      a,qc
                jr      z,End

                ld      (xix+),'E'      ; Exponenten ausgeben
                ld      wa,qbc
                exts    xwa
                ld      b,'0'           ; evtl. vornullen...
                ld      c,d             ; Bit 1-->Bit 7
                rrc     2,c
                and     c,87h           ; Bits ausmaskieren
                call    fOutDec

End:            pop     xwa             ; Register zurueck
                pop     xbc
                pop     de
                pop     xhl
                ld      (xix),0         ; NUL-Zeichen im String nicht vergessen
                sub     xix,xhl         ; Stringlaenge berechnen
                ld      bc,ix
                pop     xix

                ret

SpecialVals:    or      xde,xde         ; Ist die Mantisse Null ?
                jr      nz,IsNAN
                ldw     (xix+),'NI'     ; ja: INF einschreiben
                ld      (xix+),'F'
                jr      End
IsNAN:          ldw     (xix+),'AN'     ; nein: NAN einschreiben
                ld      (xix+),'N'
                jr      End

CleanZeros:     cp      (xix-1),'0'     ; steht da eine Null am Ende ?
                jr      nz,CleanNoZero  ; nein, Ende
                dec     xix             ; ja: Zaehler runter, so dass Ueber-
                jr      CleanZeros      ; schrieben wird und neuer Versuch
CleanNoZero:    cp      (xix-1),Comma   ; evtl. Komma entfernbar ?
                jr      nz,NoComma      ; nein-->
                dec     xix             ; ja: noch ein Zeichen weniger
                jr      NoComma

                endp

;------------------------------------------------------------------------------
; ASCII nach Gleitkomma wandeln:
; In:  Zeiger auf String (ASCIIZ) in XHL
; Out: XWA = Ergebnis bzw. fehlerhafte Stelle
;      CY = 0, falls fehlerfrei

                proc    fatof

                push    xbc             ; Register retten
                push    xde
                push    xhl
                push    xix

                ld      xix,xhl         ; Zeiger nach XIX
                ld      qbc,01          ; Phase 1 (Mantisse), noch kein Vorzeichen
                ld      xde,(Ten)       ; in der Mantisse mit 10 hochmultiplizieren
                ld      xhl,0           ; Mantisse vorbelegen
                ld      bc,0            ; Exponent vorbelegen

ReadLoop:       ld      a,(xix+)        ; ein neues Zeichen holen
                extz    wa              ; auf 32 Bit aufblasen
                extz    xwa

                cp      a,0             ; Endezeichen ?
                jrl     eq,Combine      ; ja, alles zusammen

                cp      a,' '           ; Leerzeichen ignorieren
                jr      eq,ReadLoop

                cp      a,'+'           ; Pluszeichen gnadenhalber zugelassen
                jr      ne,NoPlus       ; ist aber nur ein Dummy
                bit     0,qb            ; schon ein Vorzeichen dagewesen ?
                jrl     nz,Error        ; dann Fehler
                set     0,qb            ; ansonsten einfach setzen
                jr      ReadLoop
NoPlus:
                cp      a,'-'           ; Minuszeichen bewirkt schon eher etwas
                jr      ne,NoMinus
                bit     0,qb            ; darf auch nur einmal auftreten
                jrl     nz,Error
                set     0,qb
                cp      qc,1            ; je nach Phase anderes Flag setzen
                jr      ne,MinPhase3
                set     1,qb            ; bei Mantisse Bit 1...
                jr      ReadLoop
MinPhase3:      set     2,qb            ; bei Exponent Bit 2
                jr      ReadLoop
NoMinus:
                cp      a,'.'           ; Umschaltung zu Phase 2 (Nachkomma) ?
                jr      ne,NoPoint
                cp      qc,1            ; bish. Phase muss eins sein
                jrl     ne,Error
                ld      qc,2            ; neue Phase eintragen
                set     0,qb            ; Nachkomma darf kein Vorzeichen haben
                ld      xde,(Tenth)     ; im Nachkomma durch 10 teilen
                jr      ReadLoop
NoPoint:
                cp      a,'e'           ; kleines und grosses E zulassen
                jr      eq,IsE
                cp      a,'E'
                jr      ne,NoE
IsE:            cp      qc,3            ; vorherige Phase muss 1 oder 2 sein
                jr      eq,Error
                ld      qc,3            ; vermerken
                res     0,qb            ; Vorzeichen wieder zugelassen
                jr      ReadLoop
NoE:
                sub     a,'0'           ; jetzt nur noch 0..9 zugelassen
                jr      c,Error
                cp      a,9
                jr      ugt,Error
                set     0,qb            ; nach Ziffern kein Vorzeichen mehr zulassen

                cp      qc,1            ; Phase 1 (Mantisse) :
                jr      ne,NoPhase1
                push    xwa             ; Zeichen retten
                ld      xwa,xde         ; bish. Mantisse * 10
                call    fmul
                ld      xhl,xwa
                pop     xwa             ; Zahl nach Float wandeln
                call    fitof
                call    fadd            ; dazuaddieren
                ld      xhl,xwa         ; Mantisse zuruecklegen
                jrl     ReadLoop
NoPhase1:
                cp      qc,2            ; Phase 2 (Nachkomma) :
                jr      ne,NoPhase2
                call    fitof           ; Stelle nach Float wandeln
                push    xhl             ; Mantisse retten
                ld      xhl,xde         ; Stelle mit Zehnerpotenz skalieren
                call    fmul
                pop     xhl             ; zur Mantisse addieren
                call    fadd
                push    xwa             ; Zwischenergebnis retten
                ld      xwa,xde         ; nДchste Skalierungspotenz ausrechnen
                ld      xhl,(Tenth)
                call    fmul
                ld      xde,xwa         ; alles wieder zurueck
                pop     xhl
                jrl     ReadLoop
NoPhase2:
                mul     bc,10           ; Exponent heraufmultiplizieren
                add     bc,wa
                cp      bc,45           ; Minimum ist 1e-45
                jr      ugt,Error
                jrl     ReadLoop

Combine:        bit     2,qb            ; Exponent negativ ?
                jr      z,ExpPos
                neg     bc
ExpPos:         call    fPot10          ; Zehnerpotenz des Exponenten bilden
                call    fmul            ; mit Mantisse kombinieren
                bit     1,qb            ; Mantisse negativ ?
                jr      z,ManPos
                set     15,qwa
ManPos:         rcf                     ; Ende ohne Fehler

                pop     xix             ; Register zurueck
                pop     xhl
                pop     xde
                pop     xbc
                ret

Error:          ld      xwa,xix         ; Endzeiger laden
                pop     xix
                pop     xhl
                sub     xwa,xhl         ; rel. Position des fehlerhaften Zeichens berechnen
                pop     xde
                pop     xbc
                scf                     ; Ende mit Fehler
                ret

                endp

;------------------------------------------------------------------------------
; gemeinsames Ende

                endsection