-- PressOut.mesa -- Written by Joe Maleson -- Last changed by Doug Wyatt, November 6, 1980 9:33 AM -- Last changed by Schmidt (Pilot Conversion), 1-Jul-81 18:40:17 DIRECTORY DCSFileTypes USING [tLeaderPage], Directory USING[CreateFile, Error, ignore, Lookup, UpdateDates], Environment USING [bytesPerPage, wordsPerPage], File USING [Capability, delete, grow, Permissions, read, shrink, write], FileStream USING [Create, GetIndex, SetIndex], Heap USING [systemZone], Inline USING [BITAND, BITXOR, COPY, HighHalf, LDIVMOD, LongDiv, LongMult, LowByte, LowHalf], LongString USING[AppendString, EqualString, EquivalentStrings], PressDefs USING [DLDotsFollow, DLDrawCurve, DLDrawTo, DLGetDotsFromFile, DLMoveTo, DLSetCoding, DLSetMode, DLSetSamplingProperties, DLSetSize, DLSetWindow, DLSSPInputIntensity, DLSSPScreen, DocDir, ELAlternative, ELCommand, ELFont, ELNop, ELOnlyOnCopy, ELResetSpace, ELSetBrightness, ELSetHue, ELSetSaturation, ELSetSpaceX, ELSetSpaceXShort, ELSetSpaceY, ELSetSpaceYShort, ELSetX, ELSetY, ELShowCharacterImmediate, ELShowCharacters, ELShowCharactersAndSkip, ELShowCharactersShort, ELShowDots, ELShowDotsOpaque, ELShowObject, ELShowRectangle, ELSkipCharacters, ELSkipCharactersShort, ELSkipControlBytes, ELSkipControlBytesImmediate, ELSpace, EntityCommandTrailer, ExternalFileDirectory, Font, FontDir, FontEntry, MicaHeight, MicaWidth, Page, PartDir, PressDotsData, PressFileDescriptor, PressPassword], Profile USING [userName], Stream USING [Delete, GetBlock, Handle, PutBlock, PutByte, PutChar, PutWord, SendNow], Time USING [Append, Current, Unpack]; PressOut: PROGRAM IMPORTS Directory, FileStream, Heap, Inline, LongString, Profile, Stream, Time EXPORTS PressDefs = { OPEN PressDefs; --these guys should be per PressFileDescriptor entitySegment: TYPE = RECORD[ link: LONG POINTER TO entitySegment, length: CARDINAL, beginByte,endByte: LONG CARDINAL ]; maxEntitySegment: CARDINAL = 16000; -- MDS Usage entitySegmentHead: LONG POINTER TO entitySegment; lastSetX, lastSetY, lastFont, lastSpaceX,lastSpaceY: CARDINAL ← 0; externalFileStrings: ARRAY[0..100) OF LONG STRING; nExternalFileStrings: CARDINAL ← 0; nw: CARDINAL; -- used by ScanInitMem sl: LONG POINTER;-- used by ScanInitMem -- end of MDS Usage CopyBCPLString: PUBLIC PROC [ BCPLString: POINTER TO PACKED ARRAY [0..100) OF CHARACTER, mesaString: LONG STRING] = { i: CARDINAL; BCPLString[0]←LOOPHOLE[mesaString.length]; FOR i IN [0..mesaString.length) DO BCPLString[i+1]←mesaString[i]; ENDLOOP; }; MulDiv: PUBLIC PROC [a,b,c:CARDINAL] RETURNS [CARDINAL] = { al: LONG CARDINAL ← Inline.LongMult[a,b]; RETURN[Inline.LongDiv[al+c/2,c]]; }; SignedMulDiv: PUBLIC PROC [a,b,c:INTEGER] RETURNS [INTEGER] = { rslt: INTEGER; sgn: INTEGER ← Inline.BITXOR[Inline.BITXOR[a,b],c]; --Sign bit; rslt ← MulDiv[ABS[a],ABS[b],ABS[c]]; IF sgn < 0 THEN RETURN[-rslt]; RETURN[rslt]; }; SetBlock: PUBLIC PROC [buf: LONG POINTER TO ARRAY [0..0) OF INTEGER, val,len: INTEGER] = { buf[0]←val; Inline.COPY[to: @buf+1,from: @buf,nwords: len-1]; }; WriteCommand: PUBLIC PROC [p: LONG POINTER TO PressFileDescriptor, Command: CARDINAL,Param: POINTER TO ARRAY [0..6) OF CARDINAL] = { es: Stream.Handle ← LOOPHOLE[p.EntityCommands]; i: CARDINAL; Stuff1: PROC [Argument: CARDINAL] = { p.EntityCommandLen ← p.EntityCommandLen+1; PutLowerByte[es,Argument]; --p.EntityCommands[p.EntityCommandLen]←Argument; }; Stuff2: PROC [Argument: CARDINAL] = { PutLowerByte[es,Argument/256]; --p.EntityCommands[p.EntityCommandLen+1]←Argument/256; p.EntityCommandLen ← p.EntityCommandLen + 2; PutLowerByte[es,Argument]; --p.EntityCommands[p.EntityCommandLen]←Argument; }; SELECT Command FROM --standard "short" command ELFont => { lastFont ← Param[0];Stuff1[Command+Param[0]];}; --standard "short-1" commands ELShowCharactersShort, ELSkipCharactersShort, ELShowCharactersAndSkip=> Stuff1[Command+Param[0]-1]; --strange and puzzling set space short stuff ELSetSpaceXShort => { lastSpaceX ← Param[0];Stuff2[Command*256+Param[0]];}; ELSetSpaceYShort => { lastSpaceY ← Param[0];Stuff2[Command*256+Param[0]];}; --commands with no parameters ELResetSpace => { lastSpaceX ← lastSpaceY ← 0;Stuff1[Command];}; ELSpace,ELNop => Stuff1[Command]; --commands with a single byte parameter ELShowCharacters, ELShowCharacterImmediate, ELSkipCharacters, ELSkipControlBytesImmediate, ELSetBrightness, ELSetHue,ELSetSaturation, ELOnlyOnCopy => { Stuff1[Command]; Stuff1[Param[0]]; }; --commands with two bytes of parameters ELSetSpaceX => { Stuff1[Command]; Stuff2[Param[0]]; lastSpaceX ← Param[0]; }; ELSetSpaceY => { Stuff1[Command]; Stuff2[Param[0]]; lastSpaceY ← Param[0]; }; ELSetX => { Stuff1[Command]; Stuff2[Param[0]]; lastSetX ← Param[0]; }; ELSetY => { Stuff1[Command]; Stuff2[Param[0]]; lastSetY ← Param[0]; }; ELShowObject => { Stuff1[Command]; Stuff2[Param[0]]; }; --commands with [2 bytes] [1 byte] ELSkipControlBytes => { Stuff1[Command]; Stuff2[Param[0]]; Stuff1[Param[1]]; }; --commands with 4 bytes ELShowRectangle,ELShowDots, ELShowDotsOpaque => { Stuff1[Command]; Stuff2[Param[0]]; Stuff2[Param[1]]; }; --and the real long stuff ELAlternative => { Stuff1[Command]; FOR i IN [0..4] DO Stuff2[Param[i]]; ENDLOOP; }; ENDCASE; IF p.EntityCommandLen > maxEntitySegment THEN { h: CARDINAL ← p.CurHue; s: CARDINAL ← p.CurSat; b: CARDINAL ← p.CurBright; NewEntitySegment[p]; WriteCommand[p,ELSetX,LOOPHOLE[@lastSetX]]; WriteCommand[p,ELSetY,LOOPHOLE[@lastSetY]]; WriteCommand[p,ELSetHue,LOOPHOLE[@h]]; WriteCommand[p,ELSetSaturation,LOOPHOLE[@s]]; WriteCommand[p,ELSetBrightness,LOOPHOLE[@b]]; WriteCommand[p,ELFont,LOOPHOLE[@lastFont]]; IF lastSpaceX#0 THEN WriteCommand[p,ELSetSpaceX,LOOPHOLE[@lastSpaceX]]; IF lastSpaceY#0 THEN WriteCommand[p,ELSetSpaceY,LOOPHOLE[@lastSpaceY]]; }; }; WriteDirectory: PUBLIC PROC [p: LONG POINTER TO PressFileDescriptor] = { d: DocDir; dayTime: LONG CARDINAL ← Time.Current[]; Zero[@d,SIZE[DocDir]]; d.Password←PressPassword; d.nRecs←p.RecordStart+p.fontLen+p.partLen+1; d.nParts←p.numParts; d.partDirStart←p.RecordStart+p.fontLen; d.partLength←p.partLen; d.date ← Inline.LowHalf[dayTime]*200000B + Inline.HighHalf[dayTime]; d.firstCopy←p.FirstCopy; d.lastCopy←p.LastCopy; d.firstPage←-1; d.lastPage←-1; d.solidCode←p.solidCode; CopyBCPLString[LOOPHOLE[@d.fileName],p.PressFileName]; CopyBCPLString[LOOPHOLE[@d.userName],p.UserName]; CopyBCPLString[LOOPHOLE[@d.dateString],p.DateString]; []←WriteBlock[p.outStream,@d,SIZE[DocDir]]; }; SetSpaceX: PUBLIC PROC [p: LONG POINTER TO PressFileDescriptor, micaX: CARDINAL] = { param: ARRAY [0..6) OF CARDINAL; IF p.CurSpaceX = micaX THEN RETURN; param[0] ← micaX; WriteCommand[p,ELSetSpaceX,@param]; p.CurSpaceX ← micaX; }; SetSpaceY: PUBLIC PROC [p: LONG POINTER TO PressFileDescriptor, micaY: CARDINAL] = { param: ARRAY [0..6) OF CARDINAL; IF p.CurSpaceY = micaY THEN RETURN; param[0] ← micaY; WriteCommand[p,ELSetSpaceY,@param]; p.CurSpaceY ← micaY; }; ResetSpace: PUBLIC PROC [p: LONG POINTER TO PressFileDescriptor] = { IF p.CurSpaceX = 177777B AND p.CurSpaceY = 177777B THEN RETURN; WriteCommand[p,ELResetSpace,NIL]; p.CurSpaceX ← p.CurSpaceY ← 177777B; }; PutSpace: PUBLIC PROC [p: LONG POINTER TO PressFileDescriptor] = { WriteCommand[p,ELSpace,NIL]; }; SetColor: PUBLIC PROC [p: LONG POINTER TO PressFileDescriptor, hue,sat,bright: [0..256)] = { param: ARRAY [0..6) OF CARDINAL; IF hue#p.CurHue THEN { param[0]←hue; p.CurHue←hue; WriteCommand[p,ELSetHue,@param]; }; IF sat#p.CurSat THEN { param[0]←sat; WriteCommand[p,ELSetSaturation,@param]; p.CurSat←sat; }; IF bright#p.CurBright THEN { param[0]←bright; WriteCommand[p,ELSetBrightness,@param]; p.CurBright←bright; }; }; SetHue: PUBLIC PROC [p: LONG POINTER TO PressFileDescriptor, hue: [0..256)] = { param: ARRAY [0..6) OF CARDINAL; IF hue#p.CurHue THEN { param[0]←hue; p.CurHue←hue; WriteCommand[p,ELSetHue,@param]; }; }; SetSaturation: PUBLIC PROC [p: LONG POINTER TO PressFileDescriptor, sat: [0..256)] = { param: ARRAY [0..6) OF CARDINAL; IF sat#p.CurSat THEN { param[0]←sat; WriteCommand[p,ELSetSaturation,@param]; p.CurSat←sat; }; }; SetBrightness: PUBLIC PROC [p: LONG POINTER TO PressFileDescriptor, bright: [0..256)] = { param: ARRAY [0..6) OF CARDINAL; IF bright#p.CurBright THEN { param[0]←bright; WriteCommand[p,ELSetBrightness,@param]; p.CurBright←bright; }; }; SetFont: PUBLIC PROC [p: LONG POINTER TO PressDefs.PressFileDescriptor, Name: LONG STRING,PointSize: CARDINAL,Face: CARDINAL ← 0, Rotation: CARDINAL ← 0] = { UC: ARRAY CHARACTER ['a..'z] OF CHARACTER = ['A,'B,'C,'D,'E,'F,'G,'H,'I,'J,'K,'L,'M,'N,'O,'P,'Q,'R,'S,'T,'U,'V,'W,'X,'Y,'Z]; FBuf: LONG POINTER TO PressDefs.FontEntry; s: LONG STRING; param: ARRAY [0..6) OF CARDINAL; FOR i: CARDINAL IN [0..Name.length) DO IF Name[i] IN ['a..'z] THEN Name[i] ← UC[Name[i]]; ENDLOOP; FOR i: CARDINAL IN [0..p.numFonts) DO FBuf ← p.fontDir[i]; IF LongString.EqualString[Name,FBuf.Family] AND Face = FBuf.Face AND PointSize = FBuf.Size AND Rotation = FBuf.Rotation THEN { param[0] ← i; WriteCommand[p,ELFont,@param]; RETURN; }; ENDLOOP; IF p.numFonts = 16 THEN ERROR; --whoops, no space --grab free storage, and go FBuf ← AllocateSegment[SIZE[PressDefs.FontEntry]]; s ← AllocateString[20]; FBuf.EntryLength←16; FBuf.FontSet←0; FBuf.Font←p.numFonts; FBuf.M←0;FBuf.N←127; LongString.AppendString[s,Name]; FBuf.Family←s; FBuf.Face←Face; FBuf.Source←0; FBuf.Size←PointSize; FBuf.Rotation←Rotation; p.fontDir[p.numFonts]←FBuf; p.numFonts←p.numFonts+1; param[0] ← p.numFonts-1; WriteCommand[p,ELFont,@param]; }; PutText: PUBLIC PROC[p: LONG POINTER TO PressFileDescriptor, str: LONG STRING,xleft,ybase: CARDINAL] = { s: Stream.Handle ← p.outStream; param: ARRAY [0..6) OF CARDINAL; param[0]←xleft;WriteCommand[p,ELSetX,@param]; param[0]←ybase;WriteCommand[p,ELSetY,@param]; IF p.numFonts = 0 THEN SetFont[p,"Gacha"L,8]; FOR i: CARDINAL IN [0..str.length) DO Stream.PutChar[s,str[i]]; ENDLOOP; param[0] ← str.length; WriteCommand[p,ELShowCharacters,@param]; }; PutRectangle: PUBLIC PROC [p: LONG POINTER TO PressFileDescriptor, xstart,ystart,xlen,ylen: CARDINAL] = { param: ARRAY [0..6) OF CARDINAL; param[0]←xstart;WriteCommand[p,ELSetX,@param]; param[0]←ystart;WriteCommand[p,ELSetY,@param]; param[0]←xlen; param[1]←ylen; WriteCommand[p,ELShowRectangle,@param]; }; PutTextHere: PUBLIC PROC[p: LONG POINTER TO PressFileDescriptor, str: LONG STRING] = { s: Stream.Handle ← p.outStream; param: ARRAY [0..6) OF CARDINAL; IF p.numFonts = 0 THEN SetFont[p,"Gacha"L,8]; FOR i: CARDINAL IN [0..str.length) DO Stream.PutChar[s,str[i]]; ENDLOOP; param[0] ← str.length; WriteCommand[p,ELShowCharacters,@param]; }; PutRectangleHere: PUBLIC PROC [p: LONG POINTER TO PressFileDescriptor, xlen,ylen: CARDINAL] = { param: ARRAY [0..6) OF CARDINAL; param[0]←xlen; param[1]←ylen; WriteCommand[p,ELShowRectangle,@param]; }; ScanInitMem: PROC [mem: LONG POINTER, nWordsPerLine: CARDINAL ]={ nw ← nWordsPerLine; sl ← mem; }; nextScanLineMem: PROC RETURNS [LONG POINTER] = { mem: LONG POINTER ← sl; sl ← sl+nw; RETURN[mem]; }; PutAltoDots: PUBLIC PROC [p: LONG POINTER TO PressFileDescriptor, x,y,nDots,nScanLines: CARDINAL,dots: LONG POINTER] = { PutDots[p,x,y,nDots,nScanLines,0,nDots*32,nScanLines*32,dots]; }; PutDots: PUBLIC PROC [p: LONG POINTER TO PressFileDescriptor, x,y,nPixels,nScanLines,bitsPerPixel,width,height: CARDINAL,dots: LONG POINTER, screenFrequency: CARDINAL ← 0,screenAngle: CARDINAL ← 45] = { pdd: PressDefs.PressDotsData ← [ nBitsPerPixel: bitsPerPixel,nPixels: nPixels,nLines: nScanLines, passPixels: 0,displayPixels: nPixels,passLines: 0,displayLines: nScanLines, micaWidth: width,micaHeight: height, min: 0,max: 0, angle: screenAngle,frequency: screenFrequency, opaque: FALSE,haveDot: FALSE,mode: 3,fileName: NIL, dotPosition: ,diskPosition: ]; ScanInitMem[dots,nPixels/(16/MIN[1,bitsPerPixel])]; PutPressDotsData[p,x,y,@pdd,nextScanLineMem]; }; PutComputedDots: PUBLIC PROC [p: LONG POINTER TO PressFileDescriptor, x,y,nPixels,nScanLines,bitsPerPixel,width,height: CARDINAL, nextScanLine: PROC RETURNS [LONG POINTER], screenFrequency: CARDINAL ← 0,screenAngle: CARDINAL ← 45, min,max: CARDINAL ← 0]= { pdd: PressDefs.PressDotsData ← [ nBitsPerPixel: bitsPerPixel,nPixels: nPixels,nLines: nScanLines, passPixels: 0,displayPixels: nPixels,passLines: 0,displayLines: nScanLines, micaWidth: width,micaHeight: height, min: min,max: max, angle: screenAngle,frequency: screenFrequency, opaque: FALSE,haveDot: FALSE,mode: 3,fileName: NIL, dotPosition: ,diskPosition: ]; PutPressDotsData[p,x,y,@pdd,nextScanLine]; }; PutDotsFromFile: PUBLIC PROC [p: LONG POINTER TO PressFileDescriptor, x,y,nPixels,nScanLines,bitsPerPixel,width,height: CARDINAL,dots: LONG STRING, screenFrequency: CARDINAL ← 0,screenAngle: CARDINAL ← 45, min,max: CARDINAL ← 0] = { pdd: PressDefs.PressDotsData ← [ nBitsPerPixel: bitsPerPixel,nPixels: nPixels,nLines: nScanLines, passPixels: 0,displayPixels: nPixels,passLines: 0,displayLines: nScanLines, micaWidth: width,micaHeight: height, min: min,max: max, angle: screenAngle,frequency: screenFrequency, opaque: FALSE,haveDot: FALSE,mode: 3,fileName: dots, dotPosition: ,diskPosition: ]; PutPressDotsData[p,x,y,@pdd,]; }; PutPressDotsData: PUBLIC PROC [p: LONG POINTER TO PressFileDescriptor, x,y: CARDINAL,dotsData: LONG POINTER TO PressDefs.PressDotsData, nextScanLine: PROC RETURNS[LONG POINTER]] = { s: Stream.Handle ← p.outStream; pixelsPerWord: CARDINAL ← 16/MAX[dotsData.nBitsPerPixel,1]; displayPixels: CARDINAL ← IF dotsData.fileName=NIL THEN dotsData.displayPixels ELSE dotsData.nPixels; displayLines: CARDINAL ← IF dotsData.fileName=NIL THEN dotsData.displayLines ELSE dotsData.nLines; passPixels: CARDINAL ← IF dotsData.fileName#NIL THEN dotsData.passPixels ELSE dotsData.passPixels MOD pixelsPerWord; passLines: CARDINAL ← IF dotsData.fileName#NIL THEN dotsData.passLines ELSE 0; nWordsPerScanline: CARDINAL ← ((passPixels+displayPixels)*MAX[dotsData.nBitsPerPixel,1]+15)/16; nWords: LONG CARDINAL ← LONG[nWordsPerScanline]*displayLines; additionalWords: CARDINAL ← 0; MaxTable: ARRAY [0..8] OF CARDINAL ← [1,1,3,7,15,31,63,127,255]; param: ARRAY [0..6) OF CARDINAL; --set up scan lines to end on word boundaries nPixels: CARDINAL ← Inline.BITAND[passPixels+displayPixels+pixelsPerWord-1,-pixelsPerWord]; param[0]←x;WriteCommand[p,ELSetX,@param]; param[0]←y;WriteCommand[p,ELSetY,@param]; IF dotsData.displayPixels = 0 OR dotsData.displayLines = 0 THEN RETURN; IF Inline.BITAND[Inline.LowHalf[FileStream.GetIndex[s]],1] = 1 THEN { PutLowerByte[s,0];param[0]←1;WriteCommand[p,ELSkipCharactersShort,@param];}; PutLowerByte[s,DLSetCoding];PutLowerByte[s,dotsData.nBitsPerPixel]; [] ← WriteBlock[s,@nPixels,1];[] ← WriteBlock[s,@displayLines,1]; PutLowerByte[s,DLSetMode];PutLowerByte[s,dotsData.mode]; PutLowerByte[s,0];PutLowerByte[s,DLSetSize]; [] ← WriteBlock[s,@dotsData.micaWidth,1]; [] ← WriteBlock[s,@dotsData.micaHeight,1]; IF dotsData.frequency # 0 THEN { PutLowerByte[s,0];PutLowerByte[s,DLSetSamplingProperties]; PutLowerByte[s,0];PutLowerByte[s,7]; --7 words of property stuff PutLowerByte[s,0];PutLowerByte[s,DLSSPScreen]; [] ← WriteBlock[s,@dotsData.angle,1]; PutLowerByte[s,0];PutLowerByte[s,100]; [] ← WriteBlock[s,@dotsData.frequency,1]; PutLowerByte[s,0];PutLowerByte[s,DLSSPInputIntensity]; [] ← WriteBlock[s,@dotsData.min,1]; [] ← WriteBlock[s,IF dotsData.max=0 THEN @MaxTable[dotsData.nBitsPerPixel] ELSE @dotsData.max,1]; additionalWords ← additionalWords + 9; }; PutLowerByte[s,0];PutLowerByte[s,DLSetWindow]; [] ← WriteBlock[s,@passPixels,1]; [] ← WriteBlock[s,@dotsData.displayPixels,1]; [] ← WriteBlock[s,@passLines,1]; [] ← WriteBlock[s,@dotsData.displayLines,1]; additionalWords ← additionalWords + 5; PutLowerByte[s,0]; IF dotsData.fileName=NIL THEN { PutLowerByte[s,DLDotsFollow]; THROUGH [0..dotsData.passLines) DO [] ← nextScanLine[];ENDLOOP; THROUGH[0..dotsData.displayLines) DO [] ← WriteBlock[s,nextScanLine[]+dotsData.passPixels/pixelsPerWord, nWordsPerScanline]; ENDLOOP; param[0] ← Inline.HighHalf[nWords+8+additionalWords]; param[1] ← Inline.LowHalf[nWords+8+additionalWords]; } ELSE { dots: LONG STRING ← dotsData.fileName; nBytes: CARDINAL ← dots.length + (1-Inline.BITAND[dots.length,1]); found: BOOLEAN ← FALSE; FOR i: CARDINAL IN [0..nExternalFileStrings) DO IF LongString.EquivalentStrings[externalFileStrings[i],dots] THEN { found ← TRUE;EXIT;}; ENDLOOP; IF NOT found THEN { externalFileStrings[nExternalFileStrings] ← AllocateString[dots.length]; LongString.AppendString[from: dots, to: externalFileStrings[nExternalFileStrings]]; nExternalFileStrings ← nExternalFileStrings + 1; }; PutLowerByte[s,DLGetDotsFromFile]; PutLowerByte[s,0];PutLowerByte[s,4]; --record offset = 4 (1024 word header) PutLowerByte[s,dots.length]; FOR i: CARDINAL IN [0..nBytes) DO Stream.PutChar[s,dots[i]] ENDLOOP; param[0] ← 0; param[1] ← ((nBytes+1)/2)+9+additionalWords; }; WriteCommand[p, IF dotsData.opaque THEN ELShowDotsOpaque ELSE ELShowDots,@param]; }; NewEntitySegment: PROC [p: LONG POINTER TO PressFileDescriptor] = { esh,segmentQ: LONG POINTER TO entitySegment; offset: LONG CARDINAL ← LONG[p.RecordStart]*Environment.bytesPerPage; es: Stream.Handle ← LOOPHOLE[p.EntityCommands]; IF (p.EntityCommandLen MOD 2)=1 THEN { PutLowerByte[es,ELNop];p.EntityCommandLen←p.EntityCommandLen+1;}; esh ← AllocateSegment[SIZE[entitySegment]]; esh↑ ← [link: NIL,length: p.EntityCommandLen,beginByte:, endByte: FileStream.GetIndex[p.outStream]-offset]; IF entitySegmentHead = NIL THEN { esh.beginByte ← 0; entitySegmentHead ← esh; } ELSE { segmentQ ← entitySegmentHead; UNTIL segmentQ.link = NIL DO segmentQ ← segmentQ.link;ENDLOOP; segmentQ.link ← esh; esh.beginByte ← segmentQ.endByte; }; p.EntityCommandLen ← 0; }; WritePage: PUBLIC PROC [p: LONG POINTER TO PressFileDescriptor] = { s: Stream.Handle ← p.outStream; es: Stream.Handle ← LOOPHOLE[p.EntityCommands]; ECommand: EntityCommandTrailer; startPos,byteLen,entityLen: LONG INTEGER; padding,recordLen: INTEGER; segmentQWordLen: CARDINAL; segmentQ: LONG POINTER TO entitySegment; buffer: ARRAY [0..Environment.wordsPerPage) OF UNSPECIFIED; IF (FileStream.GetIndex[s] MOD 2)=1 THEN PutLowerByte[s,0]; startPos←p.RecordStart; startPos←startPos*Environment.bytesPerPage; byteLen ← FileStream.GetIndex[s] - startPos; --first, insert a zero word PutLowerByte[s,0];PutLowerByte[s,0]; entityLen←(byteLen/2)+1; NewEntitySegment[p]; Stream.SendNow[es]; FileStream.SetIndex[es, 0]; FOR segmentQ ← entitySegmentHead,segmentQ.link UNTIL segmentQ = NIL DO segmentQWordLen ← segmentQ.length/2; UNTIL segmentQWordLen = 0 DO [] ← ReadBlock[es,@buffer,MIN[Environment.wordsPerPage,segmentQWordLen]]; [] ← WriteBlock[s,@buffer,MIN[Environment.wordsPerPage,segmentQWordLen]]; segmentQWordLen ← segmentQWordLen-MIN[Environment.wordsPerPage,segmentQWordLen]; ENDLOOP; --THROUGH [1..segmentQ.length] DO PutLowerByte[s,Stream.GetWord[es]];ENDLOOP; startPos ← segmentQ.beginByte; byteLen ← segmentQ.endByte - startPos; ECommand ← [Type: 0, FontSet: 0, BeginByte: [Inline.HighHalf[startPos],Inline.LowHalf[startPos]], ByteLen: [Inline.HighHalf[byteLen],Inline.LowHalf[byteLen]],Xe: 0,Ye: 0, Left: 0,Bottom: 0,Width: MicaWidth,Height: MicaHeight, EntityLen: SIZE[EntityCommandTrailer] + segmentQ.length/2 ]; []←WriteBlock[s,@ECommand,SIZE[EntityCommandTrailer]]; entityLen←entityLen+ECommand.EntityLen; ENDLOOP; DO segmentQ ← entitySegmentHead; entitySegmentHead ← segmentQ.link; FreeSegment[segmentQ]; IF entitySegmentHead = NIL THEN EXIT; ENDLOOP; FileStream.SetIndex[es, 0]; --now, compute (and write) padding, update part directory padding←Inline.BITAND[Environment.wordsPerPage- Inline.BITAND[Inline.LowHalf[entityLen],377B],377B]; [recordLen,]←Inline.LDIVMOD[numlow: Inline.LowHalf[entityLen], numhigh: Inline.HighHalf[entityLen],den: Environment.wordsPerPage]; IF padding # 0 THEN recordLen←recordLen+1; []←WriteBlock[s,p,padding]; --write garbage padding p.partDir[p.numParts].Type←Page; --printed page p.partDir[p.numParts].RecordStart←p.RecordStart; p.partDir[p.numParts].RecordLength←recordLen; p.partDir[p.numParts].PaddingLength←padding; lastSetX←lastSetY←lastFont←lastSpaceX←lastSpaceY←p.CurHue←p.CurSat←p.CurBright←0; p.numParts←p.numParts+1; p.RecordStart←p.RecordStart+recordLen; }; WritePartDirectory: PUBLIC PROC [p: LONG POINTER TO PressFileDescriptor] = { padding,temp: INTEGER; FOR i: CARDINAL IN [0..p.numParts) DO []←WriteBlock[p.outStream,@p.partDir[i],4]; ENDLOOP; padding←(Environment.wordsPerPage-LOOPHOLE[(p.numParts MOD 64)*4,INTEGER]) MOD Environment.wordsPerPage; []←WriteBlock[p.outStream,p,padding]; temp←p.numParts; UNTIL temp <= 0 DO p.partLen←p.partLen+1; temp←temp-64; ENDLOOP; }; InitPressFileDescriptor: PUBLIC PROC [p: LONG POINTER TO PressFileDescriptor, filename: LONG STRING] = { entityCommandArray: Stream.Handle ← NewStream["EntityCommandList.$"L, File.read + File.write + File.grow + File.shrink + File.delete]; temp: STRING ← [40]; dateString: LONG STRING ← AllocateString[40]; userString: LONG STRING ← AllocateString[20]; pd: LONG POINTER TO PartDir ← AllocateSegment[SIZE[PartDir]]; fd: LONG POINTER TO FontDir ← AllocateSegment[SIZE[FontDir]]; nExternalFileStrings ← 0; --should be in PressFileDescriptor entitySegmentHead ← NIL; p.outStream←NewStream[filename,File.write + File.grow + File.shrink + File.delete]; p.EntityCommandLen←0; p.EntityCommands←LOOPHOLE[entityCommandArray]; p.RecordStart←0; p.fontLen←0; p.partLen←0; p.numParts←0; p.numFonts←0; p.fontDir←fd; p.partDir←pd; p.FirstCopy←1; p.LastCopy←1; p.solidCode←'t; --undefined: only 's or 't are meaningful p.PressFileName←filename; p.UserName←AllocateString[Profile.userName.length]; LongString.AppendString[p.UserName, Profile.userName]; Time.Append[temp,Time.Unpack[Time.Current[]]]; LongString.AppendString[dateString, temp]; p.DateString←dateString; p.CurSpaceX ← p.CurSpaceY ← 177777B; }; ClosePressFile: PUBLIC PROC [p: LONG POINTER TO PressFileDescriptor] = { i,j: CARDINAL; zero: CARDINAL ← 0; s: Stream.Handle ← p.outStream; es: Stream.Handle ← LOOPHOLE[p.EntityCommands]; FBuf: LONG POINTER TO PressDefs.FontEntry; IF p.EntityCommandLen # 0 THEN WritePage[p]; IF nExternalFileStrings > 0 THEN { nBytesOut: CARDINAL ← 0; str: LONG STRING; FOR i IN [0..nExternalFileStrings) DO str ← externalFileStrings[i]; PutLowerByte[s,str.length]; FOR j IN [0..str.length+1-Inline.BITAND[str.length,1]) DO Stream.PutChar[s,str[j]] ENDLOOP; nBytesOut ← nBytesOut + 1 + str.length+1-Inline.BITAND[str.length,1]; ENDLOOP; [] ← WriteBlock[s,@zero,(Environment.bytesPerPage-nBytesOut)/2]; p.partDir[p.numParts].Type←ExternalFileDirectory; p.partDir[p.numParts].RecordStart←p.RecordStart; p.partDir[p.numParts].RecordLength←1; p.partDir[p.numParts].PaddingLength←255; p.numParts←p.numParts+1; p.RecordStart ← p.RecordStart + 1; }; --WriteFontDirectory; FOR i IN [0..p.numFonts) DO FBuf ← p.fontDir[i]; Stream.PutWord[s,FBuf.EntryLength]; Stream.PutByte[s,FBuf.FontSet]; Stream.PutByte[s,FBuf.Font]; Stream.PutByte[s,FBuf.M]; Stream.PutByte[s,FBuf.N]; PutLowerByte[s,FBuf.Family.length]; FOR j IN [0..19) DO Stream.PutChar[s, IF j<FBuf.Family.length THEN FBuf.Family[j] ELSE 0C]; ENDLOOP; Stream.PutByte[s,FBuf.Face]; Stream.PutByte[s,FBuf.Source]; Stream.PutWord[s,FBuf.Size]; Stream.PutWord[s,FBuf.Rotation]; ENDLOOP; [] ← WriteBlock[s,@zero,Environment.wordsPerPage-(p.numFonts*16)]; --padding p.partDir[p.numParts].Type←Font; p.partDir[p.numParts].RecordStart←p.RecordStart; p.partDir[p.numParts].RecordLength←IF p.numFonts < 16 THEN 1 ELSE 2; p.partDir[p.numParts].PaddingLength←255; p.numParts←p.numParts+1; p.fontLen←1; WritePartDirectory[p]; WriteDirectory[p]; Stream.Delete[s]; Stream.Delete[es]; --SystemDefs.FreeSegment[p.EntityCommands]; FreeString[p.DateString]; FreeString[p.UserName]; FreeSegment[p.partDir]; }; --object commands, Written by Martin Newell, January 1980 Count: CARDINAL ← 0; --ought to be in the individual PressFileDescriptors StartOutline: PUBLIC PROC[p: LONG POINTER TO PressFileDescriptor, x0,y0: CARDINAL] = { s: Stream.Handle ← p.outStream; param: ARRAY [0..6) OF CARDINAL; IF Count#0 THEN { EndOutline[p]; Count ← 0; }; IF Inline.BITAND[Inline.LowHalf[FileStream.GetIndex[s]],1] = 1 THEN { PutLowerByte[s,0]; param[0]←1; WriteCommand[p,ELSkipCharactersShort,@param]; }; PutMoveTo[p,x0,y0]; }; PutMoveTo: PUBLIC PROC[p: LONG POINTER TO PressFileDescriptor, xend,yend: CARDINAL] = { s: Stream.Handle ← p.outStream; cmd: CARDINAL ← DLMoveTo; [] ← WriteBlock[s,@cmd,1]; [] ← WriteBlock[s,@xend,1]; [] ← WriteBlock[s,@yend,1]; Count ← Count + 3; }; PutDrawTo: PUBLIC PROC[p: LONG POINTER TO PressFileDescriptor, xend,yend: CARDINAL] = { s: Stream.Handle ← p.outStream; cmd: CARDINAL ← DLDrawTo; [] ← WriteBlock[s,@cmd,1]; [] ← WriteBlock[s,@xend,1]; [] ← WriteBlock[s,@yend,1]; Count ← Count + 3; }; PutCubic: PUBLIC PROC[p: LONG POINTER TO PressFileDescriptor, x1,y1,x2,y2,x3,y3: REAL] = { s: Stream.Handle ← p.outStream; cmd: CARDINAL ← DLDrawCurve; [] ← WriteBlock[s,@cmd,1]; [] ← WriteBlock[s,@x1,2]; [] ← WriteBlock[s,@y1,2]; [] ← WriteBlock[s,@x2,2]; [] ← WriteBlock[s,@y2,2]; [] ← WriteBlock[s,@x3,2]; [] ← WriteBlock[s,@y3,2]; Count ← Count + 13; }; EndOutline: PUBLIC PROC[p: LONG POINTER TO PressFileDescriptor] = { param: ARRAY [0..6) OF CARDINAL; param[0] ← Count; WriteCommand[p,ELShowObject,@param]; Count ← 0; }; -- compatibility routines for Pilot AllocateString: PUBLIC PROC[nchars: CARDINAL] RETURNS[s: LONG STRING] = { s ← Heap.systemZone.NEW[StringBody[nchars] ← StringBody[length: 0, maxlength: nchars, text:]]; }; FreeString: PUBLIC PROC[str: LONG STRING] = { IF str = NIL THEN RETURN; Heap.systemZone.FREE[@str]; }; AllocateSegment: PROC[nwords: CARDINAL] RETURNS[ptr: LONG POINTER] = { seq: TYPE = RECORD[ body: SEQUENCE maxsize: CARDINAL OF WORD ]; IF nwords > 32000 THEN ERROR; ptr ← Heap.systemZone.NEW[seq[nwords]]; }; FreeSegment: PUBLIC PROC[ptr: LONG POINTER] = { IF ptr = NIL THEN RETURN; Heap.systemZone.FREE[@ptr]; }; NewStream: PROC [name: LONG STRING, access: File.Permissions] RETURNS [Stream.Handle] = { cap: File.Capability; old: BOOLEAN ← FALSE; IF access ~= File.read THEN cap ← Directory.CreateFile[name, DCSFileTypes.tLeaderPage, 0 ! Directory.Error => { IF type = fileAlreadyExists THEN old ← TRUE ELSE ERROR; CONTINUE }] ELSE old ← TRUE; IF old THEN cap ← Directory.Lookup[fileName: name, permissions: Directory.ignore ! Directory.Error => ERROR]; cap ← Directory.UpdateDates[cap, access]; RETURN[FileStream.Create[cap]]; }; WriteBlock: PROC[sh: Stream.Handle, p: LONG POINTER, nwords: CARDINAL] = { Stream.PutBlock[sh, [p, 0, nwords * 2]]; }; ReadBlock: PROC[sh: Stream.Handle, p: LONG POINTER, nwords: CARDINAL] RETURNS[nbytesread: CARDINAL] = { [bytesTransferred: nbytesread] ← Stream.GetBlock[sh, [p, 0, nwords*2]]; }; Zero: PROC[p: LONG POINTER, nwords: CARDINAL] = { FOR i: CARDINAL IN [0 .. nwords) DO (p+i)↑ ← 0; ENDLOOP; }; PutLowerByte: PROC[sh: Stream.Handle, w: CARDINAL] = { Stream.PutByte[sh, Inline.LowByte[w]]; }; -- this testprogram may be called to test out changes to this Module TestProgram: PROC = { p: PressFileDescriptor; InitPressFileDescriptor[@p,"Play.PRESS"L]; PutText[@p,"Default font"L,2540,2540*9]; SetFont[@p,"Helvetica"L,10]; PutText[@p,"Helvetica 10 here"L,2540,2540*8]; SetFont[@p,"Gacha"L,8]; PutText[@p,"Gacha 8 here"L,2540,2540*7]; SetFont[@p,"HelVetiCa"L,10]; PutText[@p,"Helvetica 10 here"L,2540,2540*6]; IF TRUE THEN { xstart,ystart,xlen,ylen: CARDINAL; MicaMarg: INTEGER = 2540/2; FOR i: CARDINAL IN [0..6) DO SetColor[@p,i*40,255,255]; xlen←(MicaWidth-3*MicaMarg)/2; ylen←(MicaHeight-4*MicaMarg)/3; xstart←MicaMarg+(i MOD 2)*(xlen+MicaMarg); ystart←MicaMarg+(i/2)*(ylen+MicaMarg); PutRectangle[@p,xstart,ystart,xlen,ylen]; ENDLOOP; }; ClosePressFile[@p]; }; }.