-- OthelloOpsImpl.mesa (last edited by: Johnsson on:  1-Feb-83 10:51:57)

DIRECTORY
  Boot USING [BootFileType, DiskAddress, Location, LVBootFiles, PVBootFiles],
  BootFile USING [
    MapEntry, Header, maxEntriesPerHeader, maxEntriesPerTrailer, Trailer],
  BufferDefs USING [OisBuffer],
  Device USING [Type],
  DiskChannel USING [GetDriveAttributes, PVHandle],
  Environment,
  File,
  Inline USING [LongDivMod],
  KernelFile,
  OISCP USING [OiscpPackageDestroy, OiscpPackageMake],
  OISCPConstants USING [timeServerSocket],
  OthelloOps,
  PhysicalVolume,
  ProcessorFace USING [GreenwichMeanTime, SetGreenwichMeanTime],
  Runtime USING [IsBound],
  Socket USING [
    AssignNetworkAddress, BroadcastAddressFromSocket, ChannelHandle,
    Create, Delete, GetPacket, GetPacketBytes, GetSendBuffer,
    PutPacketToAllConnectedNets, ReturnBuffer, SetDestination,
    SetPacketWords, SetWaitTime, TimeOut],
  Space USING [Create, Delete, Handle, LongPointer, Map, Unmap, virtualMemory],
  SpecialFile,
  SpecialVolume,
  StartList USING [BootLocation, Header, VersionID],
  System USING [
    defaultSwitches, gmtEpoch, GetGreenwichMeanTime, GreenwichMeanTime,
    LocalTimeParameters, NetworkAddress, Switches, UpDown, WestEast],
  TemporaryBooting USING [MakeBootable, MakeUnbootable],
  TemporarySetGMT USING [GetNetworkGMT, TimeZoneDirection],
  Utilities USING [PageFromLongPointer],
  Volume;

OthelloOpsImpl: PROGRAM
  IMPORTS
    DiskChannel, File, Inline, KernelFile, OISCP, PhysicalVolume,
    ProcessorFace, Runtime, Socket, Space, SpecialFile, SpecialVolume,
    System, TemporaryBooting, TemporarySetGMT,
    Utilities, Volume
  EXPORTS OthelloOps, PhysicalVolume
  SHARES BufferDefs, File = PUBLIC
  BEGIN OPEN OthelloOps;

  maxFilePermissions: PRIVATE File.Permissions =
    File.read + File.write + File.grow + File.shrink + File.delete;

  ImpossibleBootFileType: PRIVATE ERROR = CODE;
  ConvertBootFileType: PROC [x: BootFileType] RETURNS [Boot.BootFileType] =
    BEGIN
    SELECT x FROM
      hardMicrocode => RETURN[hardMicrocode];
      softMicrocode => RETURN[softMicrocode];
      germ =>          RETURN[germ];
      pilot =>         RETURN[pilot];
      ENDCASE =>       ERROR ImpossibleBootFileType;
    END;

  BadSwitches:    ERROR = CODE;
  DecodeSwitches: PROC [switchString: LONG STRING]
    RETURNS [switches: System.Switches ← System.defaultSwitches] = {
    escapeCount: CARDINAL ← 0;
    setUpDown: System.UpDown ← down;
    escapeChar: CHARACTER ← 0C;

    FOR i: CARDINAL IN [0..switchString.length) DO
      c: CHARACTER = switchString[i];
      SELECT TRUE FROM
        c = '-, c = '~ => {
          IF setUpDown = up THEN ERROR BadSwitches
	  ELSE {setUpDown ← up; LOOP}};
        c = '\\ => {
          IF escapeCount # 0 THEN ERROR BadSwitches
	  ELSE {escapeCount ← 1; LOOP}};
        escapeCount = 1 => {
          SELECT c FROM
	    'n, 'N, 'r, 'R => {switches['\n] ← setUpDown; escapeCount ← 0};
	    't, 'T => {switches['\t] ← setUpDown; escapeCount ← 0};
	    'b, 'B => {switches['\b] ← setUpDown; escapeCount ← 0};
	    'f, 'F => {switches['\f] ← setUpDown; escapeCount ← 0};
	    'l, 'L => {switches['\l] ← setUpDown; escapeCount ← 0};
	    '\\ => {switches['\\] ← setUpDown; escapeCount ← 0};
	    IN ['0..'7] => {escapeChar ← c - ('0-0C); escapeCount ← escapeCount + 1};
	    ENDCASE => ERROR BadSwitches;
	  LOOP};
        escapeCount # 0 => {
          IF c NOT IN ['0..'7] THEN ERROR BadSwitches;
	  escapeChar ← (c - '0) + (escapeChar - 0C) * 8 + 0C;
	  IF escapeChar > 377C THEN ERROR BadSwitches;
	  IF (escapeCount ← escapeCount + 1) = 4 THEN {
	    switches[escapeChar] ← setUpDown; escapeCount ← 0};
	  LOOP};
        ENDCASE => switches[c] ← setUpDown;
      -- fall through to here if normal set or escape set
      -- but not on seeing or while collecting escape
      setUpDown ← down;
      ENDLOOP;
    IF escapeCount # 0 THEN ERROR BadSwitches;
    RETURN};

  -- do an old style delete temps.  Volume should be Open
  VolumeNotClosed: ERROR = CODE;
  DeleteTempFiles: PROC [lvID: Volume.ID] =
    BEGIN
    status: Volume.Status;
    DeleteTempsInner: PROC =
      BEGIN
      lastCap: File.Capability ← File.nullCapability;
      DO
        temp, immutable: BOOLEAN;
        thisCap: File.Capability = KernelFile.GetNextFile[lvID, lastCap];
        IF thisCap = File.nullCapability THEN EXIT;
        [immutable: immutable, temporary: temp] ← File.GetAttributes[thisCap];
        IF ~temp THEN lastCap ← thisCap
        ELSE
          IF immutable THEN File.DeleteImmutable[thisCap, lvID]
          ELSE File.Delete[thisCap];
        ENDLOOP;
      END;
    status ← Volume.GetStatus[lvID];
    IF status = openRead OR status = openReadWrite THEN ERROR VolumeNotClosed;
    Volume.Open[lvID];
    DeleteTempsInner[ ! UNWIND => Volume.Close[lvID]];
    Volume.Close[lvID];
    END;

  Handle: PUBLIC TYPE = DiskChannel.PVHandle;
  GetDriveSize: PROC [h: Handle] RETURNS [nPages: LONG CARDINAL] = {
    RETURN[DiskChannel.GetDriveAttributes[h.drive].nPages]};

  MakeBootable: PROC [
    file: File.Capability, type: BootFileType, firstPage: File.PageNumber] =
    BEGIN
    SELECT type FROM
      pilot => TemporaryBooting.MakeBootable[file, firstPage];
      ENDCASE =>
        BEGIN
        lvID: Volume.ID = File.GetAttributes[file].volume;
        l: SpecialFile.Link = SpecialFile.MakeBootable[
          file: file, firstPage: firstPage, count: File.GetSize[file] - firstPage,
          lastLink: SpecialFile.eofLink];
        END;
    END;

  MakeUnbootable: PROC [
    file: File.Capability, type: BootFileType, firstPage: File.PageNumber] =
    BEGIN
    SELECT type FROM
      pilot => TemporaryBooting.MakeUnbootable[file, firstPage];
      ENDCASE =>
        SpecialFile.MakeUnbootable[
          file: file, firstPage: firstPage,
          count: File.GetSize[file] - firstPage];
    END;

  SetVolumeBootFile: PROC [
    file: File.Capability, type: BootFileType, firstPage: File.PageNumber] =
    BEGIN
    bootFiles: Boot.LVBootFiles;
    lvID: Volume.ID = File.GetAttributes[file].volume;
    l: SpecialFile.Link = KernelFile.GetBootLocation[file, firstPage].link;
    SpecialVolume.GetLogicalVolumeBootFiles[lvID, @bootFiles];
    bootFiles[ConvertBootFileType[type]] ← [file.fID, firstPage, LOOPHOLE[l]];
    SpecialVolume.SetLogicalVolumeBootFiles[lvID, @bootFiles];
    END;

  SetPhysicalVolumeBootFile: PROC [
    file: File.Capability, type: BootFileType, firstPage: File.PageNumber] =
    BEGIN
    pvID: PhysicalVolume.ID = PhysicalVolume.GetContainingPhysicalVolume[
      File.GetAttributes[file].volume];
    l: SpecialFile.Link = KernelFile.GetBootLocation[file, firstPage].link;
    pBootFiles: Boot.PVBootFiles;
    SpecialVolume.GetPhysicalVolumeBootFiles[pvID, @pBootFiles];
    pBootFiles[ConvertBootFileType[type]] ← [file.fID, firstPage, LOOPHOLE[l]];
    SpecialVolume.SetPhysicalVolumeBootFiles[pvID, @pBootFiles];
    END;

  GetVolumeBootFile: PROC [lvID: Volume.ID, type: BootFileType]
    RETURNS [cap: File.Capability, firstPage: File.PageNumber] =
    BEGIN
    bootFiles: Boot.LVBootFiles;
    cType: Boot.BootFileType = ConvertBootFileType[type];
    SpecialVolume.GetLogicalVolumeBootFiles[lvID, @bootFiles];
    IF bootFiles[cType].fID = File.nullID THEN RETURN[File.nullCapability, 0]
    ELSE
      RETURN[
        [bootFiles[cType].fID, maxFilePermissions], bootFiles[cType].firstPage];
    END;

  GetPhysicalVolumeBootFile: PROC [pvID: PhysicalVolume.ID, type: BootFileType]
    RETURNS [cap: File.Capability, firstPage: File.PageNumber] =
    BEGIN
    cType: Boot.BootFileType = ConvertBootFileType[type];
    bootFiles: Boot.PVBootFiles;
    SpecialVolume.GetPhysicalVolumeBootFiles[pvID, @bootFiles];
    IF bootFiles[cType].fID = File.nullID THEN RETURN[File.nullCapability, 0]
    ELSE
      RETURN[
        [bootFiles[cType].fID, maxFilePermissions], bootFiles[cType].firstPage];
    END;

  VoidVolumeBootFile: PROC [lvID: Volume.ID, type: BootFileType] =
    BEGIN
    pBootFiles: Boot.LVBootFiles;
    SpecialVolume.GetLogicalVolumeBootFiles[lvID, @pBootFiles];
    pBootFiles[ConvertBootFileType[type]].fID ← File.nullID;
    SpecialVolume.SetLogicalVolumeBootFiles[lvID, @pBootFiles];
    END;

  VoidPhysicalVolumeBootFile: PROC [pvID: PhysicalVolume.ID, type: BootFileType] =
    BEGIN
    pBootFiles: Boot.PVBootFiles;
    SpecialVolume.GetPhysicalVolumeBootFiles[pvID, @pBootFiles];
    pBootFiles[ConvertBootFileType[type]].fID ← File.nullID;
    SpecialVolume.SetPhysicalVolumeBootFiles[pvID, @pBootFiles];
    END;

  -- Set Debugger/Get&SetSwitches implemenation
  StartListHeadPtr: PRIVATE TYPE = LONG POINTER TO StartList.Header;

  SetDebugger: PROC [
    debuggeeCap: File.Capability, debuggeeFirstPage: File.PageNumber,
    debugger: Volume.ID, debuggerType: Device.Type, debuggerOrdinal: CARDINAL]
    RETURNS [outcome: SetDebuggerSuccess] =
    BEGIN
    bootSpace:         Space.Handle;
    debuggerBootFiles: Boot.LVBootFiles ← ALL[[fID: File.nullID,firstPage: ,da: ]];
    pStartListHeader:  StartListHeadPtr;

    IF debugger#Volume.nullID THEN {
      SpecialVolume.GetLogicalVolumeBootFiles[debugger, @debuggerBootFiles];
      IF debuggerBootFiles[pilot].fID = File.nullID
        OR debuggerBootFiles[debugger].fID = File.nullID
        OR debuggerBootFiles[debuggee].fID = File.nullID THEN RETURN[noDebugger]}
    ELSE {debuggerOrdinal ← 0; debuggerType ← LOOPHOLE[0]};
    bootSpace ← Space.Create[1, Space.virtualMemory];
    [outcome, pStartListHeader] ← GetStartListHeader[
      bootSpace, debuggeeCap, debuggeeFirstPage ! UNWIND => Space.Delete[bootSpace]];
    IF outcome=success THEN {
      pStartListHeader.locDebuggerMicrocode ← LOOPHOLE[Boot.Location[
        deviceType: debuggerType, deviceOrdinal: debuggerOrdinal,
        vp: disk[debuggerBootFiles[softMicrocode]]]];
      pStartListHeader.locDebuggerGerm ← LOOPHOLE[Boot.Location[
        deviceType: debuggerType, deviceOrdinal: debuggerOrdinal,
        vp: disk[debuggerBootFiles[germ]]]];
      pStartListHeader.locDebugger ← LOOPHOLE[Boot.Location[
        deviceType: debuggerType, deviceOrdinal: debuggerOrdinal,
        vp: disk[debuggerBootFiles[debugger]]]];
      pStartListHeader.locDebuggee ← LOOPHOLE[Boot.Location[
        deviceType: debuggerType, deviceOrdinal: debuggerOrdinal,
        vp: disk[debuggerBootFiles[debuggee]]]]};
    Space.Delete[bootSpace];
    END;

  SetExpirationDate: PROC [
    cap: File.Capability, firstPage: File.PageNumber,
    expirationDate: System.GreenwichMeanTime]
    RETURNS [outcome: SetExpirationDateSuccess] =
    BEGIN
    bootSpace:         Space.Handle;
    pStartListHeader:  StartListHeadPtr;

    bootSpace ← Space.Create[1, Space.virtualMemory];
    [outcome, pStartListHeader] ← GetStartListHeader[
      bootSpace, cap, firstPage ! UNWIND => Space.Delete[bootSpace]];
    IF outcome=success THEN pStartListHeader.expirationDate ← expirationDate;
    Space.Delete[bootSpace];
    END;

  SetSwitches: PROC [
    cap: File.Capability, firstPage: File.PageNumber, switches: System.Switches]
    RETURNS [outcome: SetGetSwitchesSuccess] =
    BEGIN
    bootSpace:         Space.Handle;
    pStartListHeader:  StartListHeadPtr;

    bootSpace ← Space.Create[1, Space.virtualMemory];
    [outcome, pStartListHeader] ← GetStartListHeader[
      bootSpace, cap, firstPage ! UNWIND => Space.Delete[bootSpace]];
    IF outcome=success THEN pStartListHeader.switches ← LOOPHOLE[switches];
    Space.Delete[bootSpace];
    END;

  GetSwitches: PROC [
    cap: File.Capability, firstPage: File.PageNumber]
    RETURNS [outcome: SetGetSwitchesSuccess, switches: System.Switches] =
    BEGIN
    bootSpace:         Space.Handle;
    pStartListHeader:  StartListHeadPtr;

    bootSpace ← Space.Create[1, Space.virtualMemory];
    [outcome, pStartListHeader] ← GetStartListHeader[
      bootSpace, cap, firstPage ! UNWIND => Space.Delete[bootSpace]];
    IF outcome=success THEN switches ← LOOPHOLE[pStartListHeader.switches];
    Space.Delete[bootSpace];
    END;

  GetStartListHeader: PROC [
    space: Space.Handle, cap: File.Capability, firstPage: File.PageNumber] 
    RETURNS [
      outcome: SetGetSwitchesSuccess, pStartListHeader: StartListHeadPtr] = {
    memPage:        Environment.PageNumber;
    offset:         CARDINAL;
    pagesRemaining: CARDINAL;
    curBase:        File.PageNumber ← firstPage+1;   -- start at first data page
    nEntries:       CARDINAL ← BootFile.maxEntriesPerHeader;
    pBootHeader:    LONG POINTER TO BootFile.Header = Space.LongPointer[space];
    entries: LONG POINTER TO ARRAY [0..0) OF BootFile.MapEntry
     ← @pBootHeader.entries;

    IF cap = File.nullCapability THEN {
      outcome ← nullBootFile; RETURN};
    Space.Map[space, [cap, firstPage]];
    memPage ← Utilities.PageFromLongPointer[pBootHeader.pStartListHeader];
    offset ← Inline.LongDivMod[
      num: LOOPHOLE[pBootHeader.pStartListHeader],
      den: Environment.wordsPerPage].remainder;
    pStartListHeader ← (Space.LongPointer[space] + offset);
    pagesRemaining ← pBootHeader.countData;
    DO
      nEntries ← MIN[nEntries, pagesRemaining];
      FOR i: CARDINAL IN [0..nEntries) DO
        IF entries[i].virtual = memPage THEN {
	  Space.Unmap[space]; Space.Map[space, [cap, curBase+i]];
	  outcome ← IF pStartListHeader.version # StartList.VersionID THEN
            startListHeaderHasBadVersion ELSE success;
	  RETURN};
	ENDLOOP;
      curBase ← curBase + nEntries;
      pagesRemaining ← pagesRemaining - nEntries;
      IF pagesRemaining = 0 THEN EXIT;
      Space.Unmap[space]; Space.Map[space, [cap, curBase]];
      curBase  ← curBase + 1;
      entries  ← @LOOPHOLE[pBootHeader, LONG POINTER TO BootFile.Trailer].entries;
      nEntries ← BootFile.maxEntriesPerTrailer;
      ENDLOOP;
    outcome ← cantFindStartListHeader};


  SubVolumeUnknown: ERROR [sv: SubVolume] = CODE;

  GetNextSubVolume: PROC [pvID: PhysicalVolume.ID, thisSv: SubVolume]
    RETURNS [SubVolume] = {
    sv: SpecialVolume.SubVolume = SpecialVolume.GetNextSubVolume[
      pvID,
      IF thisSv = nullSubVolume THEN SpecialVolume.nullSubVolume
      ELSE [
        lvID: thisSv.lvID, firstLVPageNumber: thisSv.firstLVPageNumber,
        firstPVPageNumber: thisSv.firstPVPageNumber,
        subVolumeSize: thisSv.subVolumeSize] !
      SpecialVolume.SubVolumeUnknown => GOTO error];
    RETURN[
      IF sv = SpecialVolume.nullSubVolume THEN nullSubVolume
      ELSE [
        lvID: sv.lvID, subVolumeSize: sv.subVolumeSize,
        firstLVPageNumber: sv.firstLVPageNumber,
        firstPVPageNumber: sv.firstPVPageNumber]];
    EXITS error => ERROR SubVolumeUnknown[thisSv]};


  IsTimeValid: PROC RETURNS [valid: BOOLEAN] = {
    RETURN[System.GetGreenwichMeanTime[]#System.gmtEpoch]};
    
  SetProcessorTime: PROC [time: System.GreenwichMeanTime] = {
    ProcessorFace.SetGreenwichMeanTime[time]};
    
  TimeServerError: ERROR [error: TimeServerErrorType] = CODE;
  
  GetTimeFromTimeServer: PROC RETURNS[
    serverTime: System.GreenwichMeanTime,
    serverLTPs: System.LocalTimeParameters] = {
    
    pfGMT:      ProcessorFace.GreenwichMeanTime;
    isValid:    BOOLEAN;
    IF Runtime.IsBound[OISCP.OiscpPackageMake] THEN
      [isValid, serverTime, serverLTPs] ← GetOISCPTime[]
    ELSE {
      zoneDirection: TemporarySetGMT.TimeZoneDirection;    
      [networkTimeFound: isValid, timeFromNetwork: pfGMT,
      zoneDirection: zoneDirection, zone: serverLTPs.zone,
      zoneMinutes: serverLTPs.zoneMinutes, beginDST: serverLTPs.beginDST,
      endDST: serverLTPs.endDST] ← TemporarySetGMT.GetNetworkGMT[];
      serverTime ← [pfGMT];
      serverLTPs.direction ← IF zoneDirection = east THEN east ELSE west};
    IF ~isValid THEN ERROR TimeServerError[noResponse];
    RETURN};
      
  GetOISCPTime: PRIVATE PROC RETURNS [
    valid: BOOLEAN ← FALSE, time: System.GreenwichMeanTime,
    ltp: System.LocalTimeParameters] =
    BEGIN
    cH:  Socket.ChannelHandle;
    id1: CARDINAL = 12345;
    id2: CARDINAL = 6789;
    target: System.NetworkAddress = Socket.BroadcastAddressFromSocket[
      OISCPConstants.timeServerSocket];
    timeHeader: LONG POINTER TO TimeHeader;
    WireTimeFormat: TYPE = MACHINE DEPENDENT RECORD [
      timeH(0), timeL(1): CARDINAL,
      zoneS(2:0..0):      System.WestEast,
      zoneH(2:1..7):      [0..177B],
      zoneM(2:8..15):     [0..377B],
      beginDST(3):        WORD,
      endDST(4):          WORD,
      spare(5):           ARRAY [5..8) OF WORD ← ALL[0]];

    TimeHeader: TYPE = MACHINE DEPENDENT RECORD [
      id1, id2: CARDINAL, type: CARDINAL];

    -- These should move to OISCPTypes
    timeRequest:  CARDINAL = 1;
    timeResponse: CARDINAL = 2;

    OISCP.OiscpPackageMake[];
    cH ← Socket.Create[local: Socket.AssignNetworkAddress[], receive: 1];
    Socket.SetWaitTime[cH, 700]; -- milli-seconds
    THROUGH [0..3) DO
      sendBuf: BufferDefs.OisBuffer ← Socket.GetSendBuffer[cH];
      Socket.SetPacketWords[sendBuf, SIZE[TimeHeader]];
      sendBuf.ois.transCntlAndPktTp.packetType ← LOOPHOLE[123];
      Socket.SetDestination[sendBuf, target];
      timeHeader ← LOOPHOLE[@sendBuf.ois.oisWords];
      timeHeader↑ ← [id1, id2, timeRequest];
      Socket.PutPacketToAllConnectedNets[cH, sendBuf];
      DO
        recBuf: BufferDefs.OisBuffer;
	recBuf ← Socket.GetPacket[cH ! Socket.TimeOut => EXIT];
	timeHeader ← LOOPHOLE[@recBuf.ois.oisWords];
	SELECT TRUE FROM
	  Socket.GetPacketBytes[recBuf] < 2*(SIZE[WireTimeFormat] + 1)
          OR (timeHeader.id1 # id1) OR (timeHeader.id2 # id2)
	  OR (timeHeader.type # timeResponse) => LOOP;
	  ENDCASE =>
	    BEGIN OPEN t: LOOPHOLE[time, Environment.LongNumber];
	    wt: LONG POINTER TO WireTimeFormat
	       = LOOPHOLE[@recBuf.ois.oisWords[SIZE[TimeHeader]]];
	    valid ← TRUE;
	    t ← [num[lowbits: wt.timeL, highbits: wt.timeH]];
	    ltp ← [zone: wt.zoneH, direction: wt.zoneS, zoneMinutes: wt.zoneM,
	      beginDST: wt.beginDST, endDST: wt.endDST];
	    Socket.ReturnBuffer[recBuf];
	    GOTO done;
	    END;
	ENDLOOP;
      REPEAT done => NULL;
      ENDLOOP;
    Socket.Delete[cH];
    OISCP.OiscpPackageDestroy[];
    END;

END....

LOG
Time: May 31, 1980  9:54 PM  By: Forrest  Action: Re-created file from combines of old VolumeImplA & B.
Time: July 13, 1980  8:10 PM  By: Forrest  Action: Another run at the old fence.
Time: 18-Aug-81 14:36:15  By: Forrest  Action: new switches.
Time: 16-Nov-81 11:38:25 By: pasqua   Action: fold in bootstrap changes made by Knutsen
Time: 17-Nov-81 17:33:25 By: Forrest   Action: change GetOISCP time to not reference SocketInternal; implement SetExpirationDate
Time: 26-Jan-83 18:18:31 By: Johnsson   Action: only one \ for excapes.