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