; ; File: ADBMgr.a ; ; Contains: This file holds the ADB implementation as used on the Mac SE and later machines. ; It is separated out from the original Mac keyboard implementation to reduce ; conditional assembly confusion. Some of the original file’s modification ; history has been preserved here for your amusement. ; ; Written by: ADB manager re-written 15-May-89 by Gary G. Davidian ; ; Copyright: © 1981-1993 by Apple Computer, Inc. All rights reserved. ; ; Change History (most recent first): ; ; 12/13/93 PN Roll in KAOs and Horror changes to support Malcom and AJ ; machines. ; 5/20/93 RB Rolled in a patch to the KeyTrans routine that we missed last ; time... ; 11/30/92 SWC Fixed the Horror roll in. A bunch of code was left hanging ; around that doesn't need to be here. ; 11/3/92 RB Added back support for the KeyHooks (even though KeyTrans has ; been available for years) because of FoolWrite and Microsoft ; Weird. (Yikes !) ; 8/26/92 kc Roll in Horror changes. From KbdADB.a ;
7/13/92 GMR DefaultDevice now calls CrsrDevReInit (post-proc) to install the ; mice earlier so we don't loose the mouse-down event for ejecting ; floppies. ;
6/4/92 ag Added flush and delay to regenerate any keystrokes which may ; come in before the handlers are in place. The delay was ; necessary to allow the keyboard time to return keys down. The ; approx. time was determined by experimentation, then 100% margin ; was added for safety. ;

4/3/92 SWC Modified InitADB and RequestDone to use primitives tables (yes, ; another one) attached to the ProductInfo table. All ; implementation-specific code is now in ADBPrimitives.a. Removed ; the old mouse driver (and initialization) since that's now ; handled in CrsrDev.a. ;

2/21/92 SWC GMR/Backed out since DefaultDev and KbdInstall can move ; memory (they call GetResource), and InitDevT is a completion ; routine run at interrupt level 1. The symptoms were that ; _ADBReInit could hang or crash. ;

2/12/92 SWC Patched in new CrsrDev stuff for mouse/trackball/etc. ; acceleration. ; 5/21/92 kc Append "Trap" to the names of GetADBInfo, SetADBInfo, ADBOp and ; GetIndADB to avoid name conflict with the glue. ; 5/17/92 kc Add include of PowerPrivEqu.a. Export ; ExplicitRequestDone,ImplicitRequestDone and RunADBRequest. ; 4/30/92 SES Rolled in ReqDoneVIA patch from PatchIIciROM.a. The patch makes ; sure that the routine doesn't try to poll a device that's not in ; the device table. ; 4/14/92 RB Rolled in ADReInitPatch from BeforePatches.a (PTCH 0). The patch ; restores the KCHR in the data records associated with keyboard ; drivers if a KCHR pointer has already been stored in ExpandMem. ; This is done as a post-procedure on the ADBReinit hook. ; <2> 3/27/92 JSM Export InitADBDrvr. ; <1> • Pre-SuperMario comments follow • ; <14> 2/12/92 JSM Moved this file to ADBMgr folder, keeping all the old revisions; ; this file used to be included by Kbd.a, now it stands by itself, ; so merge in the Kbd.a wrapper. ; <13> 1/20/92 KC Roll in Terror changes. Original comments below. ; {6} 6/15/90 CV Adding code to check the ADB device address against the ADB ; device bit map for Egret transactions. If the device is not in ; the map, the MRU and LRU are forced to the mouse and keyboard. ; This is a work-around for an autopoll bug in Egret. ; From KbdADB.a. ; [6] 6/6/91 ag moved data structure initialization routines inside initdevT. ; From EgretPatchesToo.a (via EgretMgr.a) ; (13) 6/11/91 BG (actually GS/GA) Added a check to the RequestDonePatch routine ; to check if we are in the middle of ADB initialization. If in ; INIT then pass control to the handler address, if valid. ; <12> 10/18/91 JSM Get rid of all the stupid conditionals. ; <11> 5/21/91 gbm Nail a couple of warnings ; <10> 9/18/90 BG Removed <7>, <8>. 040s are behaving more reliably now. ; <9> 9/14/90 MSH Interface to the Power Manager changed to cut down on the ; overhead. ; <8> 8/3/90 BG Found another place to put an EclipseNOP. ; <7> 6/18/90 CCH Added EclipseNOPs for flaky 68040's. ; <6> 3/29/90 MSH Add universal test for call to IdleUpdate. ; <5> 2/28/90 GMR Backed out of changes <2>,<3>, and <4>. Implemented new ADB ; support for use with the new EgretMgr. ; <4> 2/11/90 GA Added endwith to with statements inside hasEgret Conditionals ; <3> 2/9/90 GA Moved the code which sends SendBitmap and StartAutoPoll commands ; to Egret from ADBfMgr.a to EgretMgr.a. Now, ADBMgr.a does a JSR ; StartEgretAutoPoll to EgretMgr. ; <2> 2/4/90 GA Adding ADB support for Egret. ; <2.1> 11/19/89 GGD NEEDED FOR ZONE-5 Modified IOP Based ADB to pass DevMap to the ; IOP when auto polling so that the IOP will just poll the ; addresses in the device table. This will solve the the problems ; of the jerky cursor when typing and mousing at the same time, as ; well as fixing the EvE Copy Protection device problems. ; <2.0> 11/1/89 MSH Replaced activity detection with IdleUpdate trap (hcmac). ; <1.9> 7/12/89 GGD Fixed problems when a mouse or keyboard were not present at ; boot, but were connected later. Although default entries were ; being created in the device table for them, they were not being ; auto/srq polled. They should now behave as they did on the Mac ; II. Added a small amount of padding for overpatching. ; <1.8> 5/16/89 GGD Implemented de-facto standard to allow a buffer address of zero ; to be passed to ADBop, it will not read or write to the buffer ; (not even the length byte). ; <1.7> 5/15/89 GGD Re-wrote the ADB manager, isolated the hardware dependencies and ; added universal rom support. Fixed many known problems with ; prior implementations. Modified initialization code to use ; ADBop. Re-wrote Mouse Driver to not directly access ADB manager ; variables, and to de-bounce the mouse button without loosing ; mouse up events. Moved functionality of the routine InitADBVars ; from StartInit into InitADB. ; <1.6> 5/1/89 GGD Added back some code that was inadvertently deleted from the ; ViaADB routines to try to fix some of the problems that have ; appeared in that implementation. Changed to use new RECORD ; version of AppleDesktopPriv.a. Added a few minor changes in ; preparation for universal rom version (coming soon). ; <1.5> 4/7/89 MSH Moved PMGT interrupt enable from here to startinit. ; <1.4> 3/30/89 CSL disable interrupt when command queue is manipulated. ; <1.3> 3/5/89 PKE Roll in & update KeyTrans patch (Int'l itlk processing) from ; KeyHack.a ; <1.2> 3/1/89 MSH Exported RSetKmap ; <1.1> 11/10/88 CCH Fixed Header. ; <1.0> 11/9/88 CCH Adding to EASE. ; <1.1> 10/7/88 rwh for IopADB, added MACHINE MC68020 and WITH ADBIOPVars. ; <1.0> 10/6/88 rwh New Today - split off from Kbd.a for programmer sanity. Uses ; feature-based conditionals now. ; <•2.0> 9/23/88 CCH Got rid of inc.sum.d and empty nFiles ; <1.9> 9/8/88 MSH New sleep/idel support routine. ; <1.8> 8/18/88 MSH Removed stack frame too soon. ; <1.7> 8/5/88 MSH One more bug fix for HcMac. If no device answers to a trip ; through SRQ polling, then try again. ; <1.6> 7/19/88 MSH Completely new ADBDispatch and may bug fixes for HcMac. ; <1.5> 7/6/88 MSH Label accidentally removed. ; <1.4> 7/6/88 MSH Gary Davidian found the BNE should be a BEQ to FDBNBusy. MISSING ; RTS in ShiftOut!!! Removed busy bit setting in ADBDispatch ; before calling FDBOp. ; <1.3> 5/20/88 MSH For HcMac, ShiftOut had the wrong starting address when copying ; to local buffer ; <1.2> 5/19/88 MSH ListenR3 cleared the data count to zero for HcMac. ; <1.1> 4/18/88 CSL Add support for sleep and idle in call completion routine ; <1.0> 2/10/88 BBM Adding file for the first time into EASE… ; POST MAC II MODIFICATION HISTORY: ; 11/5/87 CSL EMT Roll in patches for ADBReinit (PAB191&PAB192). ; 11/5/87 MSH Rewrote FDBShiftInt for Laguna, new interrupt interface to ; PowerMgr. ; 10/29/87 rwh Port to Modern Victorian ; 10/21/87 MSH Fixed the drba loop in FDBshiftInt for Laguna. ; 9/18/87 MSH Added support for HcMac (Laguna). State machine is gone due to ; easier power manager interface. Also cleaned up the text a bit. ; BLANKS ON STRING ASIS PRINT OFF LOAD 'StandardEqu.d' INCLUDE 'HardwarePrivateEqu.a' INCLUDE 'PowerPrivEqu.a' INCLUDE 'IopEqu.a' INCLUDE 'EgretEqu.a' INCLUDE 'AppleDeskBusPriv.a' INCLUDE 'ApplDeskBus.a' INCLUDE 'ScriptPriv.a' ; <03/05/89 pke> INCLUDE 'UniversalEqu.a' INCLUDE 'IOPrimitiveEqu.a' PRINT ON PRINT NOMDIR MACHINE MC68020 ADBManager PROC EXPORT EXPORT CountADBs,GetIndADBTrap,GetADBInfoTrap,SetADBInfoTrap EXPORT ADBReInit,ADBOpTrap,KeyTrans EXPORT InitADB,InitADBDrvr,ADBProc,RSetKMap EXPORT ExplicitRequestDone,ImplicitRequestDone,RunADBRequest IMPORT CrsrDevReInit jEgretDispatch equ OSTable+($92*4) ; OS trap table entry for _EgretDispatch Debugging equ 0 ; disable debugging checks eject Title 'KbdADB - ADB Manager - CountADBs / GetIndADB' ;______________________________________________________________________ ; ; Routine: CountADBs ; Arguments: None ; Output: D0.W Number of ADB entries ; Function: This routine counts the number of entries in the ADB ; table. ;______________________________________________________________________ with ADBVars,ADBDeviceEntry CountADBs MOVEQ #numFDBAdr, D1 ; Number of table entries MOVE.W D1, D0 ; Save it here, too MOVEA.L ADBBase, A1 ; Put Base in A1 addq.w #startDevT+FDBAddr,a1 ; point to the address field BRA.S FirstCount ; Skip past record increment CountADBLoop ADDA.W #FRecSize, A1 ; Get to next record FirstCount TST.B (A1) ; If 0, then previous entry was last DBEQ D1, CountADBLoop ; Loop until last is found SUB.W D1, D0 ; D1 is #numFDBAdr-(number of records) RTS endwith ;______________________________________________________________________ ; ; Routine: GetIndADB ; Arguments: DO.W Index from 1 to the value returned by CountADBs ; A0 Pointer to buffer in which DeviceType, OrigAddr, ; ServiceAddr, DataAddr are returned (10 bytes) ; Output: D0.L Unique ADBAddr based on index (-1 if input invalid) ; Function: This routine returns information from the ADB table ; Note: This routine shares code with GetADBInfo and FindADBInfo ;______________________________________________________________________ with ADBVars,ADBDeviceEntry GetIndADBTrap TST.W D0 ; Make sure D0 is within bounds BLE.S ADBNotFound ; <= 0? CMPI.W #numFDBAdr, D0 ; Or > Maximum number of records? BGT.S ADBNotFound ; Punt this if so MOVEA.L ADBBase, A1 ; Initialize A1 MULU #FRecSize, D0 ; Multiply index by record size LEA -FRecSize(A1, D0.L), A1 ; A1 points to record now. MOVEQ #0, D0 ; Clear out D0 MOVE.B FDBAddr(A1), D0 ; Return FDBAddr ; Load up the buffer pointed to by A0 from the record pointed to by A1. LoadBuf MOVE.B FDBDevTy(A1), (A0)+ ; Return DeviceType MOVE.B FDBOAddr(A1), (A0)+ ; Return OrigAddr MOVE.L FDBCRA(A1), (A0)+ ; Return ServiceAddr MOVE.L FDBOpData(A1), (A0) ; Return DataAddr RTS endwith Title 'KbdADB - ADB Manager - GetADBInfo / SetADBInfo / FindFDBInfo' ;______________________________________________________________________ ; ; Routine: GetADBInfo ; Arguments: DO.B ADBAddr ; A0 Pointer to buffer in which DeviceType, OrigAddr, ; ServiceAddr, DataAddr are returned (10 bytes) ; Output: D0.L 0 if found, -1 if not ; Function: This routine returns information from the ADB table ; Note: This routine shares code with GetIndADB ;______________________________________________________________________ GetADBInfoTrap BSR.S FindFDBInfo BEQ.S LoadBuf ; If found, go load up the buffer RTS ; Otherwise, go home ;______________________________________________________________________ ; ; Routine: SetADBInfo ; Arguments: DO.B ADBAddr ; A0 Pointer to buffer containing ServiceAddr and ; DataAddr (8 bytes) ; Output: D0.L 0 if found, -1 if not ; Function: This routine returns information from the ADB table ;______________________________________________________________________ with ADBDeviceEntry SetADBInfoTrap BSR.S FindFDBInfo BNE.S DoneSet ; If not found, go away MOVE.L (A0)+, FDBCRA(A1) ; Set ServiceAddr MOVE.L (A0), FDBOpData(A1) ; Set DataAddr DoneSet RTS endwith ;______________________________________________________________________ ; ; Routine: FindFDBInfo ; Arguments: DO.B FDBAddr ; Output: D0.L 0 if found, -1 if not ; A1 Real address of FDB record if found. ; Address of next available entry if not found. ; Function: This routine returns information from the FDB table ; Side Effects: (or lack thereof) Does not alter A0. ; Note: This routine shares code with GetIndADB ;______________________________________________________________________ with ADBVars,ADBDeviceEntry FindFDBInfo MOVEQ #numFDBAdr, D1 ; Number of table entries MOVEA.L ADBBase, A1 ; Put Base in A1 BRA.S FirstFind ; Skip past record increment FindFDBLoop ADDA.W #FRecSize, A1 ; Get to next record FirstFind CMP.B FDBAddr(A1), D0 ; Is this the right one? BEQ.S DoneFind ; If so, we're done TST.B FDBAddr(A1) ; Is it 0? DBEQ D1, FindFDBLoop ; Loop until it is found or no more. ADBNotFound ; If we got this far, it wasn't found. Return Error. MOVEQ #-1, D0 ; Indicate Error RTS DoneFind MOVEQ #0, D0 ; Indicate Success RTS endwith Title 'KbdADB - ADB Manager - ADBOp' ;_______________________________________________________________________ ; ; Routine: ADBOp ; Inputs: A0.L - pointer to ADBOpBlock paramater block ; D0.B - ADB command/address byte to send ; ; Outputs: D0 - Result Code (noErr, or -1 if queue full) ; ; Destroys: A0, A1, D0 ; Calls: RunADBRequest ; Called by: OsTrap Dispatch Table ; ; Function: Asynchronously issues an ADB bus transaction, using the command ; and buffer specified. When the transaction has completed, ; the completion routine will be called. ; ;_______________________________________________________________________ with ADBVars,ADBCmdQEntry ADBOpTrap ; a0-a1/d1-d2 saved by OsTrap dispatch @regs reg d3/a2/a3 ; additional registers to preserve movem.l @regs,-(sp) ; save registers movea.l ADBBase,a3 ; point to ADB private data structures move.w sr,-(sp) ; save interrupt mask ori.w #HiIntMask,sr ; disable ints while queueing movea.l fQEntPtr(a3),a1 ; pointer to next free element btst.b #fDBQEmpty,fDBFlag(a3) ; see if anything in queue bne.s @notFull ; if was empty, plenty of room to add cmpa.l fQHeadPtr(a3),a1 ; see if queue is full beq.s @queueFull ; if full, abort with error ; There is room in the queue, so insert the new request at the end of the queue @notFull ; a1 points to the ADBCmdQEntry move.b d0,(a1)+ ; fill in fQCmd addq.w #1,a1 ; skip over fQUnused move.l (a0)+,(a1)+ ; copy dataBuffPtr to fQBuff move.l (a0)+,(a1)+ ; copy opServiceRtPtr to fQComp move.l (a0)+,(a1)+ ; copy opDataAreaPtr to fQData cmpa.l fQEndPtr(a3),a1 ; see if queue pointer needs to wrap around blo.s @noWrap ; if didn't reach end, no wrap yet movea.l fQBegPtr(a3),a1 ; if end reached, wrap back to begining @noWrap move.l a1,fQEntPtr(a3) ; update the queue entry pointer ; The new entry is queued. If queue was previously empty run the new request. bclr.b #fDBQEmpty,fDBFlag(a3) ; indicate queue no longer empty beq.s @success ; if not at head of queue, just return success bsr.s RunADBRequest ; run the newly queued request @success moveq.l #noErr,d0 ; indicate success @done move.w (sp)+,sr ; restore int mask movem.l (sp)+,@regs ; restore registers rts ; all done @queueFull moveq.l #qErr,d0 ; IM vol 5 says return -1 for queue full bra.s @done ; restore regs and return endwith Title 'KbdADB - ADB Manager - RunADBRequest' ;_______________________________________________________________________ ; ; Routine: RunADBRequest ; Inputs: A3 - pointer to ADBBase ; ; Outputs: D2 - length of transmit buffer data ; D3 - command byte / implicit flag (bit 31) ; A2 - pointer to buffer containing transmit data ; A3 - pointer to ADBBase ; ; Destroys: ; Calls: exits through StartReqProc (hardware dependent) ; ; Function: Determines what command should be sent to ADB next, and calls ; the hardware dependent routine to process the next request. ; ;_______________________________________________________________________ with ADBVars,ADBCmdQEntry RunADBRequest btst.b #fDBQEmpty,fDBFlag(a3) ; see if any explicit commands to run beq.s @runFromQueue ; run an explicit command from the queue moveq.l #0,d2 ; zero the send byte count moveq.l #-1,d3 ; negative command to indicate resume polling btst.b #FDBInit,fDBFlag(a3); are we still initializing the bus beq.s @resumePolling ; if not, resume auto command polling rts ; still initializing, just return @runFromQueue movea.l fQHeadPtr(a3),a0 ; get pointer to element at head moveq.l #0,d3 ; zero extend command byte, indicate explicit cmd move.b fQCmd(a0),d3 ; D3 := command byte movea.l fQBuff(a0),a2 ; get the buffer address (pascal string) moveq.l #maskADBCmd,d2 ; mask talk/listen command bits and.b d3,d2 ; isolate bits from copy of command subq.b #ListenCmd,d2 ; see if it is a listen command seq.b d2 ; $00FF if listen, $0000 if not (only listen sends data) and.b (a2)+,d2 ; D2 := byte count, A2 := ptr to actual data @resumePolling movea.l StartReqProc(a3),a0 ; get HW dependent proc to start ADB request jmp (a0) ; start the ADB request endwith Title 'KbdADB - ADB Manager - ExplicitRequestDone' ;_______________________________________________________________________ ; ; Routine: ExplicitRequestDone ; Inputs: D2 - length of receive buffer data ; D3 - command byte / SRQ flag (bit 31) ; A2 - pointer to buffer containing receive data ; A3 - pointer to ADBBase ; ; Outputs: D2 - length of receive buffer data ; D3 - command byte / SRQ flag (bit 31) ; A0 - pointer to buffer to pass to completion routine ; A2 - pointer to buffer containing receive data ; A3 - pointer to ADBBase ; 0(SP) - completion routine address ; 4(SP) - optional data to pass in A2 to completion routine ; 8(SP) - stack cutback address, after completion routine runs ; ; Destroys: D0, D1, A0, A1 ; Calls: exits through RequestDone ; ; Function: Dequeues the paramaters to pass to the service routine. ; ;_______________________________________________________________________ with ADBVars,ADBCmdQEntry ExplicitRequestDone move.l sp,-(sp) ; allocate an empty buffer on the stack move.w sr,d1 ; save interrupt mask ori.w #HiIntMask,sr ; disable ints while dequeueing movea.l fQHeadPtr(a3),a0 ; get pointer to element to dequeue if Debugging then btst.b #fDBQEmpty,fDBFlag(a3) ; see if anything in queue beq.s @hasEntries ; if something to dequeue, we're ok _Debugger ; if not, we're dead @hasEntries cmp.b (a0),d3 ; check command sent against command received beq.s @cmdOK ; if match, we're ok _Debugger ; if not, we're dead @cmdOK endif adda.w #fQSize,a0 ; point past end of element movea.l a0,a1 ; save pointer to end of queue element cmpa.l fQEndPtr(a3),a0 ; see if queue pointer needs to wrap around blo.s @noWrap ; if didn't reach end, no wrap yet movea.l fQBegPtr(a3),a0 ; if end reached, wrap back to begining @noWrap move.l a0,fQHeadPtr(a3) ; update the queue head pointer cmpa.l fQEntPtr(a3),a0 ; see if queue is now empty bne.s @notEmpty ; if not, don't need to change empty flag bset.b #fDBQEmpty,fDBFlag(a3) ; queue is now empty, set flag to remember it @notEmpty move.l -(a1),-(sp) ; copy fQData to A2 save area on stack move.l -(a1),-(sp) ; copy fQComp to A1 save area on stack movea.l -(a1),a0 ; copy fQBuff to A0 move.w d1,sr ; restore interrupt mask bra.s RequestDone ; copy buffer data, resume ADB, call handler endwith Title 'KbdADB - ADB Manager - ImplicitRequestDone' ;_______________________________________________________________________ ; ; Routine: ImplicitRequestDone ; Inputs: D2 - length of receive buffer data ; D3 - command byte / SRQ flag (bit 31) ; A2 - pointer to buffer containing receive data ; A3 - pointer to ADBBase ; ; Outputs: D2 - length of receive buffer data ; D3 - command byte / SRQ flag (bit 31) ; A0 - pointer to buffer to pass to completion routine ; A2 - pointer to buffer containing receive data ; A3 - pointer to ADBBase ; 0(SP) - completion routine address ; 4(SP) - optional data to pass in A2 to completion routine ; 8(SP) - stack cutback address, after completion routine runs ; ; Destroys: D0, D1, A0, A1 ; Calls: exits through RequestDone ; ; Function: Locates the paramaters to pass to the service routine. ; ;_______________________________________________________________________ with ADBDeviceEntry ImplicitRequestDone tst.b d2 ; see if any data returned beq.s RunADBRequest ; if no data, ack the data, resume ADB operations move.b d3,d0 ; get command that completed lsr.b #4,d0 ; get the address from the command bsr FindFDBInfo ; get the info for this device bne.w RunADBRequest ; if unknown, ack the data, resume ADB operations suba.w #12,sp ; allocate a buffer (len byte, 8 data bytes, 3 slop) movea.l sp,a0 ; save pointer to buffer pea 12(sp) ; save stack restore address move.l fDBOpData(a1),-(sp) ; copy fDBOpData to A2 save area on stack move.l fDBCRA(a1),-(sp) ; copy fDBCRA to A1 save area on stack *Fall Into* bra.s RequestDone ; copy buffer data, resume ADB, call handler endwith Title 'KbdADB - ADB Manager - RequestDone' ;_______________________________________________________________________ ; ; Routine: RequestDone ; Inputs: D2 - length of receive buffer data ; D3 - command byte / SRQ flag (bit 31) ; A0 - pointer to buffer to pass to completion routine ; A2 - pointer to buffer containing receive data ; A3 - pointer to ADBBase ; 0(SP) - completion routine address ; 4(SP) - optional data to pass in A2 to completion routine ; 8(SP) - stack cutback address, after completion routine runs ; ; Outputs: none ; ; Destroys: A0-A3/D0-D3 ; Calls: device handler completion routine ; ; Function: Copies the receive data into the proper buffer, resumes ADB ; operations, and calls the device handler completion routine. ; ;_______________________________________________________________________ with PowerDispRec,ADBVars ; RequestDone move.l a0,-(sp) ; copy buffer address to A0 save area on stack beq.s @copyDone ; if no buffer, don't copy <1.8> move.b d2,(a0)+ ; copy length byte to form pascal string beq.s @copyDone ; if no data, don't copy subq.w #1,d2 ; adjust count for dbra loop @copyLoop move.b (a2)+,(a0)+ ; copy a byte at a time dbra d2,@copyLoop ; loop for all bytes @copyDone move.l d3,-(sp) ; copy command byte to D0 save area on stack bsr.w RunADBRequest ; acknowledge the data, resume ADB operations TestFor SupportsIdle BEQ.S @notsupported _IdleUpdate ; this is activity, stay at full speed @notsupported movem.l (sp)+,d0/a0/a1/a2 ; setup cmd, buffer, handler, data ; (13).start TestFor SupportsIdle beq.s @continue move.l d0,-(sp) ; save d0 temporarily on the stack ag move.l #((UsrActivity<<16)|\ ; set for user activity (IdleUpdateDisp<<0)),d0 ; idle update selector _PowerDispatch ; call power manager move.l (sp)+,d0 ; restore d0 ag @continue move.l a1,d1 ; test to see if handler address is valid beq.s @noHandler ; if not, don't call it BTST #fDBInit,FDBFlag(A3) ; is ADB initialization in progress? BNE.S @JustDoIt ; -> yes, calling the handler now is allowed ; jump thru the ProductInfo table to check if a keyswitch is in the secure position

MOVEA.L UnivInfoPtr,A1 ; point to the ProductInfo table

ADDA.L ProductInfo.ADBDebugUtilPtr(A1),A1 ; and get the address of its ADB table

MOVE.L 4*adbKeySwSecure(A1),D2 ; get the offset to the keyswitch code

BEQ.S @JustDoIt ; -> no keyswitch check, so just call the handler

MOVEM.L D0/D1/A0/A2,-(SP) ;

ADDA.L D2,A1 ; calculate the routine's address

JSR (A1) ; and call it

MOVEM.L (SP)+,D0/D1/A0/A2 ;

BEQ.S @noHandler ; -> the keyswitch is secure, so don't call the handler @JustDoIt MOVEA.L D1,A1 ; get the handler's address

jsr (a1) ; call the handler @noHandler movea.l (sp),sp ; deallocate the buffer (if implicit cmd) rts ; return from the interrupt endwith ; (13).end Title 'KbdADB - ADB Manager - Initialization' ;______________________________________________________________________ ; ; ADBReInit - ReInitialize the Front Desk Bus ; ;______________________________________________________________________ ADBReinit MOVE.L JADBProc,A0 ; get hook to processing routine moveq.l #0,D0 ; set it as pre-processing routine JSR (A0) ; call the routine MoveM.L D0-D4/A0-A3,-(SP) Move.L ADBBase,A3 ; A3 get local data address BSR ReInit BSR InitADBDrvr MoveM.L (SP)+,D0-D4/A0-A3 ; restore registers Move.L JADBProc,A0 ; get hook to processing routine MoveQ #1,D0 ; set it as post-processing routine JSR (A0) ; call the routine RTS ; done ;______________________________________________________________________ ; Stack frame equates for InitADBDrvr and ADBProc iADBAddr EQU -2 iDataAddr EQU iADBAddr-4 iCRAddr EQU iDataAddr-4 iOrigAddr EQU iCRAddr-1 iDeviceTy EQU iOrigAddr-1 iPBlock EQU iDeviceTy iSPBlock EQU iCRAddr iLocalData EQU iDeviceTy ;______________________________________________________________________ ; ; ADBProc - this routine lives in the JADBProc vector and is called ; by ADBReInit before and after initialization ; ;______________________________________________________________________ ADBProc ; D0 is 0 if pre-processing, non-0 if post-processing. ; In addition to the standard pascal calling conventions. TST.L D0 ; 0 = pre-processing BNE.S PostInit ; Skip if not ; PreInit is called just before the bus is initialized. It de-allocates the memory used by the driver. @PreInit BSR.L CrsrDevReInit ; Remove cursor devices
_CountADBs ; Get the number of ADB devices MOVE.W D0, D2 ; Save it in D2 BEQ.S @DoneKRemove ; Skip if none LINK A6, #iLocalData ; Make a stack frame MOVEQ #kbdAddr, D1 ; We are looking for keyboards @KRemoveLoop LEA iPBlock(A6), A0 ; A0 is parameter block MOVE.W D2, D0 _GetIndADB ; Get a device entry BMI.S @NextRec ; Skip if not valid CMP.B iOrigAddr(A6), D1 ; Keyboard? BNE.S @NextRec ; Skip if not MOVE.L iDataAddr(A6), A1 ; Save the data address pointer CLR.L iDataAddr(A6) ; Clear it out, so the keyboard won't trash memory LEA iSPBlock(A6), A0 _SetADBInfo ; D0 already contains the ADB Address MOVE.L A1, A0 ; Get the data address pointer _DisposPtr ; Throw away the block @NextRec SUBQ.W #1, D2 BGT.S @KRemoveLoop UNLK A6 @DoneKRemove RTS ;______________________________________________________________________ ; ADBReInit causes ADB keyboards to be reset to use KCHR 0. It can be called ; after booting - for example, on the Portable at wakeup if devices are ; connected to the external ADB port. This causes problems for international ; systems, which are normally using other KCHRs. On the non-Roman systems, users ; may also frequently switch back and forth between various KCHRs. Fortunately, ; a pointer to the current KCHR is kept in ExpandMem. This patch (actually, an ; ADBProc) gets the current KCHR pointer from ExpandMem and stuffs it into the ; keyboard driver data structures for all connected keyboards. rb ; Do this only as a post-procedure. Verified offsets with equates in ScriptPriv.a rb ;______________________________________________________________________ PostInit ; rb WITH ExpandMemRec,KybdDriverData ; rb _CountADBs ; Get the number of ADB devices MOVE.W D0, D2 ; Save it in D2 BEQ.S @DoneKRemove ; Skip if none LINK A6, #iLocalData ; Make a stack frame MOVEQ #kbdAddr, D1 ; We are looking for keyboards MOVE.L D4,-(SP) ; save a working register rb MOVE.L ExpandMem,A0 ; get the Expanded memory in a0 rb MOVE.L emKeyCache(A0),D4 ; save it in D4 rb @KRemoveLoop LEA iPBlock(A6), A0 ; A0 is parameter block MOVE.W D2, D0 _GetIndADB ; Get a device entry BMI.S @NextRec ; Skip if not valid CMP.B iOrigAddr(A6), D1 ; Keyboard? BNE.S @NextRec ; Skip if not MOVE.L iDataAddr(A6), A1 ; Save the data address pointer CLR.L KybdDriverData.deadKey(A1) ; clear the dead key state rb MOVE.L D4,KybdDriverData.KCHRPtr(A1) ; set KCHR pointer rb @NextRec SUBQ.W #1, D2 BGT.S @KRemoveLoop MOVE.L (SP)+,D4 ; restore the working register rb UNLK A6 @DoneKRemove RTS ENDWITH ; rb ;______________________________________________________________________ ; ; InitADBDrvr - this routine bring in all appropriate 'ADBS' resources and ; execute the initialization routines. ; ;______________________________________________________________________ InitADBDrvr _CountADBs ; get the number of valid ADB entries Move D0,D3 ; save it in D3 BEQ.S DoneSrv ; If none, nothing to do MoveQ #1,D4 ; start at first entry Link A6,#iLocalData ; reserve stack frame FSrvLoop Lea iPBlock(A6),A0 ; A0 points param block Move D4,D0 ; index goes in D0 _GetIndADB ; get a record Move.B D0,iADBAddr(A6) ; save the ADB Address BMI.S NextRec ; skip if it's not valid SubQ.L #4,SP ; make room from result Move.L #'ADBS',-(SP) ; ResType = ADBS Clr.W -(SP) ; clear it out since OrigAddr is byte Move.B iOrigAddr(A6),1(SP) ; Move OrigAddr on the stack _GetResource Move.L (SP),D1 ; get the handle BNE.S @1 ; branch, if good handle AddQ #4,SP ; clear off the handle Bra.S NextRec ; go to next record @1 _DetachResource ; detach it Move.L D1,A0 ; put handle in A0 Move.L (A0),D0 ; dereference handle _StripAddress ; make it a 24-bit address Move.L D0,A0 ; put it in A0 Move.B iADBAddr(A6),D0 ; put ADB Address in D0 Move.B iDeviceTy(A6),D1 ; put device type in D1 JSR (A0) ; execute the service routine NextRec AddQ #1,D4 ; increment the index Cmp.W D4,D3 ; are we done yet? BGE.S FSrvLoop ; if not, go around UNLK A6 DoneSrv RTS ; done ;_______________________________________________________________________ ; ; InitADB - Initialize state variables ; ;_______________________________________________________________________ with ADBVars,ADBDeviceEntry IMPORT KeyHook ; rb InitADB MOVE.L #FDBDSize,D0 ; get local data area length _NewPtr ,SYS,CLEAR ; allocate space on heap and clear movea.l a0,a3 ; ADBBase is always in A3 by convention MOVE.L A3,ADBBase ; save ptr to it Lea ADBProc,A0 ; Get the ADBProc Move.L A0,JADBProc ; install it into JAdbProc vector CLR.B KbdType ; assume we have no keyboard to start BSR RSetKMap ; reset the keyboard map, etc. LEA FDBTask,A0 ; setup the FDB VBL task MOVE.L A0,JKybdTask ; lomem vector ; jump thru the ProductInfo table to call the hardware-dependent initialization code MOVEA.L UnivInfoPtr,A0 ; point to the ProductInfo table

ADDA.L ProductInfo.ADBDebugUtilPtr(A0),A0 ; and get the address of its ADB table

MOVE.L 4*adbInitProc(A0),D0; get the offset to the InitADB entry code

BEQ.S ReInit ; -> this function is not supported

ADDA.L D0,A0 ; calculate the routine's address

JSR (A0) ; and call it

ReInit ori.w #HiIntMask,sr ; mask out interrupts MOVEQ #((endCQ/4)-1),D0 ; n entries Move.L A3,A0 ; clear out device table and command queue @ClrLoop Clr.L (A0)+ DBRA D0,@ClrLoop ; clear next entry move.b #(1<

BSR KbdInstall ; finally, install keyboard information

pea KeyHook ; get Key Hook entry point. rb move.l (sp),Key1Trans ; install the Key1Trans hook. rb move.l (sp)+,Key2Trans ; install the Key2Trans hook. rb BRA flushkbds ; after all is setup, flush to get current keys
;_______________________________________________________________________ ; ; InitDevT - Initialize the Device Table ; ; NOTE: everything after BusReset below is part of an ADB completion ; routine, and thus is run at interrupt level 1. No calls ; that could cause memory to move (i.e., GetResource, Andy). ;_______________________________________________________________________ InitDevT bsr BusReset ; reset all devices on the bus clr.b DevTOffset(a3) ; initialize the table offset clr.w HasDev(a3) ; initialize the device map moveq.l #0,d0 ; start with address zero @PollNext move.b d0,InitAddr(a3) ; save device address bsr TalkR3 ; issue a Talk R3 command (asynchronously) move.b InitAddr(a3),d0 ; restore poll address tst.b (a0)+ ; test reply length, see if device returned data beq.s @NoDevice ; no, nothing to install ; there is a response from the device in the address, so update the ; device table according to the device moveq.l #0,d1 ; zero extend for indexing move.b DevTOffset(a3),d1 ; get offset to devicetable move.b 1(a0),FDBDevTy(a3,d1.w) ; copy device handler ID into table move.b d0,FDBOAddr(a3,d1.w); save device address move.b d0,FDBAddr(a3,d1.w) ; save device address cmpi.b #KbdAddr,d0 ; is it a keyboard type device? bne.s @notKbd ; no, branch lea KbdDrvr,a0 ; get address of keyboard driver move.l a0,FDBCRA(a3,d1.w) ; save as completion routine address @notKbd ; ...used to setup mouse driver here, but that's now done in CrsrDev.a... addi.b #FRecSize,d1 ; advance offset move.b d1,DevTOffset(a3) ; save device table offset move.w HasDev(a3),d2 ; get value in HasDev bset.l d0,d2 ; remember which address has device move.w d2,HasDev(A3) ; save it @NoDevice addq.b #1,d0 ; advance device address cmpi.b #NumFDBAdr,d0 ; has it polled all addresses yet? bne.s @PollNext ; no, go to poll next device ; ChgAddr - check the device address to identify multiple devices on ; the same address move.b #MoveTime+1,FDBMvCnt(A3); initialize move retry count move.w HasDev(a3),DevMap(a3) ; initialize device map Bne.S movLoop ; branch, if there is no device ChgExit ; ; Initialization done, now setup default device and keyboard before starting auto polling ; BClr #FDBInit,FDBFlag(A3); done with initialization JSR RunADBRequest ; start auto poll for the mouse RTS movLoop ST InitAddr(A3) ; clear poll address subq.b #1,FDBMvCnt(A3) ; has it loop 50 times yet? Beq.S ChgExit ; yes, exit ; ChgNext is another entry point for this routine ChgNext AddQ.B #1,InitAddr(A3) ; advance poll address BSR GNextAddr ; get next address to change Bmi.S MovLoop ; exit when end of address range BSR GEmptyAddr ; get empty address space, D0 gets address BMI.S ChgExit ; no more empty address space, exit ; D0 has an address that can be moved to Move.B D0,NewAddr(A3) ; save address in NewAddr Move.B D0,D1 ; D1 get new address to change to Move.B InitAddr(A3),D0 ; D0 get address to issue command BSR ListenR3 ; issue a Listen R3 command ; MovAddr - a Listen R3 command has just been issued, the device is moved to ; a new address. Now issue a Talk R3 to the old address. A timeout would ; indicate no more device in the old address, we will move the device back ; to the old address by issuing a Listen R3. Move.B InitAddr(A3),D0 ; get address BSR.s TalkR3 ; issue a Talk R3 command <1.6> ; MovBack - A Talk R3 has just been issued, a timeout in S1 indicates no ; more device in original address, we want to move the device back to ; original address. tst.b (a0) ; did the device return data beq.S @1 ; no, branch ; no timeout indication, BSR.S CopyEntry ; copy entry into device table Move.B FDBByte1(A3),FDBDevTy(A3,D1.W) ; get new handle ID into table BRA.S ChgNext ; go to change next device ; there is timeout indication @1 Move.B InitAddr(A3),D1 ; get address to change back to Move.B NewAddr(A3),D0 ; get address to talk to bsr.s ListenR3 ; send a listen R3 command <1.6> ; CKNewAdr - check the new address by issuing a Talk R3, to see if ; there is still any device left. If yes, add entry into device ; table, but if not, just go to change next device address Move.B NewAddr(A3),D0 ; get address BSR.S TalkR3 ; issue a talk R3 <1.6> ; AddEntry - a Talk R3 command has just been issed to the new address, ; if there is no timeout in S1, one or more device is still in that ; address, so, add device to device table. If there is timeout, no ; device is in that address, so, just go to change next device address tst.b (a0) ; did the device return data Beq.S ExitEntry ; no, branch ; no timeout indication, thus, add entry of the new address into the ; device table. BSR.S CopyEntry ; copy entry into device table Move.B FDBByte1(A3),FDBDevTy-FRecSize(A3,D2.W) ; get new handle ID into table ExitEntry bra.s ChgNext ; go to change next device ;_______________________________________________________________________ ; ; CopyEntry - copy the device entry from the original address to the ; new address, a Talk R3 had just been issued ; ; Called by: MoveBack and AddEntry ; on exit: D1 - has record address of old device ; D2 - points to new table entry ;_______________________________________________________________________ CopyEntry MoveQ #0,D0 MoveQ #0,D1 Move.B NewAddr(A3),D0 ; get new address Move.W DevMap(A3),D1 ; get device map BSet D0,D1 ; set device address Move.W D1,DevMap(A3) ; update device map MoveQ #0,D1 ; set D1 as offset to table Move.B InitAddr(A3),D0 ; D0 get address @1 CMP.B FDBADDR(A3,D1.W),D0 ; same address? BEQ.S @2 ; yes, branch Add #FRecSize,D1 ; advance BRA.S @1 @2 MoveQ #0,D2 Move.B DevToffset(A3),D2 ; set D2 to new entry offset Move.L FDBDevTy(A3,D1.W),\ ; get first 4 byte FDBDevTy(A3,D2.W) ; save it Move.L FDBCRA(A3,D1.W),\ ; get completion routine address FDBCRA(A3,D2.W) ; save it Move.B NewAddr(A3),\ ; get new address FDBAddr(A3,D2.W) ; update address Add #FRecSize,D2 ; advance device table offset Move.B D2,DevToffset(A3) ; save it RTS ;_______________________________________________________________________ ; ; BusReset - issue a Reset command ; ; On entry, (SP) has completion routine address <1.6> ; ;_______________________________________________________________________ BusReset moveq.l #0,d0 ; address zero moveq.l #resetCmd,d1 ; reset command moveq.l #0,d2 ; no data to send bra.s MakeAsyncRequest ; start the command asynchronously ;_______________________________________________________________________ ; ; Talk R3 - issue a Talk R3 command ; ; On entry, D0 has device address ; (SP) has completion routine address <1.6> ; ;_______________________________________________________________________ TalkR3 moveq.l #talkCmd+3,d1 ; talk command, register 3 moveq.l #0,d2 ; no data to send bra.s MakeAsyncRequest ; start the command asynchronously ;_______________________________________________________________________ ; ; ListenR3 - issue a listen R3 command ; ; On entry, D0 has device address to send the command ; D1 has new device address to change to ; (SP) has completion routine address <1.6> ; ;_______________________________________________________________________ ListenR3 move.b d1,FDBByte0(a3) ; set up new address for R3 move.b #$FE,FDBByte1(a3) ; setup handle ID moveq.l #listenCmd+3,d1 ; listen command, register 3 moveq.l #2,d2 ; 2 bytes of data to send MakeAsyncRequest lsl.b #4,d0 ; shift address by 4 bits to correct position or.b d1,d0 ; insert the command and register number move.b d2,FDBCnt(a3) ; setup the send byte count pea FDBCnt(a3) ; push the buffer address movea.l sp,a0 ; setup param block pointer movea.l jADBop,a1 ; get address of _ADBop jsr (a1) ; start the request addq.w #8,sp ; pop buffer addr and return address rts ; return to callers caller ;_______________________________________________________________________ ; ; GNextAddr - get next address to change ; ;_______________________________________________________________________ GNextAddr MoveQ #0,D0 MoveQ #0,D1 Move.B InitAddr(A3),D0 ; get address Move.W DevMap(A3),D1 ; get device map @1 BTst D0,D1 ; is there a device there? BNE.S @2 ; branch, if there is device AddQ #1,D0 ; advance to next address CMP #numFDBAdr,D0 ; end of address yet? BLT.S @1 ; no, branch MoveQ #-1,D0 ; return -1 if no more address RTS @2 Move.B D0,InitAddr(A3) ; remember the address RTS ;_______________________________________________________________________ ; ; GEmptyAddr - get empty address space ; ; on return: ; D0 = empty address or ; -1 for no empty address ; ;_______________________________________________________________________ GEmptyAddr MoveQ #0,D1 MoveQ #numFDBAdr-1,D0 ; start from last address Move.W DevMap(A3),D1 ; get device map @1 BTst D0,D1 ; is there a device there? DBEQ D0,@1 ; no, branch tst.w d0 RTS ; return, D0 has empty address space ;_______________________________________________________________________ ; ; FDBTask - FDB VBL Task ; ;_______________________________________________________________________ FDBTask RTS ; just return for now endwith ; since we reset the keyboard, we better reset the map RSetKMap LEA KeyMap,A0 MOVEQ #8,D0 @1 CLR.W (A0)+ ; zero key map, key pad map, and keylast DBRA D0,@1 CLR.W HiKeyLast KbdDRTS RTS ;______________________________________________________________________ ; ; DefaultDev - check mouse and keyboard in the device table, if they ; are not there, set them up as default device anyway. ; ;______________________________________________________________________ with ADBVars,ADBDeviceEntry,ADBOpBlock,ADBDataBlock DefaultDev MoveQ #kbdAddr,D0 ; first check keyboard Bsr.w FindFDBInfo ; look for keyboard TST.B D0 ; is it there? BEQ.S ChkMouse ; branch, keyboard is there Move.B #1,FDBDevTy(A1) ; assume handleID 1 Move.B #kbdAddr,FDBOAddr(A1) ; set original address as 2 Move.B #kbdAddr,FDBAddr(A1) ; set FDB address as 2 Lea KbdDrvr,A0 ; get keyboard address Move.L A0,FDBCRA(A1) ; set completion routine address bset.b #kbdAddr,DevMap+1(a3) ; remember to poll it <1.9> ChkMouse moveq #1,d0 ; post processing
bsr.l CrsrDevReInit ; allocate a crsrDevRec for each relative pointing device
rts ;______________________________________________________________________ ; ; Routine: flushkbds ; Arguments: none ; Output: none ; Function: this routine finds all kbd devices, and sends a flush command to them ; Note: ;______________________________________________________________________ flushkbds ; Flush all keyboards
waitForKeys equ (4+4) ; add 100% margin @saved reg d0/d1/a0 movem.l @saved,-(sp) sub.l #dbBlkSize,sp ; make room for table info moveq.l #(1-1),d1 ; first device index -1 @next addq.l #1,d1 ; next indexed device move.l sp,a0 ; a0 pointer to pb move.b d1,d0 ; get index number _GetIndADB ; get indexed info tst.b d0 ; check for valid id ble.s @done ; @type cmp.b #kbdAddr,origADBAddr(a0); is this a keyboard bne.s @next ; not a keyboard, next device bsr.s sendFlush ; send a flush command to kbd bra.s @next ; next device @done movea.l #waitForKeys,a0 ; wait for keys _Delay add.l #dbBlkSize,sp ; release trap pb movem.l (sp)+,@saved ; restore registers rts ;______________________________________________________________________ ; ; Routine: sendFlush ; Arguments: d0.b adb id ; Output: none ; Function: this routine sends a "flush" command to the adb device at d0.b ; Note: ;______________________________________________________________________ sendFlush ; send flush command to device
flushcmd equ 1 ; flush command @savedReg reg d0/d1/a0 movem.l @savedReg,-(sp) ; save registers move.b d0,d1 ; save address in d1 sub.l #(opBlkSize+2),sp ; create space (pb + completion word) @retry move.b d1,d0 ; load address into d0 asl.b #4,d0 ; move the address to the upper nibble ori.b #flushcmd,d0 ; complete the flush command ; load adbop pb clr.l dataBuffPtr(sp) ; no adb data to be sent lea CompleteFlush,a0 ; point 'opServiceRtPtr' to completion routine move.l a0,opServiceRtPtr(sp) lea opBlkSize(sp),a0 ; point 'opDataAreaPtr' to completion word move.l a0,opDataAreaPtr(sp) clr.w opBlkSize(sp) ; clear completion word move.l sp,a0 ; point a0 at pb _ADBop ; send flush command tst.b d0 ; did it work ?? bmi.s @retry ; if not retry movea.l opDataAreaPtr(sp),a0 ; point a0 to the completion word @waitdone tst.w (a0) ; wait for completion beq.s @waitdone add.l #(opBlkSize+2),sp ; release pb + completion word movem.l (sp)+,@savedReg ; restore registers rts CompleteFlush move.w #1,(a2) ; set complete in result area rts Title 'KbdADB - ADB KeyBoard Driver' ;______________________________________________________________________ ; ; Routine: KbdDrvr ; Arguments: D0.B ADB Command ; A0 ADB Buffer address ; A1 ADB Completion Routine Address (= KbdServ) ; A2 ADB Data Address ; Output: None ; Function: Reads buffer and posts keyboard events as appropriate. ; Side Effects: Trashes A0, A1, D0, D1, D2, D3 ; ; Modification History: ; 26 Jun 86 EMT Created ; 15 Jul 86 EMT Updated to use KCHR resource ; 21 Jul 86 EMT Complete rewrite - to use new _KeyTrans ; 3 Oct 86 EMT Added LED bells & whistles ; EMT D1 is no longer a parameter to this routine. Must get ADB Address from D0 ;______________________________________________________________________ ; 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) ; KMAP offsets KMid EQU $00 KMtype EQU $01 KMvers EQU KMid+2 KMstart EQU KMvers+2 KMnumEx EQU KMstart+128 KMstEx EQU KMnumEx+2 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 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. Hammer MOVEM.L KeyBits(A2), D0-D2/A0 MOVEM.L D0-D2/A0, KeyMap ; Hammer in the correct 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 ; Output: None ; Function Posts the keyboard event as appropriate. ; Side Effects: Trashes A0, D0, D1 ; Called From: KeyIn twice, (1 BSR, 1 fall-through) ; ; Modification History: ; 25 Jun 86 EMT Created ; 22 Jul 86 EMT Changed order of event data (FHRL -> HFRL) ; EMT Clear the up/down bit in the event message ;______________________________________________________________________ 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 ; And leave ; End KbdDrvr ;______________________________________________________________________ ; ; FoundEx ; An exception exists for this particular keystroke. Process it appropriately. ;______________________________________________________________________ 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 ; Punt ; End FoundEx ;______________________________________________________________________ ; ; Routine: KbdListen ; Arguments: D0.B ADB Command ; D1.L DeviceType, OrigAddr, ADBAddr, Unused (byte order) ; A0 ADB Buffer Address ; A1 ADB Completion Routine Address (= KbdListen) ; A2 ADB Data Address ; 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 ; ; Modification history: ; 3 Oct 86 EMT Created ;______________________________________________________________________ KbdListen MOVE.L A0, A1 ; Copy A0 into A1 so as to avoid trashing A2 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 ADB Buffer Address ; A1 ADB Completion Routine Address (= KbdListen) ; A2 ADB Data Address ; Output: None ; Function: Marks the buffer pointed to by A0 as free. ; Side Effects: None ; ; Modification history: ; 3 Oct 86 EMT Created ;______________________________________________________________________ KbdBufFree CLR.B -1(A0) RTS ; End KbdBufFree ;______________________________________________________________________ ; ; KbdInstall - allocate memory for keyboard information and put in ADB record, ; loading resources as necessary. ; ;______________________________________________________________________ with ADBDeviceEntry KbdInstall MOVEQ #numFDBAdr, D1 ; Number of table entries MOVE.L ADBBase, A1 ; Put Base in A1 BRA.S FirstInstall ; Skip past record increment InstallLoop ADD #FRecSize, A1 ; Get to next record FirstInstall MOVEQ #kbdAddr, D0 ; We're looking for keyboards CMP.B FDBOAddr(A1), D0 ; Is this one? BNE.S NotKbd ; Nope, skip around MOVEQ #KbdDSize, D0 ; Amount of space needed for keyboard data _NewPtr ,SYS,CLEAR ; get a pointer MOVE.L A0, A2 ; Save it in A2 MOVE.B #KBufCount, KNumBufs(A2) SUBQ.L #4, SP ; Make room for result MOVE.L #'KCHR', -(SP) ; ResType = KCHR CLR.W -(SP) ; theID = 0 MOVE.W #mapTrue, RomMapInsert ; Load it from ROM _GetResource MOVE.L (SP)+, D0 ; Get the handle BEQ.S NotKbd ; Skip if NIL MOVE.L D0, A0 MOVE.L (A0), KCHRPtr(A2) ; Dereference and put away SUBQ.L #4, SP ; Make room for result MOVE.L #'KMAP', -(SP) ; ResType = KCHR CLR.W -(SP) ; theID = 0 MOVE.W #mapTrue, RomMapInsert ; Load it from ROM _GetResource MOVE.L (SP)+, D0 ; Get the handle BEQ.S NotKbd ; Skip if NIL MOVE.L D0, A0 MOVE.L (A0), KMAPPtr(A2) ; Dereference and put away MOVE.L A2, FDBOpData(A1) ; Save pointer in ADB record MOVE.B FDBDevTy(A1), KbdType ; Save the keyboard type MOVE.B FDBAddr(A1), KbdLast ; Save the ADB address NotKbd DBRA D1, InstallLoop ; Loop until no more RTS endwith ; End KbdInstall Title 'KbdADB - KeyTrans' ;______________________________________________________________________ ; ; NuMac Keyboard Mapping Proc ; ; FUNCTION KeyTrans(transData: Ptr; keycode: Integer; VAR state: LONGINT): LONGINT; ; ; Translates a keycode to ASCII. ; transData is a pointer to a KCHR resource for the use of KeyTrans. ; keycode is an integer which contains in bits: ; 0-6 "virtual" keycode ; 7 1 = key up ; 8-15 modifier flags similar to event manager. ; state is an internal code used by KeyTrans, which should be 0 on first call. ; The LONGINT returned is actually two words which are to be posted as events. ; $0000 should not be posted; if both words contain values, the high word ; should be posted first. ; ;______________________________________________________________________ ;------------------------------------------------------------------------------ ; The first part of KeyTrans is a patch rolled in from KeyHack.a <03/05/89 pke> ; ; Introduction: ; This is a special hack to make the various international keyboard ; layouts work on all of the keyboard models. We intercept calls to ; the KeyTrans trap and perform some special mapping of the virtual key ; codes, based on an optional resource that can accompany each KCHR. ; ; If we currently have an international keyboard re-mapping table (itlk resource, ; only present on some international systems), perform any re-mapping and then ; enter the original KeyTrans code. The re-mapping table consists of an integer ; count followed by a set of items with the following format: ; ; | old | old | old | care | care | new | new | ; | keyboard | modifiers | key | modifiers | code | modifiers | key | ; | type | | code | | | | code | ; ; This code compares the current keyboard type, key code, and modifiers against ; each entry. If there is a match, it substitutes the new modifiers and key ; code before calling the original KeyTrans routine. Note that the current ; modifiers and key code are masked with the care bits in the entry before the ; comparison is made. When a comparison succeeds, the new modifiers and key ; code are masked with the care bits and the current modifiers and key code ; are masked with the complement of the care bits. The logical or of these ; two products is the final result. This allows for a more compact table when ; several characters on one key are mapped together to a different key. ; ; The keyboard types are: ; $0001 Standard ADB (and Apple II GS) ; $0002 Extended ADB ; $0004 ISO Standard ADB ; $0005? Zoots (European ergonomic) ; $0006? Esprit std ; $0007? Esprit ISO ; $000b Macintosh Plus ; ; The modifier bits are: ; 7 -> (Right Control) ; 6 -> (Right Option) ; 5 -> (Right Shift) ; 4 -> Control(Left Control) ; 3 -> Option (Left Option) ; 2 -> Caps Lock ; 1 -> Shift (Left Shift) ; 0 -> Command ; ; register usage: ; d5 -> loop counter ; d4 -> current keyboard type, key code, and modifiers ; d3 -> masked current keyboard type, key code, and modifiers ; d2 -> new key code and modifiers for re-mapping ; d1 -> care masks for key code and modifiers ; d0 -> old key code and modifiers for comparison ;------------------------------------------------------------------------------ vers EQU $00 tInx EQU vers+2 tCnt EQU tInx+$100 tBgn EQU tCnt+2 ktFrame record {a6link},decr result ds.l 1 ; resulting ascii codes kchrTable ds.l 1 ; KCHR table to use codeMods ds.w 1 ; virtual key code, modifiers deadState ds.l 1 ; dead key state pointer return ds.l 1 ; return address a6link ds.l 1 ; old link pointer ktLocals equ * ; size of locals kchrTableNoLink equ kchrTable-return ; kchrTable offset before link rb endr KeyTrans ;------------------------------------------------------------------------------ with ktFrame,SMgrRecord ; if SMgr not initialized or no itlk, skip GetSMgrCore a0 ; get SMgrRecord pointer cmpa.l #-1,a0 ; SMgr initialized? beq.s @noTable ; no -> skip move.l smgrCurITLK(a0),d0 ; have an itlk currently? beq.s @noTable ; no -> skip ; bail if KCHR pointer is not same as in ExpandMem rb with ExpandMemRec ; rb move.l ExpandMem,a1 ; rb move.l emKeyCache(a1),d1 ; Get KCHR pointer in ExpandMem rb cmp.l kchrTableNoLink(sp),d1 ; same as KCHR pointer param? rb bne.s @noTable ; if not, skip itlk handling rb endwith ;ExpandMemRec ; rb link a6,#ktLocals ; build stack frame movem.l d3-d5,-(sp) ; save the registers move.l d0,a0 ; copy table handle move.l (a0),a0 ; load table pointer ; find the keyboard type and build the comparison key (type, code, modifiers) clr.l d4 ; clear a long move.b KbdType,d4 ; load type ; removed support for Classic kbd! <03/05/89 pke> swap d4 ; put type in high word move.w codeMods(a6),d4 ; load code,mods ; step through the itlk table, looking for a match move.w (a0)+,d5 ; load table length bra.s @stepLoop ; enter loop at bottom @beginLoop move.l (a0)+,d0 ; load old type,code,mods move.w (a0)+,d1 ; load don't care masks move.w (a0)+,d2 ; load new code,mods move.l d4,d3 ; copy current type,code,mods and.w d1,d3 ; mask off don't care bits cmp.l d0,d3 ; same type,code,mods? bne.s @stepLoop ; no -> try next one and.w d1,d2 ; mask off don't care bits not.w d1 ; invert care mask and.w d1,d4 ; mask off care bits or.w d4,d2 ; combine for final code,mods move.w d2,codeMods(a6) ; remap code,mods bra.s @endLoop ; exit search loop @stepLoop dbra d5,@beginLoop ; try next entry @endLoop ; remove the stack frame and continue into the original KeyTrans code movem.l (sp)+,d3-d5 ; restore the registers unlk a6 ; remove stack frame @noTable endWith ;------------------------------------------------------------------------------ MOVE.L (SP)+, D0 ; Return address in D0 MOVE.L (SP)+, A0 ; state address in A0 MOVE.W (SP)+, D1 ; keycode in D1 MOVE.L (SP)+, A1 ; KCHR address in A1 MOVE.L D0, -(SP) ; Put the return address back. MOVEM.L A2/D3, -(SP) ; Save registers ; This snippet of code added on: MOVE.W D1, D2 LSR.W #8, D2 ; Shift modifier flags down MOVE.B tInx(A1, D2.W), D2 ; Table number in D2 MOVEQ #$7F, D0 AND.B D1, D0 ; D0 contains virtual keycode only EXT.W D1 EXT.L D1 ; Move up/down bit to bit 31 MOVE.B D2, D1 ; Put the table number in D1 LSL.W #8, D1 ; Shift it up one byte MOVE.B D0, D1 ; Put in the virtual keycode ; D1 contains all the necessary input information: ; Bit 31 contains the up/down bit (BMI for up stroke) ; Bits 15-8 contain the table number (also in low byte of D2) ; Bit 7 is 0 ; Bits 6-0 contain the "virtual" keycode LSL.W #7, D2 ; Multiply by 128 OR.B D1, D2 ; Stuff in keycode ADD #tBgn, A1 ; A1 is now start of tables MOVE.B (A1, D2.W), D0 ; ASCII byte in D0 BEQ.S @mightBeDead ; Go find out if it is really a dead key ; End snippet TST.L D1 ; Key up or down? BMI.S @upKey ; Skip if up MOVE.L (A0), D3 ; Previous key dead? BEQ.S @exitTrans ; Done if not CLR.L (A0) ; Clear out dead key state LEA (A1, D3.L), A2 ; A2 points to completor table MOVE.W (A2)+, D2 ; Number of completors in D2 BEQ.S @noComp ; If none, skip SUBQ.W #1, D2 ; Turn it into a zero-based count @compLoop CMP.B (A2)+, D0 ; Do we match? BEQ.S @gotComp ; Jump out if we do ADDQ.L #1, A2 ; Get to next completor DBRA D2, @compLoop ; Go around again @noComp SWAP D0 MOVE.W (A2), D0 ; Store the default code SWAP D0 BRA.S @exitTrans @gotComp MOVE.B (A2), D0 ; Get the completor BRA.S @exitTrans @mightBeDead MOVE.W -2(A1), D3 ; Number of tables in D3 LSL.W #7, D3 ; 128 bytes per table LEA (A1, D3.W), A2 ; Start of dead key table in A2 MOVE.W (A2)+, D2 ; Number of dead keys in D2 BEQ.S @notDead ; There are no dead keys SUBQ.W #1, D2 ; Turn it into a zero-based count @deadLoop CMP.W (A2)+, D1 ; Do we match? BEQ.S @gotDead ; Jump out if we do MOVE.W (A2)+, D3 ; Get number of completors LSL.W #1, D3 ; Multiply by 2 LEA 2(A2, D3.W), A2 ; A2 now points to next entry DBRA D2, @deadLoop ; Go around again @notDead ; D0 already contains $00000000 @upKey ; D0 already contains correct ASCII @exitTrans MOVEM.L (SP)+, A2/D3 ; Restore registers MOVE.L D0, 4(SP) ; Put return value on stack RTS ; Sayonara @gotDead ; The key is a dead key. A2 points to the beginning of the completor table. TST.L D1 ; Key up or down? BMI.S @upDead ; Skip if up MOVE.L (A0), D3 ; Previous key dead? BEQ.S @saveState ; Skip if not MOVEQ #0, D2 MOVE.W (A1, D3.L), D2 ; Get the number of completors for previous LSL.W #1, D2 ; Multiply by 2 ADD.L D2, D3 MOVE.W 2(A1, D3.L), D0 ; D0 now has default code for previous dead key @saveState TST.W (A2) ; See if there are any completors BEQ.S @1 ; Skip if not MOVE.L A2, D2 ; Location of completor table SUB.L A1, D2 ; Subtract base address MOVE.L D2, (A0) ; Save it BRA.S @exitTrans @1 CLR.L (A0) ; Clear out dead key state SWAP D0 ; There might be something in the low word MOVE.W 2(A2), D0 ; Send out the default code BRA.S @exitTrans @upDead MOVE.W (A2)+, D3 ; Get the number of completors LSL.W #1, D3 ; Multiply by 2 MOVE.W (A2, D3.W), D0 ; Put default code in D0 BRA.S @exitTrans ; End KeyTrans ENDPROC ; KbdMngr ; This is the almost obsolete KeyHook routine, except FoolWrite and Microsoft rb, start ; weird still call this hook even tough they have been told to simply call ; Key1Trans, so here we go again. Do not remove this code, it is installed as ; Key1Trans and Key2Trans low mem vector. ; ----------------------------------------------------------------------------- ; Routine: KeyHook ; Input: d1.b option, alpha, shift keys in 2,1,0. ; $17A.b bit #7 1 iff feature key set. ; d2.w keycode (0-127). ; d3.b minus iff keyup, plus iff keydown. ; Output: d0.b ASCII code. ; ; Macintosh keyboard mapping hook, which relies on the new KeyTrans trap. ; ----------------------------------------------------------------------------- KeyHook PROC with ExpandMemRec bra.s SkipHeader ; skip the header. ; Global variables left here for the wacky Key Caps accessory. deadEnable dc.w $ffff ; enable the dead key processing. dc.l ('INIT') ; resource type. dc.w 0 ; resource id number. * export intlEnable intlEnable dc.b $00 ; intl keybooard flag (false). dc.b 7 ; hook version number. dc.w $0000 ; slot to mollify MacTerm. ; If this is the domestic or international keyboard hook, we check for keycodes ; that are in the keypad range. These are handled by just returning zero so ; the keypad hook can handle them. SkipHeader cmp.b #$40,d2 ; beyond keyboard range? bhs.s @domestic ; yes -> skip mapping. ; If the international flag is on in the header, this is an original ; Macintosh 128K, 512K, 512Ke, or MacPlus, and the keyboard type is not ; the MacPlus key dorfer with built-in keypad, then we perform a mapping ; from key codes to virtual key codes. The original domestic keyboard and ; MacPlus key dorfer just happen to generate virtual key codes all by ; themselves. move.b intlEnable,d0 ; international flag set? beq.s @domestic ; no -> skip remapping. move.w HwCfgFlags,d0 ; load hardware configuration. btst.l #hwCbADB,d0 ; ADB present? bne.s @domestic ; yes -> skip remapping. cmp.b #$0b,kbdType ; MacPlus keyboard? beq.s @domestic ; yes -> skip remapping. lea keyTable,a0 ; find the table. move.b 0(a0,d2.w),d2 ; map to virtual key code. ; Build the arguments and call to the keyboard trap to produce an ASCII code. ; We get the modifiers from the arguments, except for the command key, which is @domestic move.b $17A,d0 ; command key is in high bit of D0 lsl.b #1,d0 ; command bit is in X CC roxl.w #1,d1 ; command now in low bit of D1 move.b d3,d0 ; up/down key is in high bit of D0 lsl.b #1,d0 ; up/down bit is in X CC roxl.w #1,d1 ; up down now in low bit of D1 lsl.w #7,d1 ; shift all the way up or.b d2,d1 ; put keycode in low 7 bits. move.b deadEnable,d0 ; dead keys enabled? bne.s @deadOn ; yes -> allow key down calls. bset #7,d1 ; no -> force key up call. @deadOn subq.l #4,sp ; make room for result. move.l expandMem,a0 ; get low memory pointer move.l emKeyCache(a0),-(sp) ; pass the key cache pointer move.w d1,-(sp) ; push keycode and modifiers. pea emKeyDeadState(a0) ; push address of dead key state. _KeyTrans ; call keyboard trap. ; KeyTrans has left a present for us on the stack. Each word is an ASCII code ; if that word is to be reported as an event, or zero if nothing should happen. ; Coincidentally, our caller expects us to return zero if he should not report ; the event, or the ASCII code if he should. We check the first word and do ; that one ourselves. The second one is left up to the caller. Bring in the ; Hydra! moveq #0,d0 ; clear a long. move.w (sp)+,d0 ; get first return code. beq.s @noPost ; no need to post an event lsl.w #8,d2 ; shift key code over. or.w d2,d0 ; or in key code. move.w #KeyUpEvt,a0 ; assume key up tst.b d3 ; keyUp? bmi.s @postIt ; yes -> skip check move.w #KeyDwnEvt,a0 ; load key down @postIt _PostEvent ; generate an extra event. @noPost move.w (sp)+,d0 ; get the second return code. rts ; return to the caller. endwith ; ---------------------------------------------------------------------------- ; In order to map the old international keyboard correctly, we use the ; following translation table to map key codes to virtual key codes. This ; is not necessary on an old domestic keyboard as the key codes “just happen ; to match” the virtual key codes. ; ---------------------------------------------------------------------------- keyTable dc.b $00, $01, $02, $03, $04, $05, $32, $06 ; $00 .. $07 dc.b $07, $08, $2c, $09, $0c, $0d, $0e, $0f ; $08 .. $0f dc.b $10, $11, $12, $13, $14, $15, $16, $17 ; $10 .. $17 dc.b $18, $19, $1a, $1b, $1c, $1d, $1e, $1f ; $18 .. $1f dc.b $20, $21, $22, $23, $2a, $25, $26, $27 ; $20 .. $27 dc.b $28, $29, $24, $2e, $2f, $0b, $2d, $2b ; $28 .. $2f dc.b $30, $34, $0a, $33, $31, $35, $36, $37 ; $30 .. $37 dc.b $38, $39, $3a, $3b, $3c, $3d, $3e, $3f ; $38 .. $3f ENDP ; rb, end END