-- NXControl.mesa; edited by Gobbel; January 16, 1981 9:32 PM -- Last modified December 14, 1983 2:29 PM by Taft -- Last modified June 13, 1986 6:08 PM by Wyatt DIRECTORY AltoDisplay USING [DCBchainHead, DCBHandle, DCBnil], BcplOps USING [BcplJSR], Boot: FROM "BootX" USING [bootPhysicalVolume, inLoad, Location, nullDiskFileID, Request, pRequest], BootDirDefs USING [cmd, CmdDirEntry, CmdDirEntryPtr, DirEntryPtr, DirEntryType, file, FileDirEntryPtr, germ, InsertDirEntry, killProber, ListPossibleMatches, Lookup, ProbeProcess, uCode], BootSwap USING [sPilotSwitches], ControlDefs USING [FieldDescriptor, FrameHandle], DeviceTypes USING [ethernet, sa4000], DiskDefs USING [ CB, CBptr, DA, DL, DS, DSdone, DSgoodStatus, DSmaskStatus, InvalidDA], EFTPDefs USING [EFTPAbortReceiving, EFTPEndReceiving, EFTPFinishReceiving, EFTPGetBlock, EFTPOpenForReceiving, EFTPSetRecvTimeout, EFTPTimeOut], IODefs USING [BS, ControlA, ControlW, CR, DEL, ESC, NewLine, ReadChar, ReadLine, Rubout, SP, WriteChar, WriteLine, WriteDecimal, WriteOctal, WriteString], InlineDefs USING [BITAND, BITSHIFT, BITXOR, LongCOPY], KeyDefs USING [Keys], MiscDefs USING [CallDebugger, Zero], MMOps USING [BootViaNet, MakeBoot], Mopcodes USING [zLI4, zMISC, zRBL, zSHIFT, zSTARTIO, zWFS], NXDefs USING [Erase, Host, illPtr, Line, LineReset, NewLine, NoteUserAction, NXDisplay, ResetTo, Timer, SetTime, UserAbort, WatchDog], OsStaticDefs USING [OsStatics, AltoVersionNumber], ProcessDefs USING [ Detach, DisableInterrupts, EnableInterrupts, MsecToTicks, SetTimeout], PupDefs USING [AppendPupAddress, GetFreePupBuffer, PupBuffer, PupPackageMake, PupRouterSendThis, SetPupContentsWords, UniqueLocalPupAddress], PupTypes USING [PupAddress], SDDefs USING [SD, sUncaughtSignal], StringDefs USING [AppendChar, AppendDecimal, AppendString, AppendSubString, EquivalentSubStrings, InvalidNumber, StringToDecimal, SubStringDescriptor], SystemDefs USING [AllocateHeapNode, AllocateResidentPages, FreePages], StartList, TemporaryBooting USING [defaultSwitches, Switches, UpDown], TimeDefs USING [AppendDayTime, PackedTime, UnpackDT]; NXControl: MONITOR IMPORTS BootDirDefs, EFTPDefs, InlineDefs, IODefs, MiscDefs, MMOps, BcplOps, NXDefs, ProcessDefs, PupDefs, StringDefs, SystemDefs, TimeDefs EXPORTS NXDefs SHARES DiskDefs = BEGIN OPEN BootDirDefs, DiskDefs; req: Boot.Request; switches: TemporaryBooting.Switches ← TemporaryBooting.defaultSwitches; switchString: STRING ← [30]; cursor: POINTER TO ARRAY [0..16) OF WORD = LOOPHOLE[431B]; doneCursor: ARRAY [0..16) OF WORD ← -- "Loaded" [160000B, 40000B, 41616B, 42122B, 42116B, 46122B, 175637B, 0, 30006B, 10002B, 10002B, 70616B, 111122B, 111722B, 111022B, 74717B]; Question: SIGNAL [s: STRING] = CODE; alto: BOOLEAN; debug: PUBLIC BOOLEAN ← FALSE; CmdRec: TYPE = RECORD [ proc: PROC, name: STRING, validOnAlto: BOOLEAN ← TRUE]; nCommands: CARDINAL = 11; cmdTable: ARRAY [0..nCommands) OF CmdRec = [ [BootDP0, "BootDP0"], [Cedar, "Cedar", FALSE], [FileStat, "FileStat"], [Othello, "Othello", FALSE], [Partition, "Partition", FALSE], [PhysVolBoot, "PhysicalVolumeBoot", FALSE], [Probe, "Probe"], [Quit, "Quit"], [ReadSwitches, "Switches", FALSE], [SetTimeCmd, "SetTime"], [SetVersions, "SetVersions", FALSE]]; Catcher: PROC [msg, signal: UNSPECIFIED, frame: ControlDefs.FrameHandle] = {MiscDefs.CallDebugger["UNCAUGHT SIGNAL"L]}; Run: PROCEDURE = BEGIN OPEN IODefs; cmdPtr: DirEntryPtr; DO ENABLE BEGIN Rubout => BEGIN WriteString[" XXX"L]; LOOP END; Question => BEGIN ENABLE NXDefs.UserAbort => GOTO Aborted; NXDefs.LineReset["Command, from:"L]; ListPossibleMatches[s, cmd]; NXDefs.NewLine[]; NXDefs.Line["or boot file, from:"L]; ListPossibleMatches[s, file]; WriteChar[CR]; RESUME; EXITS Aborted => RESUME; END; END; IF ~IODefs.NewLine[] THEN WriteChar[CR]; WriteChar['>]; cmdPtr ← ReadCmd[">"L]; WITH entry: cmdPtr SELECT FROM cmd => entry.proc[]; file => SELECT entry.sysType FROM alto => MMOps.BootViaNet[entry.bfn, entry.host.host]; pilot => EtherBoot[@entry]; ENDCASE; ENDCASE; ENDLOOP; END; ReadCmd: PROC [ prompt: STRING, type: DirEntryType ← cmd+file, acceptDefault: BOOLEAN ← FALSE, default: STRING ← ""L] RETURNS [cmdPtr: DirEntryPtr] = BEGIN OPEN IODefs, NXDefs, StringDefs; s: STRING ← [45]; startOver: BOOLEAN ← default.length>0; s.length ← 0; AppendString[s, default]; DO SELECT ReadName[prompt, s, startOver] FROM CR, ESC, SP => BEGIN IF s.length=0 THEN IF acceptDefault THEN RETURN[NIL] ELSE {FlashDisplay[]; LOOP}; cmdPtr ← Lookup[s, type]; SELECT cmdPtr FROM NIL => {FlashDisplay[]; WriteString[" ?"L]; startOver ← TRUE; Probe[]; LOOP}; illPtr => FlashDisplay[]; ENDCASE => RETURN; END; ENDCASE; startOver ← FALSE; ENDLOOP; END; ReadDecimal: PROC [ prompt: STRING, acceptDefault: BOOLEAN ← FALSE, default: CARDINAL ← 0] RETURNS [CARDINAL] = BEGIN OPEN IODefs, NXDefs, StringDefs; s: STRING ← [45]; startOver: BOOLEAN ← acceptDefault; s.length ← 0; IF acceptDefault THEN AppendDecimal[s, default]; DO ENABLE Question => BEGIN WriteChar['?]; WriteChar[CR]; WriteLine[" Decimal number"L]; WriteString[prompt]; WriteString[s]; LOOP END; SELECT ReadName[prompt, s, startOver] FROM CR, ESC, SP => RETURN[StringToDecimal[s ! InvalidNumber => {WriteString[" ?"L]; CONTINUE}]]; '? => BEGIN WriteChar['?]; WriteChar[CR]; WriteLine[" Decimal number"L]; WriteString[prompt]; WriteString[s]; LOOP END; ENDCASE; startOver ← FALSE; ENDLOOP; END; ReadName: PROC [prompt, s: STRING, new: BOOLEAN] RETURNS [c: CHARACTER] = BEGIN OPEN IODefs, NXDefs, StringDefs; DO c ← ReadChar[]; NoteUserAction[]; ResetTo[prompt.length+s.length]; SELECT c FROM BS, ControlA => Erase[1, s]; SP, CR, ESC => RETURN; DEL => SIGNAL Rubout; ControlW => Erase[s.length, s]; '? => BEGIN WriteChar['?]; WriteChar[CR]; SIGNAL Question[IF new THEN ""L ELSE s]; WriteString[prompt]; WriteString[s]; LOOP END; >SP => BEGIN IF new THEN {ResetTo[prompt.length]; s.length ← 0}; IF s.length=s.maxlength THEN {FlashDisplay[]; LOOP}; WriteChar[c]; AppendChar[s, c]; END; ENDCASE => FlashDisplay[]; new ← FALSE; ENDLOOP; END; FlashDisplay: PROC = BEGIN dcb: AltoDisplay.DCBHandle; FOR dcb ← AltoDisplay.DCBchainHead↑, dcb.next UNTIL dcb=NIL DO dcb.background ← black ENDLOOP; THROUGH [0..10000] DO ENDLOOP; FOR dcb ← AltoDisplay.DCBchainHead↑, dcb.next UNTIL dcb=NIL DO dcb.background ← white ENDLOOP; END; InstallCommands: PROC = BEGIN OPEN SystemDefs; entry: CmdDirEntryPtr; FOR i: CARDINAL IN [0..nCommands) DO IF alto AND ~cmdTable[i].validOnAlto THEN LOOP; entry ← AllocateHeapNode[SIZE[CmdDirEntry]]; entry.name ← cmdTable[i].name; entry.vp ← cmd[cmdTable[i].proc]; InsertDirEntry[entry]; ENDLOOP; END; Quit: PROC = {MMOps.BootViaNet[0]}; Probe: PROC = {ProcessDefs.Detach[FORK ProbeProcess[]]}; SetTimeCmd: PROC = {ProcessDefs.Detach[FORK NXDefs.SetTime[]]}; Partition: PROC = BEGIN OPEN IODefs, BcplOps; setPartition: RECORD [a, b: WORD] ← [61037B, 1400B]; partition, status: CARDINAL; status ← BcplJSR[JSR, @setPartition, 0]; WriteString[" number "L]; WriteDecimal[status]; partition ← ReadDecimal[">Partition number "L, TRUE, status]; [] ← BcplJSR[JSR, @setPartition, partition]; END; BootDP0: PROC = BEGIN OPEN AltoDisplay, DiskDefs, InlineDefs, IODefs, SystemDefs; StartIO: PROC [CARDINAL] = MACHINE CODE BEGIN Mopcodes.zSTARTIO END; i: CARDINAL; bltAndJump: ARRAY [0..5] OF UNSPECIFIED ← [ 111000B, -- mov 0 2 21000B, -- lda 0 0, 2 25001B, -- lda 1 1, 2 35002B, -- lda 3 2, 2 61005B, -- blt 1]; -- jmp 1 bltArgs: RECORD [a, b, c: UNSPECIFIED]; cb: CBptr ← AllocateHeapNode[SIZE[CB]]; oldDCBChainHead: DCBHandle; label: POINTER TO DL ← AllocateHeapNode[SIZE[DL]]; data: POINTER TO ARRAY [0..256) OF WORD ← LOOPHOLE[AllocateResidentPages[1]]; csb: POINTER TO CSB = LOOPHOLE[521B]; keys: POINTER TO WORD = LOOPHOLE[177034B]; bootLabel: POINTER TO DL = LOOPHOLE[402B]; bootStatus: POINTER ← data+1; CSB: TYPE = RECORD [cb: CBptr, status: DS, addr: DA]; oldDCBChainHead ← DCBchainHead↑; ProcessDefs.DisableInterrupts[]; DCBchainHead↑ ← DCBnil; FOR i IN [0..2000] DO ENDLOOP; StartIO[3]; -- clear Ethernet csb.cb ← NIL; csb.addr ← InvalidDA; FOR i IN [1..10] DO MiscDefs.Zero[cb, SIZE[CB]]; cb.command ← [110B, DiskCheck, DiskRead, DiskRead, 0, 0]; cb.headerAddress ← @cb.header; cb.labelAddress ← label; cb.dataAddress ← data; cb.header.diskAddress ← LOOPHOLE[BITXOR[keys↑, -1]]; csb.cb ← cb; -- start the disk WHILE cb.status.done#DSdone DO ENDLOOP; -- wait for completion IF BITAND[cb.status, DSmaskStatus]=DSgoodStatus THEN EXIT; REPEAT FINISHED => BEGIN ProcessDefs.EnableInterrupts[]; DCBchainHead↑ ← oldDCBChainHead; WriteChar[CR]; WriteString["10 consecutive errors trying to read vda 0"L]; RETURN END; ENDLOOP; bootLabel↑ ← label↑; -- 402B ← label bootStatus↑ ← cb.status; -- 2B ← disk status bltArgs.a ← data-1; bltArgs.b ← 400B; bltArgs.c ← -256; [] ← BcplOps.BcplJSR[JSR, @bltAndJump, @bltArgs]; -- bye bye END; FileStat: PROC = BEGIN OPEN InlineDefs, IODefs, PupDefs, PupTypes, StringDefs, TimeDefs; ENABLE Question => BEGIN ENABLE NXDefs.UserAbort => GOTO Aborted; NXDefs.LineReset["File name, from:"L]; ListPossibleMatches[s, IF alto THEN file ELSE file+uCode+germ]; WriteChar[CR]; RESUME EXITS Aborted => RESUME; END; s: STRING ← [25]; bfNum: CARDINAL; bfDate: PackedTime; hostAddr: PupAddress; uSwitchKbd: BOOLEAN = IF alto THEN OsStaticDefs.OsStatics.AltoVersion.engineeringnumber<2 ELSE ProcessorType[]=CSL OR ProcessorType[]=Dorado; keys: ARRAY [0..15] OF STRING ← [IF uSwitchKbd THEN " <blank-top>"L ELSE " <BW>"L, IF uSwitchKbd THEN " <blank-middle>"L ELSE " <FR4>"L, " ]"L, " <quote>"L, " <comma>"L, " L"L, " O"L, " X"L, " I"L, " 9"L, " A"L, " S"L, " Q"L, " W"L, " 2"L, " 3"L]; entry: DirEntryPtr; WriteString[" for file "L]; entry ← ReadCmd[">FileStat for file "L, file+uCode+germ]; WITH entry SELECT FROM file => {bfNum ← bfn; hostAddr ← host; bfDate ← date}; uCode, germ => {bfNum ← bfn; hostAddr ← host; bfDate ← date}; ENDCASE; WriteChar[CR]; WriteString["File number "L]; WriteOctal[bfNum]; WriteString[", from host "L]; s.length ← 0; AppendPupAddress[s, hostAddr]; s.length ← s.length-1; WriteString[s]; WriteString[", created "L]; s.length ← 0; AppendDayTime[s, UnpackDT[bfDate]]; WriteLine[s]; WriteString["Keys: <BS>"L]; FOR i: INTEGER IN [0..16) DO IF BITAND[BITSHIFT[bfNum, -i], 1]=1 THEN WriteString[keys[i]]; ENDLOOP; END; SetVersions: PROC = BEGIN OPEN IODefs; entry: DirEntryPtr; WriteLine[" for germ and microcode"L]; BEGIN ENABLE Question => BEGIN ENABLE NXDefs.UserAbort => GOTO Aborted; NXDefs.LineReset["Available germ files:"L]; ListPossibleMatches[s, germ]; WriteChar[CR]; RESUME EXITS Aborted => RESUME; END; WriteString["Germ: "L]; WriteString[IF germEntry=NIL THEN pilotGerm[ProcessorType[]] ELSE germEntry.name]; entry ← ReadCmd[ "Germ: "L, germ, TRUE, IF germEntry=NIL THEN pilotGerm[ProcessorType[]] ELSE germEntry.name]; WriteChar[CR]; IF entry#NIL THEN germEntry ← entry; END; BEGIN ENABLE Question => BEGIN ENABLE NXDefs.UserAbort => GOTO Aborted; NXDefs.LineReset["Available microcode files:"L]; ListPossibleMatches[s, uCode]; WriteChar[CR]; RESUME EXITS Aborted => RESUME; END; WriteString["Microcode: "L]; WriteString[ IF uCodeEntry=NIL THEN microcodeFiles[ProcessorType[]] ELSE uCodeEntry.name]; entry ← ReadCmd[ "Microcode: "L, uCode, TRUE, IF uCodeEntry=NIL THEN microcodeFiles[ProcessorType[]] ELSE uCodeEntry.name]; WriteChar[CR]; IF entry#NIL THEN uCodeEntry ← entry; END; END; ReadSwitches: PROC = {IODefs.WriteString[": "L]; IODefs.ReadLine[switchString]}; SetSwitches: PROC = BEGIN OPEN IODefs; FOR i: CARDINAL IN [0..switchString.length) DO SELECT switchString[i] FROM IN ['A..'Z] => Set[down, @switches, (switchString[i]-'A)+10]; IN ['a..'z] => Set[down, @switches, (switchString[i]-'a)+10]; IN ['0..'9] => Set[down, @switches, switchString[i]-'0]; ENDCASE; ENDLOOP; switches.g ← down; END; BitIndex: TYPE = CARDINAL [0..4096); Bit: TYPE = CARDINAL [0..1]; BitDescriptor: TYPE = ControlDefs.FieldDescriptor; Set: PROC [upDown: TemporaryBooting.UpDown, p: POINTER, index: BitIndex] = INLINE {SetBitWithDesc[LOOPHOLE[upDown], p, BD[index]]}; BD: PROC [CARDINAL] RETURNS [BitDescriptor] = MACHINE CODE BEGIN Mopcodes.zLI4; Mopcodes.zSHIFT END; SetBitWithDesc: PROC [b: Bit, p: POINTER, bd: BitDescriptor] = MACHINE CODE BEGIN Mopcodes.zWFS END; LoadRam: PROC [p: LONG POINTER, andJump: BOOLEAN] = MACHINE CODE BEGIN Mopcodes.zMISC, 3 END; ClearDevices: PROC = MACHINE CODE BEGIN Mopcodes.zMISC, 4 END; MapFlags: TYPE = MACHINE DEPENDENT RECORD [ LogSE, W, D, Ref: BOOLEAN]; vacantFlags: MapFlags = [FALSE, TRUE, TRUE, FALSE]; cleanFlags: MapFlags = [FALSE, FALSE, FALSE, FALSE]; MapEntry: TYPE = MACHINE DEPENDENT RECORD [ flags: MapFlags, realPage: [0..7777B]]; RealPage: TYPE = CARDINAL [0..10000B); VirtualPage: TYPE = CARDINAL [0..40000B); vacant: MapEntry = [vacantFlags,0]; clean: MapEntry = [cleanFlags,0]; ASSOC: PROC [CARDINAL, MapEntry] = MACHINE CODE {Mopcodes.zMISC, 0}; SETF: PROC [CARDINAL, MapEntry] RETURNS [MapEntry] = MACHINE CODE {Mopcodes.zMISC, 1}; LPFromPage: PROC [p: CARDINAL] RETURNS [LONG POINTER] = INLINE {RETURN[LOOPHOLE[LONG[p]*pageSize]]}; LongRead: PROC [LONG POINTER] RETURNS [CARDINAL] = MACHINE CODE {Mopcodes.zRBL, 0}; CountRealPages: PROC RETURNS [pageCount: CARDINAL] = BEGIN FOR pageCount ← 0, pageCount+1 DO m: MapEntry ← SETF[pageCount, clean]; [] ← SETF[pageCount, m]; IF m = vacant THEN EXIT; ENDLOOP; RETURN END; MovePages: PROC [from, to, count: CARDINAL] = BEGIN FOR i: CARDINAL IN [0..count) DO m: MapEntry ← SETF[from+i, clean]; ASSOC[from+i, vacant]; ASSOC[to+i, m]; ENDLOOP; RETURN END; NullDA: DA = LOOPHOLE[0]; BootInfo: TYPE = ARRAY [0..8) OF UNSPECIFIED; nullBootInfo: BootInfo ← ALL[0]; pageSize: CARDINAL = 256; firstHyperPage: CARDINAL = 256; germMDSIndex: CARDINAL = 76B; germMDSPage: CARDINAL = germMDSIndex*pageSize; germMDS: LONG POINTER = LOOPHOLE[LONG[germMDSIndex]*200000B]; firstGermPage: CARDINAL = germMDSPage + 2; germStart: LONG POINTER = LPFromPage[firstGermPage]; germData: LONG POINTER = germMDS + LOOPHOLE[Boot.pRequest]; germSwitches: LONG POINTER = germMDS + LOOPHOLE[@SDDefs.SD[BootSwap.sPilotSwitches]]; pilotGerm: ARRAY Hardware OF STRING ← ["CedarD0.eg"L, "CedarD0.eg"L, "CedarD0.eg"L, "CedarDorado.eg"L]; microcodeFiles: ARRAY Hardware OF STRING ← ["CedarD0.eb"L, "CedarD0.eb"L, "CedarD0.eb"L, "CedarDorado.eb"L]; othelloFiles: ARRAY Hardware OF STRING ← ["CedarOthelloD0.pb"L, "CedarOthelloD0.pb"L, "CedarOthelloD0.pb"L, "CedarOthelloDorado.pb"L]; cedarFiles: ARRAY Hardware OF STRING ← ["BasicCedarD0.pb"L, "BasicCedarD0.pb"L, "BasicCedarD0.pb"L, "BasicCedarDorado.pb"L]; cedarGerms: ARRAY Hardware OF STRING ← ["CedarD0.eg"L, "CedarD0.eg"L, "CedarD0.eg"L, "CedarDorado.eg"L]; Hardware: TYPE = {SDD, CSL, Tor, Dorado}; uCodeEntry: DirEntryPtr ← NIL; germEntry: DirEntryPtr ← NIL; ProcessorType: PROC RETURNS [Hardware] = BEGIN Register: TYPE = MACHINE DEPENDENT RECORD [ left: [0..377B], bitClock: [0..37B], fill: [0..7B]]; Input: PROC [CARDINAL] RETURNS [Register] = MACHINE CODE BEGIN Mopcodes.zMISC, 5; END; reg: Register; IF OsStaticDefs.OsStatics.AltoVersion.engineeringnumber=5 THEN RETURN[Dorado]; FOR i: CARDINAL IN [4..16] DO reg ← Input[i*16]; IF reg.left=2 THEN EXIT ELSE IF reg.left=12B THEN RETURN[Tor]; REPEAT FINISHED => ERROR; ENDLOOP; RETURN[IF reg.bitClock=5 THEN CSL ELSE SDD]; -- 3 for SDD Dolphin END; GetFileName: PROC [entry: DirEntryPtr, type: DirEntryType, str: STRING] RETURNS [STRING] = BEGIN OPEN StringDefs; ssP: SubStringDescriptor ← ["P"L, 0, 1]; -- what a crock... ssA: SubStringDescriptor ← ["AlphaPilot"L, 0, 10]; ss: SubStringDescriptor; root: STRING ← SELECT type FROM germ => pilotGerm[ProcessorType[]], uCode => microcodeFiles[ProcessorType[]], ENDCASE => ERROR; str.length ← 0; IF entry=NIL THEN {AppendString[str, root]; RETURN[str]}; ss ← [entry.name, 0, ssP.length]; IF EquivalentSubStrings[@ss, @ssP] THEN {AppendSubString[str, @ssP]; AppendString[str, root]; RETURN[str]}; ss.length ← ssA.length; IF EquivalentSubStrings[@ss, @ssA] THEN {AppendSubString[str, @ssA]; AppendString[str, root]; RETURN[str]}; AppendString[str, root]; RETURN[str]; END; EtherBoot: PROC [entry: FileDirEntryPtr] = BEGIN req.action ← Boot.inLoad; req.location ← [deviceType: DeviceTypes.ethernet, deviceOrdinal: 0, vp: ethernet[entry.bfn, entry.host.net, entry.host.host]]; SoftBoot[entry]; END; PhysVolBoot: PROC = BEGIN -- boot Shugart disk req.action ← Boot.bootPhysicalVolume; req.location ← [deviceType: DeviceTypes.sa4000, deviceOrdinal: 0, vp: disk[Boot.nullDiskFileID]]; SoftBoot[]; END; FlipCursor: PROC = BEGIN FOR i: CARDINAL IN[0..16) DO cursor[i] ← InlineDefs.BITXOR[cursor[i], -1] ENDLOOP; END; EtherGetModule: ENTRY PROC [entry: DirEntryPtr, pProc: PROCEDURE RETURNS [LONG POINTER]] RETURNS [pages: CARDINAL] = BEGIN OPEN EFTPDefs, IODefs, ProcessDefs, PupDefs, PupTypes; n, zero, lastn: CARDINAL ← 0; i, tP, firstP, lastP: LONG POINTER; bufP: POINTER ← NIL; -- must be initialized since NIL means not allocated me: PupAddress; oneSecond: CONDITION; host: PupAddress ← WITH entry SELECT FROM file => host, uCode, germ => host, ENDCASE => ERROR; bfn: CARDINAL ← WITH entry SELECT FROM file => bfn, uCode, germ => bfn, ENDCASE => ERROR; buffer: PupBuffer; squares: ARRAY [0..16) OF WORD ← [377B, 377B, 377B, 377B, 377B, 377B, 377B, 377B, 177400B, 177400B, 177400B, 177400B, 177400B, 177400B, 177400B, 177400B]; BEGIN ENABLE EFTPTimeOut => {WriteString["receiver timed out"L]; GOTO ErrorExit}; IF entry=NIL THEN RETURN[0]; pages ← 0; SetTimeout[@oneSecond, MsecToTicks[1000]]; cursor↑ ← squares; me ← UniqueLocalPupAddress[@host]; bufP ← SystemDefs.AllocateResidentPages[1]; EFTPSetRecvTimeout[100]; -- short timeout to get things rolling -- first set up receiver [] ← EFTPOpenForReceiving[me ! EFTPTimeOut => -- at least once, since we haven't asked for file BEGIN EFTPSetRecvTimeout[5000]; IF (n←n+1)>10 THEN {WriteString["no connection established"L]; GOTO ErrorExit}; buffer ← GetFreePupBuffer[]; buffer.source ← me; buffer.dest ← host; buffer.pupType ← bootFileSend; buffer.pupID.b ← bfn; SetPupContentsWords[buffer, 0]; [] ← PupRouterSendThis[buffer]; RESUME; END]; [] ← EFTPGetBlock[bufP, 512]; -- first block is alto bootloader, discard it FlipCursor[]; IF entry.type=uCode THEN -- keep LoadRamAndJump happy BEGIN firstP ← tP ← pProc[] + pageSize - 1; pages ← pages+1; InlineDefs.LongCOPY[from: @zero, to: tP, nwords: 1]; END; lastn ← 512; -- so check below succeeds first time DO -- main receive loop n ← EFTPGetBlock[bufP, 512 ! EFTPEndReceiving => EXIT]; IF lastn<512 THEN {WriteString["short EFTP block in middle of file"L]; GOTO ErrorExit}; lastn ← n; tP ← pProc[]; pages ← pages+1; InlineDefs.LongCOPY[from: bufP, to: tP, nwords: pageSize]; FlipCursor[]; ENDLOOP; EFTPFinishReceiving[]; SystemDefs.FreePages[bufP]; bufP ← NIL; IF entry.type=uCode THEN {lastP ← tP+n/2; n ← 0; FOR i ← firstP, i+1 UNTIL i=lastP DO n ← n+LongRead[i] ENDLOOP; IF n#0 THEN {WriteString["microcode checksum error"L]; GOTO ErrorExit}}; WAIT oneSecond; EXITS ErrorExit => {IF bufP#NIL THEN SystemDefs.FreePages[bufP]; EFTPAbortReceiving[""L]; RETURN[0]}; END; END; SoftBoot: PROC [dirEntry: DirEntryPtr ← NIL] = BEGIN OPEN IODefs; germPages, rpCount, uCodePages: CARDINAL; nextGermTo, nextGermFrom: CARDINAL; nextHyperPage: CARDINAL ← firstHyperPage; entry: DirEntryPtr; fName: STRING ← [40]; GetGermPage: PROCEDURE RETURNS [p: LONG POINTER] = BEGIN ProcessDefs.DisableInterrupts[]; MovePages[from: nextGermFrom, to: nextGermTo, count: 1]; ProcessDefs.EnableInterrupts[]; p ← LPFromPage[nextGermTo]; nextGermTo ← nextGermTo+1; nextGermFrom ← nextGermFrom-1; END; GetHyperPage: PROCEDURE RETURNS [p: LONG POINTER] = BEGIN p ← LPFromPage[nextHyperPage]; nextHyperPage ← nextHyperPage+1; END; BEGIN killProber ← TRUE; SetSwitches[]; WriteString["...loading germ..."L]; entry ← IF germEntry#NIL THEN germEntry ELSE Lookup[GetFileName[dirEntry, germ, fName], germ, FALSE]; IF entry=NIL THEN {WriteChar[CR]; WriteString["Can't find file "L]; WriteLine[fName]; RETURN}; rpCount ← CountRealPages[]; nextGermFrom ← rpCount-1; -- start at end of real memory nextGermTo ← firstGermPage; germPages ← EtherGetModule[entry, GetGermPage]; IF germPages=0 THEN {WriteChar[CR]; WriteLine["EFTP problem - couldn't get germ"L]; GOTO Abort}; WriteString["loading microcode..."L]; entry ← IF uCodeEntry#NIL THEN uCodeEntry ELSE Lookup[GetFileName[dirEntry, uCode, fName], uCode, FALSE]; IF entry=NIL THEN {WriteChar[CR]; WriteString["Can't find file "L]; WriteLine[fName]; GOTO Abort}; uCodePages ← EtherGetModule[entry, GetHyperPage]; IF uCodePages=0 THEN {WriteChar[CR]; WriteLine["EFTP problem - couldn't get microcode"L]; GOTO Abort}; ProcessDefs.DisableInterrupts[]; AltoDisplay.DCBchainHead↑ ← AltoDisplay.DCBnil; THROUGH [0..2000] DO ENDLOOP; -- let things quiet down InlineDefs.LongCOPY[from: @req, to: germData, nwords: SIZE[Boot.Request]]; InlineDefs.LongCOPY[from: @switches, to: germSwitches, nwords: SIZE[TemporaryBooting.Switches]]; cursor↑ ← doneCursor; -- since display is turned off LoadRam[LPFromPage[firstHyperPage]+pageSize-1, TRUE]; EXITS Abort => -- put real pages back MovePages[from: firstGermPage, to: rpCount-germPages, count: nextGermTo-firstGermPage]; END; END; Othello: PROC = {BootGeneric[othelloFiles]}; Cedar: PROC = BEGIN IF germEntry=NIL THEN germEntry ← Lookup[cedarGerms[ProcessorType[]], germ, FALSE]; BootGeneric[cedarFiles]; END; BootGeneric: PROC [fileNameTable: ARRAY Hardware OF STRING] = BEGIN OPEN IODefs, NXDefs; cmdPtr: DirEntryPtr ← Lookup[fileNameTable[ProcessorType[]], file, FALSE]; SELECT cmdPtr FROM NIL => {FlashDisplay[]; WriteString[" ?"L]; Probe[]; RETURN}; illPtr => {FlashDisplay[]; RETURN}; ENDCASE; WITH entry: cmdPtr SELECT FROM cmd => entry.proc[]; file => SELECT entry.sysType FROM alto => MMOps.BootViaNet[entry.bfn, entry.host.host]; pilot => EtherBoot[@entry]; ENDCASE; ENDCASE; END; -- Initialization BEGIN vers: RECORD [WORD, WORD] ← [61014B, 1400B]; version: OsStaticDefs.AltoVersionNumber; debug ← KeyDefs.Keys.Spare1=down OR debug; IF ~debug THEN MMOps.MakeBoot[]; SDDefs.SD[SDDefs.sUncaughtSignal] ← Catcher; version ← BcplOps.BcplJSR[JSR, @vers, 0]; OsStaticDefs.OsStatics.AltoVersion ← version; alto ← version.engineeringnumber<4; START NXDefs.NXDisplay; InstallCommands[]; PupDefs.PupPackageMake[]; ProcessDefs.Detach[FORK NXDefs.Host]; ProcessDefs.Detach[FORK NXDefs.Timer]; ProcessDefs.Detach[FORK NXDefs.SetTime]; ProcessDefs.Detach[FORK ProbeProcess]; ProcessDefs.Detach[FORK NXDefs.WatchDog]; Run[]; END; END...