-- DebugNub.mesa; edited by Sandman October 9, 1980 9:57 AM -- Copyright Xerox Corporation 1979, 1980 DIRECTORY AltoDefs USING [BYTE], BcplOps USING [BcplJSR], BFSDefs USING [MakeCFP], ControlDefs USING [ ControlLink, FieldDescriptor, FrameHandle, GlobalFrameHandle, NullFrame, StateVector, SVPointer], CoreSwapDefs USING [ BBArray, BBHandle, CallDP, DebugParameter, VersionID, ExternalStateVector, level, PuntInfo, PuntTable, StartDP, SVPointer, SwapReason, UBBPointer, UserBreakBlock], DiskDefs USING [RealDA], ForgotDefs USING [BitmapDS], FrameDefs USING [LockCode, UnlockCode, UnNew], FrameOps USING [ GetReturnFrame, GetReturnLink, MyLocalFrame, SetReturnFrame, SetReturnLink], ImageDefs USING [ AbortMesa, AddCleanupProcedure, AddFileRequest, AllReasons, CleanupItem, CleanupMask, CleanupProcedure, FileRequest, PuntMesa, StopMesa, UserCleanupProc], KeyDefs USING [Keys], LoadStateOps USING [state], MiscOps USING [BitmapPages], Mopcodes USING [zRFS], NubOps USING [Place], NucleusOps USING [Wart], ProcessDefs USING [DisableInterrupts, EnableInterrupts], SDDefs USING [ sBreakBlock, sBreakBlockSize, sCallDebugger, sCoreSwap, SD, sInterrupt, sProcessBreakpoint, sUncaughtSignal], SegmentDefs USING [ DataSegmentHandle, DefaultXMBase, DeleteDataSegment, DeleteFileSegment, FileHandle, FileSegmentHandle, GetFileSegmentDA, InsufficientVM, LongDataSegmentAddress, LongVMtoDataSegment, MakeDataSegment, memConfig, NewFileSegment, Read, ReleaseFile, UnlockFile], SwapperOps USING [systemTable], Storage USING [Node, CopyString, Free, FreeString]; DebugNub: PROGRAM [user: PROGRAM] IMPORTS BFSDefs, CoreSwapDefs, DiskDefs, FrameDefs, FrameOps, ImageDefs, LoadStateOps, BcplOps, NucleusOps, ProcessDefs, SegmentDefs, SwapperOps, Storage EXPORTS CoreSwapDefs, MiscOps, NubOps SHARES SegmentDefs, ControlDefs = BEGIN OPEN CoreSwapDefs, SegmentDefs; WhereAmI: PUBLIC PROCEDURE RETURNS [NubOps.Place] = {RETURN[normal]}; FrameHandle: TYPE = ControlDefs.FrameHandle; SVPointer: TYPE = ControlDefs.SVPointer; ProcessBreakpoint: PROCEDURE [s: SVPointer] = BEGIN -- called by BRK trap handler in resident code inst: AltoDefs.BYTE; swap: BOOLEAN; IF ~Swappable THEN BEGIN SwatBreak[s]; RETURN END; [inst, swap] ← DoBreakpoint[s]; IF swap THEN BEGIN FrameDefs.LockCode[s.dest]; CoreSwap[breakpoint, s]; FrameDefs.UnlockCode[s.dest]; END ELSE s.instbyte ← inst; --replant the instruction and go on RETURN END; DoBreakpoint: PROCEDURE [s: SVPointer] RETURNS [AltoDefs.BYTE, BOOLEAN] = BEGIN OPEN ControlDefs; ubb: CoreSwapDefs.UBBPointer; bba: BBHandle = SDDefs.SD[SDDefs.sBreakBlock]; i: CARDINAL; l: FrameHandle ← s.dest; FOR i IN [0..bba.length) DO ubb ← @bba.blocks[i]; IF ubb.frame = l.accesslink AND ubb.pc = l.pc THEN IF TrueCondition[ubb, l, s] THEN EXIT ELSE RETURN[ubb.inst, FALSE]; ENDLOOP; RETURN[0, TRUE]; END; TrueCondition: PROCEDURE [ubb: CoreSwapDefs.UBBPointer, base: POINTER, s: SVPointer] RETURNS [BOOLEAN] = BEGIN --decide whether to take the breakpoint fd: ControlDefs.FieldDescriptor; locL, locR: POINTER; left, right: UNSPECIFIED; IF ubb.counterL THEN IF (ubb.ptrL ← ubb.ptrL + 1) = ubb.ptrR THEN BEGIN ubb.ptrL ← LOOPHOLE[0]; RETURN[TRUE] END ELSE RETURN[FALSE]; locL ← SELECT TRUE FROM ubb.localL => base + LOOPHOLE[ubb.ptrL, CARDINAL], ubb.stackRelative => @s.stk[LOOPHOLE[ubb.ptrL, CARDINAL]], ENDCASE => ubb.ptrL; fd ← [offset: 0, posn: ubb.posnL, size: ubb.sizeL]; left ← ReadField[locL, fd]; IF ~ubb.immediateR THEN BEGIN fd ← [offset: 0, posn: ubb.posnR, size: ubb.sizeR]; locR ← IF ubb.localR THEN base + LOOPHOLE[ubb.ptrR, CARDINAL] ELSE ubb.ptrR; right ← ReadField[locR, fd]; END ELSE right ← ubb.ptrR; RETURN[ SELECT ubb.relation FROM lt => left < right, gt => left > right, eq => left = right, ne => left # right, le => left <= right, ge => left >= right, ENDCASE => FALSE] END; ReadField: PROCEDURE [POINTER, ControlDefs.FieldDescriptor] RETURNS [UNSPECIFIED] = MACHINE CODE BEGIN Mopcodes.zRFS END; NumberBlocks: CARDINAL = 5; InitBreakBlocks: PROCEDURE = BEGIN OPEN SDDefs; p: CoreSwapDefs.BBHandle ← Storage.Node[ SIZE[UserBreakBlock]*NumberBlocks + SIZE[BBArray]]; SD[sBreakBlock] ← p; SD[sBreakBlockSize] ← SIZE[UserBreakBlock]*NumberBlocks + SIZE[BBArray]; p.length ← 0; RETURN END; SwatBreak: PROCEDURE [s: CoreSwapDefs.SVPointer] = BEGIN OPEN BcplOps; break: RECORD [a, b: WORD]; break ← [77400B, 1400B]; s.instbyte ← BcplJSR[JSR, @break, 0]; RETURN END; Interrupt: PROCEDURE = BEGIN -- called by BRK trap handler in resident code state: ControlDefs.StateVector; state ← STATE; state.dest ← FrameOps.MyLocalFrame[]; CoreSwap[breakpoint, @state]; END; Catcher: PROCEDURE [msg, signal: UNSPECIFIED, frame: FrameHandle] = BEGIN OPEN ControlDefs; SignallerGF: GlobalFrameHandle; state: StateVector; f: FrameHandle; state.stk[0] ← msg; state.stk[1] ← signal; state.stkptr ← 0; -- the call stack below here is: Signaller, [Signaller,] offender f ← FrameOps.GetReturnFrame[]; SignallerGF ← f.accesslink; state.dest ← f ← f.returnlink.frame; IF f.accesslink = SignallerGF THEN state.dest ← f.returnlink; IF ~Swappable THEN BEGIN SwatBreak[@state]; RETURN END; BEGIN CoreSwap[uncaughtsignal, @state ! ABORTED => IF signal = ABORTED THEN GOTO abort]; EXITS abort => {BackStop[frame]; ERROR KillThisTurkey}; END; RETURN END; BackStop: PROCEDURE [root: FrameHandle] = BEGIN endProcess: ControlDefs.ControlLink ← root.returnlink; caller: PROCEDURE = LOOPHOLE[FrameOps.GetReturnLink[]]; root.returnlink ← LOOPHOLE[FrameOps.MyLocalFrame[]]; FrameOps.SetReturnFrame[ControlDefs.NullFrame]; caller[ ! KillThisTurkey => CONTINUE]; FrameOps.SetReturnLink[endProcess]; RETURN END; KillThisTurkey: SIGNAL = CODE; -- The core swapper Quit: SIGNAL = CODE; CantSwap: PUBLIC SIGNAL = CODE; DoSwap: PORT [POINTER TO ExternalStateVector]; parmstring: STRING ← [40]; CoreSwap: PUBLIC PROCEDURE [why: SwapReason, sp: SVPointer] = BEGIN OPEN BcplOps; loadstate: FileSegmentHandle ← LoadStateOps.state; e: ExternalStateVector; DP: DebugParameter; decode: PROCEDURE RETURNS [BOOLEAN] = BEGIN OPEN ControlDefs; -- decode the SwapReason f: GlobalFrameHandle; lsv: StateVector; SELECT e.reason FROM proceed, resume => RETURN[TRUE]; call => BEGIN lsv ← LOOPHOLE[e.parameter, CallDP].sv; lsv.source ← FrameOps.MyLocalFrame[]; TRANSFER WITH lsv; lsv ← STATE; LOOPHOLE[e.parameter, CallDP].sv ← lsv; why ← return; END; start => BEGIN f ← LOOPHOLE[e.parameter, StartDP].frame; IF ~f.started THEN START LOOPHOLE[f, PROGRAM] ELSE RESTART f; why ← return; END; quit => SIGNAL Quit; kill => ImageDefs.AbortMesa[]; showscreen => BEGIN UNTIL KeyDefs.Keys.Spare3 = down DO NULL ENDLOOP; why ← return; END; ENDCASE => BEGIN RETURN[TRUE]; END; RETURN[FALSE] END; -- Body of CoreSwap IF ~Swappable THEN SIGNAL CantSwap; DP.string ← parmstring; e ← [state: sp, reason:, level:, tables: @SwapperOps.systemTable, drumFile: MesaCoreFH, parameter: @DP, versionident: VersionID, loadstateCFA: [fp: loadstate.file.fp, fa: [page: loadstate.base, byte: 0, da: GetFileSegmentDA[loadstate]]], lspages: loadstate.pages, mds: 0, bitmap: debuggerBitmap, bitmapPages: bitmapPages, fill: ALL[0]]; DO e.reason ← why; ImageDefs.UserCleanupProc[OutLd ! ANY => CONTINUE]; ProcessDefs.DisableInterrupts[]; DoSwap[@e]; ProcessDefs.EnableInterrupts[]; ImageDefs.UserCleanupProc[InLd]; BEGIN IF decode[ ! ABORTED => IF e.level > 0 THEN {why ← return; CONTINUE}; Quit => GOTO abort] THEN EXIT EXITS abort => ERROR ABORTED; END; ENDLOOP; RETURN END; -- initialization Swappable: BOOLEAN; puntData: PuntTable; MesaCoreFH: FileHandle ← NIL; FindFiles: PROCEDURE = BEGIN OPEN ControlDefs; f: FileHandle; s: FileSegmentHandle; puntData.puntESV.reason ← punt; puntData.puntESV.tables ← @SwapperOps.systemTable; s ← LoadStateOps.state; puntData.puntESV.versionident ← VersionID; puntData.puntESV.loadstateCFA.fp ← s.file.fp; puntData.puntESV.loadstateCFA.fa ← [page: s.base, byte: 0, da: GetFileSegmentDA[s]]; puntData.puntESV.lspages ← s.pages; puntData.pDebuggerFP ← puntData.pCoreFP ← LOOPHOLE[0]; puntData.puntESV.bitmap ← NIL; puntData.puntESV.bitmapPages ← 0; puntData.puntESV.fill ← ALL[0]; Swappable ← TRUE; IF (f ← requests[core].file) = NIL THEN IF (f ← requests[swatee].file) = NIL THEN Swappable ← FALSE ELSE NULL ELSE IF requests[swatee].file # NIL THEN BEGIN OPEN SegmentDefs; fh: FileHandle = requests[swatee].file; UnlockFile[fh]; ReleaseFile[fh]; END; IF Swappable THEN BEGIN OPEN DiskDefs, SegmentDefs; ENABLE ANY => GOTO bad; puntData.puntESV.drumFile ← MesaCoreFH ← f; s ← NewFileSegment[f, 1, 1, Read]; BFSDefs.MakeCFP[@puntData.coreFP, @f.fp]; puntData.coreFP.leaderDA ← LOOPHOLE[RealDA[GetFileSegmentDA[s]]]; puntData.pCoreFP ← @puntData.coreFP; DeleteFileSegment[s]; IF (f ← requests[debugger].file) = NIL THEN GOTO bad; s ← NewFileSegment[f, 1, 1, Read]; BFSDefs.MakeCFP[@puntData.debuggerFP, @f.fp]; puntData.debuggerFP.leaderDA ← LOOPHOLE[RealDA[GetFileSegmentDA[s]]]; puntData.pDebuggerFP ← @puntData.debuggerFP; UnlockFile[f]; DeleteFileSegment[s]; EXITS bad => Swappable ← FALSE; END ELSE puntData.pDebuggerFP ← NIL; Storage.FreeString[requests[debugger].name]; Storage.FreeString[requests[core].name]; Storage.FreeString[requests[swatee].name]; Storage.Free[requests]; puntData.puntESV.drumFile ← MesaCoreFH; IF bitmapWanted THEN AllocateDebuggerBitmap[MiscOps.BitmapPages]; PuntInfo↑ ← @puntData; RETURN END; RequestType: TYPE = {debugger, core, swatee}; Requests: TYPE = ARRAY RequestType OF ImageDefs.FileRequest; requests: POINTER TO Requests; debuggerBitmap: PUBLIC LONG POINTER ← NIL; bitmapPages: CARDINAL ← 0; bitmapWanted: BOOLEAN ← TRUE; AllocateDebuggerBitmap: PUBLIC PROCEDURE [nPages: CARDINAL] = BEGIN OPEN SegmentDefs; seg: DataSegmentHandle; IF debuggerBitmap # NIL THEN ReleaseDebuggerBitmap[]; bitmapWanted ← TRUE; IF ~memConfig.useXM OR ~Swappable THEN RETURN; seg ← MakeDataSegment[ base: DefaultXMBase, pages: (bitmapPages ← nPages) ! InsufficientVM => {seg ← NIL; CONTINUE}]; IF seg # NIL THEN BEGIN debuggerBitmap ← LongDataSegmentAddress[seg]; seg.type ← ForgotDefs.BitmapDS; puntData.puntESV.bitmap ← debuggerBitmap; puntData.puntESV.bitmapPages ← nPages; END; END; ReleaseDebuggerBitmap: PUBLIC PROCEDURE = BEGIN OPEN SegmentDefs; seg: DataSegmentHandle; IF debuggerBitmap # NIL THEN BEGIN seg ← LongVMtoDataSegment[debuggerBitmap]; debuggerBitmap ← puntData.puntESV.bitmap ← NIL; bitmapPages ← puntData.puntESV.bitmapPages ← 0; DeleteDataSegment[seg]; END; bitmapWanted ← FALSE; END; RequestFiles: PROCEDURE = BEGIN OPEN Storage; requests ← Node[SIZE[Requests]]; requests[debugger] ← [file: NIL, access: Read, link:, name: CopyString["MesaDebugger."L]]; requests[core] ← [file: NIL, access: Read, link:, name: CopyString["MesaCore."L]]; requests[swatee] ← [file: NIL, access: Read, link:, name: CopyString["Swatee."L]]; ImageDefs.AddFileRequest[@requests[debugger]]; ImageDefs.AddFileRequest[@requests[core]]; ImageDefs.AddFileRequest[@requests[swatee]]; END; bypassExec: PUBLIC BOOLEAN ← FALSE; item1: ImageDefs.CleanupItem ← [link:, proc: GoToDebugger, mask: ImageDefs.CleanupMask[Finish] + ImageDefs.CleanupMask[Abort]]; GoToDebugger: ImageDefs.CleanupProcedure = BEGIN IF ~bypassExec THEN RETURN; IF debuggerBitmap = NIL THEN AllocateDebuggerBitmap[MiscOps.BitmapPages]; CoreSwapDefs.level ← -1; bypassExec ← FALSE; CallTheDebugger["You Called?"L]; END; item2: ImageDefs.CleanupItem ← [link:, proc: CleanupNub, mask: ImageDefs.AllReasons]; CleanupNub: ImageDefs.CleanupProcedure = BEGIN save: BOOLEAN ← bitmapWanted; SELECT why FROM Save => {RequestFiles[]; ReleaseDebuggerBitmap[]; bitmapWanted ← save}; Restore => FindFiles[]; Checkpoint => {ReleaseDebuggerBitmap[]; bitmapWanted ← save}; Continue, Restart => IF bitmapWanted THEN AllocateDebuggerBitmap[MiscOps.BitmapPages]; ENDCASE; END; CallTheDebugger: PROCEDURE [s: STRING] = BEGIN -- user's entry point to debugger state: ControlDefs.StateVector; filler0, filler1: CARDINAL; state ← STATE; state.stk[0] ← s; state.stkptr ← 1; state.dest ← FrameOps.GetReturnLink[]; CoreSwap[explicitcall, @state]; IF FALSE THEN filler0 ← filler1 ← 0; RETURN END; SetSD: PROCEDURE = BEGIN OPEN SDDefs; sd: POINTER TO ARRAY [0..0) OF UNSPECIFIED ← SD; sd[sProcessBreakpoint] ← ProcessBreakpoint; sd[sUncaughtSignal] ← Catcher; sd[sInterrupt] ← Interrupt; sd[sCallDebugger] ← CallTheDebugger; END; -- Main body P: TYPE = MACHINE DEPENDENT RECORD [in, out: UNSPECIFIED]; -- PORT LOOPHOLE[DoSwap, P] ← [in: 0, out: SDDefs.SD[SDDefs.sCoreSwap]]; RequestFiles[]; ImageDefs.AddCleanupProcedure[@item1]; START user; STOP; BEGIN ENABLE ANY => ImageDefs.PuntMesa; FindFiles[]; InitBreakBlocks[]; SetSD[]; FrameDefs.UnNew[LOOPHOLE[NucleusOps.Wart]]; END; ImageDefs.AddCleanupProcedure[@item2]; RESTART user[ ! ABORTED => CONTINUE]; ImageDefs.StopMesa[]; END...