;_________________________________________________________________________________________ ; ; File: KbdInstall.a ; ; Contains: Code to ADB keyboard driver and code for installing keyboard driver as ; well as for installing resources KMAP & KCHR. ; This file is the code that is put in the System file's ADBS ; resource ID = 2. The driver in this file overrides the driver in the ROM ; and overrides any patches to the ROM driver. However, this driver ; depends on the ADBProc in the ROM/Patch for the deallocation of the ; keyboard data buffer at the beginning of ADBReinit. ; ; Written by: Joe Fontana, Ed Tecot and Gary G. Davidian ; ; Copyright: © 1986-1992 by Apple Computer, Inc., all rights reserved. ; ; Change History (most recent first): ; ; <4> 10/22/92 JMF Added changes so that the modifiers of the last KEYBOARD ; pressed are the modifiers that the Norsi KEYPAD uses, ; since the KEYPAD has no modifier keys of its own. ; <3> 10/22/92 JMF Incorporated ROM keyboard Driver into ADBS Resource ID=2. ; <2> 12/28/89 dba Used MAIN instead of PROC to get dead code stripping. ; <1.1> 8/28/89 SES Removed references to nFiles. ; <1.0> 11/16/88 CCH Added to EASE. ; 3/2/87 EMT Fixed bug introduced above. ; 2/9/87 EMT Permit KCHR to be overridden Check files of type 'KCAP' for ; KMAPs Use default KMAP (0) as last resort Flush keyboard on ; exit. Dispose of myself on exit. ; 10/14/86 EMT Fix bug related to 6 Oct change above. ; 10/6/86 EMT Data area can be set up by ROM. ; 7/15/86 EMT Updated to use KCHR resource. ; 6/25/86 EMT Created. ; ;_________________________________________________________________________________________ LOAD 'StandardEqu.d' ; Keyboard driver data KBufCount EQU 2 KBufLen EQU 10 ; 8 bytes + length + inuse KMAPPtr EQU $00 KeyBits EQU KMAPPtr+4 KCHRPtr EQU KeyBits+(128/8) DeadKey EQU KCHRPtr+4 KNoADBOp EQU DeadKey+4 KNumBufs EQU KNoADBOp+1 KFirstBuf EQU KNumBufs+1 KbdDSize EQU KFirstBuf+(KBufCount*KBufLen) str EQU -$100 iopb EQU str-ioFQElSize KISize EQU iopb ; KMAP offsets KMid EQU $00 KMtype EQU $01 KMvers EQU KMid+2 KMstart EQU KMvers+2 KMnumEx EQU KMstart+128 KMstEx EQU KMnumEx+2 talkCmd EQU $0C ; Command for Talk R0 keypadNorsi EQU $0E ; Handler ID for Norsi ergonomic keypad ;_________________________________________________________________________________________ ; ; Routine: KbdInst ; Arguments: D0.B ADB Address ; D1.B Device Type ; Output: None ; Function: Loads and locks the KMAP and KCHR resources used by the keyboard driver, ; updates the ADB table and installs a new keyboard driver ; ; Side Effects: Trashes A0, A1, D0, D2 ;_________________________________________________________________________________________ KbdInst MAIN EXPORT BRA.S @Start ; Branch around version number DC.W $0001 ; Version number @Start MOVEM.L D3-D7/A2-A4, -(SP) ; Save the registers LINK A6, #KISize ; Save space on stack MOVE.W D0, D2 ; Save the ADB Address SUB.L #10, SP ; Allocate space on stack MOVE.L SP, A0 ; Pointer to data area _GetADBInfo ADDQ.L #2, SP ; Discard OrigAddr and DeviceType MOVE.L 4(SP), A0 ; Put the data address in A0 MOVE.L A0, D0 ; See if it is a real address BEQ.S @AllocBuf ; Skip DisposPtr, if no buffer allocated yet _DisposPtr ; Dispose of data allocated by the ROM @AllocBuf MOVE.L #KbdDSize, D0 ; Amount of space needed for new keyboard drvr data _NewPtr ,SYS,CLEAR ; Get a pointer MOVE.L A0, A1 ; Save it in A1 MOVE.B #KBufCount, KNumBufs(A1) KCHRLoad SUBQ.L #4, SP ; Make room for result MOVE.L #'KCHR', -(SP) ; ResType = KCHR CLR.W -(SP) ; theID = 0 (for now) MOVE.W #MapTrue, ROMMapInsert ; Use ROM resource if available _GetResource MOVE.L (SP), -(SP) ; Save the handle BEQ NoKCHR ; Skip if NIL _DetachResource ; Detach it MOVE.L (SP)+, A0 _HLock ; Lock it down MOVE.L (A0), KCHRPtr(A1) ; Dereference and put away ; We didn't find the KMAP in the system file or ROM, try the blessed folder MOVE.L #'KCAP', D3 ; File type CLR.W iopb+ioVRefNum(A6) ; The blessed folder CLR.W iopb+ioFDirIndex(A6) ; Initialize the index LEA str(A6), A0 ; Get the address of local string MOVE.L A0, iopb+ioFileName(A6) ; Put in iopb MOVEQ #0, D4 ; No resource file on first pass MOVE.W #MapTrue, ROMMapInsert ; Use ROM resource first time FindKMAP ; SUBQ.L #4, SP ; Make room for result MOVE.L #'KMAP', -(SP) ; ResType = KMAP CLR.W -(SP) ; Clear it out since Device Type is byte MOVE.B D1, 1(SP) ; theID = DeviceType _GetResource MOVE.L (SP), -(SP) ; Save the handle BNE.S GotKMAP ; Skip if not NIL ADDQ.L #8, SP ; Clean off the stack TST.W D4 ; File open? BEQ.S NextFile ; Nope, go on MOVE.W D4, -(SP) ; refNum _CloseResFile ; NextFile LEA iopb(A6), A0 ; Point to the block ADD.W #1, ioFDirIndex(A0) ; Increment the file index _GetFileInfo ; BMI.S NoMoreFiles ; That's all of them CMP.L ioFlUsrWds+fdType(A0), D3 ; Correct type? BNE.S NextFile ; Nope, try again SUBQ.L #2, SP ; Make room for result PEA str(A6) ; fileName _OpenResFile ; MOVE.W (SP)+, D4 ; Store the refNum BRA.S FindKMAP ; NoMoreFiles ; All is not lost. We can try to find the default KMAP (0) SUBQ.L #4, SP ; Make room for result MOVE.L #'KMAP', -(SP) ; ResType = KMAP CLR.W -(SP) ; theID = 0 MOVE.W #MapTrue, ROMMapInsert ; Use ROM resource if available _GetResource ; MOVE.L (SP), -(SP) ; Save the handle BEQ.S NoKMAP ; Skip if NIL GotKMAP ; Assumes two copies of the resource handle are on the stack. _DetachResource ; Detach it MOVE.L (SP)+, A0 _HLock ; Lock it down MOVE.L (A0), A0 ; Dereference it MOVE.L A0, KMAPPtr(A1) ; Put it away MOVE.B D1, KbdType ; Update KbdType to show this keyboard MOVE.B D2, KbdLast ; Same with KbdLast MOVE.W D2, D0 ; ADB Address MOVE.L A1, 4(SP) ; Replace the data address LEA KbdDrvr, A0 MOVE.L A0, (SP) MOVE.L SP, A0 ; Pointer to two addresses _SetADBInfo TST.W D4 ; File open? BEQ.S Done ; Nope, go on MOVE.W D4, -(SP) ; refNum _CloseResFile ; BRA.S Done ; NoKMAP NoKCHR ADDQ.L #8, SP ; Discard unused parameters Done MOVE.W D2, D0 ; ADB Address LSL.W #4, D0 ; Put address in high nibble ADDQ.W #1, D0 ; Flush command CLR.L -(SP) ; No data address CLR.L -(SP) ; No completion routine CLR.L -(SP) ; No buffer MOVE.L SP, A0 ; Point to the block _ADBOp ; Flush the keyboard LEA 12(SP), SP ; Remove parameters from stack UNLK A6 ; MOVEM.L (SP)+, D3-D7/A2-A4 ; Restore the registers RTS ; End KbdInst ;_________________________________________________________________________________________ ; ; Routine: KbdDrvr ; Arguments: D0.B ADB Command ; A0.L ADB Buffer address ; A1.L ADB Completion Routine Address (= KbdServ) ; A2.L Pointer to private data area ; Output: None ; Function: Reads buffer and posts keyboard events as appropriate. ; Side Effects: Trashes A0, A1, D0, D1, D2, D3 ; ;_________________________________________________________________________________________ KbdDrvr MOVE.L A2, D3 ; See if A2 actually contains a pointer BEQ KbdDone ; If not, can't go on. MOVE.L A0, A1 ; Save A0 in A1 LSR.W #4, D0 ; Shift ADB Address down to low nibble MOVEQ #$F, D1 ; Mask for ADB Address AND.L D1, D0 ; D0 now contains ADB Address MOVE.L D0, D3 ; Save it in D3 LEA -10(SP), SP ; Build parameter block on stack MOVE.L SP, A0 ; Point to it _GetADBInfo ROR.L #8, D3 ; Rotate ADB Address to high byte MOVE.W (SP)+, D3 ; Put Device Type, Orig Addr in low word ADDQ.L #8, SP ; Clear off the rest of the stack SWAP D3 ; D3 is now Device Type;Orig Addr;ADB Addr;Unused MOVE.B 1(A1), D0 ; Get first stroke MOVE.B 2(A1), -(SP) ; Save second one on stack BSR.S KeyIn MOVE.B (SP)+, D0 ; Get second stroke ;_________________________________________________________________________________________ ; ; Routine: KeyIn ; Arguments: D0.B Raw Keycode ; D3.L Device Type, Orig Addr, ADB Addr, Unused ; A2.L Pointer to private data area ; Output: None ; Function Translates keycode and posts event as appropriate. ; Side Effects: Trashes A0, A1, D0, D1, D2, D3 ; Called From: KbdDrvr twice, (1 BSR, 1 fall-through) ; ;_________________________________________________________________________________________ KeyIn CMP.B #$FF, D0 ; Is it not a key? BEQ KbdDone ; Skip if so CLR.W KeyLast ; Stop repeating CLR.W HiKeyLast ; Stop repeating MOVEQ #$7F, D1 ; Mask = 01111111 binary AND.B D0, D1 ; Clear all but low 7 bits MOVE.L KMAPPtr(A2), A1 ; Get KMAP table address MOVE.B KMstart(A1, D1), D3 ; Get device independent keycode BPL.S NoExcept ; Handle normally if high bit clear ; An exception has been indicated. Find the correct entry in the exception ; table and handle as appropriate. BCLR #7, D3 ; Clear the high bit LEA KMnumEx(A1), A0 ; Get to the beginning of the exceptions MOVE.W (A0)+, D2 ; Number of entries in table BEQ.S NoExcept ; Skip if none SUBQ.W #1, D2 ; Turn it into a zero-based count ExLoop CMP.B (A0)+, D0 ; See if this is the one BEQ FoundEx ; Skip if so MOVE.B 1(A0), D1 ; Get the string length LEA 2(A0, D1), A0 ; Point to the next entry DBRA D2, ExLoop ; Go around again NoExcept MOVEQ #0, D2 ; Clear out D2 MOVE.B D3, D2 ; Copy virtual keycode to D2 LSR.W #3, D2 ; Divide by 8 for byte offset TST.B D0 ; Up or down key? BMI.S KeyUp ; Skip around if key up BSET D3, KeyBits(A2, D2) ; Set it for key down BRA.S Hammer KeyUp BCLR D3, KeyBits(A2, D2) ; Clear it for key up BSET #7, D3 ; Remember key up for raw key. ;* Begin Norsi Keypad changes (keypad uses modifiers from last keyboard pressed) 04/21/92* Hammer MOVE.L D3, D0 ; Get Device Type, Orig Addr, ADB Addr, keycode ROL.L #8, D0 ; Rotate Device Type into low byte CMP.B #keypadNorsi, D0 ; Is this a Norsi ergonomic KEYPAD? BNE.S HammerKeyMap ; If not, go update the keymap MOVE.W KeyMap+6, D0 ; Modifier bits of last pressed keyboard AND.W #$807F,D0 ; Mask off non-modifier bits MOVE.W KeyBits+6(A2), D1 ; Get Norsi KEYPAD's pseudo modifier bits AND.W #$7F80,D1 ; Mask off KEYPAD's old modifier bits OR.W D0, D1 ; Replace KEYPAD's modifier bits with the modifier MOVE.W D1, KeyBits+6(A2) ; bits from the last pressed keyboard ;* End Norsi Keypad changes (keypad uses modifiers from last keyboard pressed) 04/21/92* HammerKeyMap MOVEM.L KeyBits(A2), D0-D2/A0 ; Get current devices map of key pressed or not MOVEM.L D0-D2/A0, KeyMap ; Hammer this devices map into global keymap MOVE.L D3, D0 ; Bits 15-8 contain ADB address LSR.L #8, D0 ; Put it in the low byte MOVE.B D0, KbdLast ; Stuff it down SWAP D0 ; Now get DeviceType MOVE.B D0, KbdType ; Update KbdType to show last one used ; The next two instructions build the byte of modifier flags from the ; global key state information. This works because the modifier flags ; exist in bits $37 to $3E, which appear in the following manner: ; Byte | 6 | 7 | ; Bit |37 36 35 34 33 32 31 30|3F 3E 3D 3C 3B 3A 39 38| ; |^^ | ^^ ^^ ^^ ^^ ^^ ^^ ^^| MOVE.W KeyBits+6(A2), D0 ; Get modifier word ROL.W #1, D0 ; Rotate in command key SUBQ.L #4, SP ; Make room for result MOVE.L KCHRPtr(A2), -(SP) ; Push address of KCHR resource MOVE.W D3, -(SP) ; Push keycode (w/o modifiers) MOVE.B D0, (SP) ; Put modifiers where they belong PEA DeadKey(A2) ; Push address of dead key state _KeyTrans MOVE.W (SP)+, D0 ; Get the high word first BEQ.S NextWord ; Skip if null BSR.S PostIt ; Otherwise post the event NextWord MOVE.W (SP)+, D0 ; Get the other word BEQ.S KbdDone ; If null, we're done ;_________________________________________________________________________________________ ; ; Routine: PostIt ; Arguments: D0.W ASCII Code ; D3.W ADB Address in high byte and raw keycode in low byte ; A2.L Pointer to private data area ; Output: None ; Function Posts the keyboard event as appropriate. ; Side Effects: Trashes A0, D0, D1 ; Called From: KeyIn twice, (1 BSR, 1 fall-through) ; ;_________________________________________________________________________________________ PostIt ROR.W #8, D0 ; Swap ASCII high and low byte (xxLH) SWAP D0 ; Move to high word (LHxx) MOVE.W D3, D0 ; Move in ADB address and raw keycode (LHFR) ROL.L #8, D0 ; Rotate around (HFRL) TST.B D3 ; Key up or down? BMI.S PostKeyUp ; Skip if key up MOVE.L Ticks, D1 MOVE.L D1, KeyTime ; Mark the time for auto repeat MOVE.L D1, KeyRepTime MOVE.W D0, KeyLast ; Save event message SWAP D0 MOVE.W D0, HiKeyLast ; Save high word too SWAP D0 MOVE #KeyDwnEvt, A0 ; Get event number _PostEvent ; Post it KbdDone RTS ; And leave PostKeyUp MOVE #KeyUpEvt, A0 ; Get event number BCLR #15, D0 ; Clear the up/down bit in the raw keycode _PostEvent ; Post it RTS ; End KbdDrvr ;_________________________________________________________________________________________ ; ; Routine: FoundEx ; Arguments: A0.L Pointer to exception data ; A2.L Pointer to private data area ; D3.W Virtual keycode ; Output: None ; Function An exception exists for this particular keystroke. Process it ; appropriately. ; Side Effects: Trashes A0, D0, D1, D2 ; Called From: KeyIn twice, (1 BSR, 1 fall-through) ; ;_________________________________________________________________________________________ FoundEx MOVE.B (A0)+, D1 ; Get the operand BPL.S @notXORKey ; Skip if not MOVEQ #0, D2 ; Clear out D2 MOVE.B D3, D2 ; Copy virtual keycode to D2 LSR.W #3, D2 ; Divide by 8 for byte offset BTST D3, KeyBits(A2, D2) ; Get current key state SEQ D0 ; Invert and put in D0 @notXORKey MOVEQ #$F, D2 ; Prepare mask for ADB op AND.B D1, D2 ; D2 is ADB op w/o net address BEQ.S KbdDone ; If ADB op = 0 (Bus Reset), ignore key TST.B KNoADBOp(A2) ; See if we should even do this BNE NoExcept ; Skip if not MOVEM.L D0/A1, -(SP) ; Save D0 & A1 MOVE.L A0, -(SP) ; Data address = mask CMP.B #TalkCmd, D2 ; Is it a talk command? BGE.S @kbdTalk ; Skip if so PEA KbdBufFree ; Completion routine = KbdBufFree BRA.S @kbdBufAlloc @kbdTalk PEA KbdListen ; Completion Routine = KbdListen @kbdBufAlloc LEA KNumBufs(A2), A1 ; Point to the number of available buffers MOVE.B (A1)+, D1 ; Get the number of buffers BEQ.S @kNoBufAvail ; Skip if none available SUBQ.W #1, D1 ; Turn it into a zero based count @kBufLoop TST.B (A1)+ ; Is the buffer busy? BEQ.S @kGotABuf ; No, Go use it LEA KBufLen-1(A1), A1 ; Point to the next one DBRA D1, @kBufLoop ; Go around again BRA.S @kNoBufAvail ; It's a loss @kGotABuf MOVE.B D0, -1(A1) ; Store the up/down state in the busy info BSET #1, -1(A1) ; Make sure it shows up as busy MOVE.L A1, -(SP) ; Buffer Address MOVE.B (A0), D1 ; Get length of source string CMP.B #8, D1 ; Greater than 8? BLS.S @kStrCopyLoop ; If not, no problem MOVEQ #8, D1 ; Copy only the first 8 to avoid trashing mem @kStrCopyLoop MOVE.B (A0)+, (A1)+ ; Start copying the string DBRA D1, @kStrCopyLoop ; Repeat D1+1 times MOVE.W D3, D0 ; Get the FDB Address CLR.B D0 ; Clear out the low byte LSR.W #4, D0 ; Shift it down to form high nibble of ADB Command OR.B D2, D0 ; Include low op nibble MOVE.L SP, A0 ; Point to parameter block _ADBOp ; Pray that everything is OK BNE.S @kOpFailed ; Branch if not ADDQ.L #4, SP ; Pop Buffer Address @kNoBufAvail ADDQ.L #8, SP ; Pop Completion and Data Address MOVEM.L (SP)+, D0/A1 ; Restore D0 & A1 BRA NoExcept ; Finish dealing with the keystroke @kOpFailed MOVE.L (SP)+, A1 ; Get the buffer address CLR.B -1(A1) ; Mark it as not busy BRA.S @kNoBufAvail ; End FoundEx ;_________________________________________________________________________________________ ; ; Routine: KbdListen ; Arguments: D0.B ADB Command ; D1.L DeviceType, OrigAddr, ADBAddr, Unused (byte order) ; A0.L ADB Buffer Address ; A1.L ADB Completion Routine Address (= KbdListen) ; A2.L Pointer to private data area ; Output: None ; Function: Sets or clears bits in mask pointed to by A2 in buffer pointed ; to by A0. Used to alter values of registers in ADB devices. ; Side Effects: Trashes A0, A1, A2, D0, D1, D2 ; ;_________________________________________________________________________________________ KbdListen MOVE.L A0, A1 ; Copy A0 into A1 MOVEQ #0, D1 ; Clear out D1 MOVE.B (A1)+, D1 ; Get length of buffer MOVE.B (A2)+, D2 ; Get length of mask CMP.B D2, D1 ; Is mask length smaller? BLS.S @notSmall ; Skip if not MOVE.B D2, D1 ; Use the mask length instead @notSmall ; (A2) is a mask for (A0), 0 meaning don't change, 1 meaning clear or set ; depending upon the value of -1(A0). TST.B -1(A0) ; PL = clear, MI = set BPL.S @endClrLoop BRA.S @endSetLoop @setLoop MOVE.B (A2)+, D2 ; Get the mask byte OR.B D2, (A1)+ ; Set the correct bits @endSetLoop DBRA D1, @setLoop ; Go around again BRA.S @kLoopDone @clrLoop MOVE.B (A2)+, D2 ; Get the mask byte NOT.B D2 ; Invert it AND.B D2, (A1)+ ; Clear the correct bits @endClrLoop DBRA D1, @clrLoop ; Go around again @kLoopDone CLR.L -(SP) ; No data address needed PEA KbdBufFree ; Completion routine = KbdBufFree MOVE.L A0, -(SP) ; Use the buffer one more time MOVE.L SP, A0 ; Point to parameter block BCLR #2, D0 ; Turn the talk into a listen command _ADBOp BNE.S @kLSuccess ; Branch on success MOVE.L (SP), A0 ; Get the buffer address CLR.B -1(A0) ; Mark it as not busy @kLSuccess LEA 12(SP), SP ; Pop the parameter block RTS ; End KbdListen ;_________________________________________________________________________________________ ; ; Routine: KbdBufFree ; Arguments: D0.B ADB Command ; D1.L DeviceType, OrigAddr, ADBAddr, Unused (byte order) ; A0.L ADB Buffer Address ; A1.L ADB Completion Routine Address (= KbdListen) ; A2.L Pointer to private data area ; Output: None ; Function: Marks the buffer pointed to by A0 as free. ; Side Effects: None ; ;_________________________________________________________________________________________ KbdBufFree CLR.B -1(A0) ; Mark buffer free RTS ; End KbdBufFree ;_________________________________________________________________________________________ ;_________________________________________________________________________________________ ;_________________________________________________________________________________________ END ;End file