<> <> <> <> DIRECTORY AMEvents USING [CallDebugger], Basics USING [BYTE], FastBreak USING [FastBreakData, FastBreakId, FastBreakProc], Loader USING [MakeProcedureResident, MakeGlobalFrameResident], PrincOps USING [BytePC, Frame, FrameHandle, GlobalFrameHandle, InstWord, NullLink, StateVector, zBRK], PrincOpsUtils USING [GetReturnFrame, ReadXTS, WriteXTS], VM USING [Allocate, Free, Interval, AddressForPageNumber, Pin, Unpin, wordsPerPage], WorldVM USING [ClearBreak, LocalWorld, Read, SetBreak]; FastBreakImpl: MONITOR IMPORTS AMEvents, Loader, PrincOpsUtils, VM, WorldVM EXPORTS FastBreak = BEGIN OPEN FastBreak; FastBreakEntryPtr: TYPE = LONG POINTER TO FastBreakEntry; FastBreakEntry: TYPE = RECORD [ count: INT _ 0, next: FastBreakEntryPtr _ NIL, code: LONG POINTER _ NIL, pc: PrincOps.BytePC _ [0], inst: Basics.BYTE _ 0, data: LONG POINTER _ NIL, proc: FastBreakProc _ NIL ]; fastBreakPages: CARDINAL = 4; maxFastBreaks: CARDINAL = (fastBreakPages*VM.wordsPerPage) / SIZE[FastBreakEntry]; FastBreakArrayPtr: TYPE = LONG POINTER TO FastBreakArray; FastBreakArray: TYPE = ARRAY [0..maxFastBreaks) OF FastBreakEntry; arrayPtr: FastBreakArrayPtr _ NIL; space: VM.Interval; -- valid iff arrayPtr # NIL oldBreakTrap: PROC _ NIL; fastBreaksLeft: NAT _ maxFastBreaks; inUseChain: FastBreakEntryPtr _ NIL; freeChain: FastBreakEntryPtr _ NIL; firstTime: BOOL _ TRUE; SpecifyDefaultBreakHandler: PUBLIC PROC [old: PROC] = { <> oldBreakTrap _ old; }; FastBreakHandler: PUBLIC PROC = { <> state: RECORD [ padding: ARRAY [0..2) OF UNSPECIFIED, v: PrincOps.StateVector]; fp: PrincOps.FrameHandle; state.v _ STATE; fp _ PrincOpsUtils.GetReturnFrame[]; IF inUseChain # NIL THEN { pc: PrincOps.BytePC _ fp.pc; gf: PrincOps.GlobalFrameHandle _ fp.accesslink; code: LONG POINTER _ gf.code.longbase; ep: FastBreakEntryPtr _ inUseChain; inst: Basics.BYTE _ PrincOps.zBRK; useOld: BOOL _ FALSE; WHILE ep # NIL DO entry: FastBreakEntry _ ep^; -- copy in one burst to reduce races IF entry.code = code AND entry.pc = pc THEN { inst _ entry.inst; ep.count _ entry.count + 1; IF entry.proc # NIL THEN <> IF entry.proc[entry.data, fp, @state.v] THEN useOld _ TRUE; }; ep _ entry.next; ENDLOOP; IF inst # PrincOps.zBRK THEN { <> IF PrincOpsUtils.ReadXTS[] = on THEN PrincOpsUtils.WriteXTS[skip1]; IF useOld THEN { <> AMEvents.CallDebugger["FastBreak proc requested a break."]; }; state.v.source _ PrincOps.NullLink; state.v.dest _ LOOPHOLE[fp]; state.v.instbyte _ inst; RETURN WITH state.v; }; }; <> IF PrincOpsUtils.ReadXTS[] = on THEN PrincOpsUtils.WriteXTS[skip1]; state.v.source _ LOOPHOLE[fp]; state.v.dest _ LOOPHOLE[oldBreakTrap]; RETURN WITH state.v; }; FastBreaksLeft: PUBLIC PROC RETURNS [NAT] = TRUSTED { <> RETURN [fastBreaksLeft]; }; SetFastBreak: PUBLIC ENTRY PROC [code: LONG POINTER, pc: PrincOps.BytePC, proc: FastBreakProc _ NIL, data: FastBreakData _ NIL] RETURNS [id: FastBreakId] = TRUSTED { <> ENABLE UNWIND => NULL; ep: FastBreakEntryPtr _ freeChain; inst: Basics.BYTE _ PrincOps.zBRK; needToSet: BOOL _ TRUE; IF arrayPtr = NIL THEN { <> fb: FastBreakEntryPtr; IF firstTime THEN { <> Loader.MakeGlobalFrameResident[SetFastBreak]; Loader.MakeProcedureResident[SetFastBreak]; Loader.MakeProcedureResident[ClearFastBreak]; Loader.MakeProcedureResident[ClearAllFastBreaks]; Loader.MakeProcedureResident[ClearInternal]; Loader.MakeProcedureResident[CountBreaksAtLocation]; Loader.MakeProcedureResident[ReadCodeByte]; firstTime _ FALSE; }; space _ VM.Allocate[fastBreakPages]; VM.Pin[space]; arrayPtr _ LOOPHOLE[VM.AddressForPageNumber[space.page]]; fastBreaksLeft _ maxFastBreaks; arrayPtr^ _ ALL[FastBreakEntry[]]; ep _ freeChain _ fb _ @arrayPtr[0]; FOR i: CARDINAL IN [0..maxFastBreaks-1) DO next: FastBreakEntryPtr _ fb + SIZE[FastBreakEntry]; fb.next _ next; fb _ next; ENDLOOP; }; IF ep = NIL THEN RETURN [NIL]; inst _ ReadCodeByte[code, pc]; IF inst = PrincOps.zBRK THEN { <> FOR eep: FastBreakEntryPtr _ inUseChain, eep.next WHILE eep # NIL DO IF eep.code = code AND eep.pc = pc THEN { inst _ eep.inst; needToSet _ FALSE; EXIT}; ENDLOOP; IF inst = PrincOps.zBRK THEN <> RETURN [NIL]; }; id _ @ep.count; <> freeChain _ ep.next; <> ep^ _ [count: 0, next: inUseChain, code: code, pc: pc, inst: inst, data: data, proc: proc]; <> inUseChain _ ep; IF needToSet THEN ep.inst _ WorldVM.SetBreak[WorldVM.LocalWorld[], LOOPHOLE[code], pc]; }; ClearFastBreak: PUBLIC ENTRY PROC [id: FastBreakId, proc: FastBreakProc _ NIL, data: FastBreakData _ NIL] RETURNS [found: BOOL _ FALSE] = TRUSTED { <> ENABLE UNWIND => NULL; ep: FastBreakEntryPtr _ inUseChain; lag: FastBreakEntryPtr _ NIL; WHILE ep # NIL DO IF LOOPHOLE[id, FastBreakEntryPtr] = ep AND ep.proc = proc AND ep.data = data THEN { ClearInternal[ep, lag]; RETURN [TRUE]; }; lag _ ep; ep _ ep.next; ENDLOOP; }; ClearAllFastBreaks: PUBLIC ENTRY PROC [releaseResources: BOOL _ TRUE] RETURNS [cleared: NAT _ 0] = TRUSTED { <> ENABLE UNWIND => NULL; WHILE inUseChain # NIL DO <> ClearInternal[inUseChain]; cleared _ cleared + 1; ENDLOOP; IF releaseResources AND arrayPtr # NIL THEN { arrayPtr _ NIL; VM.Unpin[space]; VM.Free[space]; fastBreaksLeft _ maxFastBreaks; }; }; ClearInternal: INTERNAL PROC [ep: FastBreakEntryPtr, lag: FastBreakEntryPtr _ NIL] = { SELECT CountBreaksAtLocation[ep.code, ep.pc] FROM 0 => RETURN; 1 => { <