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.
- Lines 1–9: Setup — CLEAR, machine code installation, variable initialization, screen setup
- Lines 10–30: Perspective projection subroutine (GO SUB 20 from line 100)
- Lines 35–140: Main drawing loop — iterates over amplitude values and plots the surface
- Lines 1000–1050: Screen save routine using machine code at address
s(32000) - Lines 2000–2060: Screen recall loop using machine code at address
r(32050) - 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 20lands at line 20, skipping line 10’sGO TO 35), an unconventional but valid BASIC flow trick. - Lines 125–130 contain a latent bug:
sx1andsy1are assigned fromsx/syand 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 aftersx/syare 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=0butqis 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 qto a loop variable that is always positive — another remnant of the interactive version where user input could be negative. CLEAR 31999at line 3 reserves the top of RAM from 32000 upward for the machine code and screen buffer, preventing BASIC from overwriting it.
Content
Image Gallery
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
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.