This BASIC subroutine renumbers a specified range of program lines in memory without requiring any machine code. It works by directly manipulating the BASIC program area starting at address 16509, walking the linked-list structure of BASIC lines (each line header stores its number in two bytes and its length in the following two bytes) to locate user-specified start and end lines, then POKEing new line numbers into memory. The routine accepts three inputs: the last line to renumber, the first line to renumber, and a new starting line number with an increment value. A boundary check prevents renumbering into the reserved 9970–9999 range used by the subroutine itself.
Program Analysis
Program Structure
The listing is a self-contained subroutine occupying lines 9950–9999. Lines 9950–9970 are housekeeping/setup lines (SAVE, LIST, REM, STOP) intended to be deleted before the subroutine is embedded in a host program. The operative logic runs from line 9971 onward and divides into three phases:
- Line-search subroutine (9971–9979): Starting at the BASIC program area, walks the program’s line headers to find a user-specified line number.
- Input phase (9980–9989): Prompts the user for the last line to renumber, the first line to renumber, a new starting number, and an increment.
- Renumber loop (9990–9999): Iterates from the first to the last line, POKEing the new line numbers into the two-byte line-number field of each line header.
BASIC Line Memory Layout
The ZX81 BASIC program area begins at address 16509 (system variable PROG). Each line is stored as a 4-byte header followed by the tokenised statement bytes:
| Offset | Content |
|---|---|
| +0 | High byte of line number |
| +1 | Low byte of line number |
| +2 | Low byte of line length |
| +3 | High byte of line length |
| +4… | Tokenised BASIC text |
Note that the length bytes are stored in little-endian order (low byte at +2, high byte at +3), which is reflected in the pointer-advance expression at line 9977: A+4+PEEK (A+2)+256*PEEK (A+3).
Line-Search Subroutine (9971–9979)
The inner subroutine at line 9971 is a reusable routine called twice via GOSUB 9971. It reads a target line number S from INPUT, then walks the program area starting at address A (initialised to 16509). At each header it reconstructs the stored line number as 256*PEEK A + PEEK (A+1) and compares it with S. If the current line number reaches or exceeds 9970 without a match, the subroutine reports “NOT FOUND” and terminates at line 9979 (falling through without a RETURN, which will cause an error — see Bugs section).
Renumber Loop (9990–9996)
The renumber loop uses pointer A (set to the first line to renumber) and AE (set to just past the last line: AE = A+3 after the last-line search). Each iteration:
- Checks
A > AEto detect completion (line 9990). - Guards against the new line number exceeding 9969 (line 9991).
- POKEs the high byte of
Nat addressA(line 9992) and the low byte atA+1(line 9993). - Advances
Ato the next line header (line 9994). - Increments
NbyI(line 9995).
Notable Techniques
- No machine code: The entire renumber operation is performed in pure BASIC using PEEK/POKE, making it portable and easy to embed.
- Self-protection guard: Line 9976 and line 9991 both prevent the routine from renumbering its own lines (above 9969), avoiding self-corruption.
- Reusable inner subroutine: The GOSUB at 9971 is called twice with different prompts, cleanly factoring out the line-search logic.
- Boundary pointer
AE: Set toA+3after finding the last line (line 9982), so the loop conditionA > AEcorrectly terminates after the final line is processed.
Bugs and Anomalies
- “NOT FOUND” path lacks RETURN: If a searched line number is not found, lines 9979–9979 print an error message but do not RETURN or STOP, causing execution to fall through into the input phase at line 9980, which is likely unintended behaviour.
- Spelling error: Line 9999 contains
CANT COMPLEAT— “COMPLEAT” is a misspelling of “COMPLETE”. - Variable
Areuse: After the first GOSUB (finding the last line),Ais saved intoAE, but then the second GOSUB reinitialisesAto 16509 at line 9971. This is correct behaviour but relies on the programmer re-entering a line number that falls beforeAE— no validation is performed to ensure the first line precedes the last line. - No GO TO adjustment: The routine renumbers line-number headers only. Any
GO TOorGO SUBtargets embedded within the tokenised lines are not updated, which can break program flow after renumbering.
Content
Source Code
9950 SAVE "1029%9"
\n9960 LIST
\n9965 REM DELETE LINES 9950-9960 B4 USING THIS SUBROUTINE
\n9970 STOP
\n9971 LET A=16509
\n9972 INPUT S
\n9973 PRINT S
\n9974 LET L=256*PEEK A+PEEK (A+1)
\n9975 IF L=S THEN RETURN
\n9976 IF L>=9970 THEN GOTO 9979
\n9977 LET A=A+4+PEEK (A+2)+256*PEEK (A+3)
\n9978 GOTO 9974
\n9979 PRINT S;"NOT FOUND"
\n9980 PRINT "LAST LINE TO BE RENUMBERED=";
\n9981 GOSUB 9971
\n9982 LET AE=A+3
\n9983 PRINT "1ST LINE TO BE RENUMBERED=";
\n9984 GOSUB 9971
\n9985 PRINT "1ST NEW LINE NUMBER=";
\n9986 INPUT N
\n9987 PRINT N,"INCREMENT LINES BY:";
\n9988 INPUT I
\n9989 PRINT I
\n9990 IF A>AE THEN GOTO 9997
\n9991 IF N>9969 THEN GOTO 9999
\n9992 POKE A,INT (N/256)
\n9993 POKE (A+1),N-256*INT (N/256)
\n9994 LET A=A+4+PEEK (A+2)+256*PEEK (A+3)
\n9995 LET N=N+I
\n9996 GOTO 9990
\n9997 PRINT ,,"RENUMBER COMPLETE"
\n9998 STOP
\n9999 PRINT ,,"CANT COMPLEAT. LINE NR>9969."
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
