--- title: "Mandelbrot Generator" id: 54046 type: "computer_media" slug: "mandelbrot-article" url: "http://localhost/computer_media/mandelbrot-article/" markdown_url: "http://localhost/computer_media/mandelbrot-article.md" published_at: "2024-05-25T15:26:01+00:00" modified_at: "2026-03-31T18:14:36+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/05/mandel-output.png" excerpt: "Explore the Mandelbrot set with interruptible 160×160 iteration, tape-based session save/resume, hard-copy output, and UDG-mapped character rendering." category: - name: "Archived Media" slug: "archived-media" taxonomy: "category" url: "http://localhost/category/archived-media/" post_tag: - name: "Downloadable" slug: "downloadable" taxonomy: "post_tag" url: "http://localhost/tag/downloadable/" - name: "TS 2068" slug: "ts2068" taxonomy: "post_tag" url: "http://localhost/tag/ts2068/" model: - name: "Timex/Sinclair 2068" slug: "ts-2068" taxonomy: "model" url: "http://localhost/model/ts-2068/" indiv: - name: "Gerald W. Goegelein" slug: "gerald-w-goegelein" taxonomy: "indiv" url: "http://localhost/indiv/gerald-w-goegelein/" genre: - name: "Demo" slug: "demo" taxonomy: "genre" url: "http://localhost/type/demo/" - name: "Mathematics" slug: "mathematics" taxonomy: "genre" url: "http://localhost/type/mathematics/" media_type: "Program" programmers: - name: "Gerald W. Goegelein" slug: "gerald-w-goegelein" taxonomy: "indiv" url: "http://localhost/indiv/gerald-w-goegelein/" download_url: "https://archive.org/download/timex-sinclair-software-archive/Mandelbrot%20Article%20%281987%29%28Goegelein%2C%20George%29%28US%29%28TS2068%29%28Article%20and%20Programs%29.zip" mediadate: "1987" images: - url: "http://localhost/wp-content/uploads/2024/05/mandel-output.png" - url: "http://localhost/wp-content/uploads/2024/05/mandel-menu.png" related_content: - id: 54040 title: "Mandelplot for the TS 2068" type: "document" url: "http://localhost/document/mandelplot-for-the-ts-2068/" media_type_tags: "Demo, Mathematics" --- # Mandelbrot Generator 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 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. ## 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 ```