1;
2; this is a Z80 floating point package from an ancient german computer magazine
3; I'm not going to translate this into english
4; assemble this source with z80asm and run it under z80sim, if everything
5; is working it should print the numbers below
6;
7; *******************************
8; * Fliesskomma-Arithmetik fuer	*
9; * den Z80-Mikroprozessor	*
10; * (mc 12/88, Seite 100	*
11; *******************************
12
13; ********************************************************
14; * Die folgende Testroutine liefert die Ausgabe:
15; *	40400000
16; *	00700000
17; *	7F800000
18; *	35BFFFFF
19; *	00400000
20; *	7F7FFFFF
21; *	7F800000
22; *	406DB6DB
23; *	15400001
24
25START:
26	LD	SP,STACK
27	LD	BC,3F80H	; Aufruf der Additionsroutine
28	LD	DE,0000H	; mit verschiedenen Parametern
29	PUSH	BC		; entspricht 1 + 2
30	PUSH	DE
31	LD	BC,4000H
32	LD	DE,0000H
33	PUSH	BC
34	PUSH	DE
35	CALL	F_ADD
36	CALL	HEXOUT		; anschliessend Ausgabe
37
38	LD	BC,00F0H	; eine kleine, gerade noch normalisierte
39	LD	DE,0000H	; Zahl, dazu die kleinste normalisierte
40	PUSH	BC		; Zahl mit negativem Vorzeichen addieren
41	PUSH	DE
42	LD	BC,8080H
43	LD	DE,0000H
44	PUSH	BC
45	PUSH	DE
46	CALL	F_ADD
47	CALL	HEXOUT
48
49	LD	BC,7F00H	; die Summe dieser beiden Zahlen
50	LD	DE,0000H	; ergibt unendlich. Setzt man
51	PUSH	BC		; fuer die zweite Zahl den Wert
52	PUSH	DE		; 7EFFFFFE, so ist das Ergebnis
53	LD	BC,7EFFH	; gerade MAXFLOAT
54	LD	DE,0FFFFH
55	PUSH	BC
56	PUSH	DE
57	CALL	F_ADD
58	CALL	HEXOUT
59
60	LD	BC,0000H	; Multiplikation testen
61	LD	DE,0003H	; MAXFLOAT * <denormalisierte Zahl>
62	PUSH	BC
63	PUSH	DE
64	LD	BC,7F7FH
65	LD	DE,0FFFFH
66	PUSH	BC
67	PUSH	DE
68	CALL	F_MUL
69	CALL	HEXOUT
70
71	LD	BC,0080H	; die kleinste normalisierte Zahl
72	LD	DE,0000H	; mit 0.5 multiplizieren
73	PUSH	BC		; (ergibt eine denormalisierte Zahl)
74	PUSH	DE
75	LD	BC,3F00H
76	LD	DE,0000H
77	PUSH	BC
78	PUSH	DE
79	CALL	F_MUL
80	CALL	HEXOUT
81
82	LD	BC,4000H	; eine sehr grosse Zahl mit zwei
83	LD	DE,0000H	; multiplizieren. Das Ergebnis
84	PUSH	BC		; ist genau MAXFLOAT
85	PUSH	DE
86	LD	BC,7EFFH
87	LD	DE,0FFFFH
88	PUSH	BC
89	PUSH	DE
90	CALL	F_MUL
91	CALL	HEXOUT
92
93	LD	BC,0000H	; Test der Divisionsroutine
94	LD	DE,0000H	; hier 1 / 0 (ergibt unendlich)
95	PUSH	BC
96	PUSH	DE
97	LD	BC,3F80H
98	LD	DE,0000H
99	PUSH	BC
100	PUSH	DE
101	CALL	F_DIV
102	CALL	HEXOUT
103
104	LD	BC,40E0H	; jetzt 26 / 7 berechnen
105	LD	DE,0000H
106	PUSH	BC
107	PUSH	DE
108	LD	BC,41D0H
109	LD	DE,0000H
110	PUSH	BC
111	PUSH	DE
112	CALL	F_DIV
113	CALL	HEXOUT
114
115	LD	BC,1FFFH	; jetzt eine sehr kleine
116	LD	DE,0FFFFH	; denormalisierte Zahl durch
117	PUSH	BC		; eine kleine normalisierte
118	PUSH	DE		; Zahl dividieren
119	LD	BC,0000H
120	LD	DE,0003H
121	PUSH	BC
122	PUSH	DE
123	CALL	F_DIV
124	CALL	HEXOUT
125
126	HALT			; Ende des Tests
127
128	DEFS	100
129STACK:
130
131; ************************************************
132; * Zahl in BC-DE in 8 Hexadezimalziffern drucken.
133; * Dazu werden nacheinander die Nibble-Paare in
134; * B, C, D und E ausgedruckt.
135; *
136
137HEXOUT:
138	LD	A,B		; Nacheinander die einzelnen
139	CALL	DIG2		; Nibble-Paare in A laden
140	LD	A,C		; und ausdrucken
141	CALL	DIG2
142	LD	A,D
143	CALL	DIG2
144	LD	A,E
145	CALL	DIG2
146	LD	A,10
147	CALL	OUTCHAR
148	LD	A,13
149	CALL	OUTCHAR
150	RET
151
152DIG2:
153	PUSH	AF		; Nibble-Paar ausdrucken
154	RRCA			; unterstes Nibble retten
155	RRCA			; oberes Nibble rechtsbuendig
156	RRCA			; positionieren
157	RRCA
158	AND	00001111B
159	ADD	A,90H		; binaer in ASCII (hex)
160	DAA
161	ADC	A,40H
162	DAA
163	CALL	OUTCHAR		; Zeichen ausgeben
164	POP	AF		; jetzt unteres Nibble verarbeiten
165	AND	00001111B	; Nibble maskieren
166	ADD	A,90H		; binaer in ASCII (hex)
167	DAA
168	ADC	A,40H
169	DAA
170	CALL	OUTCHAR
171	RET
172
173OUTCHAR:			; Zeichen auf Console ausgeben
174	OUT	(1),A
175	RET
176
177; **********************************
178; * Globale Konstanten-Definitionen
179; * fuer das Fliesskommapaket
180; *
181
182MAXEXPO	EQU	255		; Maximal zulaessiger Exponent
183BIAS	EQU	127		; Bias des Exponenten
184
185; *************************************************
186; * Fliesskomma-Addition in Single-Precision
187; * Parameter: Operand 1 und Operand 2 ueber Stack
188; * Ergebnis:  in BC-DE: MSB in B, LSB in E
189; *
190
191; * Es folgen Offset-Definitionen fuer Stack-relativen Zugriff
192
193FHL_ALT	EQU	0		; Top of Stack liegt HL
194FADR	EQU	2		; dann die Ruecksprungadresse
195OP1	EQU	4		; jetzt Offset-Definitionen fuer
196OP2	EQU	8		; Parameter-Uebergabe
197
198OPSIZE	EQU	4		; Groesse eines Operanden
199
200F_ADD:
201	PUSH	HL		; alten Basepointer retten
202	LD	(F_STACK),SP	; aktuellen Stackpointer abspeichern
203	LD	HL,(F_STACK)	; und in HL laden (= Basepointer)
204	PUSH	AF		; benoetigte Register retten
205	PUSH	IX
206	PUSH	IY
207	LD	BC,OP1		; jeztz die Zeiger auf die
208	ADD	HL,BC		; Operanden initialisieren
209	PUSH	HL
210	POP	IX		; IX zeigt auf Operand 1
211	LD	BC,OPSIZE
212	ADD	HL,BC
213	PUSH	HL
214	POP	IY		; IY zeigt auf Operand 2
215F_ADSUB:
216	ADD	HL,BC		; HL zeigt jetzt hinter die Operanden!
217	LD	(F_STACK),HL	; diese Adresse fuer's Ende merken
218	LD	A,(IX+3)	; Vorzeichen von Operand 1 laden
219	LD	E,A		; Ergebnisvorzeichen in E, Bit 7
220	XOR	(IY+3)		; mit Vorzeichen von OP2 verknuepfen
221	LD	D,A		; Subtraktionsflag in D, Bit 7
222	RES	7,(IX+3)	; Vorzeichen in Mantisse 1 loeschen
223	RES	7,(IY+3)	; Vorzeichen in Mantisse 2 loeschen
224
225; Die Operanden sind jetzt in der Form: 0EEE EEEE EFFF ... FFFF
226
227	LD	A,(IX+0)	; Differenz OP1 - OP2 bilden
228	SUB	(IY+0)
229	LD	A,(IX+1)
230	SBC	A,(IY+1)
231	LD	A,(IX+2)
232	SBC	A,(IY+2)
233	LD	A,(IX+3)
234	SBC	A,(IY+3)
235	JR	NC,FAD_1	; Sprung falls OP1 groesser als OP2
236	PUSH	IX		; ansonsten Operanden vertauschen
237	EX	(SP),IY		; (eigentlich nur die Pointer), so
238	POP	IX		; dass IY den Kleineren adressiert
239	LD	A,E		; Ergebnisvorzeichen neu berechnen
240	XOR	D
241	LD	E,A
242FAD_1:
243	LD	A,(IX+2)
244	LD	C,(IX+3)	; Exponent der groesseren Zahl laden
245	SLA	A
246	RL	C
247	JR	Z,AD_DN1
248	SET	7,(IX+2)	; implizite Eins erzeugen
249AD_DN1:
250	LD	A,(IY+2)
251	LD	B,(IY+3)	; Exponent der kleineren Zahl laden
252	SLA	A
253	RL	B
254	JR	Z,AD_DN2
255	SET	7,(IY+2)	; implizite Eins erzeugen
256AD_DN2:
257	PUSH	BC		; Jetzt die Register fuer den
258	PUSH	DE		; Blocktransferbefehl retten
259	LD	BC,(OPSIZE*2)-1 ; beide Operanden verschieben
260	DEC	HL		; HL zeigt auf letztes Byte
261	PUSH	HL		; HL nach DE kopieren
262	POP	DE
263	DEC	HL		; HL zeigt auf vorletztes Byte
264	LDDR			; Verschiebung beider Mantissen
265	POP	DE		; um 8 Bit nach links
266	POP	BC
267	XOR	A
268	LD	(IX+0),A	; Form: FFFF ... FFFF 0000 0000
269	LD	(IY+0),A
270	LD	A,C		; Differenz der Exponenten berechnen
271	SUB	B
272	LD	B,A		; Differenz nach B fuer Loop-Befehl
273	JR	Z,AD_NAP	; falls Null, dann keine Anpassung
274	CP	25		; mehr als 24? (Abfrage mit Carry
275	JP	NC,AD_RND	; erfordert Vergleich mit 25)
276AD_ANP:
277	SRL	(IY+3)		; Anpassung der zweiten Mantisse
278	RR	(IY+2)		; durch Verschiebung nach rechts
279	RR	(IY+1)
280	RR	(IY+0)
281	DJNZ	AD_ANP		; Loop-Befehl bis B = 0
282AD_NAP:
283	BIT	7,D		; Subtraktion oder Addition?
284	JR	NZ,SUBTR	; ggf. zur Subtraktion springen
285	LD	A,(IX+0)	; jetzt werden die beiden Mantissen
286	ADD	A,(IY+0)	; zueinander addiert
287	LD	(IX+0),A
288	LD	A,(IX+1)
289	ADC	A,(IY+1)
290	LD	(IX+1),A
291	LD	A,(IX+2)
292	ADC	A,(IY+2)
293	LD	(IX+2),A
294	LD	A,(IX+3)
295	ADC	A,(IY+3)
296	LD	(IX+3),A
297	JR	NC,AD_RND	; kein Ueberlauf --> zum Runden
298	RR	(IX+3)		; Ueberlauf einschieben
299	RR	(IX+2)		; und Exponent erhoehen
300	RR	(IX+1)		; durch die Vorgeschichte ist
301	RR	(IX+0)		; gesichert, dass B Null ist; BC
302	INC	BC		; enthaelt den 16-Bit-Exponent
303	JR	AD_RND		; und zum Runden
304SUBTR:
305	LD	A,(IX+0)	; Die beiden Mantissen werden
306	SUB	(IY+0)		; voneinander subtrahiert
307	LD	(IX+0),A
308	LD	A,(IX+1)
309	SBC	A,(IY+1)
310	LD	(IX+1),A
311	LD	A,(IX+2)
312	SBC	A,(IY+2)
313	LD	(IX+2),A
314	LD	A,(IX+3)
315	SBC	A,(IY+3)
316	LD	(IX+3),A
317	JP	M,AD_RND	; bei fuehrender Eins zum Runden
318	JR	NZ,AD_NRM	; ungleich Null: Normalisieren
319	CP	(IX+2)		; Rest der Mantisse auch Null?
320	JR	NZ,AD_NRM
321	CP	(IX+1)
322	JR	NZ,AD_NRM
323	CP	(IX+0)
324	JR	Z,AD_ZERO	; alles Null --> Ergebnis ist Null
325AD_NRM:
326	XOR	A		; A = 0
327AD_NR1:
328	CP	C		; Exponent ist Null?
329	JR	NZ,AD_NR2	; nein, Normierung moeglich
330	CP	B		; oberes Byte auch Null?
331	JR	Z,AD_RND	; dann ist Ergebnis denormalisiert
332AD_NR2:
333	DEC	BC		; Exponent erniedrigen
334	SLA	(IX+0)		; Mantisse normalisieren bis
335	RL	(IX+1)		; fuehrende Eins auftaucht
336	RL	(IX+2)
337	RL	(IX+3)
338	JP	P,AD_NR1	; weiter bis fuehrende Eins auftaucht
339AD_RND:
340	LD	A,(IX+0)	; jetzt Runden auf Bit hinter
341	ADD	A,80H		; Mantisse
342	JR	NC,AD_NOV	; kein Uebertrag?
343	INC	(IX+1)		; doch, naechstes Mantissenbyte
344	JR	NZ,AD_NOV	; behandeln, jetzt auf Null pruefen,
345	INC	(IX+2)		; da der INC-Befehl kein Carry liefert
346	JR	NZ,AD_NOV
347	INC	(IX+3)
348	JR	NZ,AD_NOV
349	SCF			; Eins erzeugen
350	RR	(IX+3)		; bei Ueberlauf Mantisse durch
351	RR	(IX+2)		; Rechtsschieben wieder normalisieren
352	RR	(IX+1)		; (nur noch 24 Bit noetig)
353	INC	BC		; und Exponent korrigieren
354AD_NOV:
355	XOR	A		; A = 0
356	CP	(IX+3)		; Mantisse auf Null pruefen
357	JR	NZ,AD_NOZ
358	CP	(IX+2)
359	JR	NZ,AD_NOZ
360	CP	(IX+1)		; alle Mantissenbytes Null?
361	JR	NZ,AD_NOZ	; dann ist auch das Ergebnis Null
362AD_ZERO:			; Null Ergebnis aufbauen
363	LD	B,A
364	LD	C,A
365	LD	D,A
366	LD	E,A
367	JR	AD_EXIT		; dann Routine verlassen
368AD_NOZ:
369	CP	B		; A ist 0
370	LD	A,MAXEXPO	; Exponent oberstes Byte ungleich Null?
371	JR	NZ,AD_OVR	; dann ist Ueberlauf eingetreten
372	CP	C		; oder genau maxexpo erreicht?
373	JR	NZ,AD_NUE	; nein, --> kein Ueberlauf
374AD_OVR:
375	LD	C,A		; Exponent auf maxexpo setzen
376	XOR	A		; und Mantisse auf Null
377	LD	(IX+3),A	; fuer unendlich
378	LD	(IX+2),A
379	LD	(IX+1),A
380	JR	AD_DEN
381AD_NUE:
382	XOR	A		; A = 0
383	CP	C		; Exponent Null (Zahl denormalisiert)?
384	JR	Z,AD_DEN	; ja, -->
385	SLA	(IX+1)		; fuehrendes Bit wird nicht gespeichert
386	RL	(IX+2)		; daher Mantisse um 1 Bit nach links
387	RL	(IX+3)
388AD_DEN:
389	LD	B,C		; Ergebnis aufbauen: Exponent in B
390	LD	C,(IX+3)	; Mantisse oberstes Byte
391	LD	D,(IX+2)
392	SLA	E		; Vorzeichen aus E in Carry schieben
393	LD	E,(IX+1)
394	RR	B		; Vorzeichen in Ergebnis einschieben
395	RR	C
396	RR	D
397	RR	E
398AD_EXIT:
399	POP	IY		; Register restaurieren
400	POP	IX
401	POP	AF
402	POP	HL
403	LD	(F_HL),HL	; HL zwischenspeichern
404	EX	(SP),HL		; alte Ruecksprungadresse in HL
405	LD	SP,(F_STACK)	; Stack zuruecksetzen
406	PUSH	HL		; Ruecksprungadresse ablegen
407	LD	HL,(F_HL)	; HL wieder laden
408	RET			; Ende des Unterprogramms
409
410; *************************************************
411; * Fliesskomma-Subtraktion in Single-Precision
412; * Parameter: Operand 1 und Operand 2 ueber Stack
413; * Ergebnis: in BC-DE: MSB in B, LSB in E
414; *
415
416F_SUB:
417	PUSH	HL		; alten Basepointer retten
418	LD	(F_STACK),SP	; aktuellen Stackpointer abspeichern
419	LD	HL,(F_STACK)	; und in HL laden (= Basepointer)
420	PUSH	AF		; benoetigte Register retten
421	PUSH	IX
422	PUSH	IY
423	LD	BC,OP1
424	ADD	HL,BC
425	PUSH	HL
426	POP	IX		; IX zeigt auf Operand 1
427	LD	BC,OPSIZE
428	ADD	HL,BC
429	PUSH	HL
430	POP	IY		; IY zeigt auf Operand 2
431	LD	A,80H
432	XOR	(IY+3)		; Vorzeichenbit von Operand 2 umdrehen
433	LD	(IY+3),A	; wieder abspeichern
434	JP	F_ADSUB		; jetzt weiter bei Additionsroutine
435
436; *************************************************
437; * Fliesskomma-Multiplikation in Single-Precision
438; * Parameter: Operand 1 und Operand 2 ueber Stack
439; * Ergebnis: in BC-DE: MSB in B, LSB in E
440; *
441
442TEMP	EQU	-10		; Offset lokale Variable (6 Byte)
443
444F_MUL:
445	PUSH	HL		; alten Basepointer retten
446	LD	(F_STACK),SP	; aktuellen Stackpointer abspeichern
447	LD	HL,(F_STACK)	; und in HL laden (= Basepointer)
448	PUSH	AF		; benoetigte Register retten
449	PUSH	IX
450	PUSH	IY
451	LD	BC,OP1
452	ADD	HL,BC
453	PUSH	HL
454	EX	(SP),IX		; IX zeigt auf Operand 1
455				; 2 Dummy-Byte auf Stack fuer lokale
456	LD	BC,OPSIZE	; Variable bleiben stehen
457	ADD	HL,BC
458	PUSH	HL
459	EX	(SP),IY		; IY zeigt auf Operand 2
460	PUSH	HL		; insgesamt 6 Byte fuer lokale Variable
461	ADD	HL,BC		; HL zeigt jetzt hinter die Operanden!
462	LD	(F_STACK),HL
463	LD	A,(IX+3)	; Ergebnisvorzeichen bestimmen
464	XOR	(IY+3)
465	LD	C,A		; Vorzeichen in C Bit 7 merken
466	LD	D,0		; Exponent 1 laden
467	LD	E,(IX+3)
468	LD	A,(IX+2)	; Operand um 8 Bit nach links schieben
469	LD	(IX+3),A
470	RES	7,(IX+3)	; implizite Null vorbesetzen
471	SLA	A		; Exponent unterstes Bit in Carry
472	RL	E		; und in E einschieben
473	JR	Z,MU_DN1	; falls Null, dann OP1 denormalisieren
474	SET	7,(IX+3)	; implizite Eins erzeugen
475	DEC	DE		; Bias kompensieren
476MU_DN1:
477	LD	A,(IX+1)	; jetzt restliche Bytes verschieben
478	LD	(IX+2),A
479	LD	A,(IX+0)
480	LD	(IX+1),A
481	XOR	A		; unterste Mantissenbits loeschen
482	LD	(IX+0),A	; Form: FFFF ... FFFF 0000 0000
483	LD	(IX+TEMP+5),A	; lokale Variable mit Null vorbesetzen
484	LD	(IX+TEMP+4),A
485	LD	(IX+TEMP+3),A
486	LD	(IX+TEMP+2),A
487	LD	(IX+TEMP+1),A
488	LD	(IX+TEMP+0),A
489	LD	H,A		; Exponent 2 in HL aufbauen
490	LD	L,(IY+3)
491	LD	A,(IY+2)
492	RES	7,(IY+2)	; implizite Null vorbesetzen
493	SLA	A
494	RL	L
495	JR	Z,MU_DN2	; gleich Null, dann Op2 denormalisieren
496	SET	7,(IY+2)	; implizite Eins erzeugen
497	DEC	HL		; Bias kompensieren
498MU_DN2:
499	ADD	HL,DE		; Exponenten aufaddieren
500	LD	DE,3-BIAS	; Bias-3 subtrahieren
501	ADD	HL,DE		; bzw. 3-Bias addieren
502	JP	P,MU_NOZ
503	LD	A,L		; Exponent kleiner als -24?
504	CP	-24
505	JR	NC,MU_NOZ
506	JP	MU_ZERO		; ja, dann ist das Ergebnis Null
507MU_NOZ:
508	LD	B,24		; Multiplikationsschleifenzaehler
509	LD	DE,0		; Hilfsregister fuer Multiplikand
510MU_MUL:
511	SRL	(IX+3)		; Multiplikand nach rechts schieben
512	RR	(IX+2)
513	RR	(IX+1)
514	RR	(IX+0)
515	RR	D		; DE als Verlaengerung von Operand 1
516	RR	E
517	SLA	(IY+0)		; Multiplikator nach links schieben
518	RL	(IY+1)
519	RL	(IY+2)		; falls fuehrendes Bit Null ist, dann
520	JR	NC,MU_NAD	; muss nicht addiert werden
521	LD	A,(IX+TEMP+0)	; sonst Multiplikand aufaddieren
522	ADD	A,E
523	LD	(IX+TEMP+0),A
524	LD	A,(IX+TEMP+1)
525	ADC	A,D
526	LD	(IX+TEMP+1),A
527	LD	A,(IX+TEMP+2)
528	ADC	A,(IX+0)
529	LD	(IX+TEMP+2),A
530	LD	A,(IX+TEMP+3)
531	ADC	A,(IX+1)
532	LD	(IX+TEMP+3),A
533	LD	A,(IX+TEMP+4)
534	ADC	A,(IX+2)
535	LD	(IX+TEMP+4),A
536	LD	A,(IX+TEMP+5)
537	ADC	A,(IX+3)
538	LD	(IX+TEMP+5),A
539MU_NAD:
540	DJNZ	MU_MUL		; Schleife durchlaufen
541	LD	A,(IX+TEMP+5)
542	OR	A		; Flags setzen
543	JP	M,MU_RND	; bei fuerender Eins zum Runden
544	JR	NZ,MU_NOR	; ungleich Null --> normalisieren
545	CP	(IX+TEMP+4)
546	JR	NZ,MU_NOR
547	CP	(IX+TEMP+3)
548	JR	NZ,MU_NOR
549	CP	(IX+TEMP+2)
550	JR	NZ,MU_NOR
551	JP	MU_ZERO		; Mantisse komplett Null --> Null
552MU_NOR:
553	XOR	A		; A = 0
554	OR	H		; Exponent ist negativ?
555	JP	M,MU_UNT	; ggf. Unterlauf behandeln
556MU_NR1:
557	XOR	A		; A = 0
558	CP	L		; Exponent = Null?
559	JR	NZ,MU_NR2
560	CP	H		; bei Null zum Runden
561	JR	Z,MU_RND
562MU_NR2:
563	DEC	HL		; Exponent erniedrigen
564	SLA	(IX+TEMP+0)
565	RL	(IX+TEMP+1)
566	RL	(IX+TEMP+2)	; Mantisse solange nach links
567	RL	(IX+TEMP+3)	; verschieben bis fuerende Eins
568	RL	(IX+TEMP+4)	; auftaucht
569	RL	(IX+TEMP+5)
570	JP	P,MU_NR1
571MU_RND:
572	LD	A,(IX+TEMP+2)	; jetzt Runden auf Bit hinter
573	ADD	A,80H		; Mantisse
574	JR	NC,MU_NOV	; kein Uebertrag?
575	INC	(IX+TEMP+3)	; doch, naechstes Mantissenbyte
576	JR	NZ,MU_NOV	; behandeln, jetzt auf Null pruefen
577	INC	(IX+TEMP+4)	; da der INC-Befehl kein Carry liefert
578	JR	NZ,MU_NOV
579	INC	(IX+TEMP+5)
580	JR	NZ,MU_NOV
581	SCF			; Eins erzeugen
582	RR	(IX+TEMP+5)	; bei Ueberlauf Mantisse durch
583	RR	(IX+TEMP+4)	; Rechtsschieben wieder normalisieren
584	RR	(IX+TEMP+3)
585	INC	HL		; und Eponent korrigieren
586MU_NOV:
587	XOR	A		; A = 0
588	CP	H		; Exponent pruefen
589	LD	A,MAXEXPO	; A vorbesetzen
590	JR	NZ,MU_OVR	; groesser Null: Ueberlauf behandeln
591	CP	L		; oder genau maxexpo erreicht?
592	JR	NZ,MU_NUE	; nein, kein Ueberlauf
593MU_OVR:
594	LD	L,MAXEXPO	; Ueberlauf: Exponent = maxexpo
595	XOR	A		; 	     Mantisse = Null
596	LD	(IX+TEMP+5),A
597	LD	(IX+TEMP+4),A
598	LD	(IX+TEMP+3),A
599	JR	MU_DEN
600MU_NUE:
601	XOR	A		; A = 0
602	CP	L		; Exponent ist Null?
603	JR	Z,MU_DEN	; ja, Ergebnis ist denormalisiert
604	SLA	(IX+TEMP+3)	; nein, fuehrendes Mantissenbit
605	RL	(IX+TEMP+4)	; rausschieben
606	RL	(IX+TEMP+5)
607MU_DEN:
608	SLA	C		; Vorzeichen in Carry schieben
609	LD	B,L		; Exponent einsetzen
610	LD	C,(IX+TEMP+5)
611	LD	D,(IX+TEMP+4)
612	LD	E,(IX+TEMP+3)
613	RR	B		; und Vorzeichen einschieben
614	RR	C
615	RR	D		; Form: SEEE EEEE EFFF FFFF ... FFFF
616	RR	E
617MU_RES:
618	POP	HL		; lokale Variable deallozieren
619	POP	HL
620	POP	HL
621	POP	IY		; Register restaurieren
622	POP	IX
623	POP	AF
624	POP	HL
625	LD	(F_HL),HL	; Parameter vom Stack deallozieren
626	EX	(SP),HL
627	LD	SP,(F_STACK)
628	PUSH	HL
629	LD	HL,(F_HL)
630	RET			; und return
631MU_ZERO:
632	XOR	A		; Ergebnis ist Null
633	LD	B,A
634	LD	C,A
635	LD	D,A
636	LD	E,A
637	JR	MU_RES
638MU_UNT:
639	LD	A,L		; Exponent in A
640	NEG			; negieren fuer Schleifenzaehler
641	CP	24		; totaler Ueberlauf?
642	JR	NC,MU_ZERO	; ja, dann ist Ergebnis Null
643	LD	B,A		; in B fuer Loop
644MU_SHR:
645	SRL	(IX+TEMP+5)	; Mantisse denormalisieren
646	RR	(IX+TEMP+4)	; bis Exponent Null ist
647	RR	(IX+TEMP+3)
648	DJNZ	MU_SHR
649	LD	L,B		; Exponent in Register L = B = 0
650	JP	MU_DEN		; denormalisiertes Ergebnis erzeugen
651
652; *************************************************
653; * Fliesskomma-Division in Single-Precision
654; * Parameter: Operand 1 und Operand 2 ueber Stack
655; * Ergebnis: in BC-DE: MSB in B, LSB in E
656; *
657
658F_DIV:
659	PUSH	HL		; alten Basepointer retten
660	LD	(F_STACK),SP	; aktuellen Stackpointer abspeichern
661	LD	HL,(F_STACK)	; und in HL laden (= Basepointer)
662	PUSH	AF		; benoetigte Register retten
663	PUSH	IX
664	PUSH	IY
665	LD	BC,OP1
666	ADD	HL,BC
667	PUSH	HL
668	EX	(SP),IX		; IX zeigt auf Operand 1
669				; 2 Dummy-Byte auf Stack fuer lokale
670	LD	BC,OPSIZE	; Variable bleiben stehen
671	ADD	HL,BC
672	PUSH	HL
673	EX	(SP),IY		; IY zeigt auf Operand 2
674	PUSH	HL		; insgesamt 6 Byte fuer lokale Variable
675	ADD	HL,BC		; HL zeigt jetzt hinter die Operanden!
676	LD	(F_STACK),HL
677	LD	A,(IX+3)	; Ergebnisvorzeichen bestimmen
678	XOR	(IY+3)
679	LD	C,A		; Vorzeichen in C Bit 7 merken
680	LD	H,0		; Exponent 1 laden
681	LD	L,(IX+3)
682	LD	A,(IX+2)
683	RES	7,(IX+2)	; implizite Null vorbesetzen
684	SLA	A		; Exponent unterstes Bit in Carry
685	RL	L		; und in E einschieben
686	JR	Z,DV_DN1	; falls Null, dann Op1 denormalisieren
687	SET	7,(IX+2)	; implizite Eins erzeugen
688	DEC	HL		; Bias kompensieren
689DV_DN1:
690	LD	D,0		; Exponent 2 in DE aufbauen
691	LD	E,(IY+3)
692	LD	A,(IY+2)
693	LD	(IY+3),A	; Mantisse um 8 Bit verschieben
694	RES	7,(IY+3)	; implizite Null vorbesetzen
695	SLA	A
696	RL	E
697	JR	Z,DV_DN2	; gleich Null, dann Op2 denormalisieren
698	SET	7,(IY+3)	; implizite Eins erzeugen
699	DEC	DE		; Bias kompensieren
700DV_DN2:
701	LD	A,(IY+1)	; jetzt restliche Bytes verschieben
702	LD	(IY+2),A
703	LD	A,(IY+0)
704	LD	(IY+1),A
705	XOR	A		; A = 0
706	LD	(IY+0),A	; Form: FFFF ... FFFF 0000 0000
707	SRL	(IY+3)
708	RR	(IY+2)
709	RR	(IY+1)
710	RR	(IY+0)		; Form: 0FFF ... FFFF F000 0000
711	JR	NZ,DV_NZ1	; Mantisse 2 auf Null pruefen
712	CP	(IY+1)
713	JR	NZ,DV_NZ1
714	CP	(IY+2)
715	JR	NZ,DV_NZ1
716	CP	(IY+3)
717	JR	NZ,DV_NZ1
718	JP	MU_OVR		; Bei Division durch Null: unendlich
719DV_NZ1:
720	XOR	A		; Carry-Flag loeschen
721	SBC	HL,DE		; Exponenten subtrahieren
722	LD	DE,BIAS		; Bias addieren
723	ADD	HL,DE
724	BIT	7,H		; Exponent positiv?
725	JR	Z,DV_NOZ
726	LD	A,L		; Exponent kleiner als -24?
727	JR	NC,DV_NOZ
728	JP	MU_ZERO		; ja, dann ist das Ergebnis Null
729DV_NOZ:
730	PUSH	BC		; Vorzeichen retten
731	LD	DE,25		; Exponent um 25 erhoehen
732	ADD	HL,DE		; jetzt ist er sicher groesser als Null
733	XOR	A		; A = 0
734	LD	B,(IX+2)	; Divident in Register kopieren
735	LD	C,(IX+1)
736	LD	D,(IX+0)
737	LD	E,A		; die untersten Bits sind Null
738	CP	D		; ist Dividend Null?
739	JR	NZ,DV_NZ2
740	CP	C
741	JR	NZ,DV_NZ2
742	CP	B
743	JR	NZ,DV_NZ2
744	POP	BC		; Stack bereinigen (Vorzeichen laden)
745	JP	MU_ZERO		; und Null als Ergebnis ausgeben
746DV_NZ2:
747	LD	(IX+TEMP+5),A	; Ergebnis vorbesetzen
748	LD	(IX+TEMP+4),A
749	LD	(IX+TEMP+3),A
750	LD	(IX+TEMP+2),A
751DV_NLP:
752	BIT	6,(IY+3)	; ist der Divisor normalisiert
753	JR	NZ,DV_NOR	; ja, -->
754	INC	HL		; nein, Exponent erhoehen
755	SLA	(IY+0)		; Divisor verschieben bis in
756	RL	(IY+1)		; Form 01FF ...
757	RL	(IY+2)
758	RL	(IY+3)
759	JR	DV_NLP
760DV_NOR:
761	SRL	B
762	RR	C
763	RR	D
764	RR	E		; Form: 0FFF ... FFFF F000 0000
765DV_LOP:
766	LD	(IX+3),B	; Dividend zwischenspeichern
767	LD	(IX+2),C	; die Speicherplaetze von Op1
768	LD	(IX+1),D	; stehen zur Verfuegung, da wir OP1
769	LD	(IX+0),E	; in die Register BC-DE kopiert haben
770	LD	A,E		; jetzt Divisor abziehen
771	SUB	(IY+0)
772	LD	E,A
773	LD	A,D
774	SBC	A,(IY+1)
775	LD	D,A
776	LD	A,C
777	SBC	A,(IY+2)
778	LD	C,A
779	LD	A,B
780	SBC	A,(IY+3)
781	LD	B,A
782	JR	NC,DV_ONE	; kein Carry: Divisor passt
783	LD	E,(IX+0)	; zurueckkopieren
784	LD	D,(IX+1)	; Carry bleibt dabei erhalten
785	LD	C,(IX+2)
786	LD	B,(IX+3)
787DV_ONE:
788	CCF			; Carry-Flag umkehren
789	RL	(IX+TEMP+2)	; Ergebnis aufbauen
790	RL	(IX+TEMP+3)
791	RL	(IX+TEMP+4)
792	RL	(IX+TEMP+5)
793	SLA	E		; Dividend verschieben
794	RL	D
795	RL	C
796	RL	B
797	DEC	HL		; Exponent erniedrigen
798	XOR	A		; A = 0
799	CP	L		; Exponent = Null ?
800	JR	NZ,DV_DIV
801	CP	H
802	JR	Z,DV_DEN	; falls Null, dann denormalisiert
803DV_DIV:
804	BIT	0,(IX+TEMP+5)	; fuerende Eins in Ergebnis-Mantisse?
805	JR	Z,DV_LOP	; nein, weiter rechnen
806DV_DEN:
807	LD	B,(IX+TEMP+5)	; hoechstes Bit merken
808	LD	A,(IX+TEMP+4)
809	LD	(IX+TEMP+5),A	; Mantisse in Form
810	LD	A,(IX+TEMP+3)	; FFFF ... FFFF 0000 0000
811	LD	(IX+TEMP+4),A
812	LD	A,(IX+TEMP+2)
813	LD	(IX+TEMP+3),A
814	RR	B		; hoechstes Bit einschieben
815	RR	(IX+TEMP+5)
816	RR	(IX+TEMP+4)
817	RR	(IX+TEMP+3)	; Form: FFFF ... FFFF F000 0000
818	RR	(IX+TEMP+2)
819	POP	BC		; Vorzeichen wieder laden
820	XOR	A		; A = 0
821	CP	(IX+TEMP+5)	; Mantisse ist Null?
822	JR	NZ,DV_NZ3
823	CP	(IX+TEMP+4)
824	JR	NZ,DV_NZ3
825	CP	(IX+TEMP+3)
826	JR	NZ,DV_NZ3
827	CP	(IX+TEMP+2)
828	JP	Z,MU_ZERO	; dann ist Ergebnis auch Null
829DV_NZ3:
830	JP	MU_RND		; sonst weiter wie bei Multiplikation
831
832F_STACK:
833	DEFS	2		; Hilfsspeicher fuer Stackpointer
834F_HL:
835	DEFS	2		; Hilfsspeicher fuer Basepointer HL
836
837	END
838