-- Copyright (C) 1983  by Xerox Corporation. All rights reserved. 
-- TeledebugImplNoDisk.mesa , HGM, 14-Nov-83 19:20:32, Discard disk stuff
-- From TeledebugImpl.mesa of 14-Nov-83 10:31:45 by DKntusen
<<
This module implements a teledebugging server using the Packet Exchange Protocol over the Ethernet, device # 0. The implementation only talks to the first PilotDisk. Later versions could support other device types and ordinals.

At present, this teledebug server is promiscuous: it is willing to indiscriminately talk to several debuggers simultaneously. This is not much of a problem at present. In the future, we could envision it latching on to the "master" - the first debugger that gave it a write-type request; it would still allow other debuggers to read (only). This would require that the protocol be enhanced with a "speakToMe" PacketType, which would override the identity of the debugger that currently was the master.

There are provisions here for DEBUGGING THIS IN CoPilot. To do so:

  o  run any debugger client (Othello is good);
  o  get from the client to the debugger (by breakpoint is good);
  o  Run SimpleNSIOEthernetImpl and TeledebugImpl, in that order.
  o  Interpret call TestTeledebugging in this module.

Bottom line, this doesn't work very good. The remote debugger looks at the ESV in the PDA; this tells about CoPilot's debugee. However, it reads memory directly from CoPilot, itself. Not too good. This debugging technique may be useful for debugging the basics of communication, if they are not working.

Alternatively, you can insert code which displays values of key variables in the maintenance panel. See GermOps.ShowCardinalInMP, etc.
>>

DIRECTORY
  BootChannel USING [Result],
  DeviceTypes USING [ethernet],
  DriverTypes USING [Encapsulation],
  Environment USING [
    Base, bytesPerWord, Long, LongPointerFromPage,
    PageFromLongPointer, PageCount, PageNumber, wordsPerPage],
  GermOps USING [GermWorldError],
  NSConstants USING [teleDebugSocket],
  NSTypes USING [
    BufferBody, bytesPerExchangeHeader, maxDataBytesPerExchange, PacketType],
  PacketExchange USING [ExchangeClientType, ExchangeID],
  PageMap USING [ExchangeFlags, Flags, GetState, IsMapped],
  PilotMP USING [
    cGermDeviceError, cGermERROR, cGermFunnyPacket, Code,
    cRespondingToEtherDebugger, cWaitingForEtherDebugger],
  ProcessorFace USING [BootButton, GetNextAvailableVM, SetMP],
  SimpleNSIO USING [
    ByteCount, Finalize, fromAnyHost, fromAnyNetwork, fromAnySocket, GetRawBufferSize, Initialize,
    noTimeOut, ReceivePacket, ReturnPacket, SendPacket, timedOutBytes],
  System USING [
    broadcastHostNumber, GetClockPulses, Microseconds, MicrosecondsToPulses,
    NetworkAddress, nullNetworkNumber, Pulses],
  TeledebugProtocol;

TeledebugImplNoDisk: PROGRAM
  IMPORTS
    Environment, GermOps, PageMap, ProcessorFace, SimpleNSIO, System
  EXPORTS GermOps
  SHARES GermOps, PageMap =
  BEGIN

  -- Parameters:

  broadcastForDebuggerInterval: System.Microseconds = 30000000;
  proceedSequenceTimeout: System.Microseconds = 60000000;  -- for go, remoteDestruct
  exchangeIDTimeout:  -- a trojan horse ExchangeID will get thrown out after this delay.
    System.Microseconds = 120000000;

  -- TYPES and Constants:
  
  MaxSize: TYPE = CARDINAL [0..NSTypes.maxDataBytesPerExchange];
  bytesPerWord: CARDINAL = Environment.bytesPerWord;
  nil: Environment.Base RELATIVE POINTER = LOOPHOLE[0];
  nullExchangeID: PacketExchange.ExchangeID = [0, 0];  -- should be in PacketExchange!

  RequesterPacketType: TYPE =  -- SHOULD be in TeledebugProtocol!
    TeledebugProtocol .PacketType[coreStore..remoteDestructReply];

  -- Variables:

  -- The following ethernet buffer is allocated here statically because
  -- (1) if this module is present, we need it at any time
  -- (2) the buffer used by BootChannelSPP is only temporarily allocated,
  -- thus there is never more than one buffer permanently allocated.
  rawNSBufferSize: CARDINAL =  -- if not enough, dies with mp code.
    DriverTypes.Encapsulation.SIZE +
    NSTypes.maxDataBytesPerExchange/Environment.bytesPerWord + 36--say--;
  rawNSBuffer: ARRAY [0..rawNSBufferSize) OF WORD;
  
  b: LONG POINTER TO NSTypes.BufferBody;  -- the network buffer.

  firstUnimplementedVMPage: Environment.PageNumber;
  
  beingDebuggedInCopilot: BOOLEAN ← FALSE;
  
  protocolFlagsToPageMapFlags: ARRAY TeledebugProtocol .PageFlags[clean..vacant]
    OF PageMap.Flags = [
    clean: [readonly: FALSE, dirty: FALSE, referenced: FALSE],
    referenced: [readonly: FALSE, dirty: FALSE, referenced: TRUE],
    dirty: [readonly: FALSE, dirty: TRUE, referenced: FALSE],
    dirtyReferenced: [readonly: FALSE, dirty: TRUE, referenced: TRUE],
    writeProtected: [readonly: TRUE, dirty: FALSE, referenced: FALSE],
    writeProtectedReferenced: [readonly: TRUE, dirty: FALSE, referenced: TRUE],
    vacant: [readonly: TRUE, dirty: TRUE, referenced: FALSE]];

  coreStoreReqSize: MaxSize = TeledebugProtocol.coreStoreReqSize*bytesPerWord;
  coreStoreAckSize: MaxSize = TeledebugProtocol.coreStoreAckSize*bytesPerWord;
  coreFetchReqSize: MaxSize = TeledebugProtocol.coreFetchReqSize*bytesPerWord;
  coreFetchAckSize: MaxSize = TeledebugProtocol.coreFetchAckSize*bytesPerWord;
  diskStoreReqSize: MaxSize = TeledebugProtocol.diskStoreReqSize*bytesPerWord;
  diskStoreAckSize: MaxSize = TeledebugProtocol.diskStoreAckSize*bytesPerWord;
  diskFetchReqSize: MaxSize = TeledebugProtocol.diskFetchReqSize*bytesPerWord;
  diskFetchAckSize: MaxSize = TeledebugProtocol.diskFetchAckSize*bytesPerWord;
  nullPacketSize: MaxSize =
    SIZE[null TeledebugProtocol.TeledebugBuffer]*bytesPerWord;


  -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  -- Main Teledebugee server
  -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  GetTeledebugged: PUBLIC --GermOps.-- PROC [scratchPage: Environment.PageNumber] =
    BEGIN
    -- (scratchPage is used for reading page labels.)
    packet: LONG POINTER TO TeledebugProtocol.TeledebugBuffer;
    gotADebugger: BOOLEAN ← FALSE;
    receiveTimeout: System.Microseconds ← broadcastForDebuggerInterval;
    -- Information about current request packet.
    dataBytes: SimpleNSIO.ByteCount;
    thisExchangeID: PacketExchange.ExchangeID;
    prevExchangeID: PacketExchange.ExchangeID ← nullExchangeID;
    timePrevExchangeID: System.Pulses;
    nilRequest: TeledebugProtocol.PacketType = TeledebugProtocol.PacketType.last;
    requiredRequest: TeledebugProtocol.PacketType ←  --
      -- If non-nil, next packet must be this type (or requiredRequest.PRED).
      nilRequest;

    ProcessorFace.SetMP[PilotMP.cWaitingForEtherDebugger];

    -- Initialize Network I/O:
    -- SOMEDAY, we could make the call to Debug be passed a device
    -- and type (just like BootChannel), and thus allow teledebugging
    -- over a variety of networks.
    BEGIN
    result: BootChannel.Result;
    IF rawNSBufferSize < SimpleNSIO.GetRawBufferSize[] THEN
      GermOps.GermWorldError[PilotMP.cGermERROR];
    [bufferBody: b, result: result] ← SimpleNSIO.Initialize[
      DeviceTypes.ethernet, 0, @rawNSBuffer,
      IF beingDebuggedInCopilot THEN doCleanup ELSE avoidCleanup];
    IF result # [ok[]] THEN GermOps.GermWorldError[PilotMP.cGermDeviceError];
    packet ← LOOPHOLE[@b.exchangeWords];
    END;

    DO  --until go or remoteDestruct request--
    
      -- Get next request:
      DO  -- until got a request packet

        IF NOT gotADebugger THEN
          BEGIN  -- send mayday packet
          -- don't use a constructor here unless you want a LARGE frame
          -- packet↑ ← [type: mayday, body: null[]]];
          packet.type ← mayday;
          packet.spare ← 0;
          packet.body ← null[];
          PacketExchangeSend[
            dataBytes: nullPacketSize,
            exchangeID: LOOPHOLE[System.GetClockPulses[]],
            dest: [
            System.nullNetworkNumber, System.broadcastHostNumber,
            NSConstants.teleDebugSocket]];
	  receiveTimeout ← broadcastForDebuggerInterval;
          END;

        [dataBytes, thisExchangeID] ← PacketExchangeReceive[
          minBytes: nullPacketSize, prevExchangeID: prevExchangeID,
	  timePrevExchangeID: timePrevExchangeID,
          timeout:
          IF gotADebugger THEN SimpleNSIO.noTimeOut
          ELSE broadcastForDebuggerInterval,
          getFrom: [
          SimpleNSIO.fromAnyNetwork, SimpleNSIO.fromAnyHost,
          SimpleNSIO.fromAnySocket]];
        --ASSERT: timed out or got a packet of at least nullPacketSize.
        IF --UNTIL-- dataBytes # SimpleNSIO.timedOutBytes THEN EXIT;
	--ASSERT: timed out.
	IF requiredRequest # nilRequest THEN
	  BEGIN
	  -- Fabricate desired request packet and process it:
	  packet.type ← requiredRequest;
	  dataBytes ← nullPacketSize;
	  packet.body ← null[];
	  EXIT;
	  END;
        ENDLOOP;

      -- Process request:
      BEGIN  --scope of BadPacket--
      IF requiredRequest # nilRequest AND
        (packet.type # requiredRequest AND packet.type # requiredRequest.PRED) THEN
        GOTO BadPacket;
      SELECT packet.type FROM

        -- Each arm below sets dataBytes and the buffer body for the appropriate
        -- response and falls through to send it back to the requestor.

        mayday, ack, nak => GOTO IgnorePacket;  -- broadcast from some other debugee.

        coreFetch =>
          BEGIN
          -- this depends upon FetchReq and FetchAck pageNumber
          -- being in the same place.
          OPEN pAck: LOOPHOLE[packet, TeledebugProtocol.CoreFetchAckHandle];
          IF dataBytes # coreFetchReqSize THEN GOTO BadPacket;
          WITH pReq: packet SELECT FROM
            coreFetchReq =>
              BEGIN
              page: Environment.PageNumber = pReq.page;
              IF ImplementedAndMapped[page] THEN
                BEGIN
                pData: LONG POINTER TO TeledebugProtocol.PageData =
                  Environment.LongPointerFromPage[page];
                flags: PageMap.Flags = PageMap.GetState[page].state.flags;
                pAck.flags ← ProtocolFlagsFromPageMapFlags[flags];
                pAck.data ← pData↑;
                [] ← PageMap.ExchangeFlags[  -- restore referenced bit.
                  virtual: page, newFlags: flags];
                END
              ELSE pAck.flags ← vacant;
              END;
            ENDCASE => GOTO BadPacket;
          packet.body ← coreFetchAck[  -- (body all already set)
            page: NULL, flags: NULL, data: NULL];
          dataBytes ← coreFetchAckSize;
          packet.type ← ack;
	  receiveTimeout ← SimpleNSIO.noTimeOut;
          END;  --coreFetch--

        coreStore =>
          BEGIN
          -- this depends upon both StoreReq and StoreAck
          -- page and flags being in the same place
          IF dataBytes # coreStoreReqSize THEN GOTO BadPacket;
          WITH pReq: packet SELECT FROM
            coreStoreReq =>
              BEGIN
              page: Environment.PageNumber = pReq.page;
              IF pReq.flags ~IN [clean..vacant] THEN GOTO BadPacket;
              IF ImplementedAndMapped[page] THEN
                BEGIN
                pData: LONG POINTER TO TeledebugProtocol.PageData =
                  Environment.LongPointerFromPage[page];
                MakeWritable[page];
                pData↑ ← pReq.data;
                [] ← PageMap.ExchangeFlags[
                  virtual: page,
                  newFlags: protocolFlagsToPageMapFlags[pReq.flags]]
                END
              ELSE pReq.flags ← vacant;
              END;
            ENDCASE => GOTO BadPacket;
          packet.body ← coreStoreAck[page: NULL, flags: NULL];  -- (body all already set)
          dataBytes ← coreStoreAckSize;
          packet.type ← ack;
	  receiveTimeout ← SimpleNSIO.noTimeOut;
          END;  --coreStore--

        go, remoteDestruct =>
          BEGIN
          goReplyIsSUCCGo: BOOLEAN [TRUE..
	    (TeledebugProtocol.PacketType.goReply =
              TeledebugProtocol.PacketType.go.SUCC)] = TRUE;
          remoteDestructReplyIsSUCCGo: BOOLEAN [TRUE..
	    (TeledebugProtocol.PacketType.remoteDestructReply =
              TeledebugProtocol.PacketType.remoteDestruct.SUCC)] = TRUE;
          requiredRequest ← packet.type.SUCC;
	  IF dataBytes # nullPacketSize OR packet.request # null THEN
            GOTO BadPacket;
	  packet.type ← ack;
          packet.body ← null[];
	  dataBytes ← nullPacketSize;
	  receiveTimeout ← proceedSequenceTimeout;
          END;  --go, remoteDestruct--

        goReply =>
          BEGIN
          IF dataBytes # nullPacketSize OR packet.request # null THEN
            GOTO BadPacket;
          SimpleNSIO.Finalize[
	    IF beingDebuggedInCopilot THEN doCleanup ELSE avoidCleanup];
          RETURN;
          END;  --goReply--

        remoteDestructReply =>
          BEGIN
          IF dataBytes # nullPacketSize OR packet.request # null THEN
            GOTO BadPacket;
	  ProcessorFace.BootButton[];
          END;  --remoteDestructReply--

        ENDCASE => GOTO BadPacket;  -- unknown request type.

      -- Return response packet sitting in buffer:
      prevExchangeID ← thisExchangeID;  -- remember ID of good packets.
      timePrevExchangeID ← System.GetClockPulses[];
      PacketExchangeReturn[dataBytes: dataBytes];
      ProcessorFace.SetMP[PilotMP.cRespondingToEtherDebugger];
      gotADebugger ← TRUE;

      EXITS
        BadPacket => ProcessorFace.SetMP[PilotMP.cGermFunnyPacket];
        IgnorePacket => NULL;
      END;  --scope of BadPacket--
      ENDLOOP;  --until go or remoteDestruct request--
    END;  --Debug--


  ImplementedAndMapped: PROC [page: Environment.PageNumber] RETURNS [BOOLEAN] = {
    IF page >= firstUnimplementedVMPage THEN RETURN[FALSE]
    ELSE RETURN[PageMap.IsMapped[page]]};

  MakeWritable: PROC [page: LONG CARDINAL] = {
    flags: PageMap.Flags ← PageMap.GetState[page].state.flags;
    flags.readonly ← FALSE;
    [] ← PageMap.ExchangeFlags[virtual: page, newFlags: flags]};

  ProtocolFlagsFromPageMapFlags: PROC [in: PageMap.Flags]
    RETURNS [out: TeledebugProtocol.PageFlags] =
    BEGIN
    out ← clean;
    DO
      IF protocolFlagsToPageMapFlags[out] = in THEN RETURN;
      IF (out ← SUCC[out]) = illegal THEN RETURN;
      ENDLOOP;
    END;


  -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  -- PacketExchange Network I/O
  -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  -- This supports sending and receiving level three packets
  -- using the Packet Exchange Protocol.

  -- Note this isn't very clean since we know some about teledebug socket etc.

  PacketExchangeReceive: PROC [
    timeout: LONG CARDINAL, getFrom: System.NetworkAddress,
    minBytes: SimpleNSIO.ByteCount, prevExchangeID: PacketExchange.ExchangeID,
    timePrevExchangeID: System.Pulses]
    RETURNS [
      dataBytes: SimpleNSIO.ByteCount, exchangeID: PacketExchange.ExchangeID,
      source: LONG POINTER TO System.NetworkAddress] =
    -- The returned dataBytes is the number of client bytes in the level
    --   three packet. Returns dataBytes = SimpleNSIO.timedOutBytes if
    --   times out. In this case, exchangeID will be TRASH.
    -- Supresses duplicates of packets with exchangeID previous to
    --   prevExchangeID, and packets shorter than minBytes.
    -- ExchangeIDs which look bad are discarded for a maximum of two minutes,
    --   then are accepted as good.
    -- Duplicates of packets with exchangeID equal to prevExchangeID are
    --   returned to caller; he can suppress them if he wants.
    -- prevExchangeID = nullExchangeID means accept any exchangeID.
    BEGIN
    type: NSTypes.PacketType;
    levelTwoBytes: SimpleNSIO.ByteCount;
    result: BootChannel.Result;
    DO  --until correct teleDebug packet arrives--
      [dataBytes: levelTwoBytes, type: type, source: source, result: result] ←
        SimpleNSIO.ReceivePacket[
        getFrom: getFrom, timeout: timeout,
        mySocket: NSConstants.teleDebugSocket];
      IF levelTwoBytes = SimpleNSIO.timedOutBytes THEN
        RETURN[SimpleNSIO.timedOutBytes, TRASH, NIL];
      HandleResult[result];
      -- This packet was sent to teleDebugSocket. It should be correct.
      IF --UNTIL-- type = packetExchange
        AND levelTwoBytes >= NSTypes.bytesPerExchangeHeader + minBytes
        AND b.exchangeType = teledebug AND
          (prevExchangeID = nullExchangeID
           OR NewerOrSame[
             thisExchangeID: b.exchangeID, prevExchangeID: prevExchangeID]
	   OR System.GetClockPulses[] - timePrevExchangeID >
	     System.MicrosecondsToPulses[exchangeIDTimeout]) THEN EXIT;
      ProcessorFace.SetMP[PilotMP.cGermFunnyPacket];
      ENDLOOP;
    RETURN[levelTwoBytes - NSTypes.bytesPerExchangeHeader, b.exchangeID, source];
    END;  --PacketExchangeReceive--

  PacketExchangeReturn: PROC [dataBytes: SimpleNSIO.ByteCount] = INLINE
    -- Returns the contents of the buffer to where it came from.
    BEGIN
    -- Note that the exchangeID, teledebugExchangeType already set
    HandleResult[
      SimpleNSIO.ReturnPacket[
      dataBytes: dataBytes + NSTypes.bytesPerExchangeHeader,
      type: packetExchange].result];
    END;

  PacketExchangeSend: PROC [
    dataBytes: SimpleNSIO.ByteCount, exchangeID: PacketExchange.ExchangeID,
    dest: System.NetworkAddress] = INLINE
    BEGIN
    b.exchangeID ← exchangeID;
    b.exchangeType ← teledebug;
    HandleResult[
      SimpleNSIO.SendPacket[
      dataBytes: dataBytes + NSTypes.bytesPerExchangeHeader, type: packetExchange,
      sourceSocket: NSConstants.teleDebugSocket, dest: dest].result];
    END;

  HandleResult: PROC [result: BootChannel.Result] =
    BEGIN
    WITH r: result SELECT FROM
      ok => NULL;
      error => GermOps.GermWorldError[r.code];
      ENDCASE => GermOps.GermWorldError[PilotMP.cGermERROR];
    END;

  NewerOrSame: PROC [thisExchangeID, prevExchangeID: PacketExchange.ExchangeID]
    RETURNS [BOOLEAN] = --INLINE--
    -- Returns TRUE if thisExchangeID is newer than or the same as prevExchangeID
    -- (and therefore should not be discarded).
    BEGIN
    << We call an exchangeID older if it happened in the recent past;
    that is, its value is within the range [thisExchangeID -
    LAST[ExchangeID]/8..thisExchangeID). This means that duplicates of recent
    previous packets will be discarded with high reliability. >>

    LongCardFromExchangeID: PROC [exchangeID: PacketExchange.ExchangeID]
      RETURNS [LONG CARDINAL] = INLINE {
      RETURN[LOOPHOLE[
        Environment.Long[num[lowbits: exchangeID.b, highbits: exchangeID.a]]]]};

    newerLimit: LONG CARDINAL = 7*(LAST[LONG CARDINAL]/8);  -- utilizes wraparound
    amountThisNewer: LONG CARDINAL =  -- the amount that thisExchangeID
      -- is newer than prevExchangeID (the usual case)
      LongCardFromExchangeID[thisExchangeID] - LongCardFromExchangeID[
        prevExchangeID];  -- underflow is fine here.

    -- TEMP DEBUG
    --IF amountThisNewer >= newerLimit THEN {
    --  ShowCodeInMP[2222];
    --  ShowCardinalInMP[Inline.HighHalf[amountThisNewer]];
    --  ShowCardinalInMP[Inline.LowHalf[amountThisNewer]];
    --  ShowCodeInMP[2224];
    --  ShowCardinalInMP[Inline.HighHalf[newerLimit]];
    --  ShowCardinalInMP[Inline.LowHalf[newerLimit]];
    --  };
    RETURN[ --newerOrSame:-- amountThisNewer < newerLimit];
    END;

  -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  -- Main body, debugging code
  -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  TestTeledebugging: PROC =
    -- To test this module in CoPilot, interpret call this proc from the debugger.
    -- See comments at head of module.
    BEGIN
    storage: ARRAY  -- need one word-aligned page of storage.
      [0..2*Environment.wordsPerPage) OF WORD;
    beingDebuggedInCopilot ← TRUE;
    GetTeledebugged[scratchPage: 
      Environment.PageFromLongPointer[
      @storage + Environment.wordsPerPage-1]];
    END;


  -- MAIN BODY:

  -- Find the size of VM for this processor:  (sets firstUnimplementedVMPage)
  BEGIN
  page: Environment.PageNumber;
  count: Environment.PageCount;
  FOR firstUnimplementedVMPage ← FIRST[Environment.PageNumber], page + count DO
    [firstPage: page, count: count] ← ProcessorFace.GetNextAvailableVM[
      firstUnimplementedVMPage];
    IF count = 0 THEN EXIT;
    ENDLOOP;
  END;
  

  END.


LOG  (For previous log entries, please see Mesa 10.0 archive version.)

1-Sep-81 15:17:25   Forrest   8.0c; add DetermineDiskShape
6-Dec-81  4:27:00   Forrest   NS Teledebug lives
7-Jun-83 23:18:09   DKnutsen
   OISCP => NS. Get LongPointerFromPage from Environment. Changed IsVacant to IsMapped. Convert to new PageMap. Make compatible with new SimpleNSIO interface. Use official type definitions rather than private copies. Some responses didn't set ack packet.type. Doubled proceedSequenceTimeout. Supress copies of previous request packets. Now retransmits "shutUp" packet. Documented operation of go sequencing. Use informative mp codes. Allocate ethernet buffer here.
20-Jul-83 10:16:14   Luniewski
   NSTypes.maxDataBytePerExchange => NSTypes.maxDataBytesPerExchange, long page numbers in map log entries.
11-Nov-83 13:55:50   DKnutsen
   Must not use SendPacket to talk to remote network. Simplify Proceed logic and use timeout.
14-Nov-83 10:31:50   DKnutsen	Handle duplicate go packets.