Name MSLK250 ; MSLK250.ASM - A simple driver program for the DEC LK250 keyboard on IBM AT's ; Author: Terry Kennedy, St. Peter's College, Terry@spcvxa.Bitnet. ; ; Edit history ; Last edit Sat Apr 6 18:02:34 1991 ; 6 Apr 1991 Change internal references to this file to have right name [fdc] ; 17 Oct 1989 Add test for pre-80286 cpu and jmp $+2 breathers. [jrd] ; 16 Oct 1989 Sync USU, SPC versions; add initialization tests. [tmk] ; 9 Oct 1989 Add building instructions etc. [jrd] ; ; Purpose: Allow access to LK250 features without requiring the user to have ; any DEC drivers loaded. ; ; Method: Intercept INT 15 subfunction 4F (SysReq) and 50 (Control). If en- ; tered via 4F, place substitute scan code in buffer if the keypress ; was for a key we're remapping *and* we are active. If entered via ; 50, perform control function as follows: ; ; AL=00 - Disable substitution (keyboard "Special" [IBM] mode) ; AL=01 - Enable substitution (keyboard DEC mode) ; AL=02 - Send the byte in BL to the keyboard (caution: sending ; "random" bytes with this function can lock the key- ; board, necessitating a reboot. ; ; Construct final file MSLK250.COM by ; masm msulk2; reads file msulk2.asm, writes file msulk2.obj ; link msulk2; expect and ignore statement about no stack segment ; exe2bin msulk2 produces file msulk2.bin from msulk2.exe ; ren msulk2.bin msulk2.com rename the result to our runnable filename ; del msulk2.exe delete non-runnable intermediate files .exe & .obj ; del msulk2.obj ; ; msulk2 run msulk2.com to install the driver ; ; ; Date: 20-Aug-1989, X0.0-00 - Original version ; 26-Aug-1989, X0.0-01 - Pass Shift-PrtSc through even in DEC mode ; 16-Oct-1989, X0.0-02 - Add initialization tests suggested by jrd ; 17-Oct-1989, X0.0-03 - Add cpu test, add time between tests. jrd ; 28-Nov-1990 X0.0-04 - Fix problem on PS/2's where special keys ; were followed by a spurious divide symbol. ; 29-Nov-1990 X0.0-05 - More PS/2 fixes. ; main group code ; data segment at 40h ; db 23 dup (?) shift db ? db 2 dup (?) head dw ? tail dw ? buff dw 16 dup (?) db 66 dup (?) bstart dw ? bend dw ? db 19 dup (?) kbflg2 db ? ; data ends ; code segment public para 'code' assume cs:main,ds:nothing ; org 100h ; making a.COM file ; begin: jmp start ; program starts here ; saved15 dd ? ; previous int 15 vector active db 0 ; are we translating? ; bail: jmp [saved15] ; ; This is the new interrupt 15 routine. ; newi15 proc far assume ds:data cmp ah,50h ; control function? jne noctl jmp ctlfnc ; if so... noctl: cmp ah,4fh ; keyboard intercept? jne bail ; nope, time to leave... cmp active,1 ; are we active? jne bail ; no, just exit... cmp al,01h ; Esc/PF1? je kpf1 ; if so cmp al,45h ; Num Lk/PF2? je kpf2 ; if so cmp al,46h ; Scrl Lk/PF3? je kpf3 ; if so cmp al,37h ; PrtSc / PF4? je kpf4 ; if so cmp al,54h ; is it a scan we handle? jl bail ; nope, bail out... cmp al,69h jg bail ; likewise... jmp putkey ; otherwise go save the key... ; kpf1: mov al,6ah ; make PF1 scan 6ah jmp putkey ; kpf2: mov al,6bh ; make PF2 scan 6bh jmp putkey ; kpf3: mov al,6ch ; make PF3 scan 6ch jmp putkey ; kpf4: push ds ; save user's [DS] mov ax,40h push ax pop ds mov al,shift ; get shift states pop ds ; restore user's [DS] and al,3 ; either shift state set? jz kpf4a ; nope, this key gets translated mov al,37h ; yes, we didn't want to translate it jmp bail ; oh, this is a bad pun... ; kpf4a: mov al,6dh ; make PF4 scan 6dh jmp putkey ; ; stuff a scancode into the keyboard buffer ; putkey: push ax ; edit 04 - save registers for PS/2 push bx ; " push si ; " mov ah,al ; copy scan to someplace useful mov al,0 push ds ; save user's [DS] mov bx,40h ; point to system space push bx pop ds mov bx,tail ; offset of buffer tail mov si,bx inc bx inc bx ; point to next free cmp bx,bend ; at end? jne nowrap ; if not... mov bx,bstart ; else wrap buffer nowrap: cmp bx,head ; buffer full? je bufull ; if so... mov [si],ax ; else store character mov tail,bx ; and update pointer bufull: pop ds ; restore [DS] pop si ; edit 04 - restore registers for PS/2 pop bx ; " pop ax ; " clc ; say we did something... retf 2 ; edit 05 - preserve flags ; ; control functions: enable / disable translation, set LED's, set click ; volume, set auto-repeat rate ; ctlfnc: cmp al,0 ; disable translation? jne tst1 ; no mov active,al ; else do it mov al,0adh ; set keyboard mode call kbsend jmp exit ; and exit ; tst1: cmp al,1 ; enable translation? jne tst2 ; no mov active,al ; else do it mov al,0ach ; set keyboard mode call kbsend jmp exit ; and exit ; tst2: cmp al,2 ; send to keyboard? jne error ; nope, must be an error mov al,bl ; byte to [AL] for send call kbsend ; yes, send the byte out jmp exit ; and exit ; error: mov ax,0 ; say bad function iret ; and exit ; exit: mov ax,1234h ; say we did it iret ; and exit ; newi15 endp ; ; send a byte to the keyboard controller ; kbsend proc near push ax ; save some regs push bx push cx push ds ; save user's [DS] mov bx,40h ; point to system space push bx pop ds mov bh,al ; copy data byte for retry mov bl,3 ; set retry count sd0: cli and kbflg2,0cfh ; turn off ack, re-send bits sub cx,cx ; a nice big loop sd1: in al,64h jmp $+2 ; reduce looping rate test al,2 loopnz sd1 ; if not ready mov al,bh out 60h,al ; send the command sti mov cx,2800h ; wait a bit sd3: test kbflg2,30h ; anything happen? jnz sd7 ; seems so, go handle jmp $+2 ; reduce looping rate loop sd3 ; else wait sd5: dec bl ; call it a retry if timed out jnz sd0 ; if we have another chance or kbflg2,80h ; otherwise fail it jmp sd9 ; and exit ; sd7: test kbflg2,10h ; got a proper ack? jz sd5 ; nope, re-send it sd9: pop ds pop cx pop bx pop ax ret ; kbsend endp ; endres label byte ; end of resident code ; ; Code after here will not remain resident ; Cpu test uses DOS's stack [jrd] start: ; begin with cpu test [jrd] push sp ; push DOS's SP, 8088's push old SP-2 pop ax ; 286's and higher push old SP mov dx,offset main:cpumsg ; prepare bad news message cmp ax,sp ; pre versus post push SP's jne start0 ; ne = an 8088, sorry 'bout that [jrd] mov ax,5000h ; see if we are already loaded int 15h ; look for DOS->DEC mode driver cmp ax,1234h ; find marker 1234h jne start1 ; ne = marker not present, no driver mov dx,offset main:errmsg ; say we're already loaded start0: mov ah,9 int 21h int 20h ; and bail out ; start1: mov ax,3515h ; get existing INT 15 vector int 21h mov word ptr [saved15],bx ; save it mov word ptr [saved15+2],es mov dx,offset main:newi15 ; set new INT 15 vector mov ax,2515h int 21h ; set new vector from DS:DX ; mov dx,offset main:lodmsg ; say we're loaded mov ah,9 int 21h ; mov ax,ds:[2ch] ; de-allocate the environment mov es,ax ; load envirnoment segment into es mov ah,49h ; DOS function number int 21h ; free the environment memory ; mov dx,offset main:endres ; point to end of resident code add dx,0fh ; round up mov cl,4 shr dx,cl ; convert to paragraphs (divide by 16) mov ax,3100h ; DOS function 31h, error code=0 int 21h ; terminate and remain resident ; errmsg: db 0dh,0ah,'MSULK2 is already loaded',0dh,0ah,07h,'$' lodmsg: db 0dh,0ah,'MSULK2 X0.0-05 loaded',0dh,0ah,'$' cpumsg: db 0dh,0ah,'MSULK2 requires a 286 (AT) machine or higher' db 0dh,0ah,'$' ; wrong cpu type msg [jrd] ; code ends end begin ; start execution at BEGIN