forked from amberisvibin/chibi-pc09
838 lines
20 KiB
NASM
838 lines
20 KiB
NASM
;
|
|
; this is a Z80 floating point package from an ancient german computer magazine
|
|
; I'm not going to translate this into english
|
|
; assemble this source with z80asm and run it under z80sim, if everything
|
|
; is working it should print the numbers below
|
|
;
|
|
; *******************************
|
|
; * 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
|