<> <> DIRECTORY AMEvents USING [CallDebugger], Environment USING [wordsPerPage], FastBreak USING [FastBreakData, FastBreakId, FastBreakProc], Frame USING [GetReturnFrame], Mopcodes USING [zBRK], PrincOps USING [BYTE, BytePC, Frame, FrameHandle, GlobalFrameHandle, InstWord, NullLink, StateVector], Space USING [Create, Delete, Handle, LongPointer, Map, virtualMemory], SpecialSpace USING [MakeCodeResident, MakeGlobalFrameResident, MakeResident, MakeSwappable], WorldVM USING [ClearBreak, LocalWorld, Read, SetBreak], XferTrap USING [ReadXTS, WriteXTS]; FastBreakImpl: MONITOR IMPORTS AMEvents, Frame, Space, SpecialSpace, WorldVM, XferTrap 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: PrincOps.BYTE _ 0, data: LONG POINTER _ NIL, proc: FastBreakProc _ NIL ]; FastBreakPages: CARDINAL = 4; MaxFastBreaks: CARDINAL = (FastBreakPages*Environment.wordsPerPage) / SIZE[FastBreakEntry]; FastBreakArrayPtr: TYPE = LONG POINTER TO FastBreakArray; FastBreakArray: TYPE = ARRAY [0..MaxFastBreaks) OF FastBreakEntry; arrayPtr: FastBreakArrayPtr _ NIL; space: Space.Handle; -- 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 _ Frame.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: PrincOps.BYTE _ Mopcodes.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 # Mopcodes.zBRK THEN { <> IF XferTrap.ReadXTS[] = on THEN XferTrap.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 XferTrap.ReadXTS[] = on THEN XferTrap.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: PrincOps.BYTE _ Mopcodes.zBRK; needToSet: BOOL _ TRUE; IF arrayPtr = NIL THEN { <> fb: FastBreakEntryPtr; IF firstTime THEN { <> SpecialSpace.MakeGlobalFrameResident[FastBreakImpl]; SpecialSpace.MakeCodeResident[FastBreakImpl]; firstTime _ FALSE; }; Space.Map[space _ Space.Create[FastBreakPages, Space.virtualMemory]]; SpecialSpace.MakeResident[space]; arrayPtr _ LOOPHOLE[Space.LongPointer[space]]; 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 = Mopcodes.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 = Mopcodes.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; -- SpecialSpace.MakeCodeSwappable[FastBreakImpl]; SpecialSpace.MakeSwappable[space]; Space.Delete[space]; fastBreaksLeft _ MaxFastBreaks; }; }; ClearInternal: INTERNAL PROC [ep: FastBreakEntryPtr, lag: FastBreakEntryPtr _ NIL] = { SELECT CountBreaksAtLocation[ep.code, ep.pc] FROM 0 => RETURN; 1 => { <