Spectrum LOGO

Date: 198x
Type: Program
Platform(s): TS 2068

Spectrum LOGO is a full turtle-graphics interpreter written in BASIC that implements a substantial subset of the LOGO programming language, including commands for movement (FORWARD, BACK), turning (LEFT, RIGHT), pen control (PENUP, PENDOWN, PENCOLOUR), wrapping behavior (WRAP, NOWRAP), coordinate positioning (SETX, SETY, SETXY, SETHEADING), and structured repetition (REPEAT). Users can define and edit named procedures using DEFINE and EDIT, with each procedure stored as a packed string in arrays using CHR$ 0 as a delimiter. The interpreter supports both full keyword names and two-character abbreviations, dispatching execution via a table of line-number addresses stored in numeric arrays. Wrap-around drawing uses trigonometric calculations to clip and continue lines at screen boundaries, and the turtle cursor itself is drawn using PLOT/DRAW with INVERSE to toggle its visibility.


Program Structure

The program is organized into clearly separated functional regions by line number ranges:

  • Lines 60–300: Initialization — dimensions arrays, loads command dispatch tables from DATA statements.
  • Lines 400–480: Setup — pokes flags, allocates runtime buffers, jumps to HOME and the main input loop.
  • Lines 520–760: Error handler — dispatches to error message strings based on an err code.
  • Lines 1030–1070: Tokenizer subroutine — walks z$ extracting whitespace-delimited tokens into y$.
  • Lines 1110–1190: Turtle cursor drawing — draws a small arrow indicating heading using PLOT/DRAW with INVERSE.
  • Lines 1220–1290: Number parser — validates and converts a token to a numeric value in a.
  • Lines 1300–1350: Procedure name lookup — checks f$() array and dispatches via g() addresses.
  • Lines 1400–1710: Boundary clipping — trigonometric routines for wrapping lines at screen edges (X and Y axes separately, then combined).
  • Lines 2020–2230: Main REPL — reads input, dispatches to 2-char abbreviations via x$()/u() or full names via w$()/v().
  • Lines 3020–3660: Movement commands — FORWARD, BACK, LEFT, RIGHT, including wrap-around draw logic.
  • Lines 3820–3900: HOME and CLS — resets turtle to center screen at heading 0.
  • Lines 4020–4715: Pen and positional commands — PENUP, PENDOWN, REPEAT, PENCOLOUR, WRAP, NOWRAP, SAVE, COPY, STOP, SETX, SETY, SETXY, SETHEADING.
  • Lines 5005–5130: DEFINE — interactive procedure definition with multi-line input.
  • Lines 5205–5730: EDIT — full-screen interactive editor for stored procedures (edit, insert, delete, remove).
  • Lines 6005–6050: Procedure call executor — pushes/pops state for nested procedure calls.
  • Line 8010–8020: RANDOM command — returns a random integer in the range [0, a).

Command Dispatch Tables

All commands are resolved through three parallel arrays loaded at startup from DATA blocks:

ArrayPurposeSize
x$() / u()2-character abbreviations and their target line numbers16 entries
w$() / v()Full keyword names (up to 12 chars) and their target line numbers22 built-in entries
f$() / g()Extended or special command names and their target line numbers1 entry initially (RANDOM)

Line numbers in the dispatch tables are stored as VAL "nnnn" expressions in the DATA statements — a standard memory-optimization technique for BASIC that avoids storing large integer literals directly. Dispatch is performed with GO SUB u(i) or GO SUB v(i), targeting the appropriate handler subroutine.

Tokenizer and Parser

The subroutine at line 1030 implements a simple whitespace-delimited tokenizer. It walks the current input string z$ from position s, skipping spaces and CHR$ 0 characters, and accumulates non-whitespace characters into y$. The flag t tracks whether token collection has started. The flag t1 is set when the end of the string is reached.

Numeric parsing at line 1220 validates that each character of y$ is a digit (ASCII codes 48–57) before applying VAL y$ to obtain the value.

Turtle State and Cursor

Turtle state is maintained in the following global variables:

  • x, y — current pixel position (screen coordinates 0–255, 0–175)
  • dir — current heading in degrees (0 = up/north)
  • pp — pen state: SGN PI (1) = pen down, NOT PI (0) = pen up
  • wr — wrap mode: SGN PI = wrap enabled, NOT PI = no-wrap
  • turt — turtle visibility: 0 = visible, 1 = hidden; used as the INVERSE parameter in PLOT/DRAW calls

The turtle cursor is drawn at line 1110 as two short lines emanating from the turtle position at angles 160° and 200° offset from the heading, using PLOT INVERSE turt to toggle the pixels. This makes the cursor appear and disappear cleanly without erasing background graphics.

Wrap-Around Drawing

Lines 14001710 implement edge-wrapping using trigonometry. The subroutine at 1600 determines which boundary (left, right, top, or bottom) a line will hit first, computes the intersection point, draws to that point, subtracts the distance traveled from the remaining distance a, then continues from the opposite edge. This is repeated via GO TO 3050 until the full distance is drawn. The heading angle q is converted to radians for SIN/COS/TAN calculations.

Procedure Definition and Storage

User-defined procedures are entered interactively via the DEFINE command (line 5005). Each procedure body consists of up to 10 lines of up to 28 characters each, stored in the 2D string array b$() with lengths in c(). When definition is complete, all lines are concatenated into a single string separated by CHR$ 0 delimiters and stored in k$(def) with total length in l(def). The procedure name (padded to 12 characters) is stored in w$(n+def), extending the command dispatch table at runtime. Up to 40 user procedures can be defined (DIM k$(40, 200)).

Procedure Execution and Call Stack

The subroutine at line 6005 handles procedure calls with a software call stack using arrays m() and n() (dimensioned to 40). Before entering a procedure, it saves the current command index (comm) and string position (s). The variable count tracks the stack depth. On return, the saved state is restored. The outermost level saves the original input string z$ and position into p$ and s1.

REPEAT Implementation

REPEAT is implemented using a separate stack array r(20, 2), where each entry stores the start position in the input string and the remaining iteration count. The subroutine at 4110 finds and validates the opening [ bracket, then stores the position and count. The ] command (line 4200) decrements the count and jumps back to the saved position or pops the stack when done. Up to 20 nested REPEATs are supported.

Constant Encoding Idioms

Throughout the program, numeric constants are expressed using BASIC intrinsic functions rather than literal integers, a common technique to reduce token storage size:

  • SGN PI — evaluates to 1
  • NOT PI — evaluates to 0
  • INT PI — evaluates to 3
  • VAL "nnnn" — used for larger constants, especially line-number targets in dispatch tables

Interactive Editor

The EDIT command (line 5200) provides a full-screen procedure editor. After looking up the named procedure and unpacking it into b$()/c(), it presents a menu with five options: edit a line, insert a line, delete a line, remove the whole procedure, or return. INKEY$ polling is used throughout the editor for single-keypress menu selection. Line display is formatted with PRINT i; TAB 3; b$(i).

Notable Anomalies

  • Line 5415 has a logical bug: the condition z$<"0" AND z$> STR$ (k-SGN PI) uses AND where OR was likely intended, so valid line-number input may not be correctly rejected.
  • Line 5520 references GO TO VAL "5915" which is the RETURN of the yes/no confirmation subroutine — branching into the middle of a subroutine body to effectively skip input and return immediately, an intentional shortcut.
  • The variable names m and n are reused: initially scalar variables holding array dimension counts (lines 60–70), they are later re-dimensioned as arrays DIM m(40) and DIM n(40) at line 470, which destroys the original scalar values. This works because the scalar values are no longer needed after initialization.
  • Line 3800 (the DRAW/HOME subroutine referenced by GO SUB VAL "3800" at line 480) does not correspond to any listed line; the actual home initialization is at line 3820. The program relies on BASIC’s behavior of running the next available line when a non-existent line is targeted.

Source Code

   60 READ m:READ n:READ o
   65 DIM x$(m, VAL "2"):DIM w$(n+ VAL "40", VAL "12"):DIM f$(o, VAL "12")
   70 DIM u(m) :DIM v(n):DIM g(o)
   75 LET pp= SGN PI:LET wr= SGN PI:LET turt= SGN PI
   80 FOR i= SGN PI TO m:READ x$(i)
   85 READ u(i):NEXT i
   90 FOR i= SGN PI TO n:READ w$(i)
   95 READ v(i):NEXT i
  100 FOR i= SGN PI TO o:READ f$(i)
  105 READ g(i):NEXT i
  110 DATA VAL "16", VAL "22", SGN PI
  115 DATA "FD", VAL "3000","BK", VAL "3200","LT", VAL "3400","RT", VAL "3600"
  120 DATA "PU", VAL "4000","PD", VAL "4050"
  125 DATA "RP", VAL "4100","PC", VAL "4300","WR", VAL "4400","NW", VAL "4600"
  130 DATA "SX", VAL "4600","SY", VAL "4630","XY", VAL "4660","SH", VAL "4700"
  135 DATA "DF", VAL "5000","ED", VAL "5200"
  200 DATA "FORWARD", VAL "3000","BACK", VAL "3200","LEFT", VAL "3400"
  205 DATA "RIGHT", VAL "3600","DRAW", VAL "3800","HOME", VAL "3850"
  210 DATA "PENUP", VAL "4000","PENDOWN", VAL "4050"
  215 DATA "REPEAT", VAL "4100","]", VAL "4200","PENCOLOUR", VAL "4300","WRAP", VAL "4400","NOWRAP", VAL "4450"
  220 DATA "SAVE", VAL "4500","COPY", VAL "4520","STOP", VAL "4550"
  225 DATA "SETX", VAL "4600","SETY", VAL "4630","SETXY", VAL "4660","SETHEADING", VAL "4700"
  230 DATA "DEFINE", VAL "5000","EDIT", VAL "5200"
  300 DATA "RANDOM", VAL "8000"
  400 POKE VAL "23658", VAL "8"
  450 DIM r(VAL "20", VAL "2")
  465 LET def= NOT PI:DIM k$(VAL "40", VAL "200"):DIM l(VAL "40")
  470 DIM b$(VAL "10", VAL "28"):DIM c(VAL "10"):DIM m(VAL "40"):DIM n(VAL "40")
  480 GO SUB VAL "3800":GO TO VAL "2000"
  520 GO SUB (VAL "690"+err* VAL "10")
  530 PRINT # SGN PI;a$
  540 PAUSE VAL "250"
  550 RETURN 
  700 LET a$="Command error - re-enter the    line.":RETURN 
  710 LET a$="Number error - re-enter the line":RETURN 
  720 LET a$="No wrap - the line cannot be    drawn":RETURN 
  730 LET a$="Too many REPEATS":RETURN 
  740 LET a$="DEFINE name error":RETURN 
  750 LET a$="No room for further commands":RETURN 
  760 LET a$="Incorrect Command name":RETURN 
 1030 LET t= NOT PI:LET y$=""
 1040 LET s=s+ SGN PI:IF s> LEN z$ THEN LET t1= SGN PI:RETURN 
 1050 IF (z$(s)=" " OR z$(s)= CHR$ NOT PI) AND t= NOT PI THEN GO TO VAL "1040"
 1060 IF (z$(s)=" " OR z$(s)= CHR$ NOT PI) AND t= SGN PI THEN RETURN 
 1070 LET y$=y$+z$(s):LET t= SGN PI:GO TO VAL "1040"
 1110 IF x< INT PI OR x> VAL "252" OR y< INT PI OR y> VAL "172" THEN RETURN 
 1120 FOR j= VAL "160" TO VAL "200" STEP VAL "40"
 1130 LET q=dir+j:IF q< NOT PI THEN LET q= VAL "360"-j
 1140 IF q> VAL "360" THEN LET q=q- VAL "360"
 1150 LET q=q* PI/ VAL "180"
 1160 LET x1= VAL "5"* SIN q:LET y1= VAL "5"* COS q
 1170 PLOT INVERSE turt;x,y
 1180 DRAW INVERSE turt;x1,y1
 1190 NEXT j:RETURN 
 1220 GO SUB VAL "1020":IF err= SGN PI THEN LET err= VAL "2":RETURN 
 1230 IF CODE y$> VAL "57" THEN GO TO VAL "1300"
 1240 IF y$="" THEN LET err= VAL "2":RETURN 
 1250 FOR k= SGN PI TO LEN y$
 1260 IF CODE y$(k)< VAL "48" OR CODE y$(k)> VAL "57" THEN LET err= VAL "2"
 1270 NEXT k
 1280 IF err= NOT PI THEN LET a= VAL y$
 1290 LET t= NOT PI:RETURN 
 1310 LET y$=(y$+"            ")( TO VAL "12")
 1320 FOR i= SGN PI TO o:IF y$=f$(i) THEN GO TO VAL "1340"
 1330 NEXT i:LET err= VAL "2":RETURN 
 1340 GO SUB g(i):RETURN 
 1410 LET cr= SGN PI:IF q< VAL "2"* PI AND q> PI THEN LET cr=- SGN PI
 1420 IF cr= SGN PI THEN LET x1= VAL "255"-x:IF q> VAL "1.57" THEN LET y1= NOT PI:LET x2= NOT PI:GO TO VAL "1430"
 1425 IF cr= SGN PI THEN LET y1=x1/ TAN q:LET x2= NOT PI
 1430 IF cr=- SGN PI THEN LET x1=-x:LET y1=(x1/ TAN (q- PI)):LET x2= VAL "255"
 1440 LET y2=y+y1:LET a=a- INT SQR (ABS (x1)^ VAL "2"+ ABS (y1)^ VAL "2")
 1450 RETURN 
 1510 LET cr= SGN PI:IF q> PI/ VAL "2" AND q< INT PI* PI/ VAL "2" THEN LET cr=- SGN PI
 1520 IF cr= SGN PI THEN LET y1= VAL "175"-y:LET x1=y1* TAN q:LET y2= NOT PI
 1530 IF cr=- SGN PI THEN LET y1=y:LET x1=-(y1* TAN (q- PI)):LET y2= VAL "175":LET y1=-y
 1540 LET x2=x+x1:LET a=a- INT SQR (ABS (x1)^ VAL "2"+ ABS (y1)^ VAL "2")
 1550 RETURN 
 1605 IF q> PI/ VAL "2" THEN GO TO VAL "1630"
 1610 LET x3=x+(VAL "175"-y)* TAN q
 1615 IF x3> VAL "255" THEN GO TO VAL "1400"
 1620 IF x3< VAL "255" THEN GO TO VAL "1500"
 1625 GO TO VAL "1680"
 1630 IF q> PI THEN GO TO VAL "1645"
 1635 LET x3=x+y* TAN (PI-q)
 1640 GO TO VAL "1615"
 1645 IF q> INT PI* PI/ VAL "2" THEN GO TO VAL "1670"
 1650 LET x3=x-y* TAN (q- PI)
 1655 IF x3< NOT PI THEN GO TO VAL "1400"
 1660 IF x3> NOT PI THEN GO TO VAL "1500"
 1665 GO TO VAL "1680"
 1670 LET x3=x-(VAL "175"-y)* TAN (VAL "2"* PI-q)
 1675 GO TO VAL "1655"
 1680 IF y1 >=x1 THEN LET a1= INT (y1/ COS q+ VAL "1.5")
 1685 IF y1<x1 THEN LET a1= INT (x1/ SIN q+ VAL "1.5")
 1690 LET a=a-a1:GO SUB VAL "1450":IF y2< NOT PI THEN LET y2= VAL "175"
 1695 IF y2> VAL "175" THEN LET y2= NOT PI
 1700 GO SUB VAL "1500":IF x2< NOT PI THEN LET x2= VAL "255"
 1705 IF x2> VAL "255" THEN LET x2= NOT PI
 1710 RETURN 
 2020 INPUT "W:"; LINE z$:LET s= NOT PI
 2025 LET count= NOT PI:LET rc= NOT PI:GO SUB VAL "2040":GO TO VAL "2020"
 2060 LET t= NOT PI:LET t1= NOT PI:LET err= NOT PI
 2070 GO SUB VAL "1020"
 2080 IF t1= SGN PI AND y$="" AND count= NOT PI THEN LET turt= NOT PI:GO SUB VAL "1100"
 2085 IF t1= SGN PI AND y$="" THEN RETURN 
 2090 IF LEN y$ <> VAL "2" THEN GO TO VAL "2130"
 2100 FOR i= SGN PI TO m
 2110 IF y$=x$(i) THEN GO TO VAL "2200"
 2120 NEXT i
 2130 LET y$=(y$+"            ")( TO VAL "12")
 2140 FOR i= SGN PI TO n+def
 2150 IF y$=w$(i) THEN GO TO VAL "2210"
 2160 NEXT i
 2180 LET err= SGN PI:IF turt= SGN PI THEN LET turt= NOT PI:GO SUB VAL "1100"
 2190 GO TO VAL "520"
 2200 GO SUB u(i):GO TO VAL "2220"
 2210 IF i <=n THEN GO SUB v(i):GO TO VAL "2220"
 2215 GO SUB VAL "6000"
 2220 IF err> NOT PI THEN GO TO VAL "520"
 2230 GO TO VAL "2070"
 3020 GO SUB VAL "1200":IF err> NOT PI THEN RETURN 
 3030 IF turt= NOT PI THEN LET turt= SGN PI:GO SUB VAL "1100"
 3040 LET q=dir* PI/ VAL "180"
 3050 LET x1= INT (VAL ".5"+a* SIN q):LET y1= INT (VAL ".5"+a* COS q)
 3060 LET tr= NOT PI:LET x2=x+x1:LET y2=y+y1
 3070 IF x2< NOT PI OR x2> VAL "255" THEN LET tr= SGN PI
 3080 IF y2< NOT PI OR y2> VAL "175" THEN LET tr=tr+ VAL "2"
 3090 IF tr> NOT PI AND wr= NOT PI THEN LET err= INT PI:RETURN 
 3100 IF tr= NOT PI THEN GO TO VAL "3120"
 3110 GO SUB (VAL "1300"+tr* VAL "100")
 3120 IF pp= SGN PI THEN PLOT x,y:DRAW x1,y1
 3130 IF pp= NOT PI THEN PLOT INVERSE SGN PI; OVER SGN PI;x,y:DRAW INVERSE SGN PI; OVER SGN PI;x1,y1
 3140 LET x=x2:LET y=y2:IF tr> NOT PI THEN GO TO VAL "3050"
 3150 LET t= NOT PI:RETURN 
 3220 IF turt= NOT PI THEN LET turt= SGN PI:GO SUB VAL "1100"
 3230 LET dir=dir+ VAL "180":IF dir> VAL "360" THEN LET dir=dir- VAL "360"
 3240 GO TO VAL "3020"
 3420 IF turt= NOT PI THEN LET turt= SGN PI:GO SUB VAL "1100"
 3430 GO SUB VAL "1200":IF err> NOT PI THEN RETURN 
 3440 LET dir=dir-a
 3450 IF dir< NOT PI THEN LET dir= VAL "360"+dir:GO TO VAL "3450"
 3460 RETURN 
 3620 IF turt= NOT PI THEN LET turt= SGN PI:GO SUB VAL "1100"
 3630 GO SUB VAL "1200":IF err> NOT PI THEN RETURN 
 3640 LET dir=dir+a
 3650 IF dir> VAL "360" THEN LET dir=dir- VAL "360":GO TO VAL "3650"
 3660 RETURN 
 3820 CLS 
 3870 IF turt= NOT PI THEN LET turt= SGN PI:GO SUB VAL "1100"
 3880 LET x= VAL "128":LET y= VAL "88"
 3890 LET dir= NOT PI:LET turt= NOT PI
 3900 GO SUB VAL "1100":RETURN 
 4020 LET pp= NOT PI:RETURN 
 4070 LET pp= SGN PI:RETURN 
 4110 GO SUB VAL "1200":IF err> NOT PI THEN RETURN 
 4115 LET s=s+ SGN PI:IF s> LEN z$ THEN LET err= SGN PI:RETURN 
 4120 IF z$(s)=" " THEN GO TO VAL "4115"
 4125 IF z$(s)="[" THEN GO TO VAL "4135"
 4130 LET err= SGN PI:RETURN 
 4135 LET rc=rc+ SGN PI:LET s=s+ SGN PI
 4140 IF rc> VAL "20" THEN LET err= VAL "4":RETURN 
 4145 LET r(rc, SGN PI)=s:LET r(rc, VAL "2")=a
 4150 RETURN 
 4210 LET r(rc, VAL "2")=r(rc, VAL "2")- SGN PI
 4215 IF r(rc, VAL "2")> NOT PI THEN LET s=r(rc, SGN PI):RETURN 
 4220 LET rc=rc- SGN PI:RETURN 
 4310 GO SUB VAL "1200":IF err> NOT PI THEN RETURN 
 4315 IF a> VAL "7" THEN LET err= VAL "2":RETURN 
 4320 INK a:RETURN 
 4410 LET wr= SGN PI:RETURN 
 4460 LET wr= NOT PI:RETURN 
 4505 INPUT "SAVE - Enter file name ";n$
 4510 SAVE n$ LINE VAL "2000":RETURN 
 4525 COPY :RETURN 
 4555 CLS :STOP 
 4605 GO SUB VAL "1200":IF err> NOT PI THEN RETURN 
 4610 IF turt= NOT PI THEN LET turt= SGN PI:GO SUB VAL "1100"
 4615 IF a< NOT PI OR a> VAL "255" THEN LET err= VAL "2":RETURN 
 4620 LET y2=y:LET tr= NOT PI:LET y1= NOT PI:LET x1=a-x:LET x2=a:GO TO VAL "3120"
 4635 GO SUB VAL "1200":IF err> NOT PI THEN RETURN 
 4640 IF turt= NOT PI THEN LET turt= SGN PI:GO SUB VAL "1100"
 4645 IF a< NOT PI OR a> VAL "175" THEN LET err= VAL "2":RETURN 
 4650 LET x2=x:LET tr= NOT PI:LET x1= NOT PI:LET y1=a-y:LET y2=a:GO TO VAL "3120"
 4665 IF turt= NOT PI THEN LET turt= SGN PI:GO SUB VAL "1100"
 4670 GO SUB VAL "1200":IF err> NOT PI THEN RETURN 
 4675 IF a< NOT PI OR a> VAL "255" THEN LET err= VAL "2":RETURN 
 4680 LET x1=a-x:LET x2=a:GO SUB VAL "1200":IF err> NOT PI THEN RETURN 
 4685 IF a< NOT PI OR a> VAL "175" THEN LET err= VAL "2":RETURN 
 4690 LET tr= NOT PI:LET y1=a-y:LET y2=a:GO TO VAL "3120"
 4705 GO SUB VAL "1200":IF err> NOT PI THEN RETURN 
 4710 IF a< NOT PI OR a> VAL "359" THEN LET err= VAL "2":RETURN 
 4715 LET dir=a:RETURN 
 5005 GO SUB VAL "1020":IF t1= SGN PI AND LEN y$< VAL "2" THEN LET err= VAL "5":RETURN 
 5010 IF LEN y$ <> VAL "2" THEN GO TO VAL "5025"
 5015 FOR i= SGN PI TO m:IF y$=x$(i) THEN LET err= VAL "5":RETURN 
 5020 NEXT i
 5025 LET y$=(y$+"            ")( TO VAL "12")
 5030 FOR i= SGN PI TO def+n:IF y$=w$(i) THEN LET err= VAL "5":RETURN 
 5035 NEXT i:IF def> VAL "39" THEN LET err= VAL "6":RETURN 
 5040 CLS :PRINT "DEFINE ";y$''
 5050 LET def=def+ SGN PI:LET no= NOT PI
 5055 INPUT "W:"; LINE z$:IF LEN z$> VAL "28" THEN PRINT # SGN PI;"Too Long!":PAUSE VAL "200":GO TO VAL "5055"
 5060 IF LEN z$< VAL "2" THEN PRINT # SGN PI;"Nonsense!":PAUSE VAL "200":GO TO VAL "5055"
 5065 PRINT no; TAB INT PI;z$'':LET no=no+ SGN PI:LET len= LEN z$
 5070 IF len< INT PI THEN GO TO VAL "5085"
 5075 FOR i= SGN PI TO len- VAL "2":IF z$(i TO i+ VAL "2")="END" THEN GO TO VAL "5095"
 5080 NEXT i
 5085 IF no< VAL "11" THEN LET b$(no)=z$:LET c(no)=len:GO TO VAL "5055"
 5090 PRINT # SGN PI;"No more space is available for  ";y$:PAUSE VAL "200":GO TO VAL "5100"
 5095 LET len=len- INT PI:LET b$(no)=z$( TO len):LET c(no)=len:IF no< VAL "10" THEN LET c(no+ SGN PI)= NOT PI
 5100 LET w$(n+def)=y$:LET c$="":LET no= SGN PI:LET len= NOT PI
 5105 IF c(no)= NOT PI THEN GO TO VAL "5120"
 5110 LET c$=c$+b$(no, TO c(no))+ CHR$ NOT PI:LET len=len+c(no)+ SGN PI
 5115 LET no=no+ SGN PI:IF no< VAL "10" THEN GO TO VAL "5105"
 5120 LET k$(def)=c$:LET l(def)=len
 5125 CLS :PRINT "STORED - ";y$
 5130 PAUSE VAL "200":CLS :RETURN 
 5205 GO SUB VAL "1020":IF t1= SGN PI AND LEN y$< VAL "2" THEN LET err= VAL "6":RETURN 
 5210 LET y$=(y$+"            ")( TO VAL "12")
 5215 IF def= NOT PI THEN LET err= VAL "4":RETURN 
 5220 FOR i=n+ SGN PI TO n+def:IF y$=w$(i) THEN GO TO VAL "5230"
 5225 NEXT i:LET err= VAL "4":RETURN 
 5230 GO SUB VAL "5800"
 5240 GO SUB VAL "5950"
 5250 PRINT # SGN PI;"1:EDIT    2:INSERT    3:DELETE   4:REMOVE  5:RETURN"
 5255 LET z$= INKEY$:IF z$="" THEN GO TO VAL "5255"
 5260 IF z$<"1" OR z$>"5" THEN GO TO VAL "5255"
 5265 LET z= VAL z$:GO TO VAL "5200"+ VAL "100"*z
 5275 GO TO VAL "5235"
 5300 GO SUB VAL "5950":PRINT # SGN PI;"EDIT - Enter the line"
 5305 LET z$= INKEY$:IF z$="" THEN GO TO VAL "5305"
 5310 IF z$<"0" OR z$>"9" THEN GO TO VAL "5305"
 5315 LET lin= VAL z$
 5320 INPUT (lin);" W:"; LINE z$:IF LEN z$> VAL "28" THEN PRINT # SGN PI;" Too long":PAUSE VAL "200":GO TO VAL "5320"
 5325 LET b$(lin+ SGN PI)=z$:LET c(lin+ SGN PI)= LEN z$
 5330 GO TO VAL "5235"
 5400 IF k> VAL "9" THEN PRINT # SGN PI;"No space for another line":PAUSE VAL "200":RETURN 
 5405 GO SUB VAL "5950":PRINT # SGN PI;"INSERT - Enter line number"
 5410 LET z$= INKEY$:IF z$="" THEN GO TO VAL "5410"
 5415 IF z$<"0" AND z$> STR$ (k- SGN PI) THEN GO TO VAL "5410"
 5420 LET ins= VAL z$
 5425 INPUT (ins);" W:"; LINE z$:IF LEN z$> VAL "28" THEN PRINT # SGN PI;"Too Long":PAUSE VAL "200":GO TO VAL "5425"
 5430 LET ins=ins+ SGN PI
 5435 FOR i=k TO ins STEP - SGN PI
 5440 LET b$(i+ SGN PI)=b$(i):LET c(i+ SGN PI)=c(i):NEXT i
 5445 LET b$(ins)=z$:LET c(ins)= LEN z$
 5450 LET k=k+ SGN PI:GO TO VAL "5235"
 5500 LET z$="DELETE ":GO SUB VAL "5950":GO SUB VAL "5900"
 5505 IF a$="N" THEN GO TO VAL "5235"
 5510 GO SUB VAL "5950":PRINT # SGN PI;z$;"- Enter the line number"
 5515 LET z$= INKEY$:IF z$="" THEN GO TO VAL "5515"
 5520 IF z$<"0" OR z$> STR$ (k- SGN PI) THEN GO TO VAL "5915"
 5525 LET k=k- SGN PI
 5530 FOR i= SGN PI+ VAL z$ TO k
 5535 LET b$(i)=b$(i+ SGN PI):LET c(i)=c(i+ SGN PI):NEXT i
 5540 GO TO VAL "5235"
 5600 LET z$="REMOVE ":GO SUB VAL "5950":GO SUB VAL "5900"
 5605 IF a$="N" THEN GO TO VAL "5235"
 5610 LET def=def- SGN PI:IF def= NOT PI OR ed=def+ SGN PI THEN RETURN 
 5620 FOR i=n+ed TO def+n
 5625 LET w$(i)=w$(i+ SGN PI):NEXT i
 5630 FOR i=ed TO def
 5635 LET k$(i)=k$(i+ SGN PI):LET l(i)=l(i+ SGN PI):NEXT i
 5640 CLS :RETURN 
 5700 LET c$="":LET no= NOT PI
 5705 FOR i= SGN PI TO k
 5710 LET c$=c$+b$(i, TO c(i))+ CHR$ NOT PI:LET no=no+c(i)+ SGN PI
 5715 NEXT i
 5720 LET k$(ed)=c$:LET l(ed)=no
 5725 CLS :PRINT "STORED - ";y$:PAUSE VAL "200"
 5730 CLS :RETURN 
 5800 LET ed=i-n:CLS :PRINT "EDIT ";y$''"Please wait"
 5805 LET len= SGN PI:LET k= NOT PI:LET z$=k$(ed, TO l(ed))
 5810 IF len>l(ed) THEN RETURN 
 5815 LET c$="":LET no= NOT PI:LET k=k+ SGN PI
 5820 IF z$(len) <> CHR$ NOT PI THEN LET c$=c$+z$(len):LET no=no+ SGN PI:LET len=len+ SGN PI:GO TO VAL "5820"
 5825 LET len=len+ SGN PI:LET b$(k)=c$:LET c(k)=no:GO TO VAL "5810"
 5830 RETURN 
 5900 PRINT # SGN PI;z$;"- Are you sure? (y/n)"
 5905 LET a$= INKEY$:IF a$="" THEN GO TO VAL "5905"
 5910 IF NOT (a$="Y" OR a$="N") THEN GO TO VAL "5905"
 5915 RETURN 
 5950 CLS :PRINT "EDIT ";y$''
 5955 FOR i= SGN PI TO k
 5960 PRINT i- SGN PI; TAB INT PI;b$(i)'':NEXT i
 5965 RETURN 
 6005 IF count= NOT PI THEN LET p$=z$:LET s1=s
 6010 IF count> NOT PI THEN LET m(count)=comm:LET n(count)=s
 6015 LET comm=i-n:LET z$=k$(comm, TO l(comm))
 6020 LET count=count+ SGN PI
 6025 LET s= NOT PI:GO SUB VAL "2040"
 6030 LET count=count- SGN PI
 6035 IF count> NOT PI THEN LET comm=m(count):LET z$=k$(comm, TO l(comm)):LET s=n(count)
 6040 IF count= NOT PI THEN LET z$=p$:LET s=s1
 6045 IF count< NOT PI THEN LET err= SGN PI
 6050 RETURN 
 8010 GO SUB VAL "1210":IF err= VAL "2" THEN RETURN 
 8020 LET a= INT (RND*a):RETURN 

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