This program computes and renders the Mandelbrot set on a 160×160 grid, storing iteration counts as characters in a two-dimensional string array `P$` dimensioned at 161×160. The core iteration loop (lines 200–260) performs up to 128 iterations of the standard complex squaring formula, testing for escape when the squared magnitude exceeds 4. Users enter the southwest corner coordinates and side length of the complex plane region to explore, and the computation can be interrupted mid-run and resumed from the exact row and column where it left off. A main menu (line 1000) offers options to start a new run, save and verify the full program state including UDG data, preview the result with a boundary-detection plot routine, generate a hard-copy printout with character mapping to UDGs, and save or load the `P$` array along with control variables to tape. The save/load system stores up to ten numeric variables — including date fields for month, day, and year — in a separate `V()` array alongside the main data array, enabling interrupted sessions to be resumed later.
Program Analysis
Program Structure
The program is organized into well-separated functional blocks, navigated primarily through a central menu at line 1000:
| Lines | Purpose |
|---|---|
| 1–20 | REM headers and initial jump to setup at line 500 |
| 100–310 | Core Mandelbrot iteration engine (called as a subroutine from line 660) |
| 400–450 | Interrupt handler: displays progress, waits for ENTER or M-for-menu |
| 500–660 | Setup: prompts for SW corner coordinates and side length, initializes array |
| 1000–1100 | Main menu: dispatches to numbered options |
| 3000 | Resume run (menu option 3) |
| 4000–4120 | Preview: boundary-detection scatter plot and hard-copy (menu option 4) |
| 5000–5150 | Set character range limits for output mapping (menu option 5) |
| 6005–6180 | Final hard-copy printout using UDG character mapping (menu option 6) |
| 7000–7140 | Save and load P$ array plus control variables to/from tape |
| 8998–9002 | Master SAVE block and auto-run loader |
Mandelbrot Iteration Core
The iteration loop runs at lines 200–260. For each grid point (M, N), the real coordinate CR and imaginary coordinate CI are computed from the user-supplied origin (AC, BC) and step size GAP (= S/192). The standard recurrence Z → Z² + C is computed in lines 200–220 using a temporary variable R to avoid overwriting ZR before ZI is updated. Escape is detected at line 240 when the squared modulus SZ exceeds 4. The iteration count C is capped at 127 (line 260: IF C<128) and stored as a character code directly into P$(M+1, N+1), using BASIC’s CHR$ to pack the byte.
The outer loop variable M represents rows (imaginary axis) and N columns (real axis), both running 0–159 for a 160×160 grid. The grid is offset by 1 in the string array indices to accommodate 1-based string addressing.
Interruptible Computation and State Preservation
Line 140 checks INKEY$ for ENTER on every inner column iteration, making interruption responsive. When triggered, line 400 displays the current row M and column N, then waits at lines 420–424 for a keypress. The computation can resume at exactly the interrupted cell because FOR M=M TO 159 (line 120) and FOR N=N TO 159 (line 130) re-enter the FOR loops using the current values of M and N. This is a deliberate exploitation of BASIC’s behavior when a FOR variable already holds a value.
Menu Dispatch Technique
The menu at lines 1030–1100 uses a compact dispatch idiom at line 1090:
GO SUB MENU*VAL Z$
Since MENU is set to 1000, pressing keys “3” through “6” causes jumps to GO SUB 3000, GO SUB 4000, GO SUB 5000, and GO SUB 6000 respectively — each a real subroutine entry point. Keys “1” and “2” are handled explicitly before this line, and the guard at line 1080 rejects out-of-range keys, so the multiplication dispatch is always valid when reached. The use of VAL Z$ rather than a direct numeric conversion reflects a memory-saving style common in Spectrum BASIC.
Array Storage Strategy
The main data structure is P$, dimensioned at line 630 as DIM P$(161,160). Each element holds a single character whose ASCII code encodes the iteration count (0–127) or escape marker. This is an efficient way to store a large integer matrix in BASIC: a numeric array of the same size would consume far more memory due to the 5-byte floating-point format per element. At 161×160 bytes, P$ occupies approximately 25,760 bytes of RAM.
Save and Load System
Two complementary tape routines are provided. The quick save at line 1070 saves the whole program with SAVE "MANDEL.20" LINE 1000 plus the UDG character set. The array-only save at lines 7010–7030 stores ten numeric control variables in V() (including month, day, year date fields in V(8)–V(10), and run parameters in V(1)–V(7)) followed by P$(), both verified after saving. The loader at lines 7110–7130 restores all variables and prints the saved date before returning to the menu.
Preview and Hard-Copy Output
The preview routine (lines 4000–4120) scans the P$ array for boundary pixels — cells where the iteration count differs by exactly 1 from either the left or upper neighbor, or equals 128 (the maximum). Boundary points are plotted using PLOT P+40, O, offsetting horizontally to center the 160-column output on the 256-pixel screen. After plotting, a COPY command sends the screen to the printer.
The full hard-copy routine (lines 6005–6180) maps iteration-count ranges to UDG characters using the limits array A(2, LEN F$) set up in the limits subroutine (lines 5000–5150). The output is printed in five horizontal passes (loop variable I 0–4), each 32 characters wide, covering the full 160-column grid. An error message is triggered at line 6080 if any iteration count falls outside all defined ranges.
Notable Anomalies
- Line 100 is targeted by
GO SUB 100at line 660 and byGO TO 100at line 310, but line 100 does not exist — execution falls through to line 102. This is intentional: the missing line acts as a no-op entry point. - Line 580 uses lowercase
sinLPRINT swhile all other variable references use uppercaseS. In Spectrum BASIC, variable names are case-sensitive in that lowercase and uppercase are treated as the same variable for numeric scalars, so this is not a functional bug but is inconsistent style. - The
GAPcomputation at line 620 divides by 192 rather than 160, meaning the displayed region is slightly wider in the imaginary direction than the 160-row grid would suggest; this may be a deliberate aspect-ratio correction or an inadvertent mismatch. - Line 1090 uses
GO SUB MENU*VAL Z$, but menu option 7 (save array) and option 8 (load array) are handled with explicitGO TOjumps at lines 1072–1074 before the dispatch, sinceMENU*7 = 7000andMENU*8 = 8000— line 8000 does not exist. The explicit handling avoids a crash.
Content
Source Code
1 REM 2/22/87 07;59AM GWG MANDEL.20B F. NACHBAUR, MARCH 1986 PAGE 12,SYNCWARE NEW, VOL 3 NUMBER 5, MAY-JUNE, 1986
2 REM GOTO 8998 to save this code. Upon LOADing it will look for a UDG file and then autoRUN
10 LET MENU=1000
20 GO TO 500
102 IF M<160 AND N<160 THEN GO TO 120
104 PRINT AT 20,8; INK 7; PAPER 3; FLASH 1;" ALL DONE "
106 FOR U=1 TO 100: BEEP .025,40: BEEP .015,50: NEXT U
110 GO TO MENU+10
120 FOR M=M TO 159
130 FOR N=N TO 159
140 IF INKEY$=CHR$ 13 THEN GO TO 400
150 LET CR=N*GAP+AC
160 LET CI=M*GAP+BC
170 LET ZR=0
180 LET ZI=0
190 LET C=0
200 LET R=ZR*ZR-ZI*ZI+CR
210 LET ZI=2*ZR*ZI+CI
220 LET ZR=R
230 LET SZ=ZR*ZR+ZI*ZI
240 IF SZ>4 THEN GO TO 270
250 LET C=C+1
260 IF C<128 THEN GO TO 200
270 LET P$(M+1,N+1)=CHR$ C
280 NEXT N
290 LET N=0
300 NEXT M
310 GO TO 100
400 CLS
410 PRINT INK 2;"WORKING ON:",,, INK 6;,"ROW ";M,,"COL ";N,,,,; INK 5;"""M"" FOR MENU",,"""ENTER"" TO CONT "
412 PRINT INK 1; BRIGHT 1;AT 19,0;"AC=";AC;,"BC=";BC,;" S=";S,,
420 IF INKEY$=CHR$ 13 THEN GO TO 420
422 PAUSE 2
424 PAUSE 0
430 IF INKEY$="M" OR INKEY$="m" THEN GO TO MENU
440 CLS
450 GO TO 150
500 CLS
510 BORDER 0: PAPER 0: INK 4: CLS
520 PRINT "INPUT CO-ORDS OF SW CORNER OF PROPOSED MANDELBROT PLANE:",,,"A-CORNER (REAL PART)=";: LPRINT "INPUT CO-ORDS OF SW CORNER OF PROPOSED MANDELBROT PLANE:",,,"A-CORNER (REAL PART)=";
530 INPUT AC
540 PRINT AC,,,"B-CORNER (IMAG PART)=";: LPRINT AC,,,"B-CORNER (IMAG PART)=";
550 INPUT BC
560 PRINT BC,,," LENGTH OF EACH SIDE=";: LPRINT BC,,," LENGTH OF EACH SIDE=";
570 INPUT S
580 PRINT S: LPRINT s
590 PRINT INK 6; FLASH 1;AT 12,13;"OK?"
600 INPUT Y$
610 IF Y$<>"Y" AND Y$<>"y" THEN RUN
620 CLS : LET GAP=S/192
630 DIM P$(161,160)
640 LET M=0
650 LET N=0
660 GO SUB 100
1000 BORDER 0: PAPER 0: INK 4: CLS
1020 PRINT PAPER 2; INK 7;AT 0,6;" MAIN MENU "; PAPER 0; INK 5;,,,"1: START A NEW RUN ",; INK 6;"2: SAVE CURRENT PROGRAM STATE",; INK 5;"3: RESUME CURRENT RUN ",; INK 6;"4: PREVIEW",,; INK 5;"5: SET LIMITS",,; INK 6;"6: FINAL HARD COPY "
1022 PRINT INK 5;"7: SAVE P$(ARRAY)",; INK 6;"8: LOAD P$(ARRAY)"
1030 LET Z$=INKEY$
1040 IF Z$="" THEN GO TO 1030
1060 IF Z$="1" THEN RUN
1070 IF Z$="2" THEN SAVE "MANDEL.20" LINE 1000: SAVE "UDG"CODE 65368,167: PRINT AT 19,10;"VERIFY": VERIFY "MANDEL.20": VERIFY "UDG"CODE 65368
1072 IF Z$="7" THEN GO TO 7010
1074 IF Z$="8" THEN GO TO 7100
1080 IF Z$<"3" OR Z$>"8" THEN GO TO MENU
1090 GO SUB MENU*VAL Z$
1100 GO TO MENU
3000 CLS : GO TO 100
4000 BORDER 0: PAPER 0: INK 4: CLS
4010 FOR O=2 TO 160
4020 FOR P=2 TO 160
4030 IF ABS (CODE P$(O,P)-CODE P$(O,P-1))=1 OR ABS (CODE P$(O,P)-CODE P$(O-1,P))=1 OR CODE P$(O,P)=128 THEN GO TO 4130
4040 NEXT P
4050 NEXT O
4060 COPY
4070 LPRINT "AC=";AC,"BC=";BC," S=";S,"M=";M,"N=";N
4080 LPRINT : LPRINT : LPRINT
4120 PAUSE 0: RETURN
4130 INK 4: PLOT P+40,O: GO TO 4040
5000 LET F$=" \a\b\c\d\e\f\g\h\i": REM use (SPACE) followed by graphic A B C D E F G H I
5010 DIM A(2,LEN F$)
5020 CLS
5030 PRINT BRIGHT 1; INK 3;" INPUT MIN/MAX FOR EACH CHAR:",,,"CHARACTER",; BRIGHT 0; INK 7;"MIN MAX",,,
5040 FOR I=1 TO LEN F$
5050 PRINT F$(I);
5060 IF F$(I)=" " THEN PRINT "(SPACE)";
5070 INPUT A(1,I)
5080 PRINT ,A(1,I);TAB 22;
5090 INPUT A(2,I)
5100 PRINT A(2,I)
5110 NEXT I
5120 PRINT AT 18,14; INK 6; FLASH 1;"OK?"
5130 INPUT Y$
5140 IF Y$="Y" OR Y$="y" THEN RETURN
5150 GO TO 5000
6005 FOR I=0 TO 4
6010 FOR N=160 TO 1 STEP -1
6020 FOR J=0 TO 31
6030 LET K=32*I+J
6040 LET A=CODE P$(N,K+1)
6050 FOR L=1 TO LEN F$
6060 IF A>=A(1,L) AND A<=A(2,L) THEN GO TO 6100
6070 NEXT L
6080 PRINT BRIGHT 1; INK 2; FLASH 1;AT 20,9;"RANGING ERROR"
6090 GO TO 5130
6100 LPRINT F$(L);
6110 NEXT J
6120 NEXT N
6130 LPRINT
6140 LPRINT "--------------------------------"
6150 LPRINT
6160 NEXT I
6170 LPRINT "AC=";AC;" BC=";BC;" S=";S
6180 RETURN
7000 REM SAVE DATE, PROGRAM CONTROL VARIABLES AND MAIN ARRAY(P$)
7010 DIM V(10): CLS : PRINT INK 3;AT 10,0;"ENTER THE MONTH;","(USE NUMBER 1-12)": INPUT V(8): CLS : PRINT INK 3;AT 10,0;"ENTER THE DAY (1-31)": INPUT V(9): CLS : PRINT INK 3;AT 10,0;"ENTER THE YEAR ( 2 DIGIT NUMBER SUCH AS ""87""": INPUT V(10)
7030 LET V(1)=M: LET V(2)=N: LET V(3)=S: LET V(4)=MENU: LET V(5)=GAP: LET V(6)=AC: LET V(7)=BC: CLS : SAVE "NARRAY" DATA V(): SAVE "CARRAY" DATA P$(): PRINT "VERIFY": VERIFY "NARRAY": VERIFY "CARRAY"
7040 GO TO 1000
7100 REM LOAD DATE, PROGRAM VARIABLES AND MAIN ARRAY (P$)
7110 LOAD "NARRAY" DATA V(): LOAD "CARRAY" DATA P$()
7120 CLS : BORDER 0: PAPER 0: INK 4: PRINT INK 3;AT 11,0;"LOAD COMPLETE",,"DATE ARRAY WAS SAVED: "; INK 7;V(8);"/";V(9);"/";V(10)
7130 LET M=V(1): LET N=V(2): LET S=V(3): LET MENU=V(4): LET GAP=V(5): LET AC=V(6): LET BC=V(7)
7140 GO TO 1020
8998 CLEAR
8999 SAVE "MANDEL.20" LINE 9000: SAVE "UDG"CODE 65368,167: VERIFY "MANDEL.20": VERIFY "UDG"CODE 65368: STOP
9000 LOAD "UDG"CODE
9002 BORDER 0: PAPER 0: INK 4: RUN
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

