DIRECTORY AIS USING [CloseFile, CloseWindow, CreateFile, FRef, OpenWindow, Raster, RasterPart, WRef, WriteSample], Basics USING [BITOR, BytePair], RefText USING [New], BasicTime USING [OutOfRange, GMT, nullGMT, Pack, Unpacked, Now, Unpack], PrincOpsUtils USING [LongCopy], Real USING [Round], CedarProcess USING [CheckAbort], Rope USING [Cat, Fetch, FromProc, Length, ROPE, Substr], TapesCommon, CrosfieldTape, UnixTapeOps; CrosfieldTapeImpl: CEDAR PROGRAM IMPORTS AIS, Basics, BasicTime, PrincOpsUtils, Rope, UnixTapeOps, CedarProcess, Real, RefText EXPORTS CrosfieldTape ~ BEGIN OPEN CrosfieldTape, TapesCommon; ROPE: TYPE ~ Rope.ROPE; IdentityOnTape: TYPE ~ MACHINE DEPENDENT RECORD [ checkWord1(0): WORD _ 0, tapeID(1): WORD _ 0, dateYMD(2): WORD _ 0, dateHM(3): DateHM _ [0,0], name(4:0..159): PACKED ARRAY [0..20) OF CHAR _ ALL [' ], ws2(14): WORD _ 0, checkWord2(15): WORD _ 0 ]; nameLen: NAT ~ (159+1)/16*2; DirectoryOnTape: TYPE ~ MACHINE DEPENDENT RECORD [ idn(0): WORD _ 0, -- File number on tape (range 1- 70) ws1(1): WORD _ 0, ws3(2:0..10): [0..1024] _ 2, att(2:11..11): BOOL _ FALSE, -- TRUE if contone, FALSE if screen ws2(2:12..15): [0..15] _ 0, color(3): Color _ [], ws4(4:0..31): CARD _ 0, hRes(6): WORD _ 0, vRes(7): WORD _ 0, pixelsPerLine(8): WORD _ 0, lines(9): WORD _ 0, linesPerCylinder(10): WORD _ 0, sectorsPerLine(11): WORD _ 0, createTimeYMD(12): WORD _ 0, createTimeHM(13): DateHM _ [0, 0], ws5(14:0..31): CARD _ 0, note(16:0..159): PACKED ARRAY [0..20) OF CHAR _ ALL[' ], ws6(26:0..111): ARRAY [0..7) OF WORD _ ALL[0], cylinders(33): WORD _ 0, blocks(34): WORD _ 0, tapeID(35): WORD _ 0, ws7(36:0..31): CARD _ 0, ws8(38:0..31): CARD _ 0 ]; noteLen: NAT ~ (159+1)/16*2; DateYMD: TYPE ~ MACHINE DEPENDENT RECORD [ year(0:9..15): [0..127], -- Year-1900 month(0:5..8): [0..15], day(0:0..4): [0..31] ]; DateHM: TYPE ~ MACHINE DEPENDENT RECORD [ minute(0:0..7): [0..255], hour(0:8..15): [0..255] ]; State: TYPE ~ REF StateRep; StateRep: TYPE ~ RECORD [ newState: BOOLEAN _ TRUE, tapeHandle: TapeHandle _ NIL, dir: Directory, text: REF TEXT, --one record (32 sectors) worth atByte: INT _ -1, -- the current byte offset in text sector: [0..32) _ 31, --sector in the current record recordsLeftInCylinder: [0..19] _ 0, linesLeftInCylinder: NAT _ 0, --dir.linesPerCylinder bytesLeftInSector: [0..512] _ 0, linesLeftInFile: NAT _ 0, mapPixel: PixelProc, dirAsText: REF TEXT _ NIL ]; Pixel: TYPE ~ MACHINE DEPENDENT RECORD [ SELECT OVERLAID * FROM cmky => [cyan, magenta, yellow, black: BYTE], rgb => [red, green, blue, blank: BYTE], ENDCASE ]; Colors: TYPE ~ {cyan, magenta, yellow, black}; AISInfo: TYPE ~ RECORD [fileName: ROPE, raster: AIS.Raster, fRef: AIS.FRef, wRef: AIS.WRef]; suffix: ARRAY Colors OF ROPE = ["cyan", "magenta", "yellow", "black"]; WriteTape: PUBLIC PROC [tapeHandle: TapeHandle, files: FileSpecList, tapeNumber: [1..999] _ 102, name: ROPE] RETURNS [FileHandleList] = { fileHandleList: FileHandleList _ NIL; fileNumber: NAT _ 1; reverse: PROC[files: FileHandleList] RETURNS[r: FileHandleList] = { r _ NIL; FOR f: FileHandleList _ files, f.rest UNTIL f=NIL DO r _ CONS[f.first, r]; ENDLOOP; }; InitializeTape[tapeHandle, files, tapeNumber, name]; FOR f: FileSpecList _ files, f.rest UNTIL f=NIL DO fileSpec: FileSpec _ f.first; fileHandleList _ CONS[FileHandleFromFileSpec[fileSpec, tapeHandle, fileNumber, tapeNumber], fileHandleList]; fileNumber _ fileNumber+1; ENDLOOP; fileHandleList _ reverse[fileHandleList]; RETURN[fileHandleList]; }; InitializeTape: PUBLIC PROC [tapeHandle: TapeHandle, files: FileSpecList, tapeNumber: [1..999] _ 102, name: ROPE] = { identity: Identity _ NEW[IdentityRep _ [tapeNumber: tapeNumber, date: BasicTime.Now[], name: name]]; idAsText: REF TEXT; masterDir: REF TEXT; fileNumber: NAT _ 1; idAsText _ TextFromIdentity[identity]; UnixTapeOps.Rewind[tapeHandle]; UnixTapeOps.WriteRecord[tapeHandle, idAsText]; masterDir _ AllocateBuffer[sizeOfMasterDirectory]; FOR i: NAT IN [0..sizeOfMasterDirectory) DO masterDir[i] _ VAL[377B]; --end of directories mark ENDLOOP; FOR f: FileSpecList _ files, f.rest UNTIL f=NIL DO fileSpec: FileSpec _ f.first; dirAsText: REF TEXT _ TextFromDirectory[DirectoryFromFileSpec[fileSpec, fileNumber, tapeNumber]]; offset: NAT _ (fileNumber-1)*bytesPerDirectory; FOR i: NAT IN [0..bytesPerDirectory) DO --put directory into the master directory masterDir[offset+i] _ dirAsText[i]; ENDLOOP; fileNumber _ fileNumber+1; ENDLOOP; UnixTapeOps.WriteRecord[tapeHandle, masterDir]; UnixTapeOps.WriteFileMark[tapeHandle]; }; AddFileToTape: PUBLIC PROC [tapeHandle: TapeHandle, file: FileSpec, fileNumber, tapeNumber: NAT, inPosition: BOOLEAN _ FALSE] RETURNS [TapesCommon.FileHandle] = { IF NOT inPosition THEN { UnixTapeOps.Rewind[tapeHandle]; UnixTapeOps.ForwardSpaceFile[tapeHandle]; --skip the master directory FOR i: NAT IN [1..fileNumber) DO UnixTapeOps.ForwardSpaceFile[tapeHandle]; ENDLOOP; }; RETURN[FileHandleFromFileSpec[file,tapeHandle, fileNumber, tapeNumber]]; }; FileHandleFromFileSpec: PROC [fileSpec: FileSpec, tapeHandle: TapeHandle, fileNumber, tapeNumber: NAT] RETURNS [FileHandle] ~ { dir: Directory _ DirectoryFromFileSpec[fileSpec, fileNumber, tapeNumber]; state: State; linesPerCylinder, nCylinders, sectorsPerLine, nRecords: NAT; [linesPerCylinder, nCylinders, sectorsPerLine, nRecords] _ FileStructures[fileSpec]; state _ NEW[StateRep _ [ newState: TRUE, tapeHandle: tapeHandle, dir: dir, text: AllocateBuffer[bytesPerRecord], atByte: 0, sector: 0, recordsLeftInCylinder: recordsPerCylinder, linesLeftInCylinder: linesPerCylinder, bytesLeftInSector: bytesPerSector, linesLeftInFile: fileSpec.nLines, mapPixel: fileSpec.mapPixel, dirAsText: TextFromDirectory[dir] ]]; RETURN[NEW[FileHandleRep _ [newLine: WriteNewLine, data: state]]]; }; DirectoryFromFileSpec: PROC [fileSpec: FileSpec, fileNumber, tapeNumber: NAT] RETURNS [dir: Directory] ~ { linesPerCylinder, nCylinders, sectorsPerLine, nRecords: NAT; [linesPerCylinder, nCylinders, sectorsPerLine, nRecords] _ FileStructures[fileSpec]; dir _ NEW[DirectoryRep _ [ fileNumber: fileNumber, contone: FALSE, hRes: fileSpec.pixelsPerInch, vRes: fileSpec.pixelsPerInch, pixelsPerLine: fileSpec.nPixels, lines: fileSpec.nLines, createDate: BasicTime.Now[], note: fileSpec.note, tapeNumber: tapeNumber, linesPerCylinder: linesPerCylinder, sectorsPerLine: sectorsPerLine, cylinders: nCylinders, blocks: nRecords ]]; }; ReadTapeIdentity: PUBLIC PROC [tapeHandle: TapeHandle] RETURNS [Identity] = { tapeMark: BOOL; text: REF TEXT; UnixTapeOps.Rewind[tapeHandle]; [tapeMark, text] _ UnixTapeOps.ReadRecord[tapeHandle: tapeHandle, buffer: text]; IF tapeMark THEN ERROR; -- something wrong here RETURN[IdentityFromText[text]]; }; ReadMasterDirectory: PUBLIC PROC[tapeHandle: TapeHandle] RETURNS [nFiles: NAT, masterDirectory: LIST OF Directory] = { id: Identity _ ReadTapeIdentity[tapeHandle]; mDirAsText: REF TEXT _ NIL; tapeMark: BOOL; [tapeMark, mDirAsText] _ UnixTapeOps.ReadRecord[tapeHandle: tapeHandle, buffer: NIL]; IF tapeMark THEN ERROR; -- something wrong here [tapeMark, ] _ UnixTapeOps.ReadRecord[tapeHandle: tapeHandle, buffer: NIL]; IF NOT tapeMark THEN ERROR; -- something wrong here [nFiles, masterDirectory] _ ParseMasterDirectory[mDirAsText]; }; ParseMasterDirectory: PROC [dir: REF TEXT] RETURNS [nFiles: NAT, masterDirectory: LIST OF Directory] = { directory: Directory; list: LIST OF Directory; start, nRecords: NAT _ 0; reverse: PROC[l: LIST OF Directory] RETURNS[r: LIST OF Directory] = { FOR d: LIST OF Directory _ l, d.rest UNTIL d=NIL DO r _ CONS[d.first, r]; ENDLOOP; }; nFiles _ 0; DO directory _ DirectoryFromText[text: dir, start: start]; IF directory=NIL THEN EXIT; nFiles _ nFiles+1; nRecords _ nRecords+directory.blocks; start _ start+bytesPerDirectory; list _ CONS[directory, list]; ENDLOOP; list _ reverse[list]; RETURN[nFiles, list]; }; ReadFile: PUBLIC PROC [tapeHandle: TapeHandle, fileNumber: [1..70] _ 1] RETURNS[Directory] ~ { stateProc: PROC[line: NAT, state: State] = {}; RETURN[ReadAndProcessFile[tapeHandle, fileNumber, stateProc]]; }; ReadAndProcessFile: PROC [tape: TapeHandle, fileNumber: [1..70] _ 1, stateProc: PROC[line: NAT, state: State]] RETURNS[Directory] ~ { state: State _ NEW[StateRep _ [tapeHandle: tape]]; nFiles: NAT; tapeMark: BOOL; [nFiles, ] _ ReadMasterDirectory[tape]; IF fileNumber > nFiles THEN ERROR; FOR i: NAT IN [1..fileNumber) DO UnixTapeOps.ForwardSpaceFile[tape]; --skip over the unwanted files ENDLOOP; [tapeMark, state.dirAsText] _ UnixTapeOps.ReadRecord[tapeHandle: tape, buffer: NIL]; IF tapeMark THEN ERROR; -- something wrong here state.dir _ DirectoryFromText[text: state.dirAsText, start: 0]; state.linesLeftInFile _ state.dir.lines; FOR line: NAT IN [0 .. state.dir.lines) DO ReadNewLine[state]; stateProc[line, state]; CedarProcess.CheckAbort[]; ENDLOOP; FOR i: NAT IN [0..state.recordsLeftInCylinder) DO ReadNewRecord[state]; ENDLOOP; [tapeMark, state.text] _ UnixTapeOps.ReadRecord[tapeHandle: tape, buffer: state.text]; IF NOT tapeMark THEN ERROR; -- something wrong here RETURN[state.dir]; }; FileTo4AIS: PUBLIC PROC [tapeHandle: TapeHandle, fileNumber: [1..70] _ 1, aisRootName: ROPE, mapPixel: PixelProc, reduce: NAT _ 4] RETURNS [Directory] ~ { ais: ARRAY Colors OF AISInfo; dir: Directory; Done: PROC [c: Colors] = { AIS.CloseWindow[w: ais[c].wRef]; AIS.CloseFile[f: ais[c].fRef]; }; stateProc: PROC[line: NAT, state: State] = { SetUp: PROC [c: Colors] = { ais[c].raster _ NEW[AIS.RasterPart _ [scanCount: 1+state.dir.lines/reduce, scanLength: 1+state.dir.pixelsPerLine/reduce, scanMode: rd, bitsPerPixel: 8, linesPerBlock: -1, paddingPerBlock: 0]]; ais[c].fileName _ Rope.Cat[r1: aisRootName, r2: "-", r3: suffix[c], r4: ".ais"]; ais[c].fRef _ AIS.CreateFile[name: ais[c].fileName, raster: ais[c].raster]; ais[c].wRef _ AIS.OpenWindow[f: ais[c].fRef]; MakeScreenToByte[mapPixel]; }; IF state.newState THEN { SetUp[cyan]; SetUp[magenta]; SetUp[yellow]; SetUp[black]; state.newState _ FALSE; }; FOR pixel: NAT IN [0 .. state.dir.pixelsPerLine) DO p: Pixel _ ReadPixel[state]; store: PROC [c: Colors, data: BYTE] = { lastPixel: NAT _ state.dir.pixelsPerLine-1; data _ ScreenToByte[data]; AIS.WriteSample[w: ais[c].wRef, value: data, line: line/reduce, pixel: (lastPixel-pixel)/reduce]; }; IF line MOD reduce #0 OR pixel MOD reduce #0 THEN LOOP; store[cyan, p.cyan]; store[magenta, p.magenta]; store[yellow, p.yellow]; store[black, p.black]; ENDLOOP; }; dir _ ReadAndProcessFile[tapeHandle, fileNumber, stateProc]; Done[cyan]; Done[magenta]; Done[yellow]; Done[black]; RETURN[dir]; }; bytesPerSector: NAT ~ 512; bytesPerRecord: NAT ~ 16384; sizeOfMasterDirectory: NAT ~5602; bytesPerDirectory: NAT ~80; recordsPerCylinder: NAT ~ 19; sectorsPerRecord: NAT ~ 32; lastSector: NAT _ sectorsPerRecord-1; WriteNewLine: PROC[self: FileHandle, line: PixelBuffer] RETURNS[eof: BOOLEAN]= { state: State _ NARROW[self.data]; IF line.samplesPerPixel # 4 THEN ERROR; IF state.newState THEN { --first time with this state MakeByteToScreen[state.mapPixel]; --make the byte translation table UnixTapeOps.WriteRecord[state.tapeHandle, state.dirAsText]; --write the directory state.newState _ FALSE; }; SELECT TRUE FROM state.linesLeftInCylinder=0 => WriteNewCylinder[state]; state.atByte#0 AND state.linesLeftInCylinder#0 => WriteNewSector[state]; ENDCASE; --state.atByte=0 AND linesLeftInCylinder#0 FOR i: NAT DECREASING IN [0..line.length) DO p: Pixel; p.cyan _ ByteToScreen[line[0][i]]; p.magenta _ ByteToScreen[line[1][i]]; p.yellow _ ByteToScreen[line[2][i]]; p.black _ ByteToScreen[line[3][i]]; WritePixel[state,p]; ENDLOOP; state.linesLeftInFile _ state.linesLeftInFile-1; state.linesLeftInCylinder _ state.linesLeftInCylinder-1; IF state.linesLeftInFile = 0 THEN { --write the rest of the records and the tape mark UNTIL state.recordsLeftInCylinder=0 DO WriteNewRecord[state]; ENDLOOP; UnixTapeOps.WriteFileMark[state.tapeHandle]; eof _ TRUE; } ELSE eof _ FALSE; }; WritePixel: PROC [state: State, p: Pixel] ~ { IF state.bytesLeftInSector=0 THEN WriteNewSector[state]; state.text[state.atByte] _ VAL[p.cyan]; state.text[state.atByte+1] _ VAL[p.magenta]; state.text[state.atByte+2] _ VAL[p.yellow]; state.text[state.atByte+3] _ VAL[ p.black]; state.atByte_state.atByte+4; state.bytesLeftInSector_ state.bytesLeftInSector-4; }; WriteNewSector: PROC [state: State] ~ { IF state.sector>=lastSector THEN WriteNewRecord[state] ELSE {state.sector _ state.sector+1; state.atByte_state.sector*bytesPerSector}; state.bytesLeftInSector _ bytesPerSector; }; WriteNewCylinder: PROC [state: State] ~ { UNTIL state.recordsLeftInCylinder=0 DO WriteNewRecord[state]; ENDLOOP; state.recordsLeftInCylinder _ recordsPerCylinder; state.atByte_ 0; state.sector _ 0; state.bytesLeftInSector _ bytesPerSector; state.linesLeftInCylinder _ state.dir.linesPerCylinder; }; WriteNewRecord: PROC [state: State] ~ { UnixTapeOps.WriteRecord[state.tapeHandle, state.text]; state.atByte_ 0; state.sector _ 0; state.recordsLeftInCylinder _ state.recordsLeftInCylinder-1; }; ReadNewLine: PROC [state: State] ~ { --aligns state.atByte to start of next line IF state.linesLeftInCylinder=0 THEN ReadNewCylinder[state] ELSE ReadNewSector[state]; state.linesLeftInCylinder _ state.linesLeftInCylinder-1; state.linesLeftInFile _ state.linesLeftInFile-1; }; ReadNewCylinder: PROC [state: State] ~ { UNTIL state.recordsLeftInCylinder=0 DO ReadNewRecord[state]; ENDLOOP; state.recordsLeftInCylinder _ recordsPerCylinder; ReadNewRecord[state]; --sets atByte and sector state.bytesLeftInSector _ bytesPerSector; state.linesLeftInCylinder _ state.dir.linesPerCylinder; }; ReadNewSector: PROC [state: State] ~ { --aligns state.atByte to start of next Sector IF state.sector>=lastSector THEN ReadNewRecord[state] ELSE {state.sector _ state.sector+1; state.atByte_state.sector*512}; state.bytesLeftInSector _ bytesPerSector; }; ReadNewRecord: PROC [state: State] ~ { --reads a tape record tapeMark: BOOL; [tapeMark, state.text] _ UnixTapeOps.ReadRecord[state.tapeHandle, state.text]; IF tapeMark THEN ERROR; -- out of sync state.atByte_ 0; state.sector _ 0; state.recordsLeftInCylinder _ state.recordsLeftInCylinder-1; }; ReadPixel: PROC [state: State] RETURNS [p: Pixel] ~ { --gets pixels out of the line IF state.bytesLeftInSector=0 THEN ReadNewSector[state]; p.cyan _ ORD[state.text[state.atByte]]; p.magenta _ ORD[state.text[state.atByte+1]]; p.yellow _ ORD[state.text[state.atByte+2]]; p.black _ ORD[state.text[state.atByte+3]]; state.atByte_state.atByte+4; state.bytesLeftInSector_ state.bytesLeftInSector-4; }; Error: PUBLIC ERROR [r: ROPE] = CODE; TextFromIdentity: PROC [identity: Identity] RETURNS [text: REF TEXT] ~ { onTape: IdentityOnTape _ []; size: NAT _ SIZE[IdentityOnTape]; text _ AllocateBuffer[size*2]; onTape.checkWord1 _ ByteSwap[648Ch]; onTape.tapeID _ ByteSwap[identity.tapeNumber]; [onTape.dateYMD, onTape.dateHM] _ ToDate[identity.date]; onTape.name _ RopeToArray[identity.name]; onTape.checkWord2 _ ByteSwap[7D47h]; TRUSTED { placeToPut: LONG POINTER _ LOOPHOLE[text, LONG POINTER] + SIZE[TEXT[0]]; placeToStart: LONG POINTER _ @onTape; PrincOpsUtils.LongCopy[from: placeToStart, nwords: size, to: placeToPut]; }; }; IdentityFromText: PROC [text: REF TEXT] RETURNS [identity: Identity] ~ { i: NAT _ 0; P: PROC RETURNS [c: CHAR] = {c _ onTape.name[i]; i _ i + 1}; placeToStart: LONG POINTER _ LOOPHOLE[text, LONG POINTER] + SIZE[TEXT[0]]; onTape: IdentityOnTape; TRUSTED { placeToPut: LONG POINTER _ @onTape; PrincOpsUtils.LongCopy[from: placeToStart, nwords: SIZE[IdentityOnTape], to: placeToPut]; }; IF ByteSwap[onTape.checkWord1] # 648Ch THEN ERROR Error["bad Identity checkword 1"]; IF ByteSwap[onTape.checkWord2] # 7D47h THEN ERROR Error["bad Identity checkword 2"]; identity _ NEW[IdentityRep]; identity.tapeNumber _ ByteSwap[onTape.tapeID]; identity.date _ FromDate[onTape.dateYMD, onTape.dateHM]; identity.name _ Rope.FromProc[nameLen, P]; identity.name _ StripRope[identity.name]; }; ValHRes: ARRAY PixelsPerInch OF WORD _ [ppi300: 006000B, ppi450: 010000B, ppi600: 014000B]; ValVRes: ARRAY PixelsPerInch OF WORD _ [ppi300: 005717B, ppi450: 010667B, ppi600: 013637B]; HResFromVal: PROC [val: WORD] RETURNS [ppi: PixelsPerInch] ~ { ppi _ SELECT val FROM 006000B => ppi300, 010000B => ppi450, 014000B => ppi600, ENDCASE => ERROR; }; VResFromVal: PROC [val: WORD] RETURNS [ppi: PixelsPerInch] ~ { ppi _ SELECT val FROM 005717B => ppi300, 010667B => ppi450, 013637B => ppi600, ENDCASE => ERROR; }; TextFromDirectory: PROC [directory: Directory] RETURNS [text: REF TEXT] ~ { size: NAT _ SIZE[DirectoryOnTape]; onTape: DirectoryOnTape _ []; text _ AllocateBuffer[size*2]; onTape.idn _ ByteSwap[directory.fileNumber]; onTape.att _ directory.contone; onTape.color _ directory.color; onTape.hRes _ ByteSwap[ValHRes[directory.hRes]]; onTape.vRes _ ByteSwap[ValVRes[directory.vRes]]; onTape.pixelsPerLine _ ByteSwap[directory.pixelsPerLine]; onTape.lines _ ByteSwap[directory.lines]; [onTape.createTimeYMD, onTape.createTimeHM] _ ToDate[directory.createDate]; onTape.note _ RopeToArray[directory.note]; onTape.tapeID _ ByteSwap[directory.tapeNumber]; onTape.linesPerCylinder _ ByteSwap[directory.linesPerCylinder]; onTape.sectorsPerLine _ ByteSwap[directory.sectorsPerLine]; onTape.cylinders _ ByteSwap[directory.cylinders]; onTape.blocks _ ByteSwap[directory.blocks]; TRUSTED { placeToPut: LONG POINTER _ LOOPHOLE[text, LONG POINTER] + SIZE[TEXT[0]]; placeToStart: LONG POINTER _ @onTape; PrincOpsUtils.LongCopy[from: placeToStart, nwords: size, to: placeToPut]; }; }; DirectoryFromText: PROC [text: REF TEXT, start: CARD] RETURNS [directory: Directory] ~ { placeToStart: LONG POINTER _ LOOPHOLE[text, LONG POINTER] + SIZE[TEXT[0]] + start/2; i: NAT _ 0; P: PROC RETURNS [c: CHAR] = {c _ onTape.note[i]; i _ i + 1}; onTape: DirectoryOnTape _ []; TRUSTED { placeToPut: LONG POINTER _ @onTape; PrincOpsUtils.LongCopy[placeToStart, SIZE[DirectoryOnTape], placeToPut]; }; IF onTape.idn = 0FFFFh THEN RETURN [NIL]; -- The last one directory _ NEW[DirectoryRep]; directory.fileNumber _ ByteSwap[onTape.idn]; directory.contone _ onTape.att; directory.color _ onTape.color; directory.hRes _ HResFromVal[ByteSwap[onTape.hRes]]; directory.vRes _ VResFromVal[ByteSwap[onTape.vRes]]; directory.pixelsPerLine _ ByteSwap[onTape.pixelsPerLine]; directory.lines _ ByteSwap[onTape.lines]; directory.createDate _ FromDate[onTape.createTimeYMD, onTape.createTimeHM]; directory.note _ Rope.FromProc[noteLen, P]; directory.note _ StripRope[directory.note]; directory.tapeNumber _ ByteSwap[onTape.tapeID]; directory.linesPerCylinder _ ByteSwap[onTape.linesPerCylinder]; directory.sectorsPerLine _ ByteSwap[onTape.sectorsPerLine]; directory.cylinders _ ByteSwap[onTape.cylinders]; directory.blocks _ ByteSwap[onTape.blocks]; }; ByteSwap: PROC [in: WORD] RETURNS [WORD] = INLINE { RETURN[Basics.BITOR[LOOPHOLE[in, Basics.BytePair].low*256, LOOPHOLE[in, Basics.BytePair].high]]; }; ByteSwapProc: PROC [in: WORD] RETURNS [out: WORD] = { out _ Basics.BITOR[LOOPHOLE[in, Basics.BytePair].low*256, LOOPHOLE[in, Basics.BytePair].high]; }; StripRope: PROC [rope: ROPE] RETURNS [ROPE] ~ { last: INT _ Rope.Length[rope]; FOR i: INT DECREASING IN [0 .. Rope.Length[rope]) DO IF Rope.Fetch[base: rope, index: i] = '\000 THEN last _ last - 1 ELSE EXIT; ENDLOOP; RETURN[Rope.Substr[base: rope, start: 0, len: last]]; }; RopeToArray: PROC [rope: ROPE] RETURNS [array: PACKED ARRAY [0..20) OF CHAR] ~ { length: NAT _ Rope.Length[rope]; FOR i: NAT IN [0..length) DO array[i] _ Rope.Fetch[base: rope, index: i]; ENDLOOP; FOR i: NAT IN [length..20) DO array[i] _ '\000; ENDLOOP; }; ToDate: PROC [t: BasicTime.GMT] RETURNS [rawYMD: WORD, dateHM: DateHM] ~ { unpacked: BasicTime.Unpacked _ BasicTime.Unpack[t]; dateYMD: DateYMD; dateYMD.year _ unpacked.year - 1900; dateYMD.month _ SELECT unpacked.month FROM January => 1, February=> 2, March=> 3, April=> 4, May => 5, June => 6, July=> 7, August => 8, September => 9, October => 10, November => 11, December => 12, ENDCASE => ERROR; dateYMD.day _ unpacked.day; dateHM.hour _ unpacked.hour; dateHM.minute _ unpacked.minute; rawYMD _ ByteSwapProc[LOOPHOLE[dateYMD]]; }; FromDate: PROC [rawYMD: WORD, dateHM: DateHM] RETURNS [t: BasicTime.GMT] ~ { unpacked: BasicTime.Unpacked; dateYMD: DateYMD _ LOOPHOLE[ByteSwapProc[rawYMD]]; unpacked.year _ dateYMD.year+1900; unpacked.month _ SELECT dateYMD.month FROM 1 => January, 2=> February, 3=> March, 4=> April, 5 => May, 6 => June, 7=> July, 8 => August, 9 => September, 10 => October, 11 => November, 12 => December, ENDCASE => unspecified; IF dateYMD.day NOT IN [1..31] THEN dateYMD.day _ 1; IF dateHM.hour NOT IN [0..12] THEN dateHM.hour _ 0; IF dateHM.minute NOT IN [0..60] THEN dateHM.minute _ 0; unpacked.day _ dateYMD.day; unpacked.hour _ dateHM.hour; unpacked.minute _ dateHM.minute; t _ BasicTime.nullGMT; t _ BasicTime.Pack[unpacked ! BasicTime.OutOfRange => CONTINUE]; RETURN[t]; }; ScreenToByte: ARRAY[0..255] OF BYTE _ ALL[0]; ByteToScreen: ARRAY[0..255] OF BYTE _ ALL[0]; MakeScreenToByte: PROC[pixelProc: PixelProc] = { FOR i: NAT IN[0..255] DO ScreenToByte[i] _ pixelProc[i]; ENDLOOP; }; MakeByteToScreen: PROC[pixelProc: PixelProc] = { FOR i: NAT IN[0..255] DO ByteToScreen[i] _ pixelProc[i]; ENDLOOP; }; CrosfieldToByte: PUBLIC PixelProc = { SELECT TRUE FROM pixel =21 => RETURN[0]; pixel =229 => RETURN[255]; pixel IN [25..225] => RETURN[ Real.Round[255*0.01* (pixel -25)/2.0]]; pixel < 25 => RETURN[0]; pixel > 225 => RETURN[255]; ENDCASE => ERROR; }; KOToByte: PUBLIC PixelProc = { dotPercent: REAL _ (pixel -6)*(100.0/239.0); dotPercent _ MIN[MAX[dotPercent, 0], 255]; RETURN[Real.Round[dotPercent*2.55]]; }; ByteToCrosfield: PUBLIC PixelProc = { SELECT TRUE FROM pixel =0 => RETURN[21]; pixel =255 => RETURN[229]; pixel IN [1..254] => RETURN[ Real.Round[(100.0*pixel /255.0)*2+25]]; ENDCASE => ERROR; }; IdentityByte: PUBLIC PixelProc = {RETURN[pixel]}; --use for read for KedieOrent tapes AllocateBuffer: PROC[sizeInBytes: INT] RETURNS[t: REF TEXT] = { t _ RefText.New[sizeInBytes]; t.length _ sizeInBytes; }; FileStructures: PROC [fileSpec: FileSpec] RETURNS [linesPerCylinder, nCylinders, sectorsPerLine, nRecords: NAT] = { sectorsPerLine _ Real.Round[0.5+(fileSpec.nPixels*4.0)/bytesPerSector]; linesPerCylinder _ (recordsPerCylinder*sectorsPerRecord)/sectorsPerLine; nCylinders _ Real.Round[0.5+REAL[fileSpec.nLines]/linesPerCylinder]; nRecords _ nCylinders*recordsPerCylinder; }; sizeRecordGap: REAL _ 0.8; --the inter-record gap, in inches sizeFileGap: REAL _ 2; --the inter-file gap, in feet SizeOfHeader: PUBLIC PROC [density: UnixTapeOps.Density _ GCR6250] RETURNS [sizeInFeet: REAL] = { d: INTEGER _ SELECT density FROM GCR6250 => 6250, PE1600 => 1600, ENDCASE => ERROR; id: REAL _ sizeRecordGap+ SIZE[IdentityOnTape]/d; masterDir: REAL _ sizeRecordGap+ sizeOfMasterDirectory/d; RETURN[sizeFileGap+(id+masterDir)/12.0]; }; SizeOfFile: PUBLIC PROC [fileSpec: FileSpec, density: UnixTapeOps.Density _ GCR6250] RETURNS [sizeInFeet: REAL] = { d: INTEGER _ SELECT density FROM GCR6250 => 6250, PE1600 => 1600, ENDCASE => ERROR; dir: REAL _ sizeRecordGap+ SIZE[DirectoryOnTape]/d; records: REAL; nCylinders: NAT; [,nCylinders] _ FileStructures[fileSpec]; records _ recordsPerCylinder*nCylinders*(sizeRecordGap+REAL[bytesPerRecord]/d); RETURN[sizeFileGap+(dir+records)/12.0]; }; SizeOfList: PUBLIC PROC [files: FileSpecList, density: UnixTapeOps.Density _ GCR6250] RETURNS [sizeInFeet: REAL] = { sizeInFeet _ SizeOfHeader[density]; FOR f: FileSpecList _ files, f.rest UNTIL f=NIL DO sizeInFeet _ SizeOfFile[f.first, density]+sizeInFeet; ENDLOOP; }; END. :CrosfieldTapeImpl.mesa Copyright Σ 1988 by Xerox Corporation. All rights reserved. Tim Diebert: March 11, 1988 8:35:43 am PST Maureen Stone, June 4, 1989 12:51:31 pm PDT CountedVM USING [Handle, SimpleAllocate], DEC Tape conventions: All WORD quantities need to be ByteSwapped. DEC bit0=Cedar bit15, etc However, numberic fields within this bit order are correct. Affects DateYMD and Color 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 --tape documentation 8 9 10 11 12 13 14 15 0 1 2 3 4 5 6 7 --Cedar bit addressing 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 --normal Cedar bit addressing Identity: TYPE ~ REF IdentityRep; IdentityRep: TYPE ~ RECORD [ tapeNumber: [1..999], date: BasicTime.GMT, name: ROPE ]; All quantities marked WORD are only accurate once byteswapped Read Tape Directory: TYPE ~ REF DirectoryRep; DirectoryRep: TYPE ~ RECORD [ fileNumber: [1..70], contone: BOOL, color: Color, hRes, vRes: PixelsPerInch, pixelsPerLine: CARDINAL, lines: CARDINAL, createDate: BasicTime.GMT, note: ROPE, tapeNumber: [0..999], linesPerCylinder, sectorsPerLine, cylinders, blocks: CARDINAL ]; All quantities marked WORD are only accurate once byteswapped Color: TYPE ~ MACHINE DEPENDENT RECORD [ cyan(0:7..7): BOOL _ TRUE, magenta(0:6..6): BOOL _ TRUE, yellow(0:5..5): BOOL _ TRUE, black(0:4..4): BOOL _ TRUE, red(0:3..3): BOOL _ FALSE, green(0:2..2): BOOL _ FALSE, blue(0:1..1): BOOL _ FALSE, ws1(0:0..0): BOOL _ FALSE, -- filler negativeImage(0:15..15): BOOL _ FALSE, -- low values represent dark print/film ws2(0:14..14): BOOL _ FALSE, -- filler bytesPerPixel(0:11..13): [0 .. 7) _ 4, ws3(0:8..10): [0 .. 7) _ 0 ]; Two separate records because we need to ByteSwap the words in DateYMD Apply ByteSwap before using Write Tape Initializes the tape for writing. Writes the master directory. Returns a list of FileHandle. Client rasterizes into each FileHandle in turn. newLine.eof is TRUE when tape has recorded nLines worth of rasters. Client can check newLine.eof to verify that the client and the tape are still in sync. Build the fileHandleList Reverse the fileHandleList Must include correct FileSpecs for every file that may appear on the tape. Write the Identity block Allocate and initialize the master directory Write the master directory File numbers start at 1. Files must be added in the order specified in the master directory IF rewind, will use the file number to position the tape. Otherwise, will start writing at the current tape position. color: color, --it defaults correctly Read Tape leaves tape ready to read the master directory The tape identity Read the tape mark Will rewind the tape, read the identity and master directory, then find the file Read the directory in front of file AIS files are 1/reduce2 the size of the original (ie for reduce = 4, 300 spi => 75 spi) Raster handlers For tape file writing For tape file reading Convert from Tape to Cedar formats ByteMapping, Pixel Procs, etc In the Crosfield file, pixel = (DotPercent*2)+25 Except that 21=0 and 229=100 on some page makeup systems data on tape runs from 6 to 245 Assumes all files are on one tape Κ[˜šœ™Icode™šœœ˜Kšœ9œœ˜J—K˜—š€ œœœœ˜>šœœ˜Kšœ9œœ˜J—K˜—š €œœœœœ˜KKšœœœ˜"K˜Kšœ˜Kšœ,˜,Kšœ˜Kšœ˜Kšœ0˜0Kšœ0˜0Kšœ9˜9Kšœ)˜)KšœK˜KKšœ*˜*Kšœ/˜/Kšœ?˜?Kšœ;˜;Kšœ1˜1Kšœ+˜+šœ˜ Kšœ œœœœœœœ˜HKšœœœ ˜%KšœI˜IK˜—K˜—š €œœœœ œœ˜XKšœœœœœœœœ˜TKšœœ˜ Kš€œœœœ$˜œœ˜sKšœœœ œœ œ œœ˜SKšœœœ˜3Kšœ œ˜Kšœ œ˜Kšœ)˜)Kšœ7œ˜PKšœ!˜'K˜—š€ œ œ?œœ˜tK™!Kšœ#˜#šœ!œœ˜2Kšœ5˜5Kšœ˜—K˜—K˜K˜—Kšœ˜—…—\(‡½