[comp.sys.atari.st] OSS Personal Pascal article #2

DAVIDLI@simvax.BITNET (12/24/86)

Date:     Tue, 23 Dec 86 20:16 CST
From:     <DAVIDLI@SIMVAX.BITNET> (System Manager)
Subject:  OSS Personal Pascal article #2
To:       info-atari16@score.stanford.edu
X-Original-To:  info-atari16@score.stanford.edu, DAVIDLI

Here is the second of two articles I have written about using OSS Personal
Pascal.   Pass it along to your user groups, or whatever.  The next article will
probably get written the first two weeks of January.




                                  Now What?
                OSS Personal Pascal and the beginner - part 2
                            written by David Meile

     [Copyright 1986 David Meile, all rights reserved.  Permission is
     given to Atari user groups to reprint this article, as long as
     this statement is included.  OSS Personal Pascal is a product of
     Optimized Systems Software, Inc.  I am not related to the company,
     I simply bought their compiler.]

                         Windows, Graphics and Pascal

        One of the many nice things about OSS Personal Pascal is that it
     provides the Pascal programmer with simple access to many of the GEM
     operating system features.  Unfortunately, while access is simple,
     it is not always EASY.  GEM requires a lot of prompting before it will
     produce something like a Window.  But, once you get the basic idea
     of how to access GEM from Pascal, it DOES become easier as you write
     more programs.

        Last time, I said I was going to draw some lines.  Actually, I am
     going to do a little more than that ... I am going to introduce the
     basics of creating a Window to draw ON.  The program that follows is
     called GRAPHICS.PAS.

                                 The program

     PROGRAM graphics;

     { Clear the screen and draw something }

       CONST
         {$I gemconst.pas }

       TYPE
         {$I gemtype.pas }

       VAR
         x,y,w,h,
         my_window   : integer;
         my_event    : integer;
         wind_type   : integer;
         title       : Window_Title;

         { The following must be defined for completeness, but are not used }

         message     : Message_Buffer;
         key         : integer;
         bcnt,bstate : integer;
         mx,my       : integer;
         kbd_state   : integer;

       {$I gemsubs.pas }

     PROCEDURE Do_Graph;

     BEGIN
       wind_type := G_Name;
       title := ' Drawing a box ';
       my_window := New_Window( wind_type, title, 0, 0, 0, 0 );
       Hide_Mouse;
       Open_Window( my_window, 0, 0, 0, 0 );
       Set_Window( my_window );
       Work_Rect( my_window, x, y, w, h );
       Set_Clip( x, y, w, h );
       Paint_Color( White );
       Paint_Rect( 0, 0, w, h );
       Line_Color( Black );
       Text_Color( Black );
       Draw_String( 10,10, 'You can draw text on screen too!');
       Line( 15,15, 15,100 );
       Line_To( 100,100 );
       Line_To( 100,15 );
       Line_To( 15,15 );
       Line_To( 100,100 );
       Line( 15,100, 100,15 );
       Draw_String( 15,110, 'Click the left button to exit...');
       my_event := Get_Event( E_Button, 1, 1, 1, 0,
                              False, 0, 0, 0, 0,
                              False, 0, 0, 0, 0,
                              message, key, bcnt, bstate, mx, my, kbd_state );
       Close_Window( my_window );
       Show_Mouse;
       Delete_Window( my_window );
     END;

     BEGIN
       IF Init_Gem >= 0 THEN
         BEGIN
           Do_Graph;
           Exit_Gem;
         END;
     END.

                         And now ... the explanation

        Quite a lot of things are going on in that little program, right?
     I am sure that you recognize most of the lines at the very end.  There
     is the 'Init_Gem' function, asking GEM if it can create a new workspace.
     There is the 'Exit_Gem' procedure, telling GEM that the program is
     finished with the workspace.  And there is that funny line that simply
     says 'Do_Graph'.

                        But first, a brief digression

        Pascal works best in small chunks of code, called PROCEDURES and
     FUNCTIONS.  As you may remember, a function is a subprogram that, when
     called, returns a single value.  The function 'Init_Gem' returns an
     integer value greater or equal to zero when GEM can run the program.

        A PROCEDURE is a subprogram that, when called, performs some sort
     of action.  It may (or may not) return one or more values.  Procedures
     can require PARAMETERS, which are variables with some sort of value.
     'Draw_String' is a procedure in the program above.  It's parameters
     are two integer values and a string.  (Remember, strings are enclosed
     by single quota marks - 'This is a string').  'Exit_Gem' is a procedure
     with no parameters.

        One of the advantages of Pascal over BASIC is that procedures and
     functions can have variables that do not affect the program outside
     of that particular subprogram.  A short example:

     Program example1;

     VAR
       a, b : integer;

       Procedure dark( x : integer; y : integer );

       VAR
         a, b : integer;

       BEGIN {Procedure dark}
         a := x;
         b := a * y;
         Writeln( 'a = ', a , ' b = ', b );
       END; {dark}

     BEGIN {Main program example1}
         a := 10;
         b := 5;
         dark( a, b );
         Writeln( 'a = ', a, ' b = ', b );
     END. {example 1}

        What do you think this short program will do?  Print out values
     of 'a' and 'b' of course.  First, assign 10 to a and 5 to b.  Then
     call the procedure dark.  The two parameters are a and b ... they are
     called x and y WITHIN procedure dark.  Assign x to a (so a = 10) and
     assign a * y to b (10 * 5 = 50, so b = 50).  The Writeln will print
     out the following:

        a = 10 b = 50

        Now, exit dark and go back to the main program.  Here we print out
     the values of a and b.  But the values of a and b have not changed
     in the main program, they only changed within procedure dark.  So,
     a is STILL 10 and b is STILL 5 in the main program.  The Writeln will
     print out the following:

        a = 10 b = 5

        If you have followed this, congratulations!  You now know about LOCAL
     (within a procedure) variables.

                        Now, back to the REAL program

        OK.  You can see that 'Do_Graph' is a procedure.  There are no
     parameters, so any variables included in Do_Graph must be GLOBAL ..
     that is, the variables listed at the top of the program apply to the
     procedure as well.  A GLOBAL variable can be changed by ANY subprogram,
     and should not be used much within your own programs.   'Proper' Pascal
     programs almost always use LOCAL variables within procedures, passing
     parameters back to the main program when necessary.

        With that in mind, let's look at what's going on in the procedure
     called Do_Graph.  The first thing you can see are several assignments.
     'wind_type' is an integer variable used in the function New_Window.
     It can be made up of several parts, and describes the parts of the
     GEM Window we want to be there.  These include the scroll bars, the
     "close window" box, and the window name.  G_Name is an bit value
     for the window name.  G_Close is another bit value for the close
     window box.  When combined they produce an integer value that we can
     assign to wind_type.  To include both, you would type:

        wind_type := G_Name | G_Close;

     Additional parts of a GEM window can be included:  G_Full, G_Move,
     G_Info, etc.

        Since I'm going to have a window name, I may as well tell GEM what
     it is, right?  That is the variable 'title'.  You'll notice that the
     variable is declared 'title : Window_Title'.  Window_Title is a special
     TYPE included in the GEMTYPE.PAS file.  If you're interested, you can
     look at GEMTYPE.PAS to see how "special" types are declared.

        Having my two variables assigned, I make a call to the function
     called 'New_Window'.  You can tell it is a function because it's on the
     right side of an assignment statement.  The variable 'my_window' is
     an integer, so the function New_Window returns an integer value.  The
     function New_Window asks GEM to set up space for a window, and lets
     Pascal know how to access it by number.  It also tells GEM what the
     title for the window should be.

        I don't want the mouse arrow to clutter up my nice graphics screen,
     so I tell GEM to hide it with the procedure 'Hide_Mouse'.  You MUST
     include a 'Show_Mouse' somewhere in your program for every Hide_Mouse
     you execute.  If you don't, when your program is finished, GEM will
     still be hiding the mouse when you get back to the Desktop ... not
     a pretty 'sight'!

        OK.  The mouse is in hiding, and I can safely OPEN a new window,
     using the procedure 'Open_Window' (of course).  I tell GEM to open
     the window it has reserved and to give me the maximum amount of space
     to work with (the 0,0,0,0 tells GEM to make the window as BIG as possible,
     which generally means full-screen.  Don't exceed the values that you used
     in the New_Window function call...).  To make SURE that the window I
     just opened is the one I will be using, I use the procedure 'Set_Window'.
     If you have several windows open at once, you use Set_Window to tell
     GEM which one you're going to be working with now.  Notice that the
     variable 'my_window' lets GEM know WHICH window I'm talking about.

        The window is open and set.  I want to find out how much space I
     have to work with, so I use the procedure 'Work_Rect'.  The variables
     x,y,w, and h are given values by the procedure Work_Rect, and I can
     use them in my program to determine several things.  The x and y tell
     me where the 'origin' of the window is, and w and h tell me how wide
     and high the window is.  This is useful information -- it means I don't
     have to write separate procedures for each resolution mode (LOW, MEDIUM
     and HIGH resolution).  Instead, I can restrict myself to the boundaries
     given by these values.

        I can do better than that.  I can tell GEM to ignore things I draw
     that are outside the window boundaries by making a call to 'Set_Clip'.
     This procedure will take the values I got from Work_Rect and tell GEM
     not to do any actual drawing outside of those boundaries.  Now, regardless
     of how big or small my window is, I can draw on it without fear of
     crashing the system by going outside of the window boundaries.

        I have my window on screen, but it's got the background color of
     the GEM Desktop.  I much prefer to draw on a white background, so I
     set the 'Paint_Color' to white and then tell GEM to paint the entire
     window that color using 'Paint_Rect'.  Notice that the width and height
     are passed to Paint_Rect.  I have set the origin at 0,0, which is the
     upper left corner of the window (or it's supposed to be ... try it
     using x,y instead of 0,0 and note the results).

        OK, I have a white background, so I'm going to draw things with
     black lines.  I set 'Line_Color' and 'Text_Color' to black.  Then I
     call procedure 'Draw_String'.  The two parameters are x and y pixel
     values, x going horizontally and y going vertically.  10,10 is 10 pixels
     to the right and 10 pixels down.

        Next, I draw some lines.  Ideally, I would be drawing a square,
     with corners at 15,15 and 100,100.  However, it may not look like it
     on your screen.  The reason for this is that there are MORE x pixels
     than there are y pixels on your screen.  In LOW resolution there are
     320 x 200 pixels.  In HIGH resolution there are 640 x 400 pixels.
     In MEDIUM resolution there are 640 x 200 pixels.  The first number
     is the x pixel value, the second number is the y pixel value.

        For now, we'll not worry about it. BUT, if you're going to be doing
     lots of graphics, you'll want to set up some sort of "fudge-factor" to
     make things look right.  One possible solution is to multiply the x
     value by some constant based on the resolution mode you are in.  For
     example, in MEDIUM resolution, multiplying x by 3.2 would compensate.
     (3.2 x 200 = 640.)

        There are two procedures for drawing lines: 'Line' and 'Line_To'.
     The first requires two endpoints, the beginning of the line and the
     end of the line.  The second requires one endpoint, using the end of
     the last line as the beginning of the new line.

        My lines are drawn, and all of the text is also drawn.  The second
     Draw_String is an absolute MUST.  The reason?  You should ALWAYS let
     the person using your program know what to do next!  If the second
     line were omitted, I would have no idea that I was supposed to press
     the left mouse button to exit the program.  It is good programming
     practice to include such 'hints' where they are needed.

        How do I tell when the left button is pressed?  I use the Pascal
     function 'Get_Event'.  There are a LOT of parameters for this function.
     Some are not needed just to test for a simple click on the left mouse
     button.  These are duly noted in the VAR declaration part of the program.
     'my_event' is an integer value returned by Get_Event.  In many cases,
     your program would take this value and continue to do something else.
     Here, it simply sets up the program to await a click of the 'ol mouse
     button.

        Get_Event parameters needed to test the mouse button include
     E_Button ("I'm waiting for a mouse button event..."), and several values
     that follow.  The first '1' means "left mouse button", the second '1'
     means "waiting for user to click button", the third '1' means "wait for
     one click" and the '0' means "wait this long for the button click".  In
     this case, that '0' is meaningless (as are the 'False' and other '0's in
     the parameter list) since we are not using the E_Timer event flag.
     Everything else is simply there for completeness.

        Once I click the mouse, I close the window I created with
     'Close_Window'.  Then I 'Show_Mouse', because I hid it earlier and
     I want to SEE the mouse arrow once my program is finished!  Then I
     'Delete_Window', which releases the space GEM has allocated for my
     window since I no longer need it.

        Done at last.  Look at all of that work, simply to draw a couple of
     lines on the screen!  However, the next time I want to create a window
     and draw some lines, it'll be that much easier...

                     Of RAM disks and Pascal programming

        If you have a 1040 ST, or a 520 ST with a 1 megabyte memory upgrade,
     you can move the OSS Personal Pascal files from disk to a RAM disk.
     This makes things like calling the compiler and linker MUCH faster.
     You need to make sure that the RAM disk you are using can survive a
     computer RESET.  Talk to your local Atari ST user group to see if they
     have a copy of one of the many RAM disks available.  One such is called
     ETERNAL.PRG,  another is available in an archive file called YARD.ARC.

        To automate moving your Pascal program files to the RAM disk, you
     need to look for a program such as FLDR2DSK.PRG, also available from many
     bulletin boards and user groups.  I use this program, but do not put it
     into the AUTO folder as the instructions mention.  The reason is that
     my RAM disk is created on a number of disks, and there is no real reason
     to reboot my system simply to load OSS Personal Pascal.

        If you own an Atari 520 ST without the memory upgrade, you can speed
     things up somewhat by using a RAM disk and putting your programs on the
     RAM disk instead of Pascal.  There are some instructions available to do
     this, which should be available from your local user group.  If you can't
     find them, I will be happy to copy them for the cost of a STAMPED self-
     addressed envelope.  About the best you can hope for is a 200k RAM disk.


                             Wrapping things up

        I'd like to thank everybody who sent me letters and E-Mail regarding
     the first column.  In future months, you can expect to see some more
     articles on working with OSS Personal Pascal.

        In next month's article, I will discuss reading from and saving to
     files.  I'll also talk about using the pre-defined DIALOG boxes that
     are accessible from OSS Personal Pascal.

        I'm open to answering some SIMPLE questions regarding
     Personal Pascal. You can reach me via GEnie as D.MEILE, or
     write (include a stamp, please) to:

               David Meile
               Box 13038 - Dinkytown Station
               Minneapolis, MN  55414

                           Additional reading

        If you are interested in a college text for Pascal, you might find
     that AN INTRODUCTION TO PROGRAMMING AND PROBLEM SOLVING WITH PASCAL by
     G. Michael Schneider, Steven W. Weingart and David M. Perlman is useful.
        It is published by John Wiley and Sons, and the latest version was
     published in (I believe) 1984.  It was the text used for beginning
     programming classes at the University of Minnesota in Minneapolis when
     I learned to program in Pascal.

-------------------------------------

BITNET address:         davidli@simvax.BITNET
USENET address:         ihnp4!mmm!umn-cs!simvax!davidli