-- I/O module of silicon (pretty picture) program

-- last modified by McCreight, December 20, 1983  2:21 PM
-- modified by Petit, Sept 20, 1981

DIRECTORY
  InlineDefs,
  MiscDefs,
  StreamDefs,
  SystemDefs,
  IODefs,
  AltoDefs,
  DirectoryDefs,
  AltoFileDefs,
  StringDefs,
  ppoutdefs,
  ppdefs,
  ProcessDefs;

ppout: MONITOR
  IMPORTS ppdefs, InlineDefs, MiscDefs, StreamDefs, DirectoryDefs, ProcessDefs
  EXPORTS ppoutdefs =
  BEGIN OPEN ppdefs, ppoutdefs, InlineDefs, StreamDefs, IODefs, DirectoryDefs;

  fp: AltoFileDefs.FP;

  cellNameA: PUBLIC LONG DESCRIPTOR FOR ARRAY OF LONG POINTER TO cell object;

  oCnt, cCnt: PUBLIC CARDINAL;

  outObject: PROCEDURE [ob: LONG POINTER TO object] =
    BEGIN
    i, j: CARDINAL;
	qq:INTEGER;
    IF ob=NIL THEN outWord[0] -- read as NIL
    ELSE
    SELECT ob.otyp FROM
      cell =>
        BEGIN
        outWord[1];  -- code for cell
        j ← 0;
        FOR i IN [1..cCnt] DO
          IF ob = cellNameA[i] THEN BEGIN j ← i; EXIT; END; ENDLOOP;
        outWord[j];  -- unique # to identify cell
        END;
      xstr =>
        BEGIN
        d: LONG POINTER TO xstr object;
        d ← LOOPHOLE[ob];
        outWord[2];  -- code for xstr
        outWord[d.width];
        outWord[d.length];
        j ← d.lExt + d.wExt*64;
        IF d.pullup THEN j ← BITOR[j, 40000B];
        IF d.impl>0 THEN j ← BITOR[j, 100000B];
        IF d.angle THEN j ← BITOR[j, 20000B];
        IF d.l=pdif THEN j ← BITOR[j, 10000B];
        outWord[j];  -- packed word
	IF d.l=pdif THEN outWord[d.surround];
        IF d.angle THEN
	    BEGIN
		qq←MAX[0,d.size[1]-d.wExt-d.length-2*d.lExt];
        	outWord[qq];  -- angle extension
	    END;
        IF d.impl>0 THEN outWord[d.impl];
        END;
      cont =>
        BEGIN
        d: LONG POINTER TO cont object ← LOOPHOLE[ob];
	ss:INTEGER←d.size[1];
        outWord[3];  -- code for contact
        j ← 0;
        FOR i IN [1..contTypALen] DO
          IF d.typ = contTypA[i] THEN BEGIN j ← i; EXIT; END; ENDLOOP;
        j ← BITOR[j, BITSHIFT[ss, 8]];
        outWord[j];  -- contact type code
	outWord[d.l]; -- put out layer parameter
           outWord[d.surround];-- put out surround
	IF d.typ=burr THEN
	    BEGIN
		outWord[d.size[0]];
		outWord[BITOR[BITSHIFT[d.wExt,8],d.lExt]];
	    END;
        END;
      wire =>
        BEGIN
        d: LONG POINTER TO wire object ← LOOPHOLE[ob];
	s0:INTEGER←d.size[0];
	s1:INTEGER←d.size[1];
        outWord[4];  -- code for wire
        outPoint[[x: ob.size[0], y: ob.size[1]]];
        outWord[d.l];
	IF d.l=pdif THEN outWord[d.surround];
        END;
      rect =>
        BEGIN
        outWord[5];  -- code for rect
        outPoint[[x: ob.size[0], y: ob.size[1]]];
        outWord[ob.l];
        END;
      bus =>
        BEGIN
        d: LONG POINTER TO bus object;
        d ← LOOPHOLE[ob];
        outWord[7];  -- code for wire
        outWord[d.wwidth];
        outWord[d.firstLength];
        outWord[d.l];
        outWord[d.wCnt];
        outWord[d.wspace];
        outWord[d.topIncr];
        outWord[d.lenIncr+d.topIncr];
        END;
      ENDCASE => outWord[0];  -- code for unknown, reads as NIL
    END;

  outPoint: PROC[p: Point] = {outWord[p.x]; outWord[p.y]};

  outRect: PROC[r: Rect] = {outPoint[[r.x1, r.y1]]; outPoint[[r.x2, r.y2]]};

  outInt: PROC[i: INT] =
    BEGIN
    ln: LongNumber = [li[i]];
    outWord[ln.highbits];
    outWord[ln.lowbits]
    END;

  outPushedLevels: PROC [cs: cellSEPtr, depth: NAT ← 1] =
    BEGIN
    IF cs=NIL THEN RETURN;
    outPushedLevels[cs.nxt, depth+1];
    outWord[depth];
    outWord[cs.changes];
    outPoint[cs.origSize];
    outRect[cs.origBB];
    outObject[cs.dest];
    outList[cs.lp];
    outPositionInList[cs.instance, cs.lp];
    END;

  outPositionInList: PROC [target, within: listPtr] =
    BEGIN
    i: INT ← 1;
    FOR lp: listPtr ← within, lp.nxt WHILE lp#NIL DO
      IF lp=target THEN {outInt[i]; RETURN};
      i ← i+1;
      ENDLOOP;
    outInt[0]; -- not found
    END;

  outList: PROCEDURE [lp: LONG POINTER TO list] =
    BEGIN
    i: CARDINAL ← 0;
    FOR tp: listPtr ← lp, tp.nxt WHILE tp # NIL DO i ← i + 1 ENDLOOP;
    outWord[i]; -- count of items
    FOR lp ← lp, lp.nxt WHILE lp # NIL DO
      pop: propPtr;
      propCount: INTEGER ← 0;
      outWord[lp.lx];
      outWord[lp.ly];
      outWord[lp.idx];
      outObject[lp.ob];
      FOR pop ← lp.props, pop.next WHILE pop#NIL DO
        propCount ← propCount+1;
        ENDLOOP;
      outWord[propCount];
      FOR pop ← lp.props, pop.next WHILE pop#NIL DO
        outWord[2];
        outAtom[pop.attribute];
        outAtom[pop.value];
        ENDLOOP;
      ENDLOOP;
    END;

  freeAtom: PUBLIC INTEGER ← 1;
  auxAtom: PUBLIC ARRAY [0..atomTableSize] OF RECORD [
    older, younger: INTEGER -- doubly-linked LRU list --];

  outAtom: PROC [a: Atom] =
    BEGIN
    i: INTEGER;
      BEGIN
      FOR i ← auxAtom[0].older, auxAtom[i].older WHILE i#0 DO
        IF a=atomTable[i] THEN
          BEGIN
          outWord[-i];
          -- re-arrange LRU queue
          auxAtom[auxAtom[i].older].younger ← auxAtom[i].younger;
          auxAtom[auxAtom[i].younger].older ← auxAtom[i].older;
          GOTO Touchi;
          END;
        ENDLOOP;
      -- add a new atom
      IF freeAtom<atomTableSize THEN {i ← freeAtom; freeAtom ← freeAtom+1}
      ELSE
        BEGIN
        i ← auxAtom[0].younger; -- re-use oldest table entry
        auxAtom[0].younger ← auxAtom[i].younger;
        auxAtom[auxAtom[i].younger].older ← 0;
        END;
      atomTable[i] ← a;
      outWord[i];
      outString[LOOPHOLE[a]];
      GOTO Touchi;
      EXITS
        Touchi =>
          BEGIN
          auxAtom[i].older ← auxAtom[0].older;
          auxAtom[i].younger ← 0;
          auxAtom[auxAtom[0].older].younger ← i;
          auxAtom[0].older ← i;
          END;
      END;
    END;

  outCell: PROCEDURE [p: obPtr, cp: LONG POINTER TO cList] =
    BEGIN
    WITH dp: p SELECT FROM
      cell =>
        BEGIN
        IF dp.marked THEN RETURN;
        FOR lp: listPtr ← dp.ptr, lp.nxt WHILE lp # NIL DO
          outCell[lp.ob, cp];
          ENDLOOP;
        oCnt ← oCnt + 1;
        cellNameA[oCnt] ← @dp;
        p.marked ← TRUE;
        outWord[oCnt];
        outCellName[@dp, cp];
        outWord[p.size[0]];
        outWord[p.size[1]];
        outWord[dp.cnt];
        outList[dp.ptr];
        END;
      ENDCASE => NULL;
    END;

  outCellName: PROCEDURE [
    p: LONG POINTER TO cell object, cp: LONG POINTER TO cList] =
    BEGIN
    s: STRING ← "";
    WHILE cp # NIL DO
      IF cp.ob = p THEN BEGIN s ← cp.name; EXIT; END; cp ← cp.nxt; ENDLOOP;
    outString[s];
    END;

  outWord: PROC[UNSPECIFIED]; -- a variable

  writeAll: PUBLIC ENTRY PROCEDURE [put: PROC[UNSPECIFIED]] =
    BEGIN ENABLE UNWIND => NULL;
    cdp, ctp: LONG POINTER TO cell object;
    oCnt ← cCnt ← 0;
    outWord ← put;
    ctp ← cdp ← GetCellSuper[];
    WHILE ctp # NIL DO
      cCnt ← cCnt + 1; ctp.marked ← FALSE; ctp ← ctp.super; ENDLOOP;

    freeAtom ← 1;
    auxAtom[0] ← [older: 0, younger: 0];

    outWord[codeWordForDataFile];
    outWord[8];  -- version #
    outWord[cCnt];  -- output count of cell objects

      BEGIN ENABLE UNWIND => FreeSpace[BASE[cellNameA]];
      cellNameA ← DESCRIPTOR[
        GetSpace[(cCnt + 1)*SIZE[LONG POINTER TO cell object]], cCnt + 1];
      WHILE cdp # NIL DO outCell[cdp, cellList]; cdp ← cdp.super; ENDLOOP;
      IF oCnt # cCnt THEN ERROR;

      outPushedLevels[cellStack];
      outWord[0]; -- now level 0
      outWord[anyChanges];
      outList[masterList];  -- output master list
      END;
    FreeSpace[BASE[cellNameA]];
    END;

  outString: PROCEDURE [s: LONG STRING] =
    BEGIN
    bflg: BOOLEAN ← FALSE;
    sav: UNSPECIFIED;
    i: CARDINAL;
    obyt: PROCEDURE [u: UNSPECIFIED] =
      BEGIN
      IF bflg THEN outWord[BITOR[u, sav]] ELSE sav ← BITSHIFT[u, 8];
      bflg ← NOT bflg;
      END;
    obyt[s.length];
    FOR i IN [0..s.length) DO obyt[s[i]]; ENDLOOP;
    obyt[0];
    END;

  openOfile: PUBLIC PROCEDURE [fname: STRING, existOK: BOOLEAN]
    RETURNS [DiskHandle] =
    BEGIN
    IF NOT existOK AND DirectoryLookup[@fp, fname, FALSE] THEN RETURN[NIL];
    RETURN[NewWordStream[fname, Write + Append]];
    END;

  doBackupNow: BOOLEAN ← FALSE;
  killBackup: BOOLEAN ← FALSE;
  EnableBackup: PUBLIC PROC = {doBackupNow ← TRUE};
  DisableBackup: PUBLIC PROC = {doBackupNow ← FALSE; killBackup ← TRUE};

  backupPass: PUBLIC LONG CARDINAL ← 0;

  BackupDemonProcess: PUBLIC PROC =
    BEGIN OPEN ProcessDefs;
    WritingInterrupted: ERROR = CODE;
    bf, obf, tf: DiskHandle;

    PutBackupWord: PROC[w: UNSPECIFIED] =
      BEGIN
      Yield[];
      IF killBackup THEN ERROR WritingInterrupted;
      IF GetPosition[bf]=0 THEN w ← 0;  -- write a bad seal
      bf.put[bf, w];
      END;

    ProcessDefs.SetPriority[0];
    bf ← NewWordStream["Chipmonk-Backup1.chip",
      Write + Append];
    obf ← NewWordStream["Chipmonk-Backup2.chip",
      Write + Append];
    bf.reset[obf];
    bf.put[obf, 0]; -- smash seal, backup is obsolete if not invalid
    CleanupDiskStream[obf];
    DO
      bf.reset[bf];
      bf.put[bf, 0]; -- smash seal, backup is obsolete if not invalid
      CleanupDiskStream[bf];
      ProcessDefs.Pause[ProcessDefs.SecondsToTicks[15]];
      IF NOT doBackupNow THEN LOOP;
      killBackup ← FALSE;
      bf.reset[bf];
      writeAll[PutBackupWord !
        UNWIND => NULL;
        WritingInterrupted => LOOP;
        ANY =>
          {MiscDefs.CallDebugger["Signal to Backup writer, OK to proceed"]; LOOP}];
      CleanupDiskStream[bf];
      bf.reset[bf];
      bf.put[bf, codeWordForDataFile]; -- validate seal, backup is good
      CleanupDiskStream[bf];
      tf ← bf; bf ← obf; obf ←tf; -- swap files
      backupPass ← backupPass+1;
      ENDLOOP;
    END;

  END.