one negative, no shift: 00 + FF + carry = 00 and carry both negative, must shift: FF + FF + no carry = FE and carry both negative, no shift: FF + FF + carry = FF and carry. The resulting "indicator" will be 01 with no carry or FEwith carry if a shift is required, 00 or FF with or without carry if not. Shift the indicator right with carry into bit 7; now - "must shift" is 00 or FF - "needn't shift" is 00, 7F, 80 or FF. XOR this result with the original indicator, so that - "must shift" becomes 01 in both cases; odd - "needn't shift" becomes 00 or 80 ; even The hi bit of the indicator also shows the sign of the result: zero if both X and Y were positive, one if they were both negative. And if only one of X and Y was negative, the hi bit of the indicator is set, ie negative, if there was_no carry and zero if there was carry; and this also correctly indicates the sign of the result. The carry is the result of a five-byte addition of numbers which are positive if the hi bit of their hibyte is zero, negative if not; the rules are just the same as for one-byte additions. Adding F0h/minus 10h to 16h gives 06h with carry; the result is positive. Adding F0h/minus 10h to 06h gives F6h/minus 0Ah without carry; the result is negative. Input parameters: HL' holds the "return address" of the calculator subroutine, containing the next literal address - the numbers X and Y are on the calculator stack; one or both may be in small integer format - HL points to the start of the first and DE to the start of the second, the last on the stack; in the notes, X is called the "augend" and Y the "addend". See under addend for these tedious terms. Action: call 3293 RE ST TWO to put both numbers in full FP format - stack the "return address" and both pointers; SHIFT LEN below is going to take all the registers the law allows - call 2F9B PREP ADD for X; it gets the exponent, sets the sign byte for a true mantissa, puts zero in byte 1 for a positive, FF for a negative number, and subtracts the mantissa from zero if it is a negative number - save the exponent - call PREP ADD again for Y - save its exponent; this completes step 1, and gets ready to deal with negative numbers as well - if X has a smaller exponent than Y, or they are the same, jump on to SHIFT LEN - (X > Y) exchange the exponents and pointers; now HL ispointing to the number with the smaller exponent, and from now on the addend Y will mean the smaller of what were X and Y, the augend X will mean the larger. _3055_SHIFT_LEN (step 2): subtract the exponents; the difference is R, the number of right shifts to make in Y - call 2FBA FETCH TWO, which puts the mantissa of the larger X in H'B'C'CB and the mantissa of the smaller Y in L'D'E'DE; H' and L' are the sign markers, FF for negative and zero for positive, not as usual the exponent bytes. In effect these are five-byte mantissas - call 2FDD SHIFT FP to execute step 2, shifting L'D'E'DE to the right by R binary digits; rounding up as necessary for the bits which fall off to the right and feeding in bits on the left to match the ones there already - so that inthe leading byte FF stays as FF and 00 as 00 - add the two pairs of lo bytes, and the two pairs of hibytes with any carry from the addition of the lo bytes; step 3 - perform step 4; add the exponents with the carry from the mantissa addition - rotate the indicator right with carry into the hi bit - XOR the result with the indicator - if the result is even jump on to TEST NEG; no shift - (must shift) call 2FDD SHIFT FP with a count of one tomake a single right shift - increment the exponent to compensate for the shift - if the exponent goes to zero jump on to ADD REP 6; arithmetic overflow. _307C_TEST_NEG: AND the indicator with 10000000b/80h - put the result in the sign byte position of the resulton the calculator stack; it shows the sign of X + Y, 80h for negative and zero for positive - if it was zero jump on to GO NC MLT; positive result - (negative result) negate the 4-byte result mantissa byte by byte; from the right, with carry from each to the next - if it doesn't produce final carry jump on to END COMPL - [still a negative result; final carry means the resultmantissa was FF FF FF FF, now 00 00 00 00. The result is an exact power of 2. Negative numbers -2**(X - 1) which are powers of 2 are expressed in FP format as 8X 80 00 00 00] rotate the carry into the hi bit of the second result byte, making it 80 - increment the exponent. _309F_ADD_REP_6: if the exponent adds up to zero, report "Number too big". _30A3_END_COMPL: put the first byte in place; now the result mantissa is in D'E'DE, but it may require normalizing. _30A5_GO_NC_MLT: zero the A register and exit. Exit: into 3155 TEST NORM; it normalizes the result mantissa, ie puts it into FP format (step 5), and stacks it. Output parameters (as required for TEST NORM): - the four true mantissa bytes are in DED'E' - A is zero - HL points to the exponent byte of the result, which holds the normalized exponent byte - the second position of the result holds 00 or 80h for a positive or negative result - the carry flag shows NC for addition. Exit from: 3014 addition 303C ADDN OFLW (3014 addition) full FP format numbers see CALCULATE full multiplication see 30CA multiply functions see commands, functions and operators, 1F60 DEF FN FUNCTION SKIPOVER SUBROUTINE see 28AB FN SKPOVER ----- GEN ENT 1 335E (0028 FP CALC) Entry point to FP CALC used by 3449 series-06, for no clear reason (see under FP CALC). GEN ENT 2 3362 (0028 FP CALC) Entry point to FP CALC used by 3449 series-06 for looping calls, to avoid resetting 5C67 BREG. Called from: 3453 G LOOP get-argt subroutine 3783 Called from 0028 FP CALC with literal 39; not otherwise called from ROM. Finds a_reduced_argument corresponding to X for the calculation of COS X or SIN X. The argument X of a COS or SIN function can have any value at all, which represents an angle. Those who have difficulty visualizing the meaning of SINand COS may find it helpful to think in terms of a clock face, without hands. The angles are represented by the numbers on the clock, but the convention in trigonometry is to measure them anti-clockwise from the "three-o'clock" position. Negative angles are measured clockwise from the same start position. SIN X is a measure of the height of the clock number above the middle of the clock, COS X is a measure of the distance of the clock number to the right of the middle. Nine o'clock to three o'clock have positive SIN, three to nine o'clock have negative SIN: twelve to six have positive COS, six to twelve negative COS. For the moment, consider the angle as measured in degrees. An angle of say 500d degrees represents a complete circle, 360 degrees, plus 140d degrees: going right round the circle leaves SIN and COS as they were, ie SIN (X + 360d degrees) = SIN X, and the same for COS. Analogously, 1400 hours in the twenty- four hour clock is the same as 2 p.m. in the twelve-hour clock, and has the same SIN and COS So for all calculations of SIN or COS, X may be reduced any number of times by 360d degrees without affecting the result: eg SIN 1000d degrees = SIN 280d degrees. In Spectrum calculations, angles are measured not in degrees but in radians: radians are a measure of angles, like degrees or the numbers on the clock face, but 2pi radians = 360 degrees; so a right angle is pi/2 radians, 45 degrees is pi/4 radians, etc. For trigonometry, calculus etc, radians are a muchmore convenient unit than degrees, but for practical purposes they are rather a nuisance. If X is more than a complete circle 2pi, both its SIN and its COS are the same as those of X - 2pi; SIN and COS are called "cyclic" functions for this reason. There are further equivalences within the circle itself, which make it possible toreduce the argument further than by merely subtracting complete circles; try visualizing them in terms of the clock face: SIN (-X) = -SIN X COS (-X) = COS X SIN (pi - X) = SIN X COS (pi - X) = -COS X SIN (X - pi) = -SIN X COS (X - pi) = -COS X SIN (pi + X) = -SIN X COS (pi + X) = -COS X The get-argt subroutine simplifies the calculation of SIN and COS by finding a reduced argument V, between a positive and a negative right angle, which has the same SIN as X, and thesame COS except for a possible change of sign. "Between a positive and a negative right angle" means between -pi/2 and +pi/2 radians, or between twelve and six o'clock. It finds this value through a series of steps: 1. Divide X by 2pi and subtract the_nearest_integer_above _or_below; this is the first reduced argument Y. Y represents the same angle as X, but with a change of units: instead of radians, the unit is now the circle, 2pi radians, so an angle 4 represents four complete circles, 0.25d represents a right angle, etc. The usual procedure for finding the nearest integer is followed, ie add a half and then discard the fractional part; soY is a fraction of a circle, less than a half, positive if X is between nine o'clock and three o'clock, otherwise negative. Y will always be between minus a half circle and plus a half; ie it ranges anticlockwise once over the whole circle from nine o'clock round to nine o'clock again. The sign of SIN Y circles will be the same as that of SIN X radians. Subtracting an integer makes no further change in SIN or COS. 2. Multiply Y by 4: the result is the second reduced argument Z = 4*Y. Multiplying by 4 changes the unit of angle again: Z is in units of a 1/4 circle, ie a right angle, pi/2 radians. Z ranges from -2 to +2 right angles, still once round the circle anticlockwise from nine o'clock to nine o'clock. Now make a sign flag for COS by subtracting one from ABSZ; because the units are now right angles, one represents a right angle. If ABS Z is more than a right angle, the angle is in the left half of the circle, six to twelve o'clock; ABS Z - 1is positive, signalling "COS negative". Otherwise ABS Z - 1 is negative, signalling "COS positive". 3. The third and final reduced argument V is in the same units as Z, right angles, but its range is reduced to minus one to plus one right angles, the half circle from twelve to six o'clock. There is already a flag for the sign of COS, so it is only necessary to ensure that SIN V right angles has the same sign as SIN Z right angles, which already has the same sign as SIN X radians. There are three cases: 3.1. ABS Z is less than one: Z is OK, so put V = Z 3.2. ABS Z is more than one, Z is positive: put V = 2 - Z. Now V has been put in range, and since 2 means two right angles, ie pi, as seen above SIN (pi - X) = SIN X; in this case,if Z was "ten o'clock" V is put at "two o'clock" 3.3. ABS Z is more than one, Z is negative: put V = ABS Z - 2. Again this puts V in range, and since SIN (x - pi) = -SINX the sign has been corrected; if Z was "eight o'clock", ABS Z is "ten o'clock" and V is "four o'clock". Input parameters: none - X is the last value on the calculator stack; it must be the last value even for direct calls. Action (step 1): use the calculator to divide X by 2pi - find the nearest integer above or below; INT (X/2pi + 0.5d) - subtract it from X/2pi; the result is Y - calculate Z = 4*Y; step 2 - keep Z and take one from ABS Z [Z in the notes is usedfor what I call ABS Z - 1] - keep ABS Z - 1 and get its sign; one for ABS Z > 1 andzero for ABS Z < 1 - store it in mem-0; this is the COS sign flag - if it is one jump on to ZPLUS; more than a right angle - (case 3.1, Z positive and less than a right angle) return with V = Z = 4*Y on the stack. _37A1_ZPLUS (more than a right angle): get ABS Z - 2 - check the sign of Z - if Z is negative jump on to YNEG; negative angle - (case 3.2, Z positive and more than a right angle) negate ABS Z - 2 and return; with V = 2 - ABS Z = 2 - Z = 2 - 4*Y on the stack. _37A8_YNEG (case 3.3, Z negative and more than a right angle): return with V = ABS Z - 2 = -4*Y - 2 on the stack. Exit: RET. Output parameters: V has replaced X on the calculator stack - the flag in mem-0 is one if the COS is negative. Called from: 37AA cos 37B5 sin GET CHAR subroutine 0018 Just like 0020 NEXT CHAR, except that 5C5D CH ADD isn't incremented at the start; it reads the_present_character, the next printable character from the BASIC program or editing areas; see character codes. 20h space is_not a printable character. Input parameters: none - the BASIC pointer 5C5D CH ADD is on the start address. Action: get the character from the pointer in A. _001C_TEST_CHAR: call 007D SKIP OVER, which moves 5C5D CHADD forward over the parameters of control codes and sets the C flag for other unprintable codes - if the flag shows NC, return; ie if any printable character or newline is found. Exit: RET, from 001C TEST CHAR - or into 0020 NEXT CHAR, which moves 5C5D CH ADD forward and loops back to TEST CHAR. Output parameters: A holds the character code - HL holds the address in 5C5D CH ADD. Called from: 0652 SA DATA 06E1 SA CODE 1 12CF MAIN 3 (label omitted, misprint) 17FB LIST 1 (twice) 1B29 STMT L 1 1B55 GET PARAM 1B6F SEPARATOR 1BF4 STMT NEXT 1CBE CLASS 09 1DED READ 1E0A READ 1 1E1E READ 2 1FDF PRINT 2 1FFC PR ITEM 1 204E PR POSN 1 20C1 IN ITEM 1 21B9 IN ASSIGN (twice) 21E2 CO TEMP 2 2320 CIRCLE 2382 DRAW 24FB SCANNING 2522 S 2 COORD 25B3 S QUOTE 268D S DECIMAL 26B5 S STK DEC 2712 S CONT 2 27D9 SF ARGMTS 2852 SF ARG VL 2885 SF R BR 2 28B2 LOOK VARS 2934 V SYNTAX 293F V FOUND 2 29A1 SV SIMPLE$ 29AE SV ARRAYS 29C3 SV COMMA 29E0 SV CH ADD 29EA SV LOOP