EBESMaskImpl:
CEDAR
MONITOR
IMPORTS Ascii, Atom, CD, CDViewer, CDInline, CDOps, CDSequencer, CDProperties, CornerStitching, FS, IO, PriorityQueue, Process, Rope, --TapeStream,-- TerminalIO, BasicTime
EXPORTS =
BEGIN
Rational: TYPE = RECORD [num, denom: INT ← 1];
MaskStateRef: TYPE = REF MaskState;
MaskState:
TYPE =
RECORD [
s: IO.STREAM, -- EBES stream where rectangles are to be drawn
scale: Rational,
offset: CD.Position ← [0,0], -- in EBES co-ordinates
drawCommand: REF DrawRectangle,
level: CD.Level ← CD.combined,
bloat: CD.Number ← 0, -- of this level in EBES pixels, half per side
areas, unbloated: REF CornerStitching.Tesselation ← NIL, -- in offset EBES space
stripeClip: CD.Rect ← [0,0,0,0] -- in offset EBES space
];
DesignDataRef: TYPE = REF DesignData;
DesignData:
TYPE =
RECORD [
bbox: REF CD.DesignRect,
used: REF PACKED ARRAY CD.Level OF BOOL
];
MaskDescriptionRef: TYPE = REF MaskDescription;
MaskDescription: TYPE = RECORD [ lev: CD.Level, name: Rope.ROPE ];
The 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 CHAR ← ALL[' ]
];
AddressCode: TYPE = MACHINE DEPENDENT {Nm1000(0), Nm500(3), Nm250(6)};
Date:
TYPE =
PACKED
ARRAY [0..6)
OF
CHAR ←
ALL[' ];
month [01..12], day [01..31], year [00..99]
FileName:
TYPE =
PACKED
ARRAY [0..12)
OF
CHAR ←
ALL[' ];
[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];
Init:
ENTRY
PROC ~ {
CDSequencer.ImplementCommand[a~$EBESMask , p~ForkEBESMask];
TerminalIO.WriteRope["ChipNDale EBES mask generator loaded\n"];
};
ForkEBESMask:
PROC [ c: CDSequencer.Command ] =
TRUSTED BEGIN
TerminalIO.WriteRope[" not yet debugged\n"];
TerminalIO.WriteRope["Releases lock of design, but design MUST NOT be changed\n"];
Process.Detach[FORK EBESMask[c]]
END;
EBESMask:
ENTRY
PROC [ c: CDSequencer.Command ] =
BEGIN
ENABLE
BEGIN
ABORTED => {TerminalIO.WriteRope[" mask generation aborted\n"]; GOTO Exit};
RopeNeeded =>
BEGIN
explanation: Rope.ROPE = IO.PutFR[format: "Please enter a string corresponding to the value %g: ", v1: IO.refAny[ref^]];
ref^ ← TerminalIO.RequestRope[explanation];
RESUME;
END;
UNWIND => NULL;
END;
fileNoRef: REF INT = NEW[INT ← 0];
SELECT TerminalIO.RequestSelection[label: "Destination",
choice: LIST[" Local files", " Tape 0", " Tape 1"]
] FROM
1 => MakeMask[c: c, StreamGen: SimpleStreamGen, data: fileNoRef,
tapeDirectory: FS.Open[fileName: "Directory.ebes", accessOptions: overwrite]];
2 => TerminalIO.WriteRope["not impl\n"]; --MakeTapeMasks[c: c, drive: 0];
3 => TerminalIO.WriteRope["not impl\n"]; --MakeTapeMasks[c: c, drive: 1];
ENDCASE => RETURN;
EXITS Exit => NULL;
END;
SimpleStreamGen:
PROC [ name: Rope.
ROPE, data:
REF ]
RETURNS [ dest:
IO.
STREAM ] =
BEGIN
fileNoRef: REF INT = NARROW[data];
dest ← FS.Open[fileName: IO.PutFR[format: "%g%d.EBES", v1: IO.rope[name],
v2: IO.int[fileNoRef^]], accessOptions: overwrite];
fileNoRef^ ← fileNoRef^+1;
END;
unloadAfterward: BOOL ← TRUE;
MakeTapeMasks: INTERNAL PROC [c: CDSequencer.Command, drive: [0..4]] =
BEGIN
tape: IO.STREAM ← NIL;
BEGIN ENABLE UNWIND => IF tape#NIL THEN {tape.Close[]; tape ← NIL};
tape ← TapeStream.Create[drive: drive, density: NRZI800];
TapeStream.Rewind[tape];
MakeMask[c: c, StreamGen: TapeSubStreamGen, data: tape,
tapeDirectory: TapeSubStreamGen[name: "", data: tape]];
TapeStream.Rewind[tape];
IF unloadAfterward THEN TapeStream.Unload[tape] ELSE tape.Close[];
tape ← NIL;
END;
END;
TapeSubStreamGen: PROC [ name: Rope.ROPE, data: REF ] RETURNS [ dest: IO.STREAM ] =
BEGIN
dest ← IO.CreateProcsStream[streamProcs: IO.CreateRefStreamProcs[
putBlock: PutTapeSubStreamBlock,
close: CloseTapeSubStream], streamData: data];
END;
abortMask: BOOL ← FALSE;
MakeMask:
INTERNAL
PROC [ c: CDSequencer.Command,
StreamGen:
PROC [ name: Rope.
ROPE, data:
REF ]
RETURNS [ dest:
IO.
STREAM ], data:
REF, maskSetName: Rope.
ROPE ←
NIL, tapeDirectory:
IO.
STREAM ←
NIL ] =
BEGIN
nmPerLambda, nmPerEBESPixel: INT;
ms: MaskStateRef = NEW[ MaskState ← [
areas: CornerStitching.NewTesselation[],
unbloated: CornerStitching.NewTesselation[],
drawCommand: NEW[DrawRectangle],
offset: [0, 0]]];
design: CD.Design = c.design;
dr: CD.DrawRef = CD.NewNullDeviceDrawRef[design];
dd: DesignDataRef = NEW[DesignData ←
[bbox: NIL, used: NEW[PACKED ARRAY CD.Level OF BOOL ← ALL[FALSE]]]];
designClip: CD.DesignRect;
SELECT TerminalIO.RequestSelection[label: "Microns per lambda",
choice: LIST[" 2.0", " 1.5", " 1.0"]
] FROM
1 => nmPerLambda ← 2000;
2 => nmPerLambda ← 1500;
3 => nmPerLambda ← 1000;
ENDCASE => nmPerLambda ←
TerminalIO.RequestInt["OK, wise guy, how many nanometers per lambda? "];
SELECT TerminalIO.RequestSelection[label: "Microns per EBES pixel",
choice: LIST[" 1.0", " 0.5", " 0.25"]
] FROM
1 => nmPerEBESPixel ← 1000;
2 => nmPerEBESPixel ← 500;
3 => nmPerEBESPixel ← 250;
ENDCASE => RETURN; -- nothing else is legal
abortMask ← FALSE;
TRUSTED {Process.SetPriority[Process.priorityBackground]};
TerminalIO.WriteRope["Analyzing design "];
dr.minimalSize ← 0;
dr.drawRect ← dr.saveRect ← NoteLevel;
dr.devicePrivate ← dd;
dr.worldClip ← CDInline.ToRect[c.pos, c.sPos];
CDOps.DrawDesign[design, dr]; -- mark used levels and measure design
designClip ← CDInline.Intersection[CDInline.ToRect[c.pos, c.sPos], dd.bbox^];
BEGIN
ENABLE
-- for ERRORs
BEGIN
UNWIND => ms.s ← AbortFile[ms.s];
END;
today: Date = Today[];
scale: Rational = ReduceRational[[num: nmPerLambda, denom: CD.lambda*nmPerEBESPixel]];
ebesClip: CD.Rect;
nRows, nCols: NAT;
maskCount: NAT ← 0;
masks: LIST OF MaskDescriptionRef ← NIL;
maxBloat: CD.Number ← 0; -- EBES pixels
FOR lev:
CD.Level
IN
CD.Level
DO
levKey: ATOM = CD.LevelKey[lev];
IF dd.used[lev]
THEN
BEGIN
IF CDProperties.GetPropFromLevel[from: lev, prop: $EBESMakeMask]#
NIL
THEN
dd.used[lev] ← NARROW[CDProperties.GetPropFromLevel[from: lev, prop: $EBESMakeMask], REF BOOL]^;
END;
IF dd.used[lev]
THEN
BEGIN
maskCount ← maskCount+1;
masks ← CONS[first: NEW[MaskDescription ← [lev: lev, name: MaskName[lev]]],
rest: masks];
maxBloat ←
MAX[maxBloat, (
IF CDProperties.GetPropFromLevel[from: lev, prop: $EBESBloatActive]#
NIL
THEN
NARROW[CDProperties.GetPropFromLevel[from: lev, prop: $EBESBloatActive], REF INT]^
ELSE 0)/nmPerEBESPixel];
END;
ENDLOOP;
IF maskSetName =
NIL
THEN
maskSetName ← ToRope[CDProperties.GetPropFromDesign[from: design, prop: $EBESMaskSetName]];
IF maskSetName =
NIL
THEN
maskSetName ← ToRope[Atom.GetProp[atom: $EBESMask, prop: $MaskSetName]];
IF maskSetName = NIL THEN maskSetName ← design.name;
ms.scale ← ReduceRational[[num: nmPerLambda, denom: CD.lambda*nmPerEBESPixel]];
ebesClip ← Bloat[ScaleCDToEBES[ms, designClip], maxBloat];
nRows ← Ceiling[[num: ebesClip.y2-ebesClip.y1, denom: maskHeight]];
nCols ← Ceiling[[num: ebesClip.x2-ebesClip.x1, denom: maskWidth]];
TerminalIO.WriteRope[IO.PutFR[format: "..making %d masks at %d (x) x %d (y) EBES pixels..", v1: IO.int[maskCount], v2: IO.int[ebesClip.x2-ebesClip.x1], v3: IO.int[ebesClip.y2-ebesClip.y1]]];
masks ← SortMasks[masks];
IF tapeDirectory#
NIL
THEN
WriteTapeDirectory[tapeDirectory, maskSetName, nRows, nCols, masks, today];
dr.drawRect ← dr.saveRect ← NoteRectangle;
dr.devicePrivate ← ms;
FOR m:
LIST
OF MaskDescriptionRef ← masks, m.rest
WHILE m#
NIL
DO
mask: MaskDescription = m.first^;
ms.level ← mask.lev;
ms.bloat ← (
IF CDProperties.GetPropFromLevel[from: mask.lev, prop: $EBESBloatActive]#
NIL
THEN
NARROW[CDProperties.GetPropFromLevel[from: mask.lev, prop: $EBESBloatActive], REF INT]^
ELSE 0)/nmPerEBESPixel;
FOR col:
NAT
IN [0..nCols)
DO
FOR row:
NAT
IN [0..nRows)
DO
patternFileName: Rope.ROPE = PatternFileName[maskSetName: maskSetName,
maskName: mask.name, row: row, col: col];
ms.s ← EBESOpen[StreamGen[name: patternFileName, data: data]];
TerminalIO.WriteRope[patternFileName];
SendCommand[s: ms.s, comm:
NEW[StartDrawing ← [
patternFileName: RopeToFileName[patternFileName],
cx: MIN[maskWidth, ebesClip.x2-ebesClip.x1-col*maskWidth],
cy: MIN[maskHeight, ebesClip.y2-ebesClip.y1-row*maskHeight],
moDayYr: today,
addressCode: (
SELECT nmPerEBESPixel
FROM
1000 => Nm1000,
500 => Nm500,
250 => Nm250,
ENDCASE => Nm500)]]];
FOR stripe: [0..256)
IN [0..
MIN[maxStripes, Ceiling[[num: ebesClip.y2-ebesClip.y1-maskHeight*row, denom: stripeHeight]]])
DO
IF abortMask
THEN
GOTO AbortMask;
ms.areas.ChangeRect[rect: CDInline.universe, newvalue: NIL, checkOldvalue: FALSE];
ms.offset ← [x: -maskWidth*col-ebesClip.x1, y: -maskHeight*row-stripeHeight*stripe-ebesClip.y1];
ms.stripeClip ← [x1: 0, y1: 0,
x2: MIN[maskWidth, ebesClip.x2-ebesClip.x1-maskWidth*col],
y2: MIN[stripeHeight, ebesClip.y2-ebesClip.y1-maskHeight*row-stripeHeight*stripe]];
dr.worldClip ← ScaleEBESToCD[ms,
[x1: ms.stripeClip.x1-1-ABS[ms.bloat],
y1: ms.stripeClip.y1-1-ABS[ms.bloat],
x2: ms.stripeClip.x2+1+ABS[ms.bloat],
y2: ms.stripeClip.y2+1+ABS[ms.bloat]]];
CDViewer.ShowArrow[design: design,
pos: [x: (dr.worldClip.x1+dr.worldClip.x2)/2, y: (dr.worldClip.y1+dr.worldClip.y2)/2]];
-- keep user happy
CDOps.DrawDesign[design, dr]; -- build tesselation of the relevant design rectangle
AnalyzeTesselation[stripe, ms]; -- sends the stripe to s
ENDLOOP;
SendCommand[s: ms.s, comm: NEW[BasicCommand ← endDrawing]];
ms.s.Close[];
ms.s ← NIL;
ENDLOOP;
ENDLOOP;
ENDLOOP;
TerminalIO.WriteRope["finished\n"];
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^ ← CDInline.Surround[dd.bbox^, r];
dd.used[l] ← TRUE;
END;
NoteRectangle:
PROC [ r:
CD.DesignRect, l:
CD.Level, pr:
CD.DrawRef ] =
BEGIN
ms: MaskStateRef = NARROW[pr.devicePrivate];
IF l=ms.level
AND CDInline.NonEmpty[r]
THEN
ms.areas.ChangeRect[rect: ScaleCDToEBES[ms: ms, cdr: r],
newvalue: $covered, checkOldvalue: FALSE];
END;
ScaleCDToEBES:
PROC [ ms: MaskStateRef, cdr:
CD.DesignRect ]
RETURNS [ ebr:
CD.Rect ] =
BEGIN -- scales and offsets
ebr ← CDInline.NormalizeRect[CDInline.MoveRect[
[x1: RatMul[ms.scale, cdr.x1],
y1: RatMul[ms.scale, cdr.y1],
x2: RatMul[ms.scale, cdr.x2],
y2: RatMul[ms.scale, cdr.y2]],
ms.offset]];
END;
ScaleEBESToCD:
PROC [ ms: MaskStateRef, ebr:
CD.Rect ]
RETURNS [ cdr:
CD.DesignRect ] =
BEGIN -- scales and offsets
r: CD.Rect = CDInline.MoveRect[ebr, CDInline.NegOffset[ms.offset]];
cdr ← CDInline.NormalizeRect[
[x1: RatDiv[ms.scale, r.x1],
y1: RatDiv[ms.scale, r.y1],
x2: RatDiv[ms.scale, r.x2],
y2: RatDiv[ms.scale, r.y2]]];
END;
AnalyzeTesselation:
PROC [ stripe: [0..255), ms: MaskStateRef ] =
BEGIN
active: CD.Rect = ms.areas.ContentsBound[rect: CDInline.universe];
IF CDInline.NonEmpty[active]
THEN
BEGIN
SendCommand[s: ms.s, comm: NEW[StartStripe ← [stripeNumber: stripe+1]]];
IF ms.bloat#0
THEN
BEGIN
t: REF CornerStitching.Tesselation = ms.unbloated;
ms.unbloated ← ms.areas;
ms.areas ← t;
ms.areas.ChangeRect[rect: CDInline.universe, newvalue: NIL, checkOldvalue: FALSE];
IF ms.bloat>0
THEN
[] ← ms.unbloated.EnumerateArea[rect: CDInline.universe, perTile: BloatTile, data: ms]
ELSE
BEGIN
ms.areas.ChangeRect[rect: active, newvalue: $covered, checkOldvalue: FALSE];
[] ← ms.unbloated.EnumerateArea[rect: CDInline.universe, perTile: BloatTile, data: ms,
backgroundValue: $covered];
END;
END;
[] ← ms.areas.EnumerateArea[rect: CDInline.universe,
perTile: OutputTile, data: ms, backgroundValue: NIL];
SendCommand[s: ms.s, comm: NEW[BasicCommand ← endStripe]];
TerminalIO.WriteRope["."]; -- end of stripe
END;
END;
BloatTile:
PROCEDURE [tile: CornerStitching.TilePtr, data:
REF
ANY]
RETURNS [
REF
ANY]
-- CornerStitching.PerTileProc -- =
BEGIN
ms: MaskStateRef = NARROW[data];
cr: CD.Rect = CDInline.Intersection[CDInline.universe, Bloat[tile.Area, ABS[ms.bloat]]];
IF CDInline.NonEmpty[cr]
THEN
ms.areas.ChangeRect[rect: cr, newvalue: tile.Value, checkOldvalue: FALSE];
RETURN[ms];
END;
OutputTile:
PROCEDURE [tile: CornerStitching.TilePtr, data:
REF
ANY]
RETURNS [
REF
ANY]
-- CornerStitching.PerTileProc -- =
BEGIN -- only called on tiles with non-NIL values
ms: MaskStateRef = NARROW[data];
cr: CD.Rect = CDInline.Intersection[tile.Area, ms.stripeClip];
IF CDInline.NonEmpty[cr]
THEN
BEGIN
ms.drawCommand^ ← [x: cr.x1, y: cr.y1, w: cr.x2-cr.x1, h: cr.y2-cr.y1];
SendCommand[s: ms.s, comm: ms.drawCommand];
END;
RETURN[ms];
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.int[time.month+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]};
MaskName:
PROC [ lev:
CD.Level ]
RETURNS [ name: Rope.
ROPE ] =
BEGIN
defaultLevName: Rope.ROPE = ToRope[CDProperties.GetPropFromLevel[from: lev, prop: $CDxCIFName]];
name ← IF CDProperties.GetPropFromLevel[from: lev, prop: $EBESMaskName]#NIL THEN
ToRope[CDProperties.GetPropFromLevel[from: lev, prop: $EBESMaskName]]
ELSE Rope.Substr[base: defaultLevName, start: 1, len: Rope.Length[defaultLevName]-1];
END;
RopeToFileName:
PROC [ rfn: Rope.
ROPE ]
RETURNS [ fn: FileName ] =
{FOR i: [0..12) IN [0..12) DO fn[i] ← Rope.Fetch[rfn, i] ENDLOOP};
SortMasks:
PROC [ in:
LIST
OF MaskDescriptionRef ]
RETURNS [ out:
LIST
OF MaskDescriptionRef ] =
BEGIN
q: PriorityQueue.Ref = PriorityQueue.Create[pred: CompareMaskNames];
FOR m:
LIST
OF MaskDescriptionRef ← in, m.rest
WHILE m#
NIL
DO
q.Insert[m.first];
ENDLOOP;
out ← NIL;
WHILE
NOT q.Empty[]
DO
out ← CONS[NARROW[q.Remove[], MaskDescriptionRef], out];
ENDLOOP;
END;
CompareMaskNames:
PROC [ x, y: PriorityQueue.Item, data:
REF ←
NIL ]
RETURNS [
BOOL ]
-- PriorityQueue.SortPred -- =
BEGIN
xm: MaskDescriptionRef = NARROW[x];
ym: MaskDescriptionRef = NARROW[y];
RETURN[Rope.Compare[s1: xm.name, s2: ym.name, case: FALSE]=greater];
END;
WriteTapeDirectory:
PROC [ dest:
IO.
STREAM, maskSetName: Rope.
ROPE, nRows, nCols:
NAT, masks:
LIST
OF MaskDescriptionRef, today: Date] =
BEGIN
s: IO.STREAM = EBESOpen[dest: dest, eor: FALSE];
nMasks: NAT ← 0;
FOR m:
LIST
OF MaskDescriptionRef ← masks, m.rest
WHILE m#
NIL
DO
nMasks ← nMasks+1;
ENDLOOP;
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 MaskDescriptionRef ← 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[maskSetName, m.first.name, 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, stopIndexPlusOne: 2*sizeInWords]]};
END;
EBESStreamStateRef: TYPE = REF EBESStreamState;
EBESStreamState:
TYPE =
RECORD [
dest: IO.STREAM,
eor: BOOL,
buf: REF TEXT];
EBESOpen:
PROC [ dest:
IO.
STREAM, eor:
BOOL ←
TRUE ]
RETURNS [ self:
IO.
STREAM ] =
BEGIN
state: EBESStreamStateRef = NEW[EBESStreamState ←
[dest: dest, eor: eor, buf: NEW[TEXT[2048]]]];
state.buf.length ← 0;
self ← IO.CreateProcsStream[streamProcs: IO.CreateRefStreamProcs[
putChar: PutEBESChar,
unsafePutBlock: PutEBESBlock,
flush: FlushEBES,
close: CloseEBES], 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.stopIndexPlusOne-block.startIndex];
FOR i:
CARDINAL
IN [0..block.stopIndexPlusOne-block.startIndex)
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.stopIndexPlusOne-block.startIndex;
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:
BOOL ←
FALSE ] =
BEGIN
state: EBESStreamStateRef = NARROW[self.streamData];
self.Flush[];
state.dest.Close[];
END;
PutTapeSubStreamBlock:
PROC [ self:
IO.
STREAM, block:
REF
READONLY
TEXT, startIndex:
NAT, stopIndexPlusOne:
NAT ] =
TRUSTED BEGIN
dest: IO.STREAM = NARROW[self.streamData];
dest.PutBlock[block: block, startIndex: startIndex, stopIndexPlusOne: stopIndexPlusOne];
END;
CloseTapeSubStream: PROC [ self: IO.STREAM, abort: BOOL ← FALSE ] =
BEGIN
dest: IO.STREAM = NARROW[self.streamData];
TapeStream.WriteFileMark[dest];
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];
END;
END;
Bloat:
PROC [ r:
CD.Rect, delta:
CD.Number ]
RETURNS [ br:
CD.Rect ] =
INLINE
BEGIN
b0: NAT = delta/2; -- bloat must be >=0, split in "half"
b1: NAT = delta-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;
Floor:
PROC [ r: Rational ]
RETURNS [ f:
INT ] =
BEGIN
f ← r.num/r.denom;
f = 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 f ← f-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;
>0 => NULL;
ENDCASE => ERROR;
SELECT n
FROM
<0 => n ← -n;
>0 => NULL;
ENDCASE => ERROR;
r ← m MOD n;
WHILE r>0 DO m ← n; n ← r; r ← m MOD n; ENDLOOP;
RETURN[n];
END;
Module START code...
END. -- of EBESMaskImpl