home *** CD-ROM | disk | FTP | other *** search
- ; *******************************
- ; * 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
-