EBESMaskImpl.mesa
A package to output a ChipnDale design in EBES format for a Varian Lithographic System. The format produced herein is described in the proprietary document "Pattern Data Formats for Varian Lithography Systems", dated May, 1982, and available from
Varian/Semiconductor Equipment Group
Lithography Products Division
Blackburn Industrial Park
Gloucester, Mass. 01930
It also conforms, more or less, to the specification in the MEBES Software Manual, document A900-0102C, Data Files, document A900-0024D, publication of
Perkin-Elmer
Electron Beam Technology
3392 Investment Boulevard
Hayward, CA 94545
written by E. McCreight, November 2, 1983 6:03 pm
Last Edited by: Jacobi, October 19, 1984 10:09:54 am PDT
Last Edited by: Mccreight, November 7, 1984 7:01:24 pm PST
DIRECTORY
Ascii,
Atom,
CD,
CDCommandOps,
CDViewer,
CDBasics,
CDMenus,
CDOps,
CDSequencer,
CDProperties,
CornerStitching,
FS,
IO,
List,
PriorityQueue,
Process,
Rope,
TapeStreams,
TapeOps,
TerminalIO,
BasicTime;
EBESMaskImpl: CEDAR PROGRAM
IMPORTS Ascii, Atom, BasicTime, CD, CDCommandOps, CDBasics, CDMenus, CDOps, CDProperties, CDSequencer, CDViewer, CornerStitching, FS, IO, List, PriorityQueue, Process, Rope, TapeOps, TapeStreams, TerminalIO
EXPORTS =
BEGIN
Nm: TYPE = CD.Number;
EBESPixels: TYPE = CD.Number;
Rational: TYPE = RECORD [num, denom: INT ← 1];
MaskStateRef: TYPE = REF MaskState;
MaskState: TYPE = RECORD [
maskSetName, patternFileName: Rope.ROPENIL,
s: IO.STREAM, -- EBES stream where rectangles are to be drawn
scale: Rational,
drawCommand: REF DrawRectangle,
level: ARRAY CD.Level OF MaskDirective,
hasNegativeBloat: BOOLFALSE,
areas, nbAreas: REF CornerStitching.Tesselation ← NIL, -- EBES space
stripeClip: CD.Rect ← [0,0,0,0], -- EBES space
reticleClip: CD.Rect ← [0,0,0,0], -- EBES space
stripe: [0..maxStripes] ← 0,
stripeRectCount: INT ← 0,
scribe: Scribe,
data: REFNIL
];
DesignDataRef: TYPE = REF DesignData;
DesignData: TYPE = RECORD [
bbox: REF CD.DesignRect
];
MaskDest: TYPE = REF MaskDestRec ← NIL;
MaskDestRec: TYPE = RECORD [
name: Rope.ROPE,
complement: BOOLFALSE,
deltaDiameter: EBESPixels ← 0 -- + means mask feature is bigger
];
MaskDirective: TYPE = REF MaskDirectiveRec ← NIL;
MaskDirectiveRec: TYPE = RECORD [
cdSource: CD.Level,
dest: MaskDest
];
Scribe: TYPE = REF ScribeRec ← NIL;
ScribeRec: TYPE = RECORD [
maskName: Rope.ROPE,
dieEdgeToLineCenter, lineWidth: Nm ← 0
];
EBES command formats:
stripeHeight: INT = 256; -- in EBES units
maxStripes: INT = 255;
maskHeight: INT = maxStripes*stripeHeight;
maskWidth: INT = 32768;
StartDrawing: TYPE = MACHINE DEPENDENT RECORD [
nFields (0: 0..7): [0..256) ← 2,
addressCode (0: 8..15): AddressCode ← Nm500,
cx (1): CARDINAL[0..maskWidth], -- 0 is illegal
cy (2): CARDINAL[0..maskHeight], -- 0 is illegal
moDayYr (3): Date,
field1Size (6): CARDINAL ← 6,
patternFileName (7): FileName,
field2Size (13): CARDINAL ← 2,
maskInfo (14): PACKED ARRAY [0..4) OF CHARALL[' ]
];
AddressCode: TYPE = MACHINE DEPENDENT {Nm1000(0), Nm500(3), Nm250(6)};
Date: TYPE = PACKED ARRAY [0..6) OF CHARALL[' ];
month [01..12], day [01..31], year [00..99]
FileName: TYPE = PACKED ARRAY [0..12) OF CHARALL[' ];
[9] must be '. , [10] is column ['A..'Z], [11] is row ['A..'Z]
DrawRectangle: TYPE = MACHINE DEPENDENT RECORD [
h (0: 0..9): [1..stripeHeight], -- Mesa biases subrange so 1 is represented as 0
commandCode (0: 10..15): [0..64) ← 16,
w (1): CARDINAL[0..maskWidth], -- 0 is illegal
x (2): CARDINAL[0..maskWidth),
unused (3: 0..7): [0..256) ← 0,
y (3: 8..15): [0..stripeHeight)];
StartStripe: TYPE = MACHINE DEPENDENT RECORD [
stripeNumber (0: 0..7): [0..maxStripes] ← 0, -- 0 is illegal
commandCode (0: 8..15): [0..256) ← 7];
BasicCommand: TYPE = MACHINE DEPENDENT RECORD [commandCode (0): NAT ←];
endStripe: BasicCommand = [commandCode: 8];
endRecord: BasicCommand = [commandCode: 9];
endDrawing: BasicCommand = [commandCode: 4];
Global Variables
nmPerEBESPixel: INT ← 250;
stripesPerClump: INT ← 10;
wDir: Rope.ROPE ← "///";
tapeServerName: Rope.ROPE ← "Maggie";
Init: PROC ~ {
CDSequencer.ImplementCommand[a~$EBESMask , p~StartEBESMask, queue~doQueue];
CDMenus.CreateEntry[menu~$ProgramMenu, entry~"EBES mask generation", key~$EBESMask];
TerminalIO.WriteRope["ChipNDale EBES mask generator loaded\n"];
};
abortFlag: REF BOOLNEW[BOOL];
StartEBESMask: PROC [comm: CDSequencer.Command] =
TRUSTED BEGIN
TerminalIO.WriteRope[" EBES-mask generation\n"];
TerminalIO.WriteRope[" not yet debugged\n"];
TerminalIO.WriteRope["Releases lock of design, but design MUST NOT be changed\n"];
abortFlag^ ← FALSE;
[] ← CDCommandOps.CallWithResource[proc: EBESMask, comm: comm, resource: $EBESMask, abortFlag: abortFlag];
END;
EBESMask: PROC [ comm: CDSequencer.Command ] =
BEGIN
NotePunt: PROC = {TerminalIO.WriteRope[IO.PutFR["\n*** EBES mask generation aborted at %g\n", IO.time[]]]};
BEGIN ENABLE
BEGIN
ABORTED => GOTO Punt;
TerminalIO.UserAbort => GOTO Punt;
UNWIND => NotePunt[];
END;
fileNoRef: REF INT = NEW[INT ← 0];
SELECT TerminalIO.RequestSelection[label: "Destination",
choice: LIST[" Local files", " Tape 0", " Tape 1"]
] FROM
1 => MakeMask[comm: comm, streamGen: SimpleStreamGen];
2 => MakeTapeMasks[comm: comm, drive: 0];
3 => MakeTapeMasks[comm: comm, drive: 1];
ENDCASE => {NotePunt[]; RETURN};
EXITS Punt => NotePunt[];
END;
END;
SimpleStreamGen: PROC [ ms: MaskStateRef ] RETURNS [ dest: IO.STREAM ] =
BEGIN
dest ← FS.StreamOpen[
fileName: IO.PutFR[format: "%g%g/%g",
v1: IO.rope[wDir], v2: IO.rope[ms.maskSetName], v3: IO.rope[ms.patternFileName]
],
accessOptions: create
];
END;
MakeTapeMasks: PROC [comm: CDSequencer.Command, drive: [0..4]] =
BEGIN
tape: TapeOps.TapeHandle←NIL;
CloseTape: PROC =
BEGIN
IF tape#NIL THEN {
tape.CloseDrive[ ! TapeOps.TapeOpsError => {TerminalIO.WriteRope[IO.PutFR[" ..trouble closing tape connection: %s..", IO.rope[ec]]]; CONTINUE} ];
tape ← NIL
};
END;
BEGIN ENABLE
{TapeOps.TapeOpsError =>
BEGIN
TerminalIO.WriteRope[ec];
GOTO TapePunt;
END;
TapeStreams.Error =>
BEGIN
TerminalIO.WriteRope[error.explanation];
GOTO TapePunt;
END;
UNWIND => CloseTape[];
};
tape ← TapeOps.OpenDrive[serverName: tapeServerName, driveNumber: drive, density: PE1600];
[] ← tape.Rewind[];
MakeMask[comm: comm, streamGen: TapeSubStreamGen, data: tape];
[] ← tape.Unload[];
CloseTape[];
EXITS TapePunt => {CloseTape[]; ERROR TerminalIO.UserAbort};
END;
END;
TapeSubStreamGen: PROC [ ms: MaskStateRef ] RETURNS [ dest: IO.STREAM ] =
BEGIN
tape: TapeOps.TapeHandle = NARROW[ms.data];
dest ← TapeStreams.StreamOpen[tape: tape, access: write, blocking: 2048, padding: zeroes];
END;
MakeMask: PROC [comm: CDSequencer.Command,
streamGen: PROC [ ms: MaskStateRef ] RETURNS [ dest: IO.STREAM ], data: REFNIL ] =
BEGIN
Prop: PROC [prop: REF, default: REFNIL, nilOK: BOOLFALSE] RETURNS [value: REF] =
BEGIN
value ← CDProperties.GetPropFromDesign[design, prop];
IF value = NIL THEN value ← CDProperties.GetPropFromTechnology[design.technology, prop];
IF value = NIL THEN value ← CDProperties.GetPropFromAtom[$EBESMask, prop];
IF value = NIL THEN value ← default;
IF value = NIL AND NOT nilOK THEN
BEGIN
TerminalIO.WriteRope[IO.PutFR[" .. no value for property %g.. ", IO.rope[ToRope[prop]]]];
ERROR TerminalIO.UserAbort;
END;
END;
NumericProp: PROC [prop: REF] RETURNS [value: INT] =
{value ← NARROW[Prop[prop], REF INT]^};
design: CD.Design = comm.design;
nmPerLambda: INT;
ms: MaskStateRef = NEW[ MaskState ← [
maskSetName: ToRope[Prop[$CDxEBESMaskSetName]],
areas: CornerStitching.NewTesselation[],
nbAreas: CornerStitching.NewTesselation[],
drawCommand: NEW[DrawRectangle],
data: data]];
dr: CD.DrawRef = CD.NewNullDeviceDrawRef[design];
dd: DesignDataRef = NEW[DesignData ← [bbox: NIL]];
designClip: CD.DesignRect;
ebesSize: CD.Position;
ebesActiveToScribeCenter: EBESPixels;
maskNames: LIST OF Rope.ROPE;
scribeLines: LIST OF REF ANY -- Scribe --;
maskDirectives: LIST OF REF ANY -- MaskDirective --;
maskCount: INT;
maxBloat: EBESPixels ← 0; -- >= 0
TRUSTED {Process.SetPriority[Process.priorityBackground]};
TerminalIO.WriteRope[IO.PutFR["\nEBES Mask Generation\nPreliminary analysis begun at %g.\n", IO.time[]]];
nmPerLambda ← NumericProp[$CDxEBESNmPerLambda];
nmPerEBESPixel ← NumericProp[$CDxEBESNmPerEBESPixel];
ms.scale ← ReduceRational[[num: nmPerLambda, denom: CD.lambda*nmPerEBESPixel]];
ebesSize.x ← NumericProp[$CDxEBESXNmPerDie]/nmPerEBESPixel;
ebesSize.y ← NumericProp[$CDxEBESYNmPerDie]/nmPerEBESPixel;
scribeLines ← NARROW[Prop[$CDxEBESScribeLines]];
ebesActiveToScribeCenter ← NumericProp[$CDxEBESActiveToScribeCenter]/nmPerEBESPixel;
maskDirectives ← CollectDirectives[design, nmPerEBESPixel];
[maskNames: maskNames, maskCount: maskCount, maxBloat: maxBloat] ← FigureOutMasks[maskDirectives];
dr.minimalSize ← 0;
dr.drawRect ← dr.saveRect ← NoteLevel;
dr.devicePrivate ← dd;
dr.worldClip ← CDBasics.universe;
dr.stopFlag ← abortFlag;
CDOps.DrawDesign[design, dr]; -- measure design
designClip ← dd.bbox^;
BEGIN ENABLE -- for ERRORs
BEGIN
UNWIND => ms.s ← AbortFile[ms.s];
END;
ComputeReticleClip: PROC RETURNS [c: CD.Rect] =
BEGIN
AdjustReticleSize: PROC [coord: Rope.ROPE, z1, z2, zSize: CD.Number] RETURNS [newZ1, newZ2: CD.Number] =
BEGIN
IF z2-z1 > zSize THEN
{TerminalIO.WriteRope[IO.PutFR[" .. design %g size of %d EBES units is larger than allowed die size of %d EBES units ..", IO.rope[coord], IO.int[c.x2-c.x1+1], IO.int[zSize]]];
ERROR TerminalIO.UserAbort};
newZ1 ← (z1+z2-zSize)/2;
newZ2 ← newZ1+zSize;
END;
c ← CDBasics.Extend[Bloat[ScaleCDToEBES[ms, designClip], maxBloat], ebesActiveToScribeCenter];
[c.x1, c.x2] ← AdjustReticleSize["x", c.x1, c.x2, ebesSize.x];
[c.y1, c.y2] ← AdjustReticleSize["y", c.y1, c.y2, ebesSize.y];
END;
today: Date = Today[];
reticleClip: CD.Rect ← ms.reticleClip ← ComputeReticleClip[];
nRows: NAT = Ceiling[[num: reticleClip.y2-reticleClip.y1, denom: maskHeight]];
nCols: NAT = Ceiling[[num: reticleClip.x2-reticleClip.x1, denom: maskWidth]];
TerminalIO.WriteRope[IO.PutFR["Preliminary analysis finished at %g. Making %d masks at %d (x) x %d (y) EBES pixels.\n", IO.time[], IO.int[maskCount], IO.int[reticleClip.x2-reticleClip.x1], IO.int[reticleClip.y2-reticleClip.y1]]];
WriteTapeDirectory[streamGen, ms, nRows, nCols, maskNames, today];
dr.drawRect ← dr.saveRect ← NoteRectangle;
dr.devicePrivate ← ms;
FOR m: LIST OF Rope.ROPE ← maskNames, m.rest WHILE m#NIL DO
maskName: Rope.ROPE = m.first;
ms.hasNegativeBloat ← FALSE;
FOR levels: LIST OF CD.Level ← design.technology.usedLevels, levels.rest WHILE levels # NIL DO
ms.level[levels.first] ← NIL;
ENDLOOP;
FOR directives: LIST OF REF ANY ← maskDirectives, directives.rest WHILE directives#NIL DO
directive: MaskDirective = NARROW[directives.first];
IF Rope.Equal[directive.dest.name, maskName] THEN
BEGIN
ms.level[directive.cdSource] ← directive;
IF directive.dest.deltaDiameter < 0 THEN ms.hasNegativeBloat ← TRUE;
END;
ENDLOOP;
FOR scribes: LIST OF REF ANY ← scribeLines, scribes.rest WHILE scribes#NIL DO
ms.scribe ← NARROW[scribes.first];
IF Rope.Equal[ms.scribe.maskName, maskName] THEN EXIT;
REPEAT
FINISHED => ms.scribe ← NIL;
ENDLOOP;
FOR col: NAT IN [0..nCols) DO
FOR row: NAT IN [0..nRows) DO
ms.patternFileName ← PatternFileName[maskSetName: ms.maskSetName,
maskName: maskName, row: row, col: col];
ms.s ← EBESOpen[streamGen[ms]];
TerminalIO.WriteRope[IO.PutFR["\nStarting mask %g at %g ", IO.rope[ms.patternFileName], IO.time[]]];
SendCommand[s: ms.s, comm: NEW[StartDrawing ← [
patternFileName: RopeToFileName[ms.patternFileName],
cx: MIN[maskWidth, reticleClip.x2-reticleClip.x1-col*maskWidth],
cy: MIN[maskHeight, reticleClip.y2-reticleClip.y1-row*maskHeight],
moDayYr: today,
addressCode: (SELECT nmPerEBESPixel FROM
1000 => Nm1000,
500 => Nm500,
250 => Nm250,
ENDCASE => Nm500)]]];
FOR stripeBase: INT -- [0..maxStripes) -- 𡤀, stripeBase+stripesPerClump WHILE stripeBase < MIN[maxStripes, Ceiling[[num: reticleClip.y2-reticleClip.y1-maskHeight*row, denom: stripeHeight]]] DO
designStripeClip: CD.Rect;
IF abortFlag^ THEN GOTO AbortMask;
ms.areas.ChangeRect[rect: CDBasics.universe, newValue: NIL];
designStripeClip ← ScaleEBESToCD[ms, [
x1: ms.reticleClip.x1+col*maskWidth-1-maxBloat,
y1: ms.reticleClip.y1+row*maskHeight+stripeBase*stripeHeight-1-maxBloat,
x2: ms.reticleClip.x1+(col+1)*maskWidth+1+maxBloat,
y2: ms.reticleClip.y1+row*maskHeight+(stripeBase+stripesPerClump)*stripeHeight+1+maxBloat
]];
CDViewer.ShowArrow[design: design, pos: CDBasics.Center[designStripeClip]];
.. keep user happy
dr.worldClip ← CDBasics.Intersection[designClip, designStripeClip];
CDOps.DrawDesign[design, dr]; -- build tesselation of the relevant design rectangle
IF ms.hasNegativeBloat THEN ShrinkTiles[ms];
IF ms.scribe # NIL THEN -- insert scribe lines
BEGIN
r: CD.Rect = ms.reticleClip;
outer: EBESPixels = MAX[ms.scribe.dieEdgeToLineCenter-ms.scribe.lineWidth/2, 0]/nmPerEBESPixel;
inner: EBESPixels = (ms.scribe.dieEdgeToLineCenter+ms.scribe.lineWidth/2)/nmPerEBESPixel;
ms.areas.ChangeRect[[x1: r.x1+outer, y1: r.y1+outer, x2: r.x1+inner, y2: r.y2-outer], $scribe];
ms.areas.ChangeRect[[x1: r.x2-inner, y1: r.y1+outer, x2: r.x2-outer, y2: r.y2-outer], $scribe];
ms.areas.ChangeRect[[x1: r.x1+outer, y1: r.y1+outer, x2: r.x2-outer, y2: r.y1+inner], $scribe];
ms.areas.ChangeRect[[x1: r.x1+outer, y1: r.y2-inner, x2: r.x2-outer, y2: r.y2-outer], $scribe];
END;
FOR stripe: INT -- [0..maxStripes) -- IN [stripeBase..MIN[stripeBase+stripesPerClump, maxStripes, Ceiling[[num: reticleClip.y2-reticleClip.y1-maskHeight*row, denom: stripeHeight]]]) DO
ms.stripeClip ← CDBasics.Intersection[ms.reticleClip, [
x1: ms.reticleClip.x1+col*maskWidth,
y1: ms.reticleClip.y1+row*maskHeight+stripe*stripeHeight,
x2: ms.reticleClip.x1+(col+1)*maskWidth,
y2: ms.reticleClip.y1+row*maskHeight+(stripe+1)*stripeHeight
]];
ms.stripeRectCount ← 0;
SendCommand[s: ms.s, comm: NEW[StartStripe ← [stripeNumber: stripe+1]]];
[] ← ms.areas.EnumerateArea[rect: ms.stripeClip,
perTile: OutputTile, data: ms];
SendCommand[s: ms.s, comm: NEW[BasicCommand ← endStripe]];
TerminalIO.WriteRope[IO.PutFR[".%d", IO.int[ms.stripeRectCount]]]; -- end of stripe
ENDLOOP;
ENDLOOP;
SendCommand[s: ms.s, comm: NEW[BasicCommand ← endDrawing]];
ms.s.Close[];
ms.s ← NIL;
ENDLOOP;
ENDLOOP;
ENDLOOP;
TerminalIO.WriteRope[IO.PutFR["\nEBES mask generation finished at %g\n", IO.time[]]];
EXITS
AbortMask => {ms.s ← AbortFile[ms.s]; TerminalIO.WriteRope["aborted\n"]};
END;
END;
NoteLevel: PROC [ r: CD.DesignRect, l: CD.Level, pr: CD.DrawRef ] =
BEGIN
dd: DesignDataRef = NARROW[pr.devicePrivate];
IF dd.bbox=NIL THEN dd.bbox ← NEW[CD.DesignRect ← r];
dd.bbox^ ← CDBasics.Surround[dd.bbox^, r];
END;
NoteRectangle: PROC [ r: CD.DesignRect, l: CD.Level, pr: CD.DrawRef ] =
BEGIN
ms: MaskStateRef = NARROW[pr.devicePrivate];
IF ms.level[l]#NIL AND CDBasics.NonEmpty[r] THEN
BEGIN
er: CD.Rect ← ScaleCDToEBES[ms: ms, cdr: r];
IF ms.level[l].dest.deltaDiameter >= 0 THEN
BEGIN
er ← Bloat[er, ms.level[l].dest.deltaDiameter];
ms.areas.ChangeRect[rect: er, newValue: ms.level[l]];
END
ELSE
ms.areas.FuncChangeRect[rect: er, perTile: PreferMorePositiveBloat,
data: ms.level[l]];
END;
END;
PreferMorePositiveBloat: PROC [plane: REF CornerStitching.Tesselation, rect: CornerStitching.Rect, oldValue: REF ANY, data: REF ANY] -- CornerStitching.PerTileChangeProc -- =
BEGIN
IF oldValue=NIL THEN plane.ChangeRect[rect, data]
ELSE
BEGIN
exist: MaskDirective = NARROW[oldValue];
new: MaskDirective = NARROW[data];
IF new.dest.deltaDiameter > exist.dest.deltaDiameter THEN plane.ChangeRect[rect, data];
END;
END;
ScaleCDToEBES: PROC [ ms: MaskStateRef, cdr: CD.DesignRect ] RETURNS [ ebr: CD.Rect ] =
BEGIN
ebr ← CDBasics.NormalizeRect[
[x1: RatMul[ms.scale, cdr.x1],
y1: RatMul[ms.scale, cdr.y1],
x2: RatMul[ms.scale, cdr.x2],
y2: RatMul[ms.scale, cdr.y2]]];
END;
ScaleEBESToCD: PROC [ ms: MaskStateRef, ebr: CD.Rect ] RETURNS [ cdr: CD.DesignRect ] =
BEGIN
cdr ← CDBasics.NormalizeRect[
[x1: RatDiv[ms.scale, ebr.x1],
y1: RatDiv[ms.scale, ebr.y1],
x2: RatDiv[ms.scale, ebr.x2],
y2: RatDiv[ms.scale, ebr.y2]]];
END;
ShrinkTiles: PROC [ ms: MaskStateRef ] =
BEGIN
t: REF CornerStitching.Tesselation = ms.areas; -- swap ms.areas and ms.nbAreas
ms.areas ← ms.nbAreas;
ms.nbAreas ← t;
ms.areas.ChangeRect[rect: CDBasics.universe, newValue: NIL];
[] ← ms.nbAreas.EnumerateArea[rect: CDBasics.universe, perTile: ShrinkIfNecessary, data: ms];
ms.nbAreas.ChangeRect[rect: CDBasics.universe, newValue: NIL];
END;
examineEverything: REF INTNEW[INT ← 0]; -- a REF that doesn't match anything
ShrinkIfNecessary: PROC [tile: CornerStitching.TilePtr, data: REF ANY] -- CornerStitching.PerTileProc -- =
BEGIN
IF tile.Value#NIL THEN
BEGIN
ms: MaskStateRef = NARROW[data];
r: CornerStitching.Rect = tile.Area;
v: REF = tile.Value;
ms.areas.ChangeRect[r, v]; -- start by filling in the rectangle
WITH v SELECT FROM
m: MaskDirective =>
BEGIN
IF m.dest.deltaDiameter<0 THEN
BEGIN
ShrinkFromEmptyNeighbor: PROC [tile: CornerStitching.TilePtr, data: REF ANY] -- CornerStitching.PerTileProc -- =
BEGIN
IF tile.Value = NIL THEN
ms.areas.ChangeRect[CDBasics.Intersection[r, Bloat[tile.Area, -m.dest.deltaDiameter]], $shrunken];
END;
examine neighbors on all sides
[] ← ms.nbAreas.EnumerateArea[rect: [x1: r.x1-1, y1: r.y1, x2: r.x1, y2: r.y2], perTile: ShrinkFromEmptyNeighbor, backgroundValue: examineEverything];
[] ← ms.nbAreas.EnumerateArea[rect: [x1: r.x1, y1: r.y1-1, x2: r.x2, y2: r.y1], perTile: ShrinkFromEmptyNeighbor, backgroundValue: examineEverything];
[] ← ms.nbAreas.EnumerateArea[rect: [x1: r.x2, y1: r.y1, x2: r.x2+1, y2: r.y2], perTile: ShrinkFromEmptyNeighbor, backgroundValue: examineEverything];
[] ← ms.nbAreas.EnumerateArea[rect: [x1: r.x1, y1: r.y2, x2: r.x2, y2: r.y2+1], perTile: ShrinkFromEmptyNeighbor, backgroundValue: examineEverything];
END;
END;
ENDCASE => ERROR;
END;
END;
OutputTile: PROCEDURE [tile: CornerStitching.TilePtr, data: REF ANY]
-- CornerStitching.PerTileProc -- =
BEGIN -- only called on tiles with non-NIL values
DoOutput: PROC =
BEGIN
ms: MaskStateRef = NARROW[data];
cr: CD.Rect = CDBasics.Intersection[tile.Area, ms.stripeClip];
IF CDBasics.NonEmpty[cr] THEN
BEGIN
ms.drawCommand^ ← [x: cr.x1-ms.stripeClip.x1, y: cr.y1-ms.stripeClip.y1, w: cr.x2-cr.x1, h: cr.y2-cr.y1];
SendCommand[s: ms.s, comm: ms.drawCommand];
ms.stripeRectCount ← ms.stripeRectCount+1;
END;
END;
WITH tile.Value SELECT FROM
d: MaskDirective => DoOutput[];
a: ATOM =>
SELECT a FROM
$scribe => DoOutput[];
$shrunken => NULL;
ENDCASE => ERROR;
ENDCASE => ERROR;
END;
Today: PROC RETURNS [ d: Date ] =
TRUSTED BEGIN
time: BasicTime.Unpacked = BasicTime.Unpack[BasicTime.Now[]];
moDayYr: Rope.ROPE = IO.PutFR[format: "%02d%02d%02d",
v1: IO.card[LOOPHOLE[time.month, CARDINAL]+1], v2: IO.int[time.day], v3: IO.int[time.year MOD 100]];
FOR i: [0..6) IN [0..6) DO d[i] ← Rope.Fetch[moDayYr, i] ENDLOOP;
END;
PatternFileName: PROC [ maskSetName, maskName: Rope.ROPE, row, col: NAT ] RETURNS [ rfn: Rope.ROPE ] =
BEGIN
r: Rope.ROPE = IO.PutFR[format: "%s%s.%g%g",
v1: IO.rope[Rope.Substr[base: Rope.Cat[maskSetName, "xxxxxxx"], len: 7]],
v2: IO.rope[Rope.Substr[base: Rope.Cat[maskName, "xx"], len: 2]],
v3: IO.char[LOOPHOLE['A+col]], v4: IO.char[LOOPHOLE['A+row]]];
rfn ← Rope.Translate[base: r, translator: ];
END;
ToUpperCase: PROC [old: CHAR] RETURNS [new: CHAR] -- Rope.TranslatorType -- =
{new ← Ascii.Upper[old]};
RopeToFileName: PROC [ rfn: Rope.ROPE ] RETURNS [ fn: FileName ] =
{FOR i: [0..12) IN [0..12) DO fn[i] ← Rope.Fetch[rfn, i] ENDLOOP};
SortNoDuplicates: PROC [ in: LIST OF Rope.ROPE ] RETURNS [ out: LIST OF Rope.ROPE ] =
BEGIN
q: PriorityQueue.Ref = PriorityQueue.Create[pred: CompareRopes];
last: Rope.ROPENIL;
FOR m: LIST OF Rope.ROPE ← in, m.rest WHILE m#NIL DO
q.Insert[m.first];
ENDLOOP;
out ← NIL;
WHILE NOT q.Empty[] DO
r: Rope.ROPE = NARROW[q.Remove[], Rope.ROPE];
IF last = NIL OR NOT Rope.Compare[s1: r, s2: last, case: FALSE] = equal THEN
out ← CONS[r, out];
last ← r;
ENDLOOP;
END;
CompareRopes: PROC [ x, y: PriorityQueue.Item, data: REFNIL ] RETURNS [ BOOL ] -- PriorityQueue.SortPred -- =
BEGIN
rx: Rope.ROPE = NARROW[x];
ry: Rope.ROPE = NARROW[y];
RETURN[Rope.Compare[s1: rx, s2: ry, case: FALSE] = greater];
END;
WriteTapeDirectory: PROC [ streamGen: PROC [ms: MaskStateRef] RETURNS [dest: IO.STREAM], ms: MaskStateRef, nRows, nCols: NAT, masks: LIST OF Rope.ROPE, today: Date] =
BEGIN
s: IO.STREAM;
nMasks: NAT ← 0;
FOR m: LIST OF Rope.ROPE ← masks, m.rest WHILE m#NIL DO
nMasks ← nMasks+1;
ENDLOOP;
ms.patternFileName ← IO.PutFR["%g.arch", IO.rope[ms.maskSetName]];
s ← EBESOpen[dest: streamGen[ms], eor: FALSE];
SendCommand[s, NEW[CARDINAL ← 0]]; -- marked "reserved" in Varian Figure A-1
SendCommand[s, NEW[CARDINAL ← nRows*nCols*nMasks]]; -- count of files
FOR m: LIST OF Rope.ROPE ← masks, m.rest WHILE m#NIL DO
FOR col: NAT IN [0..nCols) DO
FOR row: NAT IN [0..nRows) DO
s.PutF[format: "%s ", v1: IO.rope[PatternFileName[ms.maskSetName, m.first, row, col]]];
ENDLOOP;
ENDLOOP;
ENDLOOP;
SendCommand[s, NEW[Date ← today]];
s.Close[];
END;
AbortFile: PROC [s: IO.STREAM ] RETURNS [ IO.STREAM ] =
{IF s#NIL THEN s.Close[abort: TRUE]; RETURN[NIL]};
SendCommand: PROC [ s: IO.STREAM, comm: REF ] =
BEGIN
sizeInWords: NAT;
WITH comm SELECT FROM
c: REF DrawRectangle => sizeInWords ← SIZE[DrawRectangle];
c: REF BasicCommand => sizeInWords ← SIZE[BasicCommand];
c: REF StartStripe => sizeInWords ← SIZE[StartStripe];
c: REF StartDrawing => sizeInWords ← SIZE[StartDrawing];
c: REF Date => sizeInWords ← SIZE[Date];
c: REF FileName => sizeInWords ← SIZE[FileName];
c: REF CARDINAL => sizeInWords ← SIZE[CARDINAL];
ENDCASE => ERROR;
TRUSTED {s.UnsafePutBlock[[base: LOOPHOLE[comm], startIndex: 0, count: 2*sizeInWords]]};
END;
EBESStreamStateRef: TYPE = REF EBESStreamState;
EBESStreamState: TYPE = RECORD [
dest: IO.STREAM,
eor: BOOL,
buf: REF TEXT];
ebesStreamProcs: REF IO.StreamProcs = IO.CreateStreamProcs[
variety: output,
class: $EBESOutputStream,
putChar: PutEBESChar,
unsafePutBlock: PutEBESBlock,
flush: FlushEBES,
close: CloseEBES
];
EBESOpen: PROC [ dest: IO.STREAM, eor: BOOLTRUE ] RETURNS [ self: IO.STREAM ] =
BEGIN
state: EBESStreamStateRef = NEW[EBESStreamState ←
[dest: dest, eor: eor, buf: NEW[TEXT[2048]]]];
state.buf.length ← 0;
self ← IO.CreateStream[
streamProcs: ebesStreamProcs,
streamData: state
];
END;
PutEBESChar: PROC [ self: IO.STREAM, char: CHAR ] =
TRUSTED BEGIN
state: EBESStreamStateRef = NARROW[self.streamData];
FlushEBESIfNecessary[state, 1];
state.buf[state.buf.length] ← char;
state.buf.length ← state.buf.length+1;
END;
PutEBESBlock: PROC [ self: IO.STREAM, block: IO.UnsafeBlock ] =
TRUSTED BEGIN
state: EBESStreamStateRef = NARROW[self.streamData];
FlushEBESIfNecessary[state, block.count];
FOR i: INT IN [0..block.count) DO
CharArrayPtr: TYPE = LONG POINTER TO PACKED ARRAY [0..0) OF CHAR;
state.buf[state.buf.length+i] ← LOOPHOLE[block.base, CharArrayPtr][block.startIndex+i];
ENDLOOP;
state.buf.length ← state.buf.length+block.count;
END;
FlushEBESIfNecessary: PROC [state: EBESStreamStateRef, newBytes: NAT] = INLINE
BEGIN
IF state.buf.maxLength<state.buf.length+newBytes+
(IF state.eor THEN 2*SIZE[BasicCommand] -- for end of record -- ELSE 0) THEN
DoFlushEBES[state];
END;
FlushEBES: PROC [ self: IO.STREAM ] = {DoFlushEBES[NARROW[self.streamData]]};
DoFlushEBES: PROC [ state: EBESStreamStateRef ] =
BEGIN
IF state.buf.length>0 THEN
BEGIN
IF state.eor THEN
BEGIN
IF state.buf.length+2<=state.buf.maxLength THEN
TRUSTED BEGIN -- append an end-of-record command to the buffer
endRec: PACKED ARRAY [0..2) OF CHAR = LOOPHOLE[endRecord];
state.buf[state.buf.length] ← endRec[0];
state.buf[state.buf.length+1] ← endRec[1];
state.buf.length ← state.buf.length+2;
END
ELSE ERROR; -- can't terminate buffer properly
END;
FOR i: CARDINAL IN [state.buf.length..state.buf.maxLength) DO
state.buf[i] ← 000C; -- pad out with 0's
ENDLOOP;
state.buf.length ← state.buf.maxLength;
state.dest.PutBlock[state.buf];
END;
state.buf.length ← 0;
END;
CloseEBES: PROC [ self: IO.STREAM, abort: BOOLFALSE ] =
BEGIN
state: EBESStreamStateRef = NARROW[self.streamData];
self.Flush[];
state.dest.Close[];
END;
CollectDirectives: PROC [design: CD.Design, nmPerEBESPixel: INT] RETURNS [maskDirectives: LIST OF REF ANY -- MaskDirective -- ] =
BEGIN
GetDirectives: PROC [p: REF, lev: CD.Level] RETURNS [l: LIST OF REF ANYNIL] =
BEGIN
IF p#NIL THEN
BEGIN
WITH p SELECT FROM
d: MaskDest =>
l ← LIST[NEW[MaskDirectiveRec ← [lev, NEW[MaskDestRec ← [
name: d.name,
complement: d.complement,
deltaDiameter: d.deltaDiameter/nmPerEBESPixel
]]]]];
list: LIST OF REF ANY =>
l ← List.Append[GetDirectives[list.first, lev], GetDirectives[list.rest, lev]];
ENDCASE =>
l ← LIST[NEW[MaskDirectiveRec ← [lev, NEW[MaskDestRec ← [name: ToRope[p]]]]]];
END;
END;
maskDirectives ← NIL;
FOR levels: LIST OF CD.Level ← design.technology.usedLevels, levels.rest WHILE levels # NIL DO
maskDirectives ← List.Append[GetDirectives[CDProperties.GetPropFromLevel[levels.first, $CDxEBESName], levels.first ], maskDirectives];
ENDLOOP;
END;
FigureOutMasks: PROC [maskDirectives: LIST OF REF ANY -- MaskDirective --] RETURNS [maskNames: LIST OF Rope.ROPE, maskCount: INT, maxBloat: EBESPixels] =
BEGIN
maskNames ← NIL;
maxBloat ← 0;
FOR ml: LIST OF REF ANY ← maskDirectives, ml.rest WHILE ml#NIL DO
md: MaskDirective = NARROW[ml.first];
maskNames ← CONS[md.dest.name, maskNames];
maxBloat ← MAX[maxBloat, ABS[md.dest.deltaDiameter]];
ENDLOOP;
maskNames ← SortNoDuplicates[maskNames];
maskCount ← 0;
FOR m: LIST OF Rope.ROPE ← maskNames, m.rest WHILE m#NIL DO
maskCount ← maskCount+1;
ENDLOOP;
END;
RopeNeeded: SIGNAL [ ref: REF REF ] = CODE;
ToRope: PROC [ ref: REF ] RETURNS [ rope: Rope.ROPE ] =
BEGIN
IF ref = NIL THEN rope ← NIL
ELSE WITH ref SELECT FROM
r: Rope.ROPE => rope ← r;
rt: REF TEXT => rope ← Rope.FromRefText[rt];
a: ATOM => rope ← Atom.GetPName[a];
ri: REF INT => rope ← IO.PutFR[format: "%d", v1: IO.int[ri^]];
ENDCASE =>
BEGIN
refRef: REF REF = NEW[REF ← ref];
SIGNAL RopeNeeded[refRef];
rope ← ToRope[refRef^ ! RopeNeeded => GOTO NoHelp];
EXITS
NoHelp => ERROR;
END;
END;
Bloat: PROC [ r: CD.Rect, deltaDiameter: EBESPixels ] RETURNS [ br: CD.Rect ] =
BEGIN
b0: NAT = deltaDiameter/2; -- split in "half" for radius
b1: NAT = deltaDiameter-b0;
Be careful not to exceed the limits of a CD.Number, even temporarily
br ← [x1: MAX[FIRST[CD.Number]+b0, r.x1]-b0,
y1: MAX[FIRST[CD.Number]+b0, r.y1]-b0,
x2: MIN[LAST[CD.Number]-b1, r.x2]+b1,
y2: MIN[LAST[CD.Number]-b1, r.y2]+b1];
END;
Ceiling: PROC [ r: Rational ] RETURNS [ c: INT ] =
BEGIN
c ← r.num/r.denom;
c = SGN[r.num]*SGN[r.denom]*FLOOR[ABS[r.num]/ABS[r.denom]] if r.denom#0
IF r.num*r.denom>0 AND r.num MOD r.denom # 0 THEN c ← c+1;
END;
ReduceRational: PROC [ r: Rational ] RETURNS [ Rational ] =
BEGIN
gcd: INT = IF r.num=0 THEN r.denom ELSE GCD[r.num, r.denom];
RETURN[[num: r.num/gcd, denom: r.denom/gcd]];
END;
RatMul: PROC [ mul: Rational, z: INT ] RETURNS [ INT ] =
INLINE {RETURN[(mul.num*z)/mul.denom]};
RatDiv: PROC [ div: Rational, z: INT ] RETURNS [ INT ] =
INLINE {RETURN[(div.denom*z)/div.num]};
GCD: PROC [ m, n: INT ] RETURNS [ INT ] =
BEGIN
r: INT;
SELECT m FROM
<0 => m ← -m;
ENDCASE => NULL;
SELECT n FROM
<0 => n ← -n;
>0 => NULL;
ENDCASE => RETURN[m];
r ← m MOD n;
WHILE r>0 DO m ← n; n ← r; r ← m MOD n; ENDLOOP;
RETURN[n];
END;
Module START code...
Init[];
END. -- of EBESMaskImpl