AID

Developer(s): Paul Holmgren
Date: 198x
Type: Program
Platform(s): TS 1000

AID is a programmer’s toolkit for BASIC development, offering seven utilities: line finding, byte-level memory inspection and POKEing, REM line creation, line guarding, self-start/save, single-line listing, and program renumbering. The renumber routine (lines 9941–9997) traces through BASIC program memory starting at address 16513, patching line-number tokens following GOTO (token 236) and GOSUB (token 237) opcodes, and updates the raw line-number bytes stored in the program area. The REM creation tool (lines 9873–9885) uses USR calls to a machine code routine and directly manipulates the STKEND/program pointers at addresses 16514–16516 to insert a blank REM line of a user-specified length. Memory addresses such as 16509 (PROG), 16425–16426 (PPC/NXTLIN), and 16530 (USR 16530 for free bytes) are accessed throughout via PEEK and POKE.


Program Analysis

Program Structure

The program loads via a short bootstrap at lines 3–4 that jumps to line 9831, where the main menu is displayed. Each menu option maps to a subroutine block in the high 9800s. The dispatch is performed at line 9838 with:

GOTO VAL "9822"+CODE ")"*G

CODE ")" evaluates to 14 (the ZX81 character code for )), so selecting menu option n jumps to line 9822 + 14×n, landing on one of the REM anchors at 9836, 9856, 9873, 9890, 9907, 9924, or 9941. The variable J is permanently set to 9831 (the menu line) and is reused everywhere a “return to menu” GOTO is needed, saving token space.

Menu Options Summary

OptionFunctionEntry line
1Find a line / show address & length9840
2Alter a byte’s data (PEEK/POKE)9857
3Create a REM line9874
4Line guard (STOP after listing self)9891
5Self-start / SAVE9908
6LIST one line at a time9926
7Renumber9943

Option 1 – Line Finder

Starting from D = 16509 (PROG system variable), the loop at 9843–9846 reads two-byte line numbers (PEEK D * 256 + PEEK (D+1)) and steps through lines using the stored line-length bytes at offsets +2 and +3. When the target line M is found, its address and byte length are printed. The user is then offered the chance to enter the byte-editing sub-loop (option 2 reused inline at 9848–9872).

Option 2 – Byte Editor

The user supplies a start address; eight consecutive bytes are displayed as address/decimal/character triplets. The user then inputs an address and a value to POKE, with a Y/N confirmation guard at line 9869–9870.

Option 3 – REM Line Creator

This option calls machine code. USR 16524 returns the start address of free memory minus 2, stored in L. A loop POKEs zero bytes into the new space. The STKEND pointer at 16514–16515 is updated each iteration to claim the growing block. RAND 16516 (i.e., RANDOMIZE USR 16516) is used as a lightweight machine-code call mid-loop. The line-length field of the new REM is written at offsets −1 and −2 from L, incorporating A2 (= CODE "▝" = 1, used as a constant for the minimum REM overhead).

Option 4 – Line Guard

Lines 9891–9897 list line 9893 itself, then read the current line pointer from system variables 16425–16426 (PPC/NXTLIN) and overwrite the first two bytes of the next line with NOT PI (= 0), effectively corrupting the line number of any line that follows, preventing further execution or listing beyond that point.

Option 5 – Self-Start / Save

Line 9910 performs a SAVE "AID". Line 9911 uses GOTO SGN PI (= GOTO 1) which targets a non-existent line, a standard technique for a clean warm restart without error.

Option 6 – Single-Line Lister

Starting at D = 16509, the loop walks program memory byte-by-byte. Token 126 (the ZX81 number-follows marker) causes a 6-byte skip via CODE "▞" (= 5, plus the +1 from the main increment). Token 118 (NEWLINE) triggers a pause and resets to the next line. Characters are printed with CHR$ PEEK D. Line numbers are right-aligned using TAB INT PI (= TAB 3) minus the string length of the line number.

Option 7 – Renumberer

This is the most complex routine. The user inputs a start number G and a step FF. The outer loop (9949–9958) scans program memory for NEWLINE tokens (118) at each line boundary, then searches for GOTO (236) or GOSUB (237) tokens. When found, subroutine 9959 is called.

Inside 9959–9984, the routine extracts the numeric argument of the GOTO/GOSUB by locating the floating-point number representation (terminated by token 126). It reconstructs the integer target line number K from the BCD-like digit tokens (tokens 28–37 representing digits 0–9) using positional arithmetic with L as a power-of-10 accumulator, multiplied by CODE "\~~" (= 10). It then counts how many program lines precede line K (loop 9977–9981), deriving offset N, and computes the renumbered target as G + N*FF.

The actual line-number rewriting is done by subroutine 9990–9997, which iterates over all lines, updating the two-byte header of each with the new sequential number built from J (high byte, incremented every 256 steps) and G (low byte, incremented by FF).

Key BASIC Idioms and Techniques

  • VAL "expression" is used throughout to encode numeric constants and expressions as strings, reducing token storage cost (e.g., VAL "9831", VAL "256", VAL "16509").
  • NOT PI = 0 and SGN PI = 1 are used as compact numeric literals.
  • CODE "char" encodes small constants: CODE ")" = 14 (dispatch multiplier), CODE "▝" = 1 (A2), CODE "7" = 7, CODE "6" = 6, CODE "▌" = 4 (line-header size), CODE "▞" = 5, CODE "\~~" = 10.
  • PAUSE J with subsequent INKEY$ check is the standard efficient keypress-wait idiom.
  • J = 9831 is reused as both the return-to-menu target and as the argument to PAUSE (a very long pause), serving dual purpose.
  • The REM blocks at 9839, 9856, 9873, 9890, 9907, 9924, 9941, 9998, 9999 act as labeled anchors and dispatch targets, with their content sometimes holding encoded data visible in the listing header (line 2).

Anomalies and Notable Points

  • Line 9847 uses TAB INT PI = TAB 3 and TAB NOT PI = TAB 0 for layout control.
  • Line 9876: USR 16524-2 — the subtraction of 2 is performed in BASIC arithmetic, not inside the string, suggesting the machine code at 16524 returns a pointer that needs a 2-byte offset correction for the REM body start.
  • The renumberer prints candidate GOTO/GOSUB targets but does not automatically patch the numeric tokens in place — it appears to be an interactive “show what would change” display rather than a fully automatic in-place patch, with the actual line-header rewrite in 9990–9997 being a separate pass.
  • Line 9951 uses VAL "PEEK (I+1)>34" to evaluate a relational expression inside a string, returning 1 (true) or 0 (false), which serves as the condition for skipping past the end-of-program marker test.

Content

Appears On

Related Products

Related Articles

Related Content

Image Gallery

Source Code

   2 REM [,,]#E▙RNDLN [.],,TAN  E£RND##TAN E0RND##5  T[Z] GOSUB PI##TAN  <>5#RNDY#W<>7<>[Y] 4 RAND <>[Y]▘COS Y REM <>[Y]▌4 FOR Y><>[Y]▞4▀<> FASTAT W<>[Y]▞4<=<> FAST LPRINT  GOSUB PI777VAL <> LPRINT <>#▖<>#▀TAN 
   3 LET J=VAL "9831"
   4 GOTO J
 9830 REM >
 9831 CLS
 9832 PRINT ,," TO DELETE ""AID"": RAND USR 16546 THENDELETE LINE 9830","  INPUT NO. TO : ","1 FIND A LINE""S ADD.","2 ALTER A BYTE""S DATA","3 CREATE A REM LINE","4 LINE GUARD",,"5 SELF START / SAVE ","6 LIST 1 LINE AT A TIME","7 RENUMBER ",,,,"   ";VAL "USR 16530";" BYTES LEFT","  [A]█["][P][E][H]█[S][O][F][T][W][A][R][E]["]█[S][P][E][C][I][A][L]"
 9834 LET F=VAL "256"
 9835 LET A2=CODE "▝"
 9836 LET D=VAL "16509"
 9837 INPUT G
 9838 GOTO VAL "9822"+CODE ")"*G
 9839 REM 
 9840 PRINT ,,"LINE NO. ? "
 9841 INPUT M
 9843 LET G=PEEK D*F+PEEK (D+SGN PI)
 9844 IF M=G THEN GOTO VAL "9847"
 9845 LET D=D+VAL "PEEK (D+2)+PEEK (D+3)*F+4"
 9846 GOTO VAL "9843"
 9847 PRINT TAB INT PI;M;" AT ";D;";";VAL "PEEK (D+2)+PEEK (D+3)*F+4";" BYTES";,TAB NOT PI;"ALTER A BYTES DATA? (Y/N)"
 9848 PAUSE J
 9849 IF INKEY$ ="N" THEN GOTO J
 9856 REM 
 9857 PRINT ,," INPUT ""D"" OR ANOTHER ADD TO  LIST "
 9858 INPUT M
 9859 CLS
 9860 FOR G=M TO M+CODE "7"
 9861 PRINT G;"/";PEEK G;"=";CHR$ PEEK G,;
 9862 NEXT G
 9863 PRINT 
 9864 PRINT " INPUT ADD. TO  POKE  THENDATA,   ""0"" TO  STOP"
 9865 INPUT G
 9866 IF NOT G THEN GOTO J
 9867 INPUT D
 9868 PRINT " POKE ";G;",";D;" RIGHT? (Y/N)"
 9869 PAUSE J
 9870 IF INKEY$ ="Y" THEN POKE G,D
 9872 GOTO J
 9873 REM 
 9874 PRINT " INPUT  REM LINE LEN "
 9875 INPUT D
 9876 LET L=VAL "USR 16524-2"
 9877 FOR G=SGN PI TO D
 9878 POKE 16515,INT ((L+G)/F)
 9879 POKE 16514,L+G-F*PEEK 16515
 9880 RAND 16516
 9881 POKE L+G,NOT PI
 9882 NEXT G
 9883 POKE L-SGN PI,INT ((D+A2)/F)
 9884 POKE L-A2,D+A2-F*PEEK (L-SGN PI)
 9885 GOTO J
 9890 REM 
 9891 CLS
 9893 LIST 9893
 9894 LET NO=VAL "PEEK 16425+256*PEEK 16426"
 9895 POKE NO,NOT PI
 9896 POKE (NO+SGN PI),NOT PI
 9897 STOP
 9907 REM 
 9908 PRINT ,," FOR AN ASIS SAVE  INPUT ""C"""
 9910 SAVE "AI[D]"
 9911 GOTO SGN PI
 9924 REM 
 9926 CLS
 9927 LET G=PEEK D*F+PEEK (D+SGN PI)
 9928 PRINT AT CODE ",,",CODE "▖"-LEN STR$ G;G;
 9929 LET D=D+CODE "▖"
 9930 IF PEEK D=VAL "126" THEN LET D=D+CODE "▞"
 9931 IF PEEK D=VAL "118" THEN LET G=NOT G
 9932 IF G THEN PRINT CHR$ PEEK D;
 9933 LET D=D+SGN PI
 9934 IF G THEN GOTO 9930
 9936 PAUSE J
 9938 GOTO VAL "9926"
 9941 REM 
 9943 PRINT " INPUT START NO. THEN INPUT  STEP "
 9944 INPUT G
 9945 LET M=G
 9946 INPUT FF
 9947 CLS
 9948 PRINT "CHANGE THESE LINES"
 9949 LET I=VAL "16513"
 9950 IF PEEK I<>VAL "118" THEN GOTO 9954
 9951 IF VAL "PEEK (I+1)>34" THEN GOTO 9985
 9952 LET M=M+FF
 9953 LET I=I+CODE "▌"
 9954 LET H=VAL "(1 AND PEEK I=236)+(2 AND PEEK I=237)"
 9955 IF PEEK I=VAL "126" THEN LET I=I+CODE "▌"
 9956 IF H THEN GOSUB 9959
 9957 LET I=I+SGN PI
 9958 GOTO 9950
 9959 PRINT M;(" GOTO " AND H=SGN PI)+(" GOSUB " AND H=A2);
 9960 LET H=NOT H
 9961 IF VAL "PEEK (I+1)>27 AND PEEK (I+1)<38" THEN GOTO 9964
 9962 PRINT " * "
 9963 RETURN
 9964 LET J=A2
 9965 IF PEEK (I+J)=VAL "126" THEN GOTO 9969
 9966 LET J=J+SGN PI
 9967 IF J>CODE "▌" THEN GOTO 9962
 9968 GOTO 9965
 9969 LET L=SGN PI
 9970 LET K=NOT J
 9971 LET K=K+VAL "(PEEK (I+J-1)-28)"*L
 9972 LET J=J-SGN PI
 9973 IF VAL "PEEK (I+J-1)=236 OR PEEK (I+J-1)=237" THEN GOTO 9976
 9974 LET L=L*CODE "~~"
 9975 GOTO 9971
 9976 LET J=VAL "16509"
 9977 LET N=NOT J
 9978 IF PEEK J*F+PEEK (J+SGN PI)>=K THEN GOTO 9982
 9979 LET N=N+SGN PI
 9980 LET J=J+VAL "PEEK (J+2)+PEEK (J+3)*F+4"
 9981 GOTO 9978
 9982 LET K=G+N*FF
 9983 PRINT K
 9984 RETURN
 9985 LET J=NOT J
 9986 LET I=VAL "16509"
 9987 GOSUB 9990
 9988 LET I=I+VAL "PEEK (I+2)+PEEK (I+3)*F+4"
 9989 GOTO 9987
 9990 IF PEEK I>CODE "6" THEN STOP
 9991 IF G<F THEN GOTO 9994
 9992 LET J=J+SGN PI
 9993 LET G=G-F
 9994 POKE I,J
 9995 POKE (I+SGN PI),G
 9996 LET G=G+FF
 9997 RETURN
 9998 REM <
 9999 REM 

Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

Scroll to Top