|Column Tag:||Developer Notes
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 dont 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 dont 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 dont 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:
thePointds.l1 ;space for a Point
theRect ds.w4 ;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.
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).
thePoint ds.l 1
theRect ds.w 4
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.
You can also declare implicit data modules by using the WITH directive. For example, if you preceded the previous example with the statement:
; 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:
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 dont really think about it, but it can get you in a lot of trouble when converting between MDS and MPW if you dont 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 applications 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
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 dont 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 applications 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
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 dont 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 applications data module, as shown below.
; 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
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.
-- 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:
MACRO _SFGetFile =
MPW equivalent macro:
-- 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.