;---------------------------------------------------------------------------
;KBM KeyBoard Mouse            by Dan Rollins                       5-20-85
;
; This program intercepts keyboard data and creates a bit pattern determined
; according to whether or not certain keys are currently being pressed.
;
; The bit pattern is stored in the "inter-application communication area"
;  at 0000:04f0.  It is interpreted as:
;
;  7 6 5 4 3 2 1 0   (bit number)
;  C m P H l d r u   (bit name)
;  | | | | | | | |
;  | | | | | | | +- bit 0 (01h) - set = 1 while [up arrow] is pressed
;  | | | | | | +--- bit 1 (02h) - set = 1 while [right arrow] is pressed
;  | | | | | +----- bit 2 (04h) - set = 1 while [down arrow] or [5] is pressed
;  | | | | +------- bit 3 (08h) - set = 1 while [left arrow] is pressed
;  | | | |
;  | | | +--------- bit 4 (10h) - set = 1 while [Home] is pressed
;  | | +----------- bit 5 (20h) - set = 1 while [PgUp] is pressed
;  | +------------- bit 6 (40h) - set = 1 while grey [-] is pressed
;  +--------------- bit 7 (80h) - set = 1 while [CapsLock] is pressed
;
; As soon as the key is released, the relevant bit is reset to 0.
;
; The byte at 0000:04f1 is the "pass-through/filter" mode flag.  When this
; byte is zero, all keystrokes are passed to the normal keyboard handler.
; When it's non-zero, the selected keystrokes are filtered (disabled for
; normal input).  BIOS and DOS keyboard calls will not recognize them.
;
; The Alt-NumLock keystroke toggles between pass-through and filter modes.
;
; This program is installed and remains resident.  It is a COM-format
; file, so it must be converted with EXE2BIN.
;
; Copyright (c) Ziff-Davis Publishing Co., 1986.  All rights reserved.
;
;= equates ===============

KB_DATA_PORT  equ     60h  ;These are listed in the PC and XT
KB_CTRL_PORT  equ     61h  ; Technical Reference Manuals

KB_FLAG       equ    417h  ; the BIOS shift-key status (in segment 0)
ALT_STATE     equ      8   ;  Bit pattern while the [Alt] key is pressed
NUMLOCK_KEY   equ     69   ;  scan-code of the [NumLock] key

INT_CTL_PORT  equ     20h  ; Interrupt controller port (8259 chip)
EOI           equ     20h  ; End-Of-Interrupt code sent to 8259

RELEASE_BIT   equ     80h  ;also called the "break" bit: a key was released

KEY_BITS      equ     04f0H ;the address of the key bit flags (segment 0)
MODE_FLAG     equ     04f1H ;when 0, all keys are passed to normal kbint
INST_FLAG     equ     04f2H ; set to 1234H during installation

com_seg  segment
         assume  cs:com_seg, ds:com_seg
         org     100h                    ;must have for COM-format program
kbm      proc    far
         jmp     set_up    ;get past data and install interrupt hander

;============= program data area ========

norm_kbd_int  label dword   ;type DWORD so it can be used in a FAR jump
nki_offset    dw    0       ; This address is stored in the SET_UP proc
nki_segment   dw    0       ; It's the address of the previous kbint routine

;-----------------------------------------------------------------------------
; KBD_INT
; 1) read the keyboard
; 2) set/reset bits in mouse movement byte
; 3) execute normal keyboard interrupt
;
;          scan  bit     key         suggested  meaning
;          code  flag    name        (defined by user)
;          ---- ----     ---------  ----------------------
kbm_tbl  db  72, 1  ;    num.pad 8   go up
         db  77, 2  ;    num.pad 6   go right
         db  80, 4  ;    num.pad 2   go down
         db  75, 8  ;    num.pad 4   go left

         db  76, 4  ;    num.pad 5   go down
         db  71, 16 ;    Home        button 1
         db  73, 32 ;    PgUp        button 2
         db  74, 64 ;    grey minus  button 3
         db  58, 128;    CapsLock    "high-gear shift" for fast motion
tbl_end  label byte

;-----------------------------------------------------------------------------
; KBD_INT
; This procedure intercepts the ROM-BIOS KB_INT.
; It sets and resets bits of a kbd flag as the user presses and releases keys.
; When the byte at 0000:04F1 is 0, the keystroke is passed on to the
; original keyboard handler.

kbd_int  proc    far
         sti
         cld
         push    ax
         push    si
         push    ds

         in      al,KB_DATA_PORT ;read scan-code from keyboard into AL
         mov     ah,al           ;save original byte in AH
         and     al,7fh          ;mask off "release bit" for comparisons

         mov     si,offset kbm_tbl
k_20:
         cmp     si,offset tbl_end   ;at end of table?
         ja      k_25                ; yes, key not found. Exit to normal kbint
         cmp     al,byte ptr cs:[si] ; is this the key?
         je      k_30                ;   yes, process the keystroke
         inc     si                  ;   no, point past the scan code
         inc     si                  ;       point past the bit-mask
         jmp     k_20                ;       and loop back for the next entry

k_25:
;------- check for mode-toggle by user
         cmp     ah,NUMLOCK_KEY                  ;is this a press of [NumLock]?
         jne     k_27                            ;  no, go
         sub     si,si                           ;  yes, look to BIOS data area
         mov     ds,si
         test    byte ptr ds:[KB_FLAG],ALT_STATE ;  is [Alt] pressed?
         jz      k_27                            ;    no, pass the key on

         xor     byte ptr ds:[MODE_FLAG],1       ;    yes, toggle the mode and
         jmp    short k_exit                     ;         exit w/o processing

;------- the keystroke is to be processed by the normal keyboard interrupt
k_27:
         pop     ds
         pop     si
         pop     ax
         jmp     cs:[norm_kbd_int]       ;continue at normal keyboard handler

k_30:
;------- process the scan code into a bit-pattern
         mov     al,cs:[si+1]     ;get bit-flag mask

         sub     si,si
         mov     ds,si            ;point to segment of KEY_BITS

         test    ah,RELEASE_BIT   ;is this key being released?
         jz      k_40             ; no, go

;------- process key release
         not     al                           ;flip-flop mask bits
         and     byte ptr ds:[KEY_BITS],al    ;mask off released key bit
         jmp     k_50
k_40:
;------- process key press
         or      byte ptr ds:[KEY_BITS],al    ;set the bit for pressed key

;------- determine whether key should be passed on to normal keyboard handler
k_50:
         cmp     byte ptr ds:[MODE_FLAG],0    ;should key be processed further?
         je      k_27                         ; yes, continue at normal kb int

;------- the keystroke is to be ignored by the rest of the system.
;------- wrap up this keyboard interrupt.

k_exit:
         in      al,KB_CTRL_PORT ;get current value of keyboard control lines
         mov     ah,al           ; save it
         or      al,80h          ;set the "enable kbd" bit
         out     KB_CTRL_PORT,al ; and write it out the control port
         xchg    ah,al           ;fetch the original control port value
         out     KB_CTRL_PORT,al ; and write it back

         pop     ds
         pop     si

         cli
         mov     al,EOI           ;send End-Of-Interrupt signal
         out     INT_CTL_PORT,al  ; to the 8259 Interrupt Controller
         pop     ax
         iret                     ;exit to interrupted program
kbd_int  endp

LAST_BYTE equ   offset $+1  ;This is the address passed to INT 27H
                            ;Notice that the code of the SET_UP
                            ;  procedure is not preserved in memory

;-----------------------------------------------------------------------------
; SET_UP
; This routine is executed only once, when the program is installed.

inst_msg db 'KBM  KeyBoard Mouse driver',0dh,0ah
         db 'Copyright (c) 1986 Ziff-Davis Publishing Co.,',0dh,0ah,'$'

err_msg1 db 07,'Already installed',0dh,0ah,'$'
err_msg2 db 'Wrong DOS version.',0dh,0ah,'$'

set_up   proc    near

;------- make sure this is DOS 2.0 or later
         mov     ah,30h
         int     21h
         cmp     al,2
         jae     su_10
         mov     dx,offset err_msg2
         jmp     msg_exit
su_10:

;------- see if KBM has already been installed
         mov     ax,0
         mov     es,ax
         cmp     es:[INST_FLAG],1234H  ;already installed?
         jne     su_20                 ;  no, continue
         mov     dx,offset err_msg1    ;  yes, exit with message
         jmp     msg_exit
su_20:
         mov     word ptr es:[INST_FLAG],1234h  ; flag says KBM is installed

;------- save the old kbint vector and set up the new one
         mov     al,9
         mov     ah,35h         ;DOS GET_VECTOR service
         int     21h            ; for interrupt 9 (KBINT)

         mov     al,9           ;get address of the current kb int handler
         mov     ah,35h         ;DOS GET_VECTOR service
         int     21h
         mov     nki_segment,es ;save old address
         mov     nki_offset,bx

         mov     dx,offset kbd_int  ;set INT 9 to local keyboard interceptor
         mov     al,9               ;set vector for INT 9 to DS:DX
         mov     ah,25h             ;DOS SET_VECTOR service
         int     21h

         mov     ax,0
         mov     es,ax                          ;initialize variables:
         mov     byte ptr es:[MODE_FLAG],0      ; process all keystrokes
         mov     byte ptr es:[KEY_BITS],0       ; no keys are pressed

;------- display message to indicate install`tion complete
         mov     dx,offset inst_msg
         mov     ah,9
         int     21h

;------- exit to DOS, leaving the interrupt handler resident
         mov     dx,LAST_BYTE
         int     27h

msg_exit:
         mov     ah,9
         int     21h
         int     20h
set_up   endp
kbm      endp
com_seg  ends
         end     kbm

