-- 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.