-- cbitfns.mesa (used in Chipmonk)
-- last modified by E. McCreight,  December 20, 1982  10:24 AM

DIRECTORY
    BitBltDefs,
    GeneralDisplayDefs,
    multiGraphicsDefs,
    InlineDefs,
    SystemDefs,
    SegmentDefs,
    ZoneAllocDefs;

cbitfns: MONITOR IMPORTS BitBltDefs, InlineDefs, multiGraphicsDefs,
  SegmentDefs, SystemDefs, ZoneAllocDefs
 EXPORTS multiGraphicsDefs =
BEGIN OPEN GeneralDisplayDefs,multiGraphicsDefs,InlineDefs;

Machine: SegmentDefs.MachineType = SegmentDefs.GetMemoryConfig[].AltoType;
DMachine: BOOLEAN = (Machine = D0) OR (Machine = Dorado);
Dor: BOOLEAN = Machine = Dorado;

Screen: BitmapPtr ← NIL;

--data structures for D0 style color display

colorData: TYPE = MACHINE DEPENDENT RECORD
 [    addr: [0..16),
    r,g,b,unused: BOOLEAN,
    data: [0..256)
 ];
colorEntry: TYPE = RECORD
 [    red,green,blue: colorData
 ];
colorTable: TYPE = RECORD --must be hexAligned
 [    front: ARRAY [0..2) OF UNSPECIFIED,
    entry: ARRAY [0..16) OF colorEntry,
    back: ARRAY [0..10) OF UNSPECIFIED,
    hexAlignPad: ARRAY [0..16) OF UNSPECIFIED
 ];

--data structures for D1 style color display from GeneralDisplayDefs

--global variables
ColorBitmap: POINTER TO POINTER = LOOPHOLE[414B];
ColorBitmapBank: POINTER TO CARDINAL = LOOPHOLE[415B];
ColorMap: POINTER TO POINTER = LOOPHOLE[416B];
ColorMapBank: POINTER TO CARDINAL = LOOPHOLE[417B];
ColorCtrlBlk: ColorControlBlock;
AChanCtrlBlk: ChannelControlBlock;
ColorMonitor: MonitorControlBlock;

ColorTablePlace:CARDINAL=26250B;

ColorTable: LONG POINTER TO colorTable ← LOOPHOLE[1200000B+ColorTablePlace];
ColorTable8: POINTER TO ATableImage;

ColorWord: CARDINAL ← 0;

freeBLTBlocks: BLTBlockPtr ← NIL;
mdsz: MDSZone = ZoneAllocDefs.GetTheMDSZone[];

AllocateBLT: PUBLIC ENTRY PROC RETURNS[bb: BLTBlockPtr] =
  BEGIN
  IF freeBLTBlocks#NIL THEN
    {bb ← freeBLTBlocks; freeBLTBlocks ← freeBLTBlocks.next}
  ELSE
    BEGIN
    bb ← mdsz.NEW[BLTBlock];
    IF LOOPHOLE[@bb.blk, CARDINAL] MOD 2 # 0 THEN -- even-word align
      bb ← LOOPHOLE[LOOPHOLE[bb, CARDINAL]+1, BLTBlockPtr];
    bb.next ← NIL;
    bb.blk ← [ptrs: IF DMachine THEN long ELSE short,
      pad: 0, sourcealt: FALSE, sourcetype: gray,
      dbca: NIL,
      gray0: 177777B, gray1: 177777B, gray2: 177777B, gray3:177777B,
       sbca: NIL, unused: 0, sbmr: 0];
    END;
  END;

FreeBLT: PUBLIC ENTRY PROC [bb: BLTBlockPtr] =
  {bb.next ← freeBLTBlocks; freeBLTBlocks ← bb};


SetColorTable: PUBLIC PROC [new: POINTER TO ARRAY [0..0) OF CARDINAL,
outputDevice: BitmapPtr ← NIL] =
BEGIN
IF outputDevice = NIL THEN outputDevice ← Screen;
IF NOT Dor THEN
        BEGIN -- a Dolphin
        i: CARDINAL;
        ColorTable↑.front ← ALL[0];
        ColorTable↑.back ← ALL[0];
        FOR i IN [0..17B] DO
            ColorTable↑.entry[i].red ← [i,TRUE,FALSE,FALSE,FALSE,new[i*3]];
            ColorTable↑.entry[i].green ← [i,FALSE,TRUE,FALSE,FALSE,new[i*3+1]];
            ColorTable↑.entry[i].blue ← [i,FALSE,FALSE,TRUE,FALSE,new[i*3+2]];
        ENDLOOP;
        END
    ELSE
        BEGIN -- a Dorado
        i: CARDINAL;
        FOR i IN [0..1024) DO
            ColorTable8[i].zeroHigh ← ColorTable8[i].zeroLow ← 0;
        ENDLOOP;
        FOR i IN [0..16) DO
            ColorTable8[i*4] ← ColorTable8[i*4*16] ←
                [0,(255-new[i*3]) MOD 16,(255-new[i*3+2]),0,(255-new[i*3+1]),(255-new[i*3])/16];
        ENDLOOP;
        pMonitorHead.Flags ← [0,FALSE,FALSE,FALSE,FALSE,TRUE,TRUE,TRUE];
        END;
END;

halftoneWord: TYPE = MACHINE DEPENDENT RECORD
 [    unused: [0..7777B],
    val1,val2,val3,val4: BOOLEAN
 ];

GetColorGray: PUBLIC PROC [c: Color] RETURNS[GrayPattern] =
BEGIN
IF c>255 THEN
    BEGIN -- two colors, in alternation
    combin: CARDINAL = BITAND[255,c];
    pat1: CARDINAL = combin + BITSHIFT[combin,8];
    pat2: CARDINAL = BITOR[BITSHIFT[pat1,4],BITSHIFT[combin,-4]];
    RETURN[[pat1,pat2,pat1,pat2,pat1,pat2,pat1]];
    END
ELSE
  BEGIN
  pixCol: CARDINAL = BITAND[c, 15];
  halfcw: CARDINAL = BITOR[BITSHIFT[pixCol, 4], pixCol];
  cw: CARDINAL = BITOR[BITSHIFT[halfcw, 8], halfcw]; -- pixCol replicated four times
  SELECT c-pixCol FROM
    0 => RETURN[ALL[cw]]; -- every pixel set to pixCol 
    ENDCASE =>
      BEGIN
      m: GrayPattern ← GetMaskGray[c];
      FOR i: GrayPatternIndex IN GrayPatternIndex DO
        m[i] ← BITAND[m[i], cw];
        ENDLOOP;
      RETURN[m];
      END;
  END;
END;

GetMaskGray: PUBLIC PROC [c: Color] RETURNS[GrayPattern] =
BEGIN
pixCol: [0..16) = BITAND[c, 15];
RETURN[SELECT c-pixCol FROM
  16 => [7400B,7400B,17B,17B,7400B,7400B,17B],
    --  The pattern
    --    .*..
    --    .*..
    --    ...*
    --    ...*

  32 => [7417B,170360B,7417B,170360B,7417B,170360B,7417B],
    --  The pattern
    --    .*.*
    --    *.*.
    --    .*.*
    --    *.*.

  48 => [7400B,7400B,0,0,7400B,7400B,0],
    --  The pattern
    --    .*..
    --    .*..
    --    ....
    --    ....

  64 => [170377B,177760B,170377B,177760B,170377B,177760B,170377B],
    --  The pattern
    --    *.**
    --    ***.
    --    *.**
    --    ***.

  ENDCASE => ALL[177777B] -- all bits --];
END;

-- The following Set/Get routines seem to be no-ops for Chipmonk's 4-bits-per-point
-- color display.

SetRed: PUBLIC PROC[gray: [0..256),newVal: [0..256),outputDevice: BitmapPtr ← NIL] = 
BEGIN
IF outputDevice = NIL THEN outputDevice ← Screen;
SELECT outputDevice.nBitsPerPixel FROM
    8=>BEGIN ColorTable8[gray*4].redHigh ← newVal/16;
        ColorTable8[gray*4].redLow ← newVal MOD 16;
        pMonitorHead.Flags ← [0,FALSE,FALSE,FALSE,FALSE,TRUE,TRUE,TRUE];
        END;
    ENDCASE;
END;

SetGreen: PUBLIC PROC[gray: [0..256),newVal: [0..256),outputDevice: BitmapPtr ← NIL] = 
BEGIN 
IF outputDevice = NIL THEN outputDevice ← Screen;
SELECT outputDevice.nBitsPerPixel FROM
    8=>BEGIN
        ColorTable8[gray*4].green ← newVal;
        pMonitorHead.Flags ← [0,FALSE,FALSE,FALSE,FALSE,TRUE,TRUE,TRUE];
        END;
    ENDCASE;
END;

SetBlue: PUBLIC PROC[gray: [0..256),newVal: [0..256),outputDevice: BitmapPtr ← NIL] = 
BEGIN 
IF outputDevice = NIL THEN outputDevice ← Screen;
SELECT outputDevice.nBitsPerPixel FROM
    8=>BEGIN
        ColorTable8[gray*4].blue ← newVal;
        pMonitorHead.Flags ← [0,FALSE,FALSE,FALSE,FALSE,TRUE,TRUE,TRUE];
        END;
    ENDCASE;
END;

GetRed: PUBLIC PROC[gray: [0..256),outputDevice: BitmapPtr ← NIL] RETURNS [CARDINAL] = 
BEGIN
IF outputDevice = NIL THEN outputDevice ← Screen;
SELECT outputDevice.nBitsPerPixel FROM
    8=>RETURN[ColorTable8[gray*4].redHigh*16+ColorTable8[gray*4].redLow];
    ENDCASE=>RETURN[0];
END;

GetGreen: PUBLIC PROC[gray: [0..256),outputDevice: BitmapPtr ← NIL] RETURNS [CARDINAL] = 
BEGIN 
IF outputDevice = NIL THEN outputDevice ← Screen;
SELECT outputDevice.nBitsPerPixel FROM
    8=>RETURN[ColorTable8[gray*4].green];
    ENDCASE=>RETURN[0];
END;

GetBlue: PUBLIC PROC[gray: [0..256),outputDevice: BitmapPtr ← NIL] RETURNS [CARDINAL] = 
BEGIN 
IF outputDevice = NIL THEN outputDevice ← Screen;
SELECT outputDevice.nBitsPerPixel FROM
    8=>RETURN[ColorTable8[gray*4].blue];
    ENDCASE=>RETURN[0];
END;

TurnOnColor: PUBLIC PROC  [bitsPerPixel: CARDINAL ← 1,portraitMode: BOOLEAN ← TRUE,scalePercent: CARDINAL ← 100, initColor: Color] RETURNS [BitmapPtr] =
BEGIN
scaleFactor: CARDINAL ← scalePercent/10;
IF Screen = NIL THEN --ought to be able to return a couple of screens...
    BEGIN
    Screen ← SystemDefs.AllocateHeapNode[SIZE[Bitmap]];
         IF portraitMode THEN
                BEGIN
                Screen.nBits ← (640*10)/scaleFactor;
                Screen.nLines ← (481*10)/scaleFactor;
                END
            ELSE
                BEGIN
                Screen.nBits ← (481*10)/scaleFactor;
                Screen.nLines ← (640*10)/scaleFactor;
                END;
    END;
Screen.nBitsPerPixel ← bitsPerPixel;
Screen.portraitMode ← portraitMode;
Screen.scaleFactor ← scaleFactor;
Screen.nWords ← IF portraitMode
    THEN 2*bitsPerPixel*((Screen.nBits+31)/32)
    ELSE 2*bitsPerPixel*((Screen.nLines+31)/32) ;

IF NOT Dor THEN
    BEGIN
    i:CARDINAL;
    temp:ARRAY[0..47] OF CARDINAL;
    FOR i IN [0..47] DO
     temp[i]←i*4;
    ENDLOOP;
    SetColorTable[LOOPHOLE[@temp]];
    ColorBitmap↑ ← LOOPHOLE[0];    --InlineDefs.LowHalf[longP];
    ColorBitmapBank↑ ← 4;    --InlineDefs.HighHalf[longP];
    ColorMap↑ ← LOOPHOLE[ColorTablePlace];
    ColorMapBank↑ ← 5;
    Screen.bank ← 4;    --1;
    Screen.bits←LOOPHOLE[ColorBitmap↑];
    EraseColorArea[0,0,Screen.nBits,Screen.nLines, initColor];
    END
ELSE
    BEGIN
    temp:ARRAY[0..47] OF CARDINAL;
    i: CARDINAL;
    FOR i IN [0..47] DO
     temp[i]←i*4;
    ENDLOOP;
    ColorTable8 ← SystemDefs.AllocateSegment[SIZE[ATableImage]];
    Screen.bank ← 4;
    Screen.bits←LOOPHOLE[0];
    ColorCtrlBlk ←
     [    ATable: ColorTable8,
        BTable: NIL,CTable: NIL,MiniMixer: NIL,
        VBlank: [VBtoVS: 3,VStoVS: 3],VStoVB: 20B,VisibleLines: 240,
        HRamMaxAddr: 379,HBlank: [HBLeadLength: 6,HSTrailAddr: 47B],
        HBTrailLength: 16B,PClock: [unused: 0,PClockMul: 130B,PClockDiv: 14B],
        reserved: 0
     ];
    AChanCtrlBlk ←
     [    NIL,Screen.nWords,MakeLongPointer[Screen.bits,Screen.bank],
        Screen.nLines/2,(Screen.nWords*16)/bitsPerPixel+377B,
        MarginOffset[Ramtek525],
        [unused: 0,
         b24BitsPerPixel: FALSE,AByPass: FALSE,BByPass: FALSE,A8B2: TRUE,
         Resolution: full,Size8: FALSE,Size4:TRUE,Size2: FALSE,Size1: FALSE
        ]
     ];
    ColorMonitor ←
     [    177456B,[0,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE],
        @AChanCtrlBlk,NIL,@ColorCtrlBlk
     ];
    ReplaceColorArea[0,0,Screen.nBits,Screen.nLines, initColor];
    pMonitorHead↑ ← @ColorMonitor;
    SetColorTable[LOOPHOLE[@temp]];
    END;
RETURN[Screen];
END;

KillGraphics:PUBLIC PROC[outputDevice:BitmapPtr←NIL] =
BEGIN
IF outputDevice = NIL THEN outputDevice ← Screen;
IF outputDevice = NIL THEN RETURN;
IF NOT Dor THEN
    BEGIN
    ColorBitmap↑ ← LOOPHOLE[0];    --InlineDefs.LowHalf[longP];
    ColorBitmapBank↑ ← 0;    --InlineDefs.HighHalf[longP];
    ColorMap↑ ← LOOPHOLE[0];
    ColorMapBank↑ ← 0;
    END ELSE pMonitorHead↑←NIL;
END;



logTable: ARRAY [1..8] OF CARDINAL = [0,1,2,2,3,3,3,3];

SetColorFromSource: PUBLIC PROC [source: BitmapPtr, x,y: INTEGER,
  fn: BitBltDefs.BBoperation, sourceType: BitBltDefs.BBsourcetype ← block,
  gray: GrayPatternPtr ← NIL, dest: BitmapPtr ← NIL] =
BEGIN
BLT: BLTBlockPtr;
x2,y2: INTEGER;
sx,sy,dy: INTEGER ← 0;
logBitsPerPixel: CARDINAL;
IF dest = NIL THEN dest ← Screen;
logBitsPerPixel ← logTable[dest.nBitsPerPixel];
x2 ← x+source.nBits-1;
y2 ← y+source.nLines-1;

IF x >= INTEGER[dest.nBits] OR y >= INTEGER[dest.nLines] THEN RETURN;
IF x2 >= INTEGER[dest.nBits] THEN x2 ← dest.nBits - 1;
IF y2 >= INTEGER[dest.nLines] THEN BEGIN dy ← y2-(INTEGER[dest.nLines]-1);y2 ← dest.nLines-1;END;
IF x < 0 THEN BEGIN sx ← -x;x ← 0;END;
IF y < 0 THEN BEGIN sy ← -y;y ← 0;END;

IF NOT dest.portraitMode THEN
    BEGIN t: CARDINAL;
    t ← (INTEGER[dest.nLines]-1)-y;y ← x;
    x ← (INTEGER[dest.nLines]-1)-y2;
    y2 ← MIN[x2,y+INTEGER[source.nBits]-1];x2 ← MIN[t,x+INTEGER[source.nLines]-1];
    sy ← sx;sx ← dy;
    END;

BLT ← AllocateBLT[];
IF gray#NIL THEN LOOPHOLE[@BLT.blk.gray0, GrayPatternPtr]↑ ← gray↑;
BLT.blk.sourcetype ← sourceType;
BLT.blk.function ← fn;
BLT.blk.dlbca ← MakeLongPointer[dest.bits,dest.bank];
BLT.blk.dbmr ← dest.nWords;
BLT.blk.dlx ← InlineDefs.BITSHIFT[x,logBitsPerPixel];
BLT.blk.dty ← y;
BLT.blk.dw ← InlineDefs.BITSHIFT[(x2+1-x),logBitsPerPixel];
BLT.blk.dh ← (y2+1-y);
BLT.blk.slbca ← MakeLongPointer[source.bits,source.bank];
BLT.blk.sbmr ← source.nWords;
BLT.blk.slx ← sx;
BLT.blk.sty ← sy;
BitBltDefs.BITBLT[@BLT.blk];
FreeBLT[BLT];
END;


SetColorPoint: PUBLIC PROC [x, y: INTEGER, fn: BitBltDefs.BBoperation,
  c: Color ← 0, b: BitmapPtr ← NIL, cGray: GrayPatternPtr ← NIL] =
BEGIN
BLT: BLTBlockPtr;
logBitsPerPixel: CARDINAL;
IF b = NIL THEN b ← Screen;
logBitsPerPixel ← logTable[b.nBitsPerPixel];
IF NOT x IN [0..INTEGER[b.nBits]) THEN RETURN;
IF NOT y IN [0..INTEGER[b.nLines]) THEN RETURN;    -- save your ass
IF NOT b.portraitMode THEN
    BEGIN t: INTEGER ← (INTEGER[b.nLines]-1)-y;y ← x;x ← t;END;
BLT ← AllocateBLT[];
LOOPHOLE[@BLT.blk.gray0, GrayPatternPtr]↑ ← IF cGray#NIL THEN cGray↑
  ELSE GetColorGray[c];
BLT.blk.sourcetype ← gray;
BLT.blk.dw ← Screen.nBitsPerPixel;
BLT.blk.dh ← 1;
BLT.blk.function ← fn;
BLT.blk.dlbca ← MakeLongPointer[b.bits,b.bank];
BLT.blk.dbmr ← b.nWords;
BLT.blk.dlx ← InlineDefs.BITSHIFT[x,logBitsPerPixel];
BLT.blk.dty ← y;
BitBltDefs.BITBLT[@BLT.blk];
FreeBLT[BLT];
END;


SetColorLine: PUBLIC PROC [x1,y1,x2,y2: INTEGER, fn: BitBltDefs.BBoperation,
  c: Color ← 0, b: BitmapPtr ← NIL, cGray: GrayPatternPtr ← NIL] =
BEGIN
fpOff:INTEGER = 20000B;
BLT: BLTBlockPtr;
logBitsPerPixel: CARDINAL;
dx,dy,oi,nn,partl,pinc:INTEGER;
li:LONG INTEGER;
IF b = NIL THEN b ← Screen;
logBitsPerPixel ← logTable[b.nBitsPerPixel];

IF NOT b.portraitMode THEN
    BEGIN t: INTEGER;
    t ← (INTEGER[b.nLines]-1)-y1;y1 ← x1;
    x1 ← (INTEGER[b.nLines]-1)-y2;y2 ← x2;x2 ← t;
    END;

dx←x2-x1;dy←y2-y1;
BLT ← AllocateBLT[];
LOOPHOLE[@BLT.blk.gray0, GrayPatternPtr]↑ ← IF cGray#NIL THEN cGray↑
  ELSE GetColorGray[c];
BLT.blk.sourcetype ← gray;
BLT.blk.function ← fn;
BLT.blk.dlbca ← MakeLongPointer[b.bits,b.bank];
BLT.blk.dbmr ← b.nWords;
BLT.blk.sty ← 0; --DORADO looks here
IF ABS[dx]>ABS[dy] THEN
    BEGIN
        BLT.blk.dh ← 1;
        IF dx<0 THEN BEGIN t:INTEGER;t←x1;x1←x2;x2←t;t←y1;y1←y2;
                y2←t;dx←-dx;dy←-dy;END;
        oi←IF dy<0 THEN -1 ELSE IF dy=0 THEN 0 ELSE 1;
        li←ABS[dy];
        li←li*fpOff;
        li←li/dx;
        pinc←InlineDefs.LowHalf[li];
        partl←0;
        UNTIL x1>=x2 DO
            nn←0;
            UNTIL partl>fpOff OR nn>=dx DO
                nn←nn+1;
                partl←partl+pinc;
            ENDLOOP;
--must worry about off-screen
            BLT.blk.dlx ← InlineDefs.BITSHIFT[x1,logBitsPerPixel];
            BLT.blk.dw ← InlineDefs.BITSHIFT[nn,logBitsPerPixel];
            BLT.blk.dty ← y1;
            partl←partl-fpOff;
            y1←y1+oi;
            x1←x1+nn;
            BitBltDefs.BITBLT[@BLT.blk];
        ENDLOOP;
    END
 ELSE
    BEGIN
        BLT.blk.dw ← b.nBitsPerPixel;
        IF dy<0 THEN BEGIN t:INTEGER;t←x1;x1←x2;x2←t;t←y1;y1←y2;
                y2←t;dx←-dx;dy←-dy;END;
        oi←IF dx<0 THEN -1 ELSE IF dx=0 THEN 0 ELSE 1;
        li←ABS[dx];
        li←li*fpOff;
        li←li/dy;
        pinc←InlineDefs.LowHalf[li];
        partl←0;
        UNTIL y1>=y2 DO
            nn←0;
            UNTIL partl>fpOff OR nn>=dy DO
                nn←nn+1;
                partl←partl+pinc;
            ENDLOOP;
--must worry about off-screen
            BLT.blk.dty ← y1;
            BLT.blk.dh ← nn;
            BLT.blk.dlx ← InlineDefs.BITSHIFT[x1,logBitsPerPixel];
            partl←partl-fpOff;
            x1←x1+oi;
            y1←y1+nn;
            BitBltDefs.BITBLT[@BLT.blk];
        ENDLOOP;
    END;
FreeBLT[BLT];
END;


SetupColorAreaBLT: PUBLIC PROC [BLT: BLTBlockPtr,
  x1,y1,x2,y2: INTEGER, fn: BitBltDefs.BBoperation, b: BitmapPtr ← NIL] =
BEGIN
logBitsPerPixel: CARDINAL;
IF b = NIL THEN b ← Screen;
logBitsPerPixel ← logTable[b.nBitsPerPixel];

x1 ← MAX[0,x1];
y1 ← MAX[0,y1];
x2 ← MIN [x2,INTEGER[b.nBits]-1];
y2 ← MIN[y2,INTEGER[b.nLines]-1];
IF NOT b.portraitMode THEN
    BEGIN -- rotate by 90 degrees
    t: INTEGER;
    t ← (INTEGER[b.nLines]-1)-y1; y1 ← x1;
    x1 ← (INTEGER[b.nLines]-1)-y2; y2 ← x2; x2 ← t;
    END;

BLT.blk.sourcetype ← gray;
BLT.blk.destalt←FALSE;
BLT.blk.dlbca ← MakeLongPointer[b.bits,b.bank];
BLT.blk.dbmr ← b.nWords;
BLT.blk.dlx ← InlineDefs.BITSHIFT[x1,logBitsPerPixel];
BLT.blk.dty ← y1;
BLT.blk.dw ← InlineDefs.BITSHIFT[(x2+1-x1),logBitsPerPixel];
BLT.blk.dh ← (y2+1-y1);
BLT.blk.sty ← 0; --DORADO looks here
BLT.blk.function ← fn;
END;

END.