-- DiskKD.Mesa  Edited by Sandman on July 1, 1980  7:54 AM
-- Copyright  Xerox Corporation 1979, 1980

DIRECTORY
  AllocDefs USING [AddSwapStrategy, CantSwap, SwappingProcedure, SwapStrategy],
  AltoDefs USING [PageCount, PageSize, wordlength],
  AltoFileDefs USING [DirFP, DISK, DiskShape, FIP, KD, LD, SN, vDA],
  SwapperOps USING [BootFile],
  DirectoryDefs USING [DirectoryLookup],
  DiskDefs USING [sysDisk],
  DiskKDDefs USING [],
  ImageDefs USING [
    AddCleanupProcedure, CleanupItem, CleanupMask, CleanupProcedure],
  InlineDefs USING [BITAND, BITNOT, BITOR, BITSHIFT],
  NucleusOps USING [],
  SegmentDefs USING [
    DefaultBase, DeleteFileSegment, FileHandle, FileNameError, FileSegmentAddress,
    FileSegmentHandle, MoveFileSegment, NewFileSegment, Read, SwapIn, SwapOut,
    SwapUp, Unlock, Write];

DiskKD: PROGRAM
  IMPORTS
    AllocDefs, SwapperOps, DirectoryDefs, DiskDefs, ImageDefs, InlineDefs,
    SegmentDefs
  EXPORTS DiskKDDefs, NucleusOps =
  BEGIN OPEN AltoDefs, AltoFileDefs, SegmentDefs;

  InitializeDiskKD: PUBLIC PROCEDURE =
    BEGIN
    nameKD: STRING = "DiskDescriptor."L;
    pages: PageCount;
    SetDiskFromSysDir[];
    IF ~DirectoryDefs.DirectoryLookup[@diskKD.file.fp, nameKD, FALSE] THEN
      SIGNAL FileNameError[nameKD];
    MoveFileSegment[diskKD, DefaultBase, 1];
    OpenDiskKD[];
    DiskDefs.sysDisk ← kd.disk;
    pages ← (kd.size + PageSize - 1)/PageSize;
    [] ← CloseDiskKD[];
    MoveFileSegment[diskKD, DefaultBase, pages];
    RETURN
    END;

  SetDiskFromSysDir: PROCEDURE =
    BEGIN OPEN SegmentDefs;
    sysdir: FileSegmentHandle ← NewFileSegment[
      SwapperOps.BootFile[Read], 0, 1, Read];
    leader: POINTER TO AltoFileDefs.LD;
    prop: POINTER TO ARRAY OF AltoFileDefs.FIP;
    i: CARDINAL;
    sysdir.file.fp ← AltoFileDefs.DirFP;
    sysdir.file.open ← TRUE;
    SwapIn[sysdir];
    leader ← FileSegmentAddress[sysdir];
    prop ← LOOPHOLE[leader, POINTER] + leader.propBegin;
    FOR i ← 0, i + prop[i].length UNTIL prop[i].length = 0 OR i >=
      leader.propLength DO
      IF prop[i].type = AltoFileDefs.DiskShape THEN
	BEGIN
	DiskDefs.sysDisk ← LOOPHOLE[@prop[i] + 1, POINTER TO AltoFileDefs.DISK]↑;
	EXIT
	END;
      ENDLOOP;
    Unlock[sysdir];
    DeleteFileSegment[sysdir];
    RETURN
    END;

  OpenDiskKD: PROCEDURE =
    BEGIN
    IF ~diskKD.swappedin THEN
      BEGIN SwapIn[diskKD]; kd ← FileSegmentAddress[diskKD]; kd.changed ← 0; END
    ELSE swapKD.proc ← AllocDefs.CantSwap;
    RETURN
    END;

  UpdateDiskKD: PUBLIC PROCEDURE =
    BEGIN
    IF diskKD.swappedin AND kd.changed # 0 THEN
      BEGIN
      diskKD.write ← TRUE;
      kd.changed ← 0;
      SwapUp[diskKD];
      diskKD.write ← FALSE;
      END;
    RETURN
    END;

  CloseDiskKD: PUBLIC PROCEDURE RETURNS [BOOLEAN] =
    BEGIN
    IF ~diskKD.swappedin THEN RETURN[FALSE];
    swapKD.proc ← AllocDefs.CantSwap;
    UpdateDiskKD[];
    Unlock[diskKD];
    SwapOut[diskKD];
    RETURN[TRUE]
    END;

  DiskKDSwapper: AllocDefs.SwappingProcedure = BEGIN RETURN[CloseDiskKD[]]; END;

  CleanupDiskKD: PUBLIC ImageDefs.CleanupProcedure =
    BEGIN
    SELECT why FROM
      Finish, Abort, OutLd => [] ← CloseDiskKD[]; -- Save =>
      --   We depend on MakeImage to call CloseDiskKD when
      --   it has finished allocating the image file pages.
      --   Logically, it should also call ResetDisk at this
      --   time, but it can't do that until the Restore.
      -- Restore =>
      --   We depend on MakeImage to call InitializeDiskKD
      --   as soon as the image file starts up so that the
      --   Real to Virtual disk address map can be set up.

      ENDCASE;
    RETURN
    END;

  AllOnes: WORD = 177777B;

  NewSN: PUBLIC PROCEDURE RETURNS [sn: SN] =
    BEGIN
    OpenDiskKD[];
    IF (kd.lastSN.part2 ← kd.lastSN.part2 + 1) = 0 THEN
      kd.lastSN.part1 ← kd.lastSN.part1 + 1;
    sn ← kd.lastSN;
    kd.changed ← AllOnes;
    swapKD.proc ← DiskKDSwapper;
    RETURN
    END;

  BitAddress: TYPE = RECORD [word: [0..7777B], bit: [0..17B]];

  DiskFull: PUBLIC SIGNAL = CODE;

  AssignDiskPage: PUBLIC PROCEDURE [da: vDA] RETURNS [vDA] =
    BEGIN OPEN InlineDefs;
    onebit: WORD;
    wa: CARDINAL;
    ba: [0..16);
    w: POINTER TO WORD;
    base: BitAddress = LOOPHOLE[da + 1];
    baseWa: CARDINAL ← base.word;
    baseBa: CARDINAL ← base.bit;
    OpenDiskKD[];
    DO
      ENABLE UNWIND => swapKD.proc ← DiskKDSwapper;
      FOR wa IN [baseWa..kd.size) DO
	IF (w ← @kd.table[wa])↑ # AllOnes THEN
	  FOR ba IN [baseBa..wordlength) DO
	    onebit ← BITSHIFT[100000B, -ba];
	    IF BITAND[w↑, onebit] = 0 THEN
	      BEGIN
	      w↑ ← BITOR[w↑, onebit];
	      kd.changed ← AllOnes;
	      kd.freePages ← MAX[kd.freePages, 1] - 1;
	      swapKD.proc ← DiskKDSwapper;
	      RETURN[vDA[wa*wordlength + ba]];
	      END;
	    ENDLOOP;
	baseBa ← 0;
	ENDLOOP;
      IF baseWa = 0 THEN
	BEGIN [] ← CloseDiskKD[]; SIGNAL DiskFull; OpenDiskKD[]; END;
      baseWa ← 0;
      ENDLOOP;
    END;

  ReleaseDiskPage: PUBLIC PROCEDURE [v: vDA] =
    BEGIN OPEN InlineDefs;
    word: POINTER TO WORD;
    mask: WORD = BITSHIFT[100000B, -LOOPHOLE[v, BitAddress].bit];
    OpenDiskKD[];
    word ← @kd.table[LOOPHOLE[v, BitAddress].word];
    IF BITAND[word↑, mask] # 0 THEN
      BEGIN
      word↑ ← BITAND[word↑, BITNOT[mask]];
      kd.changed ← AllOnes;
      kd.freePages ← kd.freePages + 1;
      END;
    swapKD.proc ← DiskKDSwapper;
    RETURN
    END;

  CountFreeDiskPages: PUBLIC PROCEDURE RETURNS [count: CARDINAL] =
    BEGIN
    OpenDiskKD[];
    count ← kd.freePages;
    swapKD.proc ← DiskKDSwapper;
    RETURN
    END;

  kd: POINTER TO KD;
  swapKD: AllocDefs.SwapStrategy;
  diskKD: FileSegmentHandle;
  cleanupKD: ImageDefs.CleanupItem;

  swapKD.proc ← AllocDefs.CantSwap;
  cleanupKD.proc ← CleanupDiskKD;
  cleanupKD.mask ←
    ImageDefs.CleanupMask[Finish] + ImageDefs.CleanupMask[Abort] +
      ImageDefs.CleanupMask[OutLd];
  diskKD ← NewFileSegment[
    SwapperOps.BootFile[Read + Write], DefaultBase, 1, Read];
  AllocDefs.AddSwapStrategy[@swapKD];
  ImageDefs.AddCleanupProcedure[@cleanupKD];
  InitializeDiskKD[];

  -- Should we support running without a DiskDescriptor?


  END.