-- StreamImpl.mesa (last edited by: Gobbel on: 20-Mar-81 14:28:24) DIRECTORY BitBlt USING [AlignedBBTable, BITBLT, BBptr, BBTableSpace], ByteBlt USING [], Environment USING [bitsPerWord, bitsPerByte, Block, Byte, bytesPerWord], Inline USING [LongCOPY], MiscPrograms USING [], PrincOps USING [UnboundLink], Stream USING [Block, Byte, CompletionCode, defaultInputOptions, Object, Handle, InputOptions, SubSequenceType, Word], SystemInternal USING [Unimplemented]; StreamImpl: PROGRAM IMPORTS Inline, BitBlt, SystemInternal EXPORTS ByteBlt, MiscPrograms, Stream = BEGIN OPEN Stream; --MiscPrograms.--InitializeStream: PUBLIC PROCEDURE = BEGIN -- may be something here someday -- END; EndOfStream: PUBLIC SIGNAL [nextIndex: CARDINAL] = CODE; LongBlock: PUBLIC SIGNAL [nextIndex: CARDINAL] = CODE; ShortBlock: PUBLIC ERROR = CODE; SSTChange: PUBLIC SIGNAL [sst: SubSequenceType, nextIndex: CARDINAL] = CODE; TimeOut: PUBLIC SIGNAL [nextIndex: CARDINAL] = CODE; LeftAndRight: TYPE = MACHINE DEPENDENT RECORD [left, right: Environment.Byte]; -- -- Stream implementation (defaults) defaultObject: PUBLIC Object ← [ options: defaultInputOptions, getByte: DefaultGetByte, putByte: DefaultPutByte, getWord: DefaultGetWord, putWord: DefaultPutWord, get: DefaultGet, put: DefaultPut, setSST: LOOPHOLE[PrincOps.UnboundLink], sendAttention: LOOPHOLE[PrincOps.UnboundLink], waitAttention: LOOPHOLE[PrincOps.UnboundLink], delete: LOOPHOLE[PrincOps.UnboundLink]]; DefaultGetByte: PROCEDURE [sH: Handle] RETURNS [byte: Byte] = -- Get one byte, using GetBlock on a one-byte block BEGIN array: PACKED ARRAY [0..1] OF Byte; options: InputOptions = [FALSE, FALSE, FALSE, TRUE, TRUE]; IF sH.get=DefaultGet THEN ERROR SystemInternal.Unimplemented; [] ← sH.get[sH, Block[@array, 1, 2], options]; RETURN[array[1]] -- right-justify the byte END; DefaultGetWord: PROCEDURE [sH: Handle] RETURNS [word: Word] = -- Get one word using GetBlock on a two-byte block BEGIN OPEN w: LOOPHOLE[word, LeftAndRight]; options: InputOptions = [FALSE, FALSE, FALSE, TRUE, TRUE]; SELECT TRUE FROM sH.getByte~=DefaultGetByte => BEGIN w.left ← sH.getByte[sH]; w.right ← sH.getByte[sH]; END; sH.get~=DefaultGet => [] ← sH.get[sH, Block[@word, 0, 2], options]; ENDCASE => ERROR SystemInternal.Unimplemented; END; DefaultGet: PROCEDURE [sH: Handle, block: Block, options: InputOptions] RETURNS [bytesTransferred: CARDINAL, why: CompletionCode, sst: SubSequenceType] = -- Get block, using GetByte repeatedly BEGIN i: CARDINAL; sstNew: SubSequenceType; IF sH.getByte=DefaultGetByte OR options.terminateOnEndPhysicalRecord OR options.signalLongBlock OR options.signalShortBlock THEN ERROR SystemInternal.Unimplemented; why ← normal; i ← block.startIndex; WHILE i<block.stopIndexPlusOne DO LOOPHOLE[block.blockPointer, LONG POINTER TO PACKED ARRAY [0..0) OF Byte][i] ← sH.getByte[sH ! SSTChange => IF ~options.signalSSTChange THEN BEGIN sstNew ← sst; GO TO PostSSTChange END ELSE BEGIN SIGNAL SSTChange[sst: sst, nextIndex: i]; RESUME END; EndOfStream => IF ~options.signalEndOfStream THEN GO TO PostEndOfStream ELSE BEGIN SIGNAL EndOfStream[nextIndex: i]; RESUME -- why is this useful? -- END]; i ← i+1; REPEAT PostSSTChange => why ← sstChange; PostEndOfStream => why ← endOfStream; ENDLOOP; bytesTransferred ← i-block.startIndex; sst ← sstNew END; DefaultPutByte: PROCEDURE [sH: Handle, byte: Byte] = -- Put one byte, using PutBlock on a one-byte block BEGIN array: PACKED ARRAY [0..1] OF Byte ← [0, byte]; IF sH.put=DefaultPut THEN ERROR SystemInternal.Unimplemented; sH.put[sH, Block[@array, 1, 2], FALSE]; END; DefaultPutWord: PROCEDURE [sH: Handle, word: Word] = -- Put one word, using PutBlock on a one-byte block BEGIN OPEN w: LOOPHOLE[word, LeftAndRight]; SELECT TRUE FROM sH.putByte~=DefaultPutByte => BEGIN sH.putByte[sH, w.left]; sH.putByte[sH, w.right]; END; sH.put~=DefaultPut => sH.put[sH, Block[@word, 0, 2], FALSE]; ENDCASE => ERROR SystemInternal.Unimplemented; END; DefaultPut: PROCEDURE [sH: Handle, block: Block, endPhysicalRecord: BOOLEAN] = -- Put block, using PutByte repeatedly BEGIN i: CARDINAL; IF sH.putByte=DefaultPutByte OR endPhysicalRecord THEN ERROR SystemInternal.Unimplemented; FOR i IN [block.startIndex..block.stopIndexPlusOne) DO sH.putByte[sH, LOOPHOLE[block.blockPointer, LONG POINTER TO PACKED ARRAY [0..0) OF Byte][i]]; ENDLOOP; END; -- -- ByteBlt implementation useBitBlt: BOOLEAN ← TRUE; ByteBlt: PUBLIC PROCEDURE [to, from: Environment.Block] RETURNS [nBytes: CARDINAL] = -- Byte-boundary block transfer BEGIN -- NB: to&from are RECORDs, not POINTERs to RECORDs, so we can update them toBytes, fromBytes: LONG POINTER TO PACKED ARRAY [0..0) OF [0..377B); moved: CARDINAL ← 0; -- This check is necessary since subtracting CARDINALs gives big numbers IF to.startIndex>to.stopIndexPlusOne OR from.startIndex>from.stopIndexPlusOne THEN ERROR StartIndexGreaterThanStopIndexPlusOne; IF (nBytes ← MIN[to.stopIndexPlusOne-to.startIndex,from.stopIndexPlusOne-from.startIndex])=0 THEN RETURN; toBytes ← to.blockPointer; fromBytes ← from.blockPointer; -- Move the first odd byte (if any) to be sure that to is word aligned IF (to.startIndex MOD 2)#0 THEN BEGIN toBytes[to.startIndex] ← fromBytes[from.startIndex]; moved ← 1; to.startIndex ← to.startIndex+1; from.startIndex ← from.startIndex+1; END; IF (from.startIndex MOD 2)=0 THEN -- Fast case: both are word aligned BEGIN words: CARDINAL = (nBytes-moved)/2; Inline.LongCOPY[to: toBytes+to.startIndex/2, from: fromBytes+from.startIndex/2, nwords: words]; IF (moved+2*words)#nBytes THEN -- Move the one and only remaining byte toBytes[to.startIndex+2*words] ← fromBytes[from.startIndex+2*words]; END -- Slow case: have to ripple things ELSE IF ~useBitBlt THEN BEGIN i: CARDINAL; count: CARDINAL = nBytes - moved; FOR i IN [0..count) DO toBytes[to.startIndex+i] ← fromBytes[from.startIndex+i]; ENDLOOP; END ELSE -- BitBlt is not interruptable except at the end of each scan line, so we break things up into chunks in order to maintain reasonable interrupt latency for the IO devices. It takes about 200microsec to move 50 bytes with the display off. BEGIN bba: BitBlt.BBTableSpace; bbt: BitBlt.BBptr = BitBlt.AlignedBBTable[@bba]; lineWidth: CARDINAL = 16; -- words per scan line: controls interrupt latency bitsPerLine: CARDINAL = lineWidth*Environment.bitsPerWord; bytesPerLine: CARDINAL =lineWidth*Environment.bytesPerWord; lines: CARDINAL = (nBytes-moved)/bytesPerLine; -- bytes left to move with first BitBlt tail: CARDINAL = (nBytes-moved) MOD bytesPerLine; -- bytes left to move with second BitBlt bbt↑ ← [ dst: [word: toBytes+to.startIndex/2, bit: 0], dstBpl: bitsPerLine, src: [word: fromBytes+from.startIndex/2, bit: 8], srcDesc: [srcBpl[bitsPerLine]], width: bitsPerLine, height: lines, flags: [direction: forward, disjoint: TRUE, disjointItems: TRUE, gray: FALSE, srcFunc: null, dstFunc: null]]; -- This BitBlt moves a rectangle that is lineWidth words wide by as many lines high as will fit. NB: It cheats and actually reads a byte from beyond the edge of the rectangle. This is not really legal, but works out OK for any reasonable inplementation of BitBlt. IF lines#0 THEN BitBlt.BITBLT[bbt]; -- update the pointers to reflect the work done, and then move one line that is less than lineWidth words wide. bbt.dst.word ← bbt.dst.word + lines*lineWidth; bbt.src.word ← bbt.src.word + lines*lineWidth; bbt.width ← Environment.bitsPerByte*tail; bbt.height ← 1; IF tail#0 THEN BitBlt.BITBLT[bbt]; END; END; StartIndexGreaterThanStopIndexPlusOne: PUBLIC ERROR = CODE; END. LOG Time: April 11, 1980 6:37 PM By: Forrest Action: Trimmed log to Amargosa; Converted ByteBlt to use PrincOps BitBlt Time: April 14, 1980 8:39 AM By: Knutsen Action: Module STARTed by InitializeStream. Time: April 16, 1980 9:07 PM By: Forrest Action: Stopped using BitBlt until it is debugged. Time: April 28, 1980 10:12 AM By: Forrest Action: ControlDefs => PrincOps. ream. Time: April 16, 1980 9:07 PM By: Forrest Action: Stopped using BitBlt until it is debugged. Time: April 28, 1980 10:12 AM By: Forrest Action: ControlDefs => PrincOps. Time: 9-Mar-81 18:22:25 By: Gobbel Action: useBitBlt ← TRUE.