Name SERKBD Title STDTSR ; ; SERKBD.ASM - - - Clock Sampled Key-pad Interrupt Handler ; ; A "well behaved" interrupt handler ; that becomes resident after DOS is running ; ; Note: This program is intended to be used in lieu of a real keyboard ; ; Copyright (C) 1990 Jim Phillips ; ; To assemble, Link, and convert this program to a COM file: ; ; C>MASM SERKBD; ; C>LINK SERKBD; ; C>EXE2BIN SERKBD.EXE SERKBD.COM ; C>DEL SERKBD.EXE ; ; CR EQU 0DH ; ASCII carriage return LF EQU 0AH ; ASCII line feed ; CSEG SEGMENT PARA PUBLIC 'CODE' ; ORG 0100H ; ; ASSUME CS:CSEG, DS:CSEG, SS:CSEG, ES:CSEG ; INIT PROC NEAR ; JMP INITST ; Jump arround imbedded parameters ; ; The following tables contain the key codes to be "Jammed" into the BIOS ; keyboard buffer. They have a direct one for one entry relationship with ; the "switch closed for number of time periods" table below. UpKyCod DW 04800H ; Up arrow DW 04700H ; Home DW 04400H ; F10 DW 04900H ; Pg Up DnKyCod DW 05000H ; Down arrow DW 04F00H ; End DW 01C0DH ; Enter DW 05100H ; Pg dn ; SPORT DW 03F8H ; Preset Port base address ; 3F8 = COM1 2F8 = COM2 ; TypMatTrigr DB 007H ; Holds the number of 1/9ths seconds ; that elapse before Typematic ; activity begins TypMatPrd DB 005H ; Holds Typematic Period. Esentially ; this parameter is used to control the speed at which typematic action occurs ; The limits for this parameter are. 0 < TypMatPrd < TypMatTrigr ; The closer TypMatPrd is set to TypMatTrigr the faster it goes. ; ; The following imbedded table discription marks the end of preset parameters DB 'Setting tables in Debug, 100hex to 102hex = JMP inst. ' DB '8 words follow that set key codes that are generated ' DB 'when DB25 RS232 connector pins are connected. ' DB 'Pin 8 - 4, Pin 22 - 4, Pin 6 - 4, Pin 5 - 4, ' DB 'Pin 8 - 20, Pin 22 - 20, Pin 6 - 20, Pin 5 - 20. ' DB '1 word follows it is the base address of the 8250 chip. ' DB '1 byte follows Typmatic Delay. ' DB '1 byte follows Typmatic Period. ' SIGNON DB 'Key Pad Driver Installed.' ; DB 0DH,0AH ; DB 'Copyright (C) 1990 Jim Phillips' ; DB 0DH,0AH,024H ; ; ; The following table contains the number of 1/9ths sec. time periods a switch ; remains closed for Typematic sense. UDCD DB 000H ; URIN DB 000H ; UDSR DB 000H ; UCTS DB 000H ; DDCD DB 000H ; DRIN DB 000H ; DDSR DB 000H ; DCTS DB 000H ; ; ; Start of program INITST: MOV AH,035H ; Get Inturrupt BDOS function number MOV AL,008H ; Interrupt number for hardware timer INT 021H ; Execute function MOV WORD PTR CHNVEC,BX ; Store target address MOV WORD PTR CHNVEC+2,ES; in double word MOV AL,008H ; Revector Interrupt 8, timer tick MOV DX,OFFSET SERKBDH ; vector to address of new tsr MOV AH,025H ; handler by using function 25H INT 021H ; Call DOS and do it MOV DX,OFFSET SIGNON ;Print identification message MOV AH,09H ; INT 021H ; MOV DX,((OFFSET PGM_LEN+15)/16)+10H ; Paragraphs of memory to ; reserve MOV AX,3100H ; Exit and stay resident command INT 021H ; w/return code 0 INIT ENDP ; ; SERKBDH PROC FAR ; This is the key pad interrupt handler MOV CS:AXTMP,AX ; Save these variables in the code MOV CS:SSTMP,SS ; segment because we don't know where MOV CS:SPTMP,SP ; the stack is and we need to be sure ; we can reconstruct it. MOV AX,CS ; Now move the stack to the code segment MOV SS,AX ; (a nice warm place where MOV SP,OFFSET CS:SPLOC ; space is reserved for it) PUSH BX ; Save the world PUSH CX ; PUSH DX ; PUSH SI ; PUSH DI ; PUSH BP ; PUSH DS ; PUSH ES ; PUSHF ; PUSH CS ; Point DS to CS POP DS ; ; ; ; Read Serial Port Modem Line Bits ; Interrupt 0C = addr 30 IRQ4 Com1: 03f8h Com3: 03e8h ; Interrupt 0B = addr 2C IRQ3 Com2: 02f8h Com4: 02e8h ; ; WD8250 Registers ; Note: Dlab = Divisor Latch Access Bit. This bit is located in the ; Line Control register bit 7 ; ; Addr Dlab state Read/write Function ; Base + 0 0 read = Receiver buffer register ; Base + 0 0 write = Transmitter holding register ; Base + 1 0 either = Interrupt enable mask register ; Base + 2 X either = Cause for interrupt register ; Base + 3 X either = Line control register ; Base + 4 X either = Modem control register ; Base + 5 X either = Line status register ; Base + 6 X either = Modem status register ; Base + 0 1 either = Divisor latch LSB ; Base + 1 1 either = Divisor latch MSB ; For the purposes of this program it is unecessary to list specifics of ; registers other than Modem control and status. They are needed because the ; serial port handshake lines are used by this program to scan a 3 by 2 matrix ; of push buttons that will "fake" a cursor keypad by filling the keyboard ; buffer with "simulated" key codes. ; Register bit0 bit1 bit2 bit3 bit4 bit5 bit6 bit7 ; Modem control reg:DTR RTS OUT1 OUT2 LOOP 0 0 0 ; ; Modem status reg: DCTS DDSR TERI DDCD CTS DSR RI DCD ; Note: "D" preceeding a signal name means Delta. ; I.E. DCTS = Delta Clear to Send. Also TERI = Trailing Edge Ring Indicator ; 1 frame ground ; > 2 xmit data ; < 3 rcv data /-------------\ ; > 4 request to send -------/--+-+------+----\----------+- ; < 5 clear to send -------/ | | o \ | ; < 6 data set ready ------------+-+--o---- \ o ; = 7 signal ground o | sw3 o \---o---- ; < 8 data carrier detect----o---- | | sw4 o ; 9 V+ test voltage sw1 o | | | ; 10 V- test voltage | | | | ; 11 DTR Not (inverse data terminal ready) | ; 12 2nd data carrier detect | | | | ; 13 2nd clear to send | | | | ; 14 2nd xmit data | | | | ; 15 transmit clock | | | | ; 16 2nd rcv data | | | | ; 17 recieve clock | | | | ; 18 unasigned | | | | ; 19 2nd request to send | | | | ; > 20 data terminal ready ------+-+--+---+---------------+- ; 21 signal quality detect | | ; < 22 ring indicator ----\ o | ; 23 data rate select \----o---- | ; 24 external xmit clock sw2 o--+ ; 25 unasigned ; Note: Each of the switches in the diagram are shown in open position spring ; return to center. Up is shown as up and down is shown as down. ; MOV DX,WORD PTR SPORT ; Setup port address ADD DX,0006H ; for WD8250 modem status IN AL,DX ; Grab bits AND AL,0F0H ; Look only at switch input matrix bits OR AL,BYTE PTR SwSel ; Glue in the current switch select bit MOV BYTE PTR Signal,AL ; Place result in the signal byte MOV DX,WORD PTR SPORT ; Setup port address ADD DX,0004H ; for WD8250 modem control IN AL,DX ; Grab old control port bits AND AL,0FCH ; Kill lower two OR AL,BYTE PTR SwSel ; Glue in the new switch select bit OUT DX,AL ; Set the wires on the WD8250 for next ; time XOR BYTE PTR SwSel,03H ; Set port select bits to opposite ; switch for time after next time. MOV AL,BYTE PTR Signal ; Get input signal AND AL,0F0H ; Look only at switch input matrix bits CMP AL,000H ; Check if any are lit JZ ResetAll ; If not reset all switch closure timers CALL KeyDwn ; If so process key closure JMP EXIT ; and exit TSR ResetAll: TEST BYTE PTR Signal,001H ; Check signal for Up/Down closure ; sensing mode JNZ ResetDwn ; If we were sensing Down closures ; then reset Down timers MOV AL,000H ; If we were sensing Up closures MOV BYTE PTR UDCD,AL ; then reset Up timers MOV BYTE PTR URIN,AL ; MOV BYTE PTR UDSR,AL ; MOV BYTE PTR UCTS,AL ; JMP EXIT ; ResetDwn: MOV AL,000H ; Reset Down timers MOV BYTE PTR DDCD,AL ; MOV BYTE PTR DRIN,AL ; MOV BYTE PTR DDSR,AL ; MOV BYTE PTR DCTS,AL ; ; EXIT: POPF ;Disable interrupts POP ES ;Restore the world POP DS ; POP BP ; POP DI ; POP SI ; POP DX ; POP CX ; POP BX ; MOV SP,CS:SPTMP ; MOV SS,CS:SSTMP ; MOV AX,CS:AXTMP ; JMP DWORD PTR CS:CHNVEC ;Jump to and execute the other time SERKBDH ENDP ; keeping functions serviced by this ; interrupt ; KeyDwn PROC NEAR ; This is the key closure handler TEST BYTE PTR Signal,002H ; Was a switch flipped up? JNZ SwUp ; If so handle an up switch MOV BP,OFFSET DDCD ; If not setup to point to down tables MOV DI,OFFSET DnKyCod ; JMP KeyDwn0 ; Enter common code SwUp: MOV BP,OFFSET UDCD ; Otherwise setup to point to up tables MOV DI,OFFSET UpKyCod ; KeyDwn0: MOV AL,080H ; Seed Walking bit (it walks right) KeyDwn1: TEST BYTE PTR Signal,AL ; Examine current bit JZ KeyDwn2 ; If it isn't hot skip that part CMP BYTE PTR [BP],000H ; If it's hot now, was it allready hot JNZ KeyDwn3 ; If it was see if it's going Typematic INC BYTE PTR [BP] ; Tick off 1/18.2th second of closure CALL SendCode ; If key press is a virgin send the code KeyDwn2: CALL BumpNchk ; Bump and Check end of bit walk JNC KeyDwn1 ; If not at the end keep going RET ; else exit proc KeyDwn3: INC BYTE PTR [BP] ; Tick off 1/18.2th second of closure MOV AH,BYTE PTR TypMatTrigr ; If it's Typematic CMP BYTE PTR [BP],AH ; JZ KeyDwn4 ; Handle a Typematic iteration CALL BumpNchk ; If not Bump and Check end of bit walk JNC KeyDwn1 ; If not at the end keep going RET ; else exit proc KeyDwn4: MOV AH,BYTE PTR TypMatPrd ; It's Typematic. MOV BYTE PTR [BP],AH ; Rewind time a little CALL SendCode ; Send the code CALL BumpNchk ; Bump and Check end of bit walk JNC KeyDwn1 ; If not at the end keep going RET ; else exit proc KeyDwn ENDP ; ; BumpNchk PROC NEAR ; This is bumps all 3 pointers and ; returns a clear carry unless exausted INC BP ; Bump to next byte INC DI ; Bump to next word INC DI ; CLC ; Set up for bit walk. Clear bit that RCR AL,1 ; will enter high bit and walk them. TEST AL,008H ; Did the walking bit drop into the low JNZ BumpNck0 ; nybble? If so set carry. CLC ; If not clear carry RET ; and leave BumpNck0: STC ; RET ; BumpNchk ENDP ; ; ; 0417 word for remapping keys (do not mess with 0418) ; 0419 one byte alternate keyboard input (for future use) ; 041A word head for bios keyboard buffer ; 041C word tail for bios keyboard buffer ; 041E 32 byte pairs the keyboard buffer ; Head & tail pointers contain values from 001E to 003D. If they match the ; buffer is empty. ; The head & tail nomenclature is backward if you're a keyboard driver! ; as a keyboard driver you put the byte into the the position pointed to by ; the TAIL and then increment the tail. As the software uses characters out ; of the buffer the HEAD catches up! Not the other way arround. ; ; Modem status reg: DCTS DDSR TERI DDCD CTS DSR RI DCD ; ; SendCode PROC NEAR ; This sends a key code to BIOS buffer PUSH AX ; PUSH BP ; PUSH DI ; MOV AX,00000H ; Point extra segment to bottom of MOV ES,AX ; memory. Where BIOS keeps keyboard ; buffer. MOV AX,ES:[041CH] ; Get tail pointer MOV BX,AX ; Save Tail pointer before wrap arround ; check and compensation CMP AX,0003CH ; Check for impending buffer overflow JL SndCod0 ; If not skip wrap arround code SUB AX,00020H ; If so rewind Tail pointer SndCod0: INC AX ; Look ahead first byte CMP AX,ES:[041AH] ; Check for potential collision JZ SndCod1 ; If so, give up fate went against you INC AX ; Look ahead second byte CMP AX,ES:[041AH] ; Check for potential collision JZ SndCod1 ; If so, give up fate went against you MOV ES:[041CH],AX ; If not we are free to write the ; buffer so update pointer ADD BX,00400H ; Add in the base value to ; pointer offset MOV AX,DS:[DI] ; Write the keycode MOV ES:[BX],AX ; into the BIOS keyboard buffer SndCod1: POP DI ; POP BP ; POP AX ; RET ; SendCode ENDP ; ; ; Signal DB 00H ; SwSel DB 01H ; Switch Select CHNVEC DD ? ; Chain vector ; SSTMP DW 0000H ; Stack segment temporary SPTMP DW 0000H ; Stack pointer temporary AXTMP DW 0000H ; AX register temporary ; DB 50 DUP ('STACK ') SPLOC EQU $ ; Local stack pointer DB 'Copywrong (w) Zap-Tek' ; Memory marker to identify end of ; program ; PGM_LEN EQU $-INIT ; CSEG ENDS ; END INIT END START