← Back to Technotes

#22: Proper Use of Dynamic Segments

Author: Guillermo Ortiz (rev. Eric Soldan & Andy Stadler)
Year: 1987

... discusses strategies that applications can use to deal with dynamic segments

View raw text file

Apple II
Technical Notes
_____________________________________________________________________________
                                                  Developer Technical Support

Apple IIgs
#22:    Proper Use of Dynamic Segments

Rewritten by:    Eric Soldan & Andy Stadler                    September 1990
Written by:      Guillermo Ortiz                                 October 1987

This Technical Note discusses strategies that applications can use to deal 
with dynamic segments.
Changes since November 1988:  Rewrote from scratch to address current 
problems.
_____________________________________________________________________________

When reading the documentation on dynamic segments, it initially appears that 
they are even better than sliced bread.  While they are incredibly useful, 
there are two issues that make dealing with them somewhat tricky.  The first 
involves loading a dynamic segment; the second involves unloading a dynamic 
segment.  Everything else works fine.


Loading Dynamic Segments

Loading dynamic segments is supposed to happen automatically.  You are 
supposed to be able to call the code in the dynamic segment, and the system 
automatically loads it.  As long as there is enough RAM to load the segment, 
this is exactly what happens.

The problem arises when there isn't enough memory.  Immediately you have a 
number of questions, such as "How do I know if it didn't load?" and "How is 
the not-enough-memory error returned?"  Unfortunately, neither of these 
questions is applicable.  Instead, you get a Fatal System Error, which is not 
the most useful thing that could happen.

However, there are some reasons for this error.  For example, in the Pascal or 
Toolbox stack frame system, the called function is responsible for removing 
the parameters pushed onto the stack.  If the dynamic segment did not load, 
these parameters cannot be pulled from the stack, and if they are not pulled 
from the stack, the operating system cannot return to the caller.

Due to this problem, the best thing to do is to try to load the dynamic 
segment with LoadSegName.  If it loads, then there is (obviously) enough RAM 
for it.  If it does not load, then there was not enough RAM; it's that simple.  
So, to call a function named dynFN in a dynamic segment called dynSeg, you 
would do the following:

       LoadSegName("\pDynSeg");
       if (!_toolErr) {
           dynFN(some, number, of, parameters);
           UnLoadSeg(dynFN);
       }
       else ErrorAlert("\pOut of RAM.");


Unloading Dynamic Segments

UnLoadSeg used to have a problem, so the above technique would not have 
worked.  As of System Software 5.0.3, this problem has been fixed.  In the 
example, the code UnLoadSeg(dynFN) does not pass the address of the dynFN that 
was loaded into RAM.  Instead, that address represents the entry in the 
dynamic segment jump table for that particular function.  The jump table is 
always in RAM.  So, you are not actually passing an address of the segment to 
be unloaded, but an address in the jump table.

The loader is responsible for figuring out that the address is actually an 
address in the jump table, and it is supposed to unload the segment to which 
the jump table entry refers.  The loader did not handle this case properly 
until 5.0.3.  So, for system disks prior to System Disk 5.0.3, you can 
preserve the segment number returned by the LoadSegName call to issue an 
UnLoadSegNum call to dispose of the dynamic segment.  Due to UnLoadSeg not 
doing the job prior to 5.0.3, you could use UnLoadSegNum.  This also has 
problems.  ExpressLoad changes the segment numbers, so it is difficult to 
maintain the segment numbers if you change the link script.  For these 
reasons, the below technique should be used for system disks prior to 5.0.3:

void   sample()
{
       struct    LoadSegNameOut    dynSegInfo;

       dynSegInfo = LoadSegName("\pDynSeg");
       if (!_toolErr) {
           dynFN(some, number, of, parameters);
           UnLoadSegNum(dynSegInfo.segNum);
       }
       else ErrorAlert("\pOut of RAM.");
}


Dynamic Segment Interdependencies:  Just Say No

Dynamic Segments calling each other almost always lead to unloading conflicts, 
and more importantly, they defeat the purpose (if they both have to be in 
simultaneously then they might as well be static).  Figure 1 is a sample 
program layout you may want to consider when designing your application 
dynamic segment usage:

            Main Program
    Dispatcher & User Interface  <-- static

         /       |       \
        /        |        \

     Mode 1    Mode 2    Mode 3  <-- dynamic
      Code      Code      Code

        \        |        /
         \       |       /

        Shared Utility Code      <-- static

    Figure 1-Sample Program Layout

Also, if one of the dynamic segments described is much more than, say, 32K or 
40K, you may wish to load a pair (or more) of dynamic segments.  These dynamic 
segment pairs would always be loaded and unloaded simultaneously.  Why?  
Because loading two 25K segments is more likely to succeed than loading one 
50K segment.


A Final Warning:

Data in a dynamic segment is a tricky issue.  When you call a dynamic segment, 
you are not sure if it got loaded, or if it was already in RAM, and therefore 
you cannot be sure of the values in your global data.  For example, say that 
you have a global variable that represents the number of times that you call 
the dynamic segment.  Every time you call the segment, you would increment 
this variable.  This technique works great until the dynamic segment gets 
purged.  Once it is purged, the next time you call it, the variable area would 
be loaded from disk again, with its original initial value.  The count is no 
longer valid.  To fix this, you can place the global could variable in the 
static globals space for the main code.  Then the variable would not get 
purged, and your count would be valid.  Of course, if you have global data 
that does not ever change, then it is okay for the data to be in the global 
segment.


Further Reference
_____________________________________________________________________________
  o  GS/OS Reference
  o  Apple IIgs Programmer's Workshop Assembler Reference