This program installs and demonstrates a machine code routine that generates sound through the beeper port (port 254). It lowers RAMTOP by 43 bytes using CLEAR to carve out space just above the existing top of RAM, then POKEs 43 bytes of Z80 machine code into that reserved area. The routine directly manipulates the speaker bit of port 254 using OUT instructions (opcode 0xD3, 0xFE) combined with SET and RES bit operations on register H, producing a tone loop controlled by a register pair acting as a delay counter. The BASIC demonstration calls the routine repeatedly via USR whenever a key is pressed, and prints the USR address so users can call it from their own programs.
Program Structure
The program is divided into four clear phases, each annotated with REM blocks:
- Lower RAMTOP (line 30): Reads the current RAMTOP from system variables at addresses 23730–23731, subtracts 43, and calls
CLEAR xto move RAMTOP down, reserving space for the machine code. - POKE machine code (lines 50–60): Recalculates the base address as RAMTOP+1 (the first byte above the new RAMTOP) and reads 43 bytes from DATA statements into that block.
- Display USR address (line 80): Prints the entry-point address so the user can call the routine independently from their own programs.
- Demo loop (lines 100–130): Waits for a keypress with a busy-wait
INKEY$loop, calls the routine viaUSR x, and repeats.
Memory Layout
System variables at addresses 23730 and 23731 hold the low and high bytes of RAMTOP respectively. The program uses the idiom PEEK 23730 + 256*PEEK 23731 twice — once to compute the new RAMTOP (subtracting 43) and again to find the installation address (adding 1 to the new RAMTOP). The 43-byte block sits just above the lowered RAMTOP, where BASIC’s memory manager will not disturb it.
Machine Code Analysis
The 43 DATA bytes disassemble to a Z80 beeper tone routine. Key observations:
58, 72, 92—LD A,(5C48H): loads the current border/speaker byte from the system variable BORDCR (0x5C48).31, 31, 31— threeSRL Ashifts: isolates the lower 3 bits of the value.230, 7—AND 07H: masks to 3 bits, giving a base value for the output register.14, 255—LD C, 0FFH: loads the outer loop counter.38, 0—LD H, 00H: initialises the high byte of the delay/pitch counter.68—LD H, B(or similar register move): sets up the inner counter.203, 231—SET 4, H(CB E4): sets the speaker bit.211, 254—OUT (FEH), A: outputs to the beeper port, toggling the speaker.16, 254—DJNZ FEH: inner delay loop (254 = −2 offset, tight loop back).203, 167—RES 4, H: clears the speaker bit.- The pattern of SET/OUT/DJNZ/RES/OUT/DJNZ repeats to produce a square wave.
36—INC H: advances the pitch/duration counter.13—DEC C: decrements the outer loop counter.32, 226—JR NZ, E2H: loops back while counter is nonzero (offset −30).201—RET: returns to BASIC.
The routine produces a rising-pitch sweep: the inner DJNZ loop creates the half-period delay, and incrementing H on each outer iteration shortens the delay, raising the pitch progressively until the outer counter C reaches zero.
Notable BASIC Techniques
- Using
RESTOREat line 30 ensures the DATA pointer is reset before the POKE loop at line 60, making the program safely re-runnable. - The address variable
xis reused for both the RAMTOP calculation (line 30) and the installation address (line 50), keeping memory usage minimal but requiring two separate PEEK calculations. - The busy-wait keypress loop at lines 110–130 does not use
PAUSE 0; it pollsINKEY$directly in aGO TOloop, which is the standard ZX Spectrum idiom for this era. USR xis used as a function (line 120) whose return value is stored inabut never used — the call is made purely for the side effect of producing sound.
Potential Issues
- The address stored in
xafter line 30 reflects the lowered RAMTOP, but line 50 re-reads PEEK 23730/23731. AfterCLEAR x, these system variables will have been updated to the new RAMTOP value, so the second calculation correctly yields the installation address. - There is no bounds checking: if RAMTOP is already very low (e.g. on a 16K machine with heavy usage), subtracting 43 could corrupt memory.
- The machine code reads from BORDCR (0x5C48) but does not actually vary the output value with the speaker bit correctly isolated — the AND 7 masks bits 0–2, which correspond to the border colour, not the MIC/speaker bits (bits 3 and 4 of port 254). The speaker bit toggling is handled entirely within the machine code via SET/RES on H, so the initial LD A,(BORDCR) serves to preserve the border colour in the low bits of A during OUT instructions.
Content
Source Code
10 REM Beep port routine
11 REM
20 REM Lower RAMTOP
21 REM
30 RESTORE : LET x=(PEEK 23730+256*PEEK 23731)-43: CLEAR x
40 REM
41 REM POKE Machine code
42 REM into memory
43 REM
50 LET x=(PEEK 23730+256*PEEK 23731)+1
60 FOR y=x TO x+42: READ z: POKE y,z: NEXT y
70 REM
71 REM Print USR Address
72 REM
80 PRINT AT 1,2;"The routine is called by the";TAB 7;"function USR ";x
90 REM
91 REM Example
92 REM
100 PRINT AT 12,10;"Press any key"
110 IF INKEY$="" THEN GO TO 110
120 LET a=USR x
130 GO TO 110
200 REM
201 REM Machine code data
202 REM
210 DATA 58,72,92,31,31,31,230,7,14,255
220 DATA 38,0,68,203,231,211,254,16,254,68
230 DATA 203,167,211,254,16,254,203,231,211,254
240 DATA 16,254,203,167,211,254,16,254,36,13
250 DATA 32,226,201
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
