TRS-80 DOS - LDOS 5.3.1 (Model I) - KI/DVR Keyboard Driver Disassembly
Page Customization
Page Index
KI/DVR
Other Navigation
Program Overview
KI/DVR is the DOS version 5 keyboard driver for the TRS-80 Model I, supplied with LDOS 5.3.1 (MISOSYS, 1991). It is not a SYS overlay but a device driver installed with the library command SET *KI TO KI/DVR (parms). Its four options are TYPE (type-ahead), JKL (screen print), DELAY=d and RATE=r (key-repeat timing). Once installed it establishes the CLEAR key as a control key for KSM, MiniDOS, LCOMM and the SPOOLer, and SHIFT-0 as a CAPS-lock toggle through KFLAG$ bit 5.
The file is a transient installer that runs at 5200H and relocates up to three resident modules into high memory: $KI, the keyboard scan, debounce, translation and key-repeat engine (always installed); $TA, the 128-character type-ahead ring buffer with its own interrupt task (only with TYPE); and $JKL, the screen-print filter (only with JKL). Each module is hooked in front of the previous keyboard driver through KIDCB$ at 4016H, or through KIJCL$ at 43BEH when a Job Control Language job holds the real driver address - the same cell SYS11/SYS saves and restores for code 9DH.
The image loads as one contiguous block, 5200H through 573CH (1341 bytes), transfer address 5200H, with no gaps. Unlike every other LDOS file examined, it carries no type-1FH copyright record ahead of the load records; the copyright string is inside the loaded image. The disassembly below is byte-verified: the memory image was rebuilt from the file's LDOS load records and every opcode and data byte matches (0 mismatches, full coverage). The message and table regions, which the .txt disassembler renders as bogus instructions, are decoded from the binary and shown as DEFB/DEFM/DEFW data.
File Format and Load Map
The file contains only load records - no copyright, module-name or directory records. Six type-01H load blocks fill 5200H-573CH (five full 256-byte pages and a 61-byte tail), followed by a type-02H transfer record to 5200H. The transient installer occupies 5200H-528BH, 5403H-5495H, 5547H-559EH and 5613H-562CH; the resident modules and their data occupy 528CH-5402H ($KI, relocation table and translation tables), 5496H-5546H ($TA) and 559FH-55F4H ($JKL); the relocator is 55F5H-5612H; the messages are 562DH-56FBH and the @PARAM table is 56FCH-573CH.
Variables, Buffers and Self-Modifying Cells
| Address Range | Purpose |
|---|---|
| 4015H 1 byte | KIDCB$ - the *KI device control block type byte; set to 01H for $KI, 05H for $TA. |
| 4016H-4017H 2 bytes | KIDCB$+1/+2 - the current keyboard driver vector; each installed module is written here so it becomes the *KI driver. |
| 4036H-403DH 8 bytes | $KI keyboard matrix row-state save table, one byte per scanned row (3801H-3840H), used for change detection and debounce. |
| 4040H 1 byte | TIMER$ - system tick counter read for the key-repeat delay and rate deadlines. |
| 4049H 2 bytes | HIGH$ - top of free memory; lowered by 016BH ($KI), 00B1H ($TA) and 0056H ($JKL) as each module is reserved. |
| 4300H vector | @KITSK - keyboard-interrupt task chain, called at the head of $KI and $TA. |
| 430FH 1 byte | SFLAG$ - bit 5 marks a Job Control Language job active, selecting KIJCL$ over KIDCB$ for the hook. |
| 441FH 1 byte | DFLAG$ - bit 1 type-ahead active, bit 2 screen-print active, bit 4 KI/DVR active, bit 7 graphics screen-print. |
| 4423H 1 byte | KFLAG$ - bit 5 CAPS lock, bit 6 special-key translation-table selector, bit 7 type-ahead buffer-has-data. |
| 43BEH 2 bytes | KIJCL$ - holds the real keyboard driver address while a JCL job runs; hooked instead of KIDCB$. |
| 4DF2H / 4DF4H / 4DF8H 2 bytes each | Reuse pointers in SYS0 resident RAM recording the reserved high-memory base of $TA, $JKL and $KI; a second SET reuses the same block. |
| 521DH-521EH 2 bytes | LD BC operand seeded with the DELAY value (20); @PARAM overwrites it when DELAY=d is given (floor 10). |
| 522CH-522DH 2 bytes | LD BC operand seeded with the RATE value (2); @PARAM overwrites it when RATE=r is given (floor 1). |
| 52EAH, 53C4H 1 byte each | $KI repeat-delay operands, patched from the resolved DELAY at 5225H/5228H. |
| 5353H, 5512H, 5519H 1 byte each | $KI/$TA repeat-rate operands, patched from the resolved RATE at 5234H/5237H/523AH. |
| 5498H / 55A1H / 529AH 2 bytes each | $TA, $JKL and $KI module link words, patched with each module's reserved base. |
| 54D8H, 54F9H 2 bytes each | $TA chained-driver call operands, patched with the previous *KI driver address plus 45. |
| 54A4H, 54E7H 2 bytes each | $TA buffer-control operands, patched with the type-ahead buffer base. |
| 55A9H, 55C1H 2 bytes each | $JKL chained-driver and abort-scan operands, patched at install time. |
| 55E5H 1 byte | $JKL non-ASCII replacement byte, set to 2EH (period) or 0FEH (graphics) from DFLAG$ bit 7. |
Major Routines
| Address | Routine and Purpose |
|---|---|
| 5200H | Installer / Sign-On Displays the banner, confirms the SET context (4758H bit 3), parses parameters and seeds the repeat-timing operands. |
| 523DH | $KI Allocation, Relocation and Hook Reserves (or reuses at 4DF8H) 363 bytes below HIGH$, relocates the $KI body, sets DFLAG$ bit 4, copies it up and points KIDCB$ at it. |
| 52C5H | $KI Resident Keyboard Driver Runs the keyboard task chain, scans and debounces the eight matrix rows, applies SHIFT/CTRL/CAPS and the translation tables, and manages the repeat delay and rate. |
| 5403H | TYPE Installer + $TA Module Reserves and copies the 177-byte type-ahead module, patches its chained-driver and buffer cells, hooks it in front and adds its interrupt task via @ADTSK. |
| 5484H | Hook Subroutine Writes the new driver entry into KIDCB$+1/+2, or into KIJCL$ under JCL. |
| 5547H | JKL Installer + $JKL Module Reserves and copies the 86-byte screen-print module, patches its cells, hooks it in front and returns to DOS via @EXIT. |
| 55F5H | Module Relocator Adds the $KI relocation displacement to every address word named in the table at 528CH, including the $TA-to-$KI cross-reference at 54F3H. |
| 5613H | Error Exits Four paths using a DD-prefix overlap trick to select one of four messages; parameter errors raise code 6CH through @ERROR. |
Cross-Reference Notes
KI/DVR issues no RST 28H request of its own. It calls the resident SYS0/SYS services @KITSK (4300H), @PARAM (4476H), @DSPLY (4467H), @LOGOT (447BH), @ERROR (4409H), @ADTSK (4410H), @EXIT (402DH) and @ABORT (4030H), the ROM @PRT (003BH) and @PAUSE (0060H) vectors, and the resident routine at 4BEFH. It reads and writes the SYS0 resident cells KIDCB$ (4015H-4017H), KIJCL$ (43BEH), DFLAG$ (441FH), KFLAG$ (4423H), SFLAG$ (430FH), HIGH$ (4049H), TIMER$ (4040H) and the reuse pointers 4DF2H/4DF4H/4DF8H, all confirmed against SYS0.SYS.
The driver word KI/DVR installs at 4016H is the same one SYS11/SYS saves into KIJCL$ (43BEH) when it starts a compiled job (RST 28H code 9DH) and restores when the job ends; the hook subroutine at 5484H honours that arrangement by writing to KIJCL$ instead of KIDCB$ whenever SFLAG$ bit 5 is set. The SPOOLer's despool interrupt task is chained through @KITSK (4300H), which is why $KI and $TA call it before scanning.
A refinement to the SYS0 resident reference: the KI/DVR-active flag is DFLAG$ bit 4 (tested and set at 5272H/5277H), not bit 3; DFLAG$ bit 2 is the screen-print/JKL flag. KFLAG$ bit 5 is CAPS lock (matching the DOS manual's POKE), bit 6 selects the special-key translation table, and bit 7 signals type-ahead data. These were read directly off the driver code.
Disassembly:
5200H - Sign-On and SET-Context Check
The transient installer entry. The SET library command loads KI/DVR at 5200H and enters here with Register Pair DE addressing the *KI device control block KIDCB$ and Register Pair HL addressing the parameter text. The banner is displayed and the SET context confirmed before any memory is committed.
5210H - Parameter Parse and RATE/DELAY Setup
Parses the command tail against the parameter table at 56FCH and seeds the repeat-timing operands with their defaults and floors.
523DH - $KI High-Memory Allocation and Relocation
Reserves space for the main $KI driver below HIGH$ (or reuses a previously reserved block recorded at 4DF8H) and relocates the module image to the address it will occupy.
526FH - Activate Flag, Copy $KI and Hook the Keyboard DCB
Marks KI/DVR active in DFLAG$, copies the relocated $KI body into high memory, and points KIDCB$ (or KIJCL$ under JCL) at it.
528CH - $KI Relocation Table
A null-terminated list of the in-image address words that the relocator at 55F5H must adjust when the $KI body is moved into high memory.
5296H - $KI Module Header and Translation Tables
The resident $KI module as it sits before being copied up: a JR entry, the module link word and $KI name that let @LINK/@ROUTE find it, and the two 16-byte special-key translation tables plus the five-byte CLR-key bracket table.
52C5H - $KI Resident Keyboard Scan Driver
The resident keyboard driver proper, reached through the header JR at 5298H. It services the keyboard-interrupt task chain, scans and debounces the eight keyboard matrix rows, applies SHIFT/CTRL/CAPS, resolves the key through the translation tables, and manages the key-repeat delay and rate.
5403H - TYPE: Type-Ahead Installer
Runs only when the TYPE switch was given. Reserves (or reuses at 4DF2H) space for the $TA module, patches its chained-driver and link cells, copies it up, hooks it in front of $KI and adds its interrupt task through @ADTSK.
5484H - Keyboard DCB / KIJCL Hook Subroutine
Shared by all three installers. Writes the new driver entry in Register Pair HL into KIDCB$+1/+2 (4016H/4017H), or into KIJCL$ (43BEH) when a JCL job holds the real driver address, so that the module just installed becomes the current *KI driver.
5496H - $TA Resident Type-Ahead Module
The resident type-ahead body copied into high memory. Header (JR entry, link word, $TA name) followed by the character-fetch and 128-byte ring-buffer code. Its chained-driver address (54D8H/54F9H) and link word (5498H) are patched by the installer rather than through the relocator.
5547H - JKL: Screen-Print Installer
Runs only when the JKL switch was given. Reserves (or reuses at 4DF4H) space for the $JKL module, patches its chained-driver (55A9H) and link (55A1H) cells, copies it up, hooks it in front, and returns to DOS through @EXIT.
559FH - $JKL Resident Screen-Print Module
The resident screen-print body. Header (JR entry, link word, $JKL name) followed by code that, when the screen-print key is seen, walks video RAM 3C00H-3FFFH and sends each line to *PR through the ROM @PRT vector, translating non-ASCII to a period unless the graphics option is set.
55F5H - Module Relocator
Adds a fixed displacement to every address word named in the relocation table at (IY), converting the $KI body's in-image references to their high-memory equivalents. Entered with Register Pair HL = old base, Register Pair DE = new base.
5613H - Error Message Exits
The four error paths. Each loads the message address, logs it through @LOGOT and aborts (or, for a parameter error, raises error 6CH through @ERROR).
DD-prefix skip: entering here (past the DD at 5616H) executes 21 E5 56 as LD HL,56E5H, the JKL is already active! message.
DD-prefix skip: entering here (past the DD at 561AH) executes 21 AD 56 as LD HL,56ADH, the KI/dvr is already active! message.
DD-prefix skip: entering here (past the DD at 561EH) executes 21 C7 56 as LD HL,56C7H, the Type-ahead is already active! message.
562DH - Message Block
The sign-on banner and the four installer error strings, each terminated by 0DH or 0AH.
56FCH - Parameter Table (@PARAM)
The table @PARAM scans at 5216H. Eight 8-byte entries, each a six-character blank-padded keyword followed by the two-byte address @PARAM acts on, terminated by 00H. Every parameter appears twice, spelled out and abbreviated to its first letter.