Classic Computer Magazine Archive START VOL. 4 NO. 3 / OCTOBER 1989

In C

Accessorize Your Programs


Write dual-purpose C programs with CINIT.ARC on your START disk.

Have you ever written a useful utility and wished you could access it as a desk accessory? Or the other way around? Perhaps you converted that accessory into an executable program but don't like maintaining two source code files that do the same thing.

The truth is that there's very little difference between desk accessories and programs if they follow some simple guidelines. On this issue's START disk you will find MMINIT.C, LASINIT.C and MWINIT.S, which replace the standard initialization routines for Megamax C, Laser C and Mark Williams C, respectively. They're in the archive file CINIT.ARC. Copy CINIT.ARC and ARCX.TTP onto a blank, formatted disk and unARC CINIT.ARC following the Disk Instructions elsewhere in this issue.

There's little
difference between
desk accessories and
programs if they
follow some simple

The compiler-specific instructions for linking these initialization routines with your program are at the end of this article. These startup routines are not always recommended as a replacement for the standard routines; they are intended for programs that you want to be as small as possible. Compiling a very small program with these startup routines will reduce the executable file size from about 4,000 bytes to less than 400 bytes. However, the startup code will not parse the command line into the argv array and dirty, unclosed file streams will not be flushed to disk when the program terminates. I'll assume that since you want your program to run as an accessory (which accepts no command line arguments and never terminates), these issues don't bother you. These routines also use the ST-specific GEMDOS file routines which are smaller and faster than the corresponding Unix-compatible routines. In addition to taking care of startup housekeeping, the startup code sets a variable (_app) which is 0xff if the application is running as a program, 0 if it's a desk accessory.

The Main Event
Let's assume you have source code to an event-driven GEM program and you'd like to turn it into a dual-purpose desk accessory/program. From the evnt_multi function call, the application receives keypresses, button clicks and window redraw messages. Also, programs may receive menu bar events, and desk accessories may receive accessory open and close events denoting that they've been activated or deactivated. (One requirement for a dual-purpose desk accessory/program: the program must not be heavily dependent upon the menu bar because when it runs as a desk accessory it won't have one.)

To convert the program to act as a desk accessory, you'll need to add the code necessary to handle desk accessory open and close messages. There's no harm in having your program check to see if it receives accessory open events; it never will. The desk accessory must stay in the evnt_multi loop forever because if it terminates, the computer will reset. See the source to PRGACC.C to find out how to handle a dual-purpose evnt_multi loop.

If your application is being run as an accessory it should use the menu_ register() call to put its name in the desk accessory menu bar so the user can activate it. Make this call right after appl_init() because if you make too many AES calls before calling menu_ register(), the name won't be registered until the second time the Desktop is drawn.

Memory Handling
At load time, a desk accessory is given as much memory space as its code and data require; it should not allocate more memory. On the other hand, when a program is loaded, it is given the largest chunk of free memory in the machine. It can keep as much of this memory as it needs, but should give some of it back to the system. Assembly language programmers are responsible for taking care of this initialization themselves. For programmers using C or other high level languages, the compiler will take care of linking initialization code that does this automatically.

Ever write a useful
program that you'd
like to use as a desk

A desk accessory should not use malloc() to allocate memory, primarily because this memory may be lost when the user changes resolution. Also, old versions of GEMDOS allow only a small number of malloc() calls (between 20 and 40), and the program the desk accessory is called from may make some malloc() calls as well. An alternative to malloc() is to reserve space by creating a pointer to a buffer.
The Resource Dumper
I wrote the utility RSCDUMP.TTP to let you merge a resource file with a C program listing. This program (and the source code) is on your START disk in CINIT.ARC. Copy CINIT.ARC and ARCX.TTP onto a blank, formatted disk and un-ARC CINIT.ARC following the Disk Instructions elsewhere in this issue. The following example program has a resource file with one object in it, a simple dialog box with one button. The following code loads the resource from disk and puts up the dialog box:
#include "resource h"      /* include object offsets */
#define RECT x,y,w,h
#define ARECT &x, &y,&w, &h


OBJECT * dialog;
int x,y,w,h, choice;

/* load resource, kill program if not found */
if (Irsrc.load("resource.rsc'')) Pterm( 1);

/* get the address of dialog box */

choice = form_do(dialog,0);
form_dial(FMD_Fl NISH,0,0,0,0,RECT);

 if (choice == BUTTON)   { /* do something */    }

Now let's build that resource file in. Run the program RSCDUMP.TTP and type in the command line:
resource. rsc >resource.c
This will give you the C data structures for the resource file in the file RESOURCE C That file will look like this
OBJECT obj0[] = {
-1,1, 1,G_BOX,0x0,0x10,0x21121L,0,0,22,6,
0,-1,-1,G_BUTTON,0x21,0x0,"A Button",3,2,16,2,
You don't really need to understand this structure, but just by looking at it you can see that it is an array whose first object is a box type, and whose second object is a button type containing the text "A Button" Now look at the RESOURCE.H file created by the resource construction program:
#define DIALOGBX 0
#define BUTTON 1
This tells you that the dialog box is the "zeroth" object array and that the button is the first object in that array. Rewrite the above program fragment to include its resource:
#define BUTTON 1

OBJECT dialog[] = {
0,-1,-1,G_BUTTON,0x21,0x0,"A Button",3,2,16,2,


int x,y,w,h, choice;

/* convert_resource is found in prgacc.c */
/* an oblect tree should be converted only once */

choice = form_do(dialog,0);
form_dial(FMD_FlNISH,0,0,0,0, RECT);

 if (choice == BUTTON)    {    /* do something */

Using an included menu bar is not difficult either.
OBJECT menubar[] = {
/* more C objects. . . */
If the application is running as a program and you wish to display this menu bar, simply call:
convert_resource(menubar);     /* only convert it once! */

Instead of
  char *ptr;
  ptr = Malloc((long)(16 * 1024));

   char buffer[(16 * 1024)];
   char *ptr = buffer;

If you must use malloc(), do so before making any AES calls. If you allocate memory after AES calls, GEMDOS may take the memory away without warning.

A Resource Dumper
Your desk accessory/program should not load a resource file because in doing so, GEM will allocate space for that file. On this issue's START disk is the tool RSCDUMP.TTP, which lets you construct dialog boxes and menu bars using a resource construction program. Then it outputs this file as an array of C structures which you can incorporate into your program easily. It works well for most resources that don't contain bitmapped structures. See the sidebar accompanying this column for a description of how to use this program.

The program PRGACC.C demonstrates a dual-purpose application. You can run PRGACC.PRG from the Desktop or rename the file PRGACC.ACC to use it as a desk accessory. The accompanying resource file PRGACC.RSC is not necessary; it's already incorporated into the program.

Link Notes for Megamax C
The Megamax linker MMUNK expects to find its initialization code in the file INIT.O in the SYSLIB library file. Unfortunately, you can't specify an alternate INIT file, so you'll need to replace this file in your system library. Make a backup copy of the system library before you replace 1NIT.O! There may be times you'll want to use to the standard library.

Compile MMINIT.C, ignoring the compiler warnings. Change the name of the object file from MMINIT.O to INIT.O and optimize it using MMIMP:

mmimp init.o
Now delete INIT.O from the system library and replace it:
mmlib dv syslib init.o
mmlib rv syslib init.o
You may now compile your programs normally. I include my stack in a separate object file that lets me customize the amount of memory taken by the stack. Note that this stack, unlike the standard Megamax C stack, is part of the 32K of data space you can use from Megamax. This is only a limiting factor in very large programs. (If it's a problem, consider the upgrade to Laser C.)

To link your program (and the stack):

mmlink your_prg.o mmstack.o -o your_prg.acc
Link Notes for Laser C
Compile the files LASINIT.C (the initialization code) and LASSTACK.C (the variable size stack). You don't need to have the initialization code in the system library; the linker assumes that the first file it links is the initialization code. If you use the graphic interface to the linker, remove the default file INIT.O and link with LASINIT.O as the first file. You may also have make call the linker with this as the first file:

prgacc.acc: prgacc.o

$(L1NKER) -v lasinit.o lasstack.o
prgacc.o $(CLIB) -o prgacc.acc

The file MKPRGACC in the CINIT.ARC archive file on your START disk is a sample Laser C makefile.

For what it is worth: the default Laser C init actually works fine as an init for programs or accessories. My init will generate smaller code for those who don't need file stream cleanup or command line arguments. Also, the current Laser init allocates the stack from GEMDOS if it initializes an accessory; this memory is lost when you change resolution.

Link Notes for Mark Williams C 2.0
Compile the file MWINITS as follows:

cc -c mwinit.s
Move the resulting file MWINIT.O into your \LIB directory. Now to compile your program/accessory:
cc prgacc.c -Nrmwinit.o -laes -lvdi
The -Nrmwinit.o tells the linker to use the file MWINIT.O as the runtime startup module. The options -laes and -lvdi tell the linker to link your program with the GEM binding libraries.

Assembly Accessories
The file ASMBLY.S is an example of how to write an assembly application which functions both as an accessory and a program. It was assembled with HiSoft Devpac, but shouldn't be too difficult to transfer to another macro assembler. It simply puts up a dialog box when it is run as a program or selected as an accessory. It doesn't do the fancy window or menu handling of the C example.

You now have all the tools to write dual-purpose applications. These techniques will make debugging your desk accessories much easier, because you can run them as programs until they work perfectly.

Samuel Streeper is currently working on an Atari ST MIDI networking system. This is his first article in START
Programming in C. . . on your START Disk
The following files are in the archive file CINIT.ARC on your START disk:

Sample program code of a dual purpose desk accessory/program. Rename PRGACC.PRG to PRGACC.ACC to try it as a desk accessory.


These files were created using the Megamax resource construction program but are not needed to run PRGACC since they have been incorporated in the program using Resource Dump.

The source and executable code to the resource-file-to-C converter.

Megamax C startup code and stack.

Laser C startup code and stack.

Makefile used to make program/ accessory with Laser C.

Mark Williams C startup code.

HiSoft Assembler code for
simple dual purpose program! accessory.