-- Resident.Mesa; edited by Sandman on October 9, 1980 7:52 AM -- Copyright Xerox Corporation 1979, 1980 DIRECTORY AltoDefs USING [PageSize], AltoFileDefs USING [CFP], BcplOps USING [BcplInLd, BcplJSR, BcplOutLd], ControlDefs USING [ AV, AVItem, ControlLink, EntryInfo, FrameHandle, FrameVec, GFT, GFTIndex, GFTItem, GlobalFrameHandle, LargeReturnSlot, Port, PrefixHandle, ProcDesc, SpecialReturnSlot, StateVector, TraceNext, TraceOff, TrapStatus], CoreSwapDefs USING [ExternalStateVector, PuntInfo, SVPointer], DiskDefs USING [CBNil, NextDiskCommand], ForgotOps USING [], FrameDefs USING [SwapInCode], FrameOps USING [ GetReturnLink, MyLocalFrame, ReleaseCode, SetReturnFrame, SetReturnLink, Start], ImageDefs USING [AbortMesa, PuntMesa], InlineDefs USING [ BcplLongNumber, BcplToMesaLongNumber, BITOR, MesaToBcplLongNumber], KeyDefs USING [KeyBits, Keys, updown], Mopcodes USING [zBRK, zKFCB, zPOP, zRBL, zSTARTIO], NucleusOps USING [InterruptPriority], OsStaticDefs USING [OsStatics], ProcessDefs USING [DisableInterrupts, EnableInterrupts, Priority], ProcessOps USING [ ActiveWord, FirstStateVector, ReadWDC, WakeupsWaiting, WriteWDC], Runtime USING [], SDDefs USING [ sAllocTrap, SD, sInterrupt, sIOResetBits, sProcessBreakpoint, sXferTrap], SDOps USING [], SegmentDefs USING [ DataSegmentAddress, DataSegmentHandle, DefaultMDSBase, DeleteDataSegment, FrameDS, MakeDataSegment], TrapDefs USING [], TrapOps USING [ReadATP, ReadOTP, ReadXTS, WriteXTS]; Resident: MONITOR IMPORTS FrameDefs, FrameOps, ImageDefs, InlineDefs, BcplOps, ProcessDefs, ProcessOps, SegmentDefs, TrapOps EXPORTS CoreSwapDefs, ForgotOps, FrameOps, NucleusOps, Runtime, SDOps, TrapDefs = BEGIN OPEN ControlDefs; -- allocation of frame space LargeFrameSlot: CARDINAL = 12; FrameSize: PUBLIC PROCEDURE [fsi: CARDINAL] RETURNS [CARDINAL] = BEGIN RETURN[FrameVec[fsi]] END; pgft: TYPE = POINTER TO ARRAY [0..0) OF GFTItem; ItemPointer: TYPE = POINTER TO ControlDefs.AVItem; FrameSegment: TYPE = MACHINE DEPENDENT RECORD [ segment: SegmentDefs.DataSegmentHandle, link: POINTER TO FrameSegment, size, fsi: CARDINAL]; -- maintain a list of all new "permanent" frame segments; SegHeader: PUBLIC TYPE = RECORD [ seg: SegmentDefs.DataSegmentHandle, link: pSegHeader]; pSegHeader: PUBLIC TYPE = POINTER TO SegHeader; SegListHead: PUBLIC pSegHeader ← NIL; ExtraSpaceSize: CARDINAL = 170B; ExtraSpace: ARRAY [0..ExtraSpaceSize) OF WORD; InitNewSpace: POINTER = LOOPHOLE[InlineDefs.BITOR[BASE[ExtraSpace], 3]]; InitWordsLeft: CARDINAL = BASE[ExtraSpace] + ExtraSpaceSize - InitNewSpace; NULLPtr: FrameHandle = LOOPHOLE[0]; AllocTrap: PUBLIC PROCEDURE [otherframe: FrameHandle] RETURNS [myframe: FrameHandle] = BEGIN OPEN SegmentDefs; ATFrame: TYPE = POINTER TO FRAME[AllocTrap]; state: StateVector; newframe: FrameHandle; newseg: DataSegmentHandle; long: BOOLEAN; i, fsize, fIndex: CARDINAL; p: POINTER; newG: GlobalFrameHandle; NewSpacePtr: POINTER; WordsLeft: CARDINAL ← 0; recurring: BOOLEAN ← otherframe = NULLPtr; alloc: BOOLEAN; dest, tempdest: ControlLink; gfi: GFTIndex; ep: CARDINAL; myframe ← FrameOps.MyLocalFrame[]; state.dest ← myframe.returnlink; state.source ← 0; state.instbyte ← 0; state.stk[0] ← myframe; state.stkptr ← 1; ProcessDefs.DisableInterrupts[]; -- so that undo below works DO ENABLE ANY => ImageDefs.PuntMesa[]; IF ~recurring THEN BEGIN LOOPHOLE[otherframe, ATFrame].NewSpacePtr ← InitNewSpace; LOOPHOLE[otherframe, ATFrame].WordsLeft ← InitWordsLeft; AV[SpecialReturnSlot] ← [data[0, empty]]; END; -- the following RR and POP is to guarantee that there is no NOOP between -- the DWDC and the LST [] ← TrapOps.ReadATP[]; ProcessDefs.EnableInterrupts[]; TRANSFER WITH state; ProcessDefs.DisableInterrupts[]; state ← STATE; dest ← TrapOps.ReadATP[]; SDDefs.SD[SDDefs.sAllocTrap] ← otherframe; myframe.returnlink ← state.source; tempdest ← dest; DO SELECT tempdest.tag FROM frame => BEGIN alloc ← TRUE; fIndex ← LOOPHOLE[tempdest, CARDINAL]/4; EXIT END; procedure => BEGIN OPEN proc: LOOPHOLE[tempdest, ProcDesc]; gfi ← proc.gfi; ep ← proc.ep; [frame: newG, epbase: fIndex] ← GFT[gfi]; -- use fIndex as temp long ← newG.code.highByte = 0; IF long THEN BEGIN GetEntryInfo: PROCEDURE [LONG POINTER] RETURNS [EntryInfo] = MACHINE CODE BEGIN Mopcodes.zRBL, 1 END; info: EntryInfo ← GetEntryInfo[ @LOOPHOLE[newG.code.longbase, LONG PrefixHandle].entry[ fIndex + ep]]; fIndex ← info.framesize; END ELSE fIndex ← LOOPHOLE[newG.code.shortbase, PrefixHandle].entry[ fIndex + ep].info.framesize; alloc ← FALSE; EXIT END; indirect => tempdest ← tempdest.link↑; ENDCASE => ImageDefs.PuntMesa[]; ENDLOOP; IF ~recurring THEN FlushLargeFrames[] ELSE IF (p ← AV[SpecialReturnSlot].link) # LOOPHOLE[AVItem[data[0, empty]]] THEN BEGIN WordsLeft ← WordsLeft + (NewSpacePtr - p + 1); NewSpacePtr ← p - 1; AV[SpecialReturnSlot] ← [data[0, empty]]; END; IF fIndex < LargeFrameSlot THEN BEGIN fsize ← FrameVec[fIndex] + 1; -- includes overhead word THROUGH [0..1] DO p ← NewSpacePtr + 1; IF fsize <= WordsLeft THEN BEGIN newframe ← p; (p - 1)↑ ← IF recurring THEN SpecialReturnSlot ELSE fIndex; WordsLeft ← WordsLeft - fsize; NewSpacePtr ← NewSpacePtr + fsize; EXIT; END ELSE BEGIN IF recurring THEN ImageDefs.PuntMesa[]; FOR i DECREASING IN [0..fIndex) DO IF FrameVec[i] < WordsLeft THEN BEGIN (p - 1)↑ ← i; p↑ ← AV[i].link; AV[i].link ← p; EXIT; END; ENDLOOP; newseg ← MakeDataSegment[DefaultMDSBase, 1, [hard, topdown, frame]]; newseg.type ← FrameDS; NewSpacePtr ← (p ← DataSegmentAddress[newseg]) + 3; LOOPHOLE[p, pSegHeader]↑ ← [newseg, SegListHead]; SegListHead ← p; WordsLeft ← AltoDefs.PageSize - 3; END; ENDLOOP END ELSE BEGIN fsize ← FrameVec[fIndex]; p ← DataSegmentAddress[ newseg ← MakeDataSegment[ base: DefaultMDSBase, info: [hard, topdown, frame], pages: (fsize + AltoDefs.PageSize + 3)/AltoDefs.PageSize]]; newseg.type ← FrameDS; newframe ← p + 4; LOOPHOLE[p, POINTER TO FrameSegment]↑ ← [segment: newseg, link: NIL, size: fsize, fsi: LargeReturnSlot]; END; IF alloc THEN BEGIN state.dest ← myframe.returnlink; state.stk[state.stkptr] ← newframe; state.stkptr ← state.stkptr + 1; END ELSE BEGIN state.dest ← dest; newframe.accesslink ← LOOPHOLE[AV[fIndex].link]; AV[fIndex].frame ← newframe; state.source ← myframe.returnlink; END; SDDefs.SD[SDDefs.sAllocTrap] ← myframe; ENDLOOP; END; -- FlushLargeFrames' frame must be larger than MDSRegion Update's frame FlushLargeFrames: PUBLIC PROCEDURE = BEGIN p: ARRAY [0..10) OF POINTER; -- ensure large enough frame item: ItemPointer ← @AV[LargeReturnSlot]; WHILE item.tag = frame DO p[0] ← item.frame; item.frame ← p[0]↑; SegmentDefs.DeleteDataSegment[LOOPHOLE[(p[0] - 4)↑]]; ENDLOOP; END; -- other traps UnboundProcedure: PUBLIC SIGNAL [dest: ControlLink] RETURNS [ControlLink] = CODE; UnboundProcedureTrap: PUBLIC PROCEDURE = BEGIN dest: ControlLink; state: StateVector; ProcessDefs.DisableInterrupts[]; state ← STATE; dest ← TrapOps.ReadOTP[]; ProcessDefs.EnableInterrupts[]; state.source ← FrameOps.GetReturnLink[]; state.dest ← SIGNAL UnboundProcedure[dest]; RETURN WITH state END; CodeTrap: PUBLIC PROCEDURE = BEGIN dest: ControlLink; state: StateVector; frame: GlobalFrameHandle; ProcessDefs.DisableInterrupts[]; state ← STATE; dest ← TrapOps.ReadOTP[]; ProcessDefs.EnableInterrupts[]; state.dest ← dest; state.source ← FrameOps.GetReturnLink[]; DO SELECT dest.tag FROM frame => BEGIN frame ← dest.frame.accesslink; EXIT END; procedure => BEGIN frame ← GFT[dest.gfi].frame; EXIT END; ENDCASE => dest ← dest.link↑; ENDLOOP; IF ~frame.started THEN FrameOps.Start[[frame[frame]]]; FrameDefs.SwapInCode[frame]; FrameOps.ReleaseCode[frame]; RETURN WITH state; END; -- Parity Errors ParityError: PUBLIC SIGNAL [address: POINTER] = CODE; PhantomParityError: PUBLIC SIGNAL = CODE; parityWakeup: PUBLIC CONDITION; ParityProcess: PUBLIC ENTRY PROCEDURE = BEGIN OPEN ProcessOps; p: ORDERED POINTER; ww: POINTER TO MACHINE DEPENDENT RECORD [ other: [0..77777B], parity: BOOLEAN] ← LOOPHOLE[WakeupsWaiting]; POP: PROCEDURE [WORD] = MACHINE CODE BEGIN Mopcodes.zPOP END; DO -- forever WAIT parityWakeup; ActiveWord↑ ← 0; FOR p DECREASING IN [LOOPHOLE[0]..LOOPHOLE[177000B]) DO POP[p↑]; IF ww.parity THEN BEGIN SIGNAL ParityError[p]; EXIT END; REPEAT FINISHED => SIGNAL PhantomParityError; ENDLOOP; ww.parity ← FALSE; ActiveWord↑ ← 77777B; ENDLOOP; END; -- Getting the Debugger level: PUBLIC INTEGER; StartIO: PROCEDURE [CARDINAL] = MACHINE CODE BEGIN Mopcodes.zSTARTIO END; CSPort: PORT RETURNS [POINTER TO CoreSwapDefs.ExternalStateVector]; WBPort: PORT [POINTER TO CoreSwapDefs.ExternalStateVector]; MemorySwap: PROCEDURE [ESV: POINTER TO CoreSwapDefs.ExternalStateVector] = BEGIN savewdc: UNSPECIFIED; xferTrapStatus: UNSPECIFIED; xferTrapHandler: UNSPECIFIED; flag: [0..2]; DO ESV ← CSPort[]; FrameOps.SetReturnLink[LOOPHOLE[CSPort, Port].dest]; StartIO[SDDefs.SD[SDDefs.sIOResetBits]]; -- reset IO devices ESV.level ← level; xferTrapStatus ← TrapOps.ReadXTS[]; xferTrapHandler ← SDDefs.SD[SDDefs.sXferTrap]; SDDefs.SD[SDDefs.sXferTrap] ← FrameOps.MyLocalFrame[]; TrapOps.WriteXTS[TraceOff]; UNTIL DiskDefs.NextDiskCommand↑ = DiskDefs.CBNil DO NULL ENDLOOP; savewdc ← ProcessOps.ReadWDC[]; flag ← BcplOps.BcplOutLd[OutLd, CoreSwapDefs.PuntInfo↑.pCoreFP, ESV]; ProcessOps.WriteWDC[savewdc]; SELECT flag FROM 0 => BcplOps.BcplInLd[InLd, CoreSwapDefs.PuntInfo↑.pDebuggerFP, ESV]; 1 => level ← ESV.level; ENDCASE => ESV.reason ← proceed; TrapOps.WriteXTS[xferTrapStatus]; SDDefs.SD[SDDefs.sXferTrap] ← xferTrapHandler; ENDLOOP; END; Break: PUBLIC PROCEDURE = -- executed by (non-worry) BRK instruction BEGIN ProcessBreakpoint: PROCEDURE [CoreSwapDefs.SVPointer] = MACHINE CODE BEGIN Mopcodes.zKFCB, SDDefs.sProcessBreakpoint END; f: FrameHandle; state: StateVector; xferTrapStatus: TrapStatus; state ← STATE; state.dest ← f ← state.source; state.source ← FrameOps.MyLocalFrame[]; f.pc ← [IF f.pc < 0 THEN -f.pc ELSE (1 - f.pc)]; ProcessBreakpoint[@state]; xferTrapStatus ← TrapOps.ReadXTS[]; IF xferTrapStatus.state = on THEN TrapOps.WriteXTS[TraceNext]; RETURN WITH state END; -- Worry mode breakpoints WorryBreaker: PUBLIC PROCEDURE RETURNS [FrameHandle] = BEGIN state: StateVector; frame: FrameHandle; esv: CoreSwapDefs.ExternalStateVector; xferTrapStatus: TrapStatus; state.instbyte ← 0; state.stkptr ← 1; state.stk[0] ← FrameOps.MyLocalFrame[]; state.dest ← FrameOps.GetReturnLink[]; ProcessDefs.DisableInterrupts[]; ProcessDefs.DisableInterrupts[]; DO xferTrapStatus ← TrapOps.ReadXTS[]; IF xferTrapStatus.state = on THEN TrapOps.WriteXTS[TraceNext]; -- previous WR is aligned so now 2 DWDCs to prevent a NOOP ProcessDefs.EnableInterrupts[]; ProcessDefs.EnableInterrupts[]; TRANSFER WITH state; ProcessDefs.DisableInterrupts[]; ProcessDefs.DisableInterrupts[]; state ← STATE; frame ← state.dest ← state.source; FrameOps.SetReturnFrame[frame]; state.source ← FrameOps.MyLocalFrame[]; frame.pc ← [IF frame.pc < 0 THEN -frame.pc ELSE (1 - frame.pc)]; esv ← CoreSwapDefs.PuntInfo↑.puntESV; esv.state ← @state; esv.reason ← worrybreak; DO OPEN KeyDefs; WBPort[@esv]; SELECT esv.reason FROM proceed => EXIT; kill => ImageDefs.AbortMesa[]; showscreen => UNTIL Keys.Spare3 = down OR Keys.FR5 = down DO NULL ENDLOOP; ENDCASE; esv.reason ← return; ENDLOOP; ENDLOOP; END; WorryCallDebugger: PUBLIC PROCEDURE RETURNS [FrameHandle] = BEGIN state: StateVector; esv: CoreSwapDefs.ExternalStateVector; state.instbyte ← 0; state.stkptr ← 1; state.stk[0] ← FrameOps.MyLocalFrame[]; state.dest ← FrameOps.GetReturnLink[]; ProcessDefs.DisableInterrupts[]; DO -- the following RR and POP is to guarantee that there is no NOOP between -- the DWDC and the LST [] ← TrapOps.ReadATP[]; ProcessDefs.EnableInterrupts[]; TRANSFER WITH state; ProcessDefs.DisableInterrupts[]; state ← STATE; state.dest ← state.source; FrameOps.SetReturnFrame[state.dest]; state.source ← FrameOps.MyLocalFrame[]; esv ← CoreSwapDefs.PuntInfo↑.puntESV; esv.state ← @state; esv.reason ← worrycall; DO OPEN KeyDefs; WBPort[@esv]; SELECT esv.reason FROM proceed => EXIT; kill => ImageDefs.AbortMesa[]; showscreen => UNTIL Keys.Spare3 = down OR Keys.FR5 = down DO NULL ENDLOOP; ENDCASE; esv.reason ← return; ENDLOOP; ENDLOOP; END; interruptWakeup: PUBLIC CONDITION; GetDebugger: PROCEDURE = MACHINE CODE BEGIN Mopcodes.zKFCB, SDDefs.sInterrupt END; TimeData: POINTER TO TimeDataRecord = LOOPHOLE[572B]; TimeDataRecord: TYPE = MACHINE DEPENDENT RECORD [ timeOfDay: InlineDefs.BcplLongNumber, milliSeconds: InlineDefs.BcplLongNumber, timeBase: InlineDefs.BcplLongNumber]; disableInterrupt: PUBLIC BOOLEAN; InterruptProcess: PUBLIC ENTRY PROCEDURE = BEGIN keys: POINTER TO KeyDefs.KeyBits ← KeyDefs.Keys; interruptState: KeyDefs.updown ← up; pp: ProcessDefs.Priority; SV: POINTER TO ARRAY ProcessDefs.Priority OF ControlDefs.StateVector = LOOPHOLE[ProcessOps.FirstStateVector↑]; timeData: POINTER TO TimeDataRecord ← TimeData; RealTimeClock: POINTER TO CARDINAL = LOOPHOLE[430B]; clockSecond: POINTER TO InlineDefs.BcplLongNumber; DO WAIT interruptWakeup[ ! ABORTED => CONTINUE]; IF disableInterrupt THEN EXIT; clockSecond ← OsStaticDefs.OsStatics.ClockSecond; IF RealTimeClock↑ - timeData.timeBase.highbits > clockSecond.highbits THEN BEGIN OPEN InlineDefs; timeData.timeOfDay ← MesaToBcplLongNumber[ BcplToMesaLongNumber[timeData.timeOfDay] + 1]; timeData.milliSeconds ← MesaToBcplLongNumber[ BcplToMesaLongNumber[timeData.milliSeconds] + 1000]; timeData.timeBase ← MesaToBcplLongNumber[ BcplToMesaLongNumber[timeData.timeBase] + BcplToMesaLongNumber[ clockSecond↑]]; END; -- The following code checks for Shft-Swat, the swat interrupt keys. IF keys.LeftShift = down AND keys.Spare3 = down THEN BEGIN ProcessDefs.DisableInterrupts[]; UNTIL DiskDefs.NextDiskCommand↑ = DiskDefs.CBNil DO NULL ENDLOOP; IF keys.Ctrl = down THEN BEGIN code: ARRAY [0..2) OF WORD ← [77410B, 1400B]; [] ← BcplOps.BcplJSR[code: JSR, address: @code, arg: NIL]; ProcessDefs.EnableInterrupts[]; END ELSE BEGIN SwatVector: POINTER TO POINTER TO RECORD [ fill: ARRAY [0..4) OF WORD, swatee: AltoFileDefs.CFP] = LOOPHOLE[567B]; [] ← BcplOps.BcplOutLd[ code: OutLd, file: @SwatVector.swatee, message: NIL]; ProcessDefs.EnableInterrupts[]; ImageDefs.AbortMesa[]; END; END -- The following code checks for Ctrl-Swat, the debugger interrupt keys. ELSE IF keys.Ctrl = down AND (keys.Spare3 = down OR keys.FR5 = down) THEN BEGIN IF interruptState = up AND CoreSwapDefs.PuntInfo↑ # LOOPHOLE[0] AND CoreSwapDefs.PuntInfo.pDebuggerFP # NIL THEN BEGIN interruptState ← down; FOR pp IN [0..NucleusOps.InterruptPriority) DO SV[pp].instbyte ← Mopcodes.zBRK; WAIT interruptWakeup; IF SV[pp].instbyte = 0 THEN EXIT ELSE SV[pp].instbyte ← 0; REPEAT FINISHED => GetDebugger[ ! ABORTED => CONTINUE]; ENDLOOP; END; END ELSE interruptState ← up; ENDLOOP; END; END.