Only used to execute the BEEP command; can be called from m/c, but it is simpler to call 03B5 BEEPER direct. This routine is concerned only with calculating, from the pitch P, as defined in the Spectrum Manual, Chapter or Part 19, and the duration t in seconds, the parameters for BEEPER: the number of complete loudspeaker on/loudspeaker off cycles required, f * t, where f is the frequency in hertz, and the length of the timing loop in units of four T states; this takes the form of a delay counter for the sound generation loop. P and t are of course input as part of the BASIC command, and are therefore found on the calculator stack. The routine checks that P and t are within acceptable limits. P as input in BASIC is zero for middle C, plus or minus one for each semitone above or below middle C; P must be between-60d and +69d inclusive, but needn't be a whole number. There are twelve semitones in an octave; doubling the frequency increases the pitch by an octave, so the increase in frequency for one semitone is an increase by a factor 1 + K where (1 + K)**12d = 2, ie K = twelfth root of 2 minus one (badly printed in the notes). Thus, since 261.63d hertz is the frequency of middle C, f = 261.63d * (1 + K)**P. The routine first calculates f in several stages: - separate P into an integer part and a fractional part, i and p - multiply the fractional part p by the constant K as described above; actually a slightly lower value is used, to allow for a slight "overhead" in the timing loops. This gives anadjustment factor to increase the frequency allowing for the fraction-of-a-semitone increase in pitch required - reject i if it is below -60d or above 69d - reduce i by twelve till it is less than twelve, keepingcount; the count indicates the octave within which the pitch falls, the reduced i indicates the nearest semitone below the pitch P = i + p - index with the reduced i into a table which gives the correct pitch for each semitone of the middle C octave, as a list of twelve constants - increment this semitone frequency by 1 + pK, the fractional adjustment - double the frequency once for each octave recorded in the count. The calculation is organised in such a way as to producecorrect results for either negative or positive i. Next it checks t; eleven-second beeps aren't allowed. Now f * t is calculated and stacked; this is the first of the two parameters for BEEP. Now the counter for the timing loop is calculated. Each delay is four T states, and the delay is used twicein each cycle; one second on the Spectrum is 3,500,000d T states; and the operation of the loops takes a total of 241d T states: so the value of the counter should be (3,500,000d/f - 241d)/8 = 437,500d/f - 30.125d This is the second parameter for BEEP; rejected if it comes out too big, indicating that P was more than +69d. [By my calculations this parameter is slightly incorrect; see under 0385 BEEPER below.] A last check is made of f * t; if it is zero no beep is made, but no error reported, allowing for BEEP x,y commands where y may be zero. Input parameters: The last number on the calculator stackis read as P, the next as t. They must be on the calculator stack even for direct calls from m/c. Action: use the calculator to split P into a fractional and integer part - get pK + 1; the fraction-of-a-semitone adjustment for the eventual frequency, see above - get the exponent of the integer part i; now in mem-0 - if it isn't zero report "Integer out of range"; i mustbe in small integer form - get its sign byte; 00 for positive, FF for negative - get its lo byte and shift its hi bit into carry; therewill be carry if the byte is more than 7Fh/127d - but if the sign is negative this means carry for -80h/128d or more - now SBC A,A; giving zero if there was no carry, FF if there was - if the result doesn't match the sign byte report "Integer out of range"; a positive byte more than 7F or a negative one less than -80h - get the hi byte - if this doesn't match the sign byte report "Integer out of range"; the hi byte should be 00 for positive, FF for negative too - add 3Ch/60d to the lo byte - if the result is "positive" jump on to BE i OK; "positive" means the hi bit is zero, so the jump is only made for positive i of 43h/67d or less - (i is negative, or positive > 43h/67d) if the additiondidn't produce a change in parity report "Integer out of range";this rejects negative values of i from -80h/128d to -3Bh/59d, ieany negative value is now correct, but incorrect positive values46h/70d to 7Fh/127d have still passed - they will be rejected when FIND INT2 is called later to get 437,500d/f - 30.125 as a small integer. _0425_BE_i_OK: make an octave counter FAh/minus 6 _0427_BE_OCTAVE (the entry value for the pitch is now theresult of the last addition, the integer part i of the pitch plus 3Ch/60d, and ranges from one to BBh/187d. Values from one to 3Bh/59d represent negative i, below middle C; 3Ch/60d is middle C itself, zero; values from 3Dh/61d upwards represent positive values, some of which are incorrect): increment the counter - take 0Ch/12d from the pitch value - if it is still positive or zero loop back to BE OCTAVE - add back 0C/12h; now i is a semitone value within the octave, ie between one and twelve, the octave count is between minus six and plus eleven - call 3406 LOC MEM with the semitone value to index into the table at 046E - call 33B4 STACK NUM to put the FP number from the table on the calculator stack; the frequency of the corresponding semitone in the octave of middle C, call it C - use the calculator to multiply C by the fractional pitch adjustment still on the stack - add the octave count to the exponent of C; this doubles the frequency once for each count of the octaves - use the calculator and call 1E94 FIND INT1 to get INT t into a register; t is the length of the BEEP in seconds as originally input from BASIC - if INT t exceeds ten seconds report "Integer out of range" - use the calculator to figure f * t; the first BEEP parameter - figure 437,500d/f - 30.125; the second BEEP parameter - call 1E99 FIND INT2 to put the 2nd parameter in a 2- byte register; if it won't go, because the pitch P was too high,this reports "Integer out of range" - call 1E99 FIND INT2 to get the first BEEP parameter - move the parameters into the registers where BEEPER expects to find them - check the first BEEP parameter for zero - if it was zero return without going through BEEPER - decrement it; because BEEPER counts from zero. Exit: into 03B5 BEEPER, but RET if the BEEP is zero. Output parameters: DE holds the number of cycles - HL holds the length of the timing loop in T states/4 - P and t have been removed from the calculator stack - mem-0 is corrupted. Rems: 03B5 BEEPER controlled by this command routine 03D6 BE H&L LP timing loop controlled by parameter 03F6 BE END completed 33B4 STACK NUM called by BEEPER subroutine 03B5 Activates the loudspeaker for a given number P of pulseseach of given duration D, half sound and half silence; D is in units of four T states, see timing. In the execution of the BEEP command from BASIC, P and Dare calculated by the subroutine BEEP from the BASIC input of pitch and total duration, but the subroutine is also called direct from 0F38 ED LOOP to make the keyboard "click", and from 107F ED ERROR and 1167 ED FULL to make a "rasp", with preset values of P and D. It can similarly be called from m/c programs, with P andD calculated in the program or outside it, or determined by goodold trial and error! The interrupt is disabled during BEEPER, since the irregular length of the interrupt routines would otherwise disturb the pitch. For simplicity, decimal numbers are used unless otherwise indicated throughout the rest of this description: The hi byte and the lo byte of the duration D are used as separate delay counters for the delay loop BE H&L LP. The hi byte H is used as it stands but the lo byte is divided by 4, giving L = INT (lo byte/4) and a remainder byte r; the value used for r is 3 minus the remainder on dividing the lo byte by four, so that D = 256 * H + 4 * L + 3 - r. The delay loop BE H&L LP is in two parts. The first parttakes 16 T states each time its counter X, say, doesn't reach zero, and 11 when it does; a total of 16 * (X - 1) + 11 = 16 * X- 5 T states. The second part adds 21 T states to the total delay of the first; resets the counter for the first to 63; and loops back with a second counter Y, say, to repeat. Thus the first turn through the whole loop takes 16 * X - 5 + 21 = 16 * (X + 1) T states, and the other Y - 1 turns take 16 * (63 + 1) =16 * 64 each; so the total delay of the loop is 16 * (X + 1) + 16 * 64 * (Y - 1) T states. When the speaker is turned off, X and Y are given the values L + 1 and H + 1, so the delay works out at 16 * L + 32 + 16 * 64 * H = [256 * H + 4 * L + 3] * 4 + 20 T states. The expression in [] is equal to the duration parameter D, plus r. When the speaker is on, L is incremented, adding 16 T states; so the delay is 4 * (D + r) + 20 for speaker off and 4 *(D + r) + 36 for speaker on. For each cycle of the on/off loop after the first, starting when the speaker is turned on by OUT (FE),A: - reset the hi counter, jump on to BE AGAIN where the lo counter is incremented, and loop; this takes 44 T states - delay by 3 - r NOPs, ie 4(3 - r) T states, plus 8 T states to increment the counters, 4 * (D + r) + 36 in the delay loop, 7 to flop the OUT byte, and 11 for the OUT itself: total from OUT "on" to OUT "off", 44 + 4 * (3 - r) + 8 + 4 * (D + r) + 36 + 7 + 11 = 4 * D + 118 - now reset the hi and lo counters, check and decrement the pulse counter, and loop; 60 T states this time - again delay by 3 - r NOPs, 8 T states to increment the counters, only 4 * (D - r) + 20 in the delay loop this time, 7 to flop the OUT byte, and 11 for the OUT itself: total from OUT "off" to OUT "on", 60 + 4 * (3 - r) + 8 + 4 * (D + r) + 20 + 7 + 11 = 4 * D + 118 again. Thus the whole loop takes 8 * D + 236 T states: however D is calculated on the assumption it takes 8 * D + 241. Either my calculations are incorrect or the Spectrum BEEP is slightly out of tune! The OUT to port FE on each half loop is a code with the present border colour in the three lo bits so that the border colour isn't changed by the output, and either one in bit 4 to turn the loudspeaker on, or zero to turn it off. See "ports". Input parameters: DE holds the number of pulses P - HL holds the duration D of each pulse. Action: disable the interrupt - halve the lo byte of the duration twice; making L = lobyte/4, so the loop counter is now 256 * H + L - reverse the original value of L; ie subtract it from 100h, 100h - L - AND this with 3; this gives the remainder on dividing 100h - L by 4, so it neatly finds r, 3 minus the remainder - add r to the address of BE IX+3 in IX; thus adding r NOPs to the loop starting at BE IX+0 - get the present border colour from 5C48 BORDCR - AND it with 00111000b/38h; isolating the PAPER colour - shift it 3 times right; making the PAPER number - AND it with 00001000b/08; this is now the OUT byte, with the BORDER colour, bit 3 set for "MIC off", and bit 4 zero for "speaker off" _03D1_BE_IX+3, etc: delay by up to 3 NOPS. _03D4_BE_IX+0 (on the first entry, the delay counter holds the small number 3 - r, and the loudspeaker is off): increment each byte of the delay counter _03D6_BE_H&L_LP: decrement the lo counter - if it hasn't gone to zero loop back to BE H&L LP - put 63d in the lo counter - decrement the hi counter - if it hasn't gone to zero loop back to BE H&L LP - XOR the OUT byte with 00010000b/10h to flop the speaker on/off bit 4; on the first loop it is now "on" - OUTput it to port FE - restore the hi byte of the delay counter to its original value H - if bit 4 of the OUT byte is set jump on to BE AGAIN; the speaker is on - (speaker off) if the pulse counter has reached zero, jump on to BE END - (more pulses to come) restore the lo byte of the delaycounter to its original value L - decrement the pulse loop counter - jump back to the start of the delay loop IX; one of BEIX+3 to BE IX+0 depending on the remainder byte r. _03F2_BE_AGAIN (speaker on): restore the lo byte of the delay counter to its original value L - increment L once; to compensate for the absence of thepulse counter check - jump back to IX. _03F6_BE_END (the pulse counter has gone to zero) re- enable the interrupt and finish. Exit: RET. Output parameters: none - interrupt is re-enabled. Called from: 0F38 ED LOOP 107F ED ERROR 1167 ED FULL Exit from: 0427 BE OCTAVE (03F8 BEEP) BE H&L LP 03D6 (03B5 BEEPER) Jumps from: auto (twice) BE i OK 0425 (03F8 BEEP) Jumps from: 03F8 BEEP BE IX+0 03D4, BE IX+1 03D3, BE IX+2 03D2, BE IX+3 03D1 (03B5BEEPER) Jumps from: 03D6 BE H&L LP (a jump to IX calculated in 03B5 BEEPER) BE OCTAVE 0427 (03F8 BEEP) Jumps from: auto BIN key (C4) see also commands, functions and operators, KEYBOARD SCANNING, 022C extended mode table (b) The B key in E mode without shift produces the function BIN; not strictly a function, it must be followed by a string ofzeros and ones which are to be read as a binary number. On execution, 24FB SCANNING indexes into the scanning function table at 2596 to find the executive routine 268D S BIN. This doesn't actually convert the binary number to 5-byte FP form: itis done by 2C9B DEC TO FP, study of which will show that (a) the binary form may not include a fractional point (b) it can have no more than 16 binary digits - up to FFFFh/65535d. These limitations aren't mentioned in the handbooks! binary format of numbers see BIN key binary operation see also "+" (2B) after end of alphabet This term is used in the notes to refer to operations which have two arguments placed one before and one after the operator, as opposed to_unary_operations which have only one placed after the operator. Addition is a binary operation; "unary minus" as in -12 is a unary operation. Nothing to do withoperating with binary numbers!