                page    64,132

Comment @
    This is a patch to the ROM BIOS keyboard routine.  When the patch is
    loaded, the Alt, Ctrl, Left Shift and Right Shift keys toggle between
    their shifted and unshifted states.

    There are two main entry points.

    1.  'Keyloc' sets up the keyboard interupt vector to point to the patch
        instead of the normal BIOS routine.  After Keyloc executes, it
        leaves the new interupt handler resident in memory.

    2.  'Ikeyloc' is the new keyboard interupt handler.  The first section
        is a duplication of the ROM BIOS routine until a point where tests
        for the input of a Alt, Ctrl, Left Shift or Right Shift key can be
        made.  If it is one of those keys, the appropriate bits in the
        keyboard flag byte are twiddled.

    John Black
    5225 Pooks Hill Rd. #1715 N.
    Bethesda MD. 20814
@

port_b          equ     60h     ; 8255 port a addr
timer           equ     40h
kb_data         equ     60h     ; keyboard scan code port
kb_ctl          equ     61h     ; control bits for keyboard sense data

abso            segment at 0
                org     24h
kb_vector       label   dword
abso            ends

rom_version = 3

   if   (rom_version eq 1) or (rom_version eq 2)
bioscode        segment at 0F000h
;
;  Location definitions from Technical Reference Manual for Versions
;  04/24/81 or 10/19/81 of BIOS rom.
;
                org     0E9AFh          ; page A-27 of Technical Reference
K16             label   far             ; resume code if not shift

                org     0EA23h          ; page A-27
K23             label   far             ; break for shift key

                org     0EA5Eh          ; page A-27
K26             label   far             ; return from interupt

                org     0EC24h          ; page A-31
K62             label   far             ; error
bioscode        ends
   endif


    if  rom_version eq 3
bioscode        segment at 0F000h
;
;  Location definitions from Technical Reference Manual for Version
;  10/27/82 of BIOS rom
;
                org     0E9ADh          ; page A-30 of Technical Reference
K16             label   far             ; resume code if not shift

                org     0EA1Eh          ; page A-30
K23             label   far             ; break for shift key

                org     0EA59h          ; page A-31
K26             label   far             ; return from interupt

                org     0EC27h          ; page A-35
K62             label   far             ; error
bioscode        ends
    endif

biosdata        segment at 40h

                org     17h
kb_flag         label   byte
;       ...bit definitions for kb_flag
ins_state       equ     80h             ; insert state is active
caps_state      equ     40h             ; caps lock state has been toggled
num_state       equ     20h             ; num lock state has been toggled
scroll_state    equ     10h             ; scroll lock state has been toggled
alt_shift       equ     08h             ; alternate shift key depressed
ctl_shift       equ     04h             ; control shift key depressed
left_shift      equ     02h             ; left shift key depressed
right_shift     equ     01h             ; right shift key depressed

;       ...scan codes
num_key         equ     69
scroll_key      equ     70
alt_key         equ     56
ctl_key         equ     29
caps_key        equ     58
left_key        equ     42
right_key       equ     54
ins_key         equ     82
del_key         equ     83
biosdata        ends

keylocs segment para public 'CODE'
                assume  cs:keylocs

keyloc_ident    db      'KEYLOC'
ident_length    equ     6
ikeyloc         proc    far             ; interupt entry point
                sti                     ; begin copy of ROM BIOS
                push    ax
                push    bx
                push    cx
                push    dx
                push    si
                push    di
                push    ds
                push    es
                cld
                mov     ax,biosdata
                mov     ds,ax
                in      al,kb_data
                push    ax
                in      al,kb_ctl
                mov     ah,al
                or      al,80h
                out     kb_ctl,al
                xchg    ah,al
                out     kb_ctl,al
                pop     ax
                mov     ah,al
                cmp     al,0FFh
                jnz     keylocpatch
                jmp     K62
                                        ; End of rom code copy
keylocpatch:
                push    ax
                assume  ds:biosdata

                and     al,01111111b    ; mask out break indicator
                cmp     al,alt_key
                je      alt_in
                cmp     al,ctl_key
                je      ctrl_in
                cmp     al,left_key
                je      leftshift_in
                cmp     al,right_key
                je      rightshift_in
;               ...return to normal bios keyboard routine
retbioskey:     pop     ax
                jmp     K16


alt_in:
                mov     al,alt_shift
                call    Depress_lock
;               ... if this is a break, enter bios at K23
                pop     ax
                mov     ah,alt_shift
                test    al,80h                  ;  if
                jz      fi                      ;  .
                test    kb_flag,alt_shift       ;  .
                jnz     fi                      ;  .
                                                ;  / break and not depressed
                                                ;  .
                or      kb_flag,alt_shift       ;  .  Reset depressed flag
                jmp     K23                     ;  .  Return in case using
                                                ;  .  alt keypad entry
fi:                                             ;  fi
                jmp     K26

ctrl_in:
                mov     al,ctl_shift
                call    Depress_lock
                jmp     retdone
leftshift_in:
                mov     al,left_shift
                call    Depress_lock
                jmp     retdone
rightshift_in:
                mov     al,right_shift
                call    Depress_lock
                jmp     retdone

retdone:        pop     ax
                jmp     K26
ikeyloc         endp

;---------------------------------------------
;
;   Depress_lock
;
;       For keys that normally are depressed and held down
;       while another key is depressed. (Cntl for example)
;
;       ah      input key, with break indicator (80h)
;       al      contains depressed indicator bit mask
;
Down            db      ?       ; indicates the key is held down
Depress_lock    proc    near
                push    dx
                test    ah,80h                  ; if1
                jnz     fi1                     ; .
                test    Down,al                 ; .
                jnz     fi1                     ; .
                                                ; / make and not down
                test    kb_flag,al              ; .   if2
                je      else2                   ; .   .
                                                ; .   / depressed
                call    high_beep               ; .   .
                call    delay                   ; .   .
                call    low_beep                ; .   .
                mov     dl,al                   ; .   . dl:=depressed bit
                not     dl                      ; .   .
                and     kb_flag,dl              ; .   . key := not depressed
                jmp     fi1                     ; .   .
else2:                                          ; .   / not depressed
                call    low_beep                ; .   .
                call    delay                   ; .   .
                call    high_beep               ; .   .
                or      kb_flag,al              ; .   . key := depressed
fi1:                                            ; fi1 fi2

                test    ah,80h                  ; if5
                jnz     else5                   ; .
                                                ; / make
                or      Down,al                 ; .  down := true
                jmp     fi5                     ; .
else5:                                          ; / break
                mov     dl,al                   ; .
                not     dl                      ; .
                and     Down,dl                 ; .  down := false
fi5:                                            ; fi5
                pop     dx
                ret
Depress_lock    endp


;---------------------------------
h_cycles        equ     20
h_half          equ     300
l_cycles        equ     h_cycles / 3
l_half          equ     h_half * 3

low_beep        proc
                push    dx
                push    bx
                mov     bx,l_cycles
                mov     dx,l_half
                call    beep
                pop     bx
                pop     dx
                ret
low_beep        endp

high_beep       proc
                push    dx
                push    bx
                mov     bx,h_cycles
                mov     dx,h_half
                call    beep
                pop     bx
                pop     dx
                ret
high_beep       endp

;---------------------------------
; Adapted from BIOS Beep routine
;
;  bx = # of cycles
;  dx = length of half cycle

beep            proc    near
                push    ax
                push    cx
                in      al,kb_ctl
                push    ax
k65:
                and     al,0FCh
                out     kb_ctl,al
                mov     cx,dx
k66:            loop    k66
                or      al,2
                out     kb_ctl,al
                mov     cx,dx
k67:            loop    k67
                dec     bx
                jnz     k65
                pop     ax
                out     kb_ctl,al
                pop     cx
                pop     ax
                ret
beep            endp

;--------------------------------
delay           proc    near
                push    cx
                mov     cx,15000
                loop    $
                pop     cx
                ret
delay           endp

ikeylast        equ     $       ; last location + 1 to remain in memory

stacks          segment stack 'STACK'
                db      64 dup('stack   ')
stacks          ends

;-------------------------------------
loaded_mess     db      'KEYLOC loaded$'
err_mess        db      'error - KEYLOC is already loaded$'
keyloc          proc    far
                push    ds
                sub     ax,ax
                push    ax
                push    ds                      ; save program seg

;               ...Check to see if a copy of KEYLOC has
;                  already been loaded by comparing Keyloc_ident
;                  with the same locations relative the the current
;                  keyboard interupt routine ( it is pointed to by the
;                  the keyboard interupt vector)
                cld                                     ; increment
                assume  ds:abso
                mov     ax,abso
                mov     ds,ax
                mov     si,offset keyloc_ident
                mov     ax,kb_vector+2                  ; segment
                mov     di,kb_vector                    ; offset to ident
                sub     di,ident_length
                mov     es,ax
                assume  ds:keylocs
                mov     ax,keylocs
                mov     ds,ax
                mov     cx,ident_length
                repne   cmpsb                           ; ds:[si],es:[di]
                cmp     cx,0                            ; if
                je      NotLoaded                       ; .
                                                        ; / already loaded
                mov     ah,9h                           ; .  print string
                mov     dx,offset err_mess              ; .
                int     21h                             ; .
                pop     ds                              ; . get rid of pseg
                ret                                     ; . return to DOS
NotLoaded:                                              ; fi

;               ...set up keyboard interupt vector
                mov     ah,9h
                mov     dx,offset loaded_mess
                int     21h
                assume  ds:abso
                mov     ax,abso
                mov     ds,ax
                mov     ax,offset ikeyloc               ; ip
                mov     kb_vector,ax
                mov     ax,seg ikeyloc                  ; cs
                mov     kb_vector+2,ax
;               ...return to DOS, but leave ikeyloc resident
                pop     es                              ; get program segment
                mov     es:byte ptr 1,27h               ; change int 20 to 27
                mov     dx,offset ikeylast+100h         ; dx:=lastaddr+1+prfx
                ret
keyloc          endp

keylocs         ends

                end     keyloc
