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 * *********************************************************************