[comp.sys.mac.programmer] a resource which is a procedure

garry@batcomputer.tn.cornell.edu (Garry Wiegand) (05/12/88)

Was: Goin' Crazy on a Mac, or, How I Love MPW "GlobalData"

I posted the original query, and, no, the code doesn't have any chunky arrays
that can be resourced out, and no, we can't switch to Aztec (this is an *object
library*, so it has to be used with whatever compiler the customers have
all picked. Ie, MPW.)  The "global data" mostly consists of billions
of floating-point constants (generated by the compiler) and small string
constants (from the original source) all scattered throughout our 60,000 
lines of source.

So: I'd like to ask again (perhaps a bit more clearly) a question I asked
before...  at several places Inside-the-Mac mentions "procedure resources". 
For example, on page I-363 it says 

   "The resource type for a menu definition procedure is 'MDEF'. The 
    resource data is simply the compiled or assembled code of the procedure."

It occurs to me that if we could load our complete library into some kind of
independent procedure resource like this, we wouldn't have to worry about the
user's program (that links to our library) making more contributions against
the 32K limit. (We'd still have to get our own stuff below 32 somehow.) Also,
our "library resource" could be loaded into the system file, and the user
executable images could get a lot smaller. Linking would be faster too. 

The Question: Does anyone know the details of how to construct a resource like
'MDEF' from C, and then how to call into it?

(sorry to take up bandwidth again, but procedure resources seem like they
ought to be an interesting idea. Object-oriented, and all that.)

garry wiegand   (garry@oak.cadif.cornell.edu - ARPA)
		(garry@crnlthry - BITNET)

earleh@eleazar.dartmouth.edu (Earle R. Horton) (05/13/88)

In article <4769@batcomputer.tn.cornell.edu> garry@oak.cadif.cornell.edu writes:
>Was: Goin' Crazy on a Mac, or, How I Love MPW "GlobalData"
>
>It occurs to me that if we could load our complete library into some kind of
>independent procedure resource like this, we wouldn't have to worry about the
>user's program (that links to our library) making more contributions against
>the 32K limit. (We'd still have to get our own stuff below 32 somehow.) Also,
>our "library resource" could be loaded into the system file, and the user
>executable images could get a lot smaller. Linking would be faster too. 
>
>The Question: Does anyone know the details of how to construct a resource like
>'MDEF' from C, and then how to call into it?
>
>(sorry to take up bandwidth again, but procedure resources seem like they
>ought to be an interesting idea. Object-oriented, and all that.)
>
>garry wiegand   (garry@oak.cadif.cornell.edu - ARPA)
>		(garry@crnlthry - BITNET)

This is the way the printing code, device drivers, and DAs are put
together, and is probably not as bad to implement as you might think.
You can tell the MPW linker to stuff the code from your object files
into a resource of type other than 'CODE' simply by using the "-rt"
option.  This is how I put the code for my printer driver into
resource 'DRVR', ID #-8192 (no, I don't ever use 'DRVRRuntime.o').

	link XPrint.a.o  XPrint.c.o {clibraries}cinterface.o \
		{libraries}interface.o -rt DRVR=-8192 \
		-ss 100000 -o XPrint -t '????'

     (Read '\' as 'option-d' here.)  XPrint.a contains assembler
instructions to format the header of my driver, and XPrint.c contains
the implementation code of the five driver routines.  This gets me a
file named "XPrint" which contains the 'DRVR' resource, which I then
extract using Rez and paste into my final printer resource file.

As far as what can go in such resources, I am not all that sure.  When
linking such resources, the linker must convert all addresses to
PC-relative addresses.  I don't know whether this means you can have
string constants in the middle of your code, or not, but I certainly
would hope so.  In situations like this, the linker puts the first
procedure which contains executable code at the top of your resource,
so you can have something there which your user routines jump to, or
you can have some sort of table which directs them to the proper
offset for a particular function.  You will probably want to format
the header in assembler, unless you do something like:

library(opcode,parameters)
int opcode;
char *parameters;
{
	switch (opcode){
		case 1:
			routine1(parameters);
			break;
		case 2:
			routine2(parameters);
		...
		case n:
		...
	}
}

If you do it this way, I think you can write the whole thing in C, and
never bother with assembler.  When you link your resource, make sure
the ".o" file containing library() gets linked first, and then you are
sure that when you load and call your resource, the proper routine
gets called.  There are other things to watch out for, of course, such
as locking your library resource when it is in use, and never writing
to any location within this resource, since that is self-modifying
code, and bad.  If your library routines need access to global data
which they can write to, then you will have to pass them a pointer or
handle to suitable space, or provide some other means for them to
access such global data.  For sure, the following code snippet is
going to blow up somewhere if it is in a stand-alone code resource:

double junk;
routine()
{
	junk = 3.12340813094;
	....
}

If you elect to put your library routines in separate resources which
user programs can load at run time, then you should also be able to
get around the 32k limit, merely by having as many code resources as
you need to do what you want.  You may also want to refer to Technical
Note # 135 from Apple, which gives an example of doing much what you
suggest, except the user writes the library routine, and the supplied
application calls it.

Please don't put anything more in the System File, which is already
too durn big.  Put it in some other file, and tell your glue routines
the name of it.

There are many ways to do what you suggest, but I would recommend
having each library resource that you write funnel all calls through a
procedure which uses the opcode method, and pointers to optional
additional parameters.  This way, you get something which is easily
extended, and your main glue routine can do things like check version
numbers and so forth to make sure each call is going to work.  Another
method, used in the printing code and other things produced by Apple,
uses an offset table at the beginning of the resource, and the glue
routine looks in this offset table to see which routine to call.  This
method works, but is harder to extend, and you have to call it, or
provide glue to call it, written in assembler.  You also get a
procedural interface which, sooner or later, you are going to look
back at and say "Who wrote this convolved, ugly mess?"  (Which is what
I am sure they do at Apple when they consider the interface to the
code in the printer resource file.)

(Sorry for the preaching, but the printer code interface really burned
my arse when I set about duplicating it for my printer driver.)

I almost forgot, the example of calling the resource from C.  The
following chunks of code illustrate loading a code resource, locking
it, and then using it as a callable function throughout your program.
In my port of MicroEMACS to the Mac, I ran into the problem that Apple
released a whole new series of keyboards just when I had gotten used
to the keycodes on the old keyboard.  I decided to write the routine
which interpreted keystrokes as a separate code resource, then I could
play with various ways of interpreting keystrokes without relinking
the whole program.  The following shows how I interface to this
routine from C.  The key interpreter is a C function which is linked
by the MPW linker into a 'GetC' resource, number 606, and which is
pasted into the final application using Rez.  This function, not shown
here, takes as a single parameter a pointer to an event record, and
returns an "int" result based on its opinion of said event record.
These two function fragments illustrate a) loading your code resource,
and b) calling it.  Of course, if you want to stuff a whole bunch of
library functions into one resource, then you have to come up with a
more sophisticated calling sequence...

int (*KeyInterpreter)();
mac_ttykopen()
{
	ProcPtr	*GetC_Resource;

/* Load the keyboard Event Record interpreter. */
	GetC_Resource = (ProcPtr *)GetResource('GetC',606);
	LoadResource(GetC_Resource);
/* Don't detach the resource if you plan on swapping it in and out of
 * RAM later. */
	DetachResource(GetC_Resource);
	HNoPurge(GetC_Resource);
	HLock(GetC_Resource);
	KeyInterpreter = *GetC_Resource;

}

int mac_ttygetc()
{
	EventRecord		theEvent;
	unsigned		c;
	int16			modFlags;

...... Get the event.	
	return((*KeyInterpreter)(&theEvent));
		
}

Hope this helps.

Earle
*********************************************************************
*Earle R. Horton, H.B. 8000, Dartmouth College, Hanover, NH 03755   *
*********************************************************************