;---------------------  KEYBOARD BUFFER EXTENDER  -------- EN#1.0SRW02R ---[]
; By - Titas Raha  (27/11/93) System Research Work # 2 (Resident program)   :
; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~   :
; Extends the keyboard buffer length by passing the additional keys through :
; a generated que.  Must be converted into a .COM file                      :

MAXKEYS            EQU    128               ; Added Keyboard buffer length
BIOSSEG            EQU    40h               ; Dada segment of the BIOS
GETKBD             EQU    3509h             ; Get INT Vector Keyboard 9
SETKBD             EQU    2509h             ; Set INT Vector Keyboard 9
GETTIM             EQU    351Ch             ; Get INT Vector Timer 1C
SETTIM             EQU    251Ch             ; Set INT Vector Timer 1C
GETBRK             EQU    351Bh             ; Get INT Vector Break 1B
SETBRK             EQU    251Bh             ; Set INT Vector Break 1B
KBD_CTL            EQU    61h               ; Keyboard control port address
EOI                EQU    20h               ; End of int No.
SPKCYOFF           EQU    28h               ; Iterations at off position
SPKCYON            EQU    68h               ; Iterations at on position
SPKCYCLES          EQU    80h               ; No. of speaker cycles

code               SEGMENT
                   ASSUME cs:code,ds:code

                   ORG 1Ah
buffhead           LABEL  WORD              ; Keyboard buffer head
                   ORG 1Ch
bufftail           LABEL  WORD              ; Keyboard buffer tail
                   ORG 80h
buffstart          LABEL  WORD              ; Location of the Keyboard buffer
                   ORG 82h
buffend            LABEL  WORD              ; Pointer to the end of buffer
                   ORG 100h                 ; In anticipation of .COM file

                                            ; Length of the resident program

RT                 LABEL WORD               ; Top of the resident program

start:             jmp    newloc            ; Jump away from
                                            ;    the resident data area

org_kbd            dw     0,0               ; Original Keyboard handler INT 9
org_tim            dw     0,0               ; Original Timer INT 1C handler
org_brk            dw     0,0               ; Original Break INT 1B handler
que_head           dw     QUE_START         ; Initial Head pointer of the que
que_tail           dw     QUE_START         ; Initial Tail pointer of the que

QUE_START          EQU    OFFSET qs         ; Start of the que
QUE_END            EQU    OFFSET qe         ; End of the que

qs                 LABEL  WORD              ; Que's physical start address
que                dw     MAXKEYS DUP('U'*256+'Q')
                                            ; Que for storing additional keys
qe                 LABEL  WORD              ; Que's physical end address

newbrk             PROC   FAR               ; New break procedure
;Clears the que with every invocation of the break interrupt

                   mov    cs:que_head,QUE_START  ; Make que head equal to
                   mov    cs:que_tail,QUE_START  ;    que tail.
                   jmp    dword ptr cs:[org_brk] ; Original Break interrupt

newbrk             ENDP

newkbd             PROC   FAR               ; The new rerouted keyboard INT 9
; Gets the extra keys from the keyboard buffer and saves it in a que.

                   push   ax                ; Save registers to use
                   push   ds
                   pushf                    ; Will be restored by the handler
                   mov    ax,BIOSSEG
                   mov    ds,ax             ; Set to BIOS's data segment
                   mov    ax,bufftail       ; Save the old tail pointer value
                   call   dword ptr cs:[org_kbd] ; Call the original handler
                   push   bx                ; Save registers to use
                   push   si
                   mov    bx,ax             ; Save tail pointer value to bx
                   mov    ax,bufftail       ; Get the new buffer position
                   mov    si,ax             ; Save in SI
                   inc    ax                ; Go to next buffer position
                   inc    ax                ; Inc 2 since it is of word length
                   cmp    ax,buffend        ; Is it end of the buffer ?
                   jne    bptrok            ; No, jump
                   mov    ax,buffstart      ; Yes, set to start of buffer
bptrok:            cmp    ax,buffhead       ; Tail touched the head ?
                   je     branch            ; If yes, jump
                   cmp    si,bx             ; Is the buffer changed by handler
                   je     kbdone            ; No, nothing to do, leave
                   mov    ax,cs:que_tail    ; Get tail ptr of the extra buffer
                   cmp    ax,cs:que_head    ; Is it equal to the head ?
                   je     kbdone            ; Yes, X buffer empty no probs
attach:            mov    si,[bx]           ; Get the char that just came in
                   mov    bufftail,bx       ; as if no change in the buffer
                   mov    ax,cs             ; set DS to the programs DATA
                   mov    ds,ax             ;    segment.
                   mov    ax,que_tail       ; Get the X buffer tail ptr
                   mov    bx,ax             ; Save it
                   inc    ax                ; Goto next X buffer position
                   inc    ax
                   cmp    ax,QUE_END        ; Is it at the end of the que ?
                   jne    qptrok            ; No, jump
                   mov    ax,QUE_START      ; Yes, set it to start of the que
qptrok:            cmp    ax,que_head       ; Que head touched the tail ?
                   je     beep              ; Yes, buffer full beep
                   mov    que_tail,ax       ; No, advance the X buffer tail
                   mov    [bx],si           ; Save the char in X buffer.

kbdone:            pop    si                ; Restore the stack and registers
                   pop    bx
                   pop    ds
                   pop    ax

                   iret                     ; Interrupt return

beep:              push   cx                ; Save CX
                   mov    al,EOI            ; End of interrupt command
                   out    20h,al            ; Send command to INT control port
                   mov    bx,SPKCYCLES      ; No of cycle
                   in     al,KBD_CTL        ; Get control information
                   push   ax                ; Save information
atoffstate:        and    al,0FCh           ; Turn off timer gate and speaker
                   out    KBD_CTL,al        ; Output to control
                   mov    cx,SPKCYOFF       ; No. of empty iterations
itr1:              loop   itr1              ; Speaker off
                   or     al,2              ; Turn on speaker bit
                   out    KBD_CTL,al        ; Output to control
                   mov    cx,SPKCYON        ; Again set up count
itr2:              loop   itr2              ; With speaker at on position
                   dec    bx                ; Total time count
                   jnz    atoffstate        ; Do another cycle
                   pop    ax                ; Recover control
                   out    KBD_CTL,al        ; output the control
                   pop    cx                ; Recover CX
                   jmp    kbdone

branch:            cmp    si,buffstart      ; Current tail ptr = buffer start?
                   jne    posfound          ; No jump
                   mov    si,buffend        ; Yes, make it at end
posfound:          dec    si                ; Get the previous position
                   dec    si
                   mov    bx,si             ; save it in bx
                   jmp    attach            ; jump to the mainstreem

newkbd             ENDP

newtim             PROC   FAR               ; New rerouted INT 1C
; Tryies to restore the keys from the que to the keyboard buffer.

                   push   ax                ; Save registers to use
                   push   si
                   push   dx
                   push   ds

                   mov    ax,cs             ; Make DATA segment as CODE
                   mov    ds,ax
                   mov    si,que_head       ; Get the que head ptr
                   cmp    si,que_tail       ; Is it equal to the tail
                   je     timdone           ; Yes, no keys present, jump
                   mov    ax,[si]           ; Get the character from the que
                   mov    dx,BIOSSEG        ; Set DS to BIOS data
                   mov    ds,dx
                   mov    si,bufftail       ; Get the kyb buffer tail
                   mov    [si],ax           ; Store the char to the kyb buffer
                   mov    ax,buffend        ; get the end of the buffer
                   inc    si                ; Get the next of the tail ptr
                   inc    si
                   cmp    si,ax             ; Is it equal to the end of buffer
                   jne    atbend1           ; Jump if not
                   mov    si,buffstart      ; Yes, Set to the start
atbend1:           mov    dx,si             ; save next of the tail ptr
                   cmp    si,buffhead       ; Is it equal to the head ?
                   je     timdone           ; Yes, Buffer full quit
                   inc    si                ; No, get next to next of tail
                   inc    si                ;    pointer
                   cmp    si,ax             ; Is it equal to the end of buffer
                   jne    atbend2           ; Jump if not
                   mov    si,buffstart      ; Yes, Set to the start
atbend2:           cmp    si,buffhead       ; Is it equal to the head ?
                   je     timdone           ; Yes, space left for 1 char, quit
                   mov    bufftail,dx       ; Increment the tail pointer
                                            ;  ( character already saved )
                   mov    ax,cs             ; Again set DS as CS
                   mov    ds,ax
                   mov    ax,que_head       ; Get the que head pointer
                   inc    ax                ; get the next position
                   inc    ax
                   cmp    ax,QUE_END        ; Is it equal to the end ?
                   jne    atbend3           ; No, jump
                   mov    ax,QUE_START      ; Yes, Set to start of the que
atbend3:           mov    que_head,ax       ; Set the new que head position

timdone:           pop    ds                ; restore the registers
                   pop    dx
                   pop    si
                   pop    ax
                   jmp    dword ptr cs:[org_tim] ; Call the old timer INT 1C

newtim             ENDP

RB                 LABEL  WORD              ; End of the resident program

newloc:            mov    ah,9              ; Display line
                   mov    dx,OFFSET heading ; Get Heading position
                   int    21h               ; Dos function does it

                   mov    ax,GETKBD         ; Get the Keyboard interrupt
                   int    21h               ; Dos function 21h does it
                   mov    ax,es             ; Get the segment of Kyb handler
                   cmp    ax,0D000h         ; Is it above 0D000h ?
                   ja     kybnotset         ; Yes, Keyboard not re-routed

                   mov    ah,9              ; Dos function no. to print a line
                   mov    dx,OFFSET msgfail ; Get the failure message to print
                   int    21h               ; Call Dos

                   int    20h               ; Terminate as normal program

kybnotset:         mov    org_kbd,bx        ; Save the value OFFSET
                   mov    org_kbd+2,es      ; Save the value SEGMENT

                   mov    ax,GETTIM         ; Get the Timer interrupt in ES:BX
                   int    21h               ; Function to get it
                   mov    org_tim,bx        ; Save the OFFSET
                   mov    org_tim+2,es      ; Save the SEGMENT

                   mov    ax,GETBRK         ; Get the Break interrupt
                   int    21h
                   mov    org_brk,bx        ; Save offset
                   mov    org_brk+2,es      ; Save segment

                   mov    ax,SETKBD         ; Set the new keyboard interrupt
                   mov    dx,OFFSET newkbd  ; OFFSET of the keyboard routine
                   int    21h               ; Dos function 21h does it

                   mov    ax,SETTIM         ; Set the new timer interrupt
                   mov    dx,OFFSET newtim  ; OFFSET of it
                   int    21h               ; Function to set it

                   mov    ax,SETBRK         ; Set new break interrupt handler
                   mov    dx,OFFSET newbrk  ; Address of it
                   int    21h

                   mov    ah,9              ; Dos. function No. to print
                   mov    dx,OFFSET msgsucc ; Get the success message
                   int    21h

                   mov    ax,3100h          ; Get ready to be resident
                   mov    dx,RESIDENT_LEN   ; No. of para to be resident
                   int    21h               ; Initiate residency

msgfail            db     'Keyboard interrupt vector re-routed',13,10
                   db     'Cannot load the resident part',13,10
                   db     'Terminating as a normal program.',13,10
                   db     10,'$'

heading            db     13,10
                   db     10
                   db     '<<<<<<-- KEYBOARD BUFFER EXTENDER -->>>>>>',9,9,9,'EN#1.0SRW02R',13,10
                   db     10
                   db     'A self taught product from :',13,10
                   db     'Titas Raha,',13,10
                   db     '17, Uttar Palli, Sodepur',13,10
                   db     'Dist. 24 Parganas (N)',13,10
                   db     'WEST BENGAL - 743178',13,10
                   db     '--------------------',13,10
                   db     10
                   db     'This is a resident program which handles the keys typed in when the',13,10
                   db     'keyboard buffer gets filled by storing those keys in another place,',13,10
                   db     'and transfering it back as soon as the keyboard buffer is in a',13,10
                   db     'position to accept it. It extends the buffer from normally 15 Keys',13,10
                   db     'to 15+127=142 Keys.  Reroutes the 1C,1B and 9 interrupt vectors.',13,10
                   db     10,'$'

msgsucc            db     'Resident portion loaded.',13,10
                   db     10,'$'

code               ends
                   end    start