-- Copyright (C) 1982, 1983  by Xerox Corporation. All rights reserved. 
-- SlaDriver.mesa, HGM, 30-Dec-83 15:54:15

DIRECTORY
  Environment USING [Byte],
  Heap USING [systemZone],
  Inline USING [LowHalf],
  System USING [GetClockPulses, HostNumber, MicrosecondsToPulses, nullNetworkNumber, Pulses],

  Buffer USING [AccessHandle, Buffer, GetBuffer, MakePool, NSBuffer, PupBuffer, Type],
  Driver USING [
    NetworkObject, Network, PutOnGlobalDoneQueue,
    AddDeviceToChain, GetInputBuffer, PutOnGlobalInputQueue, ReturnFreeBuffer],
  DriverTypes USING [slaEncapsulationOffset, slaEncapsulationBytes],
  Process USING [Detach, MsecToTicks, SetPriority, SetTimeout],
  ProcessPriorities USING [priorityIOHigh],
  PupTypes USING [PupHostID, PupErrorCode],
  PupWireFormat USING [MesaToBcplLongNumber],
  RS232CCorrespondents USING [xerox850],
  RS232CEnvironment USING [CommParamObject],
  RS232C USING [
    ChannelHandle, ChannelSuspended, CompletionHandle, Create, DeviceStatus,
    Get, GetStatus, PhysicalRecord, PhysicalRecordHandle, Put, Restart,
    SetLineType, SetParameter, Suspend, TransferWait, TransferStatus, TransmitNow],
    
  PhoneCreate USING [],
  Sla USING [
    dle, DriverStatistics, etx, LineInfo, longHop, maxHops, maxSlaHost, noPartner,
    overheadPerPacket, RoutingTableEntry, RoutingTablePacket, SlaHost, State, stx,
    syn],
  SlaFormat USING [LineState, SlaStatsEntry, slaStatsReply, slaVersion];

SlaDriver: MONITOR
  IMPORTS
    Heap, Inline, Process, System, Buffer, Driver, PupWireFormat,
    RS232C
  EXPORTS Buffer, PhoneCreate
  SHARES Buffer =
  BEGIN

  z: UNCOUNTED ZONE = Heap.systemZone;

  nonAltoEtx: BOOLEAN ← FALSE;

  Byte: TYPE = Environment.Byte;
  -- EXPORTed TYPEs
  Network: PUBLIC TYPE = Driver.Network;

  info: LONG POINTER TO Sla.LineInfo;
  RoutingTable: TYPE = ARRAY Sla.SlaHost OF Sla.RoutingTableEntry;
  routingTable: LONG POINTER TO ARRAY Sla.SlaHost OF Sla.RoutingTableEntry ← NIL;
  stats: Sla.DriverStatistics;
  pool: Buffer.AccessHandle;

  outstandingGets: CARDINAL = 2;
  channelHandle: RS232C.ChannelHandle;

  -- The microcode doesn't help as much as it should
  copySize: CARDINAL = 2*1000;
  Copy: TYPE = PACKED ARRAY [0..copySize) OF Byte;
  sendCopy: LONG POINTER TO PACKED ARRAY [0..0) OF Byte;
  recvCopy: ARRAY [0..outstandingGets) OF LONG POINTER TO PACKED ARRAY [0..0) OF Byte;

  packetToSend: CONDITION;
  currentOutputBuffer, headOutputBuffer, tailOutputBuffer: Buffer.Buffer;
  packetsOnSendQueue: CARDINAL ← 0;


  myNet: Driver.NetworkObject ← [
    next: NIL,
    decapsulateBuffer: Decapsulate,
    encapsulateAndSendPup: SendPup,
    encapsulateAndSendNS: SendNS,
    sendRawBuffer: SendRawBuffer,
    encapsulateAndForwardPup: ForwardPup,
    encapsulateAndForwardNS: ForwardNS,
    activateDriver: ActivateDriver,
    deactivateDriver: DeactivateDriver,
    deleteDriver: KillDriver,
    changeNumberOfInputBuffers: NIL,
    index:,
    device: sla,
    alive: TRUE,
    buffers: 0,
    netNumber: System.nullNetworkNumber,
    pupNetNumber: 0,
    pupHostNumber: 0,
    pupStats: SlaStats, statsLevel0: @stats, statsLevel1: NIL];
  me: Network ← @myNet;


  CreateSla: PUBLIC PROCEDURE [host, net: CARDINAL] =
    BEGIN
    temp: LONG POINTER;
    commParm: RS232CEnvironment.CommParamObject ← [
      full, bitSynchronous, bps9600, directConn[]];
    channelHandle ← RS232C.Create[0, @commParm, preemptNever, preemptNever];
    currentOutputBuffer ← headOutputBuffer ← tailOutputBuffer ← NIL;
    me.pupHostNumber ← host;
    me.pupNetNumber ← net;
    routingTable ← z.NEW[RoutingTable];
    routingTable↑ ← ALL[[hops: Sla.longHop, line: 0]];
    info ← z.NEW[Sla.LineInfo];
    info↑ ← [
      state: down, partner: Sla.noPartner, packetsSent: 0, packetsRecv: 0,
      bytesSent: 0, bytesRecv: 0, sendErrors: 0, recvErrors: 0, syncErrors: 0,
      crcErrors: 0, controlErrors: 0, garbagePackets: 0, deaths: 0, stuck: 0,
      timeout: 0, overrun: 0];
    stats ← [1, LOOPHOLE[info], routingTable];
    pool ← Buffer.MakePool[1, outstandingGets];
    temp ← z.NEW[Copy];
    sendCopy ← temp;
    FOR i: CARDINAL IN [0..outstandingGets) DO
      temp ← z.NEW[Copy]; recvCopy[i] ← temp; ENDLOOP;
    Driver.AddDeviceToChain[me];
    END;

  ActivateDriver: PROCEDURE =
    BEGIN
    RS232C.SetLineType[channelHandle, byteSynchronous];
    RS232C.SetParameter[
      channelHandle, [correspondent[RS232CCorrespondents.xerox850]]];
    RS232C.SetParameter[channelHandle, [syncChar[Sla.syn]]];
    RS232C.SetParameter[channelHandle, [dataTerminalReady[TRUE]]];
    RS232C.SetParameter[channelHandle, [requestToSend[TRUE]]];
    IF FALSE THEN RS232C.SetLineType[channelHandle, bitSynchronous];
    Process.Detach[FORK Watcher[]];
    Process.Detach[FORK Receiver[]];
    Process.Detach[FORK Sender[]];
    END;

  DeactivateDriver, KillDriver: PROCEDURE = BEGIN ERROR; END;


  Decapsulate: PROCEDURE [b: Buffer.Buffer] RETURNS [type: Buffer.Type] =
    BEGIN
    bytes: CARDINAL ← 2*b.driver.length;
    IF bytes < DriverTypes.slaEncapsulationBytes THEN GOTO Rejected;
    bytes ← bytes - DriverTypes.slaEncapsulationBytes;
    SELECT b.encapsulation.slaType FROM
      ns =>
        BEGIN
	IF bytes < b.ns.pktLength THEN GOTO Rejected;
        type ← ns;
        END;
      pup =>
        BEGIN
	IF bytes < b.pup.pupLength THEN GOTO Rejected;
        type ← pup;
        END;
      routing =>
        BEGIN
        ProcessRoutingPacket[b];
        Driver.ReturnFreeBuffer[b];
        type ← processed;
        END;
      ENDCASE => GOTO Rejected;
    EXITS Rejected =>
      BEGIN
      type ← rejected;
      info.garbagePackets ← info.garbagePackets + 1;
      END;
    END;

  SendPup: ENTRY PROCEDURE [b: Buffer.PupBuffer, host: PupTypes.PupHostID] =
    BEGIN
    IF info.state # up AND info.state # loopedBack THEN
      BEGIN Driver.PutOnGlobalDoneQueue[b]; RETURN; END;
    IF packetsOnSendQueue > 15 THEN
      BEGIN Driver.PutOnGlobalDoneQueue[b]; RETURN; END;
    b.encapsulation ← [
      sla[
      slaSpare1: 0, slaSpare2: 0, slaSpare3: 0, slaHi: FALSE, slaBroadcast: FALSE,
      slaTimeQueued: 0, slaSourceLine: 0, slaDestHost: 0, slaType: pup]];
    b.driver.length ← (b.pup.pupLength + 1 + DriverTypes.slaEncapsulationBytes)/2;
    SendBufferInternal[b];
    END;

  SendNS: ENTRY PROCEDURE [b: Buffer.NSBuffer, host: System.HostNumber] =
    BEGIN
    IF info.state # up AND info.state # loopedBack THEN
      BEGIN Driver.PutOnGlobalDoneQueue[b]; RETURN; END;
    IF packetsOnSendQueue > 15 THEN
      BEGIN Driver.PutOnGlobalDoneQueue[b]; RETURN; END;
    b.encapsulation ← [
      sla[
      slaSpare1: 0, slaSpare2: 0, slaSpare3: 0, slaHi: FALSE, slaBroadcast: FALSE,
      slaTimeQueued: 0, slaSourceLine: 0, slaDestHost: 0, slaType: ns]];
    b.driver.length ← (b.ns.pktLength + 1 + DriverTypes.slaEncapsulationBytes)/2;
    SendBufferInternal[b];
    END;
    
  SendRawBuffer: ENTRY PROCEDURE [b: Buffer.Buffer] =
    BEGIN
    IF packetsOnSendQueue > 15 THEN
      BEGIN Driver.PutOnGlobalDoneQueue[b]; RETURN; END;
    SendBufferInternal[b];
    END;

  ForwardPup: ENTRY PROCEDURE [b: Buffer.PupBuffer, host: PupTypes.PupHostID]
    RETURNS [PupTypes.PupErrorCode] =
    BEGIN
    IF info.state # up AND info.state # loopedBack THEN RETURN[cantGetTherePupErrorCode];
    IF packetsOnSendQueue > 10 THEN RETURN[gatewayResourceLimitsPupErrorCode];
    b.encapsulation ← [
      sla[
      slaSpare1: 0, slaSpare2: 0, slaSpare3: 0, slaHi: FALSE, slaBroadcast: FALSE,
      slaTimeQueued: 0, slaSourceLine: 0, slaDestHost: 0, slaType: pup]];
    b.driver.length ← (b.pup.pupLength + 1 + DriverTypes.slaEncapsulationBytes)/2;
    SendBufferInternal[b];
    RETURN[noErrorPupErrorCode];
    END;

  ForwardNS: ENTRY PROCEDURE [b: Buffer.PupBuffer, host: System.HostNumber] =
    BEGIN
    IF info.state # up AND info.state # loopedBack THEN
      BEGIN Driver.PutOnGlobalDoneQueue[b]; RETURN; END;
    IF packetsOnSendQueue > 10 THEN
      BEGIN Driver.PutOnGlobalDoneQueue[b]; RETURN; END;
    b.encapsulation ← [
      sla[
      slaSpare1: 0, slaSpare2: 0, slaSpare3: 0, slaHi: FALSE, slaBroadcast: FALSE,
      slaTimeQueued: 0, slaSourceLine: 0, slaDestHost: 0, slaType: ns]];
    b.driver.length ← (b.ns.pktLength + 1 + DriverTypes.slaEncapsulationBytes)/2;
    SendBufferInternal[b];
    END;

  SendBufferInternal: INTERNAL PROCEDURE [b: Buffer.Buffer] =
    BEGIN
    IF b.encapsulation.slaDestHost # 0 THEN
      BEGIN Driver.PutOnGlobalDoneQueue[b]; RETURN; END;
    b.next ← NIL;
    IF headOutputBuffer = NIL THEN headOutputBuffer ← tailOutputBuffer ← b
    ELSE BEGIN tailOutputBuffer.next ← b; tailOutputBuffer ← b; END;
    packetsOnSendQueue ← packetsOnSendQueue + 1;
    NOTIFY packetToSend;
    END;

  Sender: PROCEDURE =
    BEGIN
    Process.SetPriority[ProcessPriorities.priorityIOHigh];
    DO
      StartSending[];
      SendIt[];
      FinishSending[];
      ENDLOOP;
    END;

  StartSending: ENTRY PROCEDURE =
    BEGIN
    UNTIL headOutputBuffer # NIL DO WAIT packetToSend; ENDLOOP;
    currentOutputBuffer ← headOutputBuffer;
    headOutputBuffer ← currentOutputBuffer.next;
    timeSendStarted ← System.GetClockPulses[];  -- Keep Watcher happy
    END;

  FinishSending: ENTRY PROCEDURE =
    BEGIN
    Driver.PutOnGlobalDoneQueue[currentOutputBuffer];
    currentOutputBuffer ← NIL;
    packetsOnSendQueue ← packetsOnSendQueue - 1;
    END;

  SendIt: PROCEDURE =
    BEGIN
    b: Buffer.Buffer ← currentOutputBuffer;
    complHandle: RS232C.CompletionHandle;
    rec: RS232C.PhysicalRecord ← [
      header: [NIL, 0, 0], body:, trailer: [NIL, 0, 0]];
    to: CARDINAL ← 2;
    buffer: LONG POINTER TO PACKED ARRAY [0..0) OF Byte =
      LOOPHOLE[@b.encapsulation + DriverTypes.slaEncapsulationOffset];
    xferstatus: RS232C.TransferStatus;
    IF b = NIL THEN ERROR;
    -- DLE doubling via software (barf)
    sendCopy[0] ← Sla.dle;
    sendCopy[1] ← Sla.stx;
    FOR from: CARDINAL IN [0..b.driver.length*2) DO  -- even bytes
      byte: Byte ← buffer[from];
      IF byte = Sla.dle THEN BEGIN sendCopy[to] ← Sla.dle; to ← to + 1; END;
      sendCopy[to] ← byte;
      to ← to + 1;
      ENDLOOP;
    sendCopy[to] ← Sla.dle;
    sendCopy[to + 1] ← Sla.etx;
    IF nonAltoEtx THEN sendCopy[to + 1] ← Sla.etx - 200B;  --  *************************
    to ← to + 2;
    IF to > copySize THEN ERROR;
    rec.body ← [blockPointer: sendCopy, startIndex: 0, stopIndexPlusOne: to];
    complHandle ← RS232C.Put[channelHandle, @rec !
      RS232C.ChannelSuspended => GOTO Zapped];
    timeSendStarted ← System.GetClockPulses[];
    [, xferstatus] ← RS232C.TransmitNow[channelHandle, complHandle];
    IF xferstatus = aborted THEN GOTO Zapped;
    IF xferstatus = success THEN
      BEGIN
      info.packetsSent ← info.packetsSent + 1;
      info.bytesSent ← info.bytesSent + 2*b.driver.length;
      END
    ELSE info.sendErrors ← info.sendErrors + 1;
    EXITS Zapped => NULL;
    END;

  timeSendStarted: System.Pulses;
  routingInterval: System.Pulses = System.MicrosecondsToPulses[5000000];
  routingTimeout: System.Pulses = [3*routingInterval];

  Watcher: ENTRY PROCEDURE =
    BEGIN
    routeTime: System.Pulses ← System.GetClockPulses[];
    watchit: CONDITION;
    Process.SetTimeout[@watchit, Process.MsecToTicks[500]];
    Process.SetPriority[ProcessPriorities.priorityIOHigh];
    DO
      WAIT watchit;
      -- should check for lost interrupts here if there is an easy way
      FlushDeadLines[];
      IF System.GetClockPulses[] - routeTime > routingInterval THEN
        BEGIN
        SendRoutingPacket[];
        routeTime ← [routeTime + routingInterval];
        IF System.GetClockPulses[] - routeTime > routingInterval THEN
          -- don't thrash if buffers are hard to get
          routeTime ← System.GetClockPulses[];
        END;
      ENDLOOP;
    END;

  -- 9600 baud is 1200 characters/sec.  Allow 10% for slop and whatever to get 1000.
  -- There is also the possibility for DLE doubling, so that is another factor of 2.
  -- The framing is 9 bytes: SYN, SYN, SYN, DLE, STX, ..... DLE, ETX, CRC, CRC

  -- Bletch, 2400 baud has come back again   /HGM, 25-Jan-82 20:41:02

  pulsesPerByte: System.Pulses = System.MicrosecondsToPulses[4*1000*2];

  FlushDeadLines: INTERNAL PROCEDURE =
    BEGIN
    b: Buffer.Buffer;
    stuck, timeout: BOOLEAN;
    t: System.Pulses ← System.GetClockPulses[];
    THROUGH [0..1) DO
      b ← currentOutputBuffer;
      stuck ←
        (b # NIL
          AND
            ((t - timeSendStarted) >
              (Sla.overheadPerPacket + b.driver.length*2)*pulsesPerByte));
      timeout ← (t - timeOfLastRT) > routingTimeout;
      IF stuck THEN
        BEGIN
        tick: CONDITION;
        Process.SetTimeout[@tick, 1];
        RS232C.Suspend[channelHandle, output];
        UNTIL packetsOnSendQueue = 0 DO WAIT tick; ENDLOOP;
        RS232C.Restart[channelHandle, output];
        END;
      IF timeout OR stuck THEN
        BEGIN
        PurgeLineFromRT[];
        IF info.state = up OR info.state = loopedBack THEN
          BEGIN
          info.deaths ← info.deaths + 1;
          IF timeout THEN info.timeout ← info.timeout + 1;
          IF stuck THEN info.stuck ← info.stuck + 1;
          END;
        info.state ← down;
        info.partner ← Sla.noPartner;
        END;
      ENDLOOP;
    END;


  Receiver: PROCEDURE =
    BEGIN
    rec: RS232C.PhysicalRecord ← [
      header: [NIL, 0, 0], body:, trailer: [NIL, 0, 0]];
    complArray: ARRAY [0..outstandingGets) OF RS232C.CompletionHandle;
    Process.SetPriority[ProcessPriorities.priorityIOHigh];
    FOR i: CARDINAL IN [0..outstandingGets) DO
      rec.body ← [recvCopy[i], 0, copySize];
      complArray[i] ← RS232C.Get[channelHandle, @rec];
      ENDLOOP;
    DO
      FOR i: CARDINAL IN [0..outstandingGets) DO
        transferStatus: RS232C.TransferStatus;
        bytes: CARDINAL;
        [bytes, transferStatus] ← RS232C.TransferWait[
          channelHandle, complArray[i]];
        SELECT TRUE FROM
          transferStatus = success AND bytes < 6 + 22 =>
            BEGIN info.garbagePackets ← info.garbagePackets + 1; END;
          transferStatus = success =>
            BEGIN
            b: Buffer.Buffer ← Driver.GetInputBuffer[];
            to: CARDINAL ← 0;
            IF b # NIL THEN
              BEGIN
              maxBytes: CARDINAL ←
                2*(b.driver.length - DriverTypes.slaEncapsulationOffset);
              finger: LONG POINTER TO PACKED ARRAY [0..0) OF Byte = recvCopy[i];
              thumb: LONG POINTER TO PACKED ARRAY [0..0) OF Byte =
                LOOPHOLE[@b.encapsulation + DriverTypes.slaEncapsulationOffset];
              dleSeen: BOOLEAN ← FALSE;
              FOR i: CARDINAL IN [2..bytes - 4) DO  -- 2 for dle stx, 4 for dle etx crc crc
                byte: Byte ← finger[i];
                IF dleSeen THEN dleSeen ← FALSE
                ELSE IF byte = Sla.dle THEN BEGIN dleSeen ← TRUE; LOOP; END;
                IF to > maxBytes THEN EXIT;  -- Ignore rest of packet
                thumb[to] ← byte;
                to ← to + 1;
                ENDLOOP;
              b.driver ← [
	        length: to/2,
		iocb: NIL,
		faceStatus: unknown[100H] ];
              b.network ← me;
              Driver.PutOnGlobalInputQueue[b];
              END;
            info.packetsRecv ← info.packetsRecv + 1;
            info.bytesRecv ← info.bytesRecv + to;
            END
          ENDCASE =>
            BEGIN
            info.recvErrors ← info.recvErrors + 1;
            SELECT transferStatus FROM
              dataLost => info.overrun ← info.overrun + 1;
              checksumError => info.crcErrors ← info.crcErrors + 1;
              deviceError => info.controlErrors ← info.controlErrors + 1;
              ENDCASE => NULL;
            END;
        IF transferStatus = deviceError THEN
          BEGIN
          globalStatus: RS232C.DeviceStatus ← RS232C.GetStatus[channelHandle];
          IF globalStatus.dataLost THEN
            BEGIN
            RS232C.SetParameter[channelHandle, [latchBitClear[globalStatus]]];
            END;
          END;
        rec.body ← [recvCopy[i], 0, copySize];
        complArray[i] ← RS232C.Get[channelHandle, @rec];
        ENDLOOP;
      ENDLOOP;  -- of UNTIL
    END;


  SlaStats: PUBLIC PROCEDURE [b: Buffer.PupBuffer, network: Network]
    RETURNS [BOOLEAN] =
    BEGIN
    stateToExternalState: ARRAY Sla.State OF SlaFormat.LineState = [
      down: down, loopedBack: loopedBack, halfUp: halfUp, up: up];
    activeLines: CARDINAL = 1;
    sizeOfRoutingTable: CARDINAL = Sla.maxSlaHost*SIZE[Sla.RoutingTableEntry];
    rte: LONG POINTER TO Sla.RoutingTableEntry;
    sse: LONG POINTER TO SlaFormat.SlaStatsEntry;
    b.pup.pupWords[0] ← SlaFormat.slaStatsReply;
    b.pup.pupWords[1] ← SlaFormat.slaVersion;
    b.pup.pupWords[2] ← Sla.maxSlaHost;
    rte ← LOOPHOLE[@b.pup.pupWords[3]];
    FOR host: Sla.SlaHost IN Sla.SlaHost DO
      rte↑ ← routingTable[host]; rte ← rte + SIZE[Sla.RoutingTableEntry]; ENDLOOP;
    b.pup.pupWords[3 + sizeOfRoutingTable] ← activeLines - 1;
    sse ← LOOPHOLE[@b.pup.pupWords[4 + sizeOfRoutingTable]];
    sse↑ ← [
      packetsSent: PupWireFormat.MesaToBcplLongNumber[info.packetsSent],
      packetsRecv: PupWireFormat.MesaToBcplLongNumber[info.packetsRecv],
      bytesSent: PupWireFormat.MesaToBcplLongNumber[info.bytesSent],
      bytesRecv: PupWireFormat.MesaToBcplLongNumber[info.bytesRecv],
      syncErrors: Inline.LowHalf[info.syncErrors],
      badCrc: Inline.LowHalf[info.crcErrors],
      controlError: Inline.LowHalf[info.controlErrors],
      state: stateToExternalState[info.state]];
    b.pup.pupLength ←
      22 + 2*(4 + sizeOfRoutingTable + activeLines*SIZE[SlaFormat.SlaStatsEntry]);
    RETURN[TRUE];
    END;

  timeOfLastRT: System.Pulses;

  ProcessRoutingPacket: ENTRY PROCEDURE [b: Buffer.Buffer] =
    BEGIN
    p: LONG POINTER TO Sla.RoutingTablePacket = LOOPHOLE[@b.rawWords];
    timeOfLastRT ← System.GetClockPulses[];
    PurgeLineFromRT[];
    IF p.sourceHost = me.pupHostNumber THEN
      BEGIN
      info.state ← loopedBack;
      routingTable[me.pupHostNumber] ← [hops: 1, line: 0];
      END
    ELSE
      BEGIN
      SELECT info.state FROM
        up => IF p.rt[me.pupHostNumber].hops # 1 THEN info.state ← halfUp;
        halfUp => IF p.rt[me.pupHostNumber].hops = 1 THEN info.state ← up;
        ENDCASE => info.state ← halfUp;
      IF info.state = up THEN
        FOR i: CARDINAL IN (0..MIN[p.numEntries, Sla.maxSlaHost]) DO
          newHop: CARDINAL = p.rt[i].hops + 1;
          rte: LONG POINTER TO Sla.RoutingTableEntry = @routingTable[i];
          IF newHop <= rte.hops THEN
            BEGIN
            rte.hops ← IF newHop <= Sla.maxHops THEN newHop ELSE Sla.longHop;
            rte.line ← 0;
            END;
          ENDLOOP;
      END;
    info.partner ← p.sourceHost;
    END;

  PurgeLineFromRT: PROCEDURE =
    BEGIN
    FOR host: Sla.SlaHost IN Sla.SlaHost DO
      routingTable[host].hops ← Sla.longHop; ENDLOOP;
    END;

  SendRoutingPacket: INTERNAL PROCEDURE =
    BEGIN
    b: Buffer.Buffer;
    p: LONG POINTER TO Sla.RoutingTablePacket;
    UNTIL (b ← Buffer.GetBuffer[raw, pool, send, fullBuffer, FALSE]) # NIL DO
      pause: CONDITION;
      Process.SetTimeout[@pause, Process.MsecToTicks[100]];
      WAIT pause;
      ENDLOOP;
    p ← LOOPHOLE[@b.rawWords];
    p.sourceHost ← me.pupHostNumber;
    p.numEntries ← Sla.maxSlaHost;
    p.rt ← routingTable↑;
    p.rt[me.pupHostNumber].hops ← 0;
    IF info.state = halfUp AND info.partner IN Sla.SlaHost THEN
      p.rt[info.partner].hops ← 1;
    b.driver.length ← DriverTypes.slaEncapsulationBytes/2 + SIZE[Sla.RoutingTablePacket];
    b.encapsulation ← [
      sla[
      slaSpare1:, slaSpare2:, slaSpare3:, slaHi: TRUE, slaBroadcast: TRUE,
      slaTimeQueued:, slaSourceLine: 0, slaDestHost: 0, slaType: routing]];
    SendBufferInternal[b];
    END;


  END.