Mandelbrot Generator

Developer(s): Gerald W. Goegelein
Date: 1987
Type: Program
Platform(s): TS 2068

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:

LinesPurpose
1–20REM headers and initial jump to setup at line 500
100–310Core Mandelbrot iteration engine (called as a subroutine from line 660)
400–450Interrupt handler: displays progress, waits for ENTER or M-for-menu
500–660Setup: prompts for SW corner coordinates and side length, initializes array
1000–1100Main menu: dispatches to numbered options
3000Resume run (menu option 3)
4000–4120Preview: boundary-detection scatter plot and hard-copy (menu option 4)
5000–5150Set character range limits for output mapping (menu option 5)
6005–6180Final hard-copy printout using UDG character mapping (menu option 6)
7000–7140Save and load P$ array plus control variables to/from tape
8998–9002Master 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 100 at line 660 and by GO TO 100 at 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 s in LPRINT s while all other variable references use uppercase S. 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 GAP computation 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 explicit GO TO jumps at lines 1072–1074 before the dispatch, since MENU*7 = 7000 and MENU*8 = 8000 — line 8000 does not exist. The explicit handling avoids a crash.

Content

Appears On

Related Products

Related Articles

Related Content

Image Gallery

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.

Scroll to Top