--- title: "Extended SCREEN$" id: 69489 type: "computer_media" slug: "extended-screen" url: "http://localhost/computer_media/extended-screen/" markdown_url: "http://localhost/computer_media/extended-screen.md" published_at: "2026-04-13T09:12:21+00:00" modified_at: "2026-04-13T17:52:32+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2026/04/SCR-20260413-ftau.png" excerpt: "A machine code utility that extends the built-in SCREEN$ function to recognize UDGs and software-generated graphics by scanning screen memory via USR." 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: "Alvin Albrecht" slug: "albrecht-alvin" taxonomy: "indiv" url: "http://localhost/indiv/albrecht-alvin/" genre: - name: "Programming" slug: "programming" taxonomy: "genre" url: "http://localhost/type/programming/" media_type: "Program" programmers: - name: "Alvin Albrecht" slug: "albrecht-alvin" taxonomy: "indiv" url: "http://localhost/indiv/albrecht-alvin/" download_url: "https://archive.org/download/timex-sinclair-software-archive/SCREEN%20%281987%29%28Albrecht%2C%20Allen%29%28TS2068%29%28US%29%28Program%29.zip" mediadate: "1987" images: - url: "http://localhost/wp-content/uploads/2026/04/SCR-20260413-ftau.png" media_type_tags: "Programming" --- # Extended SCREEN$ Extended SCREEN$ is a machine code utility that extends the ZX Spectrum’s built-in SCREEN$ function to recognize not only standard alphanumeric characters but also user-defined graphics (UDGs) and software-generated graphics. The user POKEs the machine code to a user-specified base address (defaulting to 60000), then invokes the routine via PRINT AT X,Y followed by USR to retrieve the character code at any screen position. A checksum of 25151 is computed during the DATA load to verify integrity of the 221-byte machine code block. The routine self-modifies two bytes at offsets 87 and 88 from the base address to embed the absolute address of the software-generated graphics table, which is located 93 bytes into the loaded block. Graphic ‘A’ is always treated as identical to the standard character block, as noted in the embedded remarks. *** ## Program Analysis ### Program Structure The program is a loader and installer for a 221-byte machine code routine. It consists of: - Lines 10–80: REM blocks providing title, authorship, and usage instructions. - Line 90: Prompts the user for a base address; validates it is between 20000 and 65000, defaulting to 60000. - Line 100: Reads DATA bytes, checksums them, and POKEs them into memory. Then patches two bytes in the loaded code to embed the address of the software-generated graphics table. - Line 110: Verifies the checksum against the expected value of 25151; halts with an error message if the data is corrupt. - Line 120: Reports the installed address range and the graphics table address, then instructs the user to define `base` in their own program. - Lines 130–150: Three DATA lines containing the machine code bytes, with placeholder values `b` used for the embedded graphics pattern table. ### Self-Modifying Address Patching A technically notable feature is the self-modification performed at the end of line 100. After POKEing the machine code, two specific bytes within the loaded routine are overwritten: - `POKE (a+87), a+93-256*(INT ((a+93)/256))` — writes the low byte of the address `a+93`. - `POKE (a+88), INT ((a+93)/256)` — writes the high byte of the address `a+93`. This patches an absolute 16-bit address into the machine code so it can locate the embedded software-generated graphics pattern table, which begins 93 bytes into the loaded block. This allows the routine to be relocated to any valid address without rewriting the DATA lines. ### Checksum Verification Line 100 accumulates the sum of all DATA bytes into the variable `t`. Line 110 compares `t` to the expected value of 25151. Note that the placeholder value `b` in the DATA lines will evaluate to 0 (as `b` is an uninitialized or zero numeric variable), so those positions contribute 0 to the checksum. This means the checksum only covers the non-zero machine code bytes, with the pattern table bytes (all represented as `b`=0) excluded from the sum effectively. ### Usage Convention The routine is invoked by a two-step process in the user’s program: first, `PRINT AT X,Y;` positions the print cursor at the target screen cell; then `LET C=USR base` calls the machine code. The routine reads the screen character at the cursor position, compares its pixel pattern against known character sets (ROM alphanumerics, UDGs, and the software-generated graphics table embedded in the code), and returns the corresponding character code in the BC register pair, which BASIC receives as the USR return value. A return value of 0 indicates no match was found. ### Machine Code Details The machine code begins at line 130. Selected Z80 instruction sequences visible in the data include: - `237,91,123,92` — `LD DE,(CURS_X)` — loads the cursor row/column from the system variable at address 23675 (0x5C7B), which holds the current print position. - `42,132,92` — `LD HL,(CHARS)` — loads the address of the ROM character set from system variable 23684 (0x5C84). - `6,8` — `LD B,8` — sets up an 8-row loop to compare character bitmaps. - `201` — `RET` — return instructions at multiple points in the code. - `237,91,123,92` repeated — the cursor system variable is read again for the UDG/software-graphics comparison pass. The embedded pattern table starting at offset 93 (bytes represented as `b`=0 in the DATA) provides the pixel bitmaps for software-generated graphics, enabling the routine to recognize those characters in addition to standard ROM glyphs and UDGs. ### Notable Idioms and Observations - Using `VAL` is not present here; the base address is stored in variable `a` and used directly in arithmetic expressions across POKEs and PRINTs. - The use of an uninitialized variable `b` (which equals 0) as a placeholder in DATA lines is an unusual idiom — it avoids having to type many zeros while still satisfying the READ statement. - Line 120 uses multiple apostrophes (`'''`) to generate blank lines in the output, a standard ZX Spectrum BASIC idiom for formatting PRINT output. - The instruction to add `5 LET base=` followed by the address to the user’s own program, printed in line 120, is a practical installation guide embedded in the loader output itself. - The loop in line 100 uses `FOR z=a TO a+220`, loading exactly 221 bytes (indices 0–220 relative to base), consistent with the three DATA lines containing that total count of values. ### Potential Anomalies - The use of the variable `b` as a DATA placeholder relies on `b` being 0 at the time of execution. If the user has previously assigned a nonzero value to `b` in the same session, the loaded machine code and checksum will be incorrect. This is a latent but real bug. - The self-patching uses `a+93` rather than `a+87` as the target address, meaning the address written into bytes 87–88 points to the graphics table 93 bytes in — this is intentional and correct for relocation, but the arithmetic is easy to misread at first glance. ## Source Code ``` 10 REM <><><><><><><><><><><>< 20 REM <> EXTENDED SCREEN$ <> 30 REM <><><><><><><><><><><>< 40 REM <> By Alvin Albrecht <> 50 REM <> June 1987 <> 60 REM <><><><><><><><><><><>< 70 REM To do a SCREEN$ (X,Y), type; PRINT AT X,Y;: LET C=USR base. C will equal the CODE of the character at X,Y. If C=0, the character is not a UDG, S/W gen. gr, or alphanumeric char. 80 REM Graphic 'A' is always the same as the char. block. 90 INPUT "Base Address? (Default=60000): ";a: IF a<20000 OR a>65000 THEN LET a=60000 100 LET t=0: FOR z=a TO a+220: READ b: LET t=t+b: POKE z,b: NEXT z: POKE (a+87),a+93-256*(INT ((a+87)/256)): POKE (a+88),INT ((a+93)/256) 110 IF t<>25151 THEN PRINT "Error in DATA lines or line 100!": STOP 120 CLS : PRINT a;" to ";a+220;" is the location";TAB 9;"of the m/c."'''a+93;" is the location of the";TAB 9;"software generated";TAB 9;"graphics."'''''"Now type:"'"5 LET base=";a: STOP 130 DATA 237,91,123,92,42,132,92,6,8,126,18,19,36,16,250,62,31,60,254,165,40,53,254,144,40,247,79,48,50,254,128,48,53,42,54,92,36,214,32,95,22,0,203,35,203,18,203,35,203,18,203,35,203,18 140 DATA 25,237,91,123,92,6,8,26,190,32,7,35,19,16,248,6,0,201,121,24,198,1,0,b,201,42,123,92,214,144,24,209,33,37,176,214,128,24,202,0,b,b,b,b,b,b,b,15,b,b,b,0,b,b,b,240,b,b,b,0,b,b,b,255,b,b,b,0,b,b,b,b,b,b,b,15,b,b,b,b,b,b,b,b,b,b,b 150 DATA 240,b,b,b,15,b,b,b,255,b,b,b,15,b,b,b,0,b,b,b,240,b,b,b,15,b,b,b,240,b,b,b,b,b,b,b,b,b,b,b,255,b,b,b,240,b,b,b,0,b,b,b,255,b,b,b,15,b,b,b,255,b,b,b,240,b,b,b,255,b,b,b,b,b,b,b,b,b,b,b ```