; *******************************
; * Fliesskomma-Arithmetik fuer *
; * den Z80-Mikroprozessor      *
; * (mc 12/88, Seite 100        *
; *******************************

; ********************************************************
; * Die folgende Testroutine liefert die Ausgabe:
; *     40400000
; *     00700000
; *     7F800000
; *     35BFFFFF
; *     00400000
; *     7F7FFFFF
; *     7F800000
; *     406DB6DB
; *     15400001

START:
       LD      SP,STACK
       LD      BC,3F80H        ; Aufruf der Additionsroutine
       LD      DE,0000H        ; mit verschiedenen Parametern
       PUSH    BC              ; entspricht 1 + 2
       PUSH    DE
       LD      BC,4000H
       LD      DE,0000H
       PUSH    BC
       PUSH    DE
       CALL    F_ADD
       CALL    HEXOUT          ; anschliessend Ausgabe

       LD      BC,00F0H        ; eine kleine, gerade noch normalisierte
       LD      DE,0000H        ; Zahl, dazu die kleinste normalisierte
       PUSH    BC              ; Zahl mit negativem Vorzeichen addieren
       PUSH    DE
       LD      BC,8080H
       LD      DE,0000H
       PUSH    BC
       PUSH    DE
       CALL    F_ADD
       CALL    HEXOUT

       LD      BC,7F00H        ; die Summe dieser beiden Zahlen
       LD      DE,0000H        ; ergibt unendlich. Setzt man
       PUSH    BC              ; fuer die zweite Zahl den Wert
       PUSH    DE              ; 7EFFFFFE, so ist das Ergebnis
       LD      BC,7EFFH        ; gerade MAXFLOAT
       LD      DE,0FFFFH
       PUSH    BC
       PUSH    DE
       CALL    F_ADD
       CALL    HEXOUT

       LD      BC,0000H        ; Multiplikation testen
       LD      DE,0003H        ; MAXFLOAT * <denormalisierte Zahl>
       PUSH    BC
       PUSH    DE
       LD      BC,7F7FH
       LD      DE,0FFFFH
       PUSH    BC
       PUSH    DE
       CALL    F_MUL
       CALL    HEXOUT

       LD      BC,0080H        ; die kleinste normalisierte Zahl
       LD      DE,0000H        ; mit 0.5 multiplizieren
       PUSH    BC              ; (ergibt eine denormalisierte Zahl)
       PUSH    DE
       LD      BC,3F00H
       LD      DE,0000H
       PUSH    BC
       PUSH    DE
       CALL    F_MUL
       CALL    HEXOUT

       LD      BC,4000H        ; eine sehr grosse Zahl mit zwei
       LD      DE,0000H        ; multiplizieren. Das Ergebnis
       PUSH    BC              ; ist genau MAXFLOAT
       PUSH    DE
       LD      BC,7EFFH
       LD      DE,0FFFFH
       PUSH    BC
       PUSH    DE
       CALL    F_MUL
       CALL    HEXOUT

       LD      BC,0000H        ; Test der Divisionsroutine
       LD      DE,0000H        ; hier 1 / 0 (ergibt unendlich)
       PUSH    BC
       PUSH    DE
       LD      BC,3F80H
       LD      DE,0000H
       PUSH    BC
       PUSH    DE
       CALL    F_DIV
       CALL    HEXOUT

       LD      BC,40E0H        ; jetzt 26 / 7 berechnen
       LD      DE,0000H
       PUSH    BC
       PUSH    DE
       LD      BC,41D0H
       LD      DE,0000H
       PUSH    BC
       PUSH    DE
       CALL    F_DIV
       CALL    HEXOUT

       LD      BC,1FFFH        ; jetzt eine sehr kleine
       LD      DE,0FFFFH       ; denormalisierte Zahl durch
       PUSH    BC              ; eine kleine normalisierte
       PUSH    DE              ; Zahl dividieren
       LD      BC,0000H
       LD      DE,0003H
       PUSH    BC
       PUSH    DE
       CALL    F_DIV
       CALL    HEXOUT

       HALT                    ; Ende des Tests

       DEFS    100
STACK:

; ************************************************
; * Zahl in BC-DE in 8 Hexadezimalziffern drucken.
; * Dazu werden nacheinander die Nibble-Paare in
; * B, C, D und E ausgedruckt.
; *

HEXOUT:
       LD      A,B             ; Nacheinander die einzelnen
       CALL    DIG2            ; Nibble-Paare in A laden
       LD      A,C             ; und ausdrucken
       CALL    DIG2
       LD      A,D
       CALL    DIG2
       LD      A,E
       CALL    DIG2
       LD      A,10
       CALL    OUTCHAR
       LD      A,13
       CALL    OUTCHAR
       RET

DIG2:
       PUSH    AF              ; Nibble-Paar ausdrucken
       RRCA                    ; unterstes Nibble retten
       RRCA                    ; oberes Nibble rechtsbuendig
       RRCA                    ; positionieren
       RRCA
       AND     00001111B
       ADD     A,90H           ; binaer in ASCII (hex)
       DAA
       ADC     A,40H
       DAA
       CALL    OUTCHAR         ; Zeichen ausgeben
       POP     AF              ; jetzt unteres Nibble verarbeiten
       AND     00001111B       ; Nibble maskieren
       ADD     A,90H           ; binaer in ASCII (hex)
       DAA
       ADC     A,40H
       DAA
       CALL    OUTCHAR
       RET

OUTCHAR:                        ; Zeichen auf Console ausgeben
       OUT     (0),A
       RET

; **********************************
; * Globale Konstanten-Definitionen
; * fuer das Fliesskommapaket
; *

MAXEXPO EQU     255             ; Maximal zulaessiger Exponent
BIAS    EQU     127             ; Bias des Exponenten

; *************************************************
; * Fliesskomma-Addition in Single-Precision
; * Parameter: Operand 1 und Operand 2 ueber Stack
; * Ergebnis:  in BC-DE: MSB in B, LSB in E
; *

; * Es folgen Offset-Definitionen fuer Stack-relativen Zugriff

FHL_ALT EQU     0               ; Top of Stack liegt HL
FADR    EQU     2               ; dann die Ruecksprungadresse
OP1     EQU     4               ; jetzt Offset-Definitionen fuer
OP2     EQU     8               ; Parameter-Uebergabe

OPSIZE  EQU     4               ; Groesse eines Operanden

F_ADD:
       PUSH    HL              ; alten Basepointer retten
       LD      (F_STACK),SP    ; aktuellen Stackpointer abspeichern
       LD      HL,(F_STACK)    ; und in HL laden (= Basepointer)
       PUSH    AF              ; benoetigte Register retten
       PUSH    IX
       PUSH    IY
       LD      BC,OP1          ; jeztz die Zeiger auf die
       ADD     HL,BC           ; Operanden initialisieren
       PUSH    HL
       POP     IX              ; IX zeigt auf Operand 1
       LD      BC,OPSIZE
       ADD     HL,BC
       PUSH    HL
       POP     IY              ; IY zeigt auf Operand 2
F_ADSUB:
       ADD     HL,BC           ; HL zeigt jetzt hinter die Operanden!
       LD      (F_STACK),HL    ; diese Adresse fuer's Ende merken
       LD      A,(IX+3)        ; Vorzeichen von Operand 1 laden
       LD      E,A             ; Ergebnisvorzeichen in E, Bit 7
       XOR     (IY+3)          ; mit Vorzeichen von OP2 verknuepfen
       LD      D,A             ; Subtraktionsflag in D, Bit 7
       RES     7,(IX+3)        ; Vorzeichen in Mantisse 1 loeschen
       RES     7,(IY+3)        ; Vorzeichen in Mantisse 2 loeschen

; Die Operanden sind jetzt in der Form: 0EEE EEEE EFFF ... FFFF

       LD      A,(IX+0)        ; Differenz OP1 - OP2 bilden
       SUB     (IY+0)
       LD      A,(IX+1)
       SBC     A,(IY+1)
       LD      A,(IX+2)
       SBC     A,(IY+2)
       LD      A,(IX+3)
       SBC     A,(IY+3)
       JR      NC,FAD_1        ; Sprung falls OP1 groesser als OP2
       PUSH    IX              ; ansonsten Operanden vertauschen
       EX      (SP),IY         ; (eigentlich nur die Pointer), so
       POP     IX              ; dass IY den Kleineren adressiert
       LD      A,E             ; Ergebnisvorzeichen neu berechnen
       XOR     D
       LD      E,A
FAD_1:
       LD      A,(IX+2)
       LD      C,(IX+3)        ; Exponent der groesseren Zahl laden
       SLA     A
       RL      C
       JR      Z,AD_DN1
       SET     7,(IX+2)        ; implizite Eins erzeugen
AD_DN1:
       LD      A,(IY+2)
       LD      B,(IY+3)        ; Exponent der kleineren Zahl laden
       SLA     A
       RL      B
       JR      Z,AD_DN2
       SET     7,(IY+2)        ; implizite Eins erzeugen
AD_DN2:
       PUSH    BC              ; Jetzt die Register fuer den
       PUSH    DE              ; Blocktransferbefehl retten
       LD      BC,(OPSIZE*2)-1 ; beide Operanden verschieben
       DEC     HL              ; HL zeigt auf letztes Byte
       PUSH    HL              ; HL nach DE kopieren
       POP     DE
       DEC     HL              ; HL zeigt auf vorletztes Byte
       LDDR                    ; Verschiebung beider Mantissen
       POP     DE              ; um 8 Bit nach links
       POP     BC
       XOR     A
       LD      (IX+0),A        ; Form: FFFF ... FFFF 0000 0000
       LD      (IY+0),A
       LD      A,C             ; Differenz der Exponenten berechnen
       SUB     B
       LD      B,A             ; Differenz nach B fuer Loop-Befehl
       JR      Z,AD_NAP        ; falls Null, dann keine Anpassung
       CP      25              ; mehr als 24? (Abfrage mit Carry
       JP      NC,AD_RND       ; erfordert Vergleich mit 25)
AD_ANP:
       SRL     (IY+3)          ; Anpassung der zweiten Mantisse
       RR      (IY+2)          ; durch Verschiebung nach rechts
       RR      (IY+1)
       RR      (IY+0)
       DJNZ    AD_ANP          ; Loop-Befehl bis B = 0
AD_NAP:
       BIT     7,D             ; Subtraktion oder Addition?
       JR      NZ,SUBTR        ; ggf. zur Subtraktion springen
       LD      A,(IX+0)        ; jetzt werden die beiden Mantissen
       ADD     A,(IY+0)        ; zueinander addiert
       LD      (IX+0),A
       LD      A,(IX+1)
       ADC     A,(IY+1)
       LD      (IX+1),A
       LD      A,(IX+2)
       ADC     A,(IY+2)
       LD      (IX+2),A
       LD      A,(IX+3)
       ADC     A,(IY+3)
       LD      (IX+3),A
       JR      NC,AD_RND       ; kein Ueberlauf --> zum Runden
       RR      (IX+3)          ; Ueberlauf einschieben
       RR      (IX+2)          ; und Exponent erhoehen
       RR      (IX+1)          ; durch die Vorgeschichte ist
       RR      (IX+0)          ; gesichert, dass B Null ist; BC
       INC     BC              ; enthaelt den 16-Bit-Exponent
       JR      AD_RND          ; und zum Runden
SUBTR:
       LD      A,(IX+0)        ; Die beiden Mantissen werden
       SUB     (IY+0)          ; voneinander subtrahiert
       LD      (IX+0),A
       LD      A,(IX+1)
       SBC     A,(IY+1)
       LD      (IX+1),A
       LD      A,(IX+2)
       SBC     A,(IY+2)
       LD      (IX+2),A
       LD      A,(IX+3)
       SBC     A,(IY+3)
       LD      (IX+3),A
       JP      M,AD_RND        ; bei fuehrender Eins zum Runden
       JR      NZ,AD_NRM       ; ungleich Null: Normalisieren
       CP      (IX+2)          ; Rest der Mantisse auch Null?
       JR      NZ,AD_NRM
       CP      (IX+1)
       JR      NZ,AD_NRM
       CP      (IX+0)
       JR      Z,AD_ZERO       ; alles Null --> Ergebnis ist Null
AD_NRM:
       XOR     A               ; A = 0
AD_NR1:
       CP      C               ; Exponent ist Null?
       JR      NZ,AD_NR2       ; nein, Normierung moeglich
       CP      B               ; oberes Byte auch Null?
       JR      Z,AD_RND        ; dann ist Ergebnis denormalisiert
AD_NR2:
       DEC     BC              ; Exponent erniedrigen
       SLA     (IX+0)          ; Mantisse normalisieren bis
       RL      (IX+1)          ; fuehrende Eins auftaucht
       RL      (IX+2)
       RL      (IX+3)
       JP      P,AD_NR1        ; weiter bis fuehrende Eins auftaucht
AD_RND:
       LD      A,(IX+0)        ; jetzt Runden auf Bit hinter
       ADD     A,80H           ; Mantisse
       JR      NC,AD_NOV       ; kein Uebertrag?
       INC     (IX+1)          ; doch, naechstes Mantissenbyte
       JR      NZ,AD_NOV       ; behandeln, jetzt auf Null pruefen,
       INC     (IX+2)          ; da der INC-Befehl kein Carry liefert
       JR      NZ,AD_NOV
       INC     (IX+3)
       JR      NZ,AD_NOV
       SCF                     ; Eins erzeugen
       RR      (IX+3)          ; bei Ueberlauf Mantisse durch
       RR      (IX+2)          ; Rechtsschieben wieder normalisieren
       RR      (IX+1)          ; (nur noch 24 Bit noetig)
       INC     BC              ; und Exponent korrigieren
AD_NOV:
       XOR     A               ; A = 0
       CP      (IX+3)          ; Mantisse auf Null pruefen
       JR      NZ,AD_NOZ
       CP      (IX+2)
       JR      NZ,AD_NOZ
       CP      (IX+1)          ; alle Mantissenbytes Null?
       JR      NZ,AD_NOZ       ; dann ist auch das Ergebnis Null
AD_ZERO:                        ; Null Ergebnis aufbauen
       LD      B,A
       LD      C,A
       LD      D,A
       LD      E,A
       JR      AD_EXIT         ; dann Routine verlassen
AD_NOZ:
       CP      B               ; A ist 0
       LD      A,MAXEXPO       ; Exponent oberstes Byte ungleich Null?
       JR      NZ,AD_OVR       ; dann ist Ueberlauf eingetreten
       CP      C               ; oder genau maxexpo erreicht?
       JR      NZ,AD_NUE       ; nein, --> kein Ueberlauf
AD_OVR:
       LD      C,A             ; Exponent auf maxexpo setzen
       XOR     A               ; und Mantisse auf Null
       LD      (IX+3),A        ; fuer unendlich
       LD      (IX+2),A
       LD      (IX+1),A
       JR      AD_DEN
AD_NUE:
       XOR     A               ; A = 0
       CP      C               ; Exponent Null (Zahl denormalisiert)?
       JR      Z,AD_DEN        ; ja, -->
       SLA     (IX+1)          ; fuehrendes Bit wird nicht gespeichert
       RL      (IX+2)          ; daher Mantisse um 1 Bit nach links
       RL      (IX+3)
AD_DEN:
       LD      B,C             ; Ergebnis aufbauen: Exponent in B
       LD      C,(IX+3)        ; Mantisse oberstes Byte
       LD      D,(IX+2)
       SLA     E               ; Vorzeichen aus E in Carry schieben
       LD      E,(IX+1)
       RR      B               ; Vorzeichen in Ergebnis einschieben
       RR      C
       RR      D
       RR      E
AD_EXIT:
       POP     IY              ; Register restaurieren
       POP     IX
       POP     AF
       POP     HL
       LD      (F_HL),HL       ; HL zwischenspeichern
       EX      (SP),HL         ; alte Ruecksprungadresse in HL
       LD      SP,(F_STACK)    ; Stack zuruecksetzen
       PUSH    HL              ; Ruecksprungadresse ablegen
       LD      HL,(F_HL)       ; HL wieder laden
       RET                     ; Ende des Unterprogramms

; *************************************************
; * Fliesskomma-Subtraktion in Single-Precision
; * Parameter: Operand 1 und Operand 2 ueber Stack
; * Ergebnis: in BC-DE: MSB in B, LSB in E
; *

F_SUB:
       PUSH    HL              ; alten Basepointer retten
       LD      (F_STACK),SP    ; aktuellen Stackpointer abspeichern
       LD      HL,(F_STACK)    ; und in HL laden (= Basepointer)
       PUSH    AF              ; benoetigte Register retten
       PUSH    IX
       PUSH    IY
       LD      BC,OP1
       ADD     HL,BC
       PUSH    HL
       POP     IX              ; IX zeigt auf Operand 1
       LD      BC,OPSIZE
       ADD     HL,BC
       PUSH    HL
       POP     IY              ; IY zeigt auf Operand 2
       LD      A,80H
       XOR     (IY+3)          ; Vorzeichenbit von Operand 2 umdrehen
       LD      (IY+3),A        ; wieder abspeichern
       JP      F_ADSUB         ; jetzt weiter bei Additionsroutine

; *************************************************
; * Fliesskomma-Multiplikation in Single-Precision
; * Parameter: Operand 1 und Operand 2 ueber Stack
; * Ergebnis: in BC-DE: MSB in B, LSB in E
; *

TEMP    EQU     -10             ; Offset lokale Variable (6 Byte)

F_MUL:
       PUSH    HL              ; alten Basepointer retten
       LD      (F_STACK),SP    ; aktuellen Stackpointer abspeichern
       LD      HL,(F_STACK)    ; und in HL laden (= Basepointer)
       PUSH    AF              ; benoetigte Register retten
       PUSH    IX
       PUSH    IY
       LD      BC,OP1
       ADD     HL,BC
       PUSH    HL
       EX      (SP),IX         ; IX zeigt auf Operand 1
                               ; 2 Dummy-Byte auf Stack fuer lokale
       LD      BC,OPSIZE       ; Variable bleiben stehen
       ADD     HL,BC
       PUSH    HL
       EX      (SP),IY         ; IY zeigt auf Operand 2
       PUSH    HL              ; insgesamt 6 Byte fuer lokale Variable
       ADD     HL,BC           ; HL zeigt jetzt hinter die Operanden!
       LD      (F_STACK),HL
       LD      A,(IX+3)        ; Ergebnisvorzeichen bestimmen
       XOR     (IY+3)
       LD      C,A             ; Vorzeichen in C Bit 7 merken
       LD      D,0             ; Exponent 1 laden
       LD      E,(IX+3)
       LD      A,(IX+2)        ; Operand um 8 Bit nach links schieben
       LD      (IX+3),A
       RES     7,(IX+3)        ; implizite Null vorbesetzen
       SLA     A               ; Exponent unterstes Bit in Carry
       RL      E               ; und in E einschieben
       JR      Z,MU_DN1        ; falls Null, dann OP1 denormalisieren
       SET     7,(IX+3)        ; implizite Eins erzeugen
       DEC     DE              ; Bias kompensieren
MU_DN1:
       LD      A,(IX+1)        ; jetzt restliche Bytes verschieben
       LD      (IX+2),A
       LD      A,(IX+0)
       LD      (IX+1),A
       XOR     A               ; unterste Mantissenbits loeschen
       LD      (IX+0),A        ; Form: FFFF ... FFFF 0000 0000
       LD      (IX+TEMP+5),A   ; lokale Variable mit Null vorbesetzen
       LD      (IX+TEMP+4),A
       LD      (IX+TEMP+3),A
       LD      (IX+TEMP+2),A
       LD      (IX+TEMP+1),A
       LD      (IX+TEMP+0),A
       LD      H,A             ; Exponent 2 in HL aufbauen
       LD      L,(IY+3)
       LD      A,(IY+2)
       RES     7,(IY+2)        ; implizite Null vorbesetzen
       SLA     A
       RL      L
       JR      Z,MU_DN2        ; gleich Null, dann Op2 denormalisieren
       SET     7,(IY+2)        ; implizite Eins erzeugen
       DEC     HL              ; Bias kompensieren
MU_DN2:
       ADD     HL,DE           ; Exponenten aufaddieren
       LD      DE,3-BIAS       ; Bias-3 subtrahieren
       ADD     HL,DE           ; bzw. 3-Bias addieren
       JP      P,MU_NOZ
       LD      A,L             ; Exponent kleiner als -24?
       CP      -24
       JR      NC,MU_NOZ
       JP      MU_ZERO         ; ja, dann ist das Ergebnis Null
MU_NOZ:
       LD      B,24            ; Multiplikationsschleifenzaehler
       LD      DE,0            ; Hilfsregister fuer Multiplikand
MU_MUL:
       SRL     (IX+3)          ; Multiplikand nach rechts schieben
       RR      (IX+2)
       RR      (IX+1)
       RR      (IX+0)
       RR      D               ; DE als Verlaengerung von Operand 1
       RR      E
       SLA     (IY+0)          ; Multiplikator nach links schieben
       RL      (IY+1)
       RL      (IY+2)          ; falls fuehrendes Bit Null ist, dann
       JR      NC,MU_NAD       ; muss nicht addiert werden
       LD      A,(IX+TEMP+0)   ; sonst Multiplikand aufaddieren
       ADD     A,E
       LD      (IX+TEMP+0),A
       LD      A,(IX+TEMP+1)
       ADC     A,D
       LD      (IX+TEMP+1),A
       LD      A,(IX+TEMP+2)
       ADC     A,(IX+0)
       LD      (IX+TEMP+2),A
       LD      A,(IX+TEMP+3)
       ADC     A,(IX+1)
       LD      (IX+TEMP+3),A
       LD      A,(IX+TEMP+4)
       ADC     A,(IX+2)
       LD      (IX+TEMP+4),A
       LD      A,(IX+TEMP+5)
       ADC     A,(IX+3)
       LD      (IX+TEMP+5),A
MU_NAD:
       DJNZ    MU_MUL          ; Schleife durchlaufen
       LD      A,(IX+TEMP+5)
       OR      A               ; Flags setzen
       JP      M,MU_RND        ; bei fuerender Eins zum Runden
       JR      NZ,MU_NOR       ; ungleich Null --> normalisieren
       CP      (IX+TEMP+4)
       JR      NZ,MU_NOR
       CP      (IX+TEMP+3)
       JR      NZ,MU_NOR
       CP      (IX+TEMP+2)
       JR      NZ,MU_NOR
       JP      MU_ZERO         ; Mantisse komplett Null --> Null
MU_NOR:
       XOR     A               ; A = 0
       OR      H               ; Exponent ist negativ?
       JP      M,MU_UNT        ; ggf. Unterlauf behandeln
MU_NR1:
       XOR     A               ; A = 0
       CP      L               ; Exponent = Null?
       JR      NZ,MU_NR2
       CP      H               ; bei Null zum Runden
       JR      Z,MU_RND
MU_NR2:
       DEC     HL              ; Exponent erniedrigen
       SLA     (IX+TEMP+0)
       RL      (IX+TEMP+1)
       RL      (IX+TEMP+2)     ; Mantisse solange nach links
       RL      (IX+TEMP+3)     ; verschieben bis fuerende Eins
       RL      (IX+TEMP+4)     ; auftaucht
       RL      (IX+TEMP+5)
       JP      P,MU_NR1
MU_RND:
       LD      A,(IX+TEMP+2)   ; jetzt Runden auf Bit hinter
       ADD     A,80H           ; Mantisse
       JR      NC,MU_NOV       ; kein Uebertrag?
       INC     (IX+TEMP+3)     ; doch, naechstes Mantissenbyte
       JR      NZ,MU_NOV       ; behandeln, jetzt auf Null pruefen
       INC     (IX+TEMP+4)     ; da der INC-Befehl kein Carry liefert
       JR      NZ,MU_NOV
       INC     (IX+TEMP+5)
       JR      NZ,MU_NOV
       SCF                     ; Eins erzeugen
       RR      (IX+TEMP+5)     ; bei Ueberlauf Mantisse durch
       RR      (IX+TEMP+4)     ; Rechtsschieben wieder normalisieren
       RR      (IX+TEMP+3)
       INC     HL              ; und Eponent korrigieren
MU_NOV:
       XOR     A               ; A = 0
       CP      H               ; Exponent pruefen
       LD      A,MAXEXPO       ; A vorbesetzen
       JR      NZ,MU_OVR       ; groesser Null: Ueberlauf behandeln
       CP      L               ; oder genau maxexpo erreicht?
       JR      NZ,MU_NUE       ; nein, kein Ueberlauf
MU_OVR:
       LD      L,MAXEXPO       ; Ueberlauf: Exponent = maxexpo
       XOR     A               ;            Mantisse = Null
       LD      (IX+TEMP+5),A
       LD      (IX+TEMP+4),A
       LD      (IX+TEMP+3),A
       JR      MU_DEN
MU_NUE:
       XOR     A               ; A = 0
       CP      L               ; Exponent ist Null?
       JR      Z,MU_DEN        ; ja, Ergebnis ist denormalisiert
       SLA     (IX+TEMP+3)     ; nein, fuehrendes Mantissenbit
       RL      (IX+TEMP+4)     ; rausschieben
       RL      (IX+TEMP+5)
MU_DEN:
       SLA     C               ; Vorzeichen in Carry schieben
       LD      B,L             ; Exponent einsetzen
       LD      C,(IX+TEMP+5)
       LD      D,(IX+TEMP+4)
       LD      E,(IX+TEMP+3)
       RR      B               ; und Vorzeichen einschieben
       RR      C
       RR      D               ; Form: SEEE EEEE EFFF FFFF ... FFFF
       RR      E
MU_RES:
       POP     HL              ; lokale Variable deallozieren
       POP     HL
       POP     HL
       POP     IY              ; Register restaurieren
       POP     IX
       POP     AF
       POP     HL
       LD      (F_HL),HL       ; Parameter vom Stack deallozieren
       EX      (SP),HL
       LD      SP,(F_STACK)
       PUSH    HL
       LD      HL,(F_HL)
       RET                     ; und return
MU_ZERO:
       XOR     A               ; Ergebnis ist Null
       LD      B,A
       LD      C,A
       LD      D,A
       LD      E,A
       JR      MU_RES
MU_UNT:
       LD      A,L             ; Exponent in A
       NEG                     ; negieren fuer Schleifenzaehler
       CP      24              ; totaler Ueberlauf?
       JR      NC,MU_ZERO      ; ja, dann ist Ergebnis Null
       LD      B,A             ; in B fuer Loop
MU_SHR:
       SRL     (IX+TEMP+5)     ; Mantisse denormalisieren
       RR      (IX+TEMP+4)     ; bis Exponent Null ist
       RR      (IX+TEMP+3)
       DJNZ    MU_SHR
       LD      L,B             ; Exponent in Register L = B = 0
       JP      MU_DEN          ; denormalisiertes Ergebnis erzeugen

; *************************************************
; * Fliesskomma-Division in Single-Precision
; * Parameter: Operand 1 und Operand 2 ueber Stack
; * Ergebnis: in BC-DE: MSB in B, LSB in E
; *

F_DIV:
       PUSH    HL              ; alten Basepointer retten
       LD      (F_STACK),SP    ; aktuellen Stackpointer abspeichern
       LD      HL,(F_STACK)    ; und in HL laden (= Basepointer)
       PUSH    AF              ; benoetigte Register retten
       PUSH    IX
       PUSH    IY
       LD      BC,OP1
       ADD     HL,BC
       PUSH    HL
       EX      (SP),IX         ; IX zeigt auf Operand 1
                               ; 2 Dummy-Byte auf Stack fuer lokale
       LD      BC,OPSIZE       ; Variable bleiben stehen
       ADD     HL,BC
       PUSH    HL
       EX      (SP),IY         ; IY zeigt auf Operand 2
       PUSH    HL              ; insgesamt 6 Byte fuer lokale Variable
       ADD     HL,BC           ; HL zeigt jetzt hinter die Operanden!
       LD      (F_STACK)
,HL
       LD      A,(IX+3)        ; Ergebnisvorzeichen bestimmen
       XOR     (IY+3)
       LD      C,A             ; Vorzeichen in C Bit 7 merken
       LD      H,0             ; Exponent 1 laden
       LD      L,(IX+3)
       LD      A,(IX+2)
       RES     7,(IX+2)        ; implizite Null vorbesetzen
       SLA     A               ; Exponent unterstes Bit in Carry
       RL      L               ; und in E einschieben
       JR      Z,DV_DN1        ; falls Null, dann Op1 denormalisieren
       SET     7,(IX+2)        ; implizite Eins erzeugen
       DEC     HL              ; Bias kompensieren
DV_DN1:
       LD      D,0             ; Exponent 2 in DE aufbauen
       LD      E,(IY+3)
       LD      A,(IY+2)
       LD      (IY+3),A        ; Mantisse um 8 Bit verschieben
       RES     7,(IY+3)        ; implizite Null vorbesetzen
       SLA     A
       RL      E
       JR      Z,DV_DN2        ; gleich Null, dann Op2 denormalisieren
       SET     7,(IY+3)        ; implizite Eins erzeugen
       DEC     DE              ; Bias kompensieren
DV_DN2:
       LD      A,(IY+1)        ; jetzt restliche Bytes verschieben
       LD      (IY+2),A
       LD      A,(IY+0)
       LD      (IY+1),A
       XOR     A               ; A = 0
       LD      (IY+0),A        ; Form: FFFF ... FFFF 0000 0000
       SRL     (IY+3)
       RR      (IY+2)
       RR      (IY+1)
       RR      (IY+0)          ; Form: 0FFF ... FFFF F000 0000
       JR      NZ,DV_NZ1       ; Mantisse 2 auf Null pruefen
       CP      (IY+1)
       JR      NZ,DV_NZ1
       CP      (IY+2)
       JR      NZ,DV_NZ1
       CP      (IY+3)
       JR      NZ,DV_NZ1
       JP      MU_OVR          ; Bei Division durch Null: unendlich
DV_NZ1:
       XOR     A               ; Carry-Flag loeschen
       SBC     HL,DE           ; Exponenten subtrahieren
       LD      DE,BIAS         ; Bias addieren
       ADD     HL,DE
       BIT     7,H             ; Exponent positiv?
       JR      Z,DV_NOZ
       LD      A,L             ; Exponent kleiner als -24?
       JR      NC,DV_NOZ
       JP      MU_ZERO         ; ja, dann ist das Ergebnis Null
DV_NOZ:
       PUSH    BC              ; Vorzeichen retten
       LD      DE,25           ; Exponent um 25 erhoehen
       ADD     HL,DE           ; jetzt ist er sicher groesser als Null
       XOR     A               ; A = 0
       LD      B,(IX+2)        ; Divident in Register kopieren
       LD      C,(IX+1)
       LD      D,(IX+0)
       LD      E,A             ; die untersten Bits sind Null
       CP      D               ; ist Dividend Null?
       JR      NZ,DV_NZ2
       CP      C
       JR      NZ,DV_NZ2
       CP      B
       JR      NZ,DV_NZ2
       POP     BC              ; Stack bereinigen (Vorzeichen laden)
       JP      MU_ZERO         ; und Null als Ergebnis ausgeben
DV_NZ2:
       LD      (IX+TEMP+5),A   ; Ergebnis vorbesetzen
       LD      (IX+TEMP+4),A
       LD      (IX+TEMP+3),A
       LD      (IX+TEMP+2),A
DV_NLP:
       BIT     6,(IY+3)        ; ist der Divisor normalisiert
       JR      NZ,DV_NOR       ; ja, -->
       INC     HL              ; nein, Exponent erhoehen
       SLA     (IY+0)          ; Divisor verschieben bis in
       RL      (IY+1)          ; Form 01FF ...
       RL      (IY+2)
       RL      (IY+3)
       JR      DV_NLP
DV_NOR:
       SRL     B
       RR      C
       RR      D
       RR      E               ; Form: 0FFF ... FFFF F000 0000
DV_LOP:
       LD      (IX+3),B        ; Dividend zwischenspeichern
       LD      (IX+2),C        ; die Speicherplaetze von Op1
       LD      (IX+1),D        ; stehen zur Verfuegung, da wir OP1
       LD      (IX+0),E        ; in die Register BC-DE kopiert haben
       LD      A,E             ; jetzt Divisor abziehen
       SUB     (IY+0)
       LD      E,A
       LD      A,D
       SBC     A,(IY+1)
       LD      D,A
       LD      A,C
       SBC     A,(IY+2)
       LD      C,A
       LD      A,B
       SBC     A,(IY+3)
       LD      B,A
       JR      NC,DV_ONE       ; kein Carry: Divisor passt
       LD      E,(IX+0)        ; zurueckkopieren
       LD      D,(IX+1)        ; Carry bleibt dabei erhalten
       LD      C,(IX+2)
       LD      B,(IX+3)
DV_ONE:
       CCF                     ; Carry-Flag umkehren
       RL      (IX+TEMP+2)     ; Ergebnis aufbauen
       RL      (IX+TEMP+3)
       RL      (IX+TEMP+4)
       RL      (IX+TEMP+5)
       SLA     E               ; Dividend verschieben
       RL      D
       RL      C
       RL      B
       DEC     HL              ; Exponent erniedrigen
       XOR     A               ; A = 0
       CP      L               ; Exponent = Null ?
       JR      NZ,DV_DIV
       CP      H
       JR      Z,DV_DEN        ; falls Null, dann denormalisiert
DV_DIV:
       BIT     0,(IX+TEMP+5)   ; fuerende Eins in Ergebnis-Mantisse?
       JR      Z,DV_LOP        ; nein, weiter rechnen
DV_DEN:
       LD      B,(IX+TEMP+5)   ; hoechstes Bit merken
       LD      A,(IX+TEMP+4)
       LD      (IX+TEMP+5),A   ; Mantisse in Form
       LD      A,(IX+TEMP+3)   ; FFFF ... FFFF 0000 0000
       LD      (IX+TEMP+4),A
       LD      A,(IX+TEMP+2)
       LD      (IX+TEMP+3),A
       RR      B               ; hoechstes Bit einschieben
       RR      (IX+TEMP+5)
       RR      (IX+TEMP+4)
       RR      (IX+TEMP+3)     ; Form: FFFF ... FFFF F000 0000
       RR      (IX+TEMP+2)
       POP     BC              ; Vorzeichen wieder laden
       XOR     A               ; A = 0
       CP      (IX+TEMP+5)     ; Mantisse ist Null?
       JR      NZ,DV_NZ3
       CP      (IX+TEMP+4)
       JR      NZ,DV_NZ3
       CP      (IX+TEMP+3)
       JR      NZ,DV_NZ3
       CP      (IX+TEMP+2)
       JP      Z,MU_ZERO       ; dann ist Ergebnis auch Null
DV_NZ3:
       JP      MU_RND          ; sonst weiter wie bei Multiplikation

F_STACK:
       DEFS    2               ; Hilfsspeicher fuer Stackpointer
F_HL:
       DEFS    2               ; Hilfsspeicher fuer Basepointer HL

       END