mac-rom/OS/OSEventMgr.a

627 lines
23 KiB
Plaintext
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

;
; File: OSEventMgr.a
;
; Contains: Macintosh OS Event Manager
;
; Written by: Andy Hertzfeld 02-Nov-81
;
; Copyright: © 1981-1993 by Apple Computer, Inc. All rights reserved.
;
; Change History (most recent first):
;
; <SM12> 12/13/93 PN Roll in KAOs and Horror changes to support Malcom and AJ
; machines.
; <SM11> 11/11/93 rab Added the label OldOSEventAvail at the original entry point for
; OSEventAvail. Changed GetOSEvent to bsr to the old entry point
; and bypass the rolled in HelpMgr Patch. This fixes bug# 1123113.
; Also backed out SM7 since it is no longer necessary.
; <SM10> 5/18/93 rab Fix Radar #1082829. Change GetOSEvent so that it doesn't do
; smgrpostmunging if smgrCore hasn't been set up (= -1).
; <SM9> 12/30/92 rab Made a change to InitEvents so that it does nothing if the
; EventQueue has already been set up. This keeps DiskInsertEvents
; from getting lost if the boot fails.
; <SM8> 12/16/92 SWC Moved InitEvents here from StartBoot.
; <SM7> 12/09/92 HI Modified the HelpMgr patch code in OSEventAvail to check for the
; emIsDragging field of ExpandMem; if set, then the patch is
; bypassed. This prevents HelpMgr from removing/displaying another
; help balloon and altering the WMgrPort's clipRgn, penPat, and
; penMode and allows DragTheRgn to properly remove the drag region.
; (Hoon Im)
; <SM6> 6/22/92 RB Rolled-in the Help Manager patch to OSEventAvail from
; BalloonPthc28.a, HMOSEventAvailPatch.
; <SM5> 6/12/92 CS Roll-in Reality Changes:
; <6> 6/12/92 FM Change the BigJmp to SMgrPostMunging to be a jsr through the
; internal scriptmgr vector.
; <SM4> 5/21/92 kc Append "Trap" to the names of
; PostEvent,GetOSEvent,OSEventAvail,Enqueue and Dequeue to avoid
; name conflict with the glue.
; <SM3> 5/17/92 kc Remove "With PmgrRec."
; <5> 4/27/92 JSM Get rid of conditionals: hasIdle and isUniversal are always
; true, onMac is always false. This file now has no conditionals.
; <4> 8/30/91 JSM Cleanup header.
; <3> 6/12/91 LN Changed #include 'HardwareEqu.a' to 'HardwarePrivateEqu.a'
; <2> 3/29/90 MSH Add universal run time test for IdleUpdate call.
; <1.6> 11/1/89 MSH Rolling in changes from HcMac Reality sources: Pmgr equates now
; in record format.
; <1.5> 8/22/89 SES Removed references to nFiles.
; <1.4> 3/31/89 MSH Bump LastAct if an event is available.
; <1.3> 2/21/89 PKE Rearranged GetOSEvent so DeleteEvt doesn't return through
; SMgrPostMunging (this also fixes a FlushEvents bug caused by
; BigJMP), and so A0 is correctly set up when GetOSEvent returns
; through SMgrPostMunging.
; <1.2> 1/16/89 GGD Added a scratch register parameter to the BigJMP macro calls
; since it will be needed when they change to use PC relative
; addressing.
; <1.1> 11/10/88 CCH Fixed Header.
; <1.0> 11/9/88 CCH Adding to EASE.
; <1.4> 10/27/88 PKE added support for script manager
; <¥1.3> 9/23/88 CCH Got rid of inc.sum.d and empty nFiles
; <1.2> 9/8/88 MSH New version of sleep/idle support.
; <1.1> 4/18/88 CSL Add support for SLEEP & IDLE in PostEvent
; <1.0> 2/10/88 BBM Adding file for the first time into EASEÉ
; <C517> 12/12/86 EMT Shuffle keyboard globals for Excel. Affects OSEventMgr.a, kbd.a,
; nSysEqu.a
; <C206> 10/9/86 bbm Modified to mpw aincludes.
; <C173> 9/24/86 EMT Changed interrupt mask in PostEvent and company to disable SCC
; as well as VIA interrupts.
; <C89> 7/28/86 EMT Changed to use long word NewKeyLast instead of KeyLast for Apple
; Desktop Bus machines
; 2/19/86 BBM Made some modifications to work under MPW
; 10/28/85 DLD Changed FlushEvents to put a -1 into LastSPExtra for FontMGR
; 7/24/85 RDC Changed interrupt level settings to use equate Added spearate
; include statement for HWequ file (no longer in systlqk.sym)
; 6/7/85 JTC Make results of os routines long. <07Jun85>
; 3/3/85 LAK Don't flush events masked out by FlEvtMask in FlushEvents (this
; is a new mask, initialized at SysInit time to $FF7F, i.e., all
; events except DIP events). This is an attempt to correct the
; problem that -1 is a common parameter given to FlushEvents at
; the start of a program, even though a flush of DIP is probably
; not intended.
; 1/31/85 LAK Removed false RTE.
; 1/23/85 LAK Adapted for new equate files. Added equate for NoEvtAvail
; (equate was deleted from SysErr.Text)
; 8/17/83 LAK PostEvent now uses EvtBufCnt instead of hardwired EvtMax.
; 2/22/83 LAK made all routines observe Pascal regsave conventions.
; 1/3/83 AJH Got rid of update event, changed names of GetNextEvent and
; EventAvail
; 10/16/82 LAK Update events return 0 in D0 now for GetNextEvent and
; EventAvail.
; 10/1/82 LAK A0 saved over checkupdate call now . . .
; 7/26/82 LAK FLUSHEVENTS returns # of event type it stopped on, if any Added
; update events . . .
; 6/9/82 LAK Moved keyboard auto-repeat function here
;
;
; This is the event manager for the MacIntosh Operating
; system. It manipulates events on the system event queue.
; Its major routines are "PostEvent" which adds an event to the
; event queue, and "GetNextEvent" which returns the next event
; in the queue. PostEvent may be called from an interrupt (level 1)
; or completion routine; all other routines in the event
; manager must be called from the main thread of execution. There are
; two other Event Manager routines, "FlushEvents" and "EventAvail",
; giving a total of 4 routines. Some of these routines call other ones,
; and all are accessible via operating system calls:
;
; os call --> GetNextEvent os call --> FlushEvents
; |
; v
; os call --> EventAvail
; |
; (auto-key event generation)
; |
; v
; os call --> PostEvent
;
; The Event Manager manages its own private buffer to get storage for the
; event queueing elements. It does this because PostEvent runs at interrupt
; level and thus cannot call the standard storage allocator.
;
BLANKS ON
STRING ASIS
LOAD 'StandardEqu.d'
INCLUDE 'HardwarePrivateEqu.a'
INCLUDE 'UniversalEqu.a'
INCLUDE 'ScriptPriv.a'
INCLUDE 'Balloons.a' ;
INCLUDE 'BalloonsPriv.a' ;
INCLUDE 'PowerPrivEqu.a' ;
INCLUDE 'Processes.a'
Events PROC EXPORT
EXPORT InitEvents,PostEventTrap,GetOSEventTrap,OSEventAvailTrap,FlushEvents,NewEMFunction
IMPORT EnqueueTrap,DequeueTrap,SMgrPostMunging
NoEvtAvail EQU -1 ; (moved from SysErr.Text)
EvtOffset EQU 6 ; event record offset from start of
; event queue element
;_______________________________________________________________________
;
; Routine: InitEvents afe0
;
; Arguments: D0 (input) -- number of elements in the event queue
; D0 (output) -- result code
;
; Function: This routine initializes the event queue to the size
; specified in D0.
;
; Other: uses D0,D1,D2,A0,A1
;_______________________________________________________________________
InitEvents move.w EventQueue,d2 ; check the first word of the EventQ <SM9>
bge.s @exit ; if ­-1 it's set up, leave it alone <SM9>
MOVE.W D0,D2
MOVEQ #EvtQBlkSize,D0
MULU D2,D0
MOVE.L D0,D1 ; save a copy of the size
_NewPtr ,SYS,CLEAR ; get memory for storage allocator
LEA EvtBufCnt,A1
SUBQ #1,D2 ; set up lo mem event buffer count (-1)
MOVE.W D2,(A1) ; EvtBufCnt
CLR.L -(A1) ; EventQueue QTail
CLR.L -(A1) ; EventQueue QHead
CLR.W -(A1) ; EventQueue QFlags
MOVE.L A0,-(A1) ; SysEvtBuf
; fill buffer with all ones
@InitEvLoop NOT.W (A0)+ ; 0000 -> FFFF
SUBQ #2,D1 ; more to do?
BNE.S @InitEvLoop ; size must have been divisible by 4
MOVE.W #$FFEF,-(A1) ; SysEvtMask - enable all events except key up
CLR.W KeyLast ; initialize keyboard repeat stuff
@exit RTS ; <SM9>
;_______________________________________________________________________
;
; Routine: PostEvent b010
;
; Arguments: A0 (input) -- event number (16-bit)
; D0 (input) -- event message (32-bit)
; D0 (output) -- result: 0=event posted, 1=not posted
; A0 (output) -- ptr to event queue element if posted (used
; internally by Eventavail)
;
; Function: This routine adds an event to the system event queue. The
; specified eventnumber and event message are logged for the
; event. The current time, mouse position, state of command
; key, option key, shift key, alpha lock key, and mouse button
; are additional event queueing elements included in
; each event. An event is only posted if enabled by the System
; Event Mask; if not enabled, a result code of 1 is returned.
; This routine will delete the first element of the event queue
; (the oldest element), if the queue is full, to make room for
; the new event: this guarantees that an enabled event will be
; posted.
;
; So that events of the same type are received and
; logged in correct time order of occurrance, the calling routine
; should disable further interrupts from the same source until
; the event has been posted. This works since each event type
; typically has but one source. All interrupts are disabled
; during this routine, first to guarantee the integrity of the
; free buffer allocation and second to enforce the time-ordering
; of events. Currently, only level 1 interrupts are masked because
; SCC interrupts do not generate any events; if that changes (e.g.,
; and SCC system error event is defined), SCC interrupts would
; also have to be masked.
;
; PostEvent can be called by EventAvail to post an auto-key event;
; it is never called to post an update event.
;
; Calling sequence: MOVE.W #eventnumber,A0
; MOVE.L #message,D0
; _PostEvent
;
; Other: uses D0,D1,D2,A0,A1
;_______________________________________________________________________
PostEventTrap
with PowerDispRec
TestFor SupportsIdle
BEQ.S @notsupported
MOVE.L D0,-(SP)
_UsrIdleUpdate ; user update activity log
MOVE.L (SP)+,D0
@notsupported
MOVE.W SysEvtMask,D2 ; system event mask
MOVE.W A0,D1 ; event number
BTST D1,D2 ; is this event enabled?
BNE.S GetFreeOne
MOVEQ #EvtNotEnb,D0 ; if not, skip all this stuff
RTS
GetFreeOne MOVE SR,-(SP) ; save interrupt state
MOVE.L D0,-(SP) ; event message
MOVE.W A0,-(SP) ; event number
MOVE.L SysEvtBuf,A0 ; get address of system event buffer
ADDQ.L #EvtOffset,A0 ; point to event record ($FFFF=free)
MOVEQ #EvtQBlkSize,D0 ; for speed
MOVEQ #-1,D1 ; ibid
SUB D0,A0 ; back pointer down one
MOVE.W EvtBufCnt,D2 ; event buffer count - 1
ORI #HiIntMask,SR ; no interrupts, please <C173 24Sep86>
GetEbufLoop ADD D0,A0 ; bump to next event queue element
CMP.W EvtNum(A0),D1 ; is this element free?
DBEQ D2,GetEbufLoop ; loop till we get one or no more left
LEA EventQueue,A1 ; point A1 to queue header
BEQ.S GotEvtElement ; Z-flag set from CMP only
; no more event buffers available. remove first queue element for a wraparound
; (queue elements are added at the end of the queue, so the first element
; has been around the longest . . .); there must be >1 elements in the queue
; since there are no free elements and given that 29 interrupting processes cannot
; grab elements and not post them into the queue.
MOVE.L QHead(A1),A0 ; point A0 to first queue element
MOVE.L QLink(A0),QHead(A1) ; special case delete: first element
; of a queue of length > 1
; now A0 points to a free element
ADDQ.L #EvtOffset,A0 ; point to event record part
; We got an event queueing element, so fill in the system info and queue it up
GotEvtElement MOVE.W (SP)+,EvtNum(A0) ; set up the event number
MOVE.L (SP)+,EvtMessage(A0) ; set up the message
BSR.S FillRecord ; fill out the rest of the record
SUBQ.L #EvtOffset,A0 ; and point to start of queue element
MOVE #EvType,QType(A0) ; set up the q-element type field
JSR EnqueueTrap ; let queueing routines queue it up
MOVE (SP)+,SR ; restore interrupt state
MOVEQ #0,D0 ; zero result code
RTS ; and return
; FillRecord fills out the standard fields in an event record pointed to by A0
FillRecord MOVE.L Ticks,EvtTicks(A0) ; fill in the current time
MOVE.L Mouse,EvtMouse(A0) ; and the current mouse point
MOVE KeyMap+6,D1 ; get meta-key states
ROL.W #1,D1 ; rotate around
MOVE.B D1,EvtMeta(A0) ; update metakey field
MOVE.B MBState,EvtMBut(A0) ; get mouse button state
EndWith
RTS
;_______________________________________________________________________
;
; Routine: OSEventavail b09c
;
; Arguments: A0 (input) -- pointer to user event record (32-bit)
; D0 (input) -- set of events desired (event mask)
; D0 (output) -- 0=non-null event returned, -1=null event
; returned
; A0 (output) -- pointer to user event record
; A1 (output) -- pointer to event queue element when D0=0
; (used internally by GetNextEvent)
;
; Function: This routine polls for availability of certain types of events.
; The user-specified event record format is identical to that of
; the queue element, except for the queue header fields. If no
; events are available, the null event is returned along with
; a -1 result code in D0.
;
; EventAvail is also called by GetNextEvent.
;
; Calling sequence: MOVE.W #EventMask,D0
; LEA EventBuffer,A0
; _EventAvail
;
; Other: uses D0,D1,D2,A0,A1
;_______________________________________________________________________
OSEventAvailTrap
ANDI.L #$FFFF,D0
OSEventAvailSkippingAndi
with PowerDispRec
MOVEM.L A0-A1/D0-D2,-(SP) ; we need to save these regs
MOVE.L ExpandMem,A0 ; point to the expand mem ptr
TST.B ExpandMemRec.emIsDragging(A0) ; called from DragTheRgn? <SM7>
BNE.S @DoNothing ; yes, skip HelpMgr patch code <SM7>
MOVE.L ExpandMemRec.emHelpGlobals(A0),A0 ; A0 = global ptr
TST.B hmgWhatIs(A0) ; is Balloon help mode on?
BEQ.S @DoNothing ; no, let's not scan for a content window
TST.W hmgOSEventReentrantCount(A0) ; test the reentrant count
BNE.S @DoNothing ; do nothing on the reentrant case
ST hmgOSEventReentrantCount(A0) ; say that we're busy
; from BalloonExtensions.a: ptchHMCallTrackerInContext
SUBQ #$8, SP
SUBQ #$2, SP
PEA.L $2(SP)
MOVEQ.L #-$1, D0 ; like _GetFrontProcess but not quite
MOVE.L D0, -(SP)
MOVE #$39, -(SP)
_OSDispatch
TST (SP)+
BNE.B @wasError
SUBQ #$2, SP
PEA.L $2(SP)
_WakeUpProcess
TST (SP)+
BNE.B @wasError
SUBQ #$2, SP
MOVE #$FC, D0 ; from Balloonptch28.p: HMBalloonBulk
_Pack14
TST (SP)+
@wasError AddQ #$8, SP
; from BalloonExtensions.a: ptchHMGetHelpGlobal
MOVE.L ExpandMem,A0 ; point to the expand mem ptr
MOVE.L ExpandMemRec.emHelpGlobals(A0),A0 ; return the global ptr on the stack
CLR hmgOSEventReentrantCount(A0)
@DoNothing
MOVEM.L (SP)+,A0-A1/D0-D2 ; restore 'em, <SM6> rb, end
OldOSEventAvail ; <SM11>
LEA EventQueue,A1 ; get address of event queue
; Since PostEvent could dequeue the first element at any time, the event you
; are on may suddenly be recycled and you may end up dequeueing a different event
; or suddenly find yourself at the end of the queue when you were just at the
; beginning. By disabling interrupts during the search, this is avoided.
MOVE SR,-(SP) ; save interrupt state
ORI #HiIntMask,SR ; no event-generating interrupts <C173 24Sep86>
MOVE.L QHead(A1),D1 ; get address of 1st element
BEQ.S TstAutoEvent ; if nil, check for auto events
EventALoop MOVE.L D1,A1 ; get pointer into A-reg
MOVE EvtNum+EvtOffset(A1),D1 ; get its event number
BTST D1,D0 ; is it one we want?
BNE.S GotEventAvail ; if so, we got one!
MOVE.L QLink(A1),D1 ; follow the link to the next one
BNE.S EventALoop ; if we got one, check it out
; there wasn't an event in the queue for us, so check for auto events
TstAutoEvent AND.W SysEvtMask,D0 ; figure in system mask for pseudo-evts
BTST #AutoKeyEvt,D0 ; do we want this kind? <C517/12Dec86>
BEQ.S NoEventAvail ; go on if not <C517/12Dec86>
MOVE.W HiKeyLast, D0 ; <C517/12Dec86>
SWAP D0 ; <C517/12Dec86>
MOVE.W KeyLast, D0 ; <C517/12Dec86>
TST.L D0 ; key down to autorepeat? <C517/12Dec86>
BEQ.S NoEventAvail ; if not, return null event
MOVE.L Ticks,D1 ; check first threshold
MOVE.L D1,D2
SUB.L KeyTime,D1
CMPI.L #$8000,D1
BGE @skip
CMP KeyThresh,D1
BLT.S NoEventAvail ; br if not time yet
@skip
SUB.L KeyRepTime,D2 ; check second threshold
CMP KeyRepThresh,D2
BLT.S NoEventAvail ; br if not time yet
MOVE.L Ticks,KeyRepTime ; repeat it: first note the time
MOVE.L A0,-(SP) ; save pointer to user's buffer
MOVE #AutoKeyEvt,A0 ; get event number
; D0 already contains the correct event message <C517/12Dec86>
BSR PostEventTrap ; go post it
MOVE.L A0,A1 ; get pointer to event queue element
MOVE.L (SP)+,A0 ; restore pointer to user's buffer
; and return the event
GotEventAvail
MOVE (SP)+,SR ; restore interrupt state
MOVEQ #(EvtBlkSize/4),D0 ; get size of evt record in longwords
MOVEM.L A0-A1,-(SP) ; preserve regs
ADD.L #EvtOffset,A1 ; bump to record part of event element
TestFor SupportsIdle
BEQ.S @notsupported
MOVE.L D0,-(SP)
_IdleUpdateDispatch ; set for overall activity
MOVE.L (SP)+,D0
@notsupported
@1 MOVE.L (A1)+,(A0)+ ; move it into user's buffer
SUBQ #1,D0
BNE.S @1 ; loop till done
MOVEM.L (SP)+,A0-A1 ; get regs back
RTS ; D0 is zero
; there wasn't any event available so return the null event in the users event
; record and a flag in D0
NoEventAvail BSR.S FillRecord ; fill in record for null events
MOVE (SP)+,SR ; restore interrupt state
CLR.W EvtNum(A0) ; return the null event
CLR.L EvtMessage(A0) ; zero message for null events
MOVEQ #NoEvtAvail,D0 ; and D0 non-zero
EndWith
RTS
;_______________________________________________________________________
;
; Routine: GetOSEvent b1aa
;
; Arguments: A0 (input) -- pointer to user event buffer (32-bit)
; D0 (input) -- type of event desired (event mask)
; D0 (output) -- 0=non-null event returned, -1=null event
; returned
;
; Function: This routine returns the next event in the system event queue.
; The returned event is dequeued, thereby freeing up the space
; which holds that queue element. If no events of the types
; enabled by the mask exist, the null event is returned.
;
; Calling sequence: MOVE.W #EventMask,D0
; LEA EventBuffer,A0
; _GetNextEvent
;
; Other: uses D0,D1,D2,A0,A1
;_______________________________________________________________________
GetOSEventTrap ANDI.L #$FFFF,D0
GetOSEventSkippingAndi
BSR.S OldOSEventAvail ; first find the event <SM11>
BNE.S @EventDone ; don't dequeue null or update events
MOVE.L A0,-(SP) ; save user's event record ptr
MOVE.L A1,A0 ; pointer to event queue element
LEA EventQueue,A1 ; A1 points to the queue
BSR.S DeleteEvt ; get rid of it
MOVE.L (SP)+,A0 ; restore user's event record ptr
; (needed by SMgrPostMunging)
MOVEQ #0,D0 ; exit with result code 0
; Check for ScripMgr related events and do some processing on them
movem.l a0/a2,-(sp) ; save a0/a2
GetSMgrCore a2 ; load SMgr globals.
cmpa.l #-1,a2 ; see if smgrCore is set up yet <SM10>
beq.s @tooEarly ; bail out if it isn't <SM10>
move.l smgrRecord.sVectSMgrPostMunging(a2),a2 ; get SMgrPostMunging <4/26/88ldc>
jsr (a2) ; Expects an EventPtr in a0
@tooEarly movem.l (sp)+,a0/a2 ; restore a0/a2 <SM10>
@EventDone
rts
DeleteEvt JSR DequeueTrap ; dequeue it (D0 destroyed)
MOVE #$FFFF,EvtNum+EvtOffset(A0) ; and release its storage
RTS
;_______________________________________________________________________
;
; Routine: FlushEvents b1ea
;
; Arguments: D0 (input) -- low word: events to remove (event mask)
; high word: events on which to stop (event mask)
; D0 (output) -- event type of event which terminated search
;
; Function: This routine removes events of type specified by the caller. On
; entry, D0 contains a long word of two 16-bit event masks. The
; low-order 16 bits contains a mask of events to remove, and the
; high-order 16 bits contains a mask of events that, once
; encountered, terminate the event removal process.
;
; Calling sequence: MOVE.L #EventMasks,D0
; _FlushEvents
;
; Other: uses D0,D1,D2,A0,A1
;_______________________________________________________________________
FlushEvents MOVE SR,-(SP) ; save interrupt state
MOVEM.L D3-D4,-(SP) ; preserve Pascal regs
; put a -1 into LastSPExtra (long) for the FontMGR (fixes a bug when under Switcher).
MOVEQ #-1,D1 ; <28-Oct-85>
MOVE.L D1,LastSPExtra ; <28-Oct-85>
MOVE.L D0,D1
SWAP D1 ; mask of events to stop on
MOVE.W D0,D4 ; mask of events to remove
AND.W FlEvtMask,D4 ; only flush masked events <03Mar85>
LEA EventQueue,A1 ; A1 points to the queue
; Since PostEvent could dequeue the first element at any time, the event you
; are on may suddenly be recycled and you may end up dequeueing a different event
; or suddenly find yourself at the end of the queue when you were just at the
; beginning. By disabling interrupts during the search, this is avoided.
ORI #HiIntMask,SR ; no event-generating interrupts <C173 24Sep86>
MOVE.L QHead(A1),D3 ; get pointer to first element
BEQ.S AllFlushed ; if nothing there, we're done
FEventLoop MOVE.L D3,A0 ; get pointer into A-reg
MOVE.L QLink(A0),D3 ; save the link
MOVE EvtNum+EvtOffset(A0),D2 ; get number of current event
BTST D2,D1 ; is it in the stop mask?
BNE.S FlushStop ; if so, stop
BTST D2,D4 ; is it one to delete?
BEQ.S NoFlush ; if not, skip
BSR.S DeleteEvt ; otherwise, delete it (clobbers D0)
NoFlush TST.L D3 ; any more to do?
BNE.S FEventLoop ; if so, loop
AllFlushed MOVEQ #0,D2 ; exit with 0 result code
FlushStop MOVEM.L (SP)+,D3-D4 ; restore regs
MOVE D2,D0 ; exit with number of event we stopped
EXT.L D0 ; make result long <07Jun85>
MOVE.W (SP)+, SR ; on as our result
RTS
NewEMFunction
; b232
MoveA.L (A7)+, A1
CmpI #$1, D0
BEQ.B L13
CmpI #$2, D0
BEQ.B L14
CmpI #$3, D0
BEQ.B L15
MoveQ.L #$C, D0
DC.W $A9C9
L13 Move.L (A7)+, D0
MoveA (A7)+, A0
Move.L A1, -(A7)
Bsr GetFreeOne
Bra.B L16
L14 MoveA.L (A7)+, A0
Move.L (A7)+, D0
Move.L A1, -(A7)
Bsr OSEventAvailSkippingAndi
Not.L D0
AndI.L #$101, D0
Bra.B L16
L15 MoveA.L (A7)+, A0
Move.L (A7)+, D0
Move.L A1, -(A7)
Bsr GetOSEventSkippingAndi
Not.L D0
AndI.L #$101, D0
L16 Move.L D0, $4(A7)
Rts
END