| Routines

03B5: THE 'BEEPER' SUBROUTINE

Used by the routines at BEEP, EDITOR, ED_ERROR and ED_COPY. The loudspeaker is activated by having D4 low during an 'OUT' instruction that is using port +FE. When D4 is high in a similar situation the loudspeaker is deactivated. A 'beep' can therefore be produced by regularly changing the level of D4. Consider now the note 'middle C' which has the frequency 261.63 Hz. In order to get this note the loudspeaker will have to be alternately activated and deactivated every 1/523.26th of a second. In the Spectrum the system clock is set to run at 3.5 MHz. and the note of 'middle C' will require that the requisite 'OUT' instruction be executed as close as possible to every 6689 T states. This last value, when reduced slightly for unavoidable overheads, represents the 'length of the timing loop' in this subroutine. This subroutine is entered with the DE register pair holding the value 'f*t', where a note of given frequency 'f' is to have a duration of 't' seconds, and the HL register pair holding a value equal to the number of T states in the 'timing loop' divided by 4, i.e. for the note 'middle C' to be produced for one second DE holds +0105 (INT(261.3*1)) and HL holds +066A (derived from 6689/4-30.125).
Input
DE | Number of passes to make through the sound generation loop
HL | Loop delay parameter
BEEPER | 03B5 | DI | Disable the interrupt for the duration of a 'beep'.
| 03B6 | LD A,L | Save L temporarily.
| 03B7 | SRL L | Each '1' in the L register is to count 4 T states, but take INT (L/4) and count 16 T states instead.
| 03B9 | SRL L
| 03BB | CPL | Go back to the original value in L and find how many were lost by taking 3-(A mod 4).
| 03BC | AND $03
| 03BE | LD C,A
| 03BF | LD B,$00
| 03C1 | LD IX,$03D1 | The base address of the timing loop.
| 03C5 | ADD IX,BC | Alter the length of the timing loop. Use an earlier starting point for each '1' lost by taking INT (L/4).
| 03C7 | LD A,($5C48) | Fetch the present border colour from BORDCR and move it to bits 2, 1 and 0 of the A register.
| 03CA | AND $38
| 03CC | RRCA
| 03CD | RRCA
| 03CE | RRCA
| 03CF | OR $08 | Ensure the MIC output is 'off'.
Now enter the sound generation loop. DE complete passes are made, i.e. a pass for each cycle of the note. The HL register holds the 'length of the timing loop' with 16 T states being used for each '1' in the L register and 1024 T states for each '1' in the H register.
| 03D1 | NOP | Add 4 T states for each earlier entry point that is used.
| 03D2 | NOP
| 03D3 | NOP
| 03D4 | INC B | The values in the B and C registers will come from the H and L registers - see below.
| 03D5 | INC C
BE_H_L_LP | 03D6 | DEC C | The 'timing loop', i.e. BC*4 T states. (But note that at the half-cycle point, C will be equal to L+1.)
| 03D7 | JR NZ,BE_H_L_LP
| 03D9 | LD C,$3F
| 03DB | DEC B
| 03DC | JP NZ,BE_H_L_LP
The loudspeaker is now alternately activated and deactivated.
| 03DF | XOR $10 | Flip bit 4.
| 03E1 | OUT ($FE),A | Perform the 'OUT' operation, leaving the border unchanged.
| 03E3 | LD B,H | Reset the B register.
| 03E4 | LD C,A | Save the A register.
| 03E5 | BIT 4,A | Jump if at the half-cycle point.
| 03E7 | JR NZ,BE_AGAIN
After a full cycle the DE register pair is tested.
| 03E9 | LD A,D | Jump forward if the last complete pass has been made already.
| 03EA | OR E
| 03EB | JR Z,BE_END
| 03ED | LD A,C | Fetch the saved value.
| 03EE | LD C,L | Reset the C register.
| 03EF | DEC DE | Decrease the pass counter.
| 03F0 | JP (IX) | Jump back to the required starting location of the loop.
The parameters for the second half-cycle are set up.
BE_AGAIN | 03F2 | LD C,L | Reset the C register.
| 03F3 | INC C | Add 16 T states as this path is shorter.
| 03F4 | JP (IX) | Jump back.
Upon completion of the 'beep' the maskable interrupt has to be enabled.
BE_END | 03F6 | EI | Enable interrupt.
| 03F7 | RET | Finally return.