-- File: GateInit.mesa
--   Last Edit: REH  April 20, 1982  1:25 PM
--   Last Edit: HGM  March 31, 1981  9:58 PM
--   Last Edit: BLyon  January 16, 1981  5:22 PM

DIRECTORY
  SDDefs USING [sBLTE, SD],
  SegmentDefs USING [VMtoDataSegment],

  ByteBlt USING [ByteBlt],
  Environment USING [Block],
  Heap USING [MakeNode],
  Process USING [Detach, Priority, Yield],
  Runtime USING [GetBcdTime, IsBound],
  Storage USING [Node],
  String USING [AppendChar, AppendString],
  System USING [Pulses, GetClockPulses, PulsesToMicroseconds],
  Time USING [AppendCurrent, Append, Unpack],

  Event USING [Item, Reason, AddNotifier],
  Put USING [Line],
  TajoMisc USING [managerWindow],
  Tool USING [Create, MakeSWsProc, MakeFileSW],
  ToolWindow USING [MakeSize, TransitionProcType],
  UserInput USING [
    CreateIndirectStringInOut, DestroyIndirectStringInOut, GetDefaultWindow,
    IgnoreKeyPNR, SetKeyPNR],
  Window USING [Handle],

  BufferDefs,
  Checksums USING [TestChecksum],
  CommUtilDefs USING [
    CopyLong, FreeBuffers, GetEthernetHostNumber, LockCode, thisIsAnAlto, UnlockCode],
  DriverDefs USING [GetGiantVector, Network, GetDeviceChain],
  ForwarderDefs USING [
    GetPointerToPupGateStats, PupForwarderOn, PeekAtRoutingPup,
    SetupForwarderThings],
  GateDefs USING [GateDebugRecord, ScanParameterFile],
  BootServerDefs USING [GetPointerToBootTable],
  Lock USING [GetLockLocation],
  NameServerDefs USING [GetCacheLocation],
  OISCP USING [OiscpPackageDestroy, OiscpPackageMake],
  PupPktOps USING [SetLockRequeueProcedureFlag],
  PupRouterDefs USING [numberOfNetworks,
    PupChecksums, PupRouterIn, PupRouterOut, PupRouterCold, PupErrors, PupSockets,
    DoForwardPupBuffer, SetPupForwarder,
    GetRoutingTable, SetPupChecksum],
  PupDefs USING [
    AdjustBufferParms, CaptureErrors, GetDoStats, GetFreePupBuffer,
    GetPupPackageUseCount, PupBuffer,
    PupPackageMake, PupRouterBroadcastThis, PupSocket, PupSocketDestroy,
    PupSocketMake, ReturnFreePupBuffer, SecondsToTocks, SetErrorSuppression,
    SetPupContentsWords, UniqueLocalPupSocketID],
  PupTypes USING [gatewaySoc, fillInPupAddress, allHosts, PupSocketID],
  Router USING [ReceivePacket, RoutingInformationPacket],
  SpecialCommunication USING [SetRouterFunction],
  StatsDefs USING [StatStart],
  Trouble USING [
    PupGlitchTrap, SetUncaughtSignalTrap, SetCodeTrap, SetMagicWindow];

GateInit: PROGRAM
  IMPORTS
    SegmentDefs,
    ByteBlt, Heap, Process, Runtime, Storage, String, System, Time, Event, Put,
    TajoMisc, Tool, ToolWindow, UserInput, StatsDefs, Trouble, Lock,
    ForwarderDefs, GateDefs, CommUtilDefs, BootServerDefs, NameServerDefs,
    PupPktOps, PupRouterDefs, PupDefs, OISCP, Router, Checksums, DriverDefs,
    SpecialCommunication
  EXPORTS GateDefs
  SHARES DriverDefs, BufferDefs =
  BEGIN OPEN GateDefs;

  typescript: PUBLIC Window.Handle;

  pilot: BOOLEAN = ~CommUtilDefs.thisIsAnAlto;

  version: STRING = [17 + 18]; -- "Alto(Pilot) Gateway of  1-Feb-79  5:59:39"

  gateDebug: GateDefs.GateDebugRecord ←
    [version, NIL, NIL, NIL, NIL, NIL, 0, NIL];
  eventItem: Event.Item ← [eventMask: 177777B, eventProc: Broom];

  GetVersionText: PUBLIC PROCEDURE RETURNS [STRING] = BEGIN RETURN[version]; END;

  CheckNetworks: PROCEDURE =
    BEGIN OPEN PupRouterDefs, PupDefs;
    b: PupBuffer;
    me: PupTypes.PupSocketID ← UniqueLocalPupSocketID[];
    network, firstNetwork: DriverDefs.Network;
    pupGateSoc: PupSocket;
    time: System.Pulses;
    stop: BOOLEAN ← FALSE;
    checker: PROCESS;
    FlushAnswers: PROCEDURE =
      BEGIN
      b: PupBuffer;
      UNTIL stop DO
	-- If we get an answer here, it is probably ok.
	-- The screwup case will look like it should get forwarded.
	IF (b ← pupGateSoc.get[]) # NIL THEN ReturnFreePupBuffer[b];
	ENDLOOP;
      END;
    firstNetwork ← DriverDefs.GetDeviceChain[];
    pupGateSoc ← PupSocketMake[me, PupTypes.fillInPupAddress, SecondsToTocks[1]];
    checker ← FORK FlushAnswers[];
    FOR i: CARDINAL IN [0..5) DO
      time ← System.GetClockPulses[];
      -- We can't just broadcast a request because the answer that we are really looking for would go back to the wrong network.
      FOR network ← firstNetwork, network.next UNTIL network = NIL DO
	b ← GetFreePupBuffer[];
	b.pupTransportControl ← 0;
	b.pupType ← gatewayRequest;
	b.pupID ← [0, i];
	b.dest.net ← [0];
	b.dest.host ← PupTypes.allHosts;
	b.dest.socket ← PupTypes.gatewaySoc;
	b.source.net ← [0];
	b.source.host ← [network.hostNumber];
	b.source.socket ← me;
	SetPupContentsWords[b, 0];
	network.encapsulatePup[b, PupTypes.allHosts];
	PupRouterDefs.SetPupChecksum[b];
	network.sendBuffer[b];
	ENDLOOP;
      UNTIL System.PulsesToMicroseconds[[System.GetClockPulses[] - time]] >
	1000000 DO Process.Yield[]; ENDLOOP;
      ENDLOOP;
    stop ← TRUE;
    JOIN checker;
    PupSocketDestroy[pupGateSoc];
    END;

  StartupGateway: PUBLIC PROCEDURE =
    BEGIN
    Maybe: PROCEDURE [proc: PROCEDURE RETURNS [POINTER]] RETURNS [POINTER] =
      BEGIN RETURN[IF ~Runtime.IsBound[proc] THEN NIL ELSE proc[]]; END;
    KeepTajoFromTakingANastyCodeFault: PROCEDURE =
      BEGIN UNTIL letTajoRun DO Process.Yield[]; ENDLOOP; END;

    lockThings, noOiscp: BOOLEAN;
    priority: Process.Priority;
    tajo: PROCESS;
    letTajoRun: BOOLEAN ← FALSE;

    String.AppendString[version, IF pilot THEN "Pilot"L ELSE "Alto"L];
    String.AppendString[version, " Gateway of "L];
    Time.Append[version, Time.Unpack[Runtime.GetBcdTime[]]];

    IF pilot THEN
      BEGIN
      [] ← CommUtilDefs.GetEthernetHostNumber[];  -- Avoid deadlock on DLions
      OISCP.OiscpPackageDestroy[];
      END;
    StatsDefs.StatStart[NIL, version]; -- This leaves a message in Mesa.typescript
    SetupToolsStuff[];
    Message["starting"L];


    [] ← PupDefs.GetDoStats[]; -- Be sure it has been started
    PupDefs.AdjustBufferParms[25, 0];
    PupDefs.CaptureErrors[Trouble.PupGlitchTrap];
    Trouble.SetUncaughtSignalTrap[];
    [priority, noOiscp] ← GateDefs.ScanParameterFile[];
    lockThings ← ~pilot AND priority # 1;

    -- This kludgy mess is trying to be sure that we don't get any false alarms from the interrupt level code fault trap.

    -- Note that three things must happen for a module to be properly locked into core:
    -- 1: It must get STARTed (START traps are a variant of NotInCore traps)
    -- 2: It must get locked into core
    -- 3: It's code pointer must point to the code
    -- At one time, locking a module in core would disable START traps.

    -- Boss locks DispatcherImpl, BufferMgr, and interrupt routines.
    IF lockThings THEN
      BEGIN
      CommUtilDefs.LockCode[PupRouterDefs.PupChecksums];
      CommUtilDefs.LockCode[PupRouterDefs.PupRouterIn];
      CommUtilDefs.LockCode[PupRouterDefs.PupRouterOut];
      CommUtilDefs.LockCode[PupRouterDefs.PupErrors];
      CommUtilDefs.LockCode[PupRouterDefs.PupSockets];
      CommUtilDefs.LockCode[PupRouterDefs.DoForwardPupBuffer];
      CommUtilDefs.LockCode[DriverDefs.GetDeviceChain];  -- Boss (for Broadcast)
      CommUtilDefs.LockCode[PupRouterDefs.PupRouterCold];
      CommUtilDefs.LockCode[Process.Detach]; -- FORK
      CommUtilDefs.LockCode[ForwarderDefs.PeekAtRoutingPup];
      CommUtilDefs.LockCode[Storage.Node]; -- For 8/48 cache in AltoEthernetDriver
      PupPktOps.SetLockRequeueProcedureFlag[TRUE];
      END;

    Trouble.SetCodeTrap[priority];
    tajo ← FORK KeepTajoFromTakingANastyCodeFault[];

    ForwarderDefs.SetupForwarderThings[];
    [] ← PupDefs.GetPupPackageUseCount[];  -- Be sure PupRouterCold has been started
    PupRouterDefs.numberOfNetworks ← 255;  -- maximum number of networks
    PupDefs.PupPackageMake[];
    [] ← PupRouterDefs.GetRoutingTable[]; -- Be sure they are "in" core
    [] ← ForwarderDefs.GetPointerToPupGateStats[];
    [] ← PupDefs.UniqueLocalPupSocketID[];
    PupDefs.SetErrorSuppression[FALSE];
    BEGIN OPEN PupDefs; -- make sure things are "in" memroy
    b: PupBuffer ← GetFreePupBuffer[];
    b.dest.socket ← b.source.socket ← PupTypes.gatewaySoc;
    b.pupType ← gatewayRequest;
    SetPupContentsWords[b, 0];
    PupRouterBroadcastThis[b];
    CommUtilDefs.CopyLong[from: @b, nwords: 1, to: @b];
    END;

    IF lockThings THEN
      BEGIN
      foo: WORD;
      finger: Environment.Block ← [@foo, 0, 1];
      CommUtilDefs.LockCode[ByteBlt.ByteBlt];
      [] ← ByteBlt.ByteBlt[finger, finger]; -- get it started
      CommUtilDefs.LockCode[Router.ReceivePacket];
      CommUtilDefs.LockCode[SDDefs.SD[SDDefs.sBLTE]]; -- multi word compare
      CommUtilDefs.LockCode[Router.RoutingInformationPacket];
      CommUtilDefs.LockCode[Heap.MakeNode];  -- RoutingTable
      CommUtilDefs.LockCode[Storage.Node];
      CommUtilDefs.LockCode[Checksums.TestChecksum];
      CommUtilDefs.LockCode[CommUtilDefs.FreeBuffers];  -- AMCUCold
      CommUtilDefs.LockCode[SegmentDefs.VMtoDataSegment];  -- SegmensB
      END;

    IF ~noOiscp THEN
      BEGIN
      OISCP.OiscpPackageMake[];
      [] ← SpecialCommunication.SetRouterFunction[interNetworkRouting,0];
      END;

    PupRouterDefs.SetPupForwarder[ForwarderDefs.PeekAtRoutingPup];
    CheckNetworks[];
    PupRouterDefs.SetPupForwarder[PupRouterDefs.DoForwardPupBuffer];
    ForwarderDefs.PupForwarderOn[]; -- after thrashing

    letTajoRun ← TRUE;
    JOIN tajo;
    Trouble.SetCodeTrap[1];

    IF lockThings THEN
      BEGIN
      CommUtilDefs.UnlockCode[PupRouterDefs.PupRouterCold];
      CommUtilDefs.UnlockCode[Process.Detach];
      CommUtilDefs.UnlockCode[ForwarderDefs.PeekAtRoutingPup];
      END;

    Message["in operation"L];

    DriverDefs.GetGiantVector[].spare ← @gateDebug;
    gateDebug.boot ← Maybe[BootServerDefs.GetPointerToBootTable];
    gateDebug.cache ← Maybe[NameServerDefs.GetCacheLocation];
    gateDebug.locks ← Maybe[Lock.GetLockLocation];
    END;

  MakeSWs: Tool.MakeSWsProc =
    BEGIN
    typescript ← Tool.MakeFileSW[window: window, name: "Gate.Typescript"L];
    Trouble.SetMagicWindow[typescript];
    END;

  ClientTransition: ToolWindow.TransitionProcType =
    BEGIN
    SELECT TRUE FROM
      old = inactive => NULL;
      new = inactive => BEGIN typescript ← NIL; Trouble.SetMagicWindow[NIL]; END;
      ENDCASE;
    END;

  SetupToolsStuff: PROCEDURE =
    BEGIN
    defaultWindow: Window.Handle = UserInput.GetDefaultWindow[];
    [] ← Tool.Create[
      name: version, makeSWsProc: MakeSWs, clientTransition: ClientTransition,
      initialState: active];
    -- Bug in TtySWs:  ToolWindow.Deactivate[TajoMisc.managerWindow];
    ToolWindow.MakeSize[TajoMisc.managerWindow, tiny];
    UserInput.DestroyIndirectStringInOut[defaultWindow];
    UserInput.CreateIndirectStringInOut[from: defaultWindow, to: typescript];
    UserInput.SetKeyPNR[defaultWindow, keyboard, UserInput.IgnoreKeyPNR];
    UserInput.SetKeyPNR[defaultWindow, keyset, UserInput.IgnoreKeyPNR];
    UserInput.SetKeyPNR[typescript, keyboard, UserInput.IgnoreKeyPNR];
    UserInput.SetKeyPNR[typescript, keyset, UserInput.IgnoreKeyPNR];
    END;

  Message: PROCEDURE [s: STRING] =
    BEGIN
    text: STRING = [100];
    Time.AppendCurrent[text];
    String.AppendString[text, "  "L];
    String.AppendString[text, version];
    String.AppendChar[text, ' ];
    String.AppendString[text, s];
    String.AppendChar[text, '.];
    LogString[text];
    END;

  LogString: PROCEDURE [text: STRING] = BEGIN Put.Line[NIL, text]; END;

  Broom: PROCEDURE [why: Event.Reason] =
    BEGIN
    SELECT why FROM
      stopMesa => Message["finished"L];
      ENDCASE => NULL;
    END;

  -- initialization

  Event.AddNotifier[@eventItem];
  StartupGateway[];
  END.