--- title: "EXP" id: 64605 type: "computer_media" slug: "exp" url: "http://localhost/computer_media/exp/" markdown_url: "http://localhost/computer_media/exp.md" published_at: "2026-03-04T19:48:02+00:00" modified_at: "2026-03-31T08:03:52+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2019/02/20230809-041332.jpg" excerpt: "A fascinating 3D surface plotter that renders Gaussian bell curves with perspective projection and saves multiple rendered frames using hand-POKEd Z80 machine code." 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/" genre: - name: "Demo" slug: "demo" taxonomy: "genre" url: "http://localhost/type/demo/" media_contents: - id: 64507 title: "CATS Library Tape 9" type: "computer_media" url: "http://localhost/computer_media/cats-library-tape-9/" media_type: "Program" download_url: "https://archive.org/download/timex-sinclair-software-archive/EXP%20%28198x%29%28TS2068%29%28US%29%28Program%29.zip" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2026/03/SCR-20260331-eijt.png" media_type_tags: "Demo" --- # EXP This program renders a 3D surface plot of the function z = o·exp(−0.1·(x²+y²)) — a Gaussian bell surface — using perspective projection with configurable viewpoint parameters. It draws four variants of the surface (for o=0 to 3, scaling the amplitude) and saves each rendered screen to a different memory page using Z80 machine code routines POKEd into RAM at addresses 32000 and 32050. The machine code at address 32000 performs a LDIR-based block copy from address 32744 (0x80E8) to 16384 (0x4000), saving the display file, while the routine at 32050 reverses the transfer to restore a saved screen. A two-stage loader (lines 10–40) first loads a CODE block containing the machine code, then loads the BASIC program itself. The perspective projection uses standard rotation matrices with parameters rho, d (distance), phi, theta, and screen center coordinates cx/cy. *** ## Program Analysis ### Program Structure The program is split into two files loaded by a small bootstrap: lines 10–40 load a CODE block (machine code) then chain-load the main BASIC program. The main program’s line numbering is non-sequential by design, using low line numbers (1–9) for initialization, teens and twenties for subroutines, and higher numbers for the main loops and display recall. 1. Lines 1–9: Setup — CLEAR, machine code installation, variable initialization, screen setup 2. Lines 10–30: Perspective projection subroutine (GO SUB 20 from line 100) 3. Lines 35–140: Main drawing loop — iterates over amplitude values and plots the surface 4. Lines 1000–1050: Screen save routine using machine code at address `s` (32000) 5. Lines 2000–2060: Screen recall loop using machine code at address `r` (32050) 6. Lines 9000–9060: Save routines for both CODE and BASIC ### Machine Code Routines Two 12-byte Z80 machine code routines are POKEd into RAM at lines 4–6 (save) and 5–7 (restore). Both use the Z80 block move instruction LDIR (`ED B0`) with BC=6912 (0x1B00), the exact size of the Spectrum display file plus attributes. | Address | Purpose | Source (DE) | Destination (HL) | | --- | --- | --- | --- | | 32000 (s) | Save screen to buffer | 0x80E8 (32968) | 0x4000 (16384) | | 32050 (r) | Restore screen from buffer | 0x4000 (16384) | 0x80E8 (32968) | The DATA bytes decode as: `17,232,128` = LD DE,0x80E8; `33,0,64` = LD HL,0x4000; `1,0,27` = LD BC,0x1B00; `237,176` = LDIR; `201` = RET. Before calling USR, the program patches bytes at offsets +1/+2 (DE low/high) to redirect the copy to one of four 7000-byte page slots computed as `p*7000+26000`. ### Perspective Projection Lines 20–30 implement a full 3D perspective transform. The world coordinates (x, y, z) are rotated using sine/cosine of theta (`t`) and phi, then perspective-divided by the eye distance `ze` scaled by `d`, with the result offset to screen center `cx`=127, `cy`=87. The variable `rho`=50 acts as a Z-axis translation to keep the surface in front of the eye. ### Surface Function and Drawing The surface is defined via `DEF FN z(x)=o*EXP(-.1*(x*x+y*y))` — a Gaussian bell scaled by the outer loop variable `o` (0 to 3). The outer loop over `x` runs from 10 to −10 in steps of −0.1, and the inner loop over `y` from −10 to 10 in integer steps, producing a wireframe by connecting successive projected points with DRAW. A flag variable `fl` tracks whether the previous point was on-screen; when a point goes out of bounds (line 110), `fl` is reset to 0 so the next valid point starts a new stroke with PLOT rather than a misguided DRAW. ### Notable Techniques and Anomalies - Line 10 is used as a subroutine entry point (`GO SUB 20` lands at line 20, skipping line 10’s `GO TO 35`), an unconventional but valid BASIC flow trick. - Lines 125–130 contain a latent bug: `sx1` and `sy1` are assigned from `sx`/`sy` and then immediately used to DRAW a zero-length vector `(sx1-sx, sy1-sy)` = (0,0). The intent was likely to store the previous iteration’s coordinates before updating, but the assignment happens after `sx`/`sy` are already updated for the current point. - Many lines contain `REM`-commented-out alternatives (e.g., INPUT statements, additional loop conditions), indicating iterative development where interactive features were replaced with automatic sequencing. - Line 2004 checks `IF q=0` but `q` is the FOR loop variable running 1 to 4, so this branch can never be taken; it is a vestige of the original interactive INPUT-based design. - Line 2005 applies `ABS q` to a loop variable that is always positive — another remnant of the interactive version where user input could be negative. - `CLEAR 31999` at line 3 reserves the top of RAM from 32000 upward for the machine code and screen buffer, preventing BASIC from overwriting it. ## Source Code ``` 10 PRINT "loading pictures" 20 LOAD ""CODE 25 BEEP 1,33 30 CLS : PRINT "loading program" 40 LOAD "" 1 REM this poqram 6.4 2 REM draws a surface z=f(x,y) 3 CLEAR 31999: LET s=32000: LET r=32050 4 RESTORE : FOR j=0 TO 11: READ a: POKE s+j,a: NEXT j 5 FOR j=0 TO 11: READ a: POKE r+j,a: NEXT j 6 DATA 17,232,128,33,0,64,1,0,27,237,176,201 7 DATA 17,0,64,33,232,128,1,0,27,237,176,201 8 LET p=0 9 PAPER 0: INK 7: BORDER 0: CLS : CLS : GO TO 2000 10 GO TO 35 20 LET xe=-x*s1+y*c1: LET ye=-x*c1*c2-y*s1*c2+z*s2: LET ze=-x*s2*c1-y*s2*s1-z*c2+rho 30 LET sx=d*xe/ze+cx: LET sy=cy+d*ye/ze: RETURN 35 FOR o=0 TO 3: REM FOR t=PI/8 TO PI+.1 STEP PI/4 38 LET t=PI/8: REM t=theta 40 LET rho=50: LET d=350: LET phi=1: LET cx=127: LET cy=87: LET s1=SIN (t): LET s2=SIN (phi): LET c1=COS (t): LET c2=COS (phi) 50 DEF FN z(x)=o*EXP (-.1*(x*x+y*y)) 60 REM color 70 FOR x=10 TO -10 STEP -.1 80 LET fl=0 90 FOR y=-10 TO 10 100 LET z=FN z(x): GO SUB 20 110 IF sx<0 OR sx>255 OR sy<0 OR sy>175 THEN LET fl=0: GO TO 140 120 IF fl=0 THEN LET l=1: PLOT sx,sy 125 LET sx1=sx: LET sy1=sy 130 DRAW (sx1-sx),(sy1-sy) 140 NEXT y: NEXT x 1000 LET p=p+1: REM INPUT "Stor as page no. (1-4) ";p 1010 LET p=INT p: REM IF p<1 OR p>4 THEN GO TO 1000 1020 LET a=p*7000+26000: LET a2=INT (a/256): LET a1=a-a2*256 1030 POKE 32001,a1: POKE 32002,a2 1040 LET n=USR s 1045 IF p=4 THEN GO TO 2000 1046 CLS 1050 NEXT o 2000 FOR q=1 TO 4: REM INPUT "Recall page no. (1-4) ";p 2004 IF q=0 THEN GO TO 2050 2005 LET q=ABS q 2010 LET p=INT p: REM IF p<1 OR p>4 THEN GO TO 2000 2020 LET a=q*7000+26000: LET a2=INT (a/256): LET a1=a-a2*256 2030 POKE 32054,a1: POKE 32055,a2 2040 LET n=USR r 2045 PAUSE 3 2050 NEXT q: REM RETURN 2060 GO TO 2000 9000 SAVE "exp" LINE 0 9005 STOP 9010 SAVE "exp"CODE 32000,30000 9025 BEEP 1,22 9050 SAVE "exp" LINE 0 9060 BEEP 2,33 ```