TITLE swapkeys ; ; SWAPKEYS ; (c) Copyright 1991 Latham King ; ; ; ; CAPS_LOCK/CTRL key swapper. ; ; It swaps the functionality of the CAPS_LOCK key and the left CONTROL ; key. It also remaps what the keyboard thinks is CAPS-PAUSE into ; CTRL-BREAK and CTRL-BREAK into PAUSE. ; ; This TSR has been tested and verified with the following ROM BIOS's: ; ; Phoenix 80486 ROM BIOS PLUS Version 0.10 G10 ; Gateway 2000 486 ; Premium 386 BIOS v1.3 ; AST Premium 386 ; American Megatrends 386-BIOS (c) 1989 ; IBM PS/2 Model 50 ; IBM PS/2 Model L40SX (80386 SX) ; Toshiba BIOS (c) 1990 in the: ; T5100 ; T3200SX ; T3200SXC ; Compaq BIOS dated 1988 in the: ; Compaq 386/20 (80386) ; Compaq Portable III (80286) ; Phoenix ROM BIOS Version 3.03 ; AST Premium 286 ; PC's Limited 286 ROM BIOS ; (c) 1986 Dell Computer Corp. & ; (c) 1986 AMI ; Phoenix 80286 ROM BIOS PLUS Version 3.10 22 ; Nec Powermate 2 ; ; ; This is SHAREWARE software. So, if you find yourself using it, please ; pay for it by sending $20 US (or what ever you think this is worth) ; to me at: ; ; Latham King ; P.O. Box 957 ; Darien, CT 06820 ; USA ; ; Please leave my copyright notice within this source code. If you change ; or add anything feel free to add your copyright notice. ; ; Comments and any other random musings can be direct to the above ; address or to CompuServe account 70313,662. ; ; You will notice that there are two interrupt handlers in this file. ; If the ROM BIOS supports INT 15h function 4Fh (the keyboard ; preprocessor), then that interrupt handler is hooked into the vector. ; Otherwise, the first and larger routine is hooked into the INT 9h ; interrupt vector. ; ; I've chosen to do it this way to handle as many 101 key keyboards and ; ROM BIOS's combinations as possible. ; ; Thank you and I hope this software can be of use to you. ; - Latham King ; ; History: ; 07/20/91 v. 1.0 ; 08/24/91 v. 1.1 added logic to use INT 15H func 4Fh if it ; is supported by the BIOS ; ;scan codes CTRL_SCAN equ 01dh CAPS_SCAN equ 03ah CBRK_SCAN equ 046h PAUSE_SCAN equ 045h ; various flag bits KB3_E0_BIT equ 002h KB3_E1_BIT equ 001h KB1_CAPS_BIT equ 040h KB2_CAPS_BIT equ 040h KB1_CTRL_BIT equ 004h KB2_CTRL_BIT equ 001h KB2_PAUSE_BIT equ 008h ; notation KB_101_BIT equ 010h KB_PREPROCESS equ 010h ; keyboard i/o ports KB_DATA_PORT equ 060h ; interrupt controller i/o ports INTR_CTRL_PORT equ 020h EOI equ 020h ; end of interrupt command ; ; these are absolute address; ie: they assume that the ES (or DS) segment has ; a value of 0 ; KB_FLAGS_12 equ 0417h ; KB_FLAG && KB_FLAG_1 as a word KB_FLAGS_34 equ 0496h ; KB_MODE_STATE & KB_LED_STATUS as a word BIOS_BREAK equ 0471h ; bit 7 of byte is set for break KBBUF_START equ 0480h ; keyboard buffer start address KBBUF_HEAD equ 041ah ; head of the keyboard buffer queue KBBUF_TAIL equ 041ch ; tail of the keyboard buffer queue _text segment assume cs:_text, ds:_text, ss:_text org 100h swapkeys: jmp begin oldint dd 0 ; could be int 9h or int 15h signature dw 444Bh ; must be right before the proc ; ; Int 9h handler ; ; register usage: ; ; ax - scan code from the keyboard ; bx - scratch ; cx - keyboard flags; both KB_FLAG_1 & KB_FLAG_2 ; dx - keyboard flags; both KB_FLAG_3 & KB_FLAG_4 ; es - 0x0000; so that everything uses absolute adresses ; swap_int proc far int9_handler: sti pushf push ax push bx push cx push dx push es ; get everything that we need in al,KB_DATA_PORT xor bx,bx mov es,bx mov bx,KB_FLAGS_12 mov cx,es:[bx] ; get kb_flags1 & 2 as a word mov bx,KB_FLAGS_34 mov dx,es:[bx] ; get kb_flags3 & 4 as a word mov ah,al and al,7fh cmp al,CTRL_SCAN je map2caps cmp al,CAPS_SCAN je map2ctrl cmp al,CBRK_SCAN je map2pause cmp al,PAUSE_SCAN je map2cbrk old_int: pop es pop dx pop cx pop bx pop ax popf jmp dword ptr cs:[oldint] map2ctrl: ; remap caps ==> ctrl test ah,80h jz ctrl_on and cl, NOT KB1_CTRL_BIT and ch, NOT KB2_CTRL_BIT jmp update_flags ctrl_on: test ch,KB2_CTRL_BIT jnz swallow_it1 or cl,KB1_CTRL_BIT or ch,KB2_CTRL_BIT jmp update_flags map2caps: ; remap ctrl ==> caps test dl,KB3_E0_BIT OR KB3_E1_BIT ; leave Ctrl codes in pause jnz old_int ; sequence and right ctrl alone test ah,80h jz caps_on and ch, NOT KB2_CAPS_BIT jmp update_flags caps_on: test ch,KB2_CAPS_BIT jnz swallow_it1 xor cl,KB1_CAPS_BIT ; toggle caps_lock bit or ch,KB2_CAPS_BIT jmp update_flags old_int1: jmp old_int map2pause: jmp map2pause1 map2cbrk: test dl,KB3_E1_BIT jz old_int test ch,KB2_CTRL_BIT jz old_int test ah,080h jnz zap_ebits ; it's a break code and dl,0FCh mov bx,KB_FLAGS_34 ; es is currently 0 mov es:[bx],dl and ch,NOT KB2_PAUSE_BIT mov bx,es:[KBBUF_START] mov es:[KBBUF_HEAD],bx mov es:[KBBUF_TAIL],bx mov word ptr es:[bx],0 mov byte ptr es:[BIOS_BREAK],80h push bx push cx push es int 1bh ; break interrupt vector pop es pop cx pop bx mov ax,09102h push cx push es int 15h pop es pop cx jmp update_flags1 swallow_it1: jmp swallow_it map2pause1: ; remap CTRL-BREAK ==> PAUSE test dl,KB3_E0_BIT jz old_int1 test ch,KB2_CAPS_BIT jz old_int1 test ah,080h jnz zap_ebits ; it's a break code test ch,KB2_PAUSE_BIT jnz swallow_it mov bx,KB_FLAGS_12 inc bx or byte ptr es:[bx],KB2_PAUSE_BIT ; set pause flag mov al,EOI ; signal end of interrrupt out INTR_CTRL_PORT,al wait_loop: test byte ptr es:[bx],KB2_PAUSE_BIT jnz wait_loop jmp get_out zap_ebits: and dl,0FCh update_flags: mov bx,KB_FLAGS_34 ; es is currently 0 mov es:[bx],dx update_flags1: mov bx,KB_FLAGS_12 ; es is currently 0 mov es:[bx],cx swallow_it: mov al,EOI out INTR_CTRL_PORT,al get_out: pop es pop dx pop cx pop bx pop ax popf iret swap_int endp end_resident: ; ; Int 15h handler ; ; register usage: ; ; ax - scan code from the keyboard interrupt service routine ; bx - scratch & KB_FLAG_3 flags ; es - 0x0000; so that everything uses absolute adresses ; ; Note: ; ; Because this code may be copied on top the first portions of swap_int, ; this is considered self-modifying code. ; int15_handler: sti push es push bx xor bx,bx mov es,bx mov bx,KB_FLAGS_34 mov bx,es:[bx] ; get kb_flags as a word cmp ah,04fh jne original mov ah,al and ah,80h and al,7fh cmp al,CAPS_SCAN jne test_ctrl mov al,CTRL_SCAN jmp short make_scan test_ctrl: cmp al,CTRL_SCAN jne short make_scan test bl,KB3_E0_BIT ; if right ctrl key, leave it alone jnz short make_scan mov al,CAPS_SCAN make_scan: or al,ah ; put back the make/break bit mov ah,04fh original: stc pop bx pop es jmp dword ptr cs:[oldint] end_int15_handler: copy_right1 db 'SWAPKEYS Version 1.0 (MS-DOS)',0dh,0ah,'$' copy_right2 db '(c) Copyright 1991 Latham King',0dh,0ah,0dh,0ah,'$' its_not_os2 db 'You must be running OS/2 to use this program.',0dh,0ah,'$' its_here db 'SWAPKEYS is already resident!',0dh,0ah,'$' its_loaded1 db 'SWAPKEYS has been installed. Hooking Int 9H.',0dh,0ah,'$' its_loaded2 db 'SWAPKEYS has been installed. Hooking Int 15H.',0dh,0ah,'$' nota101 db 'You must be using a 101/102 key keyboard',0dh,0ah,'$' psp dw 0 ; ; Installation code ; begin: mov psp,es mov bx,cs mov ds,bx ; uncomment the next four lines to check for version numbers under OS/2 ; mov ax,3000h ; int 21h ; cmp al,0ah ;is it 0S/2? ; jne error_exit lea dx,copy_right1 mov ax,0900h int 21h lea dx,copy_right2 mov ax,0900h int 21h ; ; If your BIOS does not use/maintain the flag byte at 40:96 and ; thus does not know if a 101/102 key keyboard is attached to the ; computer, comment out the next four lines of code ; mov bx,0 mov es,bx ; load es with 0 so that the mov bx,KB_FLAGS_34 ; absolute address is correct test byte ptr es:[bx],KB_101_bit jz not101msg mov ah,0C0h int 15h jc short set9 test byte ptr es:[bx+5],KB_PREPROCESS jz short set9 mov al,15h jmp short isithere set9: mov al,09h isithere: push ax mov ah,35h ; get and save old interrupt vector int 21h pop ax mov word ptr [oldint],bx mov word ptr [oldint+2],es cmp word ptr es:[bx-2],444Bh je short already_res cmp al,15h je short small_handler mov di,offset end_resident mov cl,9 lea si,its_loaded1 jmp short inst_handler small_handler: ; copy the code for the small handler on top of the big one mov bx,cs mov es,bx mov di,offset int9_handler mov si,offset int15_handler ; comput the size of the small handler's code mov cx,offset end_int15_handler - offset int15_handler ; move the code rep movsb ; di = size; used @ go_tsr mov cl,al lea si,its_loaded2 inst_handler: mov dx,si mov ax,0900h int 21h lea dx,swap_int mov al,cl ; get interrupt number mov ah,25h int 21h nuke_env: mov es,psp ; blow away the environment segment mov ax,es:2ch mov es,ax mov ax,4900h int 21h go_tsr: mov dx,di add dx,10h mov cl,4 shr dx,cl mov ax,3100h int 21h not101msg: lea dx,nota101 jmp short errmsgout already_res: lea dx,its_here jmp short errmsgout error_exit: lea dx,its_not_os2 errmsgout: mov ax,0900h int 21h mov ax,4c01h int 21h _text ends end swapkeys