; ******************************* ; * 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 * 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