Legacy Documentclose button

Important: The information in this document is obsolete and should not be used for new development.

Previous Book Contents Book Index Next

Inside Macintosh: Processes
Chapter 4 - Vertical Retrace Manager / Using the Vertical Retrace Manager


Accessing Application Global Variables in a VBL Task

The Operating System stores the address of the boundary between the current application's global variables and its application parameters in the microprocessor's A5 register. For this reason, most compilers generate references to application global variables as offsets from the address contained in the A5 register. Therefore, if the value in register A5 does not point to the boundary between your application's global variables and its application parameters, your attempts to access your application's global variables will fail.

Ordinarily, applications do not need to keep track of the value in the A5 register. Although all applications share the register, the Process Manager keeps track of the address of your application's A5 world when a major or minor switch causes your application to yield the CPU to other processes, and it restores that value when your application regains access to the CPU. The A5 register is guaranteed to be correct for all code that your application executes directly (that is, for all code that is not executed in response to an interrupt or by a Toolbox or Operating System routine).

Because VBL tasks are interrupt routines, they might be executed when the value in the A5 register does not point to the A5 world of your application. As a result, if you want to access your application's global variables in a VBL task, you need to set the A5 register to its correct value when your VBL task begins executing and restore the previous value upon exit.

Note
For a more complete discussion of the A5 register, see the chapter "Memory Management Utilities" in Inside Macintosh: Memory. ·
The solution to this problem is to find a memory location that both the main program and the VBL task can access. The main program can store the value of register A5
there, and the VBL task can set A5 correctly by reading that value. The functions SetCurrentA5 and SetA5 can be used for this purpose. The application can store the value of its A5 register by calling SetCurrentA5. Then, at interrupt time, the task can begin by calling SetA5 to set the register to that value and end by calling SetA5 again, this time to restore the register to its initial value, the one used by the main program.

The only memory location that a VBL task has access to is the address of the task record, as explained in the previous section, "Accessing a Task Record at Interrupt Time." So, if your application stores the value of A5 directly following the task record in memory, it can locate the value of A5 by first locating the task record. You can do this by defining a new data type (called VBLRec in Listing 4-6) whose first field contains the VBL task and whose second field contains a long integer specifying the value of the A5 register.

Listing 4-6 Storing the value of the A5 register directly after the task record in memory

TYPE VBLRec =
   RECORD
      myVBLTask:     VBLTask;    {the actual VBL task record}
      vblA5:         LongInt;    {saved value of application's A5}
   END;

   VBLRecPtr = ^VBLRec;
Now you can modify the application-defined procedure that installs a VBL task so that it stores the value of register A5 in the vblA5 field of the VBLRec, as illustrated in
Listing 4-7.

Listing 4-7 Saving the value of the A5 register when installing a VBL task

FUNCTION InstallVBL: OSErr;
CONST
   kInterval = 6;                   {frequency in interrupts}
BEGIN
   WITH gMyVBLRec.myVBLTask DO      {initialize the VBL task}
      BEGIN
         qType := ORD(vType);       {set queue type}
         vblAddr := @DoVBL;         {set address of VBL task}
         vblCount := kInterval;     {set task frequency}
         vblPhase := 0;             {no phase}
      END;
   myVBLRec.vblA5 := SetCurrentA5;  {get our A5}

   InstallVBL := VInstall(@gMyVBLRec.myVBLTask);
END;
You must also modify the VBL task so that it sets and restores the value of register A5 correctly. Listing 4-8 illustrates a simple VBL task that increments the global variable gCounter and then resets itself to run again after the specified number of interrupts.

Listing 4-8 Setting up the A5 register and modifying a global variable in a VBL task

PROCEDURE DoVBL;
CONST
   kInterval = 6;                   {frequency in interrupts}
VAR
   curA5:   LongInt;                {stored value of A5}
   recPtr:  VBLRecPtr;              {pointer to task record}
BEGIN
   recPtr := VBLRecPtr(GetVBLRec);  {get address of task record}
   curA5 := SetA5(recPtr^.vblA5);   {set our application's A5 }
                                    { and store old A5 in curA5}
   gCounter := gCounter + 1;        {modify a global variable}

   {Reset vblCount so that this procedure executes again.}
   recPtr^.myVBLTask.vblCount := kInterval;

   curA5 := SetA5(curA5);           {restore the old A5 value}
END;
Because of the optimizations performed by some compilers, the actual work of the VBL task and the setting and restoring of the A5 register might have to be placed in separate procedures. If necessary, you can define a routine DoVBL that loads the proper value of A5, calls another routine called RunVBL, and then restores the old value of A5. The RunVBL routine does the work of the VBL task and resets the task record's vblCount field so that the DoVBL routine executes again. Listing 4-9 illustrates a sample definition of the RunVBL function that modifies an application global variable.

Listing 4-9 Modifying application global variables in a VBL task

PROCEDURE RunVBL (aRecPtr: VBLRecPtr);
CONST
   kInterval = 6;                   {frequency in interrupts}
BEGIN
   gCounter := gCounter + 1;        {modify global variable}

   {Reset vblCount so that this procedure executes again.}
   aRecPtr^.myVBLTask.vblCount := kInterval;
END;
Listing 4-10 shows how to call RunVBL from the VBL task.

Listing 4-10 Setting up and restoring the A5 register in a VBL task

PROCEDURE DoVBL;
VAR
   curA5:   LongInt;                {stored value of A5}
   recPtr:  VBLRecPtr;              {pointer to task record}
BEGIN
   recPtr := VBLRecPtr(GetVBLRec);  {get address of task record}
   curA5 := SetA5(recPtr^.vblA5);   {set our application's A5 }
                                    { and store old A5 in curA5}
   RunVBL(recPtr);                  {run the actual VBL task}
   curA5 := SetA5(curA5);           {restore the old A5 value}
END;
If this separation of routines is necessary, you must make sure that the two routines (DoVBL and RunVBL) are in the same code segment.


Previous Book Contents Book Index Next

© Apple Computer, Inc.
17 JUN 1996