--- title: "Droppages" id: 64600 type: "computer_media" slug: "droppages" url: "http://localhost/computer_media/droppages/" markdown_url: "http://localhost/computer_media/droppages.md" published_at: "2026-03-04T19:44:51+00:00" modified_at: "2026-03-30T21:41:45+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2026/03/dropages.png" excerpt: "A 3-D surface plotter stores multiple rotated views using hand-coded Z80 machine routines, then plays them back as a smooth animation loop." 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: "James Jones" slug: "james-jones" taxonomy: "indiv" url: "http://localhost/indiv/james-jones/" genre: - name: "Graphics" slug: "graphics" taxonomy: "genre" url: "http://localhost/type/graphics/" media_contents: - id: 64507 title: "CATS Library Tape 9" type: "computer_media" url: "http://localhost/computer_media/cats-library-tape-9/" media_type: "Program" programmers: - name: "James Jones" slug: "james-jones" taxonomy: "indiv" url: "http://localhost/indiv/james-jones/" download_url: "https://archive.org/download/timex-sinclair-software-archive/Droppages%20%28198x%29%28Jones%2C%20James%29%28TS2068%29%28US%29%28Program%29.zip" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2026/03/dropages.png" media_type_tags: "Graphics" --- # Droppages This program renders a three-dimensional surface plot of the function z = 3·e^(−0.1·(x²+y+y)) using a perspective projection, then stores successive views to memory pages and plays them back as a simple animation. The projection routine at lines 20–30 applies a full rotation matrix using precomputed sines and cosines of azimuth angle theta and elevation angle phi, projecting 3-D coordinates to a 2-D screen position centered at (cx, cy). Two 12-byte machine code routines are POKEd into RAM at addresses 32000 and 32050; these use the Z80 LDIR instruction (opcode ED B0) to block-copy the screen buffer (6912 bytes from address 16384) to and from off-screen memory pages, enabling multi-frame storage and retrieval. The program iterates theta in steps of PI/4 from PI/8 to PI, drawing four rotated views and cycling through them in a continuous loop via the recall routine. *** ## Program Analysis ### Program Structure The program is organized into several functional blocks: 1. **Initialization (lines 1–8):** Clears memory above 31999, defines base addresses for two machine code routines, and POKEs them into RAM. 2. **Projection subroutine (lines 20–30):** Applies a 3-D rotation and perspective transform. 3. **Main drawing loop (lines 35–140):** Iterates theta, draws the surface for each viewing angle, and saves the screen to a memory page. 4. **Page-save logic (lines 1000–1050):** Patches the save machine code routine with the destination address and calls it. 5. **Playback loop (lines 2000–2060):** Recalls saved screen pages in sequence with a short pause between frames, then repeats indefinitely. 6. **Save lines (9000–9050):** SAVE statements for backing up the BASIC program and the machine code block. ### Machine Code Routines Two 12-byte Z80 routines are installed at `s=32000` (save) and `r=32050` (recall). Both use the Z80 block-move instruction LDIR (mnemonic opcodes `ED B0`) to copy 6912 bytes (a full Spectrum display file). The DATA statements decode as follows: | Address | Bytes | Z80 Mnemonics | Purpose | | --- | --- | --- | --- | | 32000 | 17, a1, a2 | LD DE, | Destination address (patched at runtime) | | 32003 | 33, 0, 64 | LD HL, 16384 | Source = screen start | | 32006 | 1, 0, 27 | LD BC, 6912 | Byte count (27×256 = 6912) | | 32009 | 237, 176 | LDIR | Block copy | | 32011 | 201 | RET | Return to BASIC | The recall routine at 32050 mirrors this with source and destination swapped: DE is loaded with 16384 (the screen) and HL with the page address, so the off-screen data is written back to the display. Addresses are patched dynamically by poking the low and high bytes into `32001/32002` (save) and `32054/32055` (recall) before each `USR` call. ### Perspective Projection The subroutine at lines 20–30 implements a standard perspective camera transform. The 3-D point (x, y, z) is first rotated using precomputed values `s1=SIN(t)`, `c1=COS(t)`, `s2=SIN(phi)`, `c2=COS(phi)`, producing camera-space coordinates `xe`, `ye`, `ze`. The offset `rho=50` shifts the scene along the camera’s z-axis. Screen coordinates are then computed as `sx = d*xe/ze + cx` and `sy = cy + d*ye/ze` with focal length `d=350` and screen center `(cx, cy) = (127, 87)`. ### Surface Function and Drawing The surface is defined at line 50 as `DEF FN z(x) = 3*EXP(-.1*(x*x+y+y))`. Note that the exponent argument contains `y+y` (i.e., 2y) rather than the expected `y*y`, which is almost certainly a typo — the intended function is the Gaussian bell surface z = 3·e^(−0.1·(x²+y²)). The outer loop iterates x from 10 to −10 in steps of −0.1 and the inner loop iterates integer y from −10 to 10. The variable `fl` acts as a “pen down” flag: when a valid screen point is found after an out-of-bounds gap, PLOT is used to begin a new stroke; otherwise DRAW connects to the previous point using the stored values `sx1`, `sy1`. However, lines 125–130 overwrite `sx/sy` with themselves before DRAW, meaning the DRAW always has delta arguments of zero — this is a bug; the previous screen coordinates should have been saved before recalculating `sx` and `sy` for the new point. ### Memory Page Addressing Each of the four screen pages is stored at address `a = p*7000 + 26000`, giving addresses 33000, 40000, 47000, and 54000 for pages 1–4. These regions lie well above the BASIC/display area but must fit within a 64 KB address space; page 4 at 54000 + 6912 = 60912 is within bounds. The low and high byte decomposition uses integer arithmetic: `a2 = INT(a/256)`, `a1 = a - a2*256`, avoiding the BASIC `INT`/`MOD` combination. ### Notable Techniques and Anomalies - Commented-out `INPUT` and `IF` statements at lines 1000–2050 suggest the program was originally interactive but was simplified to run automatically with `p` auto-incrementing and `q` driven by a FOR loop. - `LET l=1` at line 120 appears to set a variable that is never subsequently used — likely a remnant of a color or attribute selection feature. - The `CLEAR 31999` at line 3 sets RAMTOP just below the machine code, protecting it from BASIC’s memory manager. - The playback loop (lines 2000–2060) runs indefinitely via `GO TO 2000` after exhausting the FOR loop, creating a continuous slideshow. - The REM at the end of line 2050 contains `RETURN`, suggesting an earlier design where the recall block was a subroutine. - Two separate `SAVE "droppages" LINE 0` statements exist at lines 9000 and 9050, which is redundant. ## Source Code ``` 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 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 t=PI/8 TO PI+.1 STEP PI/4 38 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)=3*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 t 2000 FOR q=1 TO 4: REM INPUT "Recall page no. (1-4) ";p 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 22 2050 NEXT q: REM RETURN 2060 GO TO 2000 9000 SAVE "droppages" LINE 0 9005 STOP 9010 SAVE "droppages"CODE 32000,30000 9050 SAVE "droppages" LINE 0 ```