PDInterpOutputTapeImpl:
CEDAR
PROGRAM
IMPORTS PrincOpsUtils, MessageWindow, PDInterpBitmap, PDInterpSysCalls, TapeOps
EXPORTS PDInterpOutput = BEGIN
Plotter constants
bpi: CARDINAL ← 200;
pageWidthInches: CARDINAL ← 40;
white: CARDINAL = 0;
bitsPerWord: NAT = Basics.bitsPerWord;
bytesPerWord: NAT = Basics.bytesPerWord;
wordsPerScan: CARDINAL = ((bpi * pageWidthInches) + bitsPerWord - 1) / bitsPerWord;
scansPerBand: CARDINAL ← 64;
wordsPerBand: LONG CARDINAL = LONG[wordsPerScan] * LONG[scansPerBand];
currentHerald: PDFileFormat.Herald;
currentStartImage: PDFileFormat.StartImage;
currentBandNumber: NAT ← 0;
bandWords: NAT ← 0;
band: PDInterpBitmap.BitmapDesc ← [sOrigin: 0, fOrigin: 0, sMin: 0, fMin: 0, sSize: 0, fSize: 0, pointer: NIL, rast: 0, lines: 0];
bandWordsAllocated: INT ← 0;
tapeHandle: TapeOps.TapeHandle ← NIL;
preamble: REF ColorPreamble;
ColorPreamble: TYPE = MACHINE DEPENDENT RECORD [
escape: [0..256) ← 9BH, -- escape
header: CHARACTER,
byteCountHigh: [0..256) ← 0,
byteCountLow: [0..256) ← 4,
lengthHigh: [0..256) ← 0,
lengthLow: [0..256) ← 0,
modeAndColor: ModeAndColor,
reserved: [0..256) ← 0,
padding: Bytepad --tape wants first record padded to 32 bytes
];
Bytepad: TYPE = ARRAY [0..12) OF CARDINAL;
pad: Bytepad = ALL[0];
Color: TYPE = MACHINE DEPENDENT {Black(0), Cyan(1), Magenta(2), Yellow(3)};
ColorMode: TYPE = MACHINE DEPENDENT {
SinglePassOfColor(0), FirstPassOfColor(1), IntermediatePassOfColor(2), (3)};
ModeAndColor: TYPE = MACHINE DEPENDENT RECORD [
padding: [0..16) ← 0, --4 bits of padding
mode: ColorMode, --2 bit field
color: Color ← Black ]; --2 bit field
CurrentBandDesc:
PROC
RETURNS [PDInterpBitmap.BitmapDesc] =
TRUSTED {
band.sOrigin ← currentHerald.bandSSize*(currentBandNumber+currentStartImage.passBands);
PDInterpBitmap.Clear[band];
RETURN [band]
};
StartImage:
PUBLIC
PROC [herald: PDFileFormat.Herald, startImage: PDFileFormat.StartImage, request: PDQueue.Request]
RETURNS [PDInterpBitmap.BitmapDesc] = {
IF taping
THEN {
rast: NAT = (--startImage.fSizePage-- (bpi*pageWidthInches)+bitsPerWord-1)/bitsPerWord;
lines: INT = herald.bandSSize;
words: INT = rast*lines;
mode: ColorMode ← IF NOT startImage.feed THEN IntermediatePassOfColor ELSE IF startImage.strip THEN SinglePassOfColor ELSE FirstPassOfColor;
color: Color ←
SELECT startImage.toner
FROM
black => Black,
cyan => Cyan,
magenta => Magenta,
yellow => Yellow,
ENDCASE => ERROR;
currentHerald ← herald;
currentStartImage ← startImage;
currentBandNumber ← 0;
IF words > bandWordsAllocated
THEN
TRUSTED {
IF band.pointer # NIL THEN PDInterpSysCalls.FreeSpace[band.pointer];
band.pointer ← PDInterpSysCalls.AllocateSpace[words];
bandWordsAllocated ← words;
};
band.sOrigin ← 0;
band.fOrigin ← 0; -- startImage.fMinPage;
band.sMin ← 0;
band.fMin ← 0;
band.sSize ← herald.bandSSize;
band.fSize ← rast*bitsPerWord;
band.rast ← rast;
band.lines ← herald.bandSSize;
bandWords ← words;
IF bandWords MOD bufferWords # 0 THEN ERROR;
IF currentStartImage.feed
THEN {
--start of new page
tapeHandle ← InitializeTape[];
};
preamble ← MakeColorPreamble[inches: IF color=Black THEN LongestColor[]+2 ELSE 0, mode: mode, color: color];
TRUSTED {PutBlock[source: LOOPHOLE[preamble], words: SIZE[ColorPreamble]];};
IF passBands[color]#0 THEN SendWhiteSpace[bands: passBands[color]]
}
ELSE {
--first pass to find images sizes and white space
rast: NAT = (--startImage.fSizePage-- (bpi*pageWidthInches)+bitsPerWord-1)/bitsPerWord;
lines: INT = herald.bandSSize;
words: INT = rast*lines;
color: Color ←
SELECT startImage.toner
FROM
black => Black,
cyan => Cyan,
magenta => Magenta,
yellow => Yellow,
ENDCASE => ERROR;
currentHerald ← herald;
currentStartImage ← startImage;
currentBandNumber ← 0;
IF words > bandWordsAllocated
THEN
TRUSTED {
IF band.pointer # NIL THEN PDInterpSysCalls.FreeSpace[band.pointer];
band.pointer ← PDInterpSysCalls.AllocateSpace[words];
bandWordsAllocated ← words;
};
band.sOrigin ← 0;
band.fOrigin ← 0; -- startImage.fMinPage;
band.sMin ← 0;
band.fMin ← 0;
band.sSize ← herald.bandSSize;
band.fSize ← rast*bitsPerWord;
band.rast ← rast;
band.lines ← herald.bandSSize;
bandWords ← words;
IF bandWords MOD bufferWords # 0 THEN ERROR;
buffersPerBand ← bandWords/bufferWords; --THIS MUST HAVE ZERO REMAINDER
IF currentStartImage.feed
THEN {
--start of new page, so initialize separation arrays
passBands ← ALL[LAST[NAT]];
nBands ← ALL[0];
};
passBands[color] ← currentStartImage.passBands;
nBands[color] ← currentStartImage.nBands;
};
RETURN [CurrentBandDesc[]];
};
MakeColorPreamble:
PROC [inches:
CARDINAL, mode: ColorMode, color: Color ← Black]
RETURNS [preamble:
REF ColorPreamble] = {
preamble ← NEW[ColorPreamble ← [
header: 'P, -- color header preamble
lengthLow: inches, -- length in inches
modeAndColor: [mode: mode, color: color],
padding: pad
]];
};
PutBlock:
PROC [source:
LONG
POINTER, words:
NAT] =
TRUSTED {
tapeStatus: TapeOps.TapeStatus;
loopCount: NAT ← words/bufferWords; --whole number of full buffers
lastBuffer: NAT ← words MOD bufferWords; --words in final, unfilled buffer
IF words > bandWordsAllocated THEN ERROR;
FOR count:
NAT
IN [0..loopCount)
DO
MoveWordsToBuffer[from: source+LONG[bufferWords]*LONG[count], words: bufferWords];
tapeStatus ← TapeOps.WriteRecord[tapeHandle: tapeHandle, writeData: textBuffer];
IF tapeStatus[EOT] THEN ERROR;
ENDLOOP;
IF lastBuffer#0
THEN {
PrincOpsUtils.LongZero[where: bufferData, nwords: bufferWords];
MoveWordsToBuffer[from: source+LONG[bufferWords]*LONG[loopCount], words: lastBuffer];
tapeStatus ← TapeOps.WriteRecord[tapeHandle: tapeHandle, writeData: textBuffer];
IF tapeStatus[EOT] THEN ERROR;
};
};
MoveWordsToBuffer:
PROC [from:
LONG
POINTER, words:
NAT] =
TRUSTED {
IF words > textBuffer.maxLength/2 THEN ERROR;
textBuffer.length ← words*2;
PrincOpsUtils.LongCopy[from: from, to: bufferData, nwords: words];
};
EndBand:
PUBLIC
PROC
RETURNS [PDInterpBitmap.BitmapDesc] =
TRUSTED {
IF taping THEN PutBlock[source: band.pointer, words: bandWords];
currentBandNumber ← currentBandNumber + 1;
RETURN [CurrentBandDesc[]]
};
EndImage:
PUBLIC
PROC [request: PDQueue.Request] = {
IF currentStartImage.strip
THEN {
-- all images completed for this page
minPass: NAT ← LAST[NAT];
FOR c: Color
IN Color
DO
minPass ← MIN[passBands[c], minPass];
ENDLOOP;
FOR c: Color
IN Color
DO
IF passBands[c] # NAT.LAST THEN passBands[c] ← passBands[c] - minPass;
ENDLOOP;
IF taping THEN FinalizeTape[tapeHandle];
taping ← NOT taping;
}
ELSE
IF taping
THEN {
tapeStatus: TapeOps.TapeStatus;
tapeStatus ← TapeOps.WriteFileMark[tapeHandle]; --file mark between separations
IF tapeStatus[EOT] THEN ERROR;
};
};
LongestColor:
PROC []
RETURNS [inches:
CARDINAL] = {
inches ← 0;
FOR c: Color IN Color DO
inches ← MAX[inches, ((passBands[c] + nBands[c]) * scansPerBand) / bpi];
ENDLOOP;
};
SendWhiteSpace:
PROC [bands:
CARDINAL] =
TRUSTED {
PrincOpsUtils.LongZero[where: bufferData, nwords: bufferWords];
FOR i:
CARDINAL
IN [0..bands)
DO
FOR j:
CARDINAL
IN [0..buffersPerBand)
DO
PutBlock[source: bufferData, words: bufferWords];
ENDLOOP;
ENDLOOP;
};
InitializeTape:
PROC
RETURNS [tapeHandle: TapeOps.TapeHandle] = {
ENABLE TapeOps.TapeOpsError => {
IF tapeHandle#NIL THEN TapeOps.CloseDrive[tapeHandle]; ERROR};
tapeStatus: TapeOps.TapeStatus;
tapeHandle ← TapeOps.OpenDrive[serverName: serverName, driveNumber: driveNumber, density: density];
tapeStatus ← TapeOps.GetStatus[tapeHandle];
UNTIL tapeStatus[
RDY]
DO
MessageWindow.Append[message: "Tape Drive Not Ready", clearFirst: TRUE];
MessageWindow.Blink[];
tapeStatus ← TapeOps.GetStatus[tapeHandle];
ENDLOOP;
};
FinalizeTape:
PROC [tapeHandle: TapeOps.TapeHandle] = {
ENABLE TapeOps.TapeOpsError => {TapeOps.CloseDrive[tapeHandle]; ERROR};
tapeStatus: TapeOps.TapeStatus;
tapeStatus ← TapeOps.WriteFileMark[tapeHandle];
IF tapeStatus[EOT] THEN ERROR;
tapeStatus ← TapeOps.WriteFileMark[tapeHandle]; --need two file marks
IF tapeStatus[EOT] THEN ERROR;
tapeStatus ← TapeOps.Rewind[tapeHandle];
IF tapeStatus[EOT] THEN ERROR;
TapeOps.CloseDrive[tapeHandle];
};
SetTapeServer:
PROC [serverNameL: Rope.
ROPE ← "Maggie", driveNumberL:
NAT ← 0, densityL: TapeOps.Density ← PE1600] = {
serverName ← serverNameL; driveNumber ← driveNumberL; density ← densityL;
};
serverName: Rope.ROPE ← "Maggie";
driveNumber: NAT ← 0;
density: TapeOps.Density ← PE1600;
taping: BOOLEAN ← FALSE; --true if second pass and writing tapes
passBands: ARRAY Color OF NAT;
nBands: ARRAY Color OF NAT;
bufferData: LONG POINTER;
bufferBytes: NAT = 16000; --16 scanline buffer
bufferWords: NAT = bufferBytes/bytesPerWord;
bufferPad: NAT = 10; --just for debugging
textBuffer: REF TEXT ← NEW[TEXT[bufferBytes+bufferPad]];
buffersPerBand: NAT ← 0; --MUST BE AN INTEGRAL NUMBER OF BUFFERS PER BAND
textBuffer.length ← bufferBytes;
TRUSTED {bufferData ← LOOPHOLE[textBuffer, LONG POINTER]+SIZE[TEXT[0]];};
END.