The KEYPAD editor (BASIC front-end)
This describes the BBC BASIC program in
voltmace-delta-14b-driver-keypad.bas —
the user-facing half of the Voltmace Delta 14B KEYPAD software. Line numbers
below refer to that program.
What it is for
The Delta 14B handset has a 12-key keypad. On its own the BBC Micro cannot tell those keys apart, so this program lets you define what each keypad key sends and then installs a small machine-code resident driver that makes the keypad behave like a set of extra keyboard keys.
You interact with an on-screen picture of the keypad: press a key on the
handset to select it, then press the key on the BBC keyboard whose
character (or control code, function key, cursor key, …) that keypad key should
produce. Two handsets can be defined independently. When you finish, the program
writes your definitions into the resident driver, installs it at &0A00, and
erases its own program text, leaving the resident driver in place. The resident driver then scans
the keypad on every 50 Hz vertical-sync event and feeds your characters into the
keyboard buffer.
The companion machine code — the resident driver the editor configures and installs — is
covered by the disassembly in
voltmace-delta-14b-driver-keypad.asm.
Shape of the program
| Lines | Role |
|---|---|
| 10–170 | Startup, protection clean-up, OS check, intro screens |
| 180–280 | Main loop and error handling |
| 290–410 | PROCINIT — initialise hardware and read the configuration tables |
| 420–510 | Drawing the on-screen keypad |
| 520–740 | The interactive editor (select a key, assign a character) |
| 750–840 | PROCREADKP — scan the physical keypad matrix |
| 850–1070 | Screen furniture: headers, instructions, the intro slideshow |
| 1080–1210 | PROCMENU — the Sound / Auto / Edit / Finish options |
| 1220–1340 | PROCFIN — build, patch, and install the resident driver |
| 1350–1370 | Finish: wipe the editor and leave the resident driver running |
Startup and protection (10–170)
Line 10 is a REM that only exists because of the load-time protection: its
length byte is stored as zero in the file and repaired to &16 by the loader
before the program can run.
Line 30 selects MODE 7, clears the status flags (Z%, E1%, E2%), and sets
the error handler to line 220. Z% selects between the protected behaviour
(Z%=0, which self-erases) and an unprotected/developer mode (Z%=1, which
stops with the editor listable). Z% is a resident integer — BBC BASIC keeps
@% and A%–Z% at fixed page-&4 addresses (Z% at &468), so unlike
ordinary variables it survives RUN and could in principle be preset by machine
code. Here it isn't: line 30 sets it to 0, nothing assigns it again, and
neither the loader nor the resident driver touches page &4. So Z% is 0
throughout, the Z%=1 branches are never taken, and the program always runs
protected.
Line 60, when Z%=0, reprograms the BREAK key. A BBC Micro has no f10 key;
soft key 10 (*KEY10) is the "break string" that a soft (single) BREAK types
into the input as part of reset. Line 60 sets that string to a self-destruct —
CLS, then a loop zeroing &C00–&3A80, the region holding the just-relocated
BASIC program. It only arms the trap (hence the "DO NOT PRESS BREAK" warning
later); it does not run the wipe now, which would erase the running editor.
Lines 70–120 guard against an incompatible operating system: ?&E8AA is read
as an OS signature (the expected value is &4F); on a mismatch the program
displays "KEYPAD DRIVER WILL NOT WORK WITH OPERATING SYSTEM OS 0.1" (line 90)
and stops. Line 130 issues *FX200,2 — OSBYTE 200 bit 1, "clear user memory on
the next BREAK" — so a BREAK now wipes the program to the power-on state. It
sets only bit 1, so ESCAPE (bit 0) stays enabled: the two exits are
deliberately different, and the warnings say so — BREAK destroys the
program, while ESCAPE raises the normal escape error (17), which the
ONERROR handler catches to restart. Line 150 dimensions the arrays, and line
170 shows the intro (PROCINTRO) before switching to MODE 1 for the editor.
Main loop (180–280)
Line 180 draws the header (PROCH1), the instructions (PROCINS), and runs the
editor (PROCEDIT). Line 190 then shows the options menu (PROCMENU), which
returns a choice in Q$:
Q$="E"(line 200) — re-edit: go back to line 180.Q$="F"(line 210) — finish:PROCFINbuilds and installs the resident driver, then branches onZ%—STOPifZ%=1(the unprotected/developer path, which leaves the editor listable), orGOTO 1350, the wipe-and-run code, ifZ%=0. In the software as shippedZ%is always0(see below), so the wipe-and-run path is always taken.
Lines 220–280 are the error handlers, including the "SYSTEM ERROR" report (line 250) and the ESCAPE-driven restart (error 17).
Initialisation and the configuration tables (290–410)
PROCINIT (line 290) enables the cursor-key/*FX4 mode, then (line 300) sets
the User VIA data-direction register ?&FE62=&F0 (top nibble outputs for the
column strobes and handset select, bottom nibble inputs for the rows) and clears
the working variables.
The DATA statements are read straight into the editor's arrays:
- Line 320 (
8,4,2,1,0,&60,&50,&30), read by line 310 →ROW%()thenCOL%().ROW%(0..3)=8,4,2,1are the row bit-masks andCOL%(0..2)=&60, &50,&30are the three column strobes — the same scan constants the machine-code driver uses. (ROW%(4)=0is a sentinel; the loop overCOL%reads one value too many, harmlessly, before line 330'sRESTOREresets the data pointer.) - Line 340 (45 values), read by line 330 as 15 keys × (X, Y, colour) into
KPOS%(N%,0..2)— the screen position and MODE-1 colour for drawing each key. The layout is a 3-column (X = 1, 10, 19) by 5-row (Y = 22, 17, 12, 7, 1) grid: the 12 keypad keys plus 3 extra cells (e.g. the REAR/SIDE fire button drawn for key 13). - Lines 370 + 380 (one continuous list ending in
-1), read by line 350 as code/name pairs intoACHR%()/ACHR$(). This is the dictionary used to show a readable label when a key is assigned a non-printable code: the VDU control codes (2→P ON,12→CLS,13→RET,127→DEL, …) in line 370, and the "high" keys (&90..&9A→f0..f10,135→COPY,136..139→the four cursor keys,0→ESC) in line 380. - Line 400 (30 values), read by line 390 as 2 handsets × 15 keys into
KNUM%(N%,KP%)— the default character each key sends.KP%=0is the numeric handset (DEL, 0, RETURN, 1…9);KP%=1is an editing/function handset (DEL, cursor keys,HOME,COPY,CLS,f0,RETURN,f1, …).PROCCONVturns each code into its on-screen label as it is read.
Drawing the keypad (420–510)
PROCKEYPAD (line 420) draws all 15 keys and the copyright line. PROCBKKEY
(430) draws a key in its unselected colour, PROCWTKEY (440) draws the currently
selected key highlighted and shows its assigned character. The workhorse is
PROCkey (450–500): it positions itself from KPOS%(), prints the label from
KCHR$(N%,KP%), and special-cases key 13, which shows REAR or SIDE
(the handset's two fire buttons) rather than a character.
The interactive editor (520–740)
PROCEDIT (line 520) is the core loop:
- Draw the keypad and take the current selection
N%=CURKEY%(530). - Highlight it (
PROCWTKEY, 540) and scan the physical keypad (PROCREADKP, 550). - If a different keypad key is now pressed (line 560), move the highlight to it and beep.
- Read a BBC keyboard key with
K%=INKEY(5)(570). Lines 580–590 handle the flashing highlight and theCTRL Q(K%=17) exit. CTRL A(K%=1, lines 600–610) toggles between the two handsets (KP%) and redraws.- Otherwise (line 620)
PROCCONVturns the pressed key into a display string,PROCYELstores it,PROCSNDbeeps, and the loop repeats.
Supporting routines:
PROCCONV(630–640): a printable key (32<K%<127) becomes its own centred character; anything else is looked up viaPROCSPECIAL.PROCSPECIAL(710–740): search theACHR%()table for the code and take the matchingACHR$()label.PROCPAD(650–680): pad a label to a fixed 5-character cell.PROCYEL(700): store the new code inKNUM%(N%,KP%); key 10 is mirrored onto keys 12 and 14 (a wide key occupying three cells).
Scanning the physical keypad (750–840)
PROCREADKP reads the handset matrix directly through User VIA port B (&FE60),
exactly as the resident driver does. Line 750 selects the handset by writing KP%*&80
(bit 7 drives the 74LS157 multiplexer) and checks for "no key". The nested loops
(780–830) strobe each column (?&FE60=COL%(X%)+&80*KP%) and test each row
(?&FE60 AND ROW%(Y%)); a pressed cell yields CURKEY%=3*CURROW%+CURCOL%.
Screens and presentation (850–1070)
These procedures build the MODE-1/MODE-7 screens: PROCH1/PROCHEAD/PROCBIG
(double-height banners), PROCA1/PROCA2 (text windows), PROCWARN (the
"DO NOT PRESS BREAK" line), PROCINS (the how-to-use instructions, 890–900), and
PROCINTRO (910–1010), the copyright-and-explanation slideshow shown at startup.
PROCCONT (1050–1070) is the "PRESS SPACE-BAR TO CONTINUE" pause. The intro text
explains that the resident driver installs at &A00–&AFF and runs under EVENT 4 (vertical
sync), which the resident driver enables itself (lines 950–960).
The options menu (1080–1210)
PROCMENU offers four choices keyed S / A / E / F: Sound (beep on keypad
press, toggled by B%, PROCONOFF), Auto repeat (A%, PROCAUTO),
re-Edit, and Finish. Lines 1130–1180 read the choice into Q$ and return
it to the main loop.
Building and installing the resident driver (1220–1340)
PROCFIN turns the on-screen definitions into a working resident driver:
- Lines 1220–1230 tell the user how to save/reload the resulting resident driver
(
*SAVE MC A00 +100,*KEY10 CALL&A00). - Lines 1240–1280 rewrite the stored codes into the form the resident driver inserts:
function-key codes in
&87–&8Bare nudged by 4, codes in&90–&9Bare reduced by&10, and a zero becomes&1B(ESCAPE). - Line 1290 pokes the finished 24-entry table into the resident driver:
?(&ADD+4*C%+R%+12*KP%)=KNUM%(3*R%+C%,KP%).&ADDis the resident driver'skey_codestable (see the machine-code disassembly), indexed bycolumn*4 + row + 12*handset— this is the bridge between the editor and the resident driver. - Line 1300 patches the resident driver to enable auto-repeat if selected; line 1310 patches the sound block for the beep option.
- Line 1330
CALL &A00runs the resident driver's install routine (enable the vsync event, set the port directions, hookEVNTV).
Finishing (1350–1370)
Reached on finish whenever Z%=0 — which is always, in the shipped program.
Line 1350 wipes the BASIC program (FOR N%=PAGE TO PAGE+&1C00 STEP4:!N%=0),
leaves an empty program (!PAGE=&FF0D), and redefines the BREAK key from its
self-destruct to *KEY10 CALL&A00 — so a subsequent soft BREAK now re-installs
the driver instead of wiping — then prints "KEYPAD OPERATIONAL". Line 1360
restores *FX200,0 (memory preserved across BREAK) on OS versions whose
signature isn't &4F; line 1370 resets PAGE and ENDs, leaving only the
now-configured resident driver in memory.
