
{ WRMODE.PAS - BGI256 write mode definitions }
{ Written by Michael Day as of 24 Jan 95 }

{Usage:    SetWriteMode(Selection+Function); }
{Example:  SetWriteMode(FloodFillMode+SeedFill); }

{Remember, use of the extended SetWriteMode commands with a standard}
{BGI driver (other than BGI256) will leave the line draw mode in an}
{indetermant state. You should always call SetWriteMode just before}
{using a command that uses the line drawing functions to insure the state}
{of the line draw mode, or SetWriteMode immediately after using an extended}
{function to restore the last drawn mode. The Borland graph driver}
{does not provide information about the last selected line draw mode.}
{You will have to track that information yourself.}

{$A+,B-,D+,E+,F+,G+,I-,L+,N-,O+,P-,Q-,R-,S+,T-,V+,X+}
{$M 65520,0,655360}
{ R - ?}

unit WRMODE;
interface

{sprite record used with PutImage sprite command ($A0)}
type  SpriteRecord = record
                       Width,Height,OldX,OldY:integer;
                       Image,Old:pointer;
                     end;

type  RGBpaletteRec  = record
                         Red,Green,Blue:byte;
                       end;
      RGBpaletteType = array[0..255] of RGBpaletteRec;


const BGIVersion : word = 0;
      VirtualBGI : boolean = false;
      DisplayImagePages : word = 1;

      ScreenOfsX    : word = 0; {screen window position on virtual display}
      ScreenOfsY    : word = 0;
      DrawingOfsX   : word = 0; {drawing window position on virtual display}
      DrawingOfsY   : word = 0;

      ScreenWidth   : word = 320;
      ScreenHeight  : word = 200;
      DrawingWidth  : word = 320;
      DrawingHeight : word = 200;
      VirtualWidth  : word = 320;
      VirtualHeight : word = 200;
      ScanLength    : word = 320;

      {graphics mode selections available}
const Mode200  = 0;  {320x200x256}
      Mode400  = 1;  {640x400x256}
      Mode480  = 2;  {640x480x256}
      Mode600  = 3;  {800x600x256}
      Mode768  = 4;  {1024x768x256}
      Mode1024 = 5;  {1280x1024x256}
      AutoMode = 127;{AutoDetect mode}

      {SetWriteMode selection commands}
const LineMode       = $00;  {line drawing write style}
      PixelMode      = $20;  {pixel drawing write style}
      FillMode       = $40;  {fill write style}
      FloodFillType  = $60;  {floodfill option selection}
      TextMode       = $80;  {bitmapped text write style}
      GetImageMode   = $C0;  {GetImage write style}

      MiscCommand    = $E0;  {misc BGI driver commands}

      PutAniMode     = $A0;  {PutImage animation modifier cmd}

      {Write mode functions}
      MoveWrite    = 0;         {foreground and background drawing}
      XorWrite     = 1;
      OrWrite      = 2;
      AndWrite     = 3;
      NotMoveWrite = 4;
      NotXorWrite  = 5;
      NotOrWrite   = 6;
      NotAndWrite  = 7;
      ForeMoveWrite    = 8;     {foreground only drawing}
      ForeXorWrite     = 9;
      ForeOrWrite      = 10;
      ForeAndWrite     = 11;
      ForeNotMoveWrite = 12;
      ForeNotXorWrite  = 13;
      ForeNotOrWrite   = 14;
      ForeNotAndWrite  = 15;
      BackMoveWrite     = 16;   {background only drawing}
      BackXorWrite      = 17;
      BackOrWrite       = 18;
      BackAndWrite      = 19;
      BackNotMoveWrite  = 20;
      BackNotXorWrite   = 21;
      BackNotOrWrite    = 22;
      BackNotAndWrite   = 23;
      SetBackColor    = 24; {set the background color for draw method}
      GetWriteMode    = 30; {return selected write mode on GetMaxMode call}
      GetBackColor    = 31; {return background color on GetMaxMode call}

      {FloodFillType functions}
      BorderFill      = 0;  {use border floodfill method - default}
      SeedFill        = 1;  {use seed floodfill method}
      AutoFill        = 8;  {auto-select simplex or complex fill - default}
      ComplexFill     = 9;  {force complex floodfill always}
      FillCompressOff = 10; {use standard floodfill stack - default}
      FillCompressOn  = 11; {use compressed floodfill stack}
      FillDelayOff    = 12; {fill area while searching - default}
      FillDelayOn     = 13; {delay fill to after search, not active in simplex}
      FillTracerOff   = 14; {no tracing of search path - default}
      FillTracerOn    = 15; {show search path as it is processed}
      SetClearColorCmd = 24; {set initcolor (used by ClearDevice)}
      GetXYStackPeak   = 26; {ret last peak val of fill XYstack}
      GetXYStackFree   = 27; {ret last val of free XYstack space}
      GetFloodFillOpt  = 31; {ret FF option flags in next GetMaxMode call}

      {Misc Command functions}
      SetGetPixelReadOnly  = 0;  {set GetPixel to read monly mode}
      SetGetPixelReadWrite = 1;  {set GetPixel to write after read mode}
      SetGetImageReadOnly  = 2;  {set GetImage to read only mode}
      SetGetImageReadWrite = 3;  {set GetImage to write after read mode}
      SetLineStyleMode0    = 4;  {set mode 0 - (default) clr pat cnt on draw - standard}
      SetLineStyleMode1    = 5;  {set mode 1 - do not clr pat cnt on draw - continuous}
      SetLineStyleMode2    = 6;  {set mode 2 - fixed line pattern mask - fixed}
      DefPaletteReloadCmd  = 22; {enable VGA palette reload on mode chg}
      KillPaletteReloadCmd = 23; {disable VGA palette reload on mode chg}
      SetPutImgBackColor   = 24; {set new putimage background color}

      {These misc cmd functions modify return value ofnext GetMaxMode call}
      GetCurrentModeCmd     = 25; {ret actual graph mode in use}
      GetDriverVersionCmd   = 28; {ret driver version number}
      GetPutImgWriteModeCmd = 30; {ret last write mode used by PutImage}
      GetPutImgBackColorCmd = 31; {ret background color used by PutImage}


      procedure SetScreenOfs(OfsX,OfsY:word);
      procedure SetDrawingOfs(OfsX,OfsY:word);
      procedure SetDrawingSize(Width,Height:word);
      procedure SetVirtualWidth(Width:word);

      procedure GetScreenOfs;   {update screen X/Y offset in virtual display}
      procedure GetDrawingOfs;  {update drawing X/Y offset in virtual display}
      procedure GetScreenSize;  {update screen size}
      procedure GetDrawingSize; {update drawing area size}
      procedure GetVirtualSize; {update virtual display size}

      procedure UpdateDisplayInfo;   {update full virtual screen info}
      procedure UpdateScreenInfo;    {update drawing and screen info only}
      procedure GetBGIVersion;       {get BGI256 version info}
      procedure GetVirtualFlag;      {get current virtual state 1=virtualBGI}
      procedure GetScanLineLength;   {update scan line length info}
      procedure GetRGBPalette(Which:word; var Red,Green,Blue:byte);
      procedure GetRGBblock(Start,Count:word; var Pal); {read VGA palette}
      procedure SetRGBblock(Start,Count:word; var Pal); {load VGA palette}
      procedure EnableBiosPaletteReload;  {enable bios palette reload on mode chg}
      procedure InhibitBiosPaletteReload; {disable bios palette reload on mode chg}
      procedure EnableGraphPaletteReload;  {enable BGI palette reload on mode chg}
      procedure InhibitGraphPaletteReload; {disable BGI palette reload on mode chg}
      procedure SetClearColor(Color:word); {set bkg color for ClearDevice}

      function DisplaySync:longint;  {wait for vert interrupt}
      function VideoMemorySize:word; {ret vid mem size in KB,   0=unknown}
      function GetImagePages:word;   {get number of image pages 0=unknown}


implementation
uses  {$IFDEF DPMI} WinApi, {$ENDIF}
        graph;

var   tCPX,tCPY : integer;

const BGIPaletteReloadDisabled : boolean = false;
      BiosPaletteReloadDisabled : boolean = false;

const DisplayCommand = $A0;  {misc display commands}

      {Misc Display functions}
const SetScreenOffsetCmd  = 0; {Set screen window X offset to cpx/cpy val}
      SetDrawingOffsetCmd = 1; {Set drawing window offset to cpx/cpy val}
      SetDrawingSizeCmd   = 2; {Set drawing window size to cpx/cpy val}
      SetVirtualSizeCmd   = 3; {Set virtual screen size to cpx/cpy val}

      GetScreenOffsetXCmd   = 4; {ret screen window X offset}
      GetScreenOffsetYCmd   = 5; {ret screen window Y offset}
      GetDrawingOffsetXCmd  = 6; {ret drawing window X offset}
      GetDrawingOffsetYCmd  = 7; {ret drawing window Y offset}

      GetScreenWidthCmd     = 8; {ret phys scrn width}
      GetScreenHeightCmd    = 9; {ret phys scrn height}
      GetDrawingWidthCmd    = 10; {ret drawing window width}
      GetDrawingHeightCmd   = 11; {ret drawing window height}
      GetVirtualWidthCmd    = 12; {ret virtual window width}
      GetVirtualHeightCmd   = 13; {ret virtual window height}

      GetScanLineLengthCmd  = 28; {ret scan line len}
      GetVirtualStatusCmd   = 29; {ret virtual display status 0=no, 1=yes}
      GetVidMemSizeCmd      = 30; {ret video memory size in kilobytes}
      GetMaxImagePagesCmd   = 31; {ret max number image pages possible}


{-------------------------------------------------}
{ protected mode stuff }

{$IFDEF DPMI}
  var PalSS : record ProtSel,RealSeg:word; end;

  var SimInt : record
        RealDI : longint;   {register structure used for    }
        RealSI : longint;   {processing real mode interrupts}
        RealBP : longint;   {from protected mode operation  }
        RealXX : longint;
        RealBX : longint;
        RealDX : longint;
        RealCX : longint;
        RealAX : longint;
        RealFlags : word;
        RealES : word;
        RealDS : word;
        RealFS : word;
        RealGS : word;
        RealIP : word;
        RealCS : word;
        RealSP : word;
        RealSS : word;
      end;

    {this must be assembler as shown. Do NOT pass any parameters.}
    function ProtPalInt:boolean; assembler;
    asm
      MOV word ptr [SimInt.RealAX],AX
      MOV word ptr [SimInt.RealBX],BX   {pass std regs}
      MOV word ptr [SimInt.RealCX],CX
      MOV word ptr [SimInt.RealDX],DX
      mov ax,[PalSS.RealSeg]       {load real mode pointer seg}
      or ax,ax
      jz @DpmiFail
      MOV word ptr [SimInt.RealES],AX
      MOV word ptr [SimInt.RealDS],AX
      MOV word ptr [SimInt.RealFlags],0
      MOV word ptr [SimInt.RealSS],0    {let DPMI make it's own stack}
      MOV word ptr [SimInt.RealSP],0
      MOV word ptr [SimInt.RealXX],0
      MOV word ptr [SimInt.RealXX+2],0
      MOV DI,SEG SimInt
      MOV ES,DI
      MOV DI,OFFSET SimInt
      MOV AX,$0300
      MOV BL,$10
      MOV BH,0
      MOV CX,0
      INT $31      {call the dpmi to do the interrupt}
      JC @DpmiFail
      MOV AH,byte ptr [SimInt.RealFlags] {Joy! call was successful}
      SAHF
      MOV AX,word ptr [SimInt.RealAX]
      JMP @DpmiRet
     @DpmiFail:
      MOV AX,-1    {eek! dpmi call failed}
      STC
     @DpmiRet:
      MOV BX,word ptr [SimInt.RealBX]   {restore std regs}
      MOV CX,word ptr [SimInt.RealCX]
      MOV DX,word ptr [SimInt.RealDX]
    end;
  {$ENDIF}

{-------------------------------------------------}
{misc stuff}

{wait for the start of vertical sync}
{returns zero if no sync happening, non-zero if all ok}
function DisplaySync:longint;
var aborttime:longint;
begin
  aborttime := 1;
  while ((port[$3DA] and 8) = 8) do
  begin
    inc(aborttime);
    if aborttime > $1fffff then
    begin
      DisplaySync := 0;
      Exit;
    end;
  end;
  while ((port[$3DA] and 8) = 0) and (aborttime < $1fffff) do
  begin
    inc(aborttime);
    if aborttime > $1fffff then
    begin
      DisplaySync := 0;
      Exit;
    end;
  end;
  DisplaySync := aborttime;
end;


{-------------------------------------------------}
{get BGI256 version. 0=not me, 200=old V2.00 driver, 300=old V3.00 driver}
{                    2.03 or 3.03 and up= newer driver versions}
procedure GetBGIVersion;
var Result : integer;
begin
  BGIVersion := 0;
  VirtualBGI := false;
  Result := GraphResult;  {clear result flag}
  if GetMaxColor < 255 then Exit; {not 256 color, must be a Borland driver}
  Result := GraphResult;
  if Result <> 0 then Exit;
  SetWriteMode(MiscCommand+GetDriverVersionCmd);
  Result := GetMaxMode;
  SetWriteMode(0);
  if ((Result >= 203) and (Result <= 299)) or
     ((Result >= 303) and (Result <= 9999)) then
  begin
    BGIVersion := Result; {Yahoo! It's us!}
    SetWriteMode(DisplayCommand+GetVirtualStatusCmd);
    Result := GetMaxMode;
    if Result = 1 then
      VirtualBGI := true; {yeah! Virtual screen is supported!}
    Exit;
  end;

  SetColor(255);  {try to read background color to see if it is BGI256 V3}
  SetWriteMode(MiscCommand+LineMode+SetBackColor);
  SetWriteMode(MiscCommand+LineMode+GetBackColor);
  Result := GetMaxMode;
  SetWriteMode(0);
  if Result = 255 then
  begin
    BGIVersion := 302;  {in version 3.02 we could read the backgroundcolor}
    Exit;
  end;
  {at this point I don't know what it is, it could be V2.02 or V2.06 BGI256}
  {or some other 3rd party BGI driver}
end;

{-------------------------------------------------}
{display procedures}

procedure SetScreenOfs(OfsX,OfsY:word);
begin
  if VirtualBGI then
  begin
    tCPX := GetX;
    tCPY := GetY;
    moveto(OfsX,OfsY);
    SetWriteMode(DisplayCommand+SetScreenOffsetCmd);
    moveto(tCPX,tCPY);
  end;
  GetScreenOfs;
end;

procedure SetDrawingOfs(OfsX,OfsY:word);
begin
  if VirtualBGI then
  begin
    tCPX := GetX;
    tCPY := GetY;
    moveto(OfsX,OfsY);
    SetWriteMode(DisplayCommand+SetDrawingOffsetCmd);
    moveto(tCPX,tCPY);
  end;
  GetDrawingOfs;
end;

{remember to call: SetGraphMode(GetGraphMode); after this procedure}
{followed by UpdateScreenInfo or UpdateDisplayInfo if VirtualWidth changed.}
{If both SetDrawingSize and SetVirtualWidth are called, this only needs}
{to be done once after the calls.}
procedure SetDrawingSize(Width,Height:word);
begin
  if VirtualBGI then
  begin
    tCPX := GetX;
    tCPY := GetY;
    moveto(Width,Height);
    SetWriteMode(DisplayCommand+SetDrawingSizeCmd);
    moveto(tCPX,tCPY);
  end;
  GetDrawingSize;
end;

{remember to call: SetGraphMode(GetGraphMode); after this procedure}
{followed by UpdateDisplayInfo}
procedure SetVirtualWidth(Width:word);
begin
  if VirtualBGI then
  begin
    tCPX := GetX;
    tCPY := GetY;
    moveto(Width,0);  {Y parameter is not used}
    SetWriteMode(DisplayCommand+SetVirtualSizeCmd);
    moveto(tCPX,tCPY);
  end;
  GetVirtualSize;
end;


procedure GetVirtualSize;
begin
  if VirtualBGI then
  begin
    SetWriteMode(DisplayCommand+GetVirtualWidthCmd);
    VirtualWidth := GetMaxMode;
    SetWriteMode(DisplayCommand+GetVirtualHeightCmd);
    VirtualHeight := GetMaxMode;
    SetWriteMode(0);
  end
  else
  begin
    VirtualWidth  := succ(GetMaxX);
    VirtualHeight := succ(GetMaxY);
  end;
end;

procedure GetDrawingSize;
begin
  if VirtualBGI then
  begin
    SetWriteMode(DisplayCommand+GetDrawingWidthCmd);
    DrawingWidth := GetMaxMode;
    SetWriteMode(DisplayCommand+GetDrawingHeightCmd);
    DrawingHeight := GetMaxMode;
    SetWriteMode(0);
  end
  else
  begin
    DrawingWidth  := succ(GetMaxX);
    DrawingHeight := succ(GetMaxY);
  end;
end;

procedure GetDrawingOfs;
begin
  if VirtualBGI then
  begin
    SetWriteMode(DisplayCommand+GetDrawingOffsetXCmd);
    DrawingOfsX := GetMaxMode;
    SetWriteMode(DisplayCommand+GetDrawingOffsetYCmd);
    DrawingOfsY := GetMaxMode;
    SetWriteMode(0);
  end
  else
  begin
    DrawingOfsX := 0;
    DrawingOfsY := 0;
  end;
end;

procedure GetScreenOfs;
begin
  if VirtualBGI then
  begin
    SetWriteMode(DisplayCommand+GetScreenOffsetXCmd);
    ScreenOfsX := GetMaxMode;
    SetWriteMode(DisplayCommand+GetScreenOffsetYCmd);
    ScreenOfsY := GetMaxMode;
    SetWriteMode(0);
  end
  else
  begin
    ScreenOfsX := 0;
    ScreenOfsY := 0;
  end;
end;

procedure GetScreenSize;
begin
  if VirtualBGI then
  begin
    SetWriteMode(DisplayCommand+GetScreenWidthCmd);
    ScreenWidth := GetMaxMode;
    SetWriteMode(DisplayCommand+GetScreenHeightCmd);
    ScreenHeight := GetMaxMode;
    SetWriteMode(0);
  end
  else
  begin
    ScreenWidth  := succ(GetMaxX);
    ScreenHeight := succ(GetMaxY);
  end;
end;

procedure GetScanLineLength;
begin
  if VirtualBGI then
  begin
    SetWriteMode(DisplayCommand+GetScanLineLengthCmd);
    ScanLength := GetMaxMode;
    SetWriteMode(0);
  end
  else
  begin
    ScanLength := succ(GetMaxX);
  end;
end;

procedure GetVirtualFlag;
begin
  if (BGIversion > 0) and (BGIversion <> 302) then
  begin
     SetWriteMode(DisplayCommand+GetVirtualStatusCmd);
     if GetMaxMode = 1 then
       VirtualBGI := true
     else
       VirtualBGI := false;
     SetWriteMode(0);
  end
  else
  begin
    VirtualBGI := false;
  end;
end;


procedure UpdateScreenInfo;
begin
  GetDrawingSize;
  GetScreenSize;
  GetScreenOfs;
  GetDrawingOfs;
  DisplayImagePages := GetImagePages;
  if DisplayImagePages = 0 then
    DisplayImagePages := 1;
end;

procedure UpdateDisplayInfo;
begin
  GetBGIversion;
  GetScanLineLength;
  GetVirtualSize;
  UpdateScreenInfo;
end;


{----------------------------------------------------}
{ misc display functions }

function VideoMemorySize:word;
begin
  if (BGIversion > 0) and (BGIversion <> 302) then
  begin
     SetWriteMode(DisplayCommand+GetVidMemSizeCmd);
     VideoMemorySize := GetMaxMode;
     SetWriteMode(0);
  end
  else
  begin
    VideoMemorySize := 0;  {zero = unknown}
  end;
end;

function GetImagePages:word;
begin
  if (BGIversion > 0) and (BGIversion <> 302) then
  begin
    SetWriteMode(DisplayCommand+GetMaxImagePagesCmd);
    GetImagePages := GetMaxMode;
    SetWriteMode(0);
  end
  else
  begin
    GetImagePages := 0;  {ret zero if unknown}
  end;
end;


{----------------------------------------------------}
{Palette functions}

{enables reload of VGA color palette on mode change}
procedure EnableGraphPaletteReload;
begin
  SetWriteMode(MiscCommand+DefPaletteReloadCmd); {en pal reload on mode chg}
  SetWriteMode(0);
  BGIPaletteReloadDisabled := false;
end;

procedure EnableBiosPaletteReload; assembler;
asm
   MOV AX,1200H {enable reload of palette}
   MOV BL,31H   {on mode change           }
   INT 10H
   MOV BYTE PTR [BiosPaletteReloadDisabled],false
end;

{disables reload of VGA color palette on mode change}
procedure InhibitGraphPaletteReload;
begin
  SetWriteMode(MiscCommand+KillPaletteReloadCmd); {dis pal reload on mode chg}
  SetWriteMode(0);
  BGIPaletteReloadDisabled := true;
end;

procedure InhibitBiosPaletteReload; assembler;
asm
   MOV AX,1201H {inhibit reload of palette}
   MOV BL,31H   {on mode change           }
   INT 10H
   MOV BYTE PTR [BiosPaletteReloadDisabled],true
end;

procedure GetRGBPalette(Which:word; var Red,Green,Blue:byte);
var rdh,rch,rcl:byte;
begin
  asm
    MOV  AX,$1015
    MOV  BX,[Which]
    INT  $10
    MOV  [rdh],DH
    MOV  [rch],CH
    MOV  [rcl],CL
  end;
  Red := rdh;
  Green := rch;
  Blue := rcl;
end;

procedure SetRGBblock(Start,Count:word; var Pal); assembler;
{var PalArray : RGBpaletteType absolute Pal;}
asm
  {$IFDEF DPMI}
    MOV  AX,[Count]
    CMP  AX,257
    JC   @PalOK
    MOV  AX,256
   @PalOK:
    MOV  CX,AX
    PUSH CX
    ADD  CX,CX
    ADD  CX,AX
    PUSH DS
    MOV  DI,0
    MOV  ES,[PalSS.ProtSel]
    LDS  SI,[Pal]
    REP  MOVSB
    POP  DS
    MOV  AX,$1012
    MOV  BX,[Start]
    MOV  DX,0
    POP  CX
    CALL ProtPalInt
  {$ELSE}
    MOV  AX,$1012
    MOV  BX,[Start]
    MOV  CX,[Count]
    LES  DX,[Pal]
    INT $10
  {$ENDIF}
end;

procedure GetRGBblock(Start,Count:word; var Pal); assembler;
{var PalArray : RGBpaletteType absolute Pal;}
asm
  {$IFDEF DPMI}
    MOV  AX,[Count]
    CMP  AX,257
    JC   @PalOK
    MOV  AX,256
   @PalOK:
    MOV  CX,AX
    PUSH CX
    MOV  AX,$1017
    MOV  BX,[Start]
    MOV  DX,0
    CALL ProtPalInt
    POP  CX
    JC   @Error
    PUSH DS
    MOV  AX,CX
    ADD  CX,CX
    ADD  CX,AX
    LES  DI,[Pal]
    MOV  SI,0
    MOV  DS,[PalSS.ProtSel]
    REP  MOVSB
    POP DS
  {$ELSE}
    MOV  AX,$1017
    MOV  BX,[Start]
    MOV  CX,[Count]
    LES  DX,[Pal]
    INT $10
  {$ENDIF}
  @Error:
end;

procedure SetClearColor(Color:word);
var Temp : word;
begin
  Temp := GetColor;
  SetColor(Color);
  SetWriteMode(FloodFillType+SetClearColorCmd);
  SetColor(Temp);
end;

{--------------------------------------------------------}
{unit related enter/exit stuff}

(*
procedure WRmodeExit; far;
begin
  if BiosPaletteReloadDisabled then
    EnableBiosPaletteReload;  {reenable palette reload on prg exit if needed}
end;

var OldExitProc : pointer;
begin
 {$IFDEF DPMI}
   longint(PalSS) := GlobalDosAlloc(sizeof(RGBpaletteType));
 {$ENDIF}
  OldExitProc := ExitProc;
  ExitProc := @WRmodeExit;
*)
end.

