#include iolib.h #include float.h #asm ; ; name... ; float ; ; purpose... ; floating point routines for C programs ; ; note... ; This code uses some of the Z-80's UNDOCUMENTED ; INSTRUCTIONS. The instructions work because the IX and ; IY escape bytes are actually a bit more general than ; Zilog advertises. Note that in the instructions ; E1 POP HL ; DD E1 POP IX ; FD E1 POP IY ; (which Zilog documents), the opcodes for the IX and IY ; registers are the same as for the HL register, but are ; preceded by the escape bytes DD and FD. Similarly, ; 6A LD L,D ; DD 6A LD XL,D undocumented ; FD 6A LD YL,D undocumented ; where the new instructions deal with the lower half ; of the IX and IY registers, respectively. All of the ; single-byte transfers, and single byte operations such ; as ADD and INC, involving the H or L registers, can be ; preceded by escape bytes in this fashion. ; ; history... ; The floating point arithmetic routines were ; written by Neil Colvin and were included in ; Xitan Disk Basic. He has consented to release ; them to the public domain for individual use ; but not for sale. Disassembly, comments, ifix(), ; float(), and the interface to c programs were by ; James R. Van Zandt. ; ; Aug 84 moved atof() and putf() to ; PRINT library. ; 17 Jun 84 including float.h & iolib.h, so ; output can be separately assembled. ; Calling err() for errors, so walkback ; trace can work. ; 26 Jun 83 Renamed: abs->fabs, mod->fmod. ; 25 Jun 83 Added 'amax', 'amin', ; 'putf', 'atof'. ; 6 Nov 82 Removed 'sqrt' declaration ; 20 Oct 82 Added 'odd', used it ; in 'ceil' ; 9 Oct 82 Added 'dswap'. ; 6 Oct 82 Removed my 'floor', renamed ; existing 'int' to 'floor'. ; 5 Oct 82 "int" -> "qint" ; declaring int and mod. ; 29 Sept 82 Added 'floor' code. ; 23 Sept 82 Removed transcendental ; routines. ; 26 Aug 82 Added comparison routines. ; 18 Aug 82 Added 'ifix' ; 15 Aug 82 Added 'float'. ; 8 Aug 82 AS.COM format source code. ; 31 Jul 82 Translated to Zilog mnemonics. ; 30 Jul 82 Disassembled from Xitan Disk ; Basic. ; ; double float(), /* integer to floating point conversion */ ; mod(), /* mod(x,y) */ ; fabs(), /* absolute value */ ; floor(), /* largest integer not greater than */ ; ceil(), /* smallest integer not less than */ ; rand(); /* random number in range 0...1 */ ; int ifix(); /* floating point to integer ; (takes floor first) */ ; EXTRA: DEFS 6 FA: DEFS 6 ;floating point accumulator FASIGN: DEFS 1 ;msb indicates sign of FA ; 0 => negative, 1 => positive ; L0F2E: DEFB 0 SEED: DEFB 80H,80H,0,0,0,0 ;seed for random number generator ; DIVZERO: CALL GRIPE DEFB 'can''t /0',0 ILLFCT: CALL GRIPE DEFB 'Illegal function',0 OFLOW: CALL GRIPE DEFB 'Arithmetic overflow',0 GRIPE: CALL QERR ;top word on stack points to message JP 0 ;error was fatal ; ; push the floating point accumulator ; (preserving return address) ; DPUSH: POP DE LD HL,(FA+4) PUSH HL LD HL,(FA+2) PUSH HL LD HL,(FA) PUSH HL EX DE,HL JP (HL) ; ; push floating point accumulator ; (preserve return address and next stacked word) ; DPUSH2: POP DE ;save return address POP BC ;save next word LD HL,(FA+4) PUSH HL LD HL,(FA+2) PUSH HL LD HL,(FA) PUSH HL EX DE,HL PUSH BC ;restore next word JP (HL) ;return ; ; convert the integer in hl to ; a floating point number in FA ; QFLOAT: LD A,H ;fetch MSB CPL ;reverse sign bit LD (FASIGN),A ;save sign (msb) RLA ;move sign into cy JR C,FL4 ;c => nonnegative number EX DE,HL SBC HL,HL ;clear hl SBC HL,DE ;get positive number into hl FL4: LD A,L DEFB 0DDH LD H,A ;move LSB to hx LD C,H ;move MSB to c LD DE,0 ;clear rest of registers LD B,D DEFB 0DDH LD L,D ;clear lx LD A,16+128 LD (FA+5),A ;preset exponent JP NORM ;go normalize c ix de b ; ; convert the floating point number in FA ; to an integer in hl (rounds toward negative numbers) ; QIFIX: CALL QFLOOR ;take floor first LD HL,0 ;initialize the result LD A,(FA+5) ;get the exponent LD B,A ; and save it OR A RET Z ;z => number was zero LD HL,(FA+3) ;get most significant bits LD C,H ;save sign bit (msb) LD A,B ;get exponent again CP 80H+16 JP M,IFIX5 ;m => fabs(fa) < 32768 JR NZ,OFLOW ;nz => fabs(fa) > 32768 ; (overflow) LD A,H CP 80H JR NZ,OFLOW ;nz => fa isn't -32768 LD A,L OR A JR NZ,OFLOW ;nz => overflow RET ;return -32768. ; IFIX5: SET 7,H ;restore hidden bit IFIX6: SRL H ;shift right (0 fill) RR L ;shift right (cy fill) INC A CP 16+80H JR NZ,IFIX6 ;nz => haven't shifted enough RL C RET NC ;nc => positive number EX DE,HL LD HL,1 ;compensate for cy bit SBC HL,DE ;negate result RET ; ADDHALF: LD HL,HALF HLADD: CALL LDBCHL JR FADD HALF: DEFB 0,0,0,0,0,80H ;0.5 ; L247E: CALL PUSHFA CALL L27EC POP BC POP IX POP DE JR FADD ; HLSUB: CALL LDBCHL JR FSUB ; ; fmod(z,x) = z-x*floor(z/x) ; if x>0 then 0 <= fmod(z,x) < x ; if x<0 then x < fmod(z,x) <= 0 ; QFMOD: POP HL ;return addr POP DE ;discard next number POP DE ; (already in FA) POP DE POP DE ;fetch next number POP IX ; (1st operand, or "z") POP BC PUSH DE ;restore stack PUSH DE PUSH DE PUSH DE PUSH DE PUSH DE PUSH HL ;replace return addr PUSH DE ;save another copy of z PUSH IX PUSH BC CALL PUSHFA ;save a copy of 2nd operand ("x") CALL FDIV ;z/x CALL QFLOOR ;floor(z/x) POP BC POP IX POP DE CALL FMUL ;x*floor(z/x) POP BC POP IX POP DE ; to find mod(z,x)=z-x*floor(z/x), fall into... FSUB: CALL MINUSFA JR FADD ; ; subtract the floating point accumulator from the value ; on the stack (under the return address), leave result ; in the floating point accumulator. ; DSUB: CALL MINUSFA ; ; add the value on the stack (under the return address) ; to the floating point accumulator ; DADD: POP HL ;save return address POP DE POP IX POP BC PUSH HL ;replace return address ; ; add bc ix de to floating point accumulator ; FADD: LD A,B OR A RET Z ;z => number to be added is zero LD A,(FA+5) OR A JP Z,LDFABC ;z => accumulator is zero, ; just load number SUB B JR NC,ADD2 ;nc => accumulator has larger number NEG ;reverse accumulator & bc ix de... EXX PUSH IX CALL LDBCFA EXX EX (SP),IX CALL LDFABC EXX POP IX ;...end of reversing ADD2: CP 29H RET NC ;nc => addition makes no change PUSH AF ;save difference of exponents CALL UNPACK ;restore hidden bit & compare signs LD H,A ;save difference in signs POP AF ;recall difference of exponents CALL RSHIFT ;shift c ix de b right by (a) OR H LD HL,FA JP P,ADD4 ;p => opposite signs, must subtract CALL FRADD ;c ix de += FA JR NC,PACK ;nc => adding caused no carry INC HL INC (HL) ;increment exponent JP Z,OFLOW LD L,1 CALL RSH8 ;shift c ix de b right by 1 JR PACK ;round, hide msb, & load into FA ; ADD4: XOR A ;negate b... SUB B LD B,A LD A,(HL) ;c ix de -= FA... SBC A,E LD E,A INC HL LD A,(HL) SBC A,D LD D,A INC HL LD A,(HL) DEFB 0DDH SBC A,L DEFB 0DDH LD L,A INC HL LD A,(HL) DEFB 0DDH SBC A,H DEFB 0DDH LD H,A INC HL LD A,(HL) SBC A,C LD C,A ;...end of subtraction, fall into... ; ; reverse sign if necessary (cy set) and normalize ; (sign reversal necessary because we're using ; sign-magnitude representation rather than ; twos-complement) ; NORMA: CALL C,MINUSBC ; ; normalize the 48 bit number in c ix de b ; current exponent is in FA+5 ; ; result loaded into FA ; NORM: LD L,B LD H,E XOR A NORM2: LD B,A LD A,C OR A JR NZ,NORM12 ;nz => 7 or fewer shifts needed ; shift c ix d hl left by one byte DEFB 0DDH LD C,H DEFB 0DDH LD A,L DEFB 0DDH LD H,A DEFB 0DDH LD L,D XOR A LD D,H LD H,L LD L,A ;...end of shifting ; LD A,B SUB 8 ;adjust exponent CP 0D0H JR NZ,NORM2 ; NORM4: XOR A NORM6: LD (FA+5),A RET ; NORM8: DEC B ; shift c ix d hl left one bit... ADD HL,HL RL D EX AF,AF' ADD IX,IX EX AF,AF' JR NC,NORM10 INC IX NORM10: EX AF,AF' RL C ;...end of shifting ; NORM12: JP P,NORM8 ;p => high order bit still zero LD A,B ; move number to c ix de b LD E,H LD B,L OR A JR Z,PACK ;z => exponent unchanged LD HL,FA+5 ;update exponent ADD A,(HL) LD (HL),A JR NC,NORM4 ;nc => underflow (set to 0) RET Z ;z => underflow (leave as 0) PACK: LD A,B PACK2: LD HL,FA+5 ;round c ix de b to 40 bits OR A CALL M,INCR LD B,(HL) ;load exponent INC HL LD A,(HL) ;recover sign AND 80H ;mask out all but sign XOR C ;add to high LD C,A ; order byte JP LDFABC ;place answer in FA ; INCR: INC E ;increment c ix de RET NZ INC D RET NZ DEFB 0DDH INC L RET NZ DEFB 0DDH INC H RET NZ INC C RET NZ ;z => carry LD C,80H ;set high order bit INC (HL) ; and increment exponent RET NZ JP OFLOW ; ; fraction add: c ix de += (hl) ; FRADD: LD A,(HL) ADD A,E LD E,A INC HL LD A,(HL) ADC A,D LD D,A INC HL LD A,(HL) DEFB 0DDH ADC A,L DEFB 0DDH LD L,A INC HL LD A,(HL) DEFB 0DDH ADC A,H DEFB 0DDH LD H,A INC HL LD A,(HL) ADC A,C LD C,A RET ; ; complement FASIGN and negate the fraction c ix de b ; MINUSBC: LD HL,FASIGN LD A,(HL) CPL LD (HL),A XOR A LD L,A LD H,A SUB B LD B,A LD A,L SBC HL,DE EX DE,HL LD L,A DEFB 0DDH SBC A,L DEFB 0DDH LD L,A LD A,L DEFB 0DDH SBC A,H DEFB 0DDH LD H,A LD A,L SBC A,C LD C,A RET ; ; shift c ix de b right by (a) ; RSHIFT: LD B,0 RSH2: SUB 8 JR C,RSH4 ;c => 7 or fewer shifts remain LD B,E ;shift c ix de b right by 8... LD E,D DEFB 0DDH LD D,L EX AF,AF' DEFB 0DDH LD A,H DEFB 0DDH LD L,A EX AF,AF' DEFB 0DDH LD H,C LD C,0 ;...end of shifting JR RSH2 ; RSH4: ADD A,9 LD L,A RSH6: XOR A DEC L RET Z ;z => requested shift is complete LD A,C RSH8: RRA ;shift c ix de b right by one... LD C,A DEFB 0DDH LD A,H RRA DEFB 0DDH LD H,A DEFB 0DDH LD A,L RRA DEFB 0DDH LD L,A RR D RR E RR B ;...end of shifting JR RSH6 ; ; multiply the floating point accumulator by the value ; on the stack (under the return address), leave result ; in the floating point accumulator. ; DMUL: POP HL ;return addr POP DE ;multiplier... POP IX POP BC PUSH HL ;replace return addr ; ; multiply floating point accumulator by bc ix de ; FMUL: CALL SGN RET Z ; z => accumulator has zero LD L,0 ;"product" flag CALL DIV14 ;find exponent of product LD A,C ;c' h'l' d'e' (multiplicand) = c ix de... PUSH DE EXX LD C,A POP DE PUSH IX POP HL EXX ;...end of multiplicand loading LD BC,0 ; c ix de b (product) = 0... LD D,B LD E,B LD IX,0 LD HL,NORM ; push addr of normalize routine PUSH HL LD HL,MULLOOP ; push addr of top of loop PUSH HL ; (5 iterations wanted, PUSH HL ; once per byte of fraction) PUSH HL PUSH HL LD HL,FA ;point to LSB MULLOOP: LD A,(HL) ;get next byte of multiplier INC HL OR A JR NZ,MUL2 ; z => next 8 bits of multiplier are 0 LD B,E ;shift c ix de b right by 8... LD E,D DEFB 0DDH LD D,L EX AF,AF' DEFB 0DDH LD A,H DEFB 0DDH LD L,A EX AF,AF' DEFB 0DDH LD H,C LD C,A ;...end of shifting RET ;go to top of loop or NORM ; MUL2: PUSH HL ;save multiplier pointer EX DE,HL LD E,8 ;8 iterations (once per bit) MUL4: RRA ;rotate a multiplier bit into cy LD D,A LD A,C JR NC,MUL6 ; nc => no addition needed PUSH HL ; c ix hl (product) += EXX ; c' h'l' d'e' (multiplicand) EX (SP),HL ADD HL,DE EX (SP),HL EX DE,HL PUSH IX EX (SP),HL ADC HL,DE EX (SP),HL POP IX EX DE,HL ADC A,C EXX POP HL ; MUL6: RRA ;shift c ix hl b (product) right by 1... LD C,A DEFB 0DDH LD A,H RRA DEFB 0DDH LD H,A DEFB 0DDH LD A,L RRA DEFB 0DDH LD L,A RR H RR L RR B ;...end of shifting DEC E LD A,D JR NZ,MUL4 ; z => 8 iterations complete EX DE,HL MUL8: POP HL ;recover multiplier pointer RET ;go to top of loop or NORM ; ; divide floating point accumulator by 10 ; DIV10: CALL PUSHFA LD BC,8420H ; 10.0 LD IX,0 LD DE,0 CALL LDFABC DIV1: POP BC POP IX POP DE JR FDIV ; ; divide the value on the stack (under the return ; address) by the floating point accumulator, leave ; result in the floating point accumulator. ; DDIV: POP HL ;save return address POP DE POP IX POP BC PUSH HL ;replace return address ; ; divide bc ix de by FA, leave result in FA ; FDIV: CALL SGN JP Z,DIVZERO ; z => attempting to divide by 0 LD L,0FFH ;"quotient" flag CALL DIV14 ;find quotient exponent PUSH IY INC (HL) INC (HL) DEC HL PUSH HL ; c' h'l' d'e' (divisor) = FA... EXX POP HL LD C,(HL) DEC HL LD D,(HL) DEC HL LD E,(HL) DEC HL LD A,(HL) DEC HL LD L,(HL) LD H,A EX DE,HL EXX LD B,C ; b iy hl (dividend) = c ix de... EX DE,HL PUSH IX POP IY XOR A ; c ix de (quotient) = 0... LD C,A LD D,A LD E,A LD IX,0 LD (EXTRA),A DIV2: PUSH HL ;save b iy hl in case the subtraction PUSH IY ; proves to be unnecessary PUSH BC PUSH HL ; EXTRA b iy hl (dividend) -= LD A,B ; c' h'l' d'e' (divisor)... EXX EX (SP),HL OR A SBC HL,DE EX (SP),HL EX DE,HL PUSH IY EX (SP),HL SBC HL,DE EX (SP),HL POP IY EX DE,HL SBC A,C EXX POP HL LD B,A LD A,(EXTRA) SBC A,0 CCF JR NC,DIV4 ; nc => subtraction caused carry LD (EXTRA),A POP AF ;discard saved value of dividend... POP AF POP AF SCF JR DIV6 DIV4: POP BC ;restore dividend... POP IY POP HL ; DIV6: INC C DEC C RRA JP M,DIV12 RLA ;shift c ix de a (quotient) left by 1... RL E RL D EX AF,AF' ;(these 6 lines are adc ix,ix...) ADD IX,IX EX AF,AF' JR NC,DIV8 INC IX DIV8: EX AF,AF' RL C ;...end of c ix de a shifting ADD HL,HL ;shift EXTRA b iy hl left by 1... EX AF,AF' ADD IY,IY EX AF,AF' JR NC,DIV9 INC IY DIV9: EX AF,AF' RL B LD A,(EXTRA) RLA LD (EXTRA),A ;...end of EXTRA b iy hl shifting LD A,C ;test c ix de... OR D OR E DEFB 0DDH OR H DEFB 0DDH OR L ;...end of c ix de testing JR NZ,DIV2 ;nz => dividend nonzero PUSH HL LD HL,FA+5 DEC (HL) POP HL JR NZ,DIV2 JR OFLOW2 ; DIV12: POP IY JP PACK2 ; ; find exponent for product (L=0) or quotient (L=ff) ; DIV14: LD A,B OR A JR Z,DIV20 LD A,L ;get product/quotient flag LD HL,FA+5 XOR (HL) ;get +-FA exponent ADD A,B ;find and... LD B,A ;...load new exponent RRA XOR B LD A,B JP P,DIV18 ADD A,80H LD (HL),A JP Z,MUL8 CALL UNPACK ;restore hidden bits & compare signs LD (HL),A ;save difference of signs DEC HL ;point to MSB of fraction RET ; DIV17: CALL SGN CPL OR A DEFB 21H ;"ignore next 2 bytes" DIV18: OR A DIV20: POP HL JP P,NORM4 OFLOW2: JP OFLOW ; ; multiply FA by 10 ; MUL10: CALL LDBCFA LD A,B ;multiply bc ix de by 4... OR A RET Z ADD A,2 JR C,OFLOW2 LD B,A ;...end of multiplication CALL FADD ;add to FA, yields FA*5 LD HL,FA+5 INC (HL) ;double again, yielding FA*10 RET NZ JR OFLOW2 ; ; L27DB: LD BC,9980H ; -2**24 LD IX,0 LD DE,0 CALL COMPARE RET Z JP ILLFCT ; L27EC: LD B,88H ; 128. LD DE,0 L27F1: LD HL,FA+5 LD C,A PUSH DE POP IX LD DE,0 LD (HL),B ;store exponent LD B,0 INC HL LD (HL),80H ;store minus sign RLA JP NORMA ; EX DE,HL XOR A LD B,98H JR L27F1 LD B,C L280C: LD D,B LD E,0 LD HL,L0F2E LD (HL),E LD B,90H JR L27F1 LD B,A XOR A JR L280C ; L281B: CALL SGN JP M,ILLFCT LD A,(FA+5) CP 91H JP C,INT2 LD BC,9180H ; -2**16 LD IX,0 LD DE,0 CALL COMPARE LD D,C RET Z L2838: JP ILLFCT CALL L281B LD A,D OR A JR NZ,L2838 LD A,E RET ; ; set z & s flags per FA ; SGN: LD A,(FA+5) OR A RET Z LD A,(FA+4) DEFB 0FEH ;"ignore next byte" SETFLGS: CPL RLA SBC A,A RET NZ INC A RET ; ; Double precision comparisons ; ; each compares top of stack ; (under two return addresses) to FA ; ;TOS >= FA? DGE: CALL DCOMPAR JR Z,YES ;z => equal JR DG ;remaining tests are shared ; ;TOS > FA? DGT: CALL DCOMPAR JR Z,NO ;z => equal DG: JP P,NO ;p => not greater than YES: LD HL,1 ;load "true" RET ; ;TOS <= FA? DLE: CALL DCOMPAR JR Z,YES JR DL ; ;TOS < FA? DLT: CALL DCOMPAR JR Z,NO DL: JP P,YES ;p => less than NO: LD HL,0 ;load "false" RET ; ;TOS == FA? DEQ: CALL DCOMPAR JR Z,YES JR NO ; ;TOS != FA? DNE: CALL DCOMPAR JR NZ,YES JR NO ; ;common routine to perform double precision comparisons DCOMPAR: POP HL ;save 1st return addr POP IY ;save 2nd return addr POP DE ;get number to compare POP IX POP BC PUSH IY ;replace 2nd addr PUSH HL ;replace 1st addr, fall into... ; ; sets flags per FA - (bc ix de) ; COMPARE: LD A,B OR A JR Z,SGN ;bc ix de = 0, so ; sign of FA is result CALL SGN LD A,C JR Z,SETFLGS ;FA = 0, so sign of ; -(bc ix de) is result LD HL,FA+4 XOR (HL) LD A,C JP M,SETFLGS ;operands have opposite ; signs, so result is sign ; of -(bc ix de) CALL CPFRAC RRA ;recover cy bit XOR C ;reverse sign if numbers are negative JR SETFLGS ; CPFRAC: INC HL ;compare bc ix de to (HL) LD A,B CP (HL) RET NZ DEC HL LD A,C CP (HL) RET NZ DEC HL DEFB 0DDH LD A,H CP (HL) RET NZ DEC HL DEFB 0DDH LD A,L CP (HL) RET NZ DEC HL LD A,D CP (HL) RET NZ DEC HL LD A,E SUB (HL) RET NZ POP HL ;return zero to program RET ;that called "COMPARE" ; QFABS: CALL SGN RET P MINUSFA: LD HL,FA+4 LD A,(HL) XOR 80H LD (HL),A RET ; PUSHFA: EX DE,HL L2895: LD HL,(FA) EX (SP),HL PUSH HL LD HL,(FA+2) EX (SP),HL PUSH HL LD HL,(FA+4) EX (SP),HL PUSH HL EX DE,HL RET ; This can be reinstated when the compiler ; understands "extern". Until then, pi ; can't be declared without reserving ; storage again. ; QPI: DEFW 0A222H ;3.1415926535 = pi ; DEFW 0FDAH ; DEFW 8249H ; ; FA = (hl) ; DLOAD: LD DE,FA LD BC,6 LDIR RET ; ; exchange floating point accumulator with ; top of stack (under return address) ; DSWAP: POP HL ;return addr POP DE POP IX POP BC EXX ;protect the values CALL DPUSH ;push FA EXX ;recover the values PUSH HL ;replace return addr, fall into... ; ; FA = bc ix de ; LDFABC: LD (FA),DE LD (FA+2),IX LD (FA+4),BC RET ; ; bc ix de = FA ; LDBCFA: LD DE,(FA) LD IX,(FA+2) LD BC,(FA+4) RET ; ; bc ix de = (hl) ; LDBCHL: LD E,(HL) INC HL LD D,(HL) INC HL LD C,(HL) DEFB 0DDH LD L,C INC HL LD C,(HL) DEFB 0DDH LD H,C INC HL LD C,(HL) INC HL LD B,(HL) INC HL RET ; ; (hl) = FA ; DSTORE: LD DE,FA LD BC,6 EX DE,HL LDIR EX DE,HL RET ; UNPACK: LD HL,FA+4 LD A,(HL) ;get MSB of fraction RLCA ;rotate sign bit into lsb SCF ;set carry RRA ;rotate sign bit into cy, cy into msb LD (HL),A ;restore MSB (with hidden bit restored) CCF ;complement sign bit... RRA INC HL INC HL LD (HL),A ;...and save in msb of FASIGN LD A,C ;similarly, get sign bit of bc ix de... RLCA SCF RRA LD C,A ;...restore hidden bit... RRA XOR (HL) ;...and compare with sign of FA. RET ; INT2: LD B,A ;if a==0, return with bc ix de = 0... LD C,A LD D,A LD E,A DEFB 0DDH LD H,A DEFB 0DDH LD L,A OR A RET Z PUSH HL CALL LDBCFA ;copy FA into bc ix de, CALL UNPACK ; restore hidden bits XOR (HL) LD H,A ;put sign in msb of h JP P,INT4 ;p => positive number DEC DE ;decrement c ix de... LD A,D AND E INC A JR NZ,INT4 DEC IX DEFB 0DDH LD A,H DEFB 0DDH AND L INC A JR NZ,INT4 DEC C ;...end of c ix de decrementing ; INT4: LD A,0A8H ;shift c ix de right so bits to SUB B ; the right of the binary point CALL RSHIFT ; are discarded LD A,H RLA CALL C,INCR ;c => negative, increment c ix de LD B,0 CALL C,MINUSBC ;negate the fraction c ix de POP HL RET ; POP BC POP IX POP DE ; ; divide with integer result ; (truncates toward zero) ; DIVI: CALL FDIV CALL SGN JP P,QFLOOR CALL MINUSFA CALL QFLOOR JP MINUSFA ; ; return -(floor(-x)) QCEIL: CALL ODD ; ; return largest integer not greater than QFLOOR: LD HL,FA+5 LD A,(HL) ;fetch exponent CP 0A8H LD A,(FA) RET NC ;nc => binary point is right of lsb LD A,(HL) CALL INT2 LD (HL),0A8H ;place binary pt at end of fraction LD A,E PUSH AF LD A,C RLA CALL NORMA POP AF RET ; LD HL,FA+5 LD (HL),0A8H INC HL LD (HL),80H LD A,C RLA JP NORMA ; amax(a,b) returns the greater of a and b QAMAX: LD HL,8 ;offset for 1st argument ADD HL,SP CALL LDBCHL ;bcixde := 1st argument CALL COMPARE JP M,LDFABC RET ; ; amin(a,b) QAMIN: LD HL,8 ADD HL,SP CALL LDBCHL CALL COMPARE JP P,LDFABC RET ; ; negate FA, and push address of MINUSFA ; called to evaluate functions f(x) when the argument is ; negative and f() satisfies f(-x)=-f(x) ODD: CALL MINUSFA L29D1: LD HL,MINUSFA EX (SP),HL JP (HL) ; QRAND: CALL SGN LD HL,SEED JP M,DSTORE CALL DLOAD RET Z LD BC,9835H ; 11879545. LD IX,447AH LD DE,0 CALL FMUL LD BC,6828H ; 3.92767775e-8 LD IX,0B146H LD DE,0 CALL FADD CALL LDBCFA LD A,E LD E,C LD C,A LD HL,FASIGN LD (HL),80H DEC HL LD B,(HL) LD (HL),80H CALL NORM LD HL,SEED JP DSTORE #endasm  .