home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1995 August / NEBULA.mdf / Apps / DevTools / MachOViewer / Source / MachOView.m < prev    next >
Encoding:
Text File  |  1994-05-29  |  29.8 KB  |  1,284 lines

  1.  
  2. #import <MachOView.h>
  3.  
  4. /* $Log:    MachOView.m,v $
  5. Revision 1.18  94/05/29  15:00:14  ediger
  6. zero out a memory allocation to make it clear it's unused
  7.  
  8. Revision 1.17  94/05/28  16:17:55  ediger
  9. finally got section names to draw acceptably.
  10.  
  11. Revision 1.16  94/05/26  21:21:06  ediger
  12. correct address bracket zooming, hi and lo address bracket checking
  13.  
  14. Revision 1.15  94/05/23  20:32:13  ediger
  15. lot's o' stuff to do address bracket zoom-in
  16.  
  17. Revision 1.14  94/05/17  23:09:48  ediger
  18. support for stacking of zoomed address ranges, changed types of
  19. some instance vars from id to pointer to that kind of object.
  20.  
  21. Revision 1.13  94/05/10  22:41:07  ediger
  22. support for displaying a "zooming box" by mouse drag
  23.  
  24. Revision 1.12  94/05/04  11:19:58  ediger
  25. rearrange drawSelf:: to be a bit more modular
  26.  
  27. Revision 1.10  94/05/01  17:32:57  ediger
  28. efficiency improvements
  29.  
  30. Revision 1.9  94/02/07  21:37:17  ediger
  31. Use of memory-mapped file object to manage file mapping,
  32. add display of segment names and use of matrix of buttons
  33. for choices of what to display.
  34.  
  35. Revision 1.7  94/01/30  16:27:34  ediger
  36. mostly correct address spacing
  37.  
  38. Revision 1.6  94/01/01  23:02:22  ediger
  39. broke address label spacing calcs into separate method.
  40.  
  41. Revision 1.5  93/12/31  17:32:28  ediger
  42. better address label spacing
  43.  
  44. Revision 1.3  93/12/30  19:01:37  ediger
  45. support for showing upper and lower addresses
  46. of memory mapped segments.
  47.  
  48. Revision 1.2  93/12/19  14:31:43  ediger
  49. corrected scaling and inclusion of mapped segments of library files
  50.  
  51. Revision 1.1  93/12/19  12:57:04  ediger
  52. Initial revision
  53.  
  54.  */
  55.  
  56. @implementation MachOView
  57.  
  58. static char rcsident[] = "$Id: MachOView.m,v 1.18 94/05/29 15:00:14 ediger Exp Locker: ediger $";
  59.  
  60. #undef DEBUG
  61. #ifdef DEBUG
  62. #define D(c) c
  63. #define ENTER(m) fprintf(stderr, "enter %s\n", m);
  64. #define EXIT(m) fprintf(stderr, "exit %s\n", m);
  65. #else
  66. #define D(c)
  67. #define ENTER(m)
  68. #define EXIT(m)
  69. #endif
  70.  
  71. //M+    -findSizes
  72. //    PURPOSE:
  73. //        Sort through the addresses of the mapped segments in the Mach-O file
  74. //        to be represented, determining the high address, low address, and
  75. //        the number of bytes used in the address space.
  76. //
  77. //    EDITORIAL:
  78. //        Should only be called after using MachOFile instance var to
  79. //        open or re-open the Mach-O file being represented.
  80. //M-
  81. - findSizes
  82. {
  83.     int iSegments, iCC;
  84.     unsigned long ulBaseAddr, ulUpperAddr;
  85.     char bvBuf[64];
  86.  
  87.     ENTER("- findSizes");
  88.  
  89.     loAddr = (unsigned long)-1;  /* cheat */
  90.     sumAddr = hiAddr = 0;
  91.  
  92.     iSegments = [theFile mappedSegments];
  93.  
  94.     for (iCC = 0; iCC < iSegments; ++iCC)
  95.     {    id oSegment = [theFile mappedSegment:iCC];
  96.  
  97.         ulBaseAddr = [oSegment getBaseAddress];
  98.         if (loAddr > ulBaseAddr) loAddr = ulBaseAddr;
  99.         ulUpperAddr = [oSegment getUpperAddress];
  100.         if (hiAddr < ulUpperAddr) hiAddr = ulUpperAddr;
  101.         sumAddr += (ulUpperAddr - ulBaseAddr);
  102.     }
  103.  
  104.     sprintf(bvBuf, "0x%x", loAddr);
  105.     [minAddress setStringValue:bvBuf];
  106.     sprintf(bvBuf, "0x%x", hiAddr);
  107.     [maxAddress setStringValue:bvBuf];
  108.  
  109.     EXIT("- findSizes");
  110.     return self;
  111. }
  112.  
  113. //M+    -(char *)fileType
  114. //M-
  115. - (char *)fileType
  116. {
  117.     return [theFile fileType];
  118. }
  119.  
  120. //M+    -openFileNamed:(char *)fileName
  121. //
  122. //M-
  123. - openFileNamed:(char *)fileName
  124. {
  125.     ENTER("- openFileNamed:");
  126.     assert(NULL != fileName);
  127.  
  128.     [self freeInternals];
  129.  
  130.     currentFileName = strcpy(malloc(strlen(fileName) + 1), fileName);
  131.  
  132.     mappedFile = [[[MappedFile alloc] init] filename:currentFileName];
  133.     if (mappedFile == NULL || [mappedFile map] == FALSE)
  134.     {    [cpuType    setStringValue:"Problem opening"];
  135.         [cpuSubtype setStringValue:currentFileName];
  136.  
  137.     } else {
  138.         theFile = [[[MachOFile alloc]init]
  139.             fillFromAddress:[mappedFile address]];
  140.         if (NULL == theFile)
  141.         {    [cpuType    setStringValue:"Not a"];
  142.             [cpuSubtype setStringValue:"Mach-O file"];
  143.         } else {
  144.             int iSegments = [theFile mappedSegments];
  145.             addressMap = (struct addressMapping *)malloc(
  146.                 iSegments * sizeof(struct addressMapping));
  147.             bzero(addressMap, iSegments * sizeof(struct addressMapping));
  148.             [cpuType    setStringValue:[theFile cpuType]];
  149.             [cpuSubtype setStringValue:[theFile cpuSubtype]];
  150.             [[self findSizes] display];
  151.         }
  152.     }
  153.  
  154.     EXIT("- openFileNamed:");
  155.     return self;
  156. }
  157.  
  158. //M+    -toggleScaling:sender
  159. //    Action called by clicking on a button.  Changes the "toScale" instance
  160. //    variable, and calls -display, to reflect the change.
  161. //M-
  162. - toggleScaling:sender
  163. {
  164.     if (toScale)
  165.     {
  166.         toScale = FALSE;
  167.         showSectionNames  = FALSE;
  168.         [showSectionsButton setEnabled:NO];
  169.         [showSectionsButton setIntValue:0];
  170.  
  171.     } else {
  172.         toScale = TRUE;
  173.         [showSectionsButton setEnabled:YES];
  174.         [showSectionsButton setIntValue:0];
  175.     }
  176.  
  177.     [self display];
  178.  
  179.     return self;
  180. }
  181.  
  182.  
  183. //M+    -toggleAddresses:sender
  184. //    Action called by clicking on a button.  Changes the "showAddresses"
  185. //    instance variable, and calls -display, to reflect the change.
  186. //M-
  187. - toggleAddresses:sender
  188. {
  189.     if (showAddresses)
  190.         showAddresses  = FALSE;
  191.     else
  192.         showAddresses  = TRUE;
  193.  
  194.     [self display];
  195.  
  196.     return self;
  197. }
  198.  
  199. //M+    -toggleNames:sender
  200. //    Action called by clicking on a button.  Changes the "showNames"
  201. //    instance variable, and calls -display, to reflect the change.
  202. //M-
  203. - toggleNames:sender
  204. {
  205.     if (showNames)
  206.         showNames  = FALSE;
  207.     else
  208.         showNames  = TRUE;
  209.  
  210.     [self display];
  211.  
  212.     return self;
  213. }
  214.  
  215. //M+    -toggleThreads:sender
  216. //    Action called by clicking on a button.  Changes the "showThreads"
  217. //    instance variable, and calls -display, to reflect the change.
  218. //M-
  219. - toggleThreads:sender
  220. {
  221.     if (showThreads)
  222.         showThreads  = FALSE;
  223.     else
  224.         showThreads  = TRUE;
  225.  
  226.     [self display];
  227.  
  228.     return self;
  229. }
  230.  
  231. //M+    -toggleSections:sender
  232. //    Action called by clicking on a button.  Changes the "showSectionNames"
  233. //    instance variable, and calls -display, to reflect the change.
  234. //M-
  235. - toggleSections:sender
  236. {
  237.     int iSegments, iCC;
  238.  
  239.     if (showSectionNames)
  240.         showSectionNames  = FALSE;
  241.     else
  242.         showSectionNames  = TRUE;
  243.  
  244.     iSegments = [theFile mappedSegments];
  245.  
  246.     for (iCC = iSegments - 1; iCC > -1; --iCC)
  247.     {
  248.         int             i;
  249.         SegmentCommand *oSegment  = [theFile mappedSegment:iCC];
  250.         int             iSection  = [oSegment numberOfSections];
  251.         struct section *spSection = [oSegment getSection:0];
  252.  
  253.         if (iSection > 0)
  254.         {
  255.             if (addressMap[iCC].sectionMap == NULL)
  256.                 addressMap[iCC].sectionMap =
  257.                     (struct sectionNameMapping *)malloc(
  258.                         iSection*sizeof(struct sectionNameMapping));
  259.  
  260.             assert(addressMap[iCC].sectionMap != NULL);
  261.  
  262.             for (i = 0; i < iSection; ++i)
  263.             {
  264.                 addressMap[iCC].sectionMap[i].sectionName
  265.                     = spSection[i].sectname;
  266.                 addressMap[iCC].sectionMap[i].sectionAddress
  267.                     = spSection[i].addr;
  268.             }
  269.  
  270.         } else
  271.             addressMap[iCC].sectionMap = NULL;
  272.     }
  273.  
  274.     [self display];
  275.  
  276.     return self;
  277. }
  278.  
  279. //M+    -toggleLibraries:sender
  280. //    PURPOSE:
  281. //    Action called by clicking on a button.  Changes the "showLibraries"
  282. //    instance variable, and calls -display, to reflect the change.
  283. //    EDITORIAL:
  284. //    Somewhat more elaborate than the other -toggle* methods, since the
  285. //    Mach-O file object instance variable gets completely redone.  This
  286. //    may be a case of bad design of the MachOFile object percolating up
  287. //    to here.
  288. //M-
  289. - toggleLibraries:sender;
  290. {
  291.     if (theFile != NULL)
  292.         [theFile free];
  293.     theFile = [[MachOFile alloc] init];
  294.  
  295.     if (NULL != addressMap)
  296.     {
  297.         int iSegments = [theFile mappedSegments];
  298.         while (--iSegments > -1)
  299.             if (addressMap[iSegments].sectionMap)
  300.                 free(addressMap[iSegments].sectionMap);
  301.  
  302.         free(addressMap);
  303.         addressMap = NULL;
  304.     }
  305.  
  306.     if (showLibraries)
  307.     {
  308.         showLibraries = FALSE;
  309.         [theFile unconsiderMappedFiles];
  310.     } else {
  311.         showLibraries = TRUE;
  312.         [theFile considerMappedFiles];
  313.     }
  314.  
  315.     [theFile fillFromAddress:[mappedFile address]];
  316.  
  317.     addressMap = (struct addressMapping *)malloc(
  318.         [theFile mappedSegments] * sizeof(struct addressMapping));
  319.     bzero(addressMap, [theFile mappedSegments] * sizeof(struct addressMapping));
  320.  
  321.     // since the MachOFile object has been redone, zoom state is meaningless.
  322.     if (NULL != zoomStack)
  323.     {
  324.         void *vp;
  325.  
  326.         while ((vp = [zoomStack pop]) != NULL)
  327.             free(vp);
  328.     }
  329.     [unzoomButton setEnabled:NO];
  330.     [zoomButton   setEnabled:NO];
  331.  
  332.     [[self findSizes] display];
  333.  
  334.     return self;
  335. }
  336.  
  337. //M+    -windowWillClose:sender
  338. //    PURPOSE:
  339. //        Ditch any memory-consuming instance vars when the window closes.
  340. //    EDITORIAL:
  341. //        one of the Window delegate methods
  342. //M-
  343. - windowWillClose:sender
  344. {
  345.     ENTER("- windowWillClose:");
  346.     [self freeInternals];
  347.     [zoomStack free];
  348.     EXIT("- windowWillClose:");
  349.     return self;
  350. }
  351.  
  352. //M+    -rescale
  353. //    PURPOSE:
  354. //        calculate the scaling instance vars
  355. //M-
  356. - rescale
  357. {
  358.     float ht;
  359.     ENTER("- rescale");
  360.  
  361.     ht = NX_HEIGHT(&bounds) - 25.0;
  362.  
  363.     scaleSlope      = ht/(float)(hiAddr - loAddr);
  364.     scaleYintercept = 5.0 - scaleSlope*(float)loAddr;
  365.  
  366.     smashedSlope      = ht/(float)sumAddr;
  367.     smashedYintercept = 0.0;
  368.  
  369.     EXIT("- rescale");
  370.     return self;
  371. }
  372.  
  373.  
  374. //M+    -initFrame:(const NXRect *)frameRect
  375. //    PURPOSE:
  376. //        Set a bunch of instance variables to some sensible values.
  377. //
  378. //    EDITORIAL:
  379. //        Override of a View method
  380. //M-
  381. - initFrame:(const NXRect *)frameRect
  382. {
  383.     ENTER("- initFrame:");
  384.     [super initFrame:frameRect];
  385.  
  386.     toScale       = TRUE;
  387.     showLibraries = FALSE;
  388.     showAddresses = TRUE;
  389.     showNames     = TRUE;
  390.     showThreads   = TRUE;
  391.     showSectionNames = FALSE;
  392.  
  393.     currentFileName = NULL;
  394.     addressMap      = NULL;
  395.     mappedFile      = NULL;
  396.     theFile         = NULL;
  397.     zoomStack = [[Queue alloc] init];
  398.  
  399.     theFont = [Font newFont:"Courier" size:(float)(LABEL_HT - LABEL_LEADING)
  400.         matrix:NX_IDENTITYMATRIX];
  401.  
  402.  
  403.     EXIT("- initFrame:");
  404.     return self;
  405. }
  406.  
  407. //M+    -drawSelf:(NXRect *)rects :(int)rectCount
  408. //
  409. //    EDITORIAL:
  410. //        Override of a View method.
  411. //        This is way too complicated.
  412. //M-
  413. - drawSelf:(NXRect *)rects :(int)rectCount
  414. {
  415.     int iSegments, iCC;
  416.     float xCoord, yCoord, yLast;
  417.     ENTER("- drawSelf::");
  418.  
  419.     [self rescale];
  420.  
  421.     // make background white and lines black
  422.     PSsetgray(NX_WHITE);
  423.     NXRectFill(&bounds);
  424.     PSsetgray(NX_BLACK);
  425.     NXFrameRect(&bounds);
  426.     PSsetgray(NX_BLACK);
  427.  
  428.     PSnewpath();
  429.     PSsetlinewidth(LINE_WDTH);
  430.  
  431.     [theFont set];
  432.  
  433.     iSegments = [theFile mappedSegments];
  434.  
  435.     if (iSegments == 0)
  436.         return self;
  437.  
  438.     /* stagger the lines representing memory allocations */
  439.     xCoord = NX_WIDTH(&bounds)/2.0 - ((float)iSegments/2.0)*LINE_STAGGER;
  440.     yCoord = VERT_MARGIN;
  441.     yLast  = [[theFile mappedSegment:iSegments - 1] getBaseAddress] - 1.0;
  442.  
  443.     /* count down so that the "not to scale" bit works */
  444.     for (iCC = iSegments - 1; iCC > -1; --iCC)
  445.     {
  446.         id            oSegment    = [theFile mappedSegment:iCC];
  447.         unsigned long ulBaseAddr  = [oSegment getBaseAddress];
  448.         unsigned long ulUpperAddr = [oSegment getUpperAddress];
  449.  
  450.         if (ulBaseAddr > hiAddr)
  451.             break;  // don't need to check any higher
  452.  
  453.         if (
  454.             (ulUpperAddr <= hiAddr && ulBaseAddr >= loAddr) ||
  455.             (ulUpperAddr >= loAddr && ulBaseAddr <  loAddr) ||
  456.             (ulUpperAddr >  hiAddr && ulBaseAddr <  hiAddr)
  457.         )
  458.         {
  459.  
  460.             addressMap[iCC].baseAddress
  461.                 = (loAddr < ulBaseAddr)?ulBaseAddr:loAddr;
  462.             addressMap[iCC].endAddress
  463.                 = (hiAddr > ulUpperAddr)?ulUpperAddr:hiAddr;
  464.  
  465.             if (showNames)
  466.                 addressMap[iCC].segmentName  = [oSegment commandName];
  467.  
  468.             if (toScale)
  469.             {
  470.                 addressMap[iCC].yCoordBase = 
  471.                     (loAddr < ulBaseAddr)?
  472.                     scaleSlope*(float)ulBaseAddr + scaleYintercept
  473.                     : scaleSlope*(float)loAddr + scaleYintercept
  474.                     ;
  475.                 addressMap[iCC].yCoordTop  =
  476.                     (hiAddr > ulUpperAddr) ?
  477.                     scaleSlope*(float)ulUpperAddr + scaleYintercept
  478.                     : scaleSlope*(float)hiAddr + scaleYintercept
  479.                     ;
  480.  
  481.                 PSmoveto(xCoord, addressMap[iCC].yCoordBase);
  482.         
  483.                 PSlineto(xCoord, addressMap[iCC].yCoordTop);
  484.  
  485.             } else {
  486.  
  487.                 if (ulBaseAddr != yLast)
  488.                     PSmoveto(xCoord, yCoord + SEGMENT_GAP);
  489.                 else
  490.                     PSmoveto(xCoord, yCoord);
  491.  
  492.                 if (ulBaseAddr != yLast)
  493.                     addressMap[iCC].yCoordBase = yCoord + SEGMENT_GAP;
  494.                 else
  495.                     addressMap[iCC].yCoordBase = yCoord;
  496.  
  497.                 if (ulUpperAddr <= hiAddr && ulBaseAddr >= loAddr)
  498.                     yCoord += smashedSlope * (float)(ulUpperAddr - ulBaseAddr)
  499.                         + smashedYintercept;
  500.                 else if (ulUpperAddr >= loAddr && ulBaseAddr <  loAddr)
  501.                     yCoord += smashedSlope * (float)(ulUpperAddr - loAddr)
  502.                         + smashedYintercept;
  503.                 else if (ulUpperAddr >  hiAddr && ulBaseAddr <  hiAddr)
  504.                     yCoord += smashedSlope * (float)(hiAddr - ulBaseAddr)
  505.                         + smashedYintercept;
  506.  
  507.                 PSlineto(xCoord, yCoord);
  508.  
  509.                 addressMap[iCC].yCoordTop = yCoord;
  510.  
  511.                 yLast = ulUpperAddr;
  512.             }
  513.  
  514.             /* stagger to the right, on the way up */
  515.             xCoord += LINE_STAGGER;
  516.  
  517.         }
  518.     }
  519.  
  520.     PSstroke();
  521.  
  522.     if (yLooping)
  523.     {
  524.         // draw on the box marking the zooming zone
  525.         drawBox(sFirstMouseDown.x, sFirstMouseDown.y,
  526.             sMouseDown.x, sMouseDown.y);
  527.     } else {
  528.         [zoomButton setEnabled:NO];
  529.     }
  530.  
  531.     /* label the ends of each memory segment with its address */
  532.     if (TRUE == showAddresses)
  533.         [self drawAddresses];
  534.  
  535.     /* label the middle of each memory segment with its name */
  536.     if (TRUE == showNames)
  537.         [self drawSegmentNames];
  538.  
  539.     /* draw in little markers for threads program counters */
  540.     if (showThreads)
  541.         [self drawThreads];
  542.  
  543.     if (showSectionNames)
  544.         [self drawSectionNames];
  545.  
  546.     EXIT("- drawSelf::");
  547.     return self;
  548. }
  549.  
  550. //M+    -drawThreads
  551. //M-
  552. - drawThreads
  553. {
  554.     int iCC, iThreadCount = [theFile threads];
  555.     int iSegCount = [theFile mappedSegments];
  556.  
  557.     PSsetlinewidth(0.50);
  558.  
  559.     for (iCC = 0; iCC < iThreadCount; ++iCC)
  560.     {
  561.         float xCoord, yCoord;
  562.         int iSegNo = [theFile segmentOfThread:iCC];
  563.  
  564.         // iSegNo is -1 if there's no segment containing this thread's PC.
  565.         // abort(2) causes just this sort of situation.
  566.         if (iSegNo > -1)
  567.         {
  568.             unsigned long ulPCAddr = [[theFile thread:iCC] pc];
  569.  
  570.             if (ulPCAddr >= loAddr && ulPCAddr <= hiAddr)
  571.             {
  572.  
  573.                 xCoord = NX_WIDTH(&bounds)/2.0
  574.                 - ((float)[theFile mappedSegments]/2.0)*(LINE_STAGGER)
  575.                 + (float)(iSegCount - iSegNo) * LINE_STAGGER;
  576.  
  577.                 yCoord = addressMap[iSegNo].yCoordBase
  578.             + (addressMap[iSegNo].yCoordTop - addressMap[iSegNo].yCoordBase)
  579.             * ((float)ulPCAddr - addressMap[iSegNo].baseAddress)
  580.             / (addressMap[iSegNo].endAddress-addressMap[iSegNo].baseAddress);
  581.  
  582.                 drawLeftArrowHead(xCoord, yCoord, 8.0);
  583.             }
  584.         }
  585.     }
  586.  
  587.     return self;
  588. }
  589.  
  590. //M+    -spaceLabels
  591. //    Figure out the spacing of the address labels
  592. //    Fills in yCoordBaseLabel and yCoordTopLabel fields such that the
  593. //    addresses don't overlap, and don't end up out of the MachOView.
  594. //    yCoordBaseLabel field ends up as -1.0 if it is the same number
  595. //    as the yCoordTopLabel of the preceding (lower in memory) segment.
  596. //M-
  597. - spaceLabels
  598. {
  599.     float lastY;
  600.     float yLastAddress;
  601.     int   iCC, iSegments = [theFile mappedSegments];
  602.  
  603.     lastY = addressMap[iSegments - 1].baseAddress - 20.0;
  604.     yLastAddress = addressMap[iSegments - 1].yCoordBase - LABEL_HT;
  605.  
  606.     for (iCC = iSegments - 1; iCC > -1; --iCC)
  607.     {
  608.         if (addressMap[iCC].baseAddress > hiAddr)
  609.             break;  // no need to go any higher
  610.  
  611.         if (
  612. (addressMap[iCC].endAddress <= hiAddr && addressMap[iCC].baseAddress >= loAddr)
  613. ||(addressMap[iCC].endAddress >= loAddr && addressMap[iCC].baseAddress < loAddr)
  614. ||(addressMap[iCC].endAddress > hiAddr && addressMap[iCC].baseAddress < hiAddr)
  615.         )
  616.         {
  617.  
  618.             if (addressMap[iCC].baseAddress != lastY)
  619.             {
  620.                 if (addressMap[iCC].yCoordBase < yLastAddress + LABEL_HT)
  621.                     addressMap[iCC].yCoordBaseLabel
  622.                         = yLastAddress + (LABEL_HT + 2.0);
  623.                 else
  624.                     addressMap[iCC].yCoordBaseLabel=addressMap[iCC].yCoordBase;
  625.  
  626.                 yLastAddress = addressMap[iCC].yCoordBaseLabel;
  627.  
  628.             } else {
  629.                 /* don't need to show this segment's base address */
  630.                 addressMap[iCC].yCoordBaseLabel = -1.0;
  631.             }
  632.  
  633.             if (addressMap[iCC].yCoordTop < yLastAddress + LABEL_HT)
  634.                 addressMap[iCC].yCoordTopLabel = yLastAddress + (LABEL_HT+2.0);
  635.             else
  636.                 addressMap[iCC].yCoordTopLabel = addressMap[iCC].yCoordTop;
  637.  
  638.             yLastAddress = addressMap[iCC].yCoordTopLabel;
  639.             lastY = addressMap[iCC].endAddress;
  640.  
  641.         }
  642.     }
  643.  
  644.     if (yLastAddress + LABEL_HT > NX_HEIGHT(&bounds))
  645.     {
  646.         yLastAddress = NX_HEIGHT(&bounds) - (LABEL_HT + 2.0);
  647.  
  648.         /* try to reshuffle address labels so everything fits */
  649.         for (iCC = 0; iCC < iSegments; ++iCC)
  650.         {
  651.  
  652.             if (addressMap[iCC].endAddress < loAddr)
  653.                 break;  // no need to go any lower
  654.  
  655.             if (
  656. (addressMap[iCC].endAddress <= hiAddr && addressMap[iCC].baseAddress >= loAddr)
  657. ||(addressMap[iCC].endAddress >= loAddr && addressMap[iCC].baseAddress < loAddr)
  658. ||(addressMap[iCC].endAddress > hiAddr && addressMap[iCC].baseAddress < hiAddr)
  659.             )
  660.             {
  661.  
  662.                 if (addressMap[iCC].yCoordTopLabel > yLastAddress)
  663.                 {
  664.                     addressMap[iCC].yCoordTopLabel = yLastAddress;
  665.                     yLastAddress -= (LABEL_HT + 2.0);
  666.                 } else if (0.0 > addressMap[iCC].yCoordTopLabel)
  667.                     continue;  // this label doesn't show.  go to next visible.
  668.                 else
  669.                     break;  /* slipped down into a gap */
  670.  
  671.                 if (addressMap[iCC].yCoordBaseLabel > yLastAddress)
  672.                 {
  673.                     addressMap[iCC].yCoordBaseLabel = yLastAddress;
  674.                     yLastAddress -= (LABEL_HT + 2.0);
  675.                 } else if (0.0 > addressMap[iCC].yCoordBaseLabel)
  676.                     continue;
  677.                 else
  678.                     break;
  679.             }
  680.         }
  681.     }
  682.  
  683.     return self;
  684. }
  685.  
  686. //M+    -spaceNames
  687. //    Figure out the spacing of the segment name labels
  688. //    Fills in yCoordBaseLabel field such that the
  689. //    names don't overlap, and don't end up out of the MachOView.
  690. //    Segment 
  691. //M-
  692. - spaceNames
  693. {
  694.     int   iCC, iSegments = [theFile mappedSegments];
  695.     float yLastAddress;
  696.  
  697.     yLastAddress = addressMap[iSegments - 1].yCoordBase - LABEL_HT;
  698.  
  699.     for (iCC = iSegments - 1; iCC > -1; --iCC)
  700.     {
  701.             if (
  702. (addressMap[iCC].endAddress <= hiAddr && addressMap[iCC].baseAddress >= loAddr)
  703. ||(addressMap[iCC].endAddress >= loAddr && addressMap[iCC].baseAddress < loAddr)
  704. ||(addressMap[iCC].endAddress > hiAddr && addressMap[iCC].baseAddress < hiAddr)
  705.             )
  706.         {
  707.         /* space name across width of view */
  708.         addressMap[iCC].nameXcoord = NX_WIDTH(&bounds) - LABEL_MARGIN
  709.             - [theFont getWidthOf:addressMap[iCC].segmentName];
  710.  
  711.         /* put name in middle of segment vertically */
  712.         addressMap[iCC].nameYcoord =
  713.             (addressMap[iCC].yCoordBase + addressMap[iCC].yCoordTop)/2.0;
  714.  
  715.         /* handle overlaps */
  716.         if (addressMap[iCC].nameYcoord < yLastAddress + LABEL_HT)
  717.             addressMap[iCC].nameYcoord = yLastAddress + LABEL_HT;
  718.  
  719.         yLastAddress = addressMap[iCC].nameYcoord;
  720.         }
  721.     }
  722.  
  723.     if (yLastAddress + LABEL_HT > NX_HEIGHT(&bounds))
  724.     {
  725.         yLastAddress = NX_HEIGHT(&bounds) - (LABEL_HT + 2.0);
  726.  
  727.         /* try to reshuffle address labels so everything fits */
  728.         for (iCC = 0; iCC < iSegments; ++iCC)
  729.         {
  730.             if (
  731. (addressMap[iCC].endAddress <= hiAddr && addressMap[iCC].baseAddress >= loAddr)
  732. ||(addressMap[iCC].endAddress >= loAddr && addressMap[iCC].baseAddress < loAddr)
  733. ||(addressMap[iCC].endAddress > hiAddr && addressMap[iCC].baseAddress < hiAddr)
  734.             )
  735.             {
  736.             if (addressMap[iCC].nameYcoord > yLastAddress)
  737.             {
  738.                 addressMap[iCC].nameYcoord = yLastAddress;
  739.                 yLastAddress -= (LABEL_HT + 2.0);
  740.             } else
  741.                 break;  /* slipped down into a gap */
  742.             }
  743.         }
  744.     }
  745.  
  746.     return self;
  747. }
  748.  
  749. //M+    -free
  750. //M-
  751. - free
  752. {
  753.     ENTER("- free");
  754.     [self freeInternals];
  755.  
  756.     [zoomStack free];
  757.  
  758.     [super free];
  759.  
  760.     EXIT("- free");
  761.     return self;
  762. }
  763.  
  764. //M+ - freeInternals
  765. //M-
  766. - freeInternals
  767. {
  768.     ENTER("- freeInternals");
  769.     if (NULL != addressMap)
  770.     {
  771.         int iSegments = [theFile mappedSegments];
  772.         while (--iSegments > -1)
  773.             if (addressMap[iSegments].sectionMap)
  774.                 free(addressMap[iSegments].sectionMap);
  775.  
  776.         free(addressMap);
  777.         addressMap = NULL;
  778.     }
  779.  
  780.     if (NULL != currentFileName)
  781.     {
  782.         free(currentFileName);
  783.         currentFileName = NULL;
  784.     }
  785.  
  786.     if (NULL != theFile)
  787.     {
  788.         [theFile free];
  789.         theFile = NULL;
  790.     }
  791.  
  792.     if (NULL != mappedFile)
  793.     {
  794.         [mappedFile free];
  795.         mappedFile = NULL;
  796.     }
  797.  
  798.     if (NULL != zoomStack)
  799.     {    // free zoomStack's contents
  800.         void *vp;
  801.  
  802.         while ((vp = [zoomStack pop]) != NULL)
  803.             free(vp);
  804.     }
  805.  
  806.     EXIT("- freeInternals");
  807.     return self;
  808. }
  809.  
  810. //M+    -show:sender
  811. //M-
  812. - show:sender
  813. {
  814.     [theWindow makeKeyAndOrderFront:self];
  815.  
  816.     return self;
  817. }
  818.  
  819. //M+    -drawAddresses
  820. //M-
  821. - drawAddresses
  822. {
  823.     float yCoord, xCoord;
  824.     int   iCC, iSegments;
  825.  
  826.     iSegments = [theFile mappedSegments];
  827.  
  828.     /* figure out address label spacing */
  829.     [self spaceLabels];
  830.  
  831.     PSnewpath();
  832.     PSsetlinewidth(0.50);  /* thin lines to each segment */
  833.     [theFont set];
  834.  
  835.     yCoord = VERT_MARGIN;
  836.     xCoord = NX_WIDTH(&bounds)/2.0 - ((float)iSegments/2.0)*(LINE_STAGGER);
  837.  
  838.     for (iCC = iSegments - 1; iCC > -1; --iCC)
  839.     {
  840.         char bvBuffer[256];
  841.  
  842.         if (addressMap[iCC].baseAddress > hiAddr)
  843.             break;  // no need to go any higher
  844.  
  845.         if (
  846. (addressMap[iCC].endAddress <= hiAddr && addressMap[iCC].baseAddress >= loAddr)
  847. ||(addressMap[iCC].endAddress >= loAddr && addressMap[iCC].baseAddress < loAddr)
  848. ||(addressMap[iCC].endAddress > hiAddr && addressMap[iCC].baseAddress < hiAddr)
  849.         )
  850.         {
  851.  
  852.             if (!(addressMap[iCC].yCoordBaseLabel < 0.0))
  853.             {    sprintf(bvBuffer, "0x%x",
  854.                     (unsigned int)addressMap[iCC].baseAddress);
  855.                 drawLabel(LABEL_MARGIN, addressMap[iCC].yCoordBaseLabel,
  856.                     bvBuffer, xCoord - (LINE_STAGGER - LABEL_MARGIN),
  857.                     addressMap[iCC].yCoordBase);
  858.             }
  859.  
  860.             sprintf(bvBuffer, "0x%x", (unsigned int)addressMap[iCC].endAddress);
  861.             drawLabel(LABEL_MARGIN, addressMap[iCC].yCoordTopLabel, bvBuffer,
  862.                 xCoord - (LINE_STAGGER - LABEL_MARGIN),
  863.                 addressMap[iCC].yCoordTop);
  864.  
  865.             xCoord += LINE_STAGGER;
  866.  
  867.         }
  868.     }
  869.  
  870.     PSstroke();
  871.  
  872.     return self;
  873. }
  874.  
  875. //M+    -drawSegmentNames
  876. //M-
  877. - drawSegmentNames
  878. {
  879.     float xCoord;
  880.     int iCC, iSegments;
  881.  
  882.     if (FALSE == showAddresses)
  883.         [self spaceLabels];
  884.  
  885.     [self spaceNames];
  886.  
  887.     PSnewpath();
  888.     PSsetlinewidth(0.50);
  889.     [theFont set];
  890.  
  891.     iSegments = [theFile mappedSegments];
  892.  
  893.     xCoord = NX_WIDTH(&bounds)/2.0
  894.         - ((float)iSegments/2.0)*(LINE_STAGGER) + 5.0;
  895.  
  896.     for (iCC = iSegments - 1; iCC > -1; --iCC)
  897.     {
  898.         if (addressMap[iCC].baseAddress > hiAddr)
  899.             break;  // no need to go any higher
  900.  
  901.         if (
  902. (addressMap[iCC].endAddress <= hiAddr && addressMap[iCC].baseAddress >= loAddr)
  903. ||(addressMap[iCC].endAddress >= loAddr && addressMap[iCC].baseAddress < loAddr)
  904. ||(addressMap[iCC].endAddress > hiAddr && addressMap[iCC].baseAddress < hiAddr)
  905.         )
  906.         {
  907.  
  908.             xCoord += LINE_STAGGER;
  909.             PSmoveto(
  910.                 xCoord,
  911.                 (addressMap[iCC].yCoordBase + addressMap[iCC].yCoordTop)/2.0);
  912.             PSlineto(
  913.                 addressMap[iCC].nameXcoord - 10.0,
  914.                 addressMap[iCC].nameYcoord);
  915.             PSmoveto(addressMap[iCC].nameXcoord, addressMap[iCC].nameYcoord);
  916.             PSshow(addressMap[iCC].segmentName);
  917.  
  918.         }
  919.     }
  920.  
  921.     PSstroke();
  922.  
  923.     return self;
  924. }
  925.  
  926. //M+    -spaceSectionNames
  927. //M-
  928. - spaceSectionNames
  929. {
  930.     int   iCC, iSegments;
  931.     float yLastAddress;
  932.  
  933.     iSegments = [theFile mappedSegments];
  934.  
  935.     yLastAddress = addressMap[iSegments - 1].yCoordBase - LABEL_HT;
  936.  
  937.     for (iCC = iSegments - 1; iCC > -1; --iCC)
  938.     {
  939.         if (addressMap[iCC].baseAddress > hiAddr)
  940.             break;  // no need to go any higher
  941.  
  942.         if (
  943. (addressMap[iCC].endAddress <= hiAddr && addressMap[iCC].baseAddress >= loAddr)
  944. ||(addressMap[iCC].endAddress >= loAddr && addressMap[iCC].baseAddress < loAddr)
  945. ||(addressMap[iCC].endAddress > hiAddr && addressMap[iCC].baseAddress < hiAddr)
  946.         )
  947.         {
  948.             SegmentCommand *oSegment = [theFile mappedSegment:iCC];
  949.             int             iSection = [oSegment numberOfSections];
  950.  
  951.             if (iSection > 0 && addressMap[iCC].sectionMap != NULL)
  952.             {
  953.                 int   i;
  954.  
  955.                 for (i = 0; i < iSection; ++i)
  956.                 {
  957.                     if(
  958.                         (addressMap[iCC].sectionMap[i].sectionAddress <= hiAddr)
  959.                     &&    (addressMap[iCC].sectionMap[i].sectionAddress >= loAddr)
  960.                     )
  961.                     {
  962.  
  963.                         if (toScale)
  964.                         {
  965.                             addressMap[iCC].sectionMap[i].nameYcoord
  966.         = scaleSlope*(float)addressMap[iCC].sectionMap[i].sectionAddress
  967.             + scaleYintercept;
  968.                         } else {
  969.                             addressMap[iCC].sectionMap[i].nameYcoord
  970.         = smashedSlope*(float)addressMap[iCC].sectionMap[i].sectionAddress
  971.             + smashedYintercept;
  972.                         }
  973.  
  974.                         if (addressMap[iCC].sectionMap[i].nameYcoord <= yLastAddress+LABEL_HT)
  975.             addressMap[iCC].sectionMap[i].nameYcoord = yLastAddress + LABEL_HT;
  976.  
  977.                         yLastAddress = addressMap[iCC].sectionMap[i].nameYcoord;
  978.  
  979.                     }
  980.  
  981.                 }
  982.             }
  983.         }
  984.     }
  985.  
  986.     return self;
  987. }
  988.  
  989. //M+    -drawSectionNames
  990. //M-
  991. - drawSectionNames
  992. {
  993.     float xCoord;
  994.     int iCC, iSegments;
  995.  
  996.     [self spaceSectionNames];
  997.  
  998.     PSnewpath();
  999.     PSsetlinewidth(0.50);
  1000.     [theFont set];
  1001.  
  1002.     iSegments = [theFile mappedSegments];
  1003.  
  1004.     xCoord = NX_WIDTH(&bounds)/2.0
  1005.         - ((float)iSegments/2.0)*(LINE_STAGGER) + 5.0;
  1006.  
  1007.     for (iCC = iSegments - 1; iCC > -1; --iCC)
  1008.     {
  1009.         if (addressMap[iCC].baseAddress > hiAddr)
  1010.             break;  // no need to go any higher
  1011.  
  1012.         if (
  1013. (addressMap[iCC].endAddress <= hiAddr && addressMap[iCC].baseAddress >= loAddr)
  1014. ||(addressMap[iCC].endAddress >= loAddr && addressMap[iCC].baseAddress < loAddr)
  1015. ||(addressMap[iCC].endAddress > hiAddr && addressMap[iCC].baseAddress < hiAddr)
  1016.         )
  1017.         {
  1018.             SegmentCommand *oSegment = [theFile mappedSegment:iCC];
  1019.             int             iSection = [oSegment numberOfSections];
  1020.  
  1021.             if (iSection > 0 && addressMap[iCC].sectionMap != NULL)
  1022.             {
  1023.                 int             i;
  1024.                 for (i = 0; i < iSection; ++i)
  1025.                 {
  1026.                     if(
  1027.                         (addressMap[iCC].sectionMap[i].sectionAddress <= hiAddr)
  1028.                       &&(addressMap[iCC].sectionMap[i].sectionAddress >= loAddr)
  1029.                     )
  1030.                     {
  1031.                         float yCoord;
  1032.  
  1033.                         if (toScale)
  1034.                         {
  1035.                             yCoord
  1036.         = scaleSlope*(float)addressMap[iCC].sectionMap[i].sectionAddress
  1037.             + scaleYintercept;
  1038.                         } else {
  1039.                             yCoord
  1040.         = smashedSlope*(float)addressMap[iCC].sectionMap[i].sectionAddress
  1041.             + smashedYintercept;
  1042.                         }
  1043.                         PSmoveto(xCoord, yCoord);
  1044.                         PSlineto(xCoord + 10.0,
  1045.                             addressMap[iCC].sectionMap[i].nameYcoord);
  1046.                         PSmoveto(xCoord + 14.0,
  1047.                             addressMap[iCC].sectionMap[i].nameYcoord);
  1048.                         PSshow(addressMap[iCC].sectionMap[i].sectionName);
  1049.                     }
  1050.                 }
  1051.             }
  1052.  
  1053.             xCoord += LINE_STAGGER;
  1054.         }
  1055.     }
  1056.  
  1057.     PSstroke();
  1058.  
  1059.     return self;
  1060. }
  1061.  
  1062. //M+ - mouseDown:(NXEvent *)spTheEvent
  1063. //    PURPOSE:
  1064. //        draw a box following the user's mouse drags
  1065. //M-
  1066. - mouseDown:(NXEvent *)spTheEvent
  1067. {
  1068.     NXEvent *spNextEvent;
  1069.     int      iOldMask, iCheckMask, iDragged = 0;
  1070.     char     bvBuf[64];
  1071.  
  1072.     [self lockFocus];
  1073.  
  1074.     iOldMask = [window eventMask];
  1075.  
  1076.     iCheckMask = NX_MOUSEUPMASK | NX_MOUSEDRAGGEDMASK;
  1077.  
  1078.     [window setEventMask:(iOldMask|iCheckMask)];
  1079.  
  1080.     sFirstMouseDown = spTheEvent->location;
  1081.     [self convertPoint:&sFirstMouseDown fromView:nil];
  1082.  
  1083.     yLooping = 1;
  1084.     while (yLooping)
  1085.     {
  1086.         iDragged = 1;
  1087.         spNextEvent = [NXApp getNextEvent:iCheckMask];
  1088.  
  1089.         if ((yLooping = (spNextEvent->type != NX_MOUSEUP)))
  1090.         {
  1091.             sMouseDown = spTheEvent->location;
  1092.             [self convertPoint:&sMouseDown fromView:nil];
  1093.             [self display];
  1094.         }
  1095.     }
  1096.  
  1097.     [self unlockFocus];
  1098.  
  1099.     [window setEventMask:iOldMask];
  1100.  
  1101.     if (!iDragged)
  1102.         return self;
  1103.  
  1104.     // For sake of aesthetics, address 0x0 is not exactly at bottom of
  1105.     // view.  This lets the user drag an address of less than zero,
  1106.     // which ends up as a very high address in an unsigned long.
  1107.     // The next bits use the view coords to keep user from screwing up.
  1108.     if (sFirstMouseDown.y < VERT_MARGIN)
  1109.         sFirstMouseDown.y = VERT_MARGIN;
  1110.     if (sMouseDown.y < VERT_MARGIN)
  1111.         sMouseDown.y = VERT_MARGIN;
  1112.  
  1113.     // set displayed coords for user's inspection
  1114.     if (toScale)
  1115.     {
  1116.         hiAddrZoom =
  1117.         (unsigned long)((sFirstMouseDown.y - scaleYintercept)/scaleSlope);
  1118.         loAddrZoom =
  1119.         (unsigned long)((sMouseDown.y - scaleYintercept)/scaleSlope);
  1120.  
  1121.     } else {
  1122.         int iCC;
  1123.         int iSegments = [theFile mappedSegments];
  1124.         int iLoSet = 0, iHiSet = 0;
  1125.         // find out where the addresses dragged out are
  1126.  
  1127.         for (iCC = iSegments - 1;
  1128.             iCC > -1 && (!iLoSet || !iHiSet);
  1129.             --iCC)
  1130.         {
  1131.             if (!iLoSet && addressMap[iCC].yCoordBase <= sMouseDown.y
  1132.                 && addressMap[iCC].yCoordTop >= sMouseDown.y)
  1133.             {
  1134.                 loAddrZoom =
  1135.     (unsigned long)((sMouseDown.y - addressMap[iCC].yCoordBase)
  1136.     /(addressMap[iCC].yCoordTop - addressMap[iCC].yCoordBase)
  1137.     *(float)(addressMap[iCC].endAddress - addressMap[iCC].baseAddress))
  1138.     + addressMap[iCC].baseAddress;
  1139.                 iLoSet = 1;
  1140.  
  1141.             }
  1142.  
  1143.             if (!iHiSet && addressMap[iCC].yCoordBase <= sFirstMouseDown.y
  1144.                 && addressMap[iCC].yCoordTop >= sFirstMouseDown.y)
  1145.             {
  1146.                 hiAddrZoom =
  1147.     (unsigned long)((sFirstMouseDown.y - addressMap[iCC].yCoordBase)
  1148.     /(addressMap[iCC].yCoordTop - addressMap[iCC].yCoordBase)
  1149.     *(float)(addressMap[iCC].endAddress - addressMap[iCC].baseAddress))
  1150.     + addressMap[iCC].baseAddress;
  1151.                 iHiSet = 1;
  1152.             }
  1153.         }
  1154.  
  1155.         if (!iLoSet)
  1156.         {
  1157.             // sMouseDown.y not in range of any segment
  1158.             if (sMouseDown.y > addressMap[0].yCoordTop)
  1159.                 loAddrZoom = hiAddr;
  1160.             else
  1161.                 loAddrZoom = loAddr;
  1162.         }
  1163.  
  1164.         if (!iHiSet)
  1165.         {
  1166.             // sFirstMouseDown.y not in range of any segment
  1167.             if (sFirstMouseDown.y > addressMap[0].yCoordTop)
  1168.                 hiAddrZoom = hiAddr;
  1169.             else
  1170.                 hiAddrZoom = loAddr;
  1171.         }
  1172.         
  1173.     }
  1174.  
  1175.     // user may have dragged from bottom to top.
  1176.     if (hiAddrZoom < loAddrZoom)
  1177.     {    unsigned long ulTmp = hiAddrZoom; hiAddrZoom = loAddrZoom;
  1178.         loAddrZoom = ulTmp; }
  1179.  
  1180.     // clamp zoomed values no matter what they calculated out as
  1181.     if (hiAddrZoom > hiAddr)
  1182.         hiAddrZoom = hiAddr;
  1183.  
  1184.     if (loAddrZoom < loAddr)
  1185.         loAddrZoom = loAddr;
  1186.  
  1187.     sprintf(bvBuf, "0x%x", loAddrZoom);
  1188.     [minAddress setStringValue:bvBuf];
  1189.     sprintf(bvBuf, "0x%x", hiAddrZoom);
  1190.     [maxAddress setStringValue:bvBuf];
  1191.  
  1192.     [zoomButton setEnabled:YES];
  1193.  
  1194.     return self;
  1195. }
  1196.  
  1197. //M+ - zoom:sender
  1198. //    PURPOSE:
  1199. //        Do what needs to be done when user clicks on "zoom" button.
  1200. //        Essentially, it pushes the current state (high address displayed,
  1201. //        low address displayed, and amount of mapped address taked up)
  1202. //        on the zoom stack, recalcs the amount of address taken up
  1203. //        inside the new high and low addresses, then redisplays.
  1204. //M-
  1205. - zoom:sender
  1206. {
  1207.     struct zoomAddress *spZoom;
  1208.     int                 iCC;
  1209.  
  1210.     // save current "zoom state".
  1211.     spZoom = malloc(sizeof(struct zoomAddress));
  1212.     assert(spZoom != NULL);
  1213.     spZoom->hiAddr = hiAddr;
  1214.     spZoom->loAddr = loAddr;
  1215.     spZoom->sumAddr = sumAddr;
  1216.     [zoomStack push:(void *)spZoom];
  1217.  
  1218.     hiAddr = hiAddrZoom;
  1219.     loAddr = loAddrZoom;
  1220.  
  1221.     // recalculate the amount of the address used by file's mapped segments
  1222.     sumAddr = 0;
  1223.     for (iCC = [theFile mappedSegments] - 1; iCC > -1; --iCC)
  1224.     {
  1225.         id            oSegment = [theFile mappedSegment:iCC];
  1226.         unsigned long ulBaseAddr, ulUpperAddr;
  1227.  
  1228.         ulBaseAddr  = [oSegment getBaseAddress];
  1229.         ulUpperAddr = [oSegment getUpperAddress];
  1230.  
  1231.         if (ulBaseAddr > hiAddr)
  1232.             break;
  1233.  
  1234.         if (
  1235.           (ulUpperAddr <= hiAddr && ulBaseAddr >= loAddr)
  1236.         ||(ulUpperAddr >= loAddr && ulBaseAddr <  loAddr)
  1237.         ||(ulUpperAddr >  hiAddr && ulBaseAddr <  hiAddr)
  1238.         )
  1239.         {
  1240.                 sumAddr +=
  1241.                     (ulUpperAddr <= hiAddr ? ulUpperAddr : hiAddr)
  1242.                     -
  1243.                     (ulBaseAddr >= loAddr ? ulBaseAddr : loAddr);
  1244.         }
  1245.     }
  1246.  
  1247.     [unzoomButton setEnabled:YES];
  1248.  
  1249.     [self display];
  1250.  
  1251.     return self;
  1252. }
  1253.  
  1254. //M+ - unzoom:sender
  1255. //    PURPOSE:
  1256. //        "unzoom" a level.
  1257. //M-
  1258. - unzoom:sender
  1259. {
  1260.     char bvBuf[64];
  1261.     struct zoomAddress *spZoom;
  1262.  
  1263.     spZoom = (struct zoomAddress *)[zoomStack pop];
  1264.  
  1265.     loAddr = spZoom->loAddr;
  1266.     hiAddr = spZoom->hiAddr;
  1267.     sumAddr = spZoom->sumAddr;
  1268.     free(spZoom);
  1269.  
  1270.     sprintf(bvBuf, "0x%x", loAddr);
  1271.     [minAddress setStringValue:bvBuf];
  1272.     sprintf(bvBuf, "0x%x", hiAddr);
  1273.     [maxAddress setStringValue:bvBuf];
  1274.  
  1275.     if ([zoomStack isEmpty])
  1276.         [unzoomButton setEnabled:NO];
  1277.  
  1278.     [self display];
  1279.  
  1280.     return self;
  1281. }
  1282.  
  1283. @end
  1284.