Routines |
Prev: 30C0 | Up: Map | Next: 31AF |
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).
|
||||||||||
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. | ||||||||
REPORT_6 | 31AD | RST $08 | Call the error handling routine. | |||||||
31AE | DEFB $05 |
Prev: 30C0 | Up: Map | Next: 31AF |