-- Miscellaneous.mesa; edited by Levin, July 25, 1979 9:39 AM -- edited for XMesa by Levin, April 18, 1979 12:53 PM DIRECTORY AllocDefs: FROM "allocdefs" USING [AllocHandle, AllocInfo, GetAllocationObject, MakeSwappedIn], AltoDefs: FROM "altodefs" USING [MaxVMPage, PageNumber, PageSize], AltoFileDefs: FROM "altofiledefs" USING [TIME], BcdDefs: FROM "bcddefs" USING [ NullLink, NullVersion, VersionID, VersionStamp, UnboundLink], BitBltDefs: FROM "bitbltdefs" USING [BBTable, BBptr, BITBLT, LongBBTable], BootDefs: FROM "bootdefs" USING [EnumerateObjects], ControlDefs: FROM "controldefs" USING [ FrameHandle, GFT, GFTIndex, GlobalFrameHandle, NullEpBase, NullGlobalFrame], FrameDefs: FROM "framedefs" USING [ EnumerateGlobalFrames, GlobalFrame, RemoveGlobalFrame, SwapInCode, SwapOutCode, UnNew, ValidateGlobalFrame], FrameOps: FROM "frameops" USING [ CodeHandle, Free, GetReturnFrame, SetReturnLink], ImageDefs: FROM "imagedefs" USING [ AbortMesa, CleanupItem, CleanupMask, CleanupProcedure, StopMesa], ImageFormat: FROM "imageformat" USING [ImageHeader, VersionID], InlineDefs: FROM "inlinedefs" USING [ BITAND, BITOR, BITSHIFT, COPY, HighHalf, LongNumber, LowHalf], MemoryOps: FROM "memoryops" USING [ BankIndex, Direction, DisableBank, EnableBank, MemoryConfig, memoryConfig], MiscDefs: FROM "miscdefs" USING [DAYTIME], Mopcodes: FROM "mopcodes" USING [zMISC, zSTARTIO], NovaOps: FROM "novaops" USING [NovaJSR], NucleusDefs: FROM "nucleusdefs" USING [Resident], OsStaticDefs: FROM "osstaticdefs" USING [OsStatics], ProcessDefs: FROM "processdefs" USING [DisableInterrupts, EnableInterrupts], SDDefs: FROM "sddefs" USING [SD, sGoingAway], SegmentDefs: FROM "segmentdefs" USING [ DataSegmentHandle, DefaultBase, DeleteFileSegment, EnumerateFileSegments, FileSegmentAddress, FileSegmentHandle, MoveFileSegment, NewFileSegment, ObjectHandle, Read, SegmentHandle, SwapError, SwapIn, Unlock], XMESA: FROM "xmesaops" USING [ BankMasks, ChocolateToVanilla, SwapOutFileSegment, VanillaToChocolate, XDataSegmentHandle, XFileSegmentHandle, XMremote, XObject], XMesaDefs: FROM "xmesadefs" USING [ DefaultMDSBase, DefaultXMBase, LongAddressFromPage, PagesPerBank, XCOPY]; Miscellaneous: PROGRAM IMPORTS AllocDefs, BitBltDefs, BootDefs, FrameDefs, FrameOps, ImageDefs, InlineDefs, MiscDefs, MemoryOps, NovaOps, NucleusDefs, ProcessDefs, SegmentDefs, XMESA, XMesaDefs EXPORTS FrameDefs, FrameOps, ImageDefs, MemoryOps, MiscDefs, NucleusDefs, XMesaDefs SHARES MemoryOps, SegmentDefs, XMESA = PUBLIC BEGIN OPEN ControlDefs, MemoryOps; DeletedFrame: PROCEDURE [gfi: GFTIndex] RETURNS [BOOLEAN] = BEGIN RETURN[GFT[gfi] = [frame: NullGlobalFrame, epbase: NullEpBase]]; END; LockCode: PROCEDURE [link: UNSPECIFIED] = BEGIN FrameDefs.SwapInCode[FrameDefs.GlobalFrame[link]]; RETURN END; UnlockCode: PROCEDURE [link: UNSPECIFIED] = BEGIN OPEN FrameDefs; seg: SegmentDefs.FileSegmentHandle ← CodeHandle[GlobalFrame[link]]; IF seg # NIL THEN SegmentDefs.Unlock[seg]; RETURN END; CodeHandle: PROCEDURE [frame: GlobalFrameHandle] RETURNS [seg: SegmentDefs.FileSegmentHandle] = BEGIN FrameDefs.ValidateGlobalFrame[frame]; RETURN[IF frame.code.highByte = 0 THEN XVMtoFileSegment[frame.code.longbase] --XM ELSE frame.code.handle] END; MoveCode: PROCEDURE [direction: Direction] = --XM BEGIN OPEN SegmentDefs; alloc: AllocDefs.AllocHandle ← AllocDefs.GetAllocationObject[]; CheckOne: PROCEDURE[fseg: FileSegmentHandle] RETURNS[BOOLEAN] = BEGIN OPEN seg: LOOPHOLE[fseg, XMESA.XFileSegmentHandle]; ChangeFlavorProc: TYPE = PROCEDURE[newVMpage: AltoDefs.PageNumber] RETURNS[AltoDefs.PageNumber]; MoveThisSegment: PROCEDURE[basePage: AltoDefs.PageNumber, proc: ChangeFlavorProc] = BEGIN OPEN XMesaDefs; ResidentCodeInfo: AllocDefs.AllocInfo = [0, hard, bottomup, initial, code, TRUE, FALSE]; oldVMpage, newVMpage: AltoDefs.PageNumber; delta: LONG CARDINAL; UpdateCodebase: PROCEDURE[f: GlobalFrameHandle] RETURNS[BOOLEAN] = BEGIN IF CodeHandle[f] = fseg AND ~f.code.out THEN SELECT direction FROM outofMDS => f.code.longbase ← LONG[f.code.shortbase]+delta; intoMDS => BEGIN f.code.longbase ← f.code.longbase+delta; IF f.code.highHalf # 0 THEN ERROR; f.code.handle ← fseg; END; ENDCASE; RETURN[FALSE] END; -- body of MoveThisSegment SwapIn[fseg]; newVMpage ← alloc.alloc[basePage, fseg.pages, fseg, ResidentCodeInfo]; ProcessDefs.DisableInterrupts[]; oldVMpage ← proc[newVMpage]; XCOPY[from: LongAddressFromPage[oldVMpage], to: LongAddressFromPage[newVMpage], nwords: AltoDefs.PageSize*fseg.pages]; delta ← AltoDefs.PageSize * (LONG[newVMpage] - LONG[oldVMpage]); [] ← FrameDefs.EnumerateGlobalFrames[UpdateCodebase]; alloc.update[oldVMpage, fseg.pages, free, NIL]; alloc.update[newVMpage, fseg.pages, inuse, fseg]; ProcessDefs.EnableInterrupts[]; Unlock[fseg]; END; -- body of CheckOne IF seg.class = code AND seg.lock > 0 THEN WITH s:seg SELECT FROM disk => SELECT direction FROM outofMDS => BEGIN VtC: ChangeFlavorProc = BEGIN RETURN[XMESA.VanillaToChocolate[fseg, newVMpage]] -- note: variant changes here END; MoveThisSegment[XMesaDefs.DefaultXMBase, VtC]; END; intoMDS => NULL; ENDCASE; remote => IF s.proc = XMESA.XMremote THEN SELECT direction FROM outofMDS => NULL; intoMDS => BEGIN CtV: ChangeFlavorProc = BEGIN RETURN[XMESA.ChocolateToVanilla[@seg, newVMpage]] -- note: variant changes here END; MoveThisSegment[XMesaDefs.DefaultMDSBase, CtV]; END; ENDCASE; ENDCASE; RETURN[FALSE] END; -- body of MoveCode IF ~memConfig.useXM THEN RETURN; [] ← EnumerateFileSegments[CheckOne]; END; -- EnableBank and DisableBank moved to the Swapper -- --XM EnableHyperspace: PROCEDURE = BEGIN i: CARDINAL; --XM IF memConfig.useXM THEN --XM FOR i IN (FIRST[BankIndex]..LAST[BankIndex]] DO --XM IF InlineDefs.BITAND[XMESA.BankMasks[i], memConfig.banks] # 0 THEN EnableBank[i]; --XM ENDLOOP; --XM END; --XM ImmovableSegmentInXM: PUBLIC SIGNAL[SegmentDefs.SegmentHandle] = CODE; --XM DisableHyperspace: PROCEDURE = --XM BEGIN --XM i: BankIndex; --XM XSegment: PROCEDURE [op: SegmentDefs.ObjectHandle] RETURNS [BOOLEAN] = --XM BEGIN OPEN objptr: LOOPHOLE[op, POINTER TO XMESA.XObject]; --XM WITH s: objptr SELECT FROM --XM segment => --XM WITH xs: s SELECT FROM --XM data => IF xs.VMpage = 0 THEN SIGNAL ImmovableSegmentInXM[LOOPHOLE[@s]]; --XM file => --XM WITH x: xs SELECT FROM --XM remote => --XM IF x.proc = XMESA.XMremote THEN --XM IF xs.lock # 0 THEN SIGNAL ImmovableSegmentInXM[LOOPHOLE[@s]] --XM ELSE XMESA.SwapOutFileSegment[LOOPHOLE[@xs, SegmentDefs.FileSegmentHandle]]; --XM ENDCASE; --XM ENDCASE; --XM ENDCASE; --XM RETURN[FALSE] --XM END; --XM FOR i IN (FIRST[BankIndex]..LAST[BankIndex]] DO DisableBank[i]; ENDLOOP; --XM [] ← BootDefs.EnumerateObjects[segment, XSegment]; --XM RETURN END; --XM IsXMesa: PROCEDURE RETURNS [BOOLEAN] = BEGIN RETURN[TRUE] END; --XM GetMDS: PROCEDURE RETURNS[BankIndex] = BEGIN RETURN[memConfig.mdsBank] END; SetMDS: PROCEDURE[mds: BankIndex] = BEGIN memConfig.mdsBank ← mds; END; -- D0/Dorado hyperspace stuff MapFlags: TYPE = MACHINE DEPENDENT RECORD [ LogSE, W, D, Ref: BOOLEAN]; VacantFlags: MapFlags = [FALSE, TRUE, TRUE, FALSE]; WriteProtectedFlags: MapFlags = [FALSE, TRUE, FALSE, FALSE]; CleanFlags: MapFlags = [FALSE, FALSE, FALSE, FALSE]; MapEntry: TYPE = MACHINE DEPENDENT RECORD [ flags: MapFlags, realPage: [0..7777B]]; Vacant: MapEntry = [VacantFlags,0]; WriteProtected: MapEntry = [WriteProtectedFlags,0]; Clean: MapEntry = [CleanFlags,0]; ASSOC: PROCEDURE [CARDINAL, MapEntry] = MACHINE CODE BEGIN Mopcodes.zMISC, 0 END; SETF: PROCEDURE [CARDINAL, MapEntry] RETURNS [MapEntry] = MACHINE CODE BEGIN Mopcodes.zMISC, 1 END; memConfig: MemoryConfig; InitMemoryConfig: PROCEDURE = BEGIN t: MapEntry; memConfig ← memoryConfig↑; IF memConfig.AltoType = D0 OR memConfig.AltoType = Dorado THEN BEGIN bankSize: CARDINAL = 256; pageCount: CARDINAL; banks: WORD ← 0; bit: WORD ← 10B; FOR pageCount ← bankSize, pageCount+1 DO t ← SETF[pageCount, Clean]; IF t = Vacant THEN EXIT; ENDLOOP; WHILE pageCount >= bankSize AND bit # 0 DO banks ← InlineDefs.BITOR[banks, bit]; bit ← InlineDefs.BITSHIFT[bit, -1]; pageCount ← pageCount - bankSize; ENDLOOP; IF banks # 10B THEN BEGIN memConfig.banks ← banks; memConfig.useXM ← TRUE; END; END; END; GetMemoryConfig: PROCEDURE RETURNS [MemoryConfig] = BEGIN RETURN[memConfig] END; -- FrameOK deleted - unnecessary in XMesa -- --XM -- Procedures Exported to XMesaDefs -- --XM InvalidLongPointer: ERROR [ptr: LONG POINTER] = CODE; PAGEDISP: TYPE = MACHINE DEPENDENT RECORD[ page: [0..AltoDefs.MaxVMPage], disp: [0..AltoDefs.PageSize)]; -- LongAddressFromPage is in the Swapper (it must be resident for SwapInCode) -- PageFromLongAddress: PROCEDURE[lp: LONG POINTER] RETURNS[page: AltoDefs.PageNumber] = BEGIN bank: BankIndex ← InlineDefs.HighHalf[lp]; IF bank ~IN BankIndex THEN ERROR InvalidLongPointer[lp]; RETURN[bank*XMesaDefs.PagesPerBank+LOOPHOLE[InlineDefs.LowHalf[lp],PAGEDISP].page]; END; XVMtoSegment: PROCEDURE [a: LONG POINTER] RETURNS [SegmentDefs.SegmentHandle] = BEGIN RETURN[AllocDefs.GetAllocationObject[].status[PageFromLongAddress[a]].seg] END; XSegmentAddress: PROCEDURE [seg: SegmentDefs.SegmentHandle] RETURNS [lp: LONG POINTER] = BEGIN WITH s: seg SELECT FROM data => lp ← XDataSegmentAddress[@s]; file => lp ← IF ~s.swappedin THEN NIL ELSE XFileSegmentAddress[@s]; ENDCASE END; XVMtoDataSegment: PROCEDURE [a: LONG POINTER] RETURNS [SegmentDefs.DataSegmentHandle] = BEGIN seg: SegmentDefs.SegmentHandle ← XVMtoSegment[a]; IF seg # NIL THEN WITH ds: seg SELECT FROM data => RETURN[@ds]; ENDCASE; RETURN[NIL] END; XDataSegmentAddress: PROCEDURE [seg:SegmentDefs.DataSegmentHandle] RETURNS [LONG POINTER] = BEGIN OPEN s: LOOPHOLE[seg, XMESA.XDataSegmentHandle]; vmpage: AltoDefs.PageNumber ← IF s.VMpage # 0 THEN seg.VMpage ELSE s.XMpage; RETURN[LOOPHOLE[LONG[vmpage]*AltoDefs.PageSize]] END; XVMtoFileSegment: PROCEDURE [a: LONG POINTER] RETURNS [SegmentDefs.FileSegmentHandle] = BEGIN seg: SegmentDefs.SegmentHandle ← XVMtoSegment[a]; IF seg # NIL THEN WITH fs: seg SELECT FROM file => RETURN[@fs]; ENDCASE; RETURN[NIL] END; XFileSegmentAddress: PROCEDURE [seg: SegmentDefs.FileSegmentHandle] RETURNS [LONG POINTER] = BEGIN vmpage: AltoDefs.PageNumber ← seg.VMpage; IF ~seg.swappedin THEN ERROR SegmentDefs.SwapError[seg]; WITH xs: LOOPHOLE[seg, XMESA.XFileSegmentHandle] SELECT FROM remote => IF xs.proc = XMESA.XMremote THEN vmpage ← xs.info.XMpage; ENDCASE; RETURN[XMesaDefs.LongAddressFromPage[vmpage]] END; NovaBBArgBlock: TYPE = MACHINE DEPENDENT RECORD [ fill: [0..37777B], bank: BankIndex, bbt: BitBltDefs.BBptr ]; NovaCode: ARRAY [0..16) OF CARDINAL ← [ 54415B, -- STA 3 saveret 115000B, -- MOV 0 3 AC3: @arg 22415B, -- LDA 0 @bankno 40413B, -- STA 0 savebank savebank ← EmulatorBankRegister 21400B, -- LDA 0 0,3 42412B, -- STA 0 @bankno EmulatorBankRegister ← arg.bank 31401B, -- LDA 2 1,3 AC2: arg.bbt 126400B, -- SUB 1 1 AC1: 0 61024B, -- BITBLT 20405B, -- LDA 0 savebank 42405B, -- STA 0 @bankno EmulatorBankRegister ← savebank 34402B, -- LDA 3 saveret 1400B, -- JMP 0 3 0B, -- saveret: 0 0B, -- savebank: 0 177740B -- bankno: 177740 ]; XMNotAvailable: ERROR = CODE; XBitBlt: PROCEDURE[bbt: BitBltDefs.BBptr, bank: BankIndex] = BEGIN OPEN BitBltDefs, InlineDefs; IF ~bbt.sourcealt AND ~bbt.destalt THEN BEGIN BITBLT[bbt]; RETURN END; IF (bank ~IN BankIndex) OR ~memConfig.useXM OR BITAND[XMESA.BankMasks[bank],memConfig.banks] = 0 THEN ERROR XMNotAvailable; IF memConfig.AltoType = D0 THEN BEGIN Even: PROCEDURE [a: UNSPECIFIED] RETURNS [UNSPECIFIED] = INLINE BEGIN RETURN[a+LOOPHOLE[a,CARDINAL] MOD 2] END; LP: PROCEDURE [low,high: UNSPECIFIED] RETURNS [LONG POINTER] = MACHINE CODE BEGIN END; BITBLTL: PROCEDURE [POINTER TO LongBBTable] = LOOPHOLE[BITBLT]; lbbtable: ARRAY [0..SIZE[LongBBTable]] OF WORD; lbbt: POINTER TO LongBBTable ← Even[@lbbtable]; InlineDefs.COPY[from:bbt, to:lbbt, nwords: SIZE[BBTable]]; lbbt.ptrs ← D0; IF lbbt.sourcealt THEN BEGIN lbbt.slbca ← LP[low: lbbt.sbca, high: bank]; lbbt.sourcealt ← FALSE; END ELSE lbbt.slbca ← LP[low: lbbt.sbca, high: memConfig.mdsBank]; IF lbbt.destalt THEN BEGIN lbbt.dlbca ← LP[low: lbbt.dbca, high: bank]; lbbt.destalt ← FALSE; END ELSE lbbt.dlbca ← LP[low: lbbt.dbca, high: memConfig.mdsBank]; BITBLTL[lbbt]; END ELSE BEGIN arg: NovaBBArgBlock ← [fill: 0, bank: bank, bbt: bbt]; [] ← NovaOps.NovaJSR[code: JSR, address: @NovaCode, arg: @arg]; END; END; MakeCodeResident: PROCEDURE [f: GlobalFrameHandle] = BEGIN OPEN SegmentDefs; seg: FileSegmentHandle; info: AllocDefs.AllocInfo = [unused: 0, effort: hard, direction: bottomup, request: initial, class: code, swapunlocked: TRUE, compact: FALSE]; IF (seg ← FrameOps.CodeHandle[f]) = NIL THEN RETURN; IF seg.lock = 0 THEN FrameDefs.SwapOutCode[f]; AllocDefs.MakeSwappedIn[seg, DefaultBase, info]; RETURN END; GetCaller: PROCEDURE RETURNS [PROGRAM] = BEGIN RETURN[LOOPHOLE[FrameOps.GetReturnFrame[].returnlink.frame.accesslink]]; END; IsBound: PROCEDURE [link: UNSPECIFIED] RETURNS [BOOLEAN] = BEGIN RETURN[link # BcdDefs.UnboundLink AND link # BcdDefs.NullLink]; END; SelfDestruct: PROCEDURE = BEGIN destructee: FrameHandle ← FrameOps.GetReturnFrame[]; FrameOps.SetReturnLink[destructee.returnlink]; FrameDefs.UnNew[FrameDefs.GlobalFrame[destructee]]; FrameOps.Free[destructee]; RETURN END; -- data shuffling SetBlock: PROCEDURE [p:POINTER, v:UNSPECIFIED, l:CARDINAL] = BEGIN IF l=0 THEN RETURN; p↑ ← v; InlineDefs.COPY[from:p, to:p+1, nwords:l-1]; END; -- Bcd Version and Time Version: PROCEDURE [frame: GlobalFrameHandle, type: {bcd, image}] RETURNS [version: BcdDefs.VersionStamp] = BEGIN OPEN SegmentDefs; codeseg: FileSegmentHandle ← FrameOps.CodeHandle[frame]; seg: FileSegmentHandle; image: POINTER TO ImageFormat.ImageHeader; id: CARDINAL; p: POINTER TO RECORD [a, pages: CARDINAL]; IF codeseg = NIL THEN RETURN[BcdDefs.NullVersion]; seg ← NewFileSegment[codeseg.file, 1, 1, Read]; SwapIn[seg]; image ← p ← FileSegmentAddress[seg]; IF type = image AND image.prefix.versionident # ImageFormat.VersionID THEN BEGIN base: CARDINAL ← p.pages+2; Unlock[seg]; MoveFileSegment[seg, base, 1]; SwapIn[seg]; image ← FileSegmentAddress[seg]; END; id ← image.prefix.versionident; version ← IF (type = image AND id = ImageFormat.VersionID) OR (type = bcd AND id = BcdDefs.VersionID) THEN image.prefix.version ELSE BcdDefs.NullVersion; Unlock[seg]; DeleteFileSegment[seg]; RETURN END; BcdVersion: PROCEDURE RETURNS [version: BcdDefs.VersionStamp] = BEGIN OPEN FrameDefs, FrameOps; RETURN[Version[frame: GlobalFrame[GetReturnFrame[]], type: bcd]] END; BcdTime: PROCEDURE RETURNS [time: LONG CARDINAL] = BEGIN OPEN FrameDefs, FrameOps; RETURN[Version[frame: GlobalFrame[GetReturnFrame[]], type: bcd].time] END; -- Image Version and Time ImageVersion: PROCEDURE RETURNS [version: BcdDefs.VersionStamp] = BEGIN OPEN FrameDefs; RETURN[Version[frame: GlobalFrame[NucleusDefs.Resident], type: image]] END; ImageTime: PROCEDURE RETURNS [time: LONG CARDINAL] = BEGIN RETURN[ImageVersion[].time] END; CurrentTime: PROCEDURE RETURNS [LONG CARDINAL] = BEGIN time: AltoFileDefs.TIME ← MiscDefs.DAYTIME[]; RETURN[LOOPHOLE[ InlineDefs.LongNumber[num[highbits: time.high, lowbits: time.low]]]]; END; -- Fake Modules DestroyFakeModule: PROCEDURE [f: GlobalFrameHandle] RETURNS [seg: SegmentDefs.FileSegmentHandle, offset: CARDINAL] = BEGIN FrameDefs.ValidateGlobalFrame[f]; seg ← CodeHandle[f]; IF seg = NIL THEN RETURN[NIL, 0]; IF ~f.shared THEN seg.class ← other; FrameDefs.RemoveGlobalFrame[f]; ProcessDefs.DisableInterrupts[]; IF f.code.out THEN BEGIN f.code.out ← FALSE; offset ← f.code.offset; END ELSE offset ← f.code.offset - seg.VMpage*AltoDefs.PageSize; ProcessDefs.EnableInterrupts[]; RETURN END; -- Get Network Number wordsPerPup: INTEGER = 280; Byte: TYPE = [0..255]; PupHeader:TYPE= MACHINE DEPENDENT RECORD [ eDest, eSource: Byte, eWord2, pupLength: INTEGER, transportControl, pupType: Byte, pupID1, pupID2: INTEGER, destNet, destHost: Byte, destSocket1, destSocket2: INTEGER, sourceNet, sourceHost: Byte, sourceSocket1, sourceSocket2: INTEGER, xSum:CARDINAL]; Pup:TYPE= MACHINE DEPENDENT RECORD [ head:PupHeader, junk:ARRAY [0..100] OF WORD]; EthernetDeviceBlock: TYPE = MACHINE DEPENDENT RECORD [ EPLocMicrocodeStatus, EPLocHardwareStatus: Byte, EBLocInterruptBit: WORD, EELocInputFinishCount: INTEGER, ELLocCollisionMagic: WORD, EILocInputCount: INTEGER, EILocInputPointer: POINTER, EOLocOutputCount: INTEGER, EOLocOutputPointer: POINTER]; timer: POINTER TO INTEGER = LOOPHOLE[430B]; GetNetworkNumber: PROCEDURE RETURNS[CARDINAL] = BEGIN myHost: Byte ← OsStaticDefs.OsStatics.SerialNumber; then: INTEGER; now: INTEGER; device: POINTER TO EthernetDeviceBlock ← LOOPHOLE[600B]; xpup: Pup; pup: POINTER TO Pup = @xpup; -- StartIO is Mesa bytecode used to control Ethernet interface StartIO: PROCEDURE [WORD] = MACHINE CODE BEGIN Mopcodes.zSTARTIO END; outputCommand: WORD = 1; inputCommand: WORD = 2; resetCommand: WORD = 3; gatewayRequest:PupHeader ← [ eDest: 0, eSource: myHost, eWord2: 1000B, pupLength: 22, transportControl: 0, pupType: 200B, pupID1:, pupID2:, destNet: 0, destHost: 0, destSocket1: 0, destSocket2: 2, sourceNet: 0, sourceHost: myHost, sourceSocket1: 0, sourceSocket2: 2, xSum: 177777B]; device.EBLocInterruptBit ← 0; THROUGH [0..2) DO StartIO[resetCommand]; device↑ ← EthernetDeviceBlock[ EPLocMicrocodeStatus: 0, EPLocHardwareStatus: 0, EBLocInterruptBit: 0, EELocInputFinishCount: 0, ELLocCollisionMagic: 0, EILocInputCount: 0, EILocInputPointer: pup, EOLocOutputCount: 13, EOLocOutputPointer: @gatewayRequest]; StartIO[outputCommand]; then ← timer↑; DO IF device.EPLocHardwareStatus#0 THEN BEGIN IF device.EPLocMicrocodeStatus = 0 AND pup.head.eWord2 = 1000B AND wordsPerPup+2-device.EELocInputFinishCount > 13 AND pup.head.destSocket1 = 0 AND pup.head.destSocket2 = 2 AND pup.head.pupType = 201B THEN RETURN[pup.head.sourceNet]; device↑ ← EthernetDeviceBlock[ EPLocMicrocodeStatus: 0, EPLocHardwareStatus: 0, EBLocInterruptBit: 0 , EELocInputFinishCount: 0, ELLocCollisionMagic: 0, EILocInputCount: wordsPerPup+2, EILocInputPointer: pup, EOLocOutputCount: 0, EOLocOutputPointer: NIL]; StartIO[inputCommand]; END; now ← timer↑; IF now-then > 14 THEN EXIT; ENDLOOP; ENDLOOP; RETURN[0]; END; -- procedure lists UserCleanupList: POINTER TO ImageDefs.CleanupItem ← NIL; AddCleanupProcedure: PROCEDURE [item: POINTER TO ImageDefs.CleanupItem] = BEGIN ProcessDefs.DisableInterrupts[]; RemoveCleanupProcedure[item]; item.link ← UserCleanupList; UserCleanupList ← item; ProcessDefs.EnableInterrupts[]; END; RemoveCleanupProcedure: PROCEDURE [item: POINTER TO ImageDefs.CleanupItem] = BEGIN prev, this: POINTER TO ImageDefs.CleanupItem; IF UserCleanupList = NIL THEN RETURN; ProcessDefs.DisableInterrupts[]; prev ← this ← UserCleanupList; IF this = item THEN UserCleanupList ← this.link ELSE UNTIL (this ← this.link) = NIL DO IF this = item THEN BEGIN prev.link ← this.link; EXIT END; prev ← this; ENDLOOP; ProcessDefs.EnableInterrupts[]; END; UserCleanupProc: ImageDefs.CleanupProcedure = BEGIN -- all interrupts off if why = finish or abort this, next: POINTER TO ImageDefs.CleanupItem; this ← UserCleanupList; UserCleanupList ← NIL; WHILE this # NIL DO next ← this.link; IF InlineDefs.BITAND[ImageDefs.CleanupMask[why], this.mask] # 0 THEN this.proc[why ! ANY => IF why = Abort OR why = Finish THEN CONTINUE]; AddCleanupProcedure[this]; this ← next; ENDLOOP; SELECT why FROM Finish => ImageDefs.StopMesa[]; Abort => ImageDefs.AbortMesa[]; ENDCASE; END; -- Main Body; InitMemoryConfig[]; SDDefs.SD[SDDefs.sGoingAway] ← UserCleanupProc; END...