-- Pupwatch: Calling name-lookup server

-- [Indigo]<Grapevine>PupWatch>GetAddress.mesa

-- Stolen from <Mesa>System>MesaInit.mesa of August 27, 1980  10:01 AM
-- Andrew Birrell  12-Nov-81  9:30:44

DIRECTORY
  LookerDefs	USING [LookupOutcome],
  Mopcodes	USING [zSTARTIO],
  OsStaticDefs	USING [OsStatics],
  ProcessDefs	USING[CV, InterruptLevel];

GetAddress: MONITOR EXPORTS LookerDefs =

BEGIN

  Byte: TYPE = [0..255];

  PupHeader: TYPE = MACHINE DEPENDENT RECORD [
    eDest, eSource: Byte,
    eWord2, pupLength: CARDINAL,
    transportControl, pupType: Byte,
    pupID1, pupID2: CARDINAL,
    destNet, destHost: Byte,
    destSocket1, destSocket2: CARDINAL,
    sourceNet, sourceHost: Byte,
    sourceSocket1, sourceSocket2: CARDINAL];

  Pup: TYPE = MACHINE DEPENDENT RECORD [
    header: PupHeader, data: PACKED ARRAY [0..130) OF CHARACTER];

  EthernetDeviceBlock: TYPE = MACHINE DEPENDENT RECORD [
    EPLocMicrocodeStatus, EPLocHardwareStatus: Byte,
    EBLocInterruptBit: WORD,
    EELocInputFinishCount: CARDINAL,
    ELLocCollisionMagic: WORD,
    EILocInputCount: CARDINAL,
    EILocInputPointer: POINTER,
    EOLocOutputCount: CARDINAL,
    EOLocOutputPointer: POINTER];

  myNet: Byte ← 0;
  myID: CARDINAL ← 0;

  Broadcast: ENTRY PROC[length: CARDINAL, socket: CARDINAL, type: Byte,
                        request, response: POINTER TO Pup]
                RETURNS[outcome: LookerDefs.LookupOutcome,
                        responseBytes: CARDINAL] =
    BEGIN
    etherLevel: ProcessDefs.InterruptLevel;
    etherMask: CARDINAL ← 1;
    myHost: Byte ← OsStaticDefs.OsStatics.SerialNumber;
    device: POINTER TO EthernetDeviceBlock ← LOOPHOLE[600B];
    StartIO: PROCEDURE [WORD] = MACHINE CODE BEGIN Mopcodes.zSTARTIO END;
    outputCommand: WORD = 1;
    inputCommand: WORD = 2;
    resetCommand: WORD = 3;
    request.header ←
      [eDest: 0, eSource: myHost, eWord2: 1000B, pupLength: 22+length,
	transportControl: 0, pupType: type,
        pupID1: 0, pupID2: (myID ← myID+1),
        destNet: 0, destHost: 0,
        destSocket1: 0, destSocket2: socket,
        sourceNet: myNet, sourceHost: myHost,
        sourceSocket1: 0, sourceSocket2: 101010B];
    BEGIN
      x: POINTER TO CARDINAL = LOOPHOLE[@request.data+(1+length)/2];
      x↑ ← 177777B;-- => don't checksum --
    END;
    FOR i: ProcessDefs.InterruptLevel IN ProcessDefs.InterruptLevel
    DO IF ProcessDefs.CV[i] = NIL THEN { etherLevel ← i; EXIT };
       etherMask ← etherMask + etherMask;
    REPEAT FINISHED => ERROR
    ENDLOOP;
    THROUGH [0..5) DO
      etherCV: CONDITION ← [timeout:30];
      status: Byte; -- for debugging --
      device.EBLocInterruptBit ← etherMask;
      device.EPLocMicrocodeStatus ← 0;
      ProcessDefs.CV[etherLevel] ← @etherCV;
      StartIO[resetCommand];
      DO WAIT etherCV;
         IF (status←device.EPLocMicrocodeStatus) = 5 --reset-- THEN EXIT;
      ENDLOOP;
      device↑ ← EthernetDeviceBlock[
	EPLocMicrocodeStatus: 0, EPLocHardwareStatus: 0,
        EBLocInterruptBit: etherMask, EELocInputFinishCount: 0,
        ELLocCollisionMagic: 0, EILocInputCount: 0,
	EILocInputPointer: response, EOLocOutputCount: 2+11+(1+length)/2,
	EOLocOutputPointer: request];
      StartIO[outputCommand];
      WAIT etherCV;
      IF (status←device.EPLocMicrocodeStatus) = 1 -- outputDone --
      AND device.EPLocHardwareStatus = 377B -- ok --
      THEN -- output ok, so wait for input --
           DO device↑ ← EthernetDeviceBlock[
	        EPLocMicrocodeStatus: 0, EPLocHardwareStatus: 0,
                EBLocInterruptBit: etherMask, EELocInputFinishCount: 0,
	        ELLocCollisionMagic: 0, EILocInputCount: SIZE[Pup],
	        EILocInputPointer: response, EOLocOutputCount: 0,
	        EOLocOutputPointer: NIL];
	      StartIO[inputCommand];
              WAIT etherCV;
              IF device.EPLocHardwareStatus = 0 THEN EXIT -- timeout --;
              IF (status←device.EPLocMicrocodeStatus) = 0 -- inputDone --
              AND device.EPLocHardwareStatus = 377B -- ok --
              AND response.header.eWord2 = 1000B -- Pup packet --
              AND device.EILocInputCount = device.EELocInputFinishCount +
                                          2+(1+response.header.pupLength)/2
              AND response.header.destSocket1 = request.header.sourceSocket1
              AND response.header.destSocket2 = request.header.sourceSocket2
              AND response.header.pupID1 = request.header.pupID1
              AND response.header.pupID2 = request.header.pupID2
              THEN BEGIN
                   IF myNet = 0 THEN myNet ← response.header.sourceNet;
                   ProcessDefs.CV[etherLevel] ← NIL;
                   RETURN[ok, response.header.pupLength-22]
                   END
            --ELSE ignore the packet--;
           ENDLOOP;
       ProcessDefs.CV[etherLevel] ← NIL;
    ENDLOOP;
    StartIO[resetCommand];
    RETURN[noResponse,];
    END;

  ParseConstant: PROC[name: STRING]
      RETURNS[outcome: LookerDefs.LookupOutcome, net, host: Byte] =
    BEGIN
    -- Syntax (I didn't invent it!) is one of:
    --   socket
    --   host#socket
    --   net#host#socket
    -- Any field may be empty, meaning 0.
    temp1: Byte ← 0;
    temp2: Byte ← 0;
    i: CARDINAL ← 0;
    WHILE i < name.length
    DO IF name[i] IN ['0..'7] THEN temp1 ← temp1*8 + name[i]-'0 ELSE EXIT;
       i ← i+1;
    REPEAT FINISHED => RETURN[outcome:ok, net:0, host:0]
    ENDLOOP;
    IF name[i] # '# THEN RETURN[badName,,] ELSE i ← i+1;
    WHILE i < name.length
    DO IF name[i] IN ['0..'7] THEN temp2 ← temp2*8 + name[i]-'0 ELSE EXIT;
       i ← i+1;
    REPEAT FINISHED => RETURN[outcome:ok, net:0, host:temp1]
    ENDLOOP;
    IF name[i] # '# THEN RETURN[badName,,] ELSE i ← i+1;
    RETURN[outcome:ok, net:temp1, host:temp2]
    END;

  Lookup: PUBLIC PROCEDURE[name: STRING]
                   RETURNS[outcome: LookerDefs.LookupOutcome,
                           net, host: Byte] =
    BEGIN
    request, response: Pup;
    responseLength: CARDINAL;
    [outcome, net, host] ← ParseConstant[name];
    IF outcome = ok THEN RETURN;
    FOR i: CARDINAL IN [0..name.length)
    DO request.data[i] ← name[i]; ENDLOOP;
    DO [outcome, responseLength]
            ← Broadcast[name.length,4,220B,@request,@response];
       IF outcome # ok THEN RETURN;
       SELECT response.header.pupType FROM
         221B =>
           IF responseLength >= 6
           THEN RETURN[ok,
                       LOOPHOLE[response.data[0]],
                       LOOPHOLE[response.data[1]]]
           ELSE RETURN[badName,,];
         222B => RETURN[badName,,];
       ENDCASE => NULL --ignore it! --;
    ENDLOOP;
    END;

  END.