[comp.binaries.ibm.pc.d] KBDL

AHS%psuvm.bitnet@rutgers.edu (06/29/88)

[Reposted from comp.binaries.ibm.pc.  DF]

June 26, 1988 -- Fourth and final dissassembly -- How KBDL swaps the keys.

Tom Almy, toma@tekgvs.TEK.COM, writes of his set of KBD*.com programs:

         All of these [AT only] programs were written by me, using the CFORTH
         Forth Compiler, and are placed in public domain for the benefit of
         other PC users who are frustrated with these keyboard designs.

         I am not supplying sources, the programs are small enough that DEBUG
         can be used to reverse-engineer them.

         Tom Almy
         toma@tekgvs.TEK.COM
-----------------------------------


Earlier, I attempted several dissassemblies of:  KBDL.COM 332 6-15-88 7:03a.

My motivation was to see if Tom knew a better way than using interrupt 9 -- as
is done in a similar program, IBMFIX, published by PC Magazine -- to move keys
around the keyboard.   The short answer is:  Tom knows.

Here is my final dissassembly, hopefully.  (Note:  A few hours after I
completed this commented dissassembly, I downloaded a dissassembly sent by Tom.
Thank you Tom.  Tom's dissassebly was not commented, except for the
manipulation of return addresses following a call.  The two dissassemblies,
made independently, do agree).

The problem that threw me off in my previous dissassemblies was:  What is the
code that is being executed after returning from the call made a label L017F
(at offset 017F) ???

This was solved by explanations supplied first by Kchula-Rrit, thank you
Kchula-Rrit, and later by Tom Almy himself in his first and second replies,
thank you Tom.

I had one other question.  What is so special about this code that it will run
successfully only on an AT ??.  This was solved; it is explained in the
dissassembly of the new interrupt handler.

In sum:  Making the dissassembly required some tidbits of information beyond
the information provided by Debug's U command.  (The Trace command is of
limited and difficult use when dissassembling a TSR).   I expect that when I
rewrite this code in assembly, I will need:  the 38h=56 bytes of the handler,
plus roughly 35 bytes to set the vectors, and roughly 35 bytes to install the
code in memory, or roughly 56+40+24=126 bytes.   That is, to install the TSR,
I will need:  very roughly 35+35=70 bytes of straighforward code versus
332-56=276 bytes of not exactly straighforward code, but probably good compiled
Forth code -- I understand that AI has made promises but has not yet fully
delivered in compilerdom -- by the way, how large and straightforward would a
QBasic EXE be?.  When I am done, I think I will post both the COM and assembly.


Finally, and most importantly, my sincere thanks to Tom Almy for showing us how
to use interrupt 15.

---------------------------------------------------------------------



TITLE   KBDL

RET_NEAR	MACRO
DB	0C3H
ENDM

.RADIX	16

S0000	SEGMENT
	ASSUME DS:S0000, SS:S0000 ,CS:S0000 ,ES:S0000

        ORG     0100H

L0100:  JMP     L010B

        DB      4C,2

L0105   DW      0000

L0107   DW      0000,000A

L010B:  MOV     SP, FF9A
        MOV     WORD PTR L0105, FF98
        MOV     BP, FFFE
        MOV     L0107, BP
        CLD
        CALL    L017F
        MOV     AX, 4C00      ;exit program, with errorlevel 00
        INT     21


;The following DW is for data write and data immediate

L0124   DW      0000            ;For Offst of old vect 15
                                ;Will be moved into PSP at offst 80

;The following DW is for data write from offset 01DA.
;Since it is not data read, nor data immediate, is it executed ??
;No, but it is moved to PSP to be used there, as part of the mov 3C

L0126   DW      0000            ;For Segmt of old vect 15
                                ;Will be moved into PSP at offst 82
                                ;(At offst 84 begins new hdlr 15)


;New int 15 hdlr                                                  ;Offset: 0128
;Moved in PSP at offst 84 by t mov 3C  (160-128=38h is enough)
;
;Int 15 is not documented in Duncan's Advanced MS DOS
;However, thanks to Ralph Brown, we learn that:
;
; Ralf Brown
; Arpa: ralf@cs.cmu.edu
; UUCP: {uunet,ucbvax,harvard}!cs.cmu.edu!ralf
; BIT:  ralf%cs.cmu.edu@cmuccvma
; FIDO: Ralf Brown at 1:129/31  (more subject to change than the others
;
; Last edited 1/30/88
;
; -----------------------------------------------------------
; INT 15 - HOOK - KEYBOARD INTERCEPT (AT model 3x9,XT2,XT286,CONV,PS)
;         AH = 4Fh
;         AL = scan code
;         CF set
; Return: AL = scan code
;         CF set
; Note: Called by INT 9 handler to translate scan codes
; -----------------------------------------------------------
;
;Thus, this TSR should work with the following machines:
; AT-3x9, XT2, XT286, Convertible, PS2
;


;New int 15 hdlr                                                  ;Offset: 0128
;Moved in PSP at offst 84 by t mov 3C  (160-128=38h is enough)

                    CMP     AH, 4F      ;Is it a kbd intercept ?
                    JNZ     L015A       ;No

                    CMP     AL, 3A      ;Is it t scancode of CapsLock ?
                    JNZ     L014B       ;No

;It is a CapsLock
                    PUSH    ES
                    CS:
                    MOV     ES, [007E]           ;Map ES onto DOS segmt
                                                 ;([7E] was earlier set to 00)
                                                 ;Not exactly a straightforward
                                                 ;manner to set ES to 00
                    ES:
                    TEST    BYTE PTR [0417], 02  ;Is LeftShiftKey down ?
                    JNZ     L0141                ;No

;LeftShiftKey is down, and CapsLock was pressed
                    MOV     AL,1D                ;Make keypress a Ctrl keypress

;It is CapsLock but LeftShift was not pressed
L0141:              MOV     [007D],AL           ;Store resultg Ctrl or CapsLock
                                                ;at 7D in PSP
                    POP     ES
                    STC
                    CS:
                    JMP     FAR [0080]           ;Execute old hdlr 15

;It was not CapsLock
L014B:              CMP     AL,BA       ;Is it t scancode of the ??1??-key
                                        ;Is BA t scancode of "CapsLockRelease"?
                                        ;BA=(80+3A)=(80+CapsLockScanCode)
                                        ;My book stops at scancode 53h.....
                    JNZ     L015A       ;No

;It is key ??1??
;It is a ?CapsLockRelease
                    MOV     AL,[007D]   ;Make it the Ctrl or CapsLock
                                        ;that was earlier stored at 7D in PSP
                    ADD     AL,80       ;Make it now into a ??2?? keypress
                                        ;But BA=(80+3A)=(80+CapsLockScanCode)
                    STC                 ;?Make it now into a ?CtrlRelease or
                                        ;a ?CapsLockRelease
                    CS:
                    JMP     FAR [0080]           ;Execute old hdlr 15

;It was not a kbd intercept
;It was not key ??1??
;It was not a ?CapsLockRelease
L015A:              STC
                    CS:
                    JMP     FAR [0080]          ;Execute old hdlr 15

;End of new int 15 hdlr



;The following code is called from offset 01F8

L0160:  POP     SI
        POP     AX
        POP     DX
        POP     BX
        PUSH    DS
        MOV     DS,BX
        MOV     AH,25   ;Get vector
        INT     21
        POP     DS
        JMP     SI


L016E:  MOV     AH,35   ;Set vector
        INT     21
        MOV     AX,BX
        MOV     BX,ES
        RET_NEAR                                ;Byte:  C3


L0177:  MOV     DX,0C
        MOV     AX,OFFSET 3100                  ;Exit but remain memory residt
        INT     21






L017F:  CALL    L01FE                           ;Bytes:  E8 7C 00
                                                ;Called from:  L011C

;Now, THE question is:  -------------------------------------------------------
;     What is the code that follows this call and
;     that we shall execute when we return from this call ???
;     Answer:  execution does not return here, but at the end of the text,
;              at CALL L0210, because the call's return addr was manipulated
;              by the routine at L01FE

;Here is the source

     ;0180  7C 00 33 43 41 50 53 4C-4F 43 4B 20 2D 3E 20 43  | 3CAPSLOCK -> C
     ;0190  54 52 4C 2C 20 20 4C 45-46 54 53 48 49 46 54 20  TRL,  LEFTSHIFT
     ;01A0  2B 20 43 41 50 53 4C 4F-43 4B 20 2D 3E 20 43 41  + CAPSLOCK -> CA
     ;01B0  50 53 4C 4F 43 4B E8 57-00 E8 42 00 0B 42 79 20  PSLOCKhW hB By



;Now, how do we ever reach those two calls?
;Answer:   By manipulating the stack in the routine at L01FE
;However, this is only one of several return address generated by routine L01FE

        CALL    L0210                           ;Bytes:  E8 57 00
        CALL    L01FE                           ;Bytes:  E8 42 00

;Now, comes more text.
;Here is the source:

      ;01B0  50 53 4C 4F 43 4B E8 57-00 E8 42 00 0B 42 79 20  PSLOCKhW hB By
      ;01C0  54 6F 6D 20 41 6C 6D 79-E8 45 00 C7 06 7E 00 00  Tom AlmyhE G^

;Here is the text:  "By Tom Almy"

        DB      42,79
        DB      20,54,6F,6DH,20
        DB      41,6C,6DH,79

;which ends here.


;Now, how do we ever reach the following line ??
;There are no explicit jumps or calls to offset 01C8.
;Answer:  By now we know, the CALL L01FE manipulated the call's return address.
;However, this is only one of several return address generated by routine L01FE

        CALL    L0210                           ;Offset:  01C8

;;L0210:  MOV     AX,0DH      ;A copy of the routine is placed here for clarity
;;        CALL    L021F

;;;;L021C   DB      1,0       ;Another copy for clarity
;;;;
;;;;L021E   DB      0
;;;;
;;;;L021F:  MOV     L021E,AL    ;AL=0D
;;;;        MOV     AH,40                  ;Write byte at L021E to handle in BX
;;;;        MOV     CX,1
;;;;        MOV     DX,OFFSET L021E
;;;;        MOV     BX,L021C
;;;;        INT     21
;;;;        RET_NEAR


;;        MOV     AX,0A
;;        JMP     L021F
;;
;;L021C   DB      1,0
;;
;;L021E   DB      0
;;
;;L021F:  MOV     L021E,AL    ;AL=0A
;;        MOV     AH,40                   ;Write byte at L021E to handle in BX
;;        MOV     CX,1
;;        MOV     DX,OFFSET L021E
;;        MOV     BX,L021C
;;        INT     21
;;        RET_NEAR



        MOV     WORD PTR DS:7E, 0    ;In PSP, set [7E]=00 = DOS segmt

        MOV     AX,15                ;15 in AL when exctg funct 35
        CALL    L016E                ;---- To execute  Funct 35 ---------------
                                     ;----- AL  is overwritn aftr funct ---

;;                                                      ;1<<<<<<<<<<<<<<<<<<<<<
;;L016E:  MOV     AH,35   ;Get vect,  AL=15     ;A copy placed here for clarity
;;        INT     21
;;        MOV     AX,BX   ;AX= Offst of old vect 15
;;        MOV     BX,ES   ;BX= Segmt of old vect 15
;;        RET_NEAR                                ;Byte:  C3

        MOV     L0124,AX        ;Offst of old vect 15
        MOV     L0126,BX        ;Segmt of old vect 15            ;Offset:  01DA

;;                              ;A copy of the routine placed here for clarity
;;;The following DW is for data write and data immediate
;;
;;L0124   DW      0000          ;Offst of old vect 15
;;
;;;The following DW is for data write from offset 01DA.
;;
;;L0126   DW      0000          ;Segmt of old vect 15


  3     MOV     DX,OFFSET L0124     ;Offst of old vect 15
  3     PUSH    DX
 2      MOV     DX,80
 2      PUSH    DX
1       MOV     DX,3C   ;Nuber of bytes to be moved
1       PUSH    DX
        CALL    L023E                                   ;Offset:  01EA

;;L023E:                        ;A copy of the routine placed here for clarity
;;CRA     POP     BX    ;Remove t call's return address (CRA)
;; 1      POP     CX    ;3C
;;  2     POP     DI    ;80
;;   3    POP     SI    ;Offst of old vect 15, ie: L0124
;;        JCXZ    L024A                                         ;Offset:  0242
;;        MOV     AX,DS
;;        MOV     ES,AX   ;Make ES = DS
;;        REPZ    MOVSB   ;Mov 3C bytes of code to [80] into PSP's cmdline area
;;                        ;
;;                        ;Code being stuffed into PSP:
;;                        ;
;;                        ;At 7D  The Resulting CapsLock or Ctrl keypress
;;                        ;At 7E  Value=00 = DOS segmt
;;                        ;At 80  Offst of old vect 15
;;                        ;At 84  New int 15 hdlr
;;                        ;At (80+3C) End of hdlr
;;
;;;Next line is jumped at from offset 0242
;;
;;L024A:  JMP     BX      ;BX=CRA.  Was set at L023E
;;                        ;This jmp is t actual return from the call


  3     MOV     AX,CS
  3     PUSH    AX
 2      MOV     DX,84
 2      PUSH    DX
1       MOV     DX,15   ;The int to be re-vectored
1       PUSH    DX
        CALL    L0160                           ;Offset:  01F8

;;L0160:                      ;A copy of the routine is placed here for clarity
;;CRA     POP     SI    ;Remove t call's return address (CRA)
;; 1      POP     AX    ;15  the int to be re-vectored
;;  2     POP     DX    ;84  Offst of new hdlr 15 in PSP cmdline area
;;                      ;    (at 80 we store offst o old int 15 vect)
;;                      ;    When was new hdlr 15 placed in PSP ???
;;                      ;    It was moved there by t mov 3C
;;   3    POP     BX    ;CS
;;1       PUSH    DS    ;Save t true DS addr
;;        MOV     DS,BX ;DS=CS=PSP
;;        MOV     AH,25 ;Set vector                    ;2<<<<<<<<<<<<<<<<<<<<<<
;;        INT     21
;;1       POP     DS
;;        JMP     SI    ;SI=CRA.   Was set at L0160


        JMP     L0177


L01FE:  POP     BX
        MOV     AL,[BX]
        XOR     AH,AH
        INC     BX
        PUSH    BX
        PUSH    AX
        ADD     BX,AX
        POP     AX
        POP     DX
        PUSH    BX
        MOV     BX,DX
        JMP     L0231                   ;Offset:  020D


L0210:  MOV     AX,0DH
        CALL    L021F
        MOV     AX,0A
        JMP     L021F


L021C   DB      1,0

L021E   DB      0

L021F:  MOV     L021E,AL
        MOV     AH,40                   ;Write byte at L021E to handle in BX
        MOV     CX,1
        MOV     DX,OFFSET L021E
        MOV     BX,L021C
        INT     21
        RET_NEAR


;Jump to next line is from offset 020D

L0231:  MOV     CX,AX
        MOV     DX,BX
        MOV     BX,L021C
        MOV     AH,40
        INT     21
        RET_NEAR

;Next line is called from offset 01EA

L023E:  POP     BX
        POP     CX
        POP     DI
        POP     SI
        JCXZ    L024A                           ;Offset:  0242
        MOV     AX,DS
        MOV     ES,AX
        REPZ    MOVSB

;Next line is jumped at from offset 0242

L024A:  JMP     BX                              ;Bytes:  FF E3

	S0000	ENDS
;
END	L0100