sys7.1-doc-wip/OS/ADBMgr/ADBMgr.a
2019-07-27 22:37:48 +08:00

2020 lines
75 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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