(*R-,V-,C-,U-*)
PROGRAM PibLoRes;

(*--------------------------------------------------------------------------*)
(*                                                                          *)
(*     Program:  PibLoRes                                                   *)
(*                                                                          *)
(*     Purpose:  Demonstrates low resolution (160x100x16 colors) graphics   *)
(*               mode on IBM and compatible color cards.                    *)
(*                                                                          *)
(*     Author:   Philip R. Burns                                            *)
(*     Version:  1.0                                                        *)
(*     Date:     May, 1985                                                  *)
(*                                                                          *)
(*     Requires: IBM PC or close compatible, color graphics card            *)
(*                                                                          *)
(*     Use:      This program demonstrates these three lo-res graphics      *)
(*               procedures:                                                *)
(*                                                                          *)
(*                  Lores_GraphMode                                         *)
(*                  Lores_Move                                              *)
(*                  Lores_Draw                                              *)
(*                                                                          *)
(*               The low resolution graphics mode provides resolution       *)
(*               of 160 horizontal by 100 vertical pixels in 16 colors.     *)
(*               This mode is not directly supported by the BIOS, so        *)
(*               special programming of the CRT registers is required.      *)
(*                                                                          *)
(*               The three procedures listed above are sufficient to        *)
(*               use the lo-res graphics mode.  You should call another     *)
(*               Turbo graphics mode procedure -- say, GraphMode --         *)
(*               at the end of a program which uses these lo-res routines,  *)
(*               otherwise you will find your display in a very peculiar    *)
(*               state indeed.                                              *)
(*                                                                          *)
(*               This low-resolution mode is not really adequate for        *)
(*               serious graphics work, but it is useful for games and      *)
(*               wherever multiple colors are more important than the       *)
(*               degree of resolution.                                      *)
(*                                                                          *)
(*     Credits:  The setup routine is based upon a Basic program called     *)
(*               LOWRES.BAS published in PC WORLD, April, 1985.  The        *)
(*               author was Bernie Lawrence.                                *)
(*                                                                          *)
(*               The line-drawing routine is a straightforward              *)
(*               implementation of an incremental plotter algorithm         *)
(*               adapted for the CRT screen.                                *)
(*                                                                          *)
(*     Problems: Send bug reports, enhancements, etc. to PHILIP BURNS       *)
(*               on either of the following two Chicago BBSs:               *)
(*                                                                          *)
(*                  Gene Plantz's IBBS   (312) 885 4227                     *)
(*                  Ron Fox's RBBS       (312) 940 6496                     *)
(*                                                                          *)
(*               If you make interesting changes, please upload them        *)
(*               so that everyone else can enjoy them too!                  *)
(*                                                                          *)
(*     P.S.      It would be nice if someone would come up with an          *)
(*               alphanumeric display routine so text could be easily       *)
(*               displayed in lo-res mode.                                  *)
(*                                                                          *)
(*--------------------------------------------------------------------------*)

CONST
                                   (* Control registers *)
   Mode_Reg     = $3D8;
   Color_Reg    = $3D9;
                                   (* BIOS saves registers here *)
   Mode_Save    = $465;
   Color_Save   = $466;
                                   (* 6845 CRT controller registers *)
   Crt_Reg      = $3D4;
   Crt_Data     = $3D5;
                                   (* Lo-res is form of 80x25 text  *)
   HiRes_Mode   = 1;
   Video_Mode   = 8;

(* Structured *) CONST
                                   (* 6845 register data        *)

   Reg_Data: ARRAY[ 0 .. 11 ] OF INTEGER

             = ( 113,              (* Horizontal total          *)
                 80,               (* Horizontal displayed      *)
                 90,               (* Horizontal sync position  *)
                 10,               (* Horizontal sync width     *)
                 127,              (* Vertical total            *)
                 6,                (* Vertical adjust           *)
                 100,              (* Vertical displayed        *)
                 112,              (* Vertical sync position    *)
                 2,                (* Non-interlace mode        *)
                 1,                (* Maximum scan line address *)
                 32,               (* Disable cursor display    *)
                 0                 (* Cursor end                *)
                    );

VAR
   Register:     INTEGER;
   Mode:         INTEGER;
                                   (* Color card memory mapped address *)
   Color_Screen: ARRAY[ 0 .. 16000 ] OF BYTE ABSOLUTE $B800:0000;
   I:            INTEGER;
   PixCol:       INTEGER;
   Y:            INTEGER;
   T:            INTEGER;
   X:            INTEGER;
   C:            CHAR;

(*--------------------------------------------------------------------------*)
(*           Lores_GraphMode -- Set Low Resolution Graphics Mode            *)
(*--------------------------------------------------------------------------*)

PROCEDURE Lores_GraphMode;

(*--------------------------------------------------------------------------*)
(*                                                                          *)
(*    Procedure:  Lores_GraphMode                                           *)
(*                                                                          *)
(*    Purpose:    Initiates low resolution graphics mode                    *)
(*                                                                          *)
(*    Calling Sequence:                                                     *)
(*                                                                          *)
(*       Lores_GraphMode;                                                   *)
(*                                                                          *)
(*    Calls:  None                                                          *)
(*                                                                          *)
(*--------------------------------------------------------------------------*)

BEGIN (* Lores_GraphMode *)

                                   (* Clear out current 6845 settings *)
   MemW[Cseg:Mode_Save]  := 0;
   MemW[Cseg:Color_Save] := 0;
   PORT[Mode_Reg]        := 0;
   PORT[Color_Reg]       := 0;

                                   (* Reprogram 6845 for lo-res       *)
   FOR Register := 0 TO 11 DO
      BEGIN
         PORT[Crt_Reg]  := Register;
         PORT[Crt_Data] := Reg_Data[Register];
      END;
                                   (* Lo-res is actually form of 80x25 *)
                                   (* text mode                        *)

   Mode := HiRes_Mode + Video_Mode;

   MemW[Cseg:Mode_Save] := Mode;
   PORT[Mode_Reg]       := Mode;

                                   (* Clear the screen                *)

   FillChar( Color_Screen, 16000, 0 );

                                   (* Fill graphics memory with 222s.  *)
                                   (* This is character which fills    *)
                                   (* left half of character cell with *)
                                   (* background color and right half  *)
                                   (* with foreground color.           *)
   FOR I := 0 TO 7999 DO
      Color_Screen[2 * I]     := 222;

END   (* Lores_GraphMode *);

(*--------------------------------------------------------------------------*)
(*           Lores_Plot -- Plot Point in Low Resolution Graphics Mode       *)
(*--------------------------------------------------------------------------*)

PROCEDURE Lores_Plot( X , Y , PixCol: INTEGER );

(*--------------------------------------------------------------------------*)
(*                                                                          *)
(*    Procedure:  Lores_Plot                                                *)
(*                                                                          *)
(*    Purpose:    Plots point in low-resolution graphics mode               *)
(*                                                                          *)
(*    Calling Sequence:                                                     *)
(*                                                                          *)
(*       Lores_Plot( X , Y , PixCol : INTEGER );                            *)
(*                                                                          *)
(*           X      -- Horizontal postion (0 through 159)                   *)
(*           Y      -- Vertical position (0 through 119)                    *)
(*           PixCol -- Color (0 through 15) of point                        *)
(*                     See the Turbo manual for color assignments.          *)
(*                                                                          *)
(*    Calls:  None                                                          *)
(*                                                                          *)
(*    Remarks:  Requests for point positions outside the correct range,     *)
(*              or for colors outside the correct range, are ignored.       *)
(*              For extra speed, remove the check.                          *)
(*                                                                          *)
(*--------------------------------------------------------------------------*)


VAR
   Pixel:      INTEGER;
   Pixel_Addr: INTEGER;
   Nibble:     INTEGER;
   Legal:      BOOLEAN;

BEGIN (* Lores_Plot *)

   Legal := ( X >= 0 ) AND ( X <= 159 ) AND ( Y >= 0 ) AND ( Y <= 119 ) AND
            ( PixCol >= 0 ) AND ( PixCol <= 15 );

   IF Legal THEN
      BEGIN

         Pixel      := X + ( Y * 160 );
         Pixel_Addr := ( Pixel AND $FFFE ) + 1;
         Nibble     := Pixel MOD 2;

         IF Nibble = 0 THEN
            Color_Screen[Pixel_Addr] := ( Color_Screen[Pixel_Addr] AND $0F )
                                        + PixCol * 16
         ELSE
            Color_Screen[Pixel_Addr] := ( Color_Screen[Pixel_Addr] AND $F0 )
                                        + PixCol;
      END;

END   (* Lores_Plot *);

(*--------------------------------------------------------------------------*)
(*           Lores_Draw -- Draw line between two points, low res. mode      *)
(*--------------------------------------------------------------------------*)

PROCEDURE Lores_Draw( X1, Y1, X2, Y2, LineCol: INTEGER );

(*--------------------------------------------------------------------------*)
(*                                                                          *)
(*    Procedure:  Lores_Draw                                                *)
(*                                                                          *)
(*    Purpose:    Draws line between two points in low-res. graphics mode   *)
(*                                                                          *)
(*    Calling Sequence:                                                     *)
(*                                                                          *)
(*       Lores_Draw( X1 , Y1 , X2, Y2, LineCol : INTEGER );                 *)
(*                                                                          *)
(*           X1      -- Horizontal postion (0 through 159), 1st point       *)
(*           Y1      -- Vertical position (0 through 119), 1st point        *)
(*           X2      -- Horizontal postion (0 through 159), 2nd point       *)
(*           Y2      -- Vertical position (0 through 119), 2nd point        *)
(*           LineCol -- Color (0 through 15) of line                        *)
(*                                                                          *)
(*    Calls:  Lores_Plot                                                    *)
(*                                                                          *)
(*    Remarks: An incremental plotter algorithm is used.                    *)
(*                                                                          *)
(*--------------------------------------------------------------------------*)

VAR
   Xinc:       INTEGER;
   Yinc:       INTEGER;
   Dx:         INTEGER;
   Cdx:        INTEGER;
   Dy:         INTEGER;
   Cdy:        INTEGER;
   X:          INTEGER;
   Y:          INTEGER;
   Correc_Inc: INTEGER;
   Plotit:     BOOLEAN;

BEGIN (* Lores_Draw *)
                                   (* Starting point *)
   X  := X1;
   Y  := Y1;
                                   (* Changes in (x,y) direction *)
   Dx := X2 - X1;
   Dy := Y2 - Y1;
                                   (* Set increments *)
   IF Dx > 0 THEN
      Xinc := 1
   ELSE
      BEGIN
         Xinc := -1;
         Dx   := -Dx;
      END;

   IF Dy > 0 THEN
      Yinc := 1
   ELSE
      BEGIN
         Yinc := -1;
         Dy   := -Dy;
      END;
                                   (* Reset_Inc is correction value *)
   IF Dy > Dx THEN
      Correc_Inc := Dy
   ELSE
      Correc_Inc := Dx;

   Cdx := Correc_Inc;
   Cdy := Correc_Inc;

                                   (* Plot first point *)
   Lores_Plot( X, Y, LineCol );

                                   (* Plot remaining points *)

   WHILE( ( X <> X2 ) AND ( Y <> Y2 ) ) DO
      BEGIN

         PlotIt := FALSE;

         Cdx    := Cdx - Dx;

         IF Cdx < 0 THEN
            BEGIN
               PlotIt := TRUE;
               X      := X + Xinc;
               Cdx    := Cdx + Correc_Inc;
            END;

         Cdy := Cdy - Dy;

         IF Cdy < 0 THEN
            BEGIN
               PlotIt := TRUE;
               Y      := Y + Yinc;
               Cdy    := Cdy + Correc_Inc;
            END;

         IF PlotIt THEN Lores_Plot( X, Y, LineCol );

      END;

END   (* Lores_Draw *);

(*--------------------------------------------------------------------------*)
(*               D E M O N S T R A T I O N   R O U T I N E S                *)
(*--------------------------------------------------------------------------*)

(*--------------------------------------------------------------------------*)
(*         Lores_Demo1 -- Draw embedded frames using Lo-Res graphics        *)
(*--------------------------------------------------------------------------*)

PROCEDURE Lores_Demo1;

(*--------------------------------------------------------------------------*)
(*                                                                          *)
(*    Procedure:  Lores_Demo1                                               *)
(*                                                                          *)
(*    Purpose:    Draws series of embedded frames                           *)
(*                                                                          *)
(*    Calling Sequence:                                                     *)
(*                                                                          *)
(*       Lores_Demo1;                                                       *)
(*                                                                          *)
(*    Calls:  Lores_Plot                                                    *)
(*                                                                          *)
(*--------------------------------------------------------------------------*)

BEGIN (* Lores_Demo1 *)

   PixCol := 0;
   Y      := 0;

   FOR T := 1 TO 99 DO
      BEGIN
         Y := Y + 1;
         IF ( T MOD 5 ) = 0 THEN
            PixCol := ( PixCol + 1 ) MOD 15;
         FOR X := 1 TO T DO
            Lores_Plot( X , Y , PixCol );
      END;

   X      := 1;
   PixCol := 8;

   FOR T := 1 TO 99 DO
      BEGIN
         X := X + 1;
         IF ( T MOD 5 ) = 0 THEN PixCol := ( PixCol + 1 ) MOD 15;
         FOR Y := 1 TO T DO
            Lores_Plot( X , Y , PixCol );
      END;

END   (* Lores_Demo1 *);


(*--------------------------------------------------------------------------*)
(*         Lores_Demo2 -- Borland's ART.PAS for lores graphics              *)
(*--------------------------------------------------------------------------*)

PROCEDURE Lores_Demo2;

(*--------------------------------------------------------------------------*)
(*                                                                          *)
(*    Procedure:  Lores_Demo2                                               *)
(*                                                                          *)
(*    Purpose:    Draws series of lines at various angles and colors.       *)
(*                From Borland's ART.PAS demonstration program for          *)
(*                medium-resolution graphics.                               *)
(*                                                                          *)
(*    Calling Sequence:                                                     *)
(*                                                                          *)
(*       Lores_Demo2;                                                       *)
(*                                                                          *)
(*    Calls:  Lores_Plot                                                    *)
(*                                                                          *)
(*--------------------------------------------------------------------------*)

Const
  Memory = 150;

var
  Line:  array [1..Memory] of record
                                LX1,LY1: integer;
                                LX2,LY2: integer;
                                LColor:  integer;
                              end;
  X1,X2,Y1,Y2,
  CurrentLine,
  ColorCount,
  IncrementCount,
  DeltaX1,DeltaY1,DeltaX2,DeltaY2,
  I,Color:            integer;
  Ch: char;

procedure Init;
begin
  for I:=1 to Memory do
  with Line[I] do
  begin
    LX1:=0; LX2:=0;
    LY1:=0; LY2:=0;
  end;
  X1:=0; Y1:=0; X2:=0; Y2:=0;
  CurrentLine:=1;
  ColorCount:=0;
  IncrementCount:=0;
  Ch:=' ';
end;

procedure AdjustX(var X,DeltaX: integer);
var
  TestX: integer;
begin
  TestX:=X+DeltaX;
  if (TestX<1) or (TestX>160) then
  begin
    TestX:=X;
    DeltaX:=-DeltaX;
  end;
  X:=TestX;
end;

procedure AdjustY(var Y,DeltaY: integer);
var
  TestY: integer;
begin
  TestY:=Y+DeltaY;
  if (TestY<1) or (TestY>90) then
  begin
    TestY:=Y;
    DeltaY:=-DeltaY;
  end;
  Y:=TestY;
end;

procedure SelectNewColor;
begin
  Repeat
     Color:=Random(16)+1;
  Until not ( Color in [0,8] ) ;
  ColorCount:=5*(1+Random(10));
end;

procedure SelectNewDeltaValues;
begin
  DeltaX1:=Random(7)-3;
  DeltaX2:=Random(7)-3;
  DeltaY1:=Random(7)-3;
  DeltaY2:=Random(7)-3;
  IncrementCount:=4*(1+Random(9));
end;


procedure SaveCurrentLine;
begin
  with Line[CurrentLine] do
  begin
    LX1:=X1;
    LY1:=Y1;
    LX2:=X2;
    LY2:=Y2;
    LColor:=Color;
  end;
end;


procedure Regenerate;
var
  I: integer;
begin

  NoSound;

  Lores_GraphMode;
  for I:=1 to Memory do with Line[I] do Lores_Draw(LX1,LY1,LX2,LY2,LColor);
  read(Kbd,Ch);
end;

begin (* Lores_Demo2 *)

  Init;
                                   (* Set up lo-res color graphics mode *)
  Lores_GraphMode;

  Color:=2;
  gotoxy(1,11);
  repeat
    with Line[CurrentLine] do Lores_Draw(LX1,LY1,LX2,LY2,0);

    if ColorCount=0 then SelectNewColor;
    if IncrementCount=0 then SelectNewDeltaValues;

    AdjustX(X1,DeltaX1); AdjustX(X2,DeltaX2);
    AdjustY(Y1,DeltaY1); AdjustY(Y2,DeltaY2);

    Lores_Draw(X1,Y1,X2,Y2,Color);

    SaveCurrentLine;

    CurrentLine:=Succ(CurrentLine);
    if CurrentLine>Memory then CurrentLine:=1;
    ColorCount:=Pred(ColorCount); IncrementCount:=Pred(IncrementCount);

    if KeyPressed then
    begin
      read(Kbd,Ch);
      if Ch<>#27 then
      begin
        Regenerate;
      end;
    end;
  until Ch=#27;
  TextMode;

END   (* Lores_Demo2 *);


(*--------------------------------------------------------------------------*)
(*                       PibLoRes --- Main Program                          *)
(*--------------------------------------------------------------------------*)

BEGIN (* PibLoRes *)

   ClrScr;
                                   (* Announce demonstration *)

   WRITELN('This program demonstrates a low-resolution graphics mode');
   WRITELN('of 160 horizontal by 100 vertical positions, with 16 colors');
   WRITELN('available for each plotting position.');
   WRITELN(' ');
   WRITELN('The first demonstration produces a series of embedded frames.');
   WRITELN('After the frames appear, hit the carriage return key to ');
   WRITELN('go on to the next demonstration.' );
   WRITELN(' ');
   WRITELN('Hit the carriage return (enter) key to start the demo.');

   WHILE( NOT KeyPressed ) DO;
   WHILE KeyPressed DO READ( KBD , C );

                                   (* Do frame demo *)
   Lores_GraphMode;
   Lores_Demo1;
                                   (* Wait for input to clear screen *)
   WHILE( NOT KeyPressed ) DO;
   WHILE KeyPressed DO READ( KBD , C );

                                   (* Announce second demo *)
   GraphMode;
   TextMode( C80 );

   WRITELN('The second demonstration is a modified version of the');
   WRITELN('sample ART.PAS program from the Turbo Pascal release disk.');
   WRITELN('To exit this demonstration, hit the ESCAPE key.  To restart');
   WRITELN('this demonstration, hit the return key.');
   WRITELN(' ');
   WRITELN('Hit the carriage return (enter) key to start the demo.');

   WHILE( NOT KeyPressed ) DO;
   WHILE KeyPressed DO READ( KBD , C );

                                   (* Do ART demo *)
   Lores_GraphMode;
   Lores_Demo2;

                                   (* Ensure screen left in text mode *)
   GraphMode;
   TextMode( C80 );

END   (* PibLoRes *).