--- title: "Laurie" id: 54985 type: "computer_media" slug: "laurie" url: "http://localhost/computer_media/laurie/" markdown_url: "http://localhost/computer_media/laurie.md" published_at: "2024-06-10T01:44:45+00:00" modified_at: "2026-03-30T21:43:27+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2019/02/20230809-041332.jpg" excerpt: "Draw joystick-controlled pixel art frame by frame, saving each one as raw memory code — this animation tool even embeds its own machine code bootstrap." 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: "Animation" slug: "animation" taxonomy: "genre" url: "http://localhost/type/animation/" - name: "Graphics" slug: "graphics" taxonomy: "genre" url: "http://localhost/type/graphics/" media_contents: - id: 54964 title: "ISTUG Public Domain Library 6" type: "computer_media" url: "http://localhost/computer_media/istug-public-domain-library-6/" media_type: "Program" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2024/06/SCR-20260329-ntjv.png" media_type_tags: "Animation, Graphics" --- # Laurie Laurie is an interactive frame-by-frame animation drawing tool that uses the joystick (STICK function) to move a cursor around the screen and plot pixels or filled squares. The user can toggle drawing on or off, adjust the brush block size with ‘+’/’-‘ keys, and save completed frames as raw CODE at address 16384 with a length of 2048 bytes. A small machine code routine is poked into RAM at address 65000 and called via RANDOMIZE USR to copy display memory between two frame buffers located at 32000 and 65005/65002 address parameters. Line 410 bootstraps the machine code by parsing a packed decimal string into 12 bytes starting at address 65000, then re-runs the program. *** ## Program Analysis ### Program Structure The program is organized into several distinct phases: 1. **Initialization / intro (lines 10–80):** Clears memory above address 32000, displays a two-screen instruction sequence with `BEEP`/`PAUSE 0` keypresses between pages. 2. **Drawing loop setup (lines 90–130):** Draws a reference line, seeds cursor position at `x=125, y=165`, sets draw flag `a=1` and block size `b=0`. 3. **Machine code call (lines 140–140):** Calls the routine at 65000 via `RANDOMIZE USR 65000` to perform a frame-buffer operation using parameters POKEd at 65002 and 65005. 4. **Main drawing loop (lines 150–330):** Reads joystick and keyboard, moves cursor, and plots pixels or inverse-video squares. 5. **Frame save (lines 340–400):** Copies the frame buffer back via the machine code, saves the display as `CODE 16384,2048`, then loops back to drawing. 6. **Bootstrap (line 410):** Installs the machine code routine from a packed decimal string, then re-runs the program. ### Machine Code Bootstrap Line 410 is the entry point when the program is first loaded. It clears memory to 65000, then decodes a 12-byte machine code routine from the string literal `"033000125017000064001000008237176201"`. The string is consumed three characters at a time using `VAL a$( TO PI)` — since `PI` truncates to 3 in integer contexts here — extracting each byte value and `POKE`ing it sequentially from address 65000. After installation the program re-runs from line 10 with `RUN`. The 12 bytes decode as follows: | Address | Byte (decimal) | Z80 Mnemonic (approximate) | | --- | --- | --- | | 65000 | 033 | `LD HL, nn` (low byte follows) | | 65001 | 000 | (low byte of address) | | 65002 | 125 | (high byte — POKEd to 125=32000 high) | | 65003 | 017 | `LD DE, nn` | | 65004 | 000 | (low byte) | | 65005 | 064 | (high byte — POKEd to 64=16384 high) | | 65006 | 001 | `LD BC, nn` | | 65007 | 000 | (low byte = 0) | | 65008 | 008 | (high byte = 8, so BC=2048) | | 65009 | 237 | `ED` prefix | | 65010 | 176 | `LDIR` — block copy HL→DE, BC bytes | | 65011 | 201 | `RET` | This is a simple Z80 `LDIR` block copy. By changing the high bytes at 65002 and 65005 before calling the routine, the program can copy either from the display (16384) to the frame buffer (32000) or vice versa, effectively implementing a double-buffer flip for animation. ### STICK-Based Joystick Input Lines 210–280 use the TS2068 `STICK` function (rendered as `|`) to read joystick port 1. The return values follow the standard numeric encoding: 1=up, 2=down, 4=left, 8=right, with diagonals as sums (9=up-right, 10=down-right, 5=up-left, 6=down-left). Line 210 uses `|(1,1)=0` to detect the neutral (no movement) state and loop back to line 150 without moving or drawing. Line 220 checks `|(2,1)=1` (fire button) and inverts the draw flag `a`, toggling between draw and erase mode. ### Drawing Logic and Block Size The cursor is represented by a single pixel plotted with `PLOT OVER 1` (XOR mode) twice in succession at line 150, which acts as a visible cursor that does not permanently alter the canvas. The block size variable `b` is adjusted by pressing ‘k’ (increase) or ‘j’ (decrease). When drawing (`a=1`), lines 300–310 plot the anchor pixel and then draw a square of side `b` using four `DRAW` calls. When erasing (`a<>1`), lines 290 and 320 use `INVERSE 1` to erase the same square. ### Key BASIC Idioms - `LET x=x+(|(1,1)=8)-(|(1,1)=4)` — Boolean expression used as ±1 delta, a compact idiom replacing IF/THEN chains for cardinal movement. - `VAL a$( TO PI)` — Using the constant `PI` (≈3.14159) as a slice index; BASIC truncates it to 3, extracting three-digit substrings for byte decoding. - `RANDOMIZE USR 65000` — Standard idiom for calling a machine code subroutine; the return value from Z80 `RET` is discarded by `RANDOMIZE`. - `PAUSE 0` at lines 50 and 80 — Waits indefinitely until any key is pressed, gating the instruction screens. ### Bugs and Anomalies - Line 320 executes unconditionally when `a<>1`, but line 290 already branches with `GO TO 320`, meaning line 320’s `IF a<>1` guard is redundant — though harmless. - Cursor boundary checking is absent; moving beyond `x=0`, `x=255`, `y=0`, or `y=175` will cause an “Out of screen” error from `PLOT`/`DRAW`. - The ‘+’/’-‘ keys mentioned in the instructions (line 60) are not actually tested in the main loop; instead lines 190–200 use ‘k’ and ‘j’. This is a discrepancy between the documentation and the code. - Line 340 uses `PRINT AT 10,0; FLASH 1; BRIGHT 1;" "` with a single space — it prints only one flashing space, likely intended as a visual indicator but has minimal visible effect. - The `SAVE "p"CODE 16384,2048` at line 380 saves only the first third of the display file (2048 bytes covers the pixel area but not the attribute area, which starts at 22528). ## Source Code ``` 10 CLEAR 125*256: REM 32000 20 REM 32000 TO 34048=oldfram 30 REM 65000=prog 40 CLS : PRINT "This is Laurie"''"Laurie is used to facilitate theanimation process."'"You simply move the cursor with the stick and draw."''"To save a frame, press the BREAK key." 50 BEEP .01,15: PAUSE 0 60 PRINT '"To erase, press '0'"'"To continue drawing, press '9'"''"To increase/decrease block size,press '+' or '-'." 70 PRINT "Once a frame is completed, and anew one is begun, the previous frame will appear."'"You are left to erase this." 80 BEEP .05,15: PAUSE 0 90 CLS : REM draw 100 PLOT 0,175-64: DRAW 255,0: LET x=125: LET y=165 110 LET a=1 120 LET b=0 130 POKE 65002,125: POKE 65005,64 140 RANDOMIZE USR 65000 150 PLOT OVER 1,x,y: PLOT OVER 1,x,y 160 IF INKEY$=" " THEN GO TO 340 170 IF INKEY$="0" THEN LET a=0 180 IF INKEY$="9" THEN LET a=1 190 IF INKEY$="k" THEN LET b=b+1 200 IF INKEY$="j" THEN LET b=b-1: IF b<0 THEN LET b=0 210 IF |(1,1)=0 THEN GO TO 150 220 IF |(2,1)=1 THEN LET a=-a 230 LET x=x+(|(1,1)=8)-(|(1,1)=4) 240 LET y=y+(|(1,1)=1)-(|(1,1)=2) 250 IF |(1,1)=9 THEN LET x=x+1: LET y=y+1 260 IF |(1,1)=10 THEN LET x=x+1: LET y=y-1 270 IF |(1,1)=5 THEN LET x=x-1: LET y=y+1 280 IF |(1,1)=6 THEN LET x=x-1: LET y=y-1 290 IF a<>1 THEN PLOT INVERSE 1,x,y: GO TO 320 300 PLOT x,y 310 IF a=1 THEN DRAW 0,b: DRAW b,0: DRAW 0,-b: DRAW -b,0 320 IF a<>1 THEN INVERSE 1: DRAW 0,b: DRAW b,0: DRAW 0,-b: DRAW -b,0: INVERSE 0 330 GO TO 150 340 PRINT AT 10,0; FLASH 1; BRIGHT 1;" " 350 POKE 65002,64: POKE 65005,125 360 RANDOMIZE USR 65000 370 PRINT AT 10,1;"The drawing is copied."'"Now it will be saved under ""p""." 380 SAVE "p"CODE 16384,2048 390 PRINT AT 20,0;"Remember CODE not SCREEN$ " 400 BEEP .05,15: PAUSE 0: GO TO 90 410 CLEAR 65000: LET a$="033000125017000064001000008237176201": FOR f=65000 TO 65011: POKE f,VAL a$( TO PI): LET a$=a$(4 TO ): NEXT f: RUN 420 SAVE "Laurie" LINE 410 ```