sys7.1-doc-wip/OS/ADBMgr/ADBMgr.a

2020 lines
75 KiB
Plaintext
Raw Normal View History

2019-07-27 14:37:48 +00:00
;
; 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 files 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):
;
; <SM11> 12/13/93 PN Roll in KAOs and Horror changes to support Malcom and AJ
; machines.
; <SM10> 5/20/93 RB Rolled in a patch to the KeyTrans routine that we missed last
; time...
; <SM9> 11/30/92 SWC Fixed the Horror roll in. A bunch of code was left hanging
; around that doesn't need to be here.
; <SM8> 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 !)
; <SM7> 8/26/92 kc Roll in Horror changes. From KbdADB.a
; <H6> 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.
; <H5> 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.
; <H4> 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.
; <H3> 2/21/92 SWC GMR/Backed out <T6> 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.
; <H2> 2/12/92 SWC Patched in new CrsrDev stuff for mouse/trackball/etc.
; acceleration.
; <SM6> 5/21/92 kc Append "Trap" to the names of GetADBInfo, SetADBInfo, ADBOp and
; GetIndADB to avoid name conflict with the glue.
; <SM5> 5/17/92 kc Add include of PowerPrivEqu.a. Export
; ExplicitRequestDone,ImplicitRequestDone and RunADBRequest.
; <SM4> 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.
; <SM3> 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:
; <C931> 11/5/87 CSL EMT Roll in patches for ADBReinit (PAB191&PAB192).
; <C930> 11/5/87 MSH Rewrote FDBShiftInt for Laguna, new interrupt interface to
; PowerMgr.
; <C914> 10/29/87 rwh Port to Modern Victorian
; <C916> 10/21/87 MSH Fixed the drba loop in FDBshiftInt for Laguna.
; <C889> 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 ; <T13>
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 <t10> ag
move.l #((UsrActivity<<16)|\ ; set for user activity <K2>
(IdleUpdateDisp<<0)),d0 ; idle update selector <K2>
_PowerDispatch ; call power manager
move.l (sp)+,d0 ; restore d0 <t10> 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 <H4>
MOVEA.L UnivInfoPtr,A1 ; point to the ProductInfo table <H4>
ADDA.L ProductInfo.ADBDebugUtilPtr(A1),A1 ; and get the address of its ADB table <H4>
MOVE.L 4*adbKeySwSecure(A1),D2 ; get the offset to the keyswitch code <H4>
BEQ.S @JustDoIt ; -> no keyswitch check, so just call the handler <H4>
MOVEM.L D0/D1/A0/A2,-(SP) ; <H4>
ADDA.L D2,A1 ; calculate the routine's address <H4>
JSR (A1) ; and call it <H4>
MOVEM.L (SP)+,D0/D1/A0/A2 ; <H4>
BEQ.S @noHandler ; -> the keyswitch is secure, so don't call the handler
@JustDoIt MOVEA.L D1,A1 ; get the handler's address <H4>
jsr (a1) ; call the handler
@noHandler movea.l (sp),sp ; deallocate the buffer (if implicit cmd)
rts ; return from the interrupt <T5>
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 <H6>
_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. <SM3> rb
; Do this only as a post-procedure. Verified offsets with equates in ScriptPriv.a <SM3> rb
;______________________________________________________________________
PostInit ; <SM3> rb
WITH ExpandMemRec,KybdDriverData ; <SM3> 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 <SM3> rb
MOVE.L ExpandMem,A0 ; get the Expanded memory in a0 <SM3> rb
MOVE.L emKeyCache(A0),D4 ; save it in D4 <SM3> 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 <SM3> rb
MOVE.L D4,KybdDriverData.KCHRPtr(A1) ; set KCHR pointer <SM3> rb
@NextRec
SUBQ.W #1, D2
BGT.S @KRemoveLoop
MOVE.L (SP)+,D4 ; restore the working register <SM3> rb
UNLK A6
@DoneKRemove
RTS
ENDWITH ; <SM3> 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 ; <SM8> 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 <H4>
ADDA.L ProductInfo.ADBDebugUtilPtr(A0),A0 ; and get the address of its ADB table <H4>
MOVE.L 4*adbInitProc(A0),D0; get the offset to the InitADB entry code <H4>
BEQ.S ReInit ; -> this function is not supported <H4>
ADDA.L D0,A0 ; calculate the routine's address <H4>
JSR (A0) ; and call it <H4>
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<<FDBInit)|\
(1<<FDBQEmpty),FDBFlag(A3) ; initialize the flags
clr.b FDBAuFlag(A3)
Lea StartCQ(A3),A0
Move.L A0,FQBegPtr(A3) ; initialize command queue pointer
Move.L A0,FQHeadPtr(A3) ; initialize command queue pointer
Move.L A0,FQEntPtr(A3) ; initialize command queue pointer
Lea EndCQ(A3),A0
Move.L A0,FQEndPtr(A3) ; initialize command queue end pointer
BSR.S InitDevT ; go initialize the device table
ANDI #$F8FF,SR ; clear Interrupt mask
@1 BTST #FDBInit,FDBFlag(A3); done with initialization?
BNE.S @1 ; no, keep in loop
BSR DefaultDev ; setup mouse & keyboard as default <t6><H3>
BSR KbdInstall ; finally, install keyboard information <t6><H3>
pea KeyHook ; get Key Hook entry point. <SM8> rb
move.l (sp),Key1Trans ; install the Key1Trans hook. <SM8> rb
move.l (sp)+,Key2Trans ; install the Key2Trans hook. <SM8> rb
BRA flushkbds ; after all is setup, flush to get current keys <H5>
;_______________________________________________________________________
;
; 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 <H6>
bsr.l CrsrDevReInit ; allocate a crsrDevRec for each relative pointing device <H6>
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 <H5>
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 <H5>
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
;<A230/17Oct86> 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)
;<A230/17Oct86> 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 <SM10> 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 <SM10> rb
with ExpandMemRec ; <SM10> rb
move.l ExpandMem,a1 ; <SM10> rb
move.l emKeyCache(a1),d1 ; Get KCHR pointer in ExpandMem <SM10> rb
cmp.l kchrTableNoLink(sp),d1 ; same as KCHR pointer param? <SM10> rb
bne.s @noTable ; if not, skip itlk handling <SM10> rb
endwith ;ExpandMemRec ; <SM10> 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 <SM8> 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. <S159>
; 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
; <SM8> rb, end
END