MDStoMPW
Volume Number: 5
Issue Number: 6
Column Tag: Developer Notes
MDS->MPW
By Dan Weston, Portland, OR
Converting MDS files to MPW
This article describes how to convert MDS assembler source files to the MPW
environment. I don’t go into all the details, but try and give you enough of the major
points so that you can get your old files over to MPW and get more productive. MPW is
a very rich and wonderful environment and I am purposely ignoring many of its subtle
and not-so-subtle features in this article for the sake of clarity (and deadline).
Declare a MAIN Procedure
MPW expects you to declare at least one code module in your source file. There
are several ways to start a code module, but the easiest is to put the MAIN directive on
the same line at the label at the start of your code producing statements. For example,
the following code fragment establishes the label “Start” and the main code module.
Start MAIN ; code statements start here
If you don’t declare a code module, either by using MAIN or PROC or FUNC, MPW
will complain heartily.
Declare Global Data Record
In MDS, it is customary to declare global variables with the DS directive. The
MDS assembler gathers all DS directives together into a common global data block and
tells the linker to locate the data block at -256(a5) when the program is loaded. (This
location is the default and you can change it with Link directives, although most people
don’t bother to change it. Later in this article when we discuss the QuickDraw globals,
you will see that the location of the application globals in MDS is important.) All the
labels associated with the DS statements become negative offsets from A5 to the proper
location in the data area. For example, if you have the following data statements in
your MDS file:
thePoint ds.l 1 ;space for a Point
theRect ds.w 4 ;4 words for a Rect
You can access those variables by using the name of the variable as an offset from
register a5, as in this line of code.
move.l thePoint(a5),-(sp)
In MDS files, DS directives can be placed anywhere in the file, and the assembler
will gather them all together and make a single data block. The MPW assembler also
gathers up DS statements and makes code modules and the MPW linker combines all the
data modules and place them just below the a5 boundary points when the program is
loaded. The MPW linker does not give you an automatic 256 byte cushion below the a5
boundary the way the MDS linker does. Labels for DS statements become negative
offsets from register a5, however, in much the same way as in MDS. You can access
MPW assembler data variables by using the variable name (label) as as offset from
register a5, as shown in the previous example.
A better way to declare global variables in MPW is to gather all your DS
statements together into a RECORD and then use that data template to access all your
globals as fields of the template. For example, to convert the global variables from the
MDS example above, you would place the following data declaration at the beginning of
your file (after the ‘INCLUDES’ and ‘EQU’ statements but before the first code
statements).
MyData RECORD
thePoint ds.l 1
theRect ds.w 4
ENDR
Once you declare a block of globals this way, you can access the individual
variables without referencing register a5, since the MPW assembler will
automatically generate the ar reference for you. You access a variable by using its data
module name ( eg. ‘MyData’) and a field name from the data template (eg. ‘thePoint’),
much like a record structure in C or Pascal. For example, you can access the variable
at the label ‘thePoint’ with the following code.
move.l MyData.thePoint,-(sp)
You can also declare implicit data modules by using the WITH directive. For
example, if you preceded the previous example with the statement:
Start MAIN
WITH MyData
; Code statements begin here
you could dispense with the data module name when accessing individual fields of the
data module, so that the access statement becomes:
move.l thePoint,-(sp)
In summary then; when converting an MDS file to MPW, gather all your DS
directive lines into a single data module and give the data module name (the label that
appears on the same line as the RECORD directive) in a WITH statement before
accessing any of the variables in the data module. You can then access the variables by
using their names without worrying about referencing register a5.
Declare QuickDraw Globals
One of the hidden features of the MDS linker is that it places the application
globals 256 bytes below the a5 boundary by default. The MPW linker places the
application globals right below the a5 boundary, without any cushion. Most casual
assembly language programmers (is there such a thing?) just accept the default and
don’t really think about it, but it can get you in a lot of trouble when converting
between MDS and MPW if you don’t take this difference into account.
In particular, it is common for MDS programs to assume that the QuickDraw
globals will be placed just below the a5 boundary, extending downward in memory for
206 bytes. The ROM call InitGraf expects a pointer to the 203rd byte of a 206 byte
area for the QuickDraw globals. Because MDS gives a 256 byte cushion below the a5
boundary before the application’s globals, MDS programs typically call InitGraf with a
pointer to a long word four bytes below the a5 boundary, as shown below.
; typical MDS technique
pea -4(a5)
_InitGraf
This works great in MDS programs because the QuickDraw globals grow
downward from the a5 boundary but are not large enough to outgrow the 256 byte
cushion provided by the MDS linker.
In MPW, you are not so lucky. You must explicitly allocate some global storage
for the Quickdraw globals in a data module and then pass a pointer to that module when
calling InitGraf. If you don’t do this and use the previous code to initialize QuickDraw,
your globals and the QuickDraw globals will share overlapping data space beneath the
a5 boundary, and that will cause your program to crash as your program and
QuickDraw write over each others globals.
The sample program provided with MPW shows a good method for allocating data
space for the QuickDraw globals. Listed below is a simplified version of that method.
First, declare a data module for the QD globals. This declaration should come just
before or just after your application’s data declaration, but before any code
statements, as shown below.
QDGlobals RECORD ,DECREMENT
thePort DS.L 1
white DS.B 8
black DS.B 8
gray DS.B 8
ltGray DS.B 8
dkGray DS.B 8
arrow DS.B cursRec
screenBits DS.B bitmapRec
randSeed DS.L 1
ORG -grafSize
ENDR
Once the QD globals data module is declared, it will be combined with any other
data modules in your program and loaded just below the a5 boundary when the program
is loaded. Because you don’t know exactly where the QD globals will be loaded in
relation to the other data modules (alright, if you dig through the MPW manuals you
can probably figure out how the data modules are ordered).
In order to use the QuickDraw globals that are defined as a data module, you
should place the name of the data module in the WITH directive along with the name of
your application’s data module, as shown below.
Start MAIN
WITH MyData,QDGlobals
; Code statements begin here
And, once you are using the QD globals you have defined, you can point directly to
the QD data area when initializing QuickDraw, as shown below.
; thePort is a variable in QDGlobals
pea thePort
_InitGraf
By explicitly defining a data area for the QuickDraw globals and pointing to it
when initializing QuickDraw you can avoid the data overlap that occurs when you
convert an MDS program without thinking about the details.
Misc Details
-- MPW uses IMPORT and EXPORT instead of the XDEF and XREF directives found in
MDS.
-- Macros are defined differently in MPW and MDS. For example, take a look at the
following macro defined for both MDS and MPW:
MDS macro:
MACRO _SFGetFile =
MOVE.W #2,-(SP)
_Pack3
|
MPW equivalent macro:
MACRO
_SFGetFile
MOVE.W #2,-(SP)
_Pack3
ENDM
-- The labels used with EQU and SET statements in MPW must be in the first column,
unlike MDS.
-- RMaker and rez source files are totally incompatable, but you use your old
resource files by either “including” them in your rez file or running derez on
them to get a rez compatable source file.
-- Make files made by the “Create Build Commands...” menu option in MPW
typically are a good way to build your programs, but you can remove the link
files “Runtime.o” and “Interface.o” put in the make file by default.