Christmas Graphics
Volume Number: 1
Issue Number: 13
Column Tag: Pascal Procedures
Christmas Graphics
By Alan Wootton, President, Top-Notch Productions, MacTutor
Contributing Editor
Last month I promised to complete my discussion on desk accessories. I still plan
to do that, but this month is Christmas so we will take it easy and do some more
lighthearted things. We will take an overview of the types of programming one might
do on the Macintosh. I have two short programs demonstrating how to write code in
resources, and a short program that draws a Christmas tree.
Resources as code
Unless you are writing code for a dedicated appliance controller, or something
similar, the programs you write will be started by another program, usually the
operating system. Your program, when finished, will return control to that 'other'
program. In the case of a Macintosh Application, the program is started by another
Application (usually the Finder) using the Toolbox procedure Launch (see Segment
Loader chapter of Inside Mac). When the program is finished it returns to the Finder
by calling ExitToShell (or Launch!), or by just reaching the end of the main
procedure, in which case ExitToShell is called for you. If you have been following this
column you will know that there are other kinds of programs besides just Applications.
Desk Accessories are called repeatedly by the toolbox when the resident Application
calls SystemTask. In addition to these two there are many other ways to get your piece
of code executed.
One of the features of native code compilers (for the 68000) is that the code they
produce is position independent. This means that it does not matter what address the
program occupies -- it always runs properly. So, potentially any portion of memory
could be loaded with code and run. In the case of an application, the Segment Loader
handles this chore. In the case of DAs, the Device Manager loads resources of type
DRVR and passes control to the code within. The Window Manager, the Menu Manager,
the Control Manager, and others will load and run pieces of code. There is no reason
why we cannot do it, too.
The compilers normal output (actually produced by the linker) is a single piece
of code that is stored in the resource CODE #1. The structure of this code is illustrated
in figure 1.
Note that the code is produced in the same order as the procedures occur. A jump
is used to transfer control to the main procedure. Further, note that the main
procedure does not begin and end like the others. It does not return control to the
caller at all. Rather, it exits to the finder. The link and unlink are used to reserve
space on the stack for the vars declared for that procedure.
An important thing to notice is the existence of phantom procedures past the end
of the program. These are known as library routines and have the purpose of
providing functions that are needed by the program. String functions and Operating
System traps are examples. You can even make your own library procedures and, by
declaring them, cause the linker to include that code.
There is a problem with the MDS linker. It is not as efficient as it should be.
When you refer to a .rel file in a link directive file (link directive files are produced
automatically by TML Pascal, although you may make your own), the whole .rel file is
linked onto the end of your code, whether it is used or not. If you need more control
you can make your own library files that only have those routines actually needed in
them (requiring much work in assembly language). Another way is the get the
Optimizing Linker and Librarian from Consulair Corp. (Portola Valley, CA).
Consulair normally sells a C compiler but, since that compiler is MDS compatible, you
may use their products in conjunction with the TML Pascal Compiler. TML may be
able to sell you that linker also.
There is another oddity about the main procedure that is not shown in the
diagram. All variables declared in the main procedure are accessed in a special way.
These variables are not created temporarily like in the other procedures. The
variables for the main procedure are created by the segment loader above the
beginning of the stack and the register A5 is reserved for the sole purpose of accessing
those variables. This means that, except for application code, procedures in resources
absolutely must not access any global variables. This was mentioned in regard to DAs
last month and is applicable here, too.
Before we lose our way in all this technical mush, let us remember that our goal
is to use a resource as a procedure. In order to do this we have one final hurdle to
overcome.
The most general method will be to use the beginning of the resource as the
beginnning of the program. A glance at figure 1 shows that this does not work on the
program in its CODE 1 form. Since we will have to use the Rmaker to convert from
type CODE to another type, (here we use PROC) we could use Rmaker to convert from
the program form to the procedure form. Rmaker does not make this an easy task.
There is a Rmaker type that will trim off the first two words ( created by the segment
loader) of the CODE 1 resource (refer to your Rmaker documentation). Unfortunately,
we need to also avoid the jump to the main procedure. If we tell the compiler to put
our procedure into a CODE 2 resource then there is no jump, but Rmaker will not trim
any other than CODE 1. After much thought I came upon what seems to be the best
solution. Use the GNRL type directive to place the word $6008 at the front of the
destination resource and then copy the CODE 1 resource after that. $6008 is the
68000 code to branch over the next 8 bytes. This effectively skips the two segment
loader bytes and the jump. For CODE types other than number 1 there will not be a
jump, so use $6004 to skip only the first two words. See the Rmaker input file below
for an example.
Prompt_For_String
The procedure I present to illustrate the use of procedures in resources is a
compiled version of the Dialog box example presented here in July (Vol.1 No. 8). Its
purpose is to open a dialog box, request a string from the user, and then go away,
returning the string to the calling program. To compile this first compile
Pr_for_Str.pas, link Pr_for_Str.link (this file is created by the compiler), and then
use Rmaker with Pr_for_String.R. The resulting file is Pr_for_Str.PROC which
contains the resources PROC 567, which is our procedure, and also the DLOG and DITL
for the dialog box.
The most interesting part of the example is the third part, the program
Pr_for_Str.Mpas. This is a short (except for the declarations) MacPascal program
that shows how you could use an external and compiled procedure from within
MacPascal.
Inline again
All the action in Pr_for_Str.Mpas (below) takes place in the main procedure.
For readability of those eleven lines I have declared the Toolbox routines as
procedures, rather than just using inlines in the code. This is the simplest use of
inline, once you get the definitions correct it is difficult to call the Toolbox traps
incorrectly. Following the example below it is easy to type in Toolbox routines using
Inside Mac as a guide.
The exceptions are register based traps. For these I use Generic (see Advanced
Mac'ing in MacTutor Vol.1 No. 5). When Inside Mac says (using Hlock as an example):
PROCEDURE Hlock(h: handle);
On entry A0: h ( handle)
On exit D0: result code (integer)
You set regs.a[0] to the handle and call Generic with $A029 (look up the trap
number in a cross reference). The record regs is read by Generic and the trap is
called. Note that later you can check loword(regs.d[0]) for an error.
This is not the end of the the ugliness. In order to call an external procedure it is
necessary to abuse inline. Instead of a trap number we use the word $4E75 which will
transfer execution to the address corresponding to the last argument to inline. The last
argument is @jsr[0] which is the address of an array of 4 words which has been set to
a short routine to shuffle some registers and then call the address that is next to last in
the arguments. In this case it is the address of the resource PROC 567. For more info
on jsr see my column in MacTutor Vol.1 No.9. The printing example uses jsr and the
68000 code is given.
Once you become comfortable with the inline kludges required, you can make
arbitrarily complex MacPascal programs. Simply work on your program until it
becomes too large (or too slow), then compile those procedures that are in a relatively
finished state. When the compiled procs are replaced by the code to call them
externally your program will then be much shorter, and you can add to it until it is
time to repeat the cycle. Eventually, all that is left is a core with the bulk of the
program compiled. At that point you move the last of the program to the compiler and
you have a completed program!
Skip over the Pr_for_Str stuff (3 files) now and we will do something much
more fun.
TML Pascal code
Program Pr_For_Str;{ file Pr_For_Str.pas }
{ by Alan Wootton 10/85 }
{ written for TML Pascal }
{ $I means include these interface files }
(*$I MemTypes.ipas *)
(*$I QuickDraw.ipas *)
(*$I OSIntf.ipas *)
(*$I ToolIntf.ipas *)
{ We will convert this code with Rmaker in
such a way as to cause execution to begin
with the FIRST PROCEDURE, and not in the
main procedure. }
{ This procedure expects the resource
DLOG 12345 to be available. It opens a