-- File: Look.mesa,  Last Edit: HGM  March 5, 1981  5:38 AM

DIRECTORY
  Ascii USING [FF],
  File USING [nullCapability],
  Format USING [],
  ImageDefs USING [StopMesa],
  Inline USING [HighHalf, LowHalf],
  InlineDefs USING [BcplLongNumber, BcplToMesaLongNumber],
  Put USING [CR, Char, Date, Decimal, Line, Number, Text],
  Storage USING [String],
  String USING [AppendString, AppendChar, AppendLongNumber],
  Time USING [Append, AppendCurrent, Current, Unpack],
  Window USING [Handle],

  CommUtilDefs USING [magicMemoryLocation],
  PupRouterDefs,
  AltoEthernetDefs USING [ethernetStatsReply, EtherStatsEntry],
  AltoSlaDefs,
  AltoPRDefs USING [prStatsReply, PRStatsEntry],
  BootServerDefs USING [
    BootFile, BootFileObject, timeNotKnown, bootStatsRequest, bootStatsReply,
    BootStatsEntry],
  EchoServerDefs USING [echoStatsRequest, echoStatsReply, EchoStatsEntry],
  GateDefs,
  NameServerDefs USING [
    CacheEntry, CacheEntryObject, nameStatsRequest, nameStatsReply,
    NameStatsEntry],
  TimeServerDefs USING [timeStatsRequest, timeStatsReply, TimeStatsEntry],
  ForwarderDefs USING [
    forwarderStatsRequest, forwarderStatsReply, ForwardStatsEntry,
    TransitMatrixEntry],
  GateControlDefs USING [
    GateControlStatsEntry, gateControlExamine, pupStatsSend, pupStatsNak,
    pupStatsAck, gateControlStatsSend, gateControlStatsNak, gateControlStatsAck],
  Lock,
  StatsDefs,
  PupDefs USING [
    PupPackageMake, defaultNumberOfNetworks, PupBuffer, GetFreePupBuffer,
    ReturnFreePupBuffer, GetPupAddress, PupNameTrouble, AppendPupAddress,
    AppendErrorPup, GetPupContentsBytes, SetPupContentsWords, PupSocket,
    PupSocketMake, PupSocketDestroy, MsToTocks, SecondsToTocks],
  PupTypes USING [
    PupAddress, fillInSocketID, echoSoc, statSoc, miscSrvSoc, gatewaySoc],
  DriverDefs;

Look: PROGRAM
  IMPORTS ImageDefs, Inline, InlineDefs, Put, Storage, String, Time, PupDefs
  SHARES DriverDefs =
  BEGIN

  who: PupTypes.PupAddress ← [, , [0, 0]];
  name: STRING = "Twinkle";
  coreSoc: PupDefs.PupSocket;
  statSoc: PupDefs.PupSocket;
  Counters: TYPE = ARRAY StatsDefs.StatCounterIndex OF LONG INTEGER;
  Strings: TYPE = ARRAY StatsDefs.StatCounterIndex OF STRING;
  hisCounters: Counters;
  hisStrings: Strings;

  giantVector: DriverDefs.GiantVector;
  giantVectorLoc: POINTER TO DriverDefs.GiantVector;
  gateVector: GateDefs.GateDebugRecord;

  wh: Window.Handle = NIL;

  Looker: PROCEDURE =
    BEGIN
    s: StatsDefs.StatCounterIndex;
    GetBig[
      to: @hisCounters, from: giantVector.statCounters, size: SIZE[Counters]];
    FOR s IN StatsDefs.StatCounterIndex DO
      IF hisCounters[s] = 0 THEN LOOP;
      LD10[hisCounters[s]];
      Put.Char[wh, ' ];
      IF hisStrings[s] # NIL THEN Put.Line[wh, hisStrings[s]] ELSE Put.CR[wh];
      ENDLOOP;
    Put.CR[wh];
    Put.CR[wh];
    END;

  ShowBoot: PROCEDURE =
    BEGIN
    b: PupDefs.PupBuffer;
    b ← GetBootStats[];
    IF b # NIL THEN BEGIN PrintBootStats[b]; PupDefs.ReturnFreePupBuffer[b]; END;
    PrintBootTable[];
    END;

  PrintBootStats: PROCEDURE [b: PupDefs.PupBuffer] =
    BEGIN
    bse: LONG POINTER TO BootServerDefs.BootStatsEntry = LOOPHOLE[@b.pupBody];
    Put.Line[wh, "Boot Server Statistics:"L];
    PrintItem[
      InlineDefs.BcplToMesaLongNumber[bse.directories],
      "Boot directories sent: "L];
    PrintItem[
      InlineDefs.BcplToMesaLongNumber[bse.fastSends], "Boot files sent: "L];
    PrintItem[
      InlineDefs.BcplToMesaLongNumber[bse.slowSends], "Slow boot file sent: "L];
    PrintItem[
      InlineDefs.BcplToMesaLongNumber[bse.filesRecv],
      "New boot files retrieved: "L];
    Put.CR[wh];
    END;

  PrintBootTable: PROCEDURE =
    BEGIN
    bf: BootServerDefs.BootFile;
    bfo: BootServerDefs.BootFileObject;
    bf ← Read[gateVector.boot];
    Put.CR[wh];
    Put.Line[wh, "Boot File Table:"L];
    Put.Line[wh, "  Code Count AvgMs      Create Time     FileName"L];
    THROUGH [0..50) UNTIL bf = NIL DO
      Get[to: @bfo, from: bf, size: SIZE[BootServerDefs.BootFileObject]];
      O6[bfo.code];
      LD6[bfo.count];
      IF bfo.count = 0 THEN Put.Text[wh, "      "L] ELSE LD6[bfo.ms/bfo.count];
      Put.Text[wh, "  "L];
      SELECT TRUE FROM
	(bfo.file = File.nullCapability) => Put.Text[wh, "Not on this disk  "L];
	(bfo.create = BootServerDefs.timeNotKnown) =>
	  Put.Text[wh, "Unknown           "L];
	ENDCASE => Put.Date[wh, bf.create, dateTime];
      Put.Text[wh, "  "L];
      ShowString[bfo.fileName];
      Put.CR[wh];
      bf ← bfo.next;
      ENDLOOP;
    Put.CR[wh];
    Put.CR[wh];
    END;

  ShowEcho: PROCEDURE =
    BEGIN
    b: PupDefs.PupBuffer;
    b ← GetEchoStats[];
    IF b # NIL THEN BEGIN PrintEchoStats[b]; PupDefs.ReturnFreePupBuffer[b]; END;
    END;

  PrintEchoStats: PROCEDURE [b: PupDefs.PupBuffer] =
    BEGIN
    ese: LONG POINTER TO EchoServerDefs.EchoStatsEntry = LOOPHOLE[@b.pupBody];
    Put.Line[wh, "Echo Server Statistics:"L];
    PrintItem[InlineDefs.BcplToMesaLongNumber[ese.pupsEchoed], "Pups Echoed: "L];
    Put.CR[wh];
    END;


  ShowTime: PROCEDURE =
    BEGIN
    b: PupDefs.PupBuffer;
    b ← GetTimeStats[];
    IF b # NIL THEN BEGIN PrintTimeStats[b]; PupDefs.ReturnFreePupBuffer[b]; END;
    END;

  PrintTimeStats: PROCEDURE [b: PupDefs.PupBuffer] =
    BEGIN
    tse: LONG POINTER TO TimeServerDefs.TimeStatsEntry = LOOPHOLE[@b.pupBody];
    Put.Line[wh, "Time Server Statistics:"L];
    PrintItem[
      InlineDefs.BcplToMesaLongNumber[tse.tenexRequests],
      "Tenex time requests: "L];
    PrintItem[
      InlineDefs.BcplToMesaLongNumber[tse.stringRequests],
      "String time requests: "L];
    PrintItem[
      InlineDefs.BcplToMesaLongNumber[tse.altoRequests], "Alto time requests: "L];
    Put.Text[wh, "  Correction factor: "L];
    Put.Decimal[wh, tse.correction];
    Put.CR[wh];
    IF tse.resetAddress # [[0], [0], [0, 0]] THEN
      BEGIN
      reset: STRING = [20];
      resetAddress: PupTypes.PupAddress ← tse.resetAddress;
      Put.Text[wh, "  Last reset from: "L];
      PupDefs.AppendPupAddress[reset, resetAddress];
      Put.Line[wh, reset];
      END;
    Put.CR[wh];
    END;

  ShowName: PROCEDURE =
    BEGIN
    b: PupDefs.PupBuffer;
    b ← GetNameStats[];
    IF b # NIL THEN BEGIN PrintNameStats[b]; PupDefs.ReturnFreePupBuffer[b]; END;
    PrintNameCache[];
    END;

  PrintNameStats: PROCEDURE [b: PupDefs.PupBuffer] =
    BEGIN
    nse: LONG POINTER TO NameServerDefs.NameStatsEntry = LOOPHOLE[@b.pupBody];
    Put.Line[wh, "Name Lookup Server Statistics:"L];
    PrintItem[
      InlineDefs.BcplToMesaLongNumber[nse.nameRequests],
      "Name lookup requests: "L];
    PrintItem[
      InlineDefs.BcplToMesaLongNumber[nse.directoriesSend],
      "NameLookup directories sent: "L];
    PrintItem[InlineDefs.BcplToMesaLongNumber[nse.cacheHits], "Cache hits: "L];
    PrintItem[
      InlineDefs.BcplToMesaLongNumber[nse.cacheMisses], "Cache misses: "L];
    Put.CR[wh];
    END;

  PrintNameCache: PROCEDURE =
    BEGIN
    limit: CARDINAL = 10;
    ce: NameServerDefs.CacheEntry;
    ceo: NameServerDefs.CacheEntryObject;
    i: CARDINAL;
    names: ARRAY [0..limit) OF STRING;
    addrs: ARRAY [0..limit) OF PupTypes.PupAddress;
    Put.CR[wh];
    Put.Line[wh, "Cache for Name Lookup Server:"L];
    Put.Line[wh, " Count  Seq  size Name(s) <=> Address(es)"L];
    ce ← Read[gateVector.cache];
    THROUGH [0..50) UNTIL ce = NIL DO
      Get[to: @ceo, from: ce, size: SIZE[NameServerDefs.CacheEntryObject]];
      Get[to: @names, from: BASE[ceo.names], size: limit*1];
      Get[
	to: @addrs, from: BASE[ceo.addrs], size: limit*SIZE[PupTypes.PupAddress]];
      LD6[ceo.count];
      D6[ceo.sequence];
      D5[ceo.size];
      Put.Text[wh, "  "L];
      IF LENGTH[ceo.names] = 0 THEN Put.Char[wh, '?]
      ELSE
	FOR i IN [0..MIN[limit, LENGTH[ceo.names]]) DO
	  IF i # 0 THEN Put.Text[wh, ", "L]; ShowString[names[i]]; ENDLOOP;
      Put.Text[wh, " <=> "L];
      IF LENGTH[ceo.addrs] = 0 THEN Put.Char[wh, '?]
      ELSE
	FOR i IN [0..MIN[limit, LENGTH[ceo.addrs]]) DO
	  IF i # 0 THEN Put.Text[wh, ", "L]; WritePupAddress[addrs[i]]; ENDLOOP;
      Put.CR[wh];
      ce ← ceo.next;
      ENDLOOP;
    Put.CR[wh];
    Put.CR[wh];
    END;

  ShowLocks: PROCEDURE =
    BEGIN
    limit: CARDINAL = 10;
    lock: Lock.Lock;
    lo: Lock.LockObject;
    lock ← Read[gateVector.locks];
    IF lock = NIL THEN BEGIN Put.Line[wh, "Nothing is locked."L]; RETURN; END;
    Put.Line[wh, "Lock table:"L];
    Put.Line[wh, " Read W  Name"L];
    THROUGH [0..30) UNTIL lock = NIL DO
      Get[to: @lo, from: lock, size: SIZE[Lock.LockObject]];
      D5[lo.useCount];
      Put.Text[wh, IF lo.write THEN " W  "L ELSE "    "L];
      ShowString[lo.name];
      Put.CR[wh];
      lock ← lo.next;
      ENDLOOP;
    Put.CR[wh];
    Put.CR[wh];
    END;

  ShowSla: PROCEDURE =
    BEGIN OPEN AltoSlaDefs;
    p: LONG POINTER ← giantVector.slaThings;
    activeLines: CARDINAL;
    hisLineTable: LONG POINTER TO ARRAY Line OF LineTableEntry;
    hisLineInfo: LONG POINTER TO ARRAY Line OF LineInfoBlock;
    hisRoutingTable: LONG POINTER TO ARRAY SlaHost OF RoutingTableEntry;
    IF p = NIL THEN RETURN;
    p ← p + maxByte; -- skip CRC Table
    activeLines ← Read[Inline.LowHalf[p]];
    hisLineTable ← p + 8;
    p ← 8 + p + maxLine*SIZE[LineTableEntry];
    hisRoutingTable ← p;
    p ← p + maxSlaHost*SIZE[RoutingTableEntry];
    hisLineInfo ← p;
    IF activeLines > maxLine THEN ERROR;
    ShowSlaInfo[activeLines, hisLineInfo, hisRoutingTable];
    Put.CR[wh];
    Put.CR[wh];
    END;

  ShowRoutingTable: PROCEDURE =
    BEGIN
    i, k: CARDINAL;
    pupRoutingTableLength: CARDINAL;
    pupRoutingTable: ARRAY [0..PupDefs.defaultNumberOfNetworks] OF
      PupRouterDefs.RoutingTableObject;
    routing: PupRouterDefs.RoutingTableEntry;
    network: DriverDefs.NetworkObject;
    pupRoutingTableLength ← LENGTH[giantVector.pupRoutingTable];
    IF pupRoutingTableLength > LENGTH[pupRoutingTable] THEN
      BEGIN
      Put.Line[wh, "pupRoutingTable is huge - truncating."L];
      pupRoutingTableLength ← LENGTH[pupRoutingTable];
      END;
    GetBig[
      to: @pupRoutingTable, from: BASE[giantVector.pupRoutingTable],
      size: pupRoutingTableLength*SIZE[PupRouterDefs.RoutingTableObject]];
    Put.Text[wh, "

Pup Network Routing Table"L];
    Put.Line[wh, "
 ix  hp  tm  rt  network num net hst"L];
    FOR i IN [0..pupRoutingTableLength) DO
      routing ← @pupRoutingTable[i];
      IF routing.network # NIL THEN
	BEGIN
	O3[i];
	D4[routing.hop];
	D4[routing.time];
	O4[routing.route];
	Put.Text[wh, "  "L];
	O6L[LOOPHOLE[routing.network]];
	Get[
	  to: @network, from: routing.network,
	  size: SIZE[DriverDefs.NetworkObject]];
	O4[network.index];
	O4[network.netNumber.b];
	O4[network.hostNumber];
	Put.Line[wh, ""L];
	END;
      ENDLOOP;
    Put.CR[wh];
    Put.Line[wh, "Routing Table:"L];
    Put.Line[
      wh, "|  Net   Via   Hops |  Net   Via   Hops |  Net   Via   Hops |"L];
    Put.Line[
      wh, "|-------------------|-------------------|-------------------|"L];
    k ← 0;
    FOR i IN [0..pupRoutingTableLength) DO
      routing ← @pupRoutingTable[i];
      IF routing.network = NIL THEN LOOP;
      IF k = 0 THEN Put.Char[wh, '|];
      O4[i];
      Get[
	to: @network, from: routing.network,
	size: SIZE[DriverDefs.NetworkObject]];
      O4[network.netNumber.b];
      Put.Char[wh, '#];
      IF routing.hop # 0 THEN O3Z[routing.route] ELSE O3Z[network.hostNumber];
      Put.Char[wh, '#];
      O4[routing.hop];
      Put.Text[wh, "  |"L];
      IF (k ← k + 1) = 3 THEN BEGIN Put.CR[wh]; k ← 0; END;
      ENDLOOP;
    IF k # 0 THEN Put.CR[wh];
    END;

  ShowSlaInfo: PUBLIC PROCEDURE [
    lines: CARDINAL, hisInfo: LONG POINTER, hisRouting: LONG POINTER] =
    BEGIN OPEN AltoSlaDefs;
    line: Line;
    info: ARRAY Line OF LineInfoBlock;
    lib: POINTER TO LineInfoBlock;
    routing: ARRAY SlaHost OF RoutingTableEntry;
    temp: LONG INTEGER;
    Put.CR[wh];
    GetBig[to: @info, from: hisInfo, size: lines*SIZE[LineInfoBlock]];
    GetBig[
      to: @routing, from: hisRouting, size: maxSlaHost*SIZE[RoutingTableEntry]];
    Put.Line[
      wh,
      "More SLA Line Statistics:
             ----Packets----              ------Bytes-----
Line       Hi      Sent  Received        Hi      Sent  Received"L];
    FOR line IN [0..lines) DO
      lib ← @info[line];
      O3[line];
      LD10[lib.hiPacketsSent];
      LD10[lib.packetsSent];
      LD10[lib.packetsRecv];
      LD10[lib.hiBytesSent];
      LD10[lib.bytesSent];
      LD10[lib.bytesRecv];
      Put.CR[wh];
      ENDLOOP;
    Put.Line[
      wh,
      "
       ---Bits/Sec---    ---Bits/Pkt---   --Delay--  Err/10↑6
Line   Hi  Send  Recv    Hi  Send  Recv    Hi  Send  Pkt Recv"L];
    FOR line IN [0..lines) DO
      lib ← @info[line];
      O3[line];
      temp ← lib.hiBytesSent + overheadPerPacket*lib.hiPacketsSent;
      Mumble[temp, hisCounters[statSeconds]];
      temp ← lib.bytesSent + overheadPerPacket*lib.packetsSent;
      Mumble[temp, hisCounters[statSeconds]];
      temp ← lib.bytesRecv + overheadPerPacket*lib.packetsRecv;
      Mumble[temp, hisCounters[statSeconds]];
      temp ← lib.hiBytesSent + overheadPerPacket*lib.hiPacketsSent;
      Mumble[temp, lib.hiPacketsSent];
      temp ← lib.bytesSent + overheadPerPacket*lib.packetsSent;
      Mumble[temp, lib.packetsSent];
      temp ← lib.bytesRecv + overheadPerPacket*lib.packetsRecv;
      Mumble[temp, lib.packetsRecv];
      temp ← lib.hiQueueDelay*39;
      LD6[temp/lib.hiPacketsSent];
      temp ← lib.queueDelay*39;
      LD6[temp/lib.packetsSent];
      temp ← lib.syncErrors + lib.crcErrors + lib.controlErrors;
      LD10[temp*1000000/lib.packetsRecv];
      Put.CR[wh];
      ENDLOOP;
    Put.Line[wh, "
Line    Deaths     Stuck   Timeout   Overrun"L];
    FOR line IN [0..lines) DO
      lib ← @info[line];
      O3[line];
      LD10[lib.deaths];
      LD10[lib.stuck];
      LD10[lib.timeout];
      LD10[lib.overrun];
      Put.CR[wh];
      ENDLOOP;
    END;

  -- Rats, 32 bits isn't enough
  -- Print a*8/b

  Mumble: PROCEDURE [a, b: LONG INTEGER] =
    BEGIN
    temp: LONG INTEGER;
    SELECT a FROM < 200000000 => temp ← (a*8)/b; ENDCASE => temp ← a/(b/8);
    LD6[temp];
    END;

  ShowGateStats: PROCEDURE =
    BEGIN
    b: PupDefs.PupBuffer;
    b ← GetGateStats[];
    IF b # NIL THEN BEGIN PrintGateStats[b]; PupDefs.ReturnFreePupBuffer[b]; END;
    b ← GetForwarderStats[];
    IF b # NIL THEN
      BEGIN
      PrintForwarderStats[b];
      PrintForwarderMatrix[b];
      PupDefs.ReturnFreePupBuffer[b];
      END;
    END;

  PrintGateStats: PROCEDURE [b: PupDefs.PupBuffer] =
    BEGIN
    text: STRING = [50];
    i: CARDINAL;
    gse: LONG POINTER TO GateControlDefs.GateControlStatsEntry ←
      LOOPHOLE[@b.pupWords];
    Put.CR[wh];
    FOR i IN [0..gse.versionText.length) DO
      String.AppendChar[text, gse.versionText.char[i]]; ENDLOOP;
    Put.Line[wh, "Gateway status: "L];
    Put.Text[wh, text];
    PrintUpTime[gse.startTime];
    Put.CR[wh];
    PrintItem[gse.freeBuffers, "Free buffers: "L];
    PrintItem[gse.freeDiskPages, "Free disk pages: "L];
    Put.CR[wh];
    END;

  PrintUpTime: PROCEDURE [startTime: InlineDefs.BcplLongNumber] =
    BEGIN
    now, then: LONG CARDINAL;
    sec: LONG CARDINAL;
    min: LONG CARDINAL;
    hours: LONG CARDINAL;
    Put.Text[wh, ", up "L];
    then ← InlineDefs.BcplToMesaLongNumber[startTime];
    now ← Time.Current[];
    sec ← now - then;
    hours ← sec/3600;
    sec ← sec - hours*3600;
    min ← sec/60;
    sec ← sec - min*60;
    LD[hours];
    Put.Char[wh, ':];
    LD[min];
    Put.Char[wh, ':];
    LD[sec];
    END;

  PrintTime: PROCEDURE =
    BEGIN
    text: STRING = [30];
    Time.AppendCurrent[text];
    Put.Text[wh, text];
    Put.Text[wh, "  "];
    END;


  PrintForwarderStats: PROCEDURE [b: PupDefs.PupBuffer] =
    BEGIN
    fse: LONG POINTER TO ForwarderDefs.ForwardStatsEntry = LOOPHOLE[@b.pupBody];
    Put.Line[wh, "Routing Server Statistics:"L];
    PrintItem[
      InlineDefs.BcplToMesaLongNumber[fse.routingInfoRequests],
      "Routing info requests: "L];
    Put.CR[wh];
    END;

  PrintForwarderMatrix: PROCEDURE [b: PupDefs.PupBuffer] =
    BEGIN
    FindIt: PROCEDURE [from, to: CARDINAL] =
      BEGIN
      i: CARDINAL;
      tme: LONG POINTER TO ForwarderDefs.TransitMatrixEntry;
      FOR i IN [0..fse.numberOfTMEs) DO
	tme ← @pupStatsTable[i];
	IF from # tme.sourceNet OR to # tme.destNet THEN LOOP;
	LD10Dash[InlineDefs.BcplToMesaLongNumber[tme.count]];
	EXIT;
	REPEAT FINISHED => LD10Dash[0];
	ENDLOOP;
      END;
    nets: LONG POINTER TO ARRAY OF CARDINAL;
    pupStatsTable: LONG POINTER TO ARRAY OF ForwarderDefs.TransitMatrixEntry;
    from, to: CARDINAL;
    fse: LONG POINTER TO ForwarderDefs.ForwardStatsEntry = LOOPHOLE[@b.pupBody];
    n: CARDINAL ← fse.numberOfNetworks;
    nets ← LOOPHOLE[@b.pupBody + SIZE[ForwarderDefs.ForwardStatsEntry]];
    pupStatsTable ←
      LOOPHOLE[@b.pupBody + SIZE[ForwarderDefs.ForwardStatsEntry] + n];
    Put.CR[wh];
    Put.Line[wh, "Packets Forwarded:"L];
    Put.CR[wh];
    Put.Line[wh, "from                to"L];
    Put.CR[wh];
    Put.Text[wh, "      Discard"L];
    FOR to IN [0..n) DO Put.Text[wh, "      "L]; O4[nets[to]]; ENDLOOP;
    Put.CR[wh];
    FOR from IN [0..n) DO
      O4[nets[from]];
      FindIt[nets[from], 0];
      FOR to IN [0..n) DO FindIt[nets[from], nets[to]]; ENDLOOP;
      Put.CR[wh];
      ENDLOOP;
    END;

  ShowDriverStats: PROCEDURE =
    BEGIN
    i: CARDINAL;
    b: PupDefs.PupBuffer;
    Put.Line[wh, "Driver statistics:"];
    Put.CR[wh];
    FOR i IN [0..35) DO
      b ← GetPupStats[i];
      IF b = NIL THEN LOOP;
      Put.Text[wh, "Network "];
      O[i];
      Put.CR[wh];
      PrintDriverStats[b];
      PupDefs.ReturnFreePupBuffer[b];
      Put.CR[wh];
      ENDLOOP;
    END;

  PrintDriverStats: PROCEDURE [b: PupDefs.PupBuffer] =
    BEGIN
    IF b = NIL THEN BEGIN Put.Line[wh, "Not Available."]; RETURN; END;
    SELECT b.pupWords[0] FROM
      AltoEthernetDefs.ethernetStatsReply =>
	PrintEtherStats[LOOPHOLE[@b.pupWords[1]]];
      AltoSlaDefs.slaStatsReply => PrintSlaStats[b];
      AltoPRDefs.prStatsReply => PrintPrStats[LOOPHOLE[@b.pupWords[1]]];
      ENDCASE => Put.Line[wh, "Unknown network type."];
    END;

  PrintEtherStats: PROCEDURE [
    p: LONG POINTER TO AltoEthernetDefs.EtherStatsEntry] =
    BEGIN OPEN p;
    i: CARDINAL;
    Put.Text[wh, "Rcv:"];
    Put.Text[wh, " good "];
    LD[InlineDefs.BcplToMesaLongNumber[packetsRecv]];
    Put.Text[wh, ", bad "];
    LD[InlineDefs.BcplToMesaLongNumber[badRecvStatus]];
    Put.Text[wh, ", off "];
    LD[InlineDefs.BcplToMesaLongNumber[inputOff]];
    Put.Text[wh, "; Xmit:"];
    Put.Text[wh, " good "];
    LD[InlineDefs.BcplToMesaLongNumber[packetsSent]];
    Put.Text[wh, ", bad "];
    LD[InlineDefs.BcplToMesaLongNumber[badSendSatus]];
    Put.Text[wh, "; Overrun "];
    LD[InlineDefs.BcplToMesaLongNumber[overruns]];
    Put.CR[wh];
    Put.Text[wh, "Lds:"];
    FOR i IN [0..16) DO
      Put.Char[wh, ' ];
      LD[InlineDefs.BcplToMesaLongNumber[loadTable[i]]];
      ENDLOOP;
    Put.Text[wh, "; Ovf: "];
    LD[InlineDefs.BcplToMesaLongNumber[loadTable[16]]];
    Put.CR[wh];
    END;

  PrintSlaStats: PROCEDURE [b: PupDefs.PupBuffer] =
    BEGIN
    -- watch the order of these things
    maxHost: CARDINAL = b.pupWords[2];
    sizeOfRoutingTable: CARDINAL = maxHost*SIZE[AltoSlaDefs.RoutingTableEntry];
    activeLines: CARDINAL = b.pupWords[3 + sizeOfRoutingTable] + 1;
    routing: LONG SlaRoutingInfo = DESCRIPTOR[@b.pupWords[3], maxHost];
    line: AltoSlaDefs.Line;
    sse: LONG POINTER TO AltoSlaDefs.SlaStatsEntry;
    IF b.pupWords[1] # AltoSlaDefs.slaVersion THEN
      Put.Line[wh, "**** BARF: Wrong version number."];
    IF b.pupWords[2] # AltoSlaDefs.maxSlaHost THEN
      Put.Line[wh, "**** BARF: Wrong routing table size."];
    IF activeLines > 16 THEN Put.Line[wh, "**** BARF: Too many lines."];
    Put.Line[
      wh,
      "SLA Line Statistics:
             ---Packets----    ------Bytes-----   ------Errors-----
Line To      Sent  Received      Sent  Received   CRC  Sync Control   State"L];
    sse ← LOOPHOLE[@b.pupWords[4 + sizeOfRoutingTable]];
    FOR line IN [0..MIN[activeLines, 16]) DO
      O3[line];
      PrintOtherEnd[line, routing];
      LD10[InlineDefs.BcplToMesaLongNumber[sse.packetsSent]];
      LD10[InlineDefs.BcplToMesaLongNumber[sse.packetsRecv]];
      LD10[InlineDefs.BcplToMesaLongNumber[sse.bytesSent]];
      LD10[InlineDefs.BcplToMesaLongNumber[sse.bytesRecv]];
      D6[sse.badCrc];
      D6[sse.syncErrors];
      D6[sse.controlError];
      Put.Text[wh, "     "L];
      SELECT LOOPHOLE[sse.state, AltoSlaDefs.LineState] FROM
	down => Put.Line[wh, "Down"L];
	halfUp => Put.Line[wh, "Half Up"L];
	up => Put.Line[wh, "Up"L];
	loopedBack => Put.Line[wh, "Looped back"L];
	missing => Put.Line[wh, "Missing"L];
	ENDCASE => Put.Line[wh, "?"L];
      sse ← sse + SIZE[AltoSlaDefs.SlaStatsEntry];
      ENDLOOP;
    PrintSlaRoutingTable[routing];
    END;


  SlaRoutingInfo: TYPE = DESCRIPTOR FOR ARRAY OF AltoSlaDefs.RoutingTableEntry;

  PrintOtherEnd: PROCEDURE [
    line: AltoSlaDefs.Line, routing: LONG SlaRoutingInfo] =
    BEGIN
    host: AltoSlaDefs.SlaHost;
    rte: LONG POINTER TO AltoSlaDefs.RoutingTableEntry;
    FOR host IN AltoSlaDefs.SlaHost DO
      rte ← @routing[host];
      IF rte.line = line AND rte.hops = 1 THEN BEGIN O4[host + 1]; EXIT; END;
      REPEAT FINISHED => Put.Text[wh, "   ?"L];
      ENDLOOP;
    END;

  PrintSlaRoutingTable: PROCEDURE [routing: LONG SlaRoutingInfo] =
    BEGIN
    host: AltoSlaDefs.SlaHost;
    rte: LONG POINTER TO AltoSlaDefs.RoutingTableEntry;
    k: CARDINAL;
    Put.Line[
      wh,
      "
Routing Table:
Host Line Hops    Host Line Hops    Host Line Hops    Host Line Hops"L];
    k ← 0;
    FOR host IN [0..LENGTH[routing]) DO
      rte ← @routing[host];
      IF rte.hops = AltoSlaDefs.longHop THEN LOOP;
      IF k # 0 THEN Put.Text[wh, "    "L];
      O4[host];
      D5[rte.line];
      D5[rte.hops];
      IF (k ← k + 1) = 4 THEN BEGIN Put.CR[wh]; k ← 0; END;
      ENDLOOP;
    IF k # 0 THEN Put.CR[wh];
    END;

  PrintPrStats: PROCEDURE [p: LONG POINTER TO AltoPRDefs.PRStatsEntry] =
    BEGIN OPEN p;
    i: CARDINAL;
    Put.Text[wh, "Rcv:"];
    Put.Text[wh, " packets "];
    LD[packetsReceived];
    Put.Text[wh, ", pups "];
    LD[oneFragPupRcvd];
    Put.Text[wh, ", "];
    LD[twoFragPupRcvd];
    Put.Text[wh, ", "];
    LD[threeFragPupRcvd];
    Put.Text[wh, ", words "];
    LD[wordsReceived];
    Put.Text[wh, ", alive "];
    LD[imAliveReceived];
    Put.CR[wh];
    Put.Text[wh, "Xmit:"];
    Put.Text[wh, " packets "];
    LD[packetsSent];
    Put.Text[wh, ", pups "];
    LD[oneFragPupSent];
    Put.Text[wh, ", "];
    LD[twoFragPupSent];
    Put.Text[wh, ", "];
    LD[threeFragPupSent];
    Put.Text[wh, ", words "];
    LD[wordsSent];
    Put.Text[wh, ", TOPs "];
    LD[topsSent];
    Put.Text[wh, ", alive "];
    LD[imAliveSent];
    Put.CR[wh];
    Put.Text[wh, "Assembly timeout "];
    LD[assemblyTimeout];
    Put.Text[wh, ", Assembly overflow "];
    LD[assemblyOverflow];
    Put.Text[wh, ", Input Filter: "];
    LD[inputFilter];
    Put.CR[wh];
    Put.Text[wh, "Old Packets: "];
    LD[oldPackets];
    Put.Text[wh, "; Skipped Packets: "];
    LD[skippedPackets];
    Put.Text[wh, "; Seq Resets: "];
    LD[sequencerResets];
    Put.CR[wh];
    Put.Text[wh, "Destination down: "];
    LD[destinationDown];
    Put.Text[wh, "; Output Discard: "];
    LD[outputPacketsDiscarded];
    Put.Text[wh, "; Xfer Timeouts: "];
    LD[transferTimeout];
    Put.CR[wh];
    Put.Text[wh, "Hosts: "];
    FOR i IN [0..maxHosts) DO
      IF i # 0 THEN Put.Char[wh, ',];
      Put.Text[wh, IF hostsUp[i] THEN " up" ELSE " down"];
      ENDLOOP;
    Put.CR[wh];
    END;

  GetBootStats: PROCEDURE RETURNS [PupDefs.PupBuffer] =
    BEGIN
    b: PupDefs.PupBuffer;
    soc: PupDefs.PupSocket;
    where: PupTypes.PupAddress ← who;
    where.socket ← PupTypes.miscSrvSoc;
    soc ← PupDefs.PupSocketMake[
      PupTypes.fillInSocketID, where, PupDefs.SecondsToTocks[10]];
    thisID ← thisID + 1;
    THROUGH [0..25) DO
      b ← PupDefs.GetFreePupBuffer[];
      b.pupID.a ← b.pupID.b ← thisID;
      b.pupType ← BootServerDefs.bootStatsRequest;
      PupDefs.SetPupContentsWords[b, 0];
      soc.put[b];
      UNTIL (b ← soc.get[]) = NIL DO
	-- Until timeout, or we find the expected one
	SELECT TRUE FROM
	  (b.pupType = error) => ShowErrorPup[b];
	  (b.pupType = BootServerDefs.bootStatsReply) AND
	    (b.pupID.a = thisID AND b.pupID.b = thisID) =>
	    BEGIN PupDefs.PupSocketDestroy[soc]; RETURN[b]; END;
	  ENDCASE => BEGIN PupDefs.ReturnFreePupBuffer[b]; LOOP; END;
	PupDefs.ReturnFreePupBuffer[b];
	ENDLOOP;
      IF b # NIL THEN PupDefs.ReturnFreePupBuffer[b]
      ENDLOOP;
    PupDefs.PupSocketDestroy[soc];
    RETURN[NIL];
    END;

  GetEchoStats: PROCEDURE RETURNS [PupDefs.PupBuffer] =
    BEGIN
    b: PupDefs.PupBuffer;
    soc: PupDefs.PupSocket;
    where: PupTypes.PupAddress ← who;
    where.socket ← PupTypes.echoSoc;
    soc ← PupDefs.PupSocketMake[
      PupTypes.fillInSocketID, where, PupDefs.SecondsToTocks[10]];
    thisID ← thisID + 1;
    THROUGH [0..25) DO
      b ← PupDefs.GetFreePupBuffer[];
      b.pupID.a ← b.pupID.b ← thisID;
      b.pupType ← EchoServerDefs.echoStatsRequest;
      PupDefs.SetPupContentsWords[b, 0];
      soc.put[b];
      UNTIL (b ← soc.get[]) = NIL DO
	-- Until timeout, or we find the expected one
	SELECT TRUE FROM
	  (b.pupType = error) => ShowErrorPup[b];
	  (b.pupType = EchoServerDefs.echoStatsReply) AND
	    (b.pupID.a = thisID AND b.pupID.b = thisID) =>
	    BEGIN PupDefs.PupSocketDestroy[soc]; RETURN[b]; END;
	  ENDCASE => BEGIN PupDefs.ReturnFreePupBuffer[b]; LOOP; END;
	PupDefs.ReturnFreePupBuffer[b];
	ENDLOOP;
      IF b # NIL THEN PupDefs.ReturnFreePupBuffer[b]
      ENDLOOP;
    PupDefs.PupSocketDestroy[soc];
    RETURN[NIL];
    END;

  GetForwarderStats: PROCEDURE RETURNS [PupDefs.PupBuffer] =
    BEGIN
    b: PupDefs.PupBuffer;
    soc: PupDefs.PupSocket;
    where: PupTypes.PupAddress ← who;
    where.socket ← PupTypes.gatewaySoc;
    soc ← PupDefs.PupSocketMake[
      PupTypes.fillInSocketID, where, PupDefs.SecondsToTocks[10]];
    thisID ← thisID + 1;
    THROUGH [0..25) DO
      b ← PupDefs.GetFreePupBuffer[];
      b.pupID.a ← b.pupID.b ← thisID;
      b.pupType ← ForwarderDefs.forwarderStatsRequest;
      PupDefs.SetPupContentsWords[b, 0];
      soc.put[b];
      UNTIL (b ← soc.get[]) = NIL DO
	-- Until timeout, or we find the expected one
	SELECT TRUE FROM
	  (b.pupType = error) => ShowErrorPup[b];
	  (b.pupType = ForwarderDefs.forwarderStatsReply) AND
	    (b.pupID.a = thisID AND b.pupID.b = thisID) =>
	    BEGIN PupDefs.PupSocketDestroy[soc]; RETURN[b]; END;
	  ENDCASE => BEGIN PupDefs.ReturnFreePupBuffer[b]; LOOP; END;
	PupDefs.ReturnFreePupBuffer[b];
	ENDLOOP;
      IF b # NIL THEN PupDefs.ReturnFreePupBuffer[b]
      ENDLOOP;
    PupDefs.PupSocketDestroy[soc];
    RETURN[NIL];
    END;

  GetNameStats: PROCEDURE RETURNS [PupDefs.PupBuffer] =
    BEGIN
    b: PupDefs.PupBuffer;
    soc: PupDefs.PupSocket;
    where: PupTypes.PupAddress ← who;
    where.socket ← PupTypes.miscSrvSoc;
    soc ← PupDefs.PupSocketMake[
      PupTypes.fillInSocketID, where, PupDefs.SecondsToTocks[10]];
    thisID ← thisID + 1;
    THROUGH [0..25) DO
      b ← PupDefs.GetFreePupBuffer[];
      b.pupID.a ← b.pupID.b ← thisID;
      b.pupType ← NameServerDefs.nameStatsRequest;
      PupDefs.SetPupContentsWords[b, 0];
      soc.put[b];
      UNTIL (b ← soc.get[]) = NIL DO
	-- Until timeout, or we find the expected one
	SELECT TRUE FROM
	  (b.pupType = error) => ShowErrorPup[b];
	  (b.pupType = NameServerDefs.nameStatsReply) AND
	    (b.pupID.a = thisID AND b.pupID.b = thisID) =>
	    BEGIN PupDefs.PupSocketDestroy[soc]; RETURN[b]; END;
	  ENDCASE => BEGIN PupDefs.ReturnFreePupBuffer[b]; LOOP; END;
	PupDefs.ReturnFreePupBuffer[b];
	ENDLOOP;
      IF b # NIL THEN PupDefs.ReturnFreePupBuffer[b]
      ENDLOOP;
    PupDefs.PupSocketDestroy[soc];
    RETURN[NIL];
    END;

  GetTimeStats: PROCEDURE RETURNS [PupDefs.PupBuffer] =
    BEGIN
    b: PupDefs.PupBuffer;
    soc: PupDefs.PupSocket;
    where: PupTypes.PupAddress ← who;
    where.socket ← PupTypes.miscSrvSoc;
    soc ← PupDefs.PupSocketMake[
      PupTypes.fillInSocketID, where, PupDefs.SecondsToTocks[10]];
    thisID ← thisID + 1;
    THROUGH [0..25) DO
      b ← PupDefs.GetFreePupBuffer[];
      b.pupID.a ← b.pupID.b ← thisID;
      b.pupType ← TimeServerDefs.timeStatsRequest;
      PupDefs.SetPupContentsWords[b, 0];
      soc.put[b];
      UNTIL (b ← soc.get[]) = NIL DO
	-- Until timeout, or we find the expected one
	SELECT TRUE FROM
	  (b.pupType = error) => ShowErrorPup[b];
	  (b.pupType = TimeServerDefs.timeStatsReply) AND
	    (b.pupID.a = thisID AND b.pupID.b = thisID) =>
	    BEGIN PupDefs.PupSocketDestroy[soc]; RETURN[b]; END;
	  ENDCASE => BEGIN PupDefs.ReturnFreePupBuffer[b]; LOOP; END;
	PupDefs.ReturnFreePupBuffer[b];
	ENDLOOP;
      IF b # NIL THEN PupDefs.ReturnFreePupBuffer[b]
      ENDLOOP;
    PupDefs.PupSocketDestroy[soc];
    RETURN[NIL];
    END;

  GetPupStats: PROCEDURE [driver: INTEGER] RETURNS [PupDefs.PupBuffer] =
    BEGIN
    b: PupDefs.PupBuffer ← NIL;
    thisID ← thisID + 1;
    DO
      -- until we get the answer
      IF b = NIL THEN b ← PupDefs.GetFreePupBuffer[];
      b.pupID ← [27182, thisID];
      b.pupWords[0] ← driver;
      b.pupType ← GateControlDefs.pupStatsSend;
      PupDefs.SetPupContentsWords[b, 1];
      statSoc.put[b];
      b ← statSoc.get[];
      IF b = NIL THEN LOOP;
      IF b.pupID.b # thisID OR b.pupType = error THEN LOOP;
      IF b.pupType = GateControlDefs.pupStatsNak THEN
	BEGIN PupDefs.ReturnFreePupBuffer[b]; RETURN[NIL]; END;
      IF b.pupType = GateControlDefs.pupStatsAck THEN EXIT;
      ERROR;
      ENDLOOP;
    RETURN[b];
    END;

  GetGateStats: PROCEDURE RETURNS [PupDefs.PupBuffer] =
    BEGIN
    b: PupDefs.PupBuffer ← NIL;
    thisID ← thisID + 1;
    DO
      -- until we get the answer
      IF b = NIL THEN b ← PupDefs.GetFreePupBuffer[];
      b.pupID ← [27182, thisID];
      b.pupType ← GateControlDefs.gateControlStatsSend;
      PupDefs.SetPupContentsWords[b, 0];
      coreSoc.put[b];
      b ← coreSoc.get[];
      IF b = NIL THEN LOOP;
      IF b.pupID.b # thisID OR b.pupType = error THEN LOOP;
      IF b.pupType = GateControlDefs.gateControlStatsNak THEN
	BEGIN PupDefs.ReturnFreePupBuffer[b]; RETURN[NIL]; END;
      IF b.pupType = GateControlDefs.gateControlStatsAck THEN EXIT;
      ERROR;
      ENDLOOP;
    RETURN[b];
    END;

  GetBig: PROCEDURE [to: POINTER, from: LONG POINTER, size: CARDINAL] =
    BEGIN
    end: CARDINAL ← 0;
    hunk: CARDINAL;
    UNTIL end = size DO
      hunk ← MIN[250, size - end];
      Get[to + end, from + end, hunk];
      end ← end + hunk;
      ENDLOOP;
    END;

  Read: PROCEDURE [p: POINTER] RETURNS [x: UNSPECIFIED] =
    BEGIN Get[to: @x, from: p, size: 1]; END;

  -- get a block of info from the "right" address space

  thisID: CARDINAL ← 0;
  Get: PROCEDURE [to: POINTER, from: LONG POINTER, size: CARDINAL] =
    BEGIN
    b: PupDefs.PupBuffer ← NIL;
    i: CARDINAL;
    IF Inline.HighHalf[from] # 0 THEN ERROR;
    IF size > 250 THEN ERROR;
    thisID ← thisID + 1;
    DO
      -- until we get the answer
      IF b = NIL THEN b ← PupDefs.GetFreePupBuffer[];
      b.pupID ← [27182, thisID];
      b.pupWords[0] ← LOOPHOLE[Inline.LowHalf[from]];
      b.pupWords[1] ← size;
      b.pupType ← GateControlDefs.gateControlExamine;
      PupDefs.SetPupContentsWords[b, 2];
      coreSoc.put[b];
      b ← coreSoc.get[];
      IF b = NIL THEN LOOP;
      IF b.pupID.b # thisID OR b.pupType = error THEN LOOP;
      IF b.pupType = GateControlDefs.gateControlStatsAck AND
	PupDefs.GetPupContentsBytes[b] = 2*(2 + size) THEN EXIT;
      ERROR;
      ENDLOOP;
    FOR i IN [0..size) DO (to + i)↑ ← b.pupWords[2 + i]; ENDLOOP;
    PupDefs.ReturnFreePupBuffer[b];
    END;

  PrintItem: PROCEDURE [d: LONG CARDINAL, s: STRING] =
    BEGIN
    IF d = 0 THEN RETURN;
    Put.Text[wh, "  "L];
    Put.Text[wh, s];
    LD[d];
    Put.CR[wh];
    END;

  ShowString: PROCEDURE [s: STRING] =
    BEGIN
    temp: STRING = [204]; -- maxlength gets clobbered
    IF s # NIL THEN
      BEGIN
      Get[to: temp, from: s, size: 100];
      IF temp.length > 200 THEN temp.length ← 25;
      Put.Text[wh, temp];
      END
    ELSE Put.Text[wh, "NIL"L];
    END;

  WritePupAddress: PROCEDURE [p: PupTypes.PupAddress] =
    BEGIN
    Put.Number[wh, p.net, [8, FALSE, TRUE, 0]];
    Put.Char[wh, '#];
    Put.Number[wh, p.host, [8, FALSE, TRUE, 0]];
    Put.Char[wh, '#];
    IF p.socket.a # 0 THEN
      BEGIN
      Put.Number[wh, p.socket.a, [8, FALSE, TRUE, 0]];
      Put.Char[wh, '|];
      END;
    Put.Number[wh, p.socket.b, [8, FALSE, TRUE, 0]];
    END;

  ShowErrorPup: PUBLIC PROCEDURE [b: PupDefs.PupBuffer] =
    BEGIN
    text: STRING = [100];
    PupDefs.AppendErrorPup[text, b];
    Put.Line[wh, text];
    END;

  O: PROCEDURE [n: CARDINAL] = BEGIN Put.Number[wh, n, [8, FALSE, TRUE, 0]]; END;

  O3: PROCEDURE [n: CARDINAL] = BEGIN Put.Number[wh, n, [8, FALSE, TRUE, 3]]; END;

  O4: PROCEDURE [n: CARDINAL] = BEGIN Put.Number[wh, n, [8, FALSE, TRUE, 4]]; END;

  O5: PROCEDURE [n: CARDINAL] = BEGIN Put.Number[wh, n, [8, FALSE, TRUE, 5]]; END;

  O6L: PROCEDURE [n: LONG CARDINAL] =
    BEGIN Put.Number[wh, Inline.LowHalf[n], [8, FALSE, TRUE, 7]]; END;

  O6: PROCEDURE [n: CARDINAL] = BEGIN Put.Number[wh, n, [8, FALSE, TRUE, 7]]; END;

  O3Z: PROCEDURE [n: CARDINAL] = BEGIN Put.Number[wh, n, [8, TRUE, TRUE, 3]]; END;

  D: PROCEDURE [n: CARDINAL] = BEGIN Put.Number[wh, n, [10, FALSE, TRUE, 0]]; END;

  D4: PROCEDURE [n: CARDINAL] =
    BEGIN Put.Number[wh, n, [10, FALSE, TRUE, 4]]; END;

  D5: PROCEDURE [n: CARDINAL] =
    BEGIN Put.Number[wh, n, [10, FALSE, TRUE, 5]]; END;

  D6: PROCEDURE [n: CARDINAL] =
    BEGIN Put.Number[wh, n, [10, FALSE, TRUE, 6]]; END;

  LD: PROCEDURE [n: LONG INTEGER] =
    BEGIN
    s: STRING = [20];
    String.AppendLongNumber[s, n, 10];
    Put.Text[wh, s];
    END;

  LD6: PROCEDURE [num: LONG INTEGER] =
    BEGIN
    s: STRING = [20];
    String.AppendLongNumber[s, num, 10];
    THROUGH [s.length..6) DO Put.Char[wh, ' ]; ENDLOOP;
    Put.Text[wh, s];
    END;

  LD10: PROCEDURE [num: LONG INTEGER] =
    BEGIN
    s: STRING = [20];
    String.AppendLongNumber[s, num, 10];
    THROUGH [s.length..10) DO Put.Char[wh, ' ]; ENDLOOP;
    Put.Text[wh, s];
    END;

  LD10Dash: PROCEDURE [n: LONG INTEGER] =
    BEGIN IF n = 0 THEN Put.Text[wh, "        - "L] ELSE LD10[n]; END;

  MakeConnection: PROCEDURE =
    BEGIN OPEN PupDefs;
      DO
	Put.Text[wh, "Looking at "L];
	Put.Text[wh, name];
	Put.Line[wh, "."L];
	GetPupAddress[
	  @who, name ! PupNameTrouble => BEGIN Put.Line[wh, e]; LOOP; END];
	Put.CR[wh];
	Put.CR[wh];
	Put.CR[wh];
	EXIT;
	ENDLOOP;
    who.socket ← [31415, 9265];
    coreSoc ← PupSocketMake[PupTypes.fillInSocketID, who, MsToTocks[1000]];
    who.socket ← PupTypes.statSoc;
    statSoc ← PupSocketMake[PupTypes.fillInSocketID, who, MsToTocks[1000]];
    giantVectorLoc ← Read[CommUtilDefs.magicMemoryLocation];
    Get[
      to: @giantVector, from: giantVectorLoc, size: SIZE[DriverDefs.GiantVector]];
    Get[
      to: @gateVector, from: giantVector.spare,
      size: SIZE[GateDefs.GateDebugRecord]];
    END;

  GetStrings: PROCEDURE =
    BEGIN
    s: StatsDefs.StatCounterIndex;
    temp: STRING = [204]; -- maxlength gets clobbered
    Get[to: @hisStrings, from: giantVector.statStrings, size: SIZE[Strings]];
    FOR s IN StatsDefs.StatCounterIndex DO
      IF hisStrings[s] = NIL THEN LOOP;
      Get[to: temp, from: hisStrings[s], size: 100];
      hisStrings[s] ← Storage.String[temp.length];
      String.AppendString[hisStrings[s], temp];
      ENDLOOP;
    END;

  -- main program

  PupDefs.PupPackageMake[];
  MakeConnection[];
  ShowGateStats[];
  Put.CR[wh];
  Put.CR[wh];
  ShowEcho[];
  ShowTime[];
  Put.Char[wh, Ascii.FF];
  Put.CR[wh];
  GetStrings[];
  Looker[];
  Put.Char[wh, Ascii.FF];
  Put.CR[wh];
  ShowDriverStats[];
  Put.CR[wh];
  Put.CR[wh];
  ShowSla[];
  Put.Char[wh, Ascii.FF];
  Put.CR[wh];
  ShowRoutingTable[];
  Put.Char[wh, Ascii.FF];
  Put.CR[wh];
  ShowBoot[];
  Put.Char[wh, Ascii.FF];
  Put.CR[wh];
  ShowName[];
  Put.Char[wh, Ascii.FF];
  Put.CR[wh];
  ShowLocks[];
  Put.Char[wh, Ascii.FF];
  Put.CR[wh];
  ImageDefs.StopMesa[];
  END.