-- File: BootChannelDicentra.mesa - last edit:
-- AOF                 11-Feb-88 16:57:45
-- HGM                  5-Dec-83 20:40:38
-- Copyright (C) 1983, 1988 by Xerox Corporation. All rights reserved. 

<<  This isn't really a Channel, but it saves a lot of space in the initial microcode.  This module must be first on the chain.  It responds to a deviceType of anyEthernet by adjusting the bootLocation so that some other channel will do the work.  

General plan:
  Look for a valid boot location in the EERom.
  If not, try for one in page 0 of the Germ EProm.  (Not implemented yet.)
  If that fails too, use a wired in default location. (NS Booting.)
  If the altBoot button is down, cycle magic numbers through the MP until altBoot goes up.
    Currently, there are 3 clumps of numbers.
    One for NS booting, a second for phone booting, and another for Pup booting.
    The thousands digit is the device index.

>>

DIRECTORY
  Boot USING [Location],
  BootChannel USING [Create, Handle, Operation, Result],
  Checksum USING [ComputeChecksum],
  DeviceTypes USING [anyEthernet, ethernet, ethernetOne],
  Environment USING [Byte, bytesPerWord],
  Inline USING [BytePair, HighByte, LowByte],
  NSConstants USING [bootServerSocket],
  PilotMP USING [cGermAction],
  ProcessorFace USING [mp, SetMP],
  System USING [
    broadcastHostNumber, GetClockPulses, HostNumber, MicrosecondsToPulses,
    NetworkAddress, nullNetworkNumber, Pulses],
  
  DicentraInputOutput USING [GetExternalStatus, IOAddress, Input, Output],
  EERom USING [BootLocation, bootLocationVersion, bytesPerChip, WholeEERom],
  MultibusAddresses USING [eprom, timeout],
  PhoneFace USING [krockDeivceOffsetForBooting, phoneLine, TurnOff];

BootChannelDicentra: PROGRAM
  IMPORTS
    remainingChannels: BootChannel,
    Checksum, ProcessorFace, System, Inline,
    DicentraInputOutput, PhoneFace
  EXPORTS BootChannel =
  BEGIN

  pupBootFileBase: CARDINAL = 140000B;
  nsBootFileBase: BootFileNumberRep = [0, 0AA00H, 400H];

  Create: PUBLIC PROC [
    pLocation: LONG POINTER TO Boot.Location, operation: BootChannel.Operation,
    buffer: LONG POINTER ← NIL]
    RETURNS [result: BootChannel.Result, handle: BootChannel.Handle] =
    BEGIN
    PhoneFace.TurnOff[];  -- START Trap allocates CSB
    IF pLocation.deviceType = DeviceTypes.ethernet
      AND pLocation.deviceOrdinal >= PhoneFace.krockDeivceOffsetForBooting THEN
      BEGIN  -- Krockery because Phone Booting isn't in any interface
      pLocation.deviceType ← PhoneFace.phoneLine;
      pLocation.deviceOrdinal ←
        pLocation.deviceOrdinal - PhoneFace.krockDeivceOffsetForBooting;
      END;
    IF pLocation.deviceType = DeviceTypes.anyEthernet THEN
      BEGIN
      SELECT TRUE FROM
        GetLocationFromEERom[pLocation] => NULL;
        GetLocationFromGermProm[pLocation] => NULL;
	ENDCASE => GetDefaultLocation[pLocation];
      IF DicentraInputOutput.GetExternalStatus[].altBoot THEN GetLocationFromButton[pLocation];
      END;
    RETURN remainingChannels.Create[pLocation, operation];
    END;
    
  GetLocationFromEERom: PROCEDURE [location: LONG POINTER TO Boot.Location] RETURNS [BOOLEAN] =
    BEGIN
    temp: EERom.BootLocation;
    FetchBootLocation[@temp];
    IF ~VersionValid[@temp] THEN RETURN[FALSE];
    IF ~ChecksumValid[@temp] THEN RETURN[FALSE];
    location↑ ← temp.location;
    RETURN[TRUE];
    END;

  GetLocationFromGermProm: PROCEDURE [location: LONG POINTER TO Boot.Location] RETURNS [BOOLEAN] =
    BEGIN
    RETURN[FALSE];
    END;

  GetDefaultLocation: PROCEDURE [location: LONG POINTER TO Boot.Location] =
    BEGIN
    FabricateEthernetLocation[location, 0];
    END;

  GetLocationFromButton: PROCEDURE [location: LONG POINTER TO Boot.Location] =
    BEGIN
    teMP: CARDINAL = ProcessorFace.mp;
    Where: TYPE = {ethernet, phone, ethernetOne};
    mp: ARRAY Where OF CARDINAL = [240, 260, 280];
    DeactivateWatchdog[];  -- Enable booting over slow lines
      FOR device: CARDINAL IN [0..3) DO
        FOR where: Where IN Where DO
          FOR i: CARDINAL IN [0..5) DO
            ProcessorFace.SetMP[mp[where] + i + device * 1000];
            PauseAWhile[];
	    IF ~DicentraInputOutput.GetExternalStatus[].altBoot THEN
	      BEGIN
	      SELECT where FROM
	        ethernet => FabricateEthernetLocation[location, i];
	        phone => FabricatePhoneLocation[location, i];
	        ethernetOne => FabricateEthernetOneLocation[location, i];
	        ENDCASE => ERROR;
	      location.deviceOrdinal ← device;
              ProcessorFace.SetMP[teMP];
ProcessorFace.SetMP[PilotMP.cGermAction];  -- Bug in GermOpsImpl
	      RETURN;
	      END;
            ENDLOOP;
          ENDLOOP;
        ENDLOOP;
    -- Maybe the MP is unplugged?
    FabricateEthernetLocation[location, 0];
    END;

  PauseAWhile: PROCEDURE =
    BEGIN
    duration: System.Pulses = System.MicrosecondsToPulses[700000];
    start: System.Pulses = System.GetClockPulses[];
    UNTIL (System.GetClockPulses[]-start) > duration DO ENDLOOP;
    END;
    
  FabricateEthernetLocation: PROCEDURE [
    location: LONG POINTER TO Boot.Location, bootFileOffset: CARDINAL] =
    BEGIN
    where: EthernetLocation;
    bfn: BootFileNumberRep ← nsBootFileBase;
    bfn.c ← bfn.c + bootFileOffset;
    where ← [
      bfn: LOOPHOLE[bfn],
      address: [
        net: System.nullNetworkNumber,
        host: System.broadcastHostNumber,
        socket: NSConstants.bootServerSocket] ];
    location↑ ← [
      deviceType: DeviceTypes.ethernet,
      deviceOrdinal: 0,
      vp: TRASH ];
    LOOPHOLE[@location.vp, LONG POINTER TO EthernetLocation]↑ ← LOOPHOLE[where];
    END;

  FabricatePhoneLocation: PROCEDURE [
    location: LONG POINTER TO Boot.Location, bootFileOffset: CARDINAL] =
    BEGIN
    FabricateEthernetLocation[location, bootFileOffset];
    location.deviceType ← PhoneFace.phoneLine;
    END;

  FabricateEthernetOneLocation: PROCEDURE [
    location: LONG POINTER TO Boot.Location, bootFileOffset: CARDINAL] =
    BEGIN
    location↑ ← [
      deviceType: DeviceTypes.ethernetOne,
      deviceOrdinal: 0,
      vp: ethernetOne [
        bootFileNumber: pupBootFileBase + bootFileOffset, net: 0, host: 0 ] ];
    END;

  BootFileNumber: TYPE = RECORD [System.HostNumber];
  BootFileNumberRep: TYPE = MACHINE DEPENDENT RECORD [a, b, c: WORD];
  EthernetLocation: TYPE = MACHINE DEPENDENT RECORD [
    bfn(0): BootFileNumber, address(3): System.NetworkAddress];
  
  defaultBootFileNumber: BootFileNumber = LOOPHOLE[nsBootFileBase];


  -- This code should live in other modules.  Having it here avoids dragging in trash.

  eprom: DicentraInputOutput.IOAddress = MultibusAddresses.eprom;
  timeout: DicentraInputOutput.IOAddress = MultibusAddresses.timeout;

  DeactivateWatchdog: PROCEDURE =
    BEGIN
    DicentraInputOutput.Output[002H, timeout + 00CH];  -- Counter 3 Cntrl ← Trgr, Gate off
    END;
    
  eeRom: POINTER TO EERom.WholeEERom = LOOPHOLE[8000H];
  writePulse: WORD = 0800H;  -- NB: Low TRUE
  firstChip: CARDINAL = 4;

  ReadByte: PROCEDURE [loc: POINTER, offset: CARDINAL] RETURNS [data: Environment.Byte] =
    BEGIN
    byteIndex, chip, byteWithinChip: CARDINAL;
    byteIndex ← Environment.bytesPerWord*LOOPHOLE[loc, CARDINAL] + offset;
    chip ← firstChip + (byteIndex / EERom.bytesPerChip);
    byteWithinChip ← byteIndex MOD EERom.bytesPerChip;
    byteWithinChip ← byteWithinChip + writePulse;
    DicentraInputOutput.Output[Inline.HighByte[byteWithinChip], eprom + 00DH];  -- Port A Data
    DicentraInputOutput.Output[Inline.LowByte[byteWithinChip], eprom + 00EH];  -- Port B Data
    DicentraInputOutput.Output[0FFH, timeout + 023H];  -- Port A Direction ← All in
    DicentraInputOutput.Output[000H, eprom + 00FH];  -- Port C Data ← Output Enable
    DicentraInputOutput.Output[chip, timeout + 00EH];  -- Port B Data
    data ← DicentraInputOutput.Input[timeout + 00DH];  -- Port A Data
    DicentraInputOutput.Output[008H, eprom + 00FH];  -- Port C Data ← No Output Enable
    DicentraInputOutput.Output[00FH, timeout + 00EH];  -- Port B Data ← Idle
    END;


  FetchBootLocation: PROCEDURE [bits: LONG POINTER TO EERom.BootLocation] =
    BEGIN
    info: LONG POINTER TO PACKED ARRAY [0..0) OF Environment.Byte ← LOOPHOLE[bits];
    address: POINTER ← @eeRom.bootLocation;
    FOR i: CARDINAL IN [0..Environment.bytesPerWord*SIZE[EERom.BootLocation]) DO
      info[i] ← ReadByte[address, i];
      ENDLOOP;
    END;
  
  VersionValid: PROCEDURE [bootLocation: POINTER TO EERom.BootLocation] RETURNS [ok: BOOLEAN] =
    BEGIN
    RETURN[bootLocation.version = EERom.bootLocationVersion];
    END;
    
  ChecksumValid: PROCEDURE [bootLocation: POINTER TO EERom.BootLocation] RETURNS [ok: BOOLEAN] =
    BEGIN
    -- checksum is known to be the first word
    checksum: WORD = Checksum.ComputeChecksum[0, SIZE[EERom.BootLocation]-1, bootLocation+1];
    RETURN[checksum = bootLocation.checksum];
    END;
    
  END....