KeyScan

Date: 198x
Type: Program
Platform(s): TS 2068

This program installs and demonstrates a 25-byte Z80 machine code routine that scans two specific keyboard rows and returns a value indicating which of two keys are pressed. The routine is loaded just above the current RAMTOP using PEEK of system variables 23730–23731 (the RAMTOP pointer) and CLEAR to lower RAMTOP by 26 bytes, reserving safe space for the code. It is invoked as a USR function, returning 0 for no key, 1 for left, 2 for right, or 3 for both keys simultaneously. The machine code uses IN instructions with port FEh against keyboard half-rows, masking the result with AND 31 to isolate the five relevant bits of each row. The BASIC demo loop continuously polls the routine and displays the decoded result on screen.


Program Analysis

Program Structure

The program is divided into four logical phases, clearly marked by REM blocks:

  1. Lower RAMTOP by 26 bytes to reserve space for machine code (line 30).
  2. POKE 25 bytes of Z80 machine code into the reserved area (lines 50–60).
  3. Print the USR address so the user knows where the routine lives (line 80).
  4. Continuously call the routine, decode the return value, and display it (lines 100–170).

RAMTOP Manipulation

System variables at addresses 23730–23731 hold the current RAMTOP as a little-endian 16-bit value. Line 30 reads this, subtracts 26, and issues CLEAR x to lower RAMTOP — this prevents the BASIC memory manager from overwriting the code. Line 50 then adds 1 back, because the actual writable area starts one byte above the new RAMTOP. This is a standard and reliable technique for embedding machine code in a BASIC program.

Machine Code Disassembly

The 25 bytes loaded by the DATA statements (lines 190–210) disassemble as follows:

OffsetBytesMnemonicNotes
001 00 00LD BC,0000hInitialise result register to 0
33E F0LD A,F0hAddress keyboard half-row (row containing left key)
5DB FEIN A,(FEh)Read keyboard port
72FCPLInvert (pressed = 1)
8E6 1FAND 1FhMask to 5 key bits
1028 02JR Z,+2Skip if no key in this row
12CB C1SET 0,CSet bit 0 of C → left pressed
143E 0FLD A,0FhAddress second keyboard half-row (right key)
16DB FEIN A,(FEh)Read keyboard port
182FCPLInvert
19E6 1FAND 1FhMask to 5 key bits
21C8RET ZReturn if no key in second row
22CB C9SET 1,CSet bit 1 of C → right pressed
24C9RETReturn BC to USR

The routine returns the value of the BC register pair to BASIC via the USR mechanism. Bit 0 of C encodes one key group and bit 1 the other, giving the four states 0–3 decoded in lines 120–150.

Key BASIC Idioms

  • PEEK 23730+256*PEEK 23731 — standard little-endian 16-bit read of a system variable pair.
  • RESTORE at line 30 ensures the DATA pointer is reset before the POKE loop, preventing issues if the program is re-run.
  • FOR y=x TO x+24: READ z: POKE y,z: NEXT y — compact single-line loop for loading machine code from DATA.
  • PRINT AT 12,14;: LET a=USR x — the PRINT AT positions the cursor before the USR call so the decoded string is always printed at a fixed location, avoiding screen drift.

Notable Techniques

Using AND 1Fh (31 decimal) after CPL is the canonical Z80 keyboard-scanning idiom: the five low bits of the IN result each represent one key on that half-row, and inverting before masking converts “low = pressed” hardware logic into “non-zero = pressed” software logic.

The asymmetry between the two half-rows is notable: the first row uses JR Z to skip the SET instruction, whereas the second row uses RET Z to exit entirely. This means if the left key is not pressed but the right key is, bit 0 is never set — only bit 1 is set, returning 2. The result table is therefore: 0 = none, 1 = left only, 2 = right only, 3 = both.

Bugs and Anomalies

The first three bytes of the machine code (LD BC,0000h) initialise BC to zero, but BC is only ever modified by setting bits of C; the B register is not used. This is harmless but slightly wasteful — LD C,0 (two bytes) would suffice. Additionally, the address bytes F0h and 0Fh are the high bytes of the keyboard port address, selecting specific half-rows; these correspond to particular physical key groups rather than universally labelled “left” and “right” — the actual keys depend on which half-rows those values address on the keyboard matrix.

Content

Appears On

This tape is a compilation of programs from user group members (Robert Burton, David Baulch, Frank Bouldin, Chuck Dawson, Ryan
One of a series of library tapes. Programs on these tapes were renamed to a number series. This tape contained

Related Products

Related Articles

Related Content

Image Gallery

KeyScan

Source Code

   10 REM Keyboard scanning
   11 REM function
   12 REM 
   20 REM lower RAMTOP
   21 REM 
   30 RESTORE : LET x=(PEEK 23730+256*PEEK 23731)-26: CLEAR x
   40 REM 
   41 REM POKE Machine code
   42 REM into memory
   43 REM 
   50 LET x=(PEEK 23730+256*PEEK 23731)+1
   60 FOR y=x TO x+24: READ z: POKE y,z: NEXT y
   70 REM 
   71 REM Print USR address
   72 REM 
   80 PRINT AT 1,2;"The routine is called by the";TAB 7;"function USR ";x
   90 REM 
   91 REM Example
   92 REM 
  100 PRINT AT 8,10;"Press any keys"
  110 PRINT AT 12,14;: LET a=USR x
  120 IF a=0 THEN PRINT "NONE "
  130 IF a=1 THEN PRINT "LEFT "
  140 IF a=2 THEN PRINT "RIGHT"
  150 IF a=3 THEN PRINT "BOTH "
  160 PRINT AT 16,10;"USR ";x;" = ";a
  170 GO TO 110
  180 REM 
  181 REM Machine code data
  182 REM 
  190 DATA 1,0,0,62,240,219,254,47
  200 DATA 230,31,40,2,203,193,62,15
  210 DATA 219,254,47,230,31,200,203,201,201

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

People

No people associated with this content.

Scroll to Top