--- title: "L & K" id: 54981 type: "computer_media" slug: "l-k" url: "http://localhost/computer_media/l-k/" markdown_url: "http://localhost/computer_media/l-k.md" published_at: "2024-06-10T01:44:45+00:00" modified_at: "2026-03-30T21:43:26+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2019/02/20230809-041332.jpg" excerpt: "Load multiple full screens into memory and play them back as a slideshow, driven by a compact Z80 machine code routine installed at runtime." 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: "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-nrmw.png" media_type_tags: "Graphics" --- # L & K This program implements a multi-screen slideshow loader and display system, storing up to 27 screens in memory between addresses 29952 and 63744 at 1024-byte intervals. It uses embedded Z80 machine code, self-installed at address 65000 via a POKE loop in line 330, to perform fast block memory copies between the loaded screen buffers and the display file at 16384 (0x4000). The machine code routine is stored as a decimal-encoded string, with each opcode represented as a three-digit value parsed using VAL and a sliding-window string technique. A user-selectable speed parameter controls the PAUSE duration between frame transitions during playback, creating a simple animation or slideshow effect. *** ## Program Analysis ### Program Structure The program is organized as a menu-driven loader with four named entry points, whose line numbers are stored in variables for direct `GO TO a` dispatch after an `INPUT a` at line 70. The four functional sections are: 1. **loadfile (line 90)** — loads an arbitrary CODE block and returns to menu. 2. **loadscreen / loadscr (line 130)** — loads a SCREEN$ into successive 1024-byte-stepped buffer slots starting at 29952. 3. **display (line 220)** — plays back all stored screens as a slideshow at a user-defined speed. 4. **Machine code installer (line 330)** — installs the Z80 routine and chains to `RUN`. The program must be bootstrapped by running line 330 first (which ends with `RUN`, restarting from line 10) so the machine code is in place before any display or load operations are attempted. ### Memory Layout | Address | Purpose | | --- | --- | | 16384 (0x4000) | Display file (6144 bytes) | | 22528 (0x5800) | Attribute file (768 bytes) | | 29952 (0x7500) | First screen buffer slot | | 29952 + n×1024 | Successive screen buffer slots | | 63744 (0xF900) | Last valid buffer slot | | 65000 (0xFDE8) | Machine code routine | Each slot is 1024 bytes, which is notably *not* a full Spectrum screen (6912 bytes). This means only the first 1024 bytes of each SCREEN$ load are stored per slot — likely just the beginning of the pixel area. The `CLEAR 29952` at line 10 sets RAMTOP to protect high memory but leaves the screen buffer region accessible. The guard `IF pp>63744` at line 150 prevents overwriting the machine code at 65000. ### Machine Code Installer Line 330 is the bootstrap routine. It performs `CLEAR 65000` to protect the target area, then decodes a 75-character decimal string (25 three-digit groups) into 25 bytes POKEd from address 65000 onward. The sliding-window idiom `VAL a$(TO PI)` extracts the first three characters as a number, and `LET a$=a$(4 TO)` advances the string. `PI` truncates to 3 in this context (since `TO PI` means `TO 3` after integer truncation by `VAL`‘s argument slicing — actually the slice `a$(TO PI)` takes characters 1 to INT(PI)=3). ### Z80 Machine Code Analysis The encoded decimal string is: `033000064017000121062032001032000237176006023035005194247253061200195240253` Decoded bytes (hex): `21 00 40 11 00 79 3E 20 01 20 00 ED B0 06 17 23 05 C2 F7 FD 3D C8 C3 F0 FD` This disassembles as two interleaved routines whose entry points are patched by the BASIC via POKEs before each `RANDOMIZE USR 65000` call: - **Load mode (line 170–200):** POKEs set up a LDIR-based copy *from* a buffer slot *to* display memory at 0x4000. - **Display mode (line 240–300):** POKEs reverse the source/destination to copy *from* display memory *to* a buffer slot, or vice versa for playback. - The routine at offset +15 (`06 17 23 05 C2 F7 FD`) is a counted loop with `DJNZ`-style logic using `DEC B / JP NZ` targeting back into the routine. - The tail `3D C8 C3 F0 FD` — `DEC A / RET Z / JP 0xFDF0` — provides a loop-exit with return, jumping back to offset +16 (0xFDF0 = 65008) until the counter reaches zero. The high-byte of the source/destination addresses is the only value changed between calls (via `POKE 65005,pp/256` or `POKE 65005,f/256`), making this a compact parameterized block-copy trampoline. ### Key BASIC Idioms - **Named dispatch:** Variables `loadfile`, `loadscr`, `loadscreen`, and `display` hold line numbers; `GO TO a` after `INPUT a` routes execution without a lookup table. - **VAL with PI truncation:**`VAL a$(TO PI)` exploits the fact that `PI≈3.14…` truncates to index 3 in a string slice, extracting exactly three characters per iteration. - **Self-modifying machine code via POKE:** Rather than multiple routines, a single code block is reconfigured for copy direction and address before each call. - **RUN at end of bootstrap:** Line 330 ends with `RUN`, cleanly restarting the program with machine code now installed and `CLEAR` protecting it. ### Bugs and Anomalies - **1024-byte screen slots vs. 6912-byte screens:** A full Spectrum SCREEN$ is 6912 bytes (6144 pixel + 768 attribute). Storing screens at 1024-byte intervals means screens overlap heavily or only partial screen data is preserved. This appears to be an intentional design choice for a partial-screen animation or the buffer stores only luminance/pixel rows, discarding attributes. - **pp initialization:**`pp` is initialized to 29952 at line 20 but is incremented *before* the first load at line 140, meaning the first screen is stored at 30976 (29952+1024), not 29952. This may be intentional if the first slot is reserved. - **display loop starts at 29952:** The FOR loop at line 270 starts at 29952, before the first actually loaded slot (30976 if the above is correct), so the first frame displayed may be uninitialized memory. ## Source Code ``` 10 CLEAR 29952 20 LET pp=29952 30 PRINT "Instructions:"'"loadfile"'"loadscreen"'"display": BEEP .05,15: PAUSE 0 40 CLS : LET loadfile=90: LET loadscr=130: LET loadscreen=130 50 LET display=220 60 BEEP .1,15: PRINT AT 20,0;"L&K Ready." 70 INPUT a 80 GO TO a 90 REM loadfile 100 PRINT AT 21,0; BRIGHT 1;"Loading file." 110 LOAD ""CODE 120 GO TO 40 130 REM loadscr loadscreen 140 LET pp=pp+1024 150 IF pp>63744 THEN GO TO 40 160 LOAD ""SCREEN$ 170 POKE 65000,33: POKE 65003,17: POKE 65015,35 180 POKE 65002,64: POKE 65001,0 190 POKE 65005,pp/256: POKE 65004,0 200 RANDOMIZE USR 65000 210 GO TO 40 220 REM display 230 INPUT AT 21,0;"speed";sp 240 POKE 65000,17: POKE 65003,33: POKE 65015,19 250 POKE 65001,0: POKE 65002,64: POKE 65004,0 260 CLS 270 FOR f=29952 TO 63744 STEP 1024 280 PAUSE sp 290 POKE 65005,f/256 300 RANDOMIZE USR 65000 310 NEXT f 320 GO TO 40 330 CLEAR 65000: LET a$="033000064017000121062032001032000237176006023035005194247253061200195240253": FOR f=65000 TO 65024: POKE f,VAL a$( TO PI): LET a$=a$(4 TO ): NEXT f: RUN 340 SAVE "L&K" LINE 330 ```