-- MesaBandsImpl.mesa
-- Last changed by Ken Pier,           March 30, 1982  10:47 AM
DIRECTORY
  MesaBands, 
  MesaBandStream USING [InitBandFileBuffer, GetBand],
  MesaBandFormat USING [Bit, Type, Marker, TBrickRef, tBrickSize,
                        Run, runSize, HeraldRef, HeraldObject,
                        heraldObjectSize, Line, LineRef,
                        ObjectRef, Object, objectSize],
  MesaBrickBLT USING [InitializeBrickBLT,  BrickBLT],
  PressBandsDefs USING [StoreScanLine],
  PressNetDefs USING [PageAttributes, ScanDirection],
  Inline USING [LowHalf, HighHalf, LongMult, LongCOPY],
  DynamicZone USING [InitializeZones, SystemZone],
  MiscDefs USING [CallDebugger],
  SegmentDefs USING [NewDataSegment, DefaultXMBase, LongVMtoDataSegment,
                     DeleteDataSegment, LongDataSegmentAddress],
  BitBltDefs USING [BBTableSpace, BBptr, BITBLT];

MesaBandsImpl: PROGRAM
IMPORTS MesaBandStream, MesaBrickBLT, PressBandsDefs,
	Inline, BitBltDefs, SegmentDefs, DynamicZone, MiscDefs
EXPORTS MesaBands = {
OPEN BStream: MesaBandStream, BFormat: MesaBandFormat,
     BB: BitBltDefs, Seg: SegmentDefs,
     PB: PressBandsDefs, PN: PressNetDefs,
     DZ: DynamicZone, MiscDefs, MesaBands;

bitsPerWord: CARDINAL = 16;
wordsPerPage: CARDINAL = 256;
debug: BOOLEAN = TRUE;

Error: SIGNAL = CODE;

myMark: BFormat.Marker;-- = 98765432

Assert: PROC[pred: BOOLEAN] = INLINE {
  IF debug AND ~pred THEN CallDebugger["FalseAssertion"L]
  };

Data: TYPE =   POINTER TO DataRep;
DataRep: TYPE = RECORD [
  device: BandDevice,
  base: LONG POINTER, -- base address of bitmap in upper bank
  pages: [0..254], -- pages in bitmap
  rast: CARDINAL, -- bitmap words per line
  lines: CARDINAL -- bitmap lines
  ];


New: PUBLIC PROC [bDevice: BandDevice] RETURNS [Data] = {

  data ← uz.NEW[DataRep ← [device: bDevice, base: NIL, pages: 0,
                            rast: 0, lines: 0]];
  InitBands[data];--verifies device type match
  [data.base, data.pages] ← AcquireBandBitmap[data];
  RETURN[data];
  };

InitBands: PROC [data: Data] = {

  wordsRead: CARDINAL ← 0;
  BStream.InitBandFileBuffer[];
  wordsRead ← BStream.GetBand[heraldRef, BFormat.heraldObjectSize];
  Assert[wordsRead=BFormat.heraldObjectSize];
  Assert[heraldRef.mark=myMark];
  Assert[heraldRef.device=data.device];
  data.lines ← heraldRef.bandCount*heraldRef.bandHeight;
  data.rast ← heraldRef.bandWidth;
  Assert[data.rast*data.lines<65024D];--one bank less two pages
  runsRef ← uz.NEW[RunsBuffer[heraldRef.bandHeight]];
  lineRef ← uz.NEW[BFormat.Line[maxLx2+(heraldRef.bandWidth*bitsPerWord)]];--brickline limit is (L+xmax+L-1) bits
  };--InitBands
  
AcquireBandBitmap: PROC[data: Data] RETURNS [lp: LONG POINTER, pages: CARDINAL ← 0] = {

  SELECT data.device FROM
     IN [hornet..end] =>  {
       longrast, longtemp, longDiv: LONG CARDINAL;--KLUDGE
       longDiv ← heraldRef.bandCount*wordsPerPage;
       longrast ← data.rast;
       longtemp ← ((longrast*data.lines)+longDiv-1)/longDiv;
  -- number of words per image (+normalize for arithmetic)/(256 words per diskpage * # bands per image)
       Assert[Inline.HighHalf[longtemp]=0];
       pages ← Inline.LowHalf[longtemp];
       lp ← Seg.LongDataSegmentAddress[Seg.NewDataSegment[Seg.DefaultXMBase, pages]];
       };
    screen  => ERROR;
    ENDCASE => ERROR;
  };--AcquireBandBitmap

SpaceZero: PROC[base: LONG POINTER, pages: CARDINAL] = {

  Inline.LongCOPY[from: longZAddress, nwords: 1, to: base];
  Inline.LongCOPY[from: base, nwords: (pages*wordsPerPage)-1, to: base+1];
  };--SpaceZero   
       
Close: PUBLIC PROC[data: Data] = {
  Seg.DeleteDataSegment[Seg.LongVMtoDataSegment[data.base]];
  data.base ← NIL;
  uz.FREE[@lineRef]; lineRef ← NIL;
  uz.FREE[@runsRef]; runsRef ← NIL;
  };--Close
  
 -- Modd copied from CGBrickImpl
Modd: PROC[x, y: LONG INTEGER] RETURNS [LONG INTEGER] = INLINE {
  RETURN [IF x >= 0 THEN  (x MOD y) ELSE ((y-1) + (x+1) MOD y)];
  };--Modd

GetNextOb: PROC [bandIndex: CARDINAL, objRef: BFormat.ObjectRef] = {
  --GetNextOb takes a local buffer and puts an object head 
  --into the buffer, then fills in globals RunsBuffer, tBrick buffer,  as appropriate.
  
  OPEN BFormat;
    wordsRead, runWords, brickWords: CARDINAL ← 0;
    wordsRead ← BStream.GetBand[to: objRef, words: objectSize];
    Assert[ wordsRead=objectSize];
    Assert[objRef.mark=myMark];
    IF objRef.flags.type=end THEN RETURN;
    Assert[objRef.band=bandIndex ];
    
    runWords ← IF objRef.flags.rect THEN BFormat.runSize ELSE BFormat.runSize*objRef.height;
    wordsRead ← BStream.GetBand[to: runsRef, words: runWords];--read runs
    Assert[ wordsRead=runWords];
    SELECT objRef.flags.type FROM
      all0, all1, bits => RETURN; --no data for allx, scanlines later for bits 
      brick => { --tBrick and runs. Read brick.
        wordsRead ← BStream.GetBand[to: tBrick, words: tBrickSize];
        Assert[ wordsRead=tBrickSize];
        brickWords ← (tBrick.brickSize + bitsPerWord-1)/bitsPerWord;
        wordsRead ← BStream.GetBand[to: brickBuffer, words: brickWords];
        Assert[ wordsRead=brickWords];
        Assert[ maxL>tBrick.L];
	RETURN  };--tBrick
      fontcache, herald, end => ERROR;
      ENDCASE => ERROR;
    };--GetNextOb
        
ShowBandsInit: PUBLIC PROC[bDevice: BandDevice] RETURNS[p: PN.PageAttributes] = {
    scanDirection: PN.ScanDirection;
    data ← New[bDevice];
    scanDirection ← SELECT bDevice FROM
      hornet => landscape,
      platemaker => portrait,
      reticlemaker => landscape,
      ENDCASE => ERROR;
    p ← [scanDirection: scanDirection, filler: , firstScan: 0,
          lastScan: data.lines-1, margin: 0, bitWc: data.rast];
    };--ShowBandsInit

ShowBands: PUBLIC PROC [] = {

  lptr: LONG POINTER ← NIL;
  yOffset ← 0;
  FOR bI: CARDINAL IN [0..heraldRef.bandCount) DO 
      SpaceZero[data.base, data.pages];
  
  --GetNextOb takes a band and a buffer and puts an object head for this band
  --into the buffer, then fills in globals RunsBuffer, tBrick buffer, Scanline
  -- as appropriate.
  
    DO
      GetNextOb[bandIndex: bI, objRef: objectRef];
      SELECT objectRef.flags.type FROM
        all0, all1, bits, brick =>  Render[data: data];
        herald => ERROR;
        end => EXIT;
        ENDCASE=> ERROR;
      ENDLOOP;--for each trap UNTIL end of band

    --write out the completed band buffer to the bits file
    lptr ← data.base;
    THROUGH [0..heraldRef.bandHeight) DO
      PB.StoreScanLine[lptr];
      lptr ← lptr+data.rast;
      ENDLOOP;
    
    yOffset ← yOffset + heraldRef.bandHeight;-- band relative baseline
    ENDLOOP;--for each band in [0..bandcount)

  Close[data];--release the band segment
  };--ShowBands

Render: PROC [data: Data] = {
  rect: BOOLEAN ← objectRef.flags.rect;
  type: BFormat.Type ← objectRef.flags.type;
  y, yTop, height, bandWidth, grayWord: CARDINAL;
  longline: LONG POINTER TO WORD;
  run: POINTER TO BFormat.Run;
  runBump: CARDINAL;
  black: CARDINAL = LAST[CARDINAL];
  white: CARDINAL = 0;

  y ← yTop ← objectRef.yStart-yOffset;
  height ← objectRef.height;
  bandWidth ← heraldRef.bandWidth; 
  -- pointer to beginning of first destination scan line
  longline ← data.base + Inline.LongMult[yTop, data.rast];
  run ← LOOPHOLE[runsRef]; -- pointer to first Run
  -- rectangles use the same Run for each line
  -- trapezoids have a new Run for each line
  runBump ← IF rect THEN 0 ELSE BFormat.runSize;
  
  SELECT type FROM
    all0, all1 => { -- constant white or black
      grayWord ← IF type = all1 THEN black ELSE white;
      bbPointer↑ ← [
        ptrs: short,
        pad: 0,
        sourcealt: FALSE,
        destalt:  TRUE,
        sourcetype:  gray,
        function:  replace,
        unused: Inline.HighHalf[data.base], --NOT REALLY UNUSED
        dbca: Inline.LowHalf[data.base],
        dbmr:  bandWidth, -- destination raster width(in words)
        dlx:  run.xmin, -- destination left x
        dty:  y, -- destination top y
        dw:   run.xmax-run.xmin,--may be zero !!  block width in bits
        dh:   height, -- block height in scanlines  may be zero !!  
        sbca: NIL, -- no source
        sbmr:   0, -- source raster width(in words)
        slx:    0, -- source left x
        sty:    0, -- source top y
        gray0:  grayWord, -- four words of "gray"
        gray1:  grayWord,
        gray2:  grayWord,
        gray3:  grayWord, 
        slbca:  NIL, -- ignored
        dlbca:  NIL]; -- ignored
      IF rect THEN  BB.BITBLT[bbPointer]    --BLT a rectangle in one shot!!
      ELSE  {--BLT a trapezoid one line at a time
        bbPointer.dh ← 1;
	THROUGH [0..height) DO
            bbPointer.dlx ← run.xmin; -- destination left x
            bbPointer.dty ← y; -- destination top y
            bbPointer.dw ← run.xmax-run.xmin; --may be zero !!  block width bits
	  BB.BITBLT[bbPointer];
	  run ← run + runBump;
	  y ← y+1;--index of next line
	  ENDLOOP;
        };--BLT a trapezoid one line at a time
      }; -- constant white or black
    
    bits => { -- specified bits (opaque), fetch each line and BLT it
       bbPointer↑ ← [
         ptrs: short,
         pad: 0,
         sourcealt: FALSE,
         destalt:  TRUE,
         sourcetype:  block,
         function:  replace,
         unused: Inline.HighHalf[data.base], --NOT REALLY UNUSED
         dbca: Inline.LowHalf[data.base],
         dbmr:  bandWidth, -- destination raster width(in words)
         dlx:  0, -- destination left x
         dty:  0, -- destination top y
         dw:   0, -- block width in bits
         dh:   1, -- block height in scanlines
         sbca: lineRef,
         sbmr:   0, -- source raster width(UNUSED for height=1)
         slx:    0, -- source left x
         sty:    0, -- source top y
         slbca:  NIL, -- ignored
         dlbca:  NIL]; -- ignored

      THROUGH [0..height) DO--BLT either trap or rect one line at a time
        [] ← BStream.GetBand[lineRef,
                     (run.xmax-run.xmin+15)/bitsPerWord];--may fetch 0 words
        bbPointer.dlx ← run.xmin; -- destination left x
        bbPointer.dty ← y; -- destination top y
        bbPointer.dw ← run.xmax-run.xmin; -- block width in bits
        BB.BITBLT[bbPointer];
        run ← run + runBump;
        y ← y + 1;--index of start of next line
        ENDLOOP;
      };-- specified bits (opaque), fetch each line and BLT it

    brick => { -- construct each line and BLT it
      L, p, hx, hy: CARDINAL;
      D: INTEGER ← 0;
      L ← tBrick.L; p ← tBrick.p; D  ← tBrick.D;
      y ← objectRef.yStart; -- absolute, not relative y
      THROUGH [0..height) DO
        hy ← y MOD p;
        hx ← Inline.LowHalf[Modd[(run.xmin - LONG[D]*(y/p)), L]];
        MesaBrickBLT.BrickBLT[bbptr: bbPointer, tBrick: tBrick,
	       destLine: longline, hx: hx, hy: hy,
	       xmin: run.xmin, xmax: run.xmax, lineBuffer: lineRef];
        run ← run + runBump;
        y ← y+1;--index of start of next line absolute
        longline ← longline + bandWidth;
        ENDLOOP;
      };-- construct each line and BLT it

    ENDCASE => ERROR;
  };--Render
  

--START CODE FOR MesaBandImageImpl

uz: MDSZone;
data: Data ← NIL;
yOffset: CARDINAL ← 0;

-- global buffering requiring  POINTERS
heraldRef: BFormat.HeraldRef;
heraldSizeX2: CARDINAL ← 2*BFormat.heraldObjectSize;
objectRef: BFormat.ObjectRef;
zeroWord: CARDINAL ← 0;
longZAddress: LONG POINTER ← LONG[@zeroWord]; -- see SpaceZero


TBrick: TYPE = RECORD[SEQUENCE COMPUTED CARDINAL OF WORD];--buffer for tBrick words coming from stream
maxTBrick: CARDINAL = 400+BFormat.tBrickSize;--6400 bits maximum in a tBrick for now
TBrickPtr: TYPE = POINTER TO TBrick;
brickBuffer: TBrickPtr;
tBrick: BFormat.TBrickRef; 

RunsBuffer: TYPE = RECORD[SEQUENCE COMPUTED CARDINAL OF BFormat.Run];
RunsRef: TYPE = POINTER TO RunsBuffer;
runsRef: RunsRef ← NIL; -- initialized by InitBands
runsIndex: CARDINAL ← 0; -- runs buffer index

maxL: CARDINAL = 2000;--longest permitted L for a thresholded brick 
maxLx2: CARDINAL = maxL*2;--longest permitted L for a thresholded brick times 2
lineRef:  BFormat.LineRef ← NIL; -- initialized by InitBands
lineIndex: CARDINAL ← 0;-- Scanline bit index

BBTable: BB.BBTableSpace;
bbPointer: BB.BBptr;

DZ.InitializeZones[];
uz ← DZ.SystemZone[];
heraldRef ← uz.NEW[BFormat.HeraldObject];
objectRef ← uz.NEW[BFormat.Object[1]];
brickBuffer ← uz.NEW[TBrick[maxTBrick]];
bbPointer ← MesaBrickBLT.InitializeBrickBLT[@BBTable];

--fix up tBrick from brickBuffer
tBrick ← LOOPHOLE[brickBuffer];--common storage covering tBHead+brickBuffer
brickBuffer ← brickBuffer+BFormat.tBrickSize;

}. 
LOG
March 11, 1982  changed everything to SHORT POINTERS
March 30, 1982  changed everything to use Assert