Prev: 30C0 Up: Map Next: 31AF
30CA: THE 'MULTIPLICATION' OPERATION (offset +04)
The address of this routine is found in the table of addresses. It is called via the calculator literal +04 by the routines at BEEP, CIRCLE, DRAW, CD_PRMS1, S_RND, DEC_TO_FP, INT_TO_FP, e_to_fp, LOG_2_A, series, n_mod_m, exp, ln, get_argt, sin, atn, asn and to_power. It is also called indirectly via fp_calc_2.
This subroutine first tests whether the two numbers to be multiplied are 'small integers'. If they are, it uses INT_FETCH to get them from the stack, HL_HLxDE to multiply them and INT_STORE to return the result to the stack. Any overflow of this 'short multiplication' (i.e. if the result is not itself a 'small integer') causes a jump to multiplication in full five byte floating-point form (see below).
Input
DE Address of the first byte of the second number
HL Address of the first byte of the first number
Output
multiply 30CA LD A,(DE) Test whether the first bytes of both numbers are zero.
30CB OR (HL)
30CC JR NZ,MULT_LONG If not, jump for 'long' multiplication.
30CE PUSH DE Save the pointers to the second number.
30CF PUSH HL And to the first number.
30D0 PUSH DE And to the second number yet again.
30D1 CALL INT_FETCH Fetch sign in C, number in DE.
30D4 EX DE,HL Number to HL now.
30D5 EX (SP),HL Number to stack, second pointer to HL.
30D6 LD B,C Save first sign in B.
30D7 CALL INT_FETCH Fetch second sign in C, number in DE.
30DA LD A,B Form sign of result in A: like signs give plus (+00), unlike give minus (+FF).
30DB XOR C
30DC LD C,A Store sign of result in C.
30DD POP HL Restore the first number to HL.
30DE CALL HL_HLxDE Perform the actual multiplication.
30E1 EX DE,HL Store the result in DE.
30E2 POP HL Restore the pointer to the first number.
30E3 JR C,MULT_OFLW Jump on overflow to 'full' multiplication.
30E5 LD A,D These 5 bytes ensure that 00 FF 00 00 00 is replaced by zero; that they should not be needed if this number were excluded from the system is noted at ADDN_OFLW.
30E6 OR E
30E7 JR NZ,MULT_RSLT
30E9 LD C,A
MULT_RSLT 30EA CALL INT_STORE Now store the result on the stack.
30ED POP DE Restore STKEND to DE.
30EE RET Finished.
MULT_OFLW 30EF POP DE Restore the pointer to the second number.
MULT_LONG 30F0 CALL RE_ST_TWO Re-stack both numbers in full five byte floating-point form.
The full multiplication subroutine prepares the first number for multiplication by calling PREP_M_D, returning if it is zero; otherwise the second number is prepared by again calling PREP_M_D, and if it is zero the subroutine goes to set the result to zero. Next it fetches the two numbers from the calculator stack and multiplies their mantissas in the usual way, rotating the first number (treated as the multiplier) right and adding in the second number (the multiplicand) to the result whenever the multiplier bit is set. The exponents are then added together and checks are made for overflow and for underflow (giving the result zero). Finally, the result is normalised and returned to the calculator stack with the correct sign bit in the second byte.
30F3 XOR A A is set to zero so that the sign of the first number will go into A.
30F4 CALL PREP_M_D Prepare the first number, and return if zero. (Result already zero.)
30F7 RET C
30F8 EXX Exchange the registers.
30F9 PUSH HL Save the next literal address.
30FA EXX Exchange the registers.
30FB PUSH DE Save the pointer to the multiplicand.
30FC EX DE,HL Exchange the pointers.
30FD CALL PREP_M_D Prepare the 2nd number.
3100 EX DE,HL Exchange the pointers again.
3101 JR C,ZERO_RSLT Jump forward if 2nd number is zero.
3103 PUSH HL Save the pointer to the result.
3104 CALL FETCH_TWO Get the two numbers from the stack.
3107 LD A,B M5 to A (see FETCH_TWO).
3108 AND A Prepare for a subtraction.
3109 SBC HL,HL Initialise HL to zero for the result.
310B EXX Exchange the registers.
310C PUSH HL Save M1 and N1 (see FETCH_TWO).
310D SBC HL,HL Also initialise HL' for the result.
310F EXX Exchange the registers.
3110 LD B,$21 B counts thirty three shifts.
3112 JR STRT_MLT Jump forward into the loop.
Now enter the multiplier loop.
MLT_LOOP 3114 JR NC,NO_ADD Jump forward to NO_ADD if no carry, i.e. the multiplier bit was reset.
3116 ADD HL,DE Else, add the multiplicand in D'E'DE (see FETCH_TWO) into the result being built up in H'L'HL.
3117 EXX
3118 ADC HL,DE
311A EXX
NO_ADD 311B EXX Whether multiplicand was added or not, shift result right in H'L'HL; the shift is done by rotating each byte with carry, so that any bit that drops into the carry is picked up by the next byte, and the shift continued into B'C'CA.
311C RR H
311E RR L
3120 EXX
3121 RR H
3123 RR L
STRT_MLT 3125 EXX Shift right the multiplier in B'C'CA (see FETCH_TWO and above). A final bit dropping into the carry will trigger another add of the multiplicand to the result.
3126 RR B
3128 RR C
312A EXX
312B RR C
312D RRA
312E DJNZ MLT_LOOP Loop 33 times to get all the bits.
3130 EX DE,HL Move the result from H'L'HL to D'E'DE.
3131 EXX
3132 EX DE,HL
3133 EXX
Now add the exponents together.
3134 POP BC Restore the exponents - M1 and N1.
3135 POP HL Restore the pointer to the exponent byte.
3136 LD A,B Get the sum of the two exponent bytes in A, and the correct carry.
3137 ADD A,C
3138 JR NZ,MAKE_EXPT If the sum equals zero then clear the carry; else leave it unchanged.
313A AND A
MAKE_EXPT 313B DEC A Prepare to increase the exponent by +80.
313C CCF
This entry point is used by the routine at division.
DIVN_EXPT 313D RLA These few bytes very cleverly make the correct exponent byte. Rotating left then right gets the exponent byte (true exponent plus +80) into A.
313E CCF
313F RRA
3140 JP P,OFLW1_CLR If the sign flag is reset, no report of arithmetic overflow needed.
3143 JR NC,REPORT_6 Report the overflow if carry reset.
3145 AND A Clear the carry now.
OFLW1_CLR 3146 INC A The exponent byte is now complete; but if A is zero a further check for overflow is needed.
3147 JR NZ,OFLW2_CLR
3149 JR C,OFLW2_CLR
314B EXX If there is no carry set and the result is already in normal form (bit 7 of D' set) then there is overflow to report; but if bit 7 of D' is reset, the result in just in range, i.e. just under 2**127.
314C BIT 7,D
314E EXX
314F JR NZ,REPORT_6
OFLW2_CLR 3151 LD (HL),A Store the exponent byte, at last.
3152 EXX Pass the fifth result byte to A for the normalisation sequence, i.e. the overflow from L into B'.
3153 LD A,B
3154 EXX
This entry point is used by the routine at addition.
The remainder of the subroutine deals with normalisation and is common to all the arithmetic routines.
TEST_NORM 3155 JR NC,NORMALISE If no carry then normalise now.
3157 LD A,(HL) Else, deal with underflow (zero result) or near underflow (result 2**-128): return exponent to A, test if A is zero (case 2**-128) and if so produce 2**-128 if number is normal; otherwise produce zero. The exponent must then be set to zero (for zero) or 1 (for 2**-128).
3158 AND A
NEAR_ZERO 3159 LD A,$80
315B JR Z,SKIP_ZERO
ZERO_RSLT 315D XOR A
SKIP_ZERO 315E EXX
315F AND D
3160 CALL ZEROS_4_5
3163 RLCA
3164 LD (HL),A Restore the exponent byte.
3165 JR C,OFLOW_CLR Jump if case 2**-128.
3167 INC HL Otherwise, put zero into second byte of result on the calculator stack.
3168 LD (HL),A
3169 DEC HL
316A JR OFLOW_CLR Jump forward to transfer the result.
The actual normalisation operation.
NORMALISE 316C LD B,$20 Normalise the result by up to 32 shifts left of D'E'DE (with A adjoined) until bit 7 of D' is set. A holds zero after addition so no precision is gained or lost; A holds the fifth byte from B' after multiplication or division; but as only about 32 bits can be correct, no precision is lost. Note that A is rotated circularly, with branch at carry...eventually a random process.
SHIFT_ONE 316E EXX
316F BIT 7,D
3171 EXX
3172 JR NZ,NORML_NOW
3174 RLCA
3175 RL E
3177 RL D
3179 EXX
317A RL E
317C RL D
317E EXX
317F DEC (HL) The exponent is decremented on each shift.
3180 JR Z,NEAR_ZERO If the exponent becomes zero, then numbers from 2**-129 are rounded up to 2**-128.
3182 DJNZ SHIFT_ONE Loop back, up to 32 times.
3184 JR ZERO_RSLT If bit 7 never became 1 then the whole result is to be zero.
Finish the normalisation by considering the 'carry'.
NORML_NOW 3186 RLA After normalisation add back any final carry that went into A. Jump forward if the carry does not ripple right back.
3187 JR NC,OFLOW_CLR
3189 CALL ADD_BACK
318C JR NZ,OFLOW_CLR
318E EXX If it should ripple right back then set mantissa to 0.5 and increment the exponent. This action may lead to arithmetic overflow (final case).
318F LD D,$80
3191 EXX
3192 INC (HL)
3193 JR Z,REPORT_6
The final part of the subroutine involves passing the result to the bytes reserved for it on the calculator stack and resetting the pointers.
OFLOW_CLR 3195 PUSH HL Save the result pointer.
3196 INC HL Point to the sign byte in the result.
3197 EXX The result is moved from D'E'DE to BCDE, and then to ACDE.
3198 PUSH DE
3199 EXX
319A POP BC
319B LD A,B
319C RLA The sign bit is retrieved from its temporary store and transferred to its correct position of bit 7 of the first byte of the mantissa.
319D RL (HL)
319F RRA
31A0 LD (HL),A The first byte is stored.
31A1 INC HL Next.
31A2 LD (HL),C The second byte is stored.
31A3 INC HL Next.
31A4 LD (HL),D The third byte is stored.
31A5 INC HL Next.
31A6 LD (HL),E The fourth byte is stored.
31A7 POP HL Restore the pointer to the result.
31A8 POP DE Restore the pointer to second number.
31A9 EXX Exchange the register.
31AA POP HL Restore the next literal address.
31AB EXX Exchange the registers.
31AC RET Finished.
This entry point is used by the routines at DEC_TO_FP, addition and division.
Report 6 - Arithmetic overflow.
REPORT_6 31AD RST $08 Call the error handling routine.
31AE DEFB $05
Prev: 30C0 Up: Map Next: 31AF