Classic Computer Magazine Archive ANTIC VOL. 6, NO. 2 / JUNE 1987


Get started in GEM programming


Programming the ST series computer by using calls to the GEM interface is a lot like learning to fly a jet plane. It can be done easily if you're familiar with all the switches and buttons to press, but if you're not, you wont be able to get very far off the ground.

Before jet pilots can start flying, they've got to perform some standard duties first: check out the aircraft, climb in, close the canopy, start 'er up and go. Before we can start to program the ST with GEM we have some duties to perform also.

No, we don't have to close the canopy, but GEM has lots of switches and conditions which must be flipped and met before we can get any work out of it. GEM is very smart about some aspects of its work, but when starting out, there are a few things we must specifically tell it to do.

What does GEM want to know? Basically, there are 22 things we need to tell it, ranging from where GEM's "Blackboard" is to which linetype, text style and color to use. We'll cover each of these in detail below.

This month, we present a program "shell," which you can use as a base to build any other program out of GEM. It contains only the essential features GEM needs for power-up. Using this file will allow you to concentrate on the logic of your program, and not worry about how to kick-start GEM.

Carefully type in Listing 1., GEMSHELL.C, with your favorite word processor and save a copy to your disk. There are no special instructions or tricky lines. 1ST Word, MicroEMACS, or Word Writer are perfect for this job. If you insist on using ST Writer, you'll have to PRINT the file to disk, using a left margin of zero, a right margin of 79, set the top and bottom margins at zero, and edit out the page breaks later.

After you've saved your file to disk, construct the proper batch file for your flavor of C and compile your program shell down to a program, so we can see how it works.

With the Developers' C, we'll need to create our Batch File first. This is a file which holds all of the commands normally needed to complete compilation of a program. For example, using Developers' C, we need to pass our typed-in source code through six primary and two secondary programs before we can call it a program. If we did these steps one at a time, we would have to type in the instructions for each stage of the compilation at each step.

When we create a Batch File, we type in the instructions once, save it, and then direct the Batch Program (BATCH.PRG) to execute the instructions, line-by-line. For places where we need specific filenames or conditions, Batch allows us to use parameters, which act like place-holders in the Batch File, and are replaced by the filename or condition you type when you activate the Batch Program. Let's create the batch file for our shell program. It's quite short, as you can see:

   cp68 %1.c %1.i

c068 %1.i %1.1 %1.2 %1.3 -f

c168 %l.l %l.2 %1.s

rm %l.i

rm %1.l

rm %1.2

as68 -l -u %1.s

rm %1.s

link68 [u,s] %l.68k= apstart,

% 1, vdibind, aesbind, osbind, libf

relmod %1.68K %l.prg

rm %1.o

rm %1.68k


   Again, type it in using your favorite word processor and save a copy to disk. What exactly does each line do? Let's follow it and see. First, however, keep in mind what happens when we double-click on BATCH.PRG to activate it. A small "Open Applications" dialog box pops up, where we are expected to type in the filename sans extender of the Batch File to activate, a space, and the filename sans extender of the file to work on. For our example we'll type in "C GEMSHELL" and tap [RETURN]. The "C" will be the filename of our Batch File (C.BAT), and "GEMSHELL" is the name of the file to work on. When you type in the filename, it will be considered a parameter to be passed to the C.BAT file.

Now we'll follow the Batch File as it processes the compilation of our program. In the example below, the "percent" sign/number is what BATCH.PRG uses as a parameter holder. There may be up to ten parameters passed.

CP68 %1.c %1.i means the preprocessor, CP68.PRG, will accept a file called whatever.c, work on it changing

All C programs
must have a function
called main(). It's the only
way the compiler knows where the
real start of the program is.

the #defines into what they were #defined as, and then will output a file called whatever.i. C068 %1.i %1.1 %1.2 %1.3 -f tells how the first pass of the C compiler, C068.PRG (the parser) will accept the whatever.i file from CP68.PRG, work on it, and create upto three files, whatever.1, whatever.2, and whatever.3. C168 %1.1 %1.2 %1.s describes how the second pass of the C compiler (the code generator) will take the whatever.1 and whatever.2 files as input, finish the compilation, and output a whatever.s file, which is nothing but pure ASCII 68000 assembler source code. Yes, the format is a little funny, because a machine wrote it and a machine is destined to read it, but it's source code nonetheless. It's at this point you will eventually go in and "optimize" the code, because compilers are (taken as a whole) pretty stupid when it comes to writing assembly language source code.

So what the Batch file has done so far is take your typewritten C source code, and convert it into standard 68000 assembly source code. What's next?

Well, right now we have three files on the disk we don't need anymore. The whatever.i, whatever.1, and whatever.2 files. Since we don't need them, we pass their names to a program called RM.PRG, which removes (deletes, erases) the file from the disk. The next line which does something new comes after the three "removefile:" lines. as68 -1 -u %1.s shows how the assembler, AS68.PRG, will take the source code just generated (whatever.s) and assemble it into an object code program. While we show it what file to pick up, we don't need to give it a name for an output file, since it is a convention that assemblers output an .o file, standing for "object" code. The switches -1 and -u tell AS68.PRG to ensure all address constants are coded as longwords and to handle all unresolved labels as being globally defined. When this section is finished, we should find a whatever.o file on the disk.

Before we progress further, we need to study something a little more closely. The languages available on the ST series computer allow you to do something the Atari 8-bits can't. Say, for example, you have written a large program which takes thirty minutes to compile. On the Atari 8-bits, you need to assemble the entire file at the same time. Even if you make just one small change, you still need to perform the entire job over again. With the ST, however, you may break the individual sections of the program apart, assemble each of them separately and then glue them all together right before you need the program. If you make a small change in one section, you need to reassemble only that section, and then glue it back into the original group.

If you wish, you may preassemble different subroutines you know you'll need in every program you write, like printing values to the screen, and then just glue them in when you need them. In practice, gluing this chain of .o files together is described as linking the program. Atari, staying a jump ahead of us, has already preassembled the roughly 300 subroutines/functions GEM needs into three small packages just waiting for you. All you need to do is link the proper ones in when you compile your program. These three files are called AESBIND, VDIBIND, and OSBIND. As their names imply, AESBIND holds the subroutines for interaction with the AES Library inside GEM, VDIBIND will let you interact with the VDI Library, and OSBIND holds the calls to GEMDOS, BIOS, and XBIOS.

Now, to get back to our Batch File, the next line is: link68 [u,s] %1.68k= apstart, %1, vdibind, aesbind, osbind, libf, which breaks down as follows. 1ink68 is the name of the linker program, LINK68.PRG. It gets passed two flags in our example, [u,s], which means "ignore the presence of undefined symbols in the input files, and please generate a symbol table." Next we find the parameter, which for us is the filename typed in, whatever given an extender of .68k, which is the type of file produced by the linker. It will be made up of the list of files behind it all linked together. The first file, APSTART (Application Start) is a short section of code which will determine how large your program is, reserves that much memory, and then releases the rest of memory. This is because when a program starts running under TOS, it "owns" all of memory, and normally we don't need it all.

So the Atari ST will create the file whatever.68k out of the files APSTART.o, whatever.o, VDIBIND.o, AESBIND.o OSBIND.o and LIBF.o. Notice the .o extenders aren't needed in this example, since LINK68 only recognizes an object file for linking purposes. When the linker is finished, the file whatever.68K is technically an absolute file, meant to run at a single particular address in memory. This is a no-no for the ST, so we need to run the .68K file through one more file, called RELMOD.

relmnod %1.68k %1.prg describes how RELMOD.PRG will accept the .68k file the linker produces, resolve all relocation information inside the file, and output a completely relocatable .PRG file. I'm sure you've seen that .PRG extender before. The .PRG file is what winds up on your desktop as the executable program you double-click on.

However, before we leave we have two files to erase. Run the newly-created .o and .68k files into RM.PRG, and they disappear. This leaves nothing to do but run WAIT PRG, which simply holds the screen and stops un-

With the ST,
you can break a program apart,
assemble each section separately,
and then glue them all together
right before you need the program.

til you press any key. This allows you to come back and see if anything went wrong during the compile.

That takes us through the contents and operation of the BATCH file.

As I described earlier, the program this month is really a sub-program we mean for you to build on. This provides a stable "base" for your logic and code. Let's examine the program line by line.

The top seven lines are remarks, like REM in Atari BASIC. Here's where I put the program title and copyright information. The two lines right below them which start with #define will work with the preprocessor CP68.PRG to replace the first string (example: TRUE) with what comes after it on the same line (example: 1). This can be very powerful, but here we use it just to redefine two words.

Next we #include a file called OSBIND.H . The .h extender tells us this is a header file, which typically contains definitions or reusable code for inclusion with your C programs. This file contains all of the definitions for interaction with GEMDOS, BIOS and XBIOS. You may SHOW osbind.h to the TV screen if you desire to read it. When the C compiler gets to the #include line, it will stop and search for the file requested. If found, the file is opened, and information is compiled out of that file until it is finished, at which time the compiler will return to the next line down in our program and continue.

The C language, like most any assembled language, requires us to reserve space for variables before we use them. With the Developers' C, an int declaration will reserve 16 bits (two bytes/one word) of memory for each variable name stated, unless the variable is an array, in which case the subscript number inside the array declaration tells us how many "ints" to reserve.

We run into that below the #include line, where we reserve space for the variables we'll need to use here. We'll examine each one of them. The first five declarations are where GEM keeps its "Blackboard." We need to communicate with GEM somehow, so we've set up a space where we can write numbers to GEM, GEM can find them, work on them, and then write its answers back out to us. This works a lot like a blackboard.

This is set up as a series of five arrays. CONTRL has 12 spaces reserved, and is where we put Control numbers, like commands. INTIN and PTSIN both reserve 256 places, and is where we place numbers and coordinate points for GEM to find them. INTIN stands for Integers-In and PTSIN stands for Points-In. INTOUT and PTSOUT are, cleverly enough, where GEM places its answers for us to find. INTOUT stands for Integers-Out, and PTSOUT stands for Points-Out. Next, WORKIN is an array which is pre-initialized, or rather the array is filled when it is declared. WORKIN holds eleven default values for GEM when it powers up. The default values are:

   0: Device ID number.

1: LineType

2: Polyline Color

3: Marker Type

4: PolyMarker Color

5: Text face

6: Text Color

7: Fill interior style

8: Fill style index

9: Fill color
  10: NDC to RC flag.

   Notice all of the default values are 1, with the exception of the last entry, which tells GEM we want to use Raster Coordinates instead of Normalized Device Coordinates. Why? Well, even though Normalized Device Coordinates would give us resolution up to 32,767 x 32,767, we can't use it because GDOS isn't supplied with the machine. We specify Raster Coordinates so we can use typical ST screen resolutions (320x200, 640x200, 640x400).

Next in line is the array workout, where GEM will return a world of information about what type of terminal GEM thinks it's operating on. Almost anything about the machine can be found here, from whether the terminal supports color, to if you may rotate text on the output device.

The bottom line in the variable definition section holds place for the variables i, finished, and gem_handle. The variable i will be used as a dummy placeholder, finished is used as a flag to show if we are finished (TRUE/FALSE), and gem_handle is used as the place we keep the identification number (for our application) GEM will assign to us when we power up the program.

All C programs must have a function called main(). It is the only way the compiler knows where the real start of the program is. In C, main() may come at the start, middle, or end of the file. It doesn't matter. However, here we put it at the start of the program.

There is also a convention in writing C programs for the Atari ST you should know about. When reading an ST C program, how do you know which functions are supplied by the ST, and which are supplied by the writer of the program? Basically, any function call that begins

Programming the ST with calls
to GEM is like learning to fly
a jet plane-if you're not familiar
with all the switches and buttons,
you won't get off the ground.

with a lowercase letter "v" is a call to the VDI library. Examples include v_ pline() and v_opnvwk(). Function calls to the AES library fall into eleven different groups.

   1. APPL_ Applications Library

2. EVNT_ Events Library

3. MENU_ Menu Library

4. OBJC_ Objects Library

5. FORM_ Forms Library

6. GRAF_ Graphics Library

7. SCRP_ Scrap Library

8. FSEL__ File Selector Library

9. WIND_ Windows Library
  10. RSRC_ Resources Library
  11. SHEL_ Shell Library

   Function calls to GEMDOS, BIOS, or XBIOS will start with a capital letter, for example, Setscreen() and Fread().

So, inside our main() function, the very first function called is initialize(). When the program passes this point, it will make (in essence) a subroutine call to a function called initialize(), perform it,and then return to the next line down in main().

This turns out to be where the gist of the function lies. The line reads as: "Do your work while not finished." The exclamation point means NOT to C. The function your_work() is where you would insert your own section of C code for the program to execute. Eventually; your program code should decide it has finished its job, and control will pass to the next line down, which has the terminate() function call. This call will properly clean up and exit your application, and return you to the GEM desktop.

The function initialize() shows just what needs to be tweaked in GEM to get your application up and running. Let's take it line-by-line.

First, the appl_init() (application initialize) call will inform GEM a new application is starting up. Next, the graf_handle() call will return the number GEM has assigned as the identification number for your application. In our example, we place the returned number into the variable gem_handle.

The most important call here is v_opnvwk(), which stands for "VDI-Open Virtual Workstation" and actually begins the interaction with GEM. From this point on, you need to follow (and adhere to) GEM programming standards. Notice that the call requires three parameters: Our workin array, which we initialized up in the variable definitions section; the address of the variable gem_ handle, so GEM will know where to put the identification number; and the workout array, where GEM will return answers to us.

And right before we leave initialize(), since we're fairly certain we haven't finished with the program yet, we set the value in the variable finished to FALSE

In the function your_work(), we've simply included a sample line which will put an Alert Box onscreen to tell you you've made it that far. In practice, you'll want to put your own C code here. The form_alert() function actually puts the box onscreen. When you click on the [Exit] button inside the Alert Box, our sample program has finished, so we set the variable finished to TRUE.

There are only two function calls to exit a GEM application First v__clsvwk() will close the Virtual Workstation you opened earlier and disconnect you from GEM, and the function appl_exit() performs the action of releasing the memory your application used and returning you to the GEM desktop.

Believe it or not, that's all you need to begin programming your own applications using the GEM interface. Now, for your assignment, I want all of you to write PACMAN in C and have it ready tomorrow. Class dismissed.


Listing 1  GEMSHELL.C Download / View