boot3/OS/Keyboard/KbdInstall.a
Elliot Nunn 5b0f0cc134 Bring in CubeE sources
Resource forks are included only for .rsrc files. These are DeRezzed into their data fork. 'ckid' resources, from the Projector VCS, are not included.

The Tools directory, containing mostly junk, is also excluded.
2017-12-26 10:02:57 +08:00

559 lines
19 KiB
Plaintext

;_________________________________________________________________________________________
;
; 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