Zebra Light Pen

Products: Light Pen
Date: 198x
Type: Program
Platform(s):
Tags: Art

Zebra Light Pen is a two-part ZX81/TS1000 program package consisting of a light pen driver and a Concentration-style card-matching game. The first listing (the game) uses a 6×7 grid stored in arrays P and C, assigning pairs of characters from the ZX81 character set (CHR$ values starting at 166) to random board positions, then using a light pen scanning routine to detect which cell the player selects. The second listing is a standalone test/demonstration routine that also POKEs 17 bytes of Z80 machine code starting at address 16514; this machine code reads the EAR port (IN A,(254)), tests bit 3, and returns a non-zero value if the light pen is detected, which BASIC then calls via USR 16514. Sound effects are produced by POKEing values to address 9000, which is also used as the BLIP variable. The game awards 10 points per match and deducts 5 per mismatch, running until all 21 pairs are found.


Program Analysis

Overview and Structure

The package comprises two separate BASIC programs. The first is the Concentration game (LPEN), and the second is a light pen test routine (LPTEST). Both share the same Z80 machine code driver POKEd into memory starting at address 16514. The game program embeds the machine code implicitly inside its REM line 1, while the test program re-POKEs it explicitly at lines 80–96.

Machine Code Light Pen Driver

Lines 80–96 of the test program POKE 17 bytes of Z80 machine code into addresses 16514–16530. These bytes implement the light pen polling routine called via USR PEN (where PEN=16514). The disassembly is approximately:

  1. XOR A — clear accumulator
  2. LD C,A — zero C register
  3. LD B,A — zero B register (BC = return value candidate)
  4. LD A,(0FEh) / IN A,(254) — read EAR/MIC port
  5. Test bit 3 of the result; if clear (light detected), jump forward
  6. Load a count and loop, returning 1 if light pen pulse found, 0 otherwise
  7. RET — return to BASIC

The routine reads port 254 (the keyboard/EAR port), isolating bit 3 which corresponds to the EAR input — the standard method for interfacing a light pen on this hardware. USR PEN returns a non-zero value when the pen is detected over a bright screen area.

Game Board Initialization

The board is a 6×7 grid (42 cells) managed by two arrays: C(6,7) stores character codes for each cell’s hidden value, and P(6,7) tracks whether a cell has been matched (1) or not (0). Fourteen pairs are placed (covering 28 of 42 cells), with character codes starting at 167 (Y+166 for Y=1..14). Pairs are placed by random selection with collision checking at line 2093.

An unusual loop controls how many times each pair value is placed:

FOR Z=1 TO 2+(Y/2=INT(Y/2))*2

This iterates 2 times for odd Y and 4 times for even Y, meaning even-numbered card types get placed four times (two pairs) while odd ones get two. This gives 7×2 + 7×4 = 42 placements — exactly filling the board. However, the win condition checks MATCHED<21, implying 21 pairs, which is inconsistent with only 14 distinct card values across 42 cells.

Light Pen Scanning Routine

The scan is a two-phase process. First, a row scan (lines 2130–2200) iterates Y from 1 to 6, printing a solid bar (O$, 29 block characters) across each row, sampling USR PEN before and after, then restoring the row. A hit is detected by Q AND NOT R — the pen fired while the bar was displayed but not after it was removed. The column scan (lines 2300–2370) then similarly sweeps across the 7 columns of the identified row, printing a 2-character block at each column position.

The variables DELAY and DELAY1 are set to line numbers 3010 and 3030 respectively and used as GOSUB DELAY and GOSUB DELAY1. Line 3010 contains only a REM and a RETURN, making it a near-zero delay. Line 3030 runs a FOR J=1 TO D: NEXT J loop for a variable delay. This is a clever use of numeric variables as GOSUB targets.

Sound Effects

Audio feedback is generated by POKEing values to address 9000, stored in variable BLIP. This directly manipulates the cassette/speaker output bit. Three distinct sound patterns are used:

  • Match sound (subroutine 3100): ascending POKE sequence 1..16
  • Select beep (subroutine 3060): brief POKE 7, short loop, POKE 0
  • Mismatch buzz (subroutine 3300): POKE 15, short delay, POKE 0
  • Setup chime (subroutine 3200): POKE 3, loop, then random POKE

Subroutine 3250 (lines 3250–3280) implements a toggling sound effect using variable W as a flag, but this routine is never called from the main game flow — it appears to be an unused utility.

Screen Layout

The board occupies even rows 2–14 (for the 6 card rows, addressed as 2*Y+1). Each column occupies 4 character positions (4*ZC-3). The string O$ (29 solid block characters) is used both as a “curtain” during scanning and to blank status lines. X$ (built as 7 repetitions of " ██") forms the hidden-card display pattern.

Test Routine Structure

The second program provides a standalone light pen verification tool. It displays a fixed trivia question about New York state capitals and scans four answer rows. The scan subroutine at line 300 flashes each cell on and off twice, accumulating a score in N: +10 if pen detected when cell is blank, +1 if detected when cell is lit. A score of exactly 20 (N=20) indicates a confident pen detection at that row. Only answer W=1 (“ALBANY”) triggers the “CORRECT” response.

Bugs and Anomalies

  • Line 2590 prints “CONGRATIONS” — a typo for “CONGRATULATIONS.”
  • Line 2120 prints “MAKE Y0UR FIRST CHOICE” with a zero instead of the letter O, but line 2490 corrects this with “MAKE YOUR FIRST CHOICE.”
  • The win condition MATCHED<21 at line 2600 requires 21 matches, but only 14 distinct values are placed (yielding at most 14 or 7 pairs depending on interpretation) — the game may never reach the win state as written.
  • Subroutines at lines 3250–3280 are defined but never called from any reachable code path.
  • Line 2341 contains POKE 9000,0 using a literal address rather than the BLIP variable, breaking the abstraction used elsewhere.
  • The score variable SC is initialized at line 2024 but MATCHED is initialized at line 105 in a different section; a RUN 2000 or mid-program restart would not reset MATCHED.

Key Variables Summary

VariablePurpose
PENAddress of machine code USR routine (16514)
DELAYLine number used as GOSUB target for near-zero delay (3010)
DELAY1Line number used as GOSUB target for variable delay (3030)
BLIPAddress for speaker POKE sound output (9000)
C(6,7)Board cell character codes
P(6,7)Board cell matched flags
ZC, ZDColumn and row of pen-selected cell
XC, XDColumn and row of first chosen cell
SCCurrent score
MATCHEDCount of matched pairs found

Content

Appears On

Related Products

Light pen, manual, computer interface module, demonstration programs on cassette.

Related Articles

Related Content

Image Gallery

Source Code

   1 REM [J]###<= RETURN▛ABS [:]RND:▘+TAB ▚RNDTAN 21
  88 CLS
  89 PRINT "[L][I][G][H][T]█[C][O][N][C][E][N][T][R][A][T][I][O][N]"
  90 PRINT "[C][O][P][Y][R][I][G][H][T]█[1][9][8][3]"
  91 PRINT "[Z][E][B][R][A]█[S][Y][S][T][E][M][S][,][I][N][C][.]"
  92 REM [A][L][L]█[R][I][G][H][T][S]█[R][E][S][E][R][V][E][D]
 100 LET PEN=16514
 101 LET DELAY=3010
 102 DIM P(6,7)
 103 LET O$="█████████████████████████████"
 104 DIM C(6,7)
 105 LET MATCHED=0
 106 LET DELAY1=3030
 107 LET BLIP=9000
 2000 REM [C][O][N][C][E][N][T][R][A][T][I][O][N]
 2024 LET SC=100
 2030 LET W$="  ██"
 2031 LET X$=""
 2040 FOR Y=1 TO 7
 2042 LET X$=X$+W$
 2044 NEXT Y
 2069 SLOW
 2070 PRINT "I AM SETTING UP THE BOARD"
 2071 PRINT "PLEASE WAIT 20 SECONDS"
 2075 GOSUB 3100
 2076 FAST
 2079 LET H=42
 2080 FOR Y=1 TO 14
 2082 FOR Z=1 TO 2+(Y/2=INT (Y/2))*2
 2090 LET S=INT (RND*6)+1
 2091 LET T=INT (RND*7)+1
 2093 IF C(S,T)<>0 THEN GOTO 2090
 2100 LET C(S,T)=Y+166
 2101 GOSUB 3200
 2102 NEXT Z
 2104 NEXT Y
 2105 GOSUB 3100
 2109 SLOW
 2110 CLS
 2111 GOSUB 3000
 2115 PRINT AT 1,1;"THE SCORE IS ";SC;"██";
 2120 PRINT AT 16,4;"MAKE Y0UR FIRST CHOICE";
 2122 GOTO 2490
 2125 REM [R][O][W]█[S][C][A][N]
 2130 FOR Y=1 TO 6
 2131 LET Q=USR PEN
 2135 PRINT AT 2*Y+1,0;O$;
 2136 GOSUB DELAY
 2137 LET R=USR PEN
 2160 PRINT AT 2*Y+1,1;X$;
 2165 IF Q AND NOT R THEN GOTO 2300
 2180 NEXT Y
 2200 GOTO 2125
 2300 REM [C][O][L][U][M][N]█[S][C][A][N]
 2305 LET X=Y
 2310 FOR Y=1 TO 7
 2315 LET Q=USR PEN
 2320 PRINT AT 2*X+1,Y*4-3;"██";
 2322 GOSUB DELAY
 2330 LET R=USR PEN
 2340 PRINT AT 2*X+1,Y*4-3;"  ";
 2341 POKE 9000,0
 2350 IF Q AND NOT R THEN GOTO 2375
 2360 NEXT Y
 2370 GOTO 2125
 2375 LET ZC=Y
 2376 LET ZD=X
 2400 RETURN
 2401 REM [E][N][D]█[P][E][N]█[I][N][P][U][T]█[R][O][U][T][I][N][E]
 2490 PRINT AT 16,4;"MAKE YOUR FIRST CHOICE██";
 2495 GOSUB 2125
 2496 LET XD=ZD
 2497 LET XC=ZC
 2498 GOSUB 3060
 2500 IF P(ZD,ZC)=0 THEN GOTO 2530
 2510 PRINT AT 15,4;"TRY AGAIN";
 2520 GOTO 2495
 2530 PRINT AT 2*ZD,4*ZC-3;CHR$ C(ZD,ZC);
 2540 PRINT AT 15,0;O$;
 2550 PRINT AT 16,4;"MAKE YOUR SECOND CHOICE";
 2555 GOSUB 2125
 2559 REM [S][A][M][E]█[O][R]█[U][S][E][D]
 2560 IF P(ZD,ZC)=0 AND NOT (ZC=XC AND ZD=XD) THEN GOTO 2577
 2570 PRINT AT 15,4;"TRY AGAIN             ";
 2571 GOSUB 3060
 2575 GOTO 2555
 2577 REM [M][A][T][C][H]
 2578 PRINT AT 15,0;O$;
 2579 PRINT AT 2*ZD,4*ZC-3;CHR$ C(ZD,ZC);
 2580 IF C(ZD,ZC)<>C(XD,XC) THEN GOTO 2650
 2582 LET MATCHED=MATCHED+1
 2590 PRINT AT 16,4;"MATCH FOUND. CONGRATIONS█";
 2591 GOSUB 3100
 2592 LET P(ZD,ZC)=1
 2593 LET P(XD,XC)=1
 2594 LET SC=SC+10
 2595 PRINT AT 1,1;"THE SCORE IS ";SC;"██";
 2596 LET D=20
 2597 GOSUB DELAY1
 2600 IF MATCHED<21 THEN GOTO 2490
 2610 PRINT AT 15,4;"GAME OVER";
 2620 PRINT AT 16,4;"PRESS ENTER TO BEGIN AGAIN";
 2630 INPUT K$
 2640 RUN 
 2650 REM [M][I][S][M][A][T][C][H]
 2652 PRINT AT 15,0;O$;
 2653 GOSUB 3300
 2655 PRINT AT 16,4;"NO MATCH FOUND█████████";
 2657 LET SC=SC-5
 2658 PRINT AT 1,1;"THE SCORE IS ";SC;"██";
 2660 LET D=20
 2665 GOSUB DELAY1
 2670 PRINT AT 2*ZD,4*ZC-3;"█";
 2675 PRINT AT 2*XD,4*XC-3;"█";
 2680 GOTO 2490
 2998 REM [S][U][B][R][O][U][T][I][N][E][S]
 2999 REM [B][L][A][C][K]█[S][C][R][E][E][N]
 3000 FOR T=1 TO 20
 3002 PRINT O$
 3004 NEXT T
 3005 PRINT "██[L][I][G][H][T][W][A][R][E]█[B][Y]█[Z][E][B][R][A]█[S][Y][S][T][E][M][S][.]";
 3006 RETURN
 3010 REM [D][E][L][A][Y]
 3020 RETURN
 3030 REM [D][E][L][A][Y][1]
 3031 FOR J=1 TO D
 3032 NEXT J
 3033 RETURN
 3050 REM [S][O][U][N][D]█[R][O][U][T][I][N][E][S]
 3060 POKE BLIP,7
 3065 FOR W=1 TO 4
 3066 NEXT W
 3070 POKE BLIP,0
 3080 RETURN
 3100 FOR W=1 TO 16
 3110 POKE BLIP,W
 3120 NEXT W
 3130 RETURN
 3200 POKE BLIP,3
 3210 FOR W=1 TO 10
 3211 NEXT W
 3220 POKE BLIP,INT (16*RND)
 3230 RETURN
 3250 IF W THEN GOTO 3270
 3255 LET W=1
 3260 POKE BLIP,7
 3265 RETURN
 3270 LET W=0
 3275 POKE BLIP,6
 3280 RETURN
 3300 POKE BLIP,15
 3310 LET D=5
 3320 GOSUB DELAY1
 3325 POKE BLIP,0
 3330 RETURN
 9000 SAVE "LPEN[1]"
 9010 RUN 
 
   1 REM [J]###<= RETURN▛ABS [:]RND:▘+TAB ▚RNDTAN 
  79 REM [M][A][C][H][I][N][E]█[C][O][D][E]█[P][O][K][E][D]█[L][I][N][E][1]
  80 POKE 16514,175
  81 POKE 16515,79
  82 POKE 16516,71
  83 POKE 16517,87
  84 POKE 16518,219
  85 POKE 16519,254
  86 POKE 16520,7
  87 POKE 16521,210
  88 POKE 16522,142
  89 POKE 16523,64
  90 POKE 16524,14
  91 POKE 16525,1
  92 POKE 16526,21
  93 POKE 16527,194
  94 POKE 16528,134
  95 POKE 16529,64
  96 POKE 16530,201
 100 LET A=16514
 101 LET Y=5
 200 LET X=10
 205 CLS
 206 PRINT "LIGHT PEN TEST ROUTINE"
 210 GOSUB 300
 220 PRINT AT 6,5;"█ N=";N;" ";
 230 GOTO 210
 300 REM [S][C][A][N]█[B][L][O][C][K]█[A][T]█[X][,][Y]
 310 LET N=0
 330 FOR I=1 TO 2
 340 PRINT AT X,Y;" ";
 350 FOR F=1 TO 2
 360 NEXT F
 380 IF USR A THEN LET N=N+10
 390 PRINT AT X,Y;"█";
 400 FOR F=1 TO 2
 420 NEXT F
 440 IF USR A THEN LET N=N+1
 470 NEXT I
 475 RETURN
 480 REM [P][R][I][N][T]█[Q][U][E][S][T][I][O][N]
 490 PRINT AT 3,0;"WHAT CITY IS THE CAPITOL OF","NEW YORK STATE?";
 1000 PRINT AT 10,7;"ALBANY";
 1010 PRINT AT 12,7;"NEW YORK";
 1020 PRINT AT 14,7;"MANHATTAN";
 1030 PRINT AT 16,7;"ROCHESTER";
 1099 REM [F][I][N][D]█[L][I][G][H]█[P][E][N]
 1100 FOR W=1 TO 4
 1110 LET X=2*W+8
 1120 GOSUB 300
 1124 REM [P][R][I][N][T]█[R][E][S][U][L][T]
 1130 IF N=20 THEN GOSUB 1500
 1140 IF N<>20 THEN PRINT AT 6,5;"        ";
 1199 NEXT W
 1300 GOTO 1100
 1500 IF W=1 THEN GOTO 1550
 1505 IF W<>1 THEN PRINT AT 6,5;"WRONG    ";
 1510 RETURN
 1550 PRINT AT 6,5;"CORRECT";
 1560 RETURN
 2000 SAVE "LPTES[T]"
 2010 RUN 

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

People

No people associated with this content.

Scroll to Top