;Assembly language file for ENHKBD unit
;Written by Kim Kokkonen, TurboPower Software

;Version 1.0, 1/3/88
;  initial release

;Version 1.1, 1/29/88
;  support the NumLock key!

;Version 1.2, 2/23/88, with help from Brian Foley
;  don't let NumLock key affect dedicated cursor pad on enhanced keyboard
;  minimize conflict with QuickKeys and other int 9 handlers
;  allow Ctrl-Alt-Del with int 9 installed
;  add method to disable handlers temporarily
;  use safer test for enhanced keyboard
;  support alt-keypad keys with Alt-LeftShift combination
;  add RestoreKbdVectors routine for use by TSR's

;Released to the public domain

DATA    SEGMENT BYTE PUBLIC
        EXTRN   HasEnhancedKbd : BYTE
        EXTRN   EnableEnhanced : BYTE
DATA    ENDS

CODE    SEGMENT BYTE PUBLIC

        ASSUME  CS:CODE, DS:DATA

        PUBLIC  GetIntVec, SetIntVec
        PUBLIC  InitVectors
        PUBLIC  EnhancedKbd
        PUBLIC  NewInt09, NewInt16

;CS-relative variables, easily accessible to interrupt handlers
PrevInt09       LABEL DWORD
PrevInt09Ofs    DW ?
PrevInt09Seg    DW ?
PrevInt16       LABEL DWORD
PrevInt16Ofs    DW ?
PrevInt16Seg    DW ?

;*********************************************************** GetIntVec
;Return an interrupt vector
;procedure GetIntVec(IntNo : Byte; var Vector : pointer);

GetIntVec PROC NEAR
        PUSH    BP
        MOV     BP,SP
        MOV     AL,[BP+8]               ;Get interrupt number
        MOV     AH,35h
        INT     21h
        MOV     AX,ES                   ;Save interrupt segment
        LES     DI,[BP+4]               ;ES:DI => result
        MOV     ES:[DI],BX              ;Offset
        MOV     ES:[DI+2],AX            ;Segment
        MOV     SP,BP
        POP     BP
        RET     6
GetIntVec ENDP

;*********************************************************** SetIntVec
;Set an interrupt vector
;procedure SetIntVec(IntNo : Byte; Vector : Pointer);

SetIntVec PROC NEAR
        MOV     BX,SP                   ;Set up stack frame
        PUSH    DS
        LDS     DX,SS:[BX+2]            ;Get interrupt address
        MOV     AL,SS:[BX+6]            ;Get interrupt number
        MOV     AH,25h
        INT     21h
        POP     DS
        RET     6
SetIntVec ENDP

;*********************************************************** EnhancedKbd
;See if enhanced keyboard BIOS installed
;function EnhancedKbd : Boolean;

EnhancedKbd PROC NEAR
        MOV     AH,05                   ;Stuff buffer function
        MOV     CX,0FFFFh               ;Stuff FFFF
        INT     16h
        MOV     AH,11h                  ;Read enhanced function
        INT     16h
        CMP     AX,0FFFFh               ;Did we read the FFFF?
        MOV     AX,0                    ;Prepare for False
        JNZ     EnhDone
        MOV     AH,10h                  ;Get the key out of the buffer
        INT     16h
        MOV     AX,1                    ;Set to true
EnhDone:
        RET
EnhancedKbd ENDP

;*********************************************************** NewInt09
;Handle hardware keyboard interrupts

IndexTable        LABEL BYTE            ;Table of indexes into WordTable
        DB       0FFh   ;00h
        DB        00    ;01h - Esc
        DB       0FFh   ;02h
        DB       0FFh   ;03h
        DB       0FFh   ;04h
        DB       0FFh   ;05h
        DB       0FFh   ;06h
        DB       0FFh   ;07h
        DB       0FFh   ;08h
        DB       0FFh   ;09h
        DB       0FFh   ;0Ah
        DB       0FFh   ;0Bh
        DB       0FFh   ;0Ch
        DB       0FFh   ;0Dh
        DB        01    ;0Eh - Backspace
        DB        02    ;0Fh - Tab
        DB       0FFh   ;10h
        DB       0FFh   ;11h
        DB       0FFh   ;12h
        DB       0FFh   ;13h
        DB       0FFh   ;14h
        DB       0FFh   ;15h
        DB       0FFh   ;16h
        DB       0FFh   ;17h
        DB       0FFh   ;18h
        DB       0FFh   ;19h
        DB        03    ;1Ah - Left Brack
        DB        04    ;1Bh - Right Brack
        DB        05    ;1Ch - Enter
        DB       0FFh   ;1Dh
        DB       0FFh   ;1Eh
        DB       0FFh   ;1Fh
        DB       0FFh   ;20h
        DB       0FFh   ;21h
        DB       0FFh   ;22h
        DB       0FFh   ;23h
        DB       0FFh   ;24h
        DB       0FFh   ;25h
        DB       0FFh   ;26h
        DB        06    ;27h - Semicolon
        DB        07    ;28h - Quote
        DB        08    ;29h - Backquote
        DB       0FFh   ;2Ah
        DB        09    ;2Bh - Backslash
        DB       0FFh   ;2Ch
        DB       0FFh   ;2Dh
        DB       0FFh   ;2Eh
        DB       0FFh   ;2Fh
        DB       0FFh   ;30h
        DB       0FFh   ;31h
        DB       0FFh   ;32h
        DB        10    ;33h - Comma
        DB        11    ;34h - Period
        DB        12    ;35h - Slash
        DB       0FFh   ;36h
        DB        13    ;37h - Pad-Asterisk
        DB       0FFh   ;38h
        DB       0FFh   ;39h
        DB       0FFh   ;3Ah
        DB       0FFh   ;3Bh
        DB       0FFh   ;3Ch
        DB       0FFh   ;3Dh
        DB       0FFh   ;3Eh
        DB       0FFh   ;3Fh
        DB       0FFh   ;40h
        DB       0FFh   ;41h
        DB       0FFh   ;42h
        DB       0FFh   ;43h
        DB       0FFh   ;44h
        DB       0FFh   ;45h
        DB       0FFh   ;46h
        DB        14    ;47h - Home
        DB        15    ;48h - Up
        DB        16    ;49h - PgUp
        DB        17    ;4Ah - Pad-Minus
        DB        18    ;4Bh - Left
        DB        19    ;4Ch - Pad-5
        DB        20    ;4Dh - Right
        DB        21    ;4Eh - Pad-Plus
        DB        22    ;4Fh - End
        DB        23    ;50h - Down
        DB        24    ;51h - PgDn
        DB        25    ;52h - Insert
        DB        26    ;53h - Del

WordTable         LABEL WORD            ;Table of Scan words to return
        ;         Control   Alt
        DW       0FFFFh,    0100h  ;Esc
        DW       0FFFFh,    0E00h  ;Backspace
        DW        9400h,   0A500h  ;Tab
        DW       0FFFFh,    1A00h  ;Left Brack
        DW       0FFFFh,    1B00h  ;Right Brack
        DW       0FFFFh,    1C00h  ;Enter
        DW       0FFFFh,    2700h  ;Semicolon
        DW       0FFFFh,    2800h  ;Quote
        DW       0FFFFh,    2900h  ;Backquote
        DW       0FFFFh,    2B00h  ;Backslash
        DW       0FFFFh,    3300h  ;Comma
        DW       0FFFFh,    3400h  ;Period
        DW       0FFFFh,    3500h  ;Slash
        DW       0FFFFh,    3700h  ;Pad-Asterisk
        DW       0FFFFh,    9700h  ;Home
        DW        8D00h,    9800h  ;Up
        DW       0FFFFh,    9900h  ;PgUp
        DW        8E00h,    4A00h  ;Pad-minus
        DW       0FFFFh,    9B00h  ;Left
        DW        8F00h,    9C00h  ;Pad-5
        DW       0FFFFh,    9D00h  ;Right
        DW        9000h,    4E00h  ;Pad-Plus
        DW       0FFFFh,    9F00h  ;End
        DW        9100h,   0A000h  ;Down
        DW       0FFFFh,   0A100h  ;PgDn
        DW        9200h,   0A200h  ;Insert
        DW        9300h,   0A300h  ;Del

        BiosShiftFlags  EQU BYTE PTR 17h  ;Addresses in BIOS data area
        BufferHead      EQU WORD PTR 1Ah
        BufferTail      EQU WORD PTR 1Ch
        BufferStart     EQU WORD PTR 80h
        BufferEnd       EQU WORD PTR 82h

NewInt09 PROC FAR
        STI                             ;Interrupts on
        PUSH    AX                      ;Save registers we use
        PUSH    BX
        PUSH    CX
        PUSH    DS

        MOV     AX,SEG DATA
        MOV     DS,AX                   ;Point to Turbo data area
        CMP     EnableEnhanced,0        ;Enhanced functions enabled?
        JZ      Int09Orig               ;No, get out

        MOV     AX,0040h
        MOV     DS,AX                   ;Point to BIOS data area

        MOV     AL,DS:BiosShiftFlags    ;Shift status in AL
        MOV     CL,AL                   ;Save shift state in CL
        TEST    AL,00001100b            ;Either Control or Alt depressed?
        JZ      TestPad5                ;No, check special case of Pad-5

        AND     AL,00001100b            ;Just Ctrl-Alt bits
        CMP     AL,00001100b            ;Both Ctrl and Alt depressed?
        JE      Int09Orig               ;Get out in case of Ctrl-Alt-Del

        MOV     AL,CL                   ;Restore shift state
        AND     AL,00001111b            ;All shift bits
        CMP     AL,00001010b            ;Just Alt-LeftShift?
        JE      Int09Orig               ;Get out so Alt-Keypad works

        IN      AL,60h                  ;Read scan code
        CMP     AL,53h                  ;Is it in range 0..53h?
        JA      Int09Orig               ;If not, pass on to BIOS int 09 handler

        MOV     BX,offset IndexTable    ;Point to index table
        XLAT    BYTE PTR CS:[0]         ;Get index
        CMP     AL,0FFh                 ;AL = FFh?
        JNZ     MatchedScan             ;Special case if AL <> FFh

Int09Orig:                              ;Let BIOS int 09 handler take care of it
        POP     DS                      ;Restore registers
        POP     CX
        POP     BX
        POP     AX
        JMP     PrevInt09               ;Transfer to previous interrupt 09

TestPad5:
        IN      AL,60h                  ;Read scan code
        CMP     AL,4Ch                  ;Pad-5 key?
        JNZ     Int09Orig               ;No, get out
        MOV     AX,4C00h                ;Set up scan code to return
        TEST    CL,00100000b            ;Is NumLock set?
        JZ      NoNumLock               ;No, it's not
NumLockSet:
        TEST    CL,00000011b            ;Is a shift key depressed?
        JNZ     StoreScanWord           ;Yes, we want to store 4C00 in buffer
        JMP SHORT Int09Orig             ;No, let original int 9 do it
NoNumLock:
        TEST    CL,00000011b            ;Is a shift key depressed?
        JZ      StoreScanWord           ;No, store scan word
        JMP SHORT Int09Orig             ;Yes, let original int 9 do it

MatchedScan:
        SHL     AX,1                    ;Multiply scan index by 2
        SHL     AX,1                    ;Multiply by 2 again
        MOV     BX,AX                   ;BX = Index * 4
        TEST    CL,00001000b            ;Alt depressed?
        JZ      NoAltKey                ;No, use first column of table
        ADD     BX,2                    ;Yes, use second column of table
NoAltKey:
        MOV     AX,CS:WordTable[BX]     ;Get the scan word
        CMP     AX,0FFFFh               ;Case handled by BIOS?
        JZ      Int09Orig               ;Yes, get out of here

StoreScanWord:
        MOV     CX,AX                   ;Save scan word in CX
        IN      AL,61h                  ;Read control port value
        MOV     AH,AL                   ;Save in AH
        OR      AL,80h                  ;Set high bit
        OUT     61h,AL                  ;Reset keyboard
        MOV     AL,AH                   ;Retrieve original value
        OUT     61h,AL                  ;Enable keyboard
        CLI                             ;Stop CPU interrupts
        MOV     AL,20h                  ;End of interrupt
        OUT     20h,AL                  ;To the interrupt controller

        MOV     BX,DS:BufferTail        ;Point to end of keyboard buffer
        MOV     AX,BX                   ;Transfer to AX
        ADD     AX,0002                 ;Advance to next position
        CMP     AX,DS:BufferEnd         ;Wrap around if needed
        JNE     CheckFull               ;No need to wrap
        MOV     AX,DS:BufferStart       ;Else to beginning of buffer
CheckFull:
        CMP     AX,DS:BufferHead        ;Bumping in to start?
        JE      NewInt09Done            ;Exit if full
        MOV     [BX],CX                 ;Store keystroke
        MOV     DS:BufferTail,AX        ;Advance tail
NewInt09Done:
        STI                             ;Interrupts on
        POP     DS                      ;Restore registers
        POP     CX
        POP     BX
        POP     AX
        IRET                            ;Return to caller
NewInt09 ENDP

;*********************************************************** NewInt16
;Handle software keyboard interrupts
NewInt16 PROC FAR
        PUSH    DX                      ;Save registers we use
        PUSH    DS
        PUSHF                           ;Save flags
        STI                             ;Allow interrupts

        MOV     DX,SEG DATA
        MOV     DS,DX                   ;Assure DS points to Turbo data
        CMP     EnableEnhanced,0        ;Enhanced functions enabled?
        JZ      Int16Orig               ;No, pass to previous int 16
        CMP     HasEnhancedKbd,0        ;Is enhanced keyboard installed?
        JZ      Int16Orig               ;No, leave call alone

        OR      AH,AH                   ;AH=0?
        JNZ     TryAH1                  ;No, try AH=1
AH0:
        OR      AH,10h                  ;Use enhanced BIOS call instead
        CALL    PrevInt16               ;Call previous int 16 (pops flags)
        OR      AH,AH                   ;AH=0?
        JZ      AH0Done                 ;Yes, done
        CMP     AL,0E0h                 ;AL=E0h?
        JNZ     AH0Done                 ;No, leave it alone
        XOR     AL,AL                   ;Clear low byte of scan word
AH0Done:
        POP     DS                      ;Restore registers
        POP     DX
        IRET                            ;Return to caller

TryAH1:
        CMP     AH,1                    ;AH=1?
        JNZ     Int16Orig               ;No, let original int 16 do it
AH1:
        OR      AH,10h                  ;Use enhanced BIOS call instead
        CALL    PrevInt16               ;Call previous int 16 (pops flags)
        PUSHF                           ;Save result flags
        JZ      AH1Done                 ;Just return if no key available
        OR      AH,AH                   ;AH=0?
        JZ      AH1Done                 ;Yes, done
        CMP     AL,0E0h                 ;AL=E0h?
        JNZ     AH1Done                 ;Yes, clear it
        XOR     AL,AL                   ;Clear low byte of scan word
AH1Done:
        POPF                            ;Restore result flags
        POP     DS                      ;Restore registers
        POP     DX
        RET     2                       ;Return with flags intact

Int16Orig:
        POPF                            ;Restore flags
        POP     DS
        POP     DX
        JMP     PrevInt16               ;Let old int16 handle the rest
NewInt16 ENDP

;*********************************************************** InitVectors
;Save and setup interrupt vectors
InitVectors PROC NEAR
        MOV     AX,3509h                ;Get current int 9
        INT     21h
        MOV     PrevInt09Ofs,BX         ;Save it in CS-relative variables
        MOV     PrevInt09Seg,ES
        PUSH    DS
        PUSH    CS
        POP     DS
        MOV     DX,offset NewInt09
        MOV     AX,2509h                ;Install new int 9
        INT     21h
        POP     DS
        MOV     AX,3516h                ;Get current int 16
        INT     21h
        MOV     PrevInt16Ofs,BX         ;Save it in CS-relative variables
        MOV     PrevInt16Seg,ES
        PUSH    DS
        PUSH    CS
        POP     DS
        MOV     DX,offset NewInt16
        MOV     AX,2516h                ;Install new int 16
        INT     21h
        POP     DS
        RET
InitVectors ENDP

CODE    ENDS
        END
