-- ROMMakerCommon.mesa
-- cell subroutines to run within Chipmonk

-- last modified by E. McCreight, February 2, 1982  6:46 PM
-- written by E. McCreight, August 7, 1981  3:52 PM

DIRECTORY
  ChipUserInt,
  InlineDefs,
  ppdddefs,
  ppddefs,
  ppdefs,
  RomMakerDefs,
  StringDefs;

RomMakerCommon: PROGRAM
  IMPORTS ChipUserInt, InlineDefs,
  ppdddefs, ppddefs, ppdefs, StringDefs
  EXPORTS RomMakerDefs =
  PUBLIC BEGIN OPEN ChipUserInt, ppdddefs, ppddefs,
    ppdefs, RomMakerDefs;

  lpp: listPtr ← NIL;

  dirFactor:  ARRAY Direction OF Point ←
    [ -- vertical -- [x: 0, y: 1],
    -- horizontal -- [x: 1, y: 0],
    -- diagonal -- [x: 1, y: 1],
    -- up -- [x: 0, y: -1],
    -- down -- [x: 0, y: 1],
    -- left -- [x: -1, y: 0],
    -- right -- [x: 1, y: 0],
    -- upAndLeft -- [x: -1, y: -1],
    -- upAndRight -- [x: 1, y: -1],
    -- downAndLeft -- [x: -1, y: 1],
    -- downAndRight -- [x: 1, y: 1]];


  FindCell:  PROCEDURE [
    cellFamily: STRING, member: STRING, required: BOOLEAN ← FALSE,
    vCompat: Cell ← NIL, hCompat: Cell ← NIL] RETURNS [Cell] =
    BEGIN OPEN StringDefs;
    s: STRING ← [100];
    s.length ← 0;
    AppendString[to: s, from: cellFamily];
    AppendString[to: s, from: member];
    FOR cp: LONG POINTER TO cList ← cellList, cp.nxt WHILE cp # NIL DO
      IF StringDefs.EquivalentString[cp.name, s] THEN
        BEGIN
        c: Cell ← cp.ob;
        IF vCompat # NIL THEN CheckPitchCompatability[c, vCompat, vertical];
        IF hCompat # NIL THEN CheckPitchCompatability[c, hCompat, horizontal];
        RETURN[c];
        END;
      ENDLOOP;
    IF required THEN
      {Explain["Missing cell in family [Confirm]"L, s]; SIGNAL Punt};
    RETURN[NIL];
    END;

  FindDCell:  PROCEDURE [
    cellFamily, memberPrefix: STRING, required: BOOLEAN ← FALSE,
    vCompat: Cell ← NIL, hCompat: Cell ← NIL] RETURNS [d: DCell] =
    BEGIN OPEN StringDefs;
    originalLength: INTEGER ← memberPrefix.length;
    member: STRING ← [50];
    member.length ← 0;
    AppendString[to: member, from: memberPrefix];
    AppendString[to: member, from: "0"L];
    d[FALSE] ← FindCell[cellFamily, member, FALSE, vCompat, hCompat];
    member.length ← originalLength;
    AppendString[to: member, from: "1"L];
    d[TRUE] ← FindCell[cellFamily, member, FALSE, vCompat, hCompat];
    IF d[FALSE]=NIL THEN
      d[FALSE] ← FindCell[cellFamily, memberPrefix, required,
        vCompat, hCompat];
    IF d[TRUE]=NIL THEN d[TRUE] ← d[FALSE];
    CheckPitchCompatability[d[FALSE], d[TRUE], diagonal];
    END;  -- of FindDCell

  FindDCellPr:  PROCEDURE [
    cellFamily, memberPrefix: STRING, required: BOOLEAN ← FALSE,
    vCompat: Cell ← NIL, hCompat: Cell ← NIL] RETURNS [p: DCellPr] =
    BEGIN OPEN StringDefs;
    originalLength: INTEGER ← memberPrefix.length;
    member: STRING ← [50];
    member.length ← 0;
    AppendString[to: member, from: memberPrefix];
    AppendString[to: member, from: "0"L];
    p[FALSE] ← FindDCell[cellFamily, member, required, vCompat, hCompat];
    member.length ← originalLength;
    AppendString[to: member, from: "1"L];
    p[TRUE] ← FindDCell[cellFamily, member, required, vCompat, hCompat];
    CheckPitchCompatability[p[FALSE][FALSE], p[TRUE][FALSE], diagonal];
    END;  -- of FindDCell

  CheckPitchCompatability:  PROCEDURE [
    c1, c2: Cell, dir: Direction, rep1: Point ← [1, 1], rep2: Point ← [1, 1]] =
    BEGIN OPEN StringDefs;

    FindCellName: PROCEDURE [c: Cell] RETURNS [STRING] =
      BEGIN
      FOR cp: LONG POINTER TO cList ← cellList, cp.nxt WHILE cp # NIL DO
        IF c = cp.ob THEN RETURN[cp.name] ENDLOOP;
      RETURN[""];
      END;

    IF c1#NIL AND c2#NIL AND Offset[, c1, dir, rep1]#Offset[, c2, dir, rep2] THEN
      BEGIN
      cellNames: STRING ← [50];
      cellNames.length ← 0;
      AppendString[to: cellNames, from: FindCellName[c1]];
      AppendString[to: cellNames, from: " - "L];
      AppendString[to: cellNames, from: FindCellName[c2]];
      SELECT dir FROM
        vertical, up, down =>
          Explain["Incompatible cell vertical pitches [Confirm]"L, cellNames];
        horizontal, left, right =>
          Explain["Incompatible cell horizontal pitches [Confirm]"L, cellNames];
        ENDCASE => Explain["Incompatible cell sizes [Confirm]"L, cellNames];
      SIGNAL Punt;
      END;
    END;  -- of CheckPitchCompatability


  Repeat:  PROCEDURE[p: Point, cell: Cell, corner: Direction ← topLeft,
    dir: Direction ← horizontal, rep: locNum ← 1,
    midCell: Cell ← NIL, midGap: locNum ← 0,
    altCell: Cell ← NIL, altMod: INTEGER ← 0,
    name, midName: NameGroup ← nilNameGroup] =
    BEGIN

    GetCell: PROCEDURE[i: INTEGER] RETURNS[Cell] =
      {RETURN[
        IF i>0 AND altCell#NIL AND altMod>0 AND (i MOD altMod)#0 THEN altCell
        ELSE cell]};

    RepeatArbitrary[p: p, cp: GetCell, corner: corner, dir: dir,
      rep: rep, midCell: midCell, midGap: midGap,
      name: name, midName: midName];
    END;


  RepeatArbitrary:  PROCEDURE[p: Point, cp: PROCEDURE[i: INTEGER] RETURNS[Cell],
    corner: Direction ← topLeft, dir: Direction ← upAndLeft,
    rep: INTEGER ← 1, midCell: Cell ← NIL, midGap: locNum ← 0,
    name, midName: NameGroup ← nilNameGroup] =
    BEGIN
    cellSize: Point ← CellSize[cp[0]];
    midCellSize: Point ← CellSize[midCell];

    SELECT corner FROM
      topRight => p.x ← p.x-cellSize.x;
      bottomLeft => p.y ← p.y-cellSize.y;
      bottomRight => p ← [x: p.x-cellSize.x, y: p.y-cellSize.y];
      ENDCASE => NULL;

    SELECT dir FROM
      vertical, up, down =>
        BEGIN
        FOR iy: INTEGER IN [0..rep) DO
          PlaceCell[p: p, cell: cp[iy],
            rep: [0, iy], midCellSize: midCellSize,
            midGap: [0, midGap], name: name];
          ENDLOOP;
        FOR iy: INTEGER IN [0..MidCellRep[midGap, rep-1, cellSize.y]) DO
          PlaceCell[p: [p.x,
            p.y+(iy+1)*cellSize.y*CellsPerGap[midGap, cellSize.y]+iy*midCellSize.y],
            cell: midCell, name: midName];
          ENDLOOP;
        END;
      horizontal, left, right =>
        BEGIN
        FOR ix: INTEGER IN [0..rep) DO
          PlaceCell[p: p, cell: cp[ix],
            rep: [ix, 0], midCellSize: midCellSize,
            midGap: [midGap, 0], name: name];
          ENDLOOP;
        FOR ix: INTEGER IN [0..MidCellRep[midGap, rep-1, cellSize.x]) DO
          PlaceCell[
            p: [p.x+(ix+1)*cellSize.x*CellsPerGap[midGap, cellSize.x]+ix*midCellSize.x,
              p.y],
            cell: midCell, name: midName];
          ENDLOOP;
        END;
      ENDCASE => NULL;
    END; -- of RepeatArbitrary


  CellsPerGap: PROCEDURE[gap, sizePerCell: locNum] RETURNS[INTEGER] =
    {RETURN[(gap+sizePerCell-1)/sizePerCell] -- ceiling --};


  MidCellRep: PROCEDURE[gap, nCells, sizePerCell: locNum] RETURNS[INTEGER] =
    BEGIN
    cellsPerGap: INTEGER ← CellsPerGap[gap, sizePerCell];
    RETURN[IF cellsPerGap=0 THEN 0 ELSE nCells/cellsPerGap];
    END;


  MakeMidGrid: PROCEDURE[p: Point, cell: Cell, rep: Point,
    midVCell, midHCell, midVHCell: Cell ← NIL, midGap: Point ← [0,0]] =
    BEGIN
    cellSize: Point ← CellSize[cell];
    midCellSize: Point ← [x: CellSize[midVCell].x,
      y: CellSize[midHCell].y];
    sizePerGap: Point ← [x: cellSize.x*CellsPerGap[midGap.x, cellSize.x],
      y: cellSize.y*CellsPerGap[midGap.y, cellSize.y]];
    midCellRep: Point ← [x: MidCellRep[midGap.x, rep.x-1, cellSize.x],
      y: MidCellRep[midGap.y, rep.y-1, cellSize.y]];
    FOR ix: INTEGER IN [0..rep.x) DO
      FOR iy: INTEGER IN [0..midCellRep.y) DO
        PlaceCell[p: [p.x, p.y+sizePerGap.y+iy*(sizePerGap.y+midCellSize.y)], cell: midHCell,
          rep: [ix, 0], midCellSize: CellSize[midVHCell], midGap: midGap];
        ENDLOOP;
      ENDLOOP;
    FOR ix: INTEGER IN [0..midCellRep.x) DO
      Repeat[p: [p.x+sizePerGap.x+ix*(sizePerGap.x+midCellSize.x), p.y], cell: midVCell,
          dir: vertical, rep: rep.y, midCell: midVHCell, midGap: midGap.y];
      ENDLOOP;
    END; -- of MakeMidGrid


  PlaceCell: PROCEDURE[p: Point, cell: Cell, rep: Point ← [0,0], midCellSize: Point ← [0,0],
    midGap: Point ← [0,0], name: NameGroup ← nilNameGroup] =
    BEGIN
    IF cell#NIL THEN
      BEGIN
      cp: Point ← Offset[p: p, cell: cell, dir: diagonal, rep: rep,
        midCellSize: midCellSize, midGap: midGap];
      lp: listPtr ← makeList[cell, cp.x, cp.y, 0, 0];
      AddInstName[lp, name, rep];
      lpp ← insertList[lpp, lp];
      END;
    END;


  AddInstName: PROCEDURE[lp: listPtr, name: NameGroup,
    rep: Point] =
    BEGIN OPEN StringDefs;

    ComputeIndex: PROCEDURE[i: INTEGER, coef: Point]
      RETURNS[indx: INTEGER] =
      BEGIN
      k: INTEGER ← coef.x*i+coef.y;
      RETURN[IF k<0 THEN 0 ELSE k];
      END;

    s: STRING ← [100];
    tp: LONG POINTER TO text prop ← NIL;
    xIndx: INTEGER ← ComputeIndex[rep.x, name.x];
    yIndx: INTEGER ← ComputeIndex[rep.y, name.y];

    IF lp=NIL OR name.basicName=NIL THEN RETURN;
    s.length ← 0;
    AppendString[to: s, from: name.basicName];
    IF xIndx#0 OR yIndx#0 THEN AppendChar[s: s, c: '-];
    IF xIndx#0 THEN AppendDecimal[s: s, n: xIndx];
    IF yIndx#0 THEN
      BEGIN
      AppendChar[s: s, c: '-];
      AppendDecimal[s: s, n: yIndx];
      END;
    tp ← GetSpace[SIZE[text prop]];
    tp↑ ← [nxt: lp.props, varpart: text[s: newString[s]]];
    lp.props ← tp;
    END; -- of AddInstName


  DirSize: PROCEDURE[size: Point, dir: Direction ← diagonal] RETURNS[Point] =
    {RETURN[
      [x: dirFactor[dir].x*size.x, y: dirFactor[dir].y*size.y]]};


  CellSize: PROCEDURE[cell: Cell, dir: Direction ← diagonal] RETURNS[Point] =
    {RETURN[IF cell=NIL THEN [0,0] ELSE
      DirSize[size: [x: cell.size[0], y: cell.size[1]], dir: dir]]};


  Offset: PROCEDURE[p: Point ← [0, 0], cell: Cell, dir: Direction, rep: Point ← [0, 0],
    midCell: Cell ← NIL, midCellSize: Point ← [0,0],
    midGap: Point ← [0, 0], includeTerminalMid: BOOLEAN ← TRUE]
    RETURNS [Point] =
    BEGIN
    midRep: Point ← IF includeTerminalMid THEN rep ELSE
      [x: rep.x-1, y: rep.y-1];
    midCellSize ← IF midCell#NIL THEN CellSize[midCell, dir]
      ELSE DirSize[midCellSize, dir];
    RETURN[IF cell=NIL THEN p ELSE 
      [x: p.x+rep.x*CellSize[cell, dir].x+
        MidCellRep[midGap.x, midRep.x, CellSize[cell].x]*
          midCellSize.x,
      y: p.y+rep.y*CellSize[cell, dir].y+
        MidCellRep[midGap.y, midRep.y, CellSize[cell].y]*
          midCellSize.y]];
    END;


  BlockSize: PROCEDURE [cell: Cell, rep: Point ← [1, 1],
    midCell: Cell ← NIL, midGap: Point ← [0, 0]]
    RETURNS [Point] =
    {RETURN[Offset[cell: cell, rep: rep, midCell: midCell, midGap: midGap,
      p: [0,0], dir: diagonal, includeTerminalMid: FALSE]]};


  MakeNewCell: PROCEDURE [name: STRING, lpp: listPtr]
    RETURNS [cp: LONG POINTER TO cList] =
    BEGIN
    min: Point ← [LAST[locNum], LAST[locNum]];
    max: Point ← [FIRST[locNum], FIRST[locNum]];
    lp: listPtr;
    FOR lp ← lpp, lp.nxt WHILE lp # NIL DO
      ii: [0..1] ← IF InlineDefs.BITAND[lp.idx, 4] = 0 THEN 0 ELSE 1;
      min ← [MIN[min.x, lp.lx], MIN[min.y, lp.ly]];
      max ← [
        MAX[max.x, lp.lx + lp.ob.size[ii]], MAX[
        max.y, lp.ly + lp.ob.size[ii + 1]]];
      ENDLOOP;
    FOR lp ← lpp, lp.nxt WHILE lp # NIL DO
      lp.lx ← lp.lx - min.x; lp.ly ← lp.ly - min.y; lp.selected ← FALSE; ENDLOOP;
    cp ← alocCList[];
    cp.nxt ← cellList;
    cellList ← cp;
    cp.ob ← makeCell[max.x - min.x, max.y - min.y, 0, lpp];
    cp.name ← name;
    END;  -- of MakeNewCell

  AddToMasterList: PROCEDURE[lpp: listPtr] =
    BEGIN
    min: Point ← [LAST[locNum], LAST[locNum]];
    max: Point ← [FIRST[locNum], FIRST[locNum]];
    lp: listPtr;
    FOR lp ← lpp, lp.nxt WHILE lp # NIL DO
      ii: [0..1] ← IF InlineDefs.BITAND[lp.idx, 4] = 0 THEN 0 ELSE 1;
      min ← [MIN[min.x, lp.lx], MIN[min.y, lp.ly]];
      max ← [
        MAX[max.x, lp.lx + lp.ob.size[ii]], MAX[
        max.y, lp.ly + lp.ob.size[ii + 1]]];
      ENDLOOP;
    FOR lp ← masterList, lp.nxt WHILE lp # NIL DO
      IF lp.selected THEN
        BEGIN
        lp.selected ← FALSE;
        reDrawRect[getRect[lp], 0, TRUE, TRUE, FALSE];
        END;
      ENDLOOP;
    FOR lp ← lpp, lp WHILE lp # NIL DO
      p: listPtr ← lp; lp ← p.nxt; p.nxt ← NIL;
      p.lx ← p.lx - min.x + xx;
      p.ly ← p.ly - min.y + yy;
      p.selected ← TRUE;
      masterList ← insertList[masterList, p];
      ENDLOOP;
    putMark[xx, yy];
    reDrawRect[
      [x1: xx, x2: xx+max.x-min.x, y1: yy, y2: yy+max.y-min.y],
      0, TRUE, TRUE, FALSE];
    anyChanges ← sinceIOchanges ← TRUE;
    END; -- of AddToMasterList

  DrawCell: PROCEDURE [obp: Cell] =
    BEGIN
    lp: listPtr ← makeList[obp.p.anotherme[obp], xx, yy, 0, 0];
    obp.returnable ← FALSE;
    masterList ← insertList[masterList, lp];
    selNewThing[masterList, lp, TRUE];
    putMark[xx, yy];
    reDrawRect[getRect[lp], 0, TRUE, TRUE, FALSE];
    anyChanges ← sinceIOchanges ← TRUE;
    END;  -- of DrawCell

  END. -- of RomMakerCommon