home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PASCAL / NUTUG11.ZIP / VIDEO.PAS < prev   
Encoding:
Pascal/Delphi Source File  |  1987-11-22  |  43.9 KB  |  1,311 lines

  1. Unit Video;
  2.  
  3.             {NORTHWESTERN UNIVERSITY TURBO USERS GROUP UTILITIES}
  4.  
  5.                       (** NUtility VIDEO ROUTINES **)
  6.  
  7.                          {(C) J. E. Hilliard 1986}
  8.  
  9.          {This is a set of routines for manipulating the video text
  10.          displays on either a monochrome or a color/graphics monitor.
  11.          The routines that involve transfers between of the video
  12.          will have to be modified for graphic displays. Note, how-
  13.          ever the availabilty of the TURBO built-in routines: PutPic
  14.          and GetPic which will be faster than similar routines writ-
  15.          ten in source language. Modifications may also be required
  16.          for video cards other than the standard Monochrome or Color/
  17.          graphics.                                                   }
  18.  
  19.          {/Some of the routines provide examples of the use of pointer
  20.          variables, memory block moves and BIOS video services. A good
  21.          treatment of video basics is to be found in Chapter 4 of the
  22.          book by Norton.                                            /}
  23.  
  24. INTERFACE
  25.  
  26. Uses     Dos,
  27.          Crt;
  28.  
  29.  
  30. PROCEDURE LVid;
  31.          {Replaces TURBO LowVideo.                                          }
  32.  
  33. PROCEDURE NVid;
  34.          {Replaces TURBO NormVideo.                                         }
  35.  
  36. PROCEDURE RVid;
  37.          {Reverse Video - Black on a bright background.                     }
  38.  
  39. PROCEDURE NoVid;
  40.          {At first sight, there would appear to be little need for an
  41.          invisible video mode. However, it is occasionally useful for
  42.          erasing part of a line by overwriting in this mode.        }
  43.  
  44. PROCEDURE UVid;
  45.          {Underlines the current video mode on a monochrome monitor
  46.          and yields a non-underlined blue foreground on a color
  47.          monitor. (NOTE: There is no underline RVid mode.)        }
  48.  
  49. PROCEDURE BlinkVid;
  50.          {Sets the current video mode blinking for subsequent input
  51.          until the next video command is given.                      }
  52.  
  53. PROCEDURE PrintScreen;
  54.          {This is equivalent to a PrtSc from the keyboard. NOTE: This
  55.          routine may not function properly if the user has not loaded
  56.          a PSC file.                                                 }
  57.  
  58. PROCEDURE SwapPage (P1, P2 : integer);
  59.          {This procdedure exchanges the contents of text pages P1 and
  60.          P2 (0 <= P1, P2 <= 3). It also sets the cursors to the correct
  61.          locations in both pages.                                    }
  62.  
  63. PROCEDURE CopyPage (P1, P2 : integer);
  64.          {Copies Page P1 to Page P2. Contents of Page P1 are retained.}
  65.  
  66. PROCEDURE CopyBufferToPage (VAR Buffer; P : integer);
  67.          {Copies contents of Buffer to Page P.                       }
  68.  
  69. PROCEDURE DisplayPage (P : integer);
  70.          {Uses a call to interrupt $10 to transfer display to Page P}
  71.  
  72. PROCEDURE ClearPage (P : integer);
  73.          {Loads addresses in Page P with 0's thus clearing the page.
  74.          It is equivalent to: LowVideo; ClrScr; on the display page.
  75.          NOTE: ClrScr clears the page presently been displayed and
  76.          not Page 0 as one might expect.                             }
  77.  
  78. PROCEDURE Cursor (ONorOFF : Boolean);
  79.          {Turns screen cursor ON or OFF.                                    }
  80.  
  81. PROCEDURE DisplayVideoModes;
  82.          {This displays the 128 possible non-blinking video modes
  83.           available on the IBM PC and compatables.                }
  84.  
  85. PROCEDURE Frame (TX, TY, BX, BY : integer; Form : byte);
  86.           {The input parameters are the coordinates for the top-left
  87.           and bottom-right corners of the frame and are with reference
  88.           to the inside corners. (This is for convenience when using
  89.           the TURBO Window procedure.)  If TY = BY then a single hori-
  90.           zontal line is drawn and if TX = BX a single vertical line.
  91.           Form = 1 uses the -- graph symbol and Form = 2 the === symbol.
  92.           The video modes on entry determine the attributes of the
  93.           boarder.                                                    }
  94.  
  95. FUNCTION  ModeCG : Boolean;
  96.          {If color/graphics card is installed changes display to this
  97.          card. Returns false if card not found.                      }
  98.  
  99. FUNCTION ModeMono : Boolean;
  100.          {If Monochrome card is installed changes display to this card.
  101.          Returns false if card not found.                             }
  102.  
  103. FUNCTION ScreenToFile (FileName : String) : Boolean;
  104.          {Stores screen display  on disk as the  file 'FileName'.  Re-
  105.          turns 'False' if there is an I/O error.                     }
  106.  
  107. FUNCTION FileToScreen (FileName : String) : Boolean;
  108.  
  109.          {Loads and displays screen stored in 'FileName'. Returns false
  110.          if there is an I/O error.                                   }
  111.  
  112. FUNCTION HeapOK (BytesReqd : LongInt) : Boolean;
  113.           {Returns true if there is 'BytesReqd' contiguous bytes avail-
  114.           able on the heap.                                           }
  115.  
  116. FUNCTION LPushPopScr (ONorOFF : Boolean) : Boolean;
  117.          {The purpose of this function is to store or retrieve text
  118.          displays on or from the heap in a first-in last-out sequence.
  119.          The function acts in two modes depending on the value of:
  120.  
  121.          ONorOFF = ON
  122.          ------------
  123.               The display on the current monitor (monochrome or color)
  124.          is stored on the heap and the function returns 'TRUE' if there
  125.          is sufficient memory available to store another screen.
  126.  
  127.          ONorOFF = OFF
  128.          -------------
  129.               The last stored stored screen is transferred from the
  130.          heap to the current monitor and the heap space is recovered.
  131.          The function returns 'FALSE' if the heap is empty.
  132.  
  133.          This function can be used in conjunction with 'Frame' and
  134.          'Window' to display pull-down menus. (See: PROCEDURE Screen-
  135.          StackDemo.)                                                        }
  136.  
  137. FUNCTION TPushPopScr (ONorOFF : Boolean) : Boolean;
  138.           {This function is operationally similar to LPushPopScr.     }
  139.  
  140. implementation
  141.  
  142. CONST
  143.  
  144.   ON         = true;
  145.   OFF        = false;
  146.     {/In some applications ON/OFF is more descriptive than true/false./}
  147.   MonoSeg    = $B000;                  {Segment address of Mono Ram.        }
  148.   CGSeg      = $B800;                  {   "        "    " C/G Ram.         }
  149.   ScrMemSize = $FA0;                   {Text: 2 * 80 * 25. For graphics     }
  150.                                        {change to $4000 (16K).              }
  151.  
  152.          (** The following define the attribute bytes used by the vid-
  153.          eo commands. They are defined as typed constants (rather than
  154.          global variables) so that they will be stored in the code
  155.          area and can therefore be saved after modification by the
  156.          user of a COM file.                                       **)
  157.  
  158.   Attr     : byte = $6;                {This holds the attribute of the     }
  159.   LowAttr  : byte = $6;                {current video setting.              }
  160.   NormAttr : byte = $1E;
  161.   RevAttr  : byte = $70;
  162.  
  163. TYPE
  164.  
  165.           {/TURBO uses a record type variable to pass information to
  166.           and from the 8088 registers that are used for function calls.
  167.           The X registers (A - D) are each two bytes long, but very fre-
  168.           quently they are split into a low order and high order byte
  169.           designated by an L and H respectively. For example, AX is an
  170.           integer register and AL and AH are the byte components. A
  171.           neat way of allowing for the addressing of either type of
  172.           register without having to do bitwise manipulations is to
  173.           define a variant record.
  174.  
  175.           In a variant record (which is a Pascal speciality) one can set
  176.           up overlapping fields. In the case of RegType declared above,
  177.           variant 2 occupies the same memory locations as variant 1.
  178.           Thus AL is the low order byte of the integer AX.
  179.  
  180.           Another application of variant records is to be found in the
  181.           REALFAST routines.
  182.  
  183.           A good treatment of DOS interrupts is given in Chapter 16 of
  184.           Norton's book.                                             /}
  185.  
  186.  
  187.   ScrRAMType = array [1..ScrMemSize] of byte;
  188.   ScrPtrType = ^ScrRAMType;
  189.   MaxLine    = string;
  190.  
  191.                          (**  VIDEO MODE COMMANDS  **)
  192.  
  193.          {The following set of procedures supplement the LowVideo and
  194.          NormVido commands provided by TURBO. The latter provide un-
  195.          satisfactory displays on some monitors. DisplayVideoModes
  196.          can be used to select suitable values for insertion in the
  197.          table of attribute constants. The routines use the standard
  198.          TURBO color commands.                                       }
  199.  
  200.          {/In text mode, each of the possible 2000 (80 x 25) charac-
  201.          ters on the screen is stored in a buffer as a pair of bytes.
  202.          One is the ASCII value of the character and the other deter-
  203.          mines the 'attribute' of the character display (ie. the color
  204.          and intensity). [It would have made some coding simpler and
  205.          faster in execution if the bytes had been stored in separate
  206.          arrays, but IBM decided otherwise.] The mapping of the attri-
  207.          bute byte can be found in Norton (p. 80). The TURBO command
  208.          TextColor () can be used to set bits 0 to 3 and the command
  209.          TextBackground () bits 4 to 6. (Illogically, bit 7 is set by
  210.          adding 16 to the argument of TextColor.)                   /}
  211.  
  212.  
  213. PROCEDURE LVid;
  214.  
  215.          {Replaces TURBO LowVideo.                                          }
  216.  
  217. Begin
  218.  
  219.   Attr := LowAttr;
  220.   TextColor (Attr and $0F);
  221.   TextBackground (Attr shr 4);
  222.  
  223. End; {LVid}
  224.  
  225.  
  226. PROCEDURE NVid;
  227.  
  228.          {Replaces TURBO NormVideo.                                         }
  229.  
  230. Begin
  231.  
  232.   Attr := NormAttr;
  233.   TextColor (Attr and $0F);
  234.   TextBackground (Attr shr 4);
  235.  
  236. End; {NVid}
  237.  
  238.  
  239. PROCEDURE RVid;
  240.  
  241.          {Reverse Video - Black on a bright background.                     }
  242.  
  243. Begin
  244.  
  245.   Attr := RevAttr;
  246.   TextColor (Attr and $0F);
  247.   TextBackground (Attr shr 4);
  248.  
  249. End; {RVid}
  250.  
  251.  
  252. PROCEDURE NoVid;
  253.  
  254.          {At first sight, there would appear to be little need for an
  255.          invisible video mode. However, it is occasionally useful for
  256.          erasing part of a line by overwriting in this mode.        }
  257.  
  258. Begin
  259.  
  260.   TextColor (0);
  261.   TextBackground (0);
  262.   Attr := 0;
  263.  
  264. End;
  265.  
  266. PROCEDURE UVid;
  267.  
  268.          {Underlines the current video mode on a monochrome monitor
  269.          and yields a non-underlined blue foreground on a color
  270.          monitor. (NOTE: There is no underline RVid mode.)        }
  271.  
  272. Begin
  273.  
  274.   Attr := Attr and $8 or $01;
  275.   TextColor (Attr);
  276.  
  277. End; {UVid}
  278.  
  279.  
  280. PROCEDURE BlinkVid;
  281.  
  282.          {Sets the current video mode blinking for subsequent input
  283.          until the next video command is given.                      }
  284.  
  285.          {/The blink mode is obtained by setting bit 7 of the attribute
  286.          byte. However, the TURBO command (Manual p. 161) is not a
  287.          logical one, since it requires adding 16 to the text color./}
  288.  
  289. Begin
  290.  
  291.   TextColor (Attr and $0F + $10);
  292.  
  293. End; {BlinkVid}
  294.  
  295.  
  296.     (***** CHANGING THE VIDEO ATTRIBUTES USED BY THE TURBO EDITOR *****)
  297.  
  298.          {   For some users (particularly those with color monitors)
  299.          the choice made by Borland for the text editor video modes
  300.          is not a happy one. The following information will enable
  301.          you to change the modes if you so desire. The procedure
  302.          DisplayVideoModes can be run to provide a guide in the select-
  303.          ion of replacement attribute bytes.
  304.  
  305.              For PC DOS Turbo Pascal V3.10 (including the 8087 and BCD
  306.          versions) the character attributes used by the editor are
  307.          stored in eight bytes starting at location CS:016F. The first
  308.          four bytes are used for display on the Monochrome monitor and
  309.          the second four for a monitor attached to the color/graphics
  310.          card. The following is the disposition of the bytes (numbers
  311.          are in Hex):
  312.                        ------------------------------------
  313.                        Mono    C/G        Use         Value
  314.                        ------------------------------------
  315.                        016F    0173    Text             0F
  316.                        0170    0174    Menus            07
  317.                        0171    0175    Block marking    07
  318.                        0172    0176    Error Messages   70
  319.                        ------------------------------------
  320.  
  321.               Note, these attribute bytes are used only by the editor
  322.          and have no effect on the video commands in a Pascal program.
  323.  
  324.               When the TURBO.COM is loaded, it first checks which type
  325.          of monitor is being used and then copies the appropriate four
  326.          bytes into higher memory. For the non 8087/BCD version the
  327.          starting location is 9C00. By addressing these locations from
  328.          within Turbo one could change the editor attributes after the
  329.          program is loaded. However, it is much better to patch the loc-
  330.          ations listed above using the DOS utility DEBUG.
  331.  
  332.          {/If your are unfamiliar with DEBUG ask someone to show you how
  333.          to make the patch. (Its easier than trying to understand the
  334.          manual.) Also, make the patch on a COPY of your working file
  335.          and test the patched copy thoroughly before deleting the orig-
  336.          inal.                                                        /}
  337.  
  338.     (*******************************************************************)
  339.  
  340.                          (** DISPLAY MODE HANDLING **)
  341.  
  342.           {/In a non-enhanced PC (ie. one not containing a special
  343.           graphics card) there are two possible video modes. One uti-
  344.           lizes a Color/Graphics card and the other, which can only be
  345.           used for text, a Monochrome card. (The term 'Monochrome' is
  346.           somewhat confusing since the Color/Graphics card may be con-
  347.           nected to a black and white monitor and, in that sense, is
  348.           also monochrome.)
  349.  
  350.           For routines that directly access the video memory it is
  351.           necessary to know which monitor is being used. Obviously, it
  352.           would be tacky to query the user (who is probably not to be
  353.           relied on anyway). Two routines are therefore provided for
  354.           eliciting the information from the system.
  355.  
  356.           If both types of monitor are connected routines are provided
  357.           for switching between the two monitors.                   /}
  358.  
  359.  
  360. FUNCTION DisplayMode : byte;
  361.  
  362.           {/Service $F of interrupt $10 returns information about the
  363.           current video mode. One important application is determining
  364.           whether a color/graphics or monochrome is the display device.
  365.           This function returns the contents of the AL register. A value
  366.           of 7 denotes a monochrome monitor.  [See Norton, p. 184.] /}
  367.  
  368. VAR
  369.  
  370.   Reg    : Registers;
  371.  
  372. begin
  373.  
  374.   Reg.AH := $0F;
  375.   intr ($10, Reg);
  376.   DisplayMode := Reg.AL;
  377.  
  378. End; {DisplayMode : byte}
  379.  
  380.  
  381. FUNCTION DisplayP : ScrPtrType;
  382.  
  383.           {This function returns a pointer to the address of the cur-
  384.           rent display memory. It is very useful when doing direct mem-
  385.           ory transfers to the screen since it avoids having to dupli-
  386.           cate the code for the two different types of display. NOTE:
  387.           The compiler will not allow the use of DisplayP^ in a com-
  388.           mand. This is easily circumvented by declaring a holding var-
  389.           iable (say HoldP), and setting HoldP := DisplayP.  HoldP^ can
  390.           then be used in the command.                                }
  391.  
  392.           {/This routine provides an example of the use of pointers for
  393.           purposes other than managing heap variables.                /}
  394.  
  395. VAR
  396.  
  397.   Reg    : Registers;
  398.  
  399. begin
  400.  
  401.   Reg.AH := $0F;                       {The function DisplayMode is dupli-  }
  402.   intr ($10, Reg);                     {cated to save execution time.       }
  403.   if  Reg.AL = 7                       {Display mode is returned in AL. For }
  404.     then                               {a monochrome monitor, AL = 7.       }
  405.       DisplayP := Ptr (MonoSeg, $0)
  406.     else
  407.       DisplayP := Ptr (CGSeg, $0);
  408.  
  409. End; {DisplayP : ScrPtrType}
  410.  
  411.  
  412. FUNCTION  ModeCG : Boolean;
  413.  
  414.          {If color/graphics card is installed changes display to this
  415.          card. Returns false if card not found.                      }
  416.  
  417.          (*** CAUTION: Some RAM resident utilities (Sidekick V 1.0 is
  418.          one) get confused if displays are switched after they have
  419.          they have been loaded.                                   ***)
  420.  
  421. VAR
  422.  
  423.   CGT : integer absolute $B800:$FF0;   {Address within CG buffer.           }
  424.   B   : byte absolute $0:$410;         {Location equipment-list word. See   }
  425.                                        {Norton p. 53.                       }
  426. Begin
  427.  
  428.   ModeCG := false;
  429.   CGT    := 1234;
  430.   if CGT <> 1234 then
  431.     Exit;                              {No color/graphics board.            }
  432.   B := $2F;
  433.   ModeCG := true;
  434.  
  435. End; {ModeCG : Boolean}
  436.  
  437.  
  438. FUNCTION ModeMono : Boolean;
  439.  
  440.          {If Monochrome card is installed changes display to this card.
  441.          Returns false if card not found.                             }
  442.  
  443.  
  444. VAR
  445.  
  446.   MT  : integer absolute $B000:$FF0;   {Address within mono buffer.         }
  447.   B   : byte absolute $0:$410;
  448.  
  449. Begin
  450.  
  451.   ModeMono := false;
  452.   MT := 1234;
  453.   if MT <> 1234 then                   {No monochrome card.                 }
  454.     Exit;
  455.   B := $3F;
  456.   ModeMono := true;
  457.   TextMode(Lo(LastMode));                            {Necessary for proper functioning of }
  458.                                        {the command, but not clear why.     }
  459. End; {ModeMono : Boolean}
  460.  
  461.                      (** SCREEN BUFFER TRANSFERS **)
  462.  
  463. PROCEDURE PrintScreen;
  464.  
  465.          {This is equivalent to a PrtSc from the keyboard. NOTE: This
  466.          routine may not function properly if the user has not loaded
  467.          a PSC file.                                                 }
  468.  
  469.          {/This routine provides a good opportunity for demonstrating
  470.          INLINE coding, since it is the most economical way of gener-
  471.          ating a DOS interrupt that does not involve any input or out-
  472.          put.                                                       /}
  473.  
  474. Begin
  475.  
  476.   Inline ($CD/$05);                    {INT 05                              }
  477.  
  478. End; {PrintScreen}
  479.  
  480.  
  481. FUNCTION ScreenToFile (FileName : MaxLine) : Boolean;
  482.  
  483.          {Stores screen display  on disk as the  file 'FileName'.  Re-
  484.          turns 'False' if there is an I/O error.                     }
  485.  
  486.          {/In TURBO V2 the record length for a Block Read or Write was
  487.          fixed at 128 bytes. In V3 an optional argument was added to
  488.          the ReSet and ReWrite commands allowing for the specification
  489.          of the record size. This feature is not documented in the man-
  490.          ual but is described in the 'ReadMe' file.                 /}
  491.  
  492. VAR
  493.  
  494.   HoldP   : ScrPtrType;
  495.   OutFile : File;
  496.  
  497. Begin
  498.  
  499.   ScreenToFile := false;
  500.   HoldP := DisplayP;
  501.   Assign (OutFile, FileName);
  502.   {$I-} Rewrite (OutFile, ScrMemSize);
  503.   BlockWrite (OutFile, HoldP^, 1); {$I+}
  504.   if IOresult <> 0 then
  505.     Exit;
  506.   Close (OutFile);
  507.   ScreenToFile := true;
  508.  
  509. End; {ScreenToFile (FileName : MaxLine) : Boolean}
  510.  
  511.  
  512. FUNCTION FileToScreen (FileName : MaxLine) : Boolean;
  513.  
  514.          {Loads and displays screen stored in 'FileName'. Returns false
  515.          if there is an I/O error.                                   }
  516.  
  517.  
  518. VAR
  519.  
  520.   HoldP   : ScrPtrType;
  521.   InFile  : File;
  522.  
  523. Begin
  524.  
  525.   FileToScreen := false;
  526.   HoldP := DisplayP;
  527.   Assign (InFile, FileName);
  528.   {$I-} ReSet (InFile, ScrMemSize);
  529.   BlockRead (InFile, HoldP^, 1); {$I+}
  530.   if IOresult <> 0 then
  531.     Exit;
  532.   Close (InFile);
  533.   FileToScreen := true;
  534.  
  535. End; {FileToScreen (FileName : MaxLine) : Boolean}
  536.  
  537.  
  538.              (** MANIPULATION OF TEXT PAGES ON C/G BOARD **)
  539.  
  540.          {/The Color/Graphics card contains 16K of memory which is
  541.          fully used when in the graph mode. However, in the text mode
  542.          only 4K is required for the display of one screen of text.
  543.          Hence the memory is divided into 4 'pages' each of which can
  544.          store one screen of text. In Basic there is a built-in com-
  545.          mand 'SCREEN' for manipulating the pages. The following
  546.          series of procedures enable similar operations to be perform-
  547.          ed in TURBO.
  548.  
  549.          Switching the page being displayed provides a method of in-
  550.          stantaneously (at least as it appears to the eye) rewriting
  551.          the screen. It also has one important advantage over the
  552.          procedures given above that write directly to the display
  553.          memory. With some C/G boards (in particular those supplied by
  554.          IBM) any re-writing of the memory being displayed produces an
  555.          unpleasant 'snow' effect. There are methods of eliminating
  556.          the effect by writing to the memory only during the vertical
  557.          and horizontal retraces when the screen is blanked. However,
  558.          this requires a fairly complex buffering technique and also
  559.          slows down the re-writing. Page switching does not produce
  560.          any snow. (It is somewhat irritating to know that the snow
  561.          problem only exists because of shortcuts in the design. Some
  562.          boards - for example those supplied by Zenith - do not
  563.          exhibit this problem.)
  564.  
  565.          (* It may be clear that Prof. Hilliard owned a Zenith 151    *)
  566.  
  567.          As far as I know, it is only possible using the built-in
  568.          commands of TURBO to write to Page 0. I do have procedures
  569.          for writing to any part of RAM, but they need cleaning up.
  570.          But in any case, we need something in reserve for NUTILITY
  571.          V2.
  572.  
  573.          Obviously these procedures can only be used if C/G board is
  574.          installed. The standard Monochrome board contains only 4K of
  575.          memory and therefore does not allow for paging.            /}
  576.  
  577.  
  578. PROCEDURE SwapPage (P1, P2 : integer);
  579.  
  580.          {This procdedure exchanges the contents of text pages P1 and
  581.          P2 (0 <= P1, P2 <= 3). It also sets the cursors to the correct
  582.          locations in both pages.                                    }
  583.  
  584. TYPE
  585.  
  586.   ScreenClass = array[$1..$1000] of byte;
  587.  
  588. VAR
  589.  
  590.   CGScrs : array [0..3] of ScreenClass absolute $B800:$0;
  591.   ScrHLD : ScreenClass;
  592.   CurPos : array [0..3] of integer absolute $0000:$0450;
  593.   CurHLD : integer;
  594.  
  595. Begin
  596.  
  597.   if (not P1 in [0..3]) or (not P2 in [0..3]) then
  598.     Exit;                              {Input error.                        }
  599.  
  600.   CurHLD     := CurPos[P1];            {Exchange cursor positions.          }
  601.   CurPos[P1] := CurPos[P2];
  602.   CurPos[P2] := CurHLD;
  603.  
  604.   Move (CGScrs[P1], ScrHLD,     SizeOf (ScrHLD));
  605.   Move (CGScrs[P2], CGScrs[P1], SizeOf (ScrHLD));
  606.   Move (ScrHLD,     CGScrs[P2], SizeOf (ScrHLD));
  607.  
  608. End; {SwapPage (P1, P2 : integer)}
  609.  
  610.  
  611. PROCEDURE CopyPage (P1, P2 : integer);
  612.  
  613.          {Copies Page P1 to Page P2. Contents of Page P1 are retained.}
  614.  
  615. TYPE
  616.  
  617.   ScreenClass = array[$1..$1000] of byte;
  618.  
  619. VAR
  620.  
  621.   CGScrs : array [0..3] of ScreenClass absolute $B800:$0;
  622.   CurPos : array [0..3] of integer     absolute $0000:$0450;
  623.  
  624. Begin
  625.  
  626.   if (not P1 in [0..3]) or (not P2 in [0..3]) then
  627.     Exit;
  628.  
  629.   CurPos[P2] := CurPos[P1];
  630.   Move (CGScrs[P1], CGScrs[P2], SizeOf (CGScrs[P1]));
  631.  
  632. End; {CopyPage (P1, P2 : integer)}
  633.  
  634.  
  635. PROCEDURE CopyBufferToPage (VAR Buffer; P : integer);
  636.  
  637.          {Copies contents of Buffer to Page P.                       }
  638.  
  639. TYPE
  640.  
  641.   ScreenClass = array[$1..$1000] of byte;
  642.  
  643. VAR
  644.  
  645.   CGScrs : array [0..3] of ScreenClass absolute $B800:$0;
  646.   Start  : ScreenClass absolute Buffer;
  647.  
  648. Begin
  649.  
  650.   Move (Start, CGScrs[P], SizeOf (Start));
  651.  
  652. End; {CopyBufferToPage (VAR Buffer; P : integer)}
  653.  
  654.  
  655. PROCEDURE DisplayPage (P : integer);
  656.  
  657.          {Uses a call to interrupt $10 to transfer display to Page P}
  658.  
  659. VAR
  660.  
  661.   Reg : Registers;
  662.  
  663. Begin
  664.  
  665.   if not (P in [0..3]) then
  666.     Exit;
  667.   Reg.AX := $0500 + P;
  668.   Intr ($10, Reg);
  669.  
  670. End; {DisplayPage (P : integer)}
  671.  
  672.  
  673. PROCEDURE ClearPage (P : integer);
  674.  
  675.          {Loads addresses in Page P with 0's thus clearing the page.
  676.          It is equivalent to: LowVideo; ClrScr; on the display page.
  677.          NOTE: ClrScr clears the page presently been displayed and
  678.          not Page 0 as one might expect.                             }
  679.  
  680. TYPE
  681.  
  682.   ScreenClass = array[$1..$1000] of byte;
  683.  
  684. VAR
  685.  
  686.   CGScrs : array [0..3] of ScreenClass absolute $B800:$0;
  687.   CurPos : array [0..3] of integer     absolute $0000:$0450;
  688.  
  689. Begin
  690.  
  691.   if not P in [0..3] then
  692.     Exit;
  693.  
  694.   CurPos [P] := 0;
  695.   FillChar (CGScrs[P], SizeOf (CGScrs[P]), 0);
  696.  
  697. End; {ClearPage (P : integer)}
  698.  
  699.  
  700. PROCEDURE PageDemonstration;
  701.  
  702.          {This provides a demonstration of the use of some of the
  703.          page manipulating routines.                                 }
  704.  
  705. VAR
  706.  
  707.   J,  K    : integer;
  708.   ch       : char;
  709.  
  710. Procedure Message;
  711.  
  712. begin
  713.  
  714.   GoToXY (5, 24);
  715.   write ('Enter 0 - 3 to display page or ''Q'' to quit.');
  716.  
  717. end; {Message}
  718.  
  719. Begin
  720.  
  721.   TextMode(Lo(LastMode));
  722.   DisplayPage (1);
  723.   GoToXY (33, 10); write ('Please Stand By');
  724.   CopyPage (0, 3);
  725.   DisplayPage (3);
  726.   for J := 1 to 2 do
  727.     begin
  728.       ClearPage (0);
  729.       for K := 1 to 1600 do
  730.         write (J);
  731.       Message;
  732.       CopyPage (0, J);
  733.     end;
  734.   DisplayPage (1);
  735.   ClearPage (0);
  736.   for K := 1 to 1600 do
  737.     write ('3');
  738.   Message;
  739.   CopyPage (0, 3);
  740.   ClearPage (0);
  741.   for K := 1 to 1600 do
  742.     write ('0');
  743.   Message;
  744.  
  745.   repeat
  746.     ch := ReadKey;
  747.     J := ord (ch) - 48;
  748.     DisplayPage (J);
  749.   until UpCase (ch) = 'Q';
  750.   TextMode(lo(LastMode));
  751.  
  752. End; {PageDemonstration}
  753.  
  754.  
  755. FUNCTION HeapOK (BytesReqd : LongInt) : Boolean;
  756.  
  757.           {Returns true if there is 'BytesReqd' contiguous bytes avail-
  758.           able on the heap.                                           }
  759.  
  760.           {/Before storing a pointer variable on the heap, it is advis-
  761.           able to check that there is sufficient room because, if there
  762.           isn't, the program will crash. TURBO has two functions that
  763.           allow you to determine the space available: MemAvail and
  764.           MaxAvail. The former returns the total free memory including
  765.           that in holes left by the disposal of previous heap variables
  766.           and the latter returns the largest contiguous block avail-
  767.           able. This is the one that is usually relevant unless you are
  768.           certain that the variable will fit in one of the holes. For
  769.           both functions the values are in units of paragraphs (16
  770.           bytes).                                                    /}
  771.  
  772. (*        Version 4.0 gives the results in bytes, not paragraphs and
  773.           the result is type longint                                 *)
  774.  
  775.           {/One must always be on guard against integer overflow, and
  776.           this is particularly the case when dealing with memory
  777.           functions and also the BlockRead and BlockWrite commands. (If
  778.           I were to be granted only one wish for TURBO V4 it would be
  779.           for the addition of a four-byte integer or, at least, an
  780.           unsigned integer.)                                         /}
  781. (*        We're sorry that Professor Hilliard was not able to see his
  782.           wish come true                                             *)
  783.  
  784. VAR
  785.  
  786.   R1  : LongInt;
  787.  
  788. Begin
  789.  
  790.   R1 := MaxAvail;                {If there is more than 512 K of      }
  791.   HeapOK := (BytesReqd + 400 < R1);    {400 safety factor in case stack will}
  792.                                        {added to.                           }
  793.  
  794. End; {HeapOK (BytesReqd : LongInt) : Boolean}
  795.  
  796.  
  797.  
  798.           (***    Two Examples of the Use of Pointer Variables     ***)
  799.           (***    --------------------------------------------     ***)
  800.  
  801.           {/There are two different ways of using pointers for storing
  802.           variables on the heap. One is to set up an array of pointers.
  803.           This array is stored in the Data Segment and its size must
  804.           therefore be declared at the time the program is compiled.
  805.  
  806.           The second is to use what is known as a 'linked list'. Each
  807.           item in the list contains one or more pointers to another
  808.           item. With this arrangement, it is not necessary to prede-
  809.           clare the maximum number of items (although it is still, of
  810.           course, limited by the amount of memory available). Linked
  811.           lists are commonly used in the construction of data bases.
  812.  
  813.           If your only requirement is to gain additional memory for the
  814.           storage of data, I strongly recommend the first method since
  815.           it requires less coding and is easier to understand. (And, as
  816.           a consequence, less error prone.)
  817.  
  818.           The following Functions for storing and retrieving screen
  819.           images provide examples of the two methods. TPushPopScr uses
  820.           a table of pointers and LPushPopScr a linked list.         /}
  821.  
  822.  
  823. FUNCTION LPushPopScr (ONorOFF : Boolean) : Boolean;
  824.  
  825.          {The purpose of this function is to store or retrieve text
  826.          displays on or from the heap in a first-in last-out sequence.
  827.          The function acts in two modes depending on the value of:
  828.  
  829.          ONorOFF = ON
  830.          ------------
  831.               The display on the current monitor (monochrome or color)
  832.          is stored on the heap and the function returns 'TRUE' if there
  833.          is sufficient memory available to store another screen.
  834.  
  835.          ONorOFF = OFF
  836.          -------------
  837.               The last stored stored screen is transferred from the
  838.          heap to the current monitor and the heap space is recovered.
  839.          The function returns 'FALSE' if the heap is empty.
  840.  
  841.          This function can be used in conjunction with 'Frame' and
  842.          'Window' to display pull-down menus. (See: PROCEDURE Screen-
  843.          StackDemo.)                                                        }
  844.  
  845.  
  846.               {****************** WARNING ******************}
  847.  
  848.          {It is essential that the two pointers FirstP and LastP which
  849.          are stored in the code segment be intitialized to Nil before
  850.          the function is entered for the first time. This can be done
  851.          either by:
  852.  
  853.          (A) Recocompiling the source before re-running or saving to
  854.          a COM file, or
  855.  
  856.          (B) Executing the following routine before exiting the program:
  857.  
  858.                 ***   while LPushPopScr (OFF) do; (*Null*)  ***              }
  859.  
  860.  
  861. TYPE
  862.  
  863.   ScreenRecPtr = ^ScreenRec;
  864.  
  865.   ScreenRec    = Record
  866.                    Screen : ScrRAMType;
  867.                    PrevP  : ScreenRecPtr;
  868.                  end;
  869.  
  870. CONST
  871.  
  872.          {It is necessary to store the pointers FirstP and LastP
  873.          as typed constants so that there values are retained on
  874.          re-entry to the function. The following is a subterfuge
  875.          required because variables of the pointer type cannot be
  876.          formally declared as typed constants.}
  877.  
  878.   LocFirstP : array [1..2] of integer = (0, 0);  {In effect this initial-   }
  879.   LocLastP  : array [1..2] of integer = (0, 0);  {izes the pointers to Nil. }
  880.  
  881. VAR
  882.  
  883.   FirstP : ScreenRecPtr absolute LocFirstP;  {These two pointers will now be  }
  884.   LastP  : ScreenRecPtr absolute LocLastP;   {stored in the code area.        }
  885.   HoldP  : ScreenRecPtr;                     {Temporary so can be local.      }
  886.   P      : ScrPtrType;
  887.  
  888. Procedure Push;
  889.  
  890.          {Adds the current screen to the heap.                       }
  891.  
  892. begin
  893.  
  894.   if not HeapOK (SizeOf (ScreenRec)) then
  895.     Exit;
  896.   if FirstP = Nil                      {This is the first screen to be      }
  897.     then                               {stored. Special attention needed.   }
  898.       begin
  899.         New (FirstP);
  900.         FirstP^.PrevP := Nil;
  901.         LastP := FirstP;
  902.       end
  903.     else
  904.       begin
  905.         HoldP := LastP;
  906.         New (LastP);
  907.         LastP^.PrevP := HoldP;
  908.       end;
  909.  
  910.   P := DisplayP;
  911.   LastP^.Screen := P^;
  912.   LPushPopScr := HeapOK (SizeOf (ScreenRec));
  913.  
  914. end; {Push}
  915.  
  916. Procedure Pop;
  917.  
  918.          {Gets first string on the queue and moves up remaining lines.}
  919.  
  920. begin
  921.  
  922.   if FirstP = Nil then
  923.     Exit;                              {Nothing to Pop.                     }
  924.  
  925.   P := DisplayP;
  926.   P^ := LastP^.Screen;
  927.   if LastP^.PrevP = Nil then          {This was the last screen on the heap.}
  928.     begin
  929.       Dispose (FirstP);
  930.       LastP  := Nil;
  931.       FirstP := Nil;
  932.       Exit;
  933.     end;
  934.  
  935.   HoldP := LastP^.PrevP;
  936.   Dispose (LastP);
  937.   LastP := HoldP;
  938.   LPushPopScr := true;
  939.  
  940. end; {Pop}
  941.  
  942.  
  943. Begin
  944.  
  945.   LPushPopScr := false;                 {Default return.                     }
  946.   if ONorOFF = ON
  947.     then
  948.       Push
  949.     else
  950.       Pop;
  951.  
  952. End; {LPushPopScr (ONorOFF : Boolean) : Boolean;}
  953.  
  954.  
  955. FUNCTION TPushPopScr (ONorOFF : Boolean) : Boolean;
  956.  
  957.           {This function is operationally similar to LPushPopScr.     }
  958.  
  959.           {It is necessary to store the table of pointers either as a
  960.           global variable or as a typed constant so that the table is
  961.           retained on re-entry to the function. We are avoiding the
  962.           use of global variables in these utilities so as to minimize
  963.           the possibility of side efforts. The second method will there-
  964.           fore be used even though it is somewhat clumsy.
  965.  
  966.           One of the problems is that variables of the pointer type
  967.           cannot be formally declared as typed constants. It is there-
  968.           fore necessay to reserve the space with a 'stand-in' array
  969.           of integers. Setting the integers in this array to zero is
  970.           equivalent to initializing the pointers to nil.            }
  971.  
  972.  
  973. CONST
  974.  
  975.   MaxScrs         = 20;                {Maximum No. of screens to store.    }
  976.   DoubleMaxScrs   = 40;                {Change if MaxScrs is changed.       }
  977.   ScrNumber       : integer = 0;       {Must be retained on re-entry.       }
  978.  
  979.   HeapScrPStandIn : array [1..DoubleMaxScrs] of integer =
  980.  
  981.         (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  982.          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
  983.  
  984. VAR
  985.  
  986.   HeapScrP : array [1..MaxScrs] of ScrPtrType absolute HeapScrPStandIn;
  987.   HoldP    : ScrPtrType;               {Temporary, so can be local.         }
  988.  
  989. Begin
  990.  
  991.   TPushPopScr := false;                {Default return.                     }
  992.  
  993.   if ONorOFF = ON then
  994.     begin
  995.       if (not HeapOK (ScrMemSize)) or (ScrNumber >= MaxScrs) then
  996.         Exit;
  997.       ScrNumber := succ (ScrNumber);
  998.       New (HeapScrP [ScrNumber]);      {Allocate a new heap space.          }
  999.         HoldP := DisplayP;
  1000.  
  1001.          {/The following Move procedure is faster than the more obvious:
  1002.                         HeapScrP [ScrNumber]^ := HoldP^.                   /}
  1003.  
  1004.       Move (HoldP^, HeapScrP [ScrNumber]^, ScrMemSize);
  1005.       TPushPopScr := (HeapOK (ScrMemSize) and (ScrNumber < MaxScrs));
  1006.     end; {if ONorOFF = ON}
  1007.  
  1008.   if ONorOFF = OFF then
  1009.     begin
  1010.       if ScrNumber = 0 then
  1011.         Exit;                                            {Heap is empty.    }
  1012.       HoldP := DisplayP;
  1013.       Move (HeapScrP [ScrNumber]^, HoldP^, ScrMemSize);
  1014.       Dispose (HeapScrP [ScrNumber]);                    {Reclaim space.    }
  1015.       ScrNumber := pred (ScrNumber);
  1016.       TPushPopScr := (ScrNumber > 0);
  1017.     end; {if ONorOFF = OFF}
  1018.  
  1019. End; {TPushPopScr (ONorOFF : Boolean) : Boolean}
  1020.  
  1021.  
  1022.                    (** MISCELLANEOUS SCREEN ROUTINES **)
  1023.  
  1024. PROCEDURE Cursor (ONorOFF : Boolean);
  1025.  
  1026.          {Turns screen cursor ON or OFF.                                    }
  1027.  
  1028.          {/For a description of the commands to control the cursor size
  1029.           see Norton (p. 174).                                        /}
  1030.  
  1031.                             (***** CAUTION *****)
  1032.  
  1033.          {** Be sure that the cursor is restored before an exit
  1034.               from the program as otherwise the user will have
  1035.                           to reboot to restore it. **}
  1036.  
  1037. VAR
  1038.  
  1039.   Reg        : Registers;
  1040.   CursorSize : integer;
  1041.  
  1042. Begin
  1043.  
  1044.   if ONorOFF = OFF
  1045.     then
  1046.       CursorSize := $2000              {This blanks the cursor on either    }
  1047.     else                               {monitor.                            }
  1048.       if DisplayMode = 7
  1049.         then
  1050.           CursorSize := $B0C           {Monochrome monitor.                 }
  1051.         else
  1052.           CursorSize := $607;          {Color/Graphics monitor.             }
  1053.  
  1054.   with Reg do
  1055.     begin
  1056.       AH := $1;
  1057.       CX := CursorSize;
  1058.     end;
  1059.  
  1060.   intr ($10, Reg);
  1061.  
  1062. End; {Cursor (ONorOFF : Boolean)}
  1063.  
  1064.  
  1065. PROCEDURE DisplayVideoModes;
  1066.  
  1067.          {This displays the 128 possible non-blinking video modes
  1068.           available on the IBM PC and compatables.                }
  1069.  
  1070.          {/Note the use of the mod and div operators for formatting
  1071.          the display.                                            /}
  1072.  
  1073. VAR
  1074.  
  1075.   X, Y, J   : byte;
  1076.   ch        : Char;
  1077.  
  1078. Begin
  1079.  
  1080.   LowVideo; ClrScr;
  1081.   GoToXY (31, 2); NormVideo;
  1082.   write (' VIDEO ATTRIBUTES ');
  1083.   for J := 0 to 127 do
  1084.     begin
  1085.       TextColor (J and $F);
  1086.       TextBackground (J shr 4);
  1087.       X := 2 + 5 * (J mod 16);
  1088.       Y := 4 + 2 * (J div 16);
  1089.       GoToXY (X, Y); write (J:3, ' ');
  1090.     end;
  1091.     Ch := ReadKey;
  1092.  
  1093.  
  1094. End; {DisplayVideoModes}
  1095.  
  1096.  
  1097. PROCEDURE Frame (TX, TY, BX, BY : integer; Form : byte);
  1098.  
  1099.           {The input parameters are the coordinates for the top-left
  1100.           and bottom-right corners of the frame and are with reference
  1101.           to the inside corners. (This is for convenience when using
  1102.           the TURBO Window procedure.)  If TY = BY then a single hori-
  1103.           zontal line is drawn and if TX = BX a single vertical line.
  1104.           Form = 1 uses the -- graph symbol and Form = 2 the === symbol.
  1105.           The video modes on entry determine the attributes of the
  1106.           boarder.                                                    }
  1107.  
  1108. VAR
  1109.  
  1110.   J     : integer;
  1111.   Line  : string[80];
  1112.   LineB : string[80];
  1113.   K     : byte;
  1114.   HDash, VDash, TLC, TRC, BLC, BRC, TT, BT : char;
  1115.   CHold : Boolean;
  1116.  
  1117. Begin
  1118.  
  1119. {  CHold  := CBreak;                    {Save $C compiler option.            }
  1120. {  CBreak := false;                     {For faster display.                 }
  1121.  
  1122.  
  1123.   if Form = 1
  1124.     then
  1125.       begin
  1126.         HDash := #196; VDash := #179;
  1127.         TLC := #218; TRC := #191; BLC := #192; BRC := #217;
  1128.         TT  := #194; BT  := #193;
  1129.       end;
  1130.  
  1131.   if Form = 2
  1132.     then
  1133.       begin
  1134.         HDash := #205; VDash := #186;
  1135.         TLC := #201; TRC := #187; BLC := #200; BRC := #188;
  1136.         TT  := #203; BT  := #202;
  1137.       end;
  1138.  
  1139.   if TX = BX
  1140.     then                               {Only single vertical line required. }
  1141.       begin
  1142.         GoToXY (TX, TY);
  1143.         write (TT);
  1144.         for J := succ (TY) to pred (BY) do
  1145.           begin
  1146.             GoToXY (TX, J);
  1147.             write (VDash);
  1148.           end;
  1149.         GoToXY (TX, BY);
  1150.         write (BT);
  1151. {        CBreak := CHold;               {Restore $C compiler option.         }
  1152.         Exit;
  1153.       end;
  1154.  
  1155.   if (TY <> BY) then                   {Frame required.                     }
  1156.     begin
  1157.       TX := TX - 1; TY := TY - 1;      {Enlarge frame by one space for      }
  1158.       BX := BX + 1; BY := BY + 1;      {compatability with TURBO Window.    }
  1159.     end;
  1160.  
  1161.   K := BX - TX - 1;
  1162.   FillChar (Line[1], K, HDash);        {Fill line with = graph symbol.      }
  1163.   Line[0] := chr (K);                  {Set length of line.                 }
  1164.  
  1165.   if TY = BY
  1166.     then                               {Only single horizontal line required}
  1167.       begin
  1168.         GoToXY (TX, TY);
  1169.         write (HDash + Line + Hdash);
  1170. {        CBreak := CHold;               {Restore $C compiler option.         }
  1171.         Exit;
  1172.       end;
  1173.  
  1174.   GoToXY (TX, TY);                      {Top of frame.                       }
  1175.   write (TLC + Line + TRC);
  1176.   GoToXY (TX, BY);                      {Bottom of frame.                    }
  1177.   write (BLC + Line + BRC);
  1178.   for J := succ (TY) to pred (BY) do    {Sides of frame.                     }
  1179.     begin
  1180.       GoToXY (TX, J);
  1181.       write (VDash);
  1182.       GoToXY (BX, J);
  1183.       write (VDash);
  1184.     end;
  1185.  
  1186. {  CBreak := CHold;                      {Restore $C compiler option.         }
  1187.  
  1188. End; {Frame (TX, TY, BX, BY : integer; Form : byte)}
  1189.  
  1190.  
  1191. PROCEDURE WindowDemo;
  1192.  
  1193.           {The technique for creating windows or pull-down menus is a
  1194.           very simple one. The current screen is first saved to another
  1195.           part of memory and the screen is then overwritten with the
  1196.           window. This can be repeated so as to overlay one window on
  1197.           top of another. The only limitation is the amount of memory
  1198.           available.                                                  }
  1199.  
  1200.           {/For simplicity in this demonstration the whole screen is
  1201.           saved. Memory could be conserved by only saving that part of
  1202.           the screen which will be overlayed by the window. However,
  1203.           this complicates the coding and the assignment of heap space./}
  1204.  
  1205. VAR
  1206.  
  1207.   ch        : char;
  1208.   WNumber   : integer;
  1209.   CBHold    : Boolean;
  1210.   ONreturn  : Boolean;
  1211.   OFFreturn : Boolean;
  1212.   TX, TY, BX, BY, Form : byte;
  1213.  
  1214. Procedure GetFrameCoordinates;
  1215.  
  1216.          {This generates specifications for a random window satisfying
  1217.          certain limitations on size and placement.                  }
  1218.  
  1219. begin
  1220.  
  1221.   TX := 2 + Random (50);
  1222.   BX := TX + 10 + Random (68 - TX);
  1223.   TY := 2 + Random (17);
  1224.   BY := TY + 5 + Random (19 - TY);
  1225.   Form := 1 + Random (2);
  1226.  
  1227. end; {GetFrameCoordinates}
  1228.  
  1229. Begin
  1230.  
  1231. {  CBHold := CBreak;                    {Save $C compiler option.            }
  1232. {  CBreak := false;                     {For faster display.                 }
  1233.   LVid;
  1234.   ClrScr;
  1235.   Randomize;                           {Initialize random number generator. }
  1236.   NVid;
  1237.   Frame (10, 8, 70, 18, 1);
  1238.   Window (10, 8, 70, 18);
  1239.   ClrScr;
  1240.   write (' Window #1');
  1241.   UVid;
  1242.   GoToXY (16, 2);
  1243.   Write ('NUtility WINDOW DEMONSTRATION');
  1244.   NVid;
  1245.   GoToXY (6, 4);
  1246.   Write ('Enter text and press ''+'' to store or ''-'' to recover');
  1247.   GoToXY (6, 6);
  1248.   writeln ('                 previous window.');
  1249.   writeln;
  1250.   ch := ' ';
  1251.   repeat
  1252.     write (ch);                        {A CR will not work - but not worth  }
  1253.     ch := ReadKey;                    {taking care of.                     }
  1254.   until ch = '+';
  1255.   WNumber := 1;
  1256.   repeat
  1257.     if ch = '+'
  1258.       then
  1259.         begin
  1260.           ONreturn := LPushPopScr (ON);
  1261.           if ONreturn then             {Space still available.              }
  1262.             WNumber  := Succ (WNumber);
  1263.           Window (1, 1, 80, 25);
  1264.           GetFrameCoordinates;
  1265.           Frame (TX, TY, BX, BY, Form);
  1266.           Window (TX, TY, BX, BY);
  1267.           TextColor (1 + Random (15));
  1268.           TextBackground (Random (8));
  1269.           ClrScr;
  1270.           writeln (' Window #', WNumber);
  1271.           if not ONreturn then         {Space not available.                }
  1272.             begin
  1273.               if not HeapOK (ScrMemSize)
  1274.                 then
  1275.                   writeln ('Heap full.')
  1276.                 else
  1277.                   writeln ('Reached limit');
  1278.               writeln ('Cannot save');
  1279.               writeln ('this window.');
  1280.             end;
  1281.           Cursor (ON);
  1282.           ch := ' ';
  1283.           repeat
  1284.             write (ch);
  1285.             ch := ReadKey;
  1286.           until ch in ['+', '-'];
  1287.         end
  1288.       else                             {ONorOFF = OFF.                      }
  1289.         begin
  1290.           Cursor (OFF);
  1291.           OFFreturn := LPushPopScr (OFF);
  1292.           WNumber := Pred (WNumber);
  1293.           repeat
  1294.             ch := ReadKey;
  1295.           until ch in ['+', '-'];
  1296.         end;
  1297.   until WNumber = 0;
  1298.   Window (1, 1, 80, 25);
  1299. {  CBreak := CBHold;                    {Restore $C option.                  }
  1300.   Cursor (ON);
  1301.  
  1302. End; {WindowDemo}
  1303.  
  1304. BEGIN (* Of Initialization of Unit Video *)
  1305.  
  1306.   WindowDemo;
  1307.   PageDemonstration;
  1308.   DisplayVideoModes;
  1309.  
  1310. END.
  1311.