Extended SCREEN$

Developer(s): Alvin Albrecht
Date: 1987
Type: Program
Platform(s): TS 2068

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,92LD DE,(CURS_X) — loads the cursor row/column from the system variable at address 23675 (0x5C7B), which holds the current print position.
  • 42,132,92LD HL,(CHARS) — loads the address of the ROM character set from system variable 23684 (0x5C84).
  • 6,8LD B,8 — sets up an 8-row loop to compare character bitmaps.
  • 201RET — 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.

Content

Appears On

Related Products

Related Articles

Related Content

Image Gallery

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

Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

Scroll to Top