Metropoli BBS
VIEWER: quickeys.asm MODE: TEXT (ASCII)
TITLE QUICKEYS 

COMMENT | This memory resident routine augments the typematic key repetition
          feature of the IBM PC, XT, and AT.  Upon each keystroke interrupt,
          QUICKEYS resets itself, then calls the standard keystroke routines for
          processing.  Upon return to QUICKEYS, if a new keystroke has been
          stored in the ROM BIOS keyboard buffer, it is memorized and a count-
          down is begun.  If not reset by release of the key, the key will be
          repeated in the buffer after a delay of DUP_DELAY1 timer ticks and
          then again every DUP_DELAY2 ticks thereafter, until the key is
          released.

          Timer delay settings of 9 and 2 for DUP_DELAY1 and DUP_DELAY2 are
          effectively equivalent to the standard IBM PC typematic settings for
          the delay before the first repeat of a held-down key and for the delay
          between subsequent repetions of it while the key remains down.

          Press the (Alt) and left (Shift) keys together, to empty the buffer
          if too many keystrokes have been entered.  Handy way to get around
          spread sheet is to fill buffer with cursor keys and then zap excess
          with (ALT)(Shift) when arrive where wanted to go.

          Copyright 1986 - Ziff-Davis Publishing Company
                                                                              |
BIOS_SEG       SEGMENT AT 40H		     ;ESTABLISH BIOS SEG
BIOS_SEG       ENDS                          ;ADDRESS.

CSEG   SEGMENT
       ASSUME  CS:CSEG                 ;WHEN RESIDENT, NOT SURE OF DS OR ES.
       ORG     100H                    ;STD CODE OFFSET FOR COM FILES.
BEGIN:         JMP     INITIALIZE      ;GO SET VECTORS, WHEN LOADED BY DOS.
ROM_INT8       DW      ?,?             ;ORIGINAL TIMER VECTOR ADDRESS
ROM_INT9       DW      ?,?             ;ORIGINAL KEYSTROKE VECTOR ADDR
DUP_CHAR       DW      ?               ;CHAR TO BE REPEATED NEXT.
DUP_SWTCH      DB      0               ;1, IF CHAR IS TO BE REPEATED
DUP_CNT        DB      0               ;TIMER TICKS LEFT B/4 REPEAT KEY
DUP_DELAY1     DB      7               ;# TICKS B/4 1ST REPEAT OF A KEY.
DUP_DELAY2     DB      1               ;# TICKS DELAY BETWEEN REPEATS.
ALT_SHFT       EQU     0AH             ;BITS SET WHEN IN ALT-LEFT_SHIFT STATE.
BIOS_KBFLAGS   EQU     17H             ;BIOS OFFSET TO KEYBOARD SHIFT FLAGS.
BIOS_HEAD      EQU     1AH             ;BIOS BUFFER ADDRESS OF NEXT OUTPUT KEY.
BIOS_TAIL      EQU     1CH             ;BIOS BUFFER ADDRESS FOR NEXT INPUT KEY.
BIOS_END       EQU     3EH             ;ENDING LOCATION OF BIOS KEYBOARD BUFFER.
BIOS_START     EQU     1EH             ;START  LOCATION OF BIOS KEYBOARD BUFFER.
;*****************************************************************************
INT9:                           ;KEYSTROKE INTERRUPT PROCESSING
;*****************************************************************************
       PUSH    DS                      ;SAVE CALLER'S DATA SEGMENT ADDRESS
       PUSH    BX                      ;SAVE, SO CAN USE TO STORE CURRENT TAIL.
       MOV     BX,BIOS_SEG             ;SET UP ADDRESSING TO BIOS DATA SEGMENT.
       MOV     DS,BX
       ASSUME  DS:BIOS_SEG
       MOV     DUP_SWTCH,0             ;TURN OFF AUTOMATIC KEY REPETITIONS.
       MOV     BX,DS:[BIOS_TAIL]       ;SAVE CURRENT KEYBOARD TAIL LOCATION.
       PUSHF                           ;CALL ROM BIOS KEYSTROKE PROCESSING
       CALL    DWORD PTR ROM_INT9      ; INTERRUPT, AND RETURN HERE WHEN DONE.
       CMP     BX,DS:[BIOS_TAIL]       ;IF NEW CHARACTER ENTERED, TAIL MOVED.
       JNE     WAS_NEW_KEY             ;IF NOT MOVED, SET DUP_CHAR SO WILL NOT
       MOV     DUP_CHAR,-1             ;MATCH ANY KEY PUSHED NEXT TIME.
       TEST    BYTE PTR DS:[BIOS_KBFLAGS],ALT_SHFT ;SEE IF WAS (ALT)(SHIFT).
       JZ      KEY_RETURN              ;IF SO, EMPTY THE BIOS BUFFER BY MOVING
       MOV     DS:[BIOS_HEAD],BX       ;THE HEAD POINTER TO THE TAIL.
       JMP     KEY_RETURN              ;     ALL DONE FOR NOW.
WAS_NEW_KEY:                           ;IF TAIL HAS MOVED, HAVE NEW KEY, SO
       MOV     BX,[BX]                 ;GET CHARACTER NOW STORED IN OLD TAIL.
       CMP     BX,DUP_CHAR             ;COMPARE WITH PRIOR REPEAT CHARACTER AND
       MOV     DUP_CHAR,BX             ;STORE AS NEW REPEAT KEY.  ASSUME OLD AND
       MOV     BL,DUP_DELAY1           ;NEW CHARS DIFFERENT, SO WILL START SLOW.
       JNE     SWITCH_ON               ;OTHERWISE, THIS INTERRUPT WAS FROM STD
       MOV     BL,DUP_DELAY2           ;TYPEMATIC ACTION, SO STAY AT HI RATE.
SWITCH_ON:
       MOV     DUP_CNT,BL              ;SET REPETITION RATE FOR THE KEY AND
       MOV     DUP_SWTCH,1             ;TURN TICK COUNTER BACK ON.
KEY_RETURN:
       POP     BX                      ;ALL DONE.  KEY HAS BEEN PROCESSED BY
       POP     DS                      ;ROM BIOS; REPEAT FLAG NOW RESET AND
       IRET                            ;TICK COUNT READY.  RETURN TO USER.
;*****************************************************************************
INT8:                          ;TIMER INTERRUPT PROCESSING
;*****************************************************************************

       CMP     DUP_SWTCH,1             ;MOVE ON TO STANDARD TIMER PROCESSING, IF
       JNE     MOVE_ON                 ;AUTOMATIC REPEAT FLAG IS NOT ON OR THE
       DEC     DUP_CNT                 ;TIMER TICK COUNT IS NOT ZERO YET.
       JNZ     MOVE_ON                 ;
       PUSH    DS                      ;TIME TO REPEAT THE DUP_CHAR KEY, SO
       PUSH    DI                      ; (1) SAVE ES,DI,BX,AND AX, SO CAN USE
       PUSH    BX                      ;          TO DO THE STORING.
       PUSH    AX                      ;
       MOV     BX,BIOS_SEG             ; (2) ESTABLISH BIOS DATA SEGMENT
       MOV     DS,BX                   ;     ADDRESSABILITY.
       MOV     BX,DS:[BIOS_TAIL]       ; (3) GET CURRENT TAIL LOCATION INTO BX.
       MOV     DI,BX                   ; (4) SAVE POINTER, SO CAN FILL, IF ROOM.
       ADD     BX,2                    ; (5) MOVE TAIL POINTER TO NEXT WORD IN
       CMP     BX,BIOS_END             ;     ROM BUFFER.
       JNE     FULL_CHECK              ; (6) IF NEXT LOCATION IS PAST END OF
       MOV     BX,BIOS_START           ;     BUFFER, WRAP AROUND TO START LOC.
FULL_CHECK:                            ;
       CMP     BX,DS:[BIOS_HEAD]       ; (7) IF NEW TAIL IS SAME AS HEAD, THERE
       JNE     HAVE_ROOM               ;     ISN'T ROOM FOR ANOTHER CHAR,
       MOV     DUP_SWTCH,0             ;     SO STOP REPEATING IT.
       JMP     REG_RESTORE
HAVE_ROOM:
       CLI
       MOV     AX,DUP_CHAR             ; (8) IF ROOM LEFT, MOVE THE REPEAT KEY
       MOV     [DI],AX                 ;     INTO THE BIOS KEYBOARD BUFFER AND
       MOV     DS:[BIOS_TAIL],BX       ; (9) UPDATE THE KEYBOARD TAIL POINTER.
       STI
       MOV     AL,DUP_DELAY2           ; (10) FINALLY, SET TICK COUNTER FOR THE
       MOV     DUP_CNT,AL              ;      THE HIGHER BETWEEN-KEY REPEAT RATE.
REG_RESTORE:
       POP     AX                      ; (11) RESTORE REGISTERS WE USED.
       POP     BX
       POP     DI
       POP     DS
MOVE_ON:
       JMP     DWORD PTR ROM_INT8      ;GO DO STANDARD TIMER INTERRUPT WORK.
;*****************************************************************************
INITIALIZE:                    ;INITIALIZE INTERRUPT VECTORS 8 AND 9.
;*****************************************************************************
       ASSUME  DS:CSEG
       XOR     AX,AX
       MOV     ES,AX                  ;SET ES TO ABSOLUTE 0 (START OF VECTORS).
       MOV     AX,ES:[8*4]            ; AX=OFFSET ADDRESS TO CURRENT TIMER CODE.
       MOV     BX,ES:[8*4+2]          ; BX=SEGMENT ADDRESS OF CURRENT TIMER CODE
       MOV     CX,ES:[9*4]            ; CX=OFFSET ADDRESS TO ROM KEYSTROKE CODE.
       MOV     DX,ES:[9*4+2]          ; DX=SEGMENT ADDRESS OF ROM KEYSTROKE CODE
       MOV     ROM_INT8,AX
       MOV     ROM_INT8[2],BX         ;SAVE CURRENT ADDRESSES FOR LATER USE.
       MOV     ROM_INT9,CX
       MOV     ROM_INT9[2],DX
       CLI
       LEA     AX,INT8                ;AX=OFFSET TO OUR TIMER CODE.
       MOV     ES:[8*4],AX            ;RESET TIMER VECTOR TO POINT TO OUR
       MOV     ES:[8*4+2],CS          ;OFFSET AND CODE SEGMENT.
       LEA     AX,INT9                ;AX=OFFSET TO OUR KEYSTROKE CODE.
       MOV     ES:[9*4],AX            ;RESET KEYSTROKE VECTOR TO POINT TO
       MOV     ES:[9*4+2],CS          ;OUR OFFSET AND CODE SEGMENT.
       STI
       LEA     DX,INITIALIZE          ;POINT TO END OF CODE WE NEED TO KEEP
       INT     27H                    ;AND TERMINATE SETUP OPERATION.
CSEG   ENDS
       END     BEGIN

[ RETURN TO DIRECTORY ]