home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Mac Game Programming Gurus / TricksOfTheMacGameProgrammingGurus.iso / Information / CSMP Digest / volume 1 / csmp-v1-218.txt < prev    next >
Encoding:
Text File  |  1994-12-08  |  57.7 KB  |  1,670 lines  |  [TEXT/R*ch]

  1. C.S.M.P. Digest             Tue, 24 Nov 92       Volume 1 : Issue 218
  2.  
  3. Today's Topics:
  4.  
  5.     Best way to get A5 into VBLTaskProc?
  6.     How to flip images?
  7.     Genetic Algorithems
  8.     Scratch area low-memory globals
  9.  
  10.  
  11.  
  12. The Comp.Sys.Mac.Programmer Digest is moderated by Michael A. Kelly.
  13.  
  14. The digest is a collection of article threads from the internet newsgroup
  15. comp.sys.mac.programmer.  It is designed for people who read c.s.m.p. semi-
  16. regularly and want an archive of the discussions.  If you don't know what a
  17. newsgroup is, you probably don't have access to it.  Ask your systems
  18. administrator(s) for details.  You can post articles to any newsgroup by
  19. mailing your article to newsgroup@ucbvax.berkeley.edu.  So, to post an
  20. article to comp.sys.mac.programmer, you mail it to
  21. comp-sys-mac-programmer@ucbvax.berkeley.edu.  Note the '-' instead of '.'
  22. in the newsgroup name.
  23.  
  24. Each issue of the digest contains one or more sets of articles (called
  25. threads), with each set corresponding to a 'discussion' of a particular
  26. subject.  The articles are not edited; all articles included in this digest
  27. are in their original posted form (as received by our news server at
  28. cs.uoregon.edu).  Article threads are not added to the digest until the last
  29. article added to the thread is at least one month old (this is to ensure that
  30. the thread is dead before adding it to the digest).  Article threads that
  31. consist of only one message are generally not included in the digest.
  32.  
  33. The entire digest is available for anonymous ftp from ftp.cs.uoregon.edu
  34. [128.223.8.8] in the directory /pub/mac/csmp-digest.  Be sure to read the
  35. file /pub/mac/csmp-digest/README before downloading any files.  The most
  36. recent issues are available from sumex-aim.stanford.edu [36.44.0.6] in the
  37. directory /info-mac/digest/csmp.  If you don't have ftp capability, the sumex
  38. archive has a mail server; send a message with the text '$MACarch help' (no
  39. quotes) to LISTSERV@ricevm1.rice.edu for more information.
  40.  
  41. The digest is also available via email.  Just send a note saying that you
  42. want to be on the digest mailing list to mkelly@cs.uoregon.edu, and you will
  43. automatically receive each new issue as it is created.  Sorry, back issues
  44. are not available through the mailing list.
  45.  
  46. Send administrative mail to mkelly@cs.uoregon.edu.
  47.  
  48.  
  49. -------------------------------------------------------
  50.  
  51. From: sherman@michael.nmr.upmc.edu (Sherman Uitzetter)
  52. Subject: Best way to get A5 into VBLTaskProc?
  53. Date: 14 Oct 92 15:58:34 GMT
  54. Organization: Pittsburgh NMRI
  55.  
  56.  
  57.  
  58.     Hi, I want to synchronize some animation to the
  59. vertical blanking of the main monitor (to avoid tearing
  60. and flickering).  I figure I create a VBLTask and use
  61. SlotVInstall so I know when a vertical blank occurs.
  62. The thing is, I need the VBLTaskProc to update some
  63. variable that my program can poll to wait for a vertical
  64. blank.  Ok, I declare a global in my application,
  65. gVBLCount, but I need a way for the VBLTaskProc to get
  66. at this global to increment it.  "Oh this is easy", I
  67. say, before I realize there's no convenient place to put
  68. A5 (as returned by CurrentA5()) so that the VBLTaskProc
  69. can get at my applications globals.  What I end up doing
  70. works, but is hideous (see code below).  I know there
  71. are game-writers out there who have done this. Here's
  72. your chance to spread your knowledge - how did you do
  73. it?
  74.  
  75.  
  76. Thanks in advance,
  77. - -Sherman.
  78.  
  79. P.S. what is the "phase" field in the VBLTask record?
  80.  
  81.  
  82. The code I was talking about above (THINK C 5.0.3) :
  83.  
  84. #include <Retrace.h>
  85.  
  86. /* so that the way this code compiles won't change if the project's
  87. options are changed: */
  88. #pragma    options(!profile, force_frame, !global_optimizer, !defer_adjust,
  89. !redundant_loads, !assign_registers)
  90.  
  91. long        gVBLCount = 0L;    /* the variable that is incremented by theVBLProc()
  92. */
  93. VBLTask        gTask;
  94.  
  95. /* here is the VBLTaskProc (called during vertical blanking interrupt) */
  96. pascal void theVBLProc(void)
  97. {
  98.     unsigned long    oldA5;
  99.  
  100.     /* set the A5 world to this application's A5 world */
  101.     oldA5 = SetA5(0x12345678);    /* the 0x12345678 is what needs to be
  102. modified by InitVBLTask() */
  103.  
  104.     gVBLCount++;
  105.  
  106.     /* restore the A5 world */
  107.     SetA5(oldA5);
  108. }
  109.  
  110. /* initializes the VBLTask record and modifies theVBLProc */
  111. void InitVBLTask(void)
  112. {
  113.     unsigned long    *procPtr;    /* used to modify theVBLProc() */
  114.  
  115.     gTask.qType = vType;
  116.     gTask.vblAddr = theVBLProc;
  117.     gTask.vblCount = 1;    /* trigger right away */
  118.     gTask.vblPhase = 0;    /* phase??? */
  119.  
  120.     /* this is awful, but I have to modify code so that I can use globals in
  121. theVBLProc() */
  122.     procPtr = (unsigned long *) ((unsigned long) gTask.vblAddr + 2);
  123.     procPtr = (unsigned long *) ((unsigned long) *procPtr + 6);    /* the + 6
  124. is very compiler dependent */
  125.     *procPtr = SetCurrentA5();    /* theVBLProc() is now correctly modified (I
  126. hope, I hope :-} ) */
  127. }
  128.  
  129. /* wastes time until a vertical blank occurs */ 
  130. void SpinUntilVBL(void)
  131. {
  132.     gVBLCount = 0;
  133.     gTask.vblCount = 1;    /* trigger right away */
  134.     if (SlotVInstall((QElemPtr) &gTask, 0) != noErr)
  135.         return;
  136.  
  137.     while (gVBLCount == 0)
  138.         ;
  139. }
  140.  
  141. +++++++++++++++++++++++++++
  142.  
  143. From: k044477@hobbes.kzoo.edu (Jamie R. McCarthy)
  144. Organization: Kalamazoo College
  145. Date: Wed, 14 Oct 1992 17:07:45 GMT
  146.  
  147. sherman@michael.nmr.upmc.edu (Sherman Uitzetter) writes:
  148. >
  149. >Ok, I declare a global in my application,
  150. >gVBLCount, but I need a way for the VBLTaskProc to get
  151. >at this global to increment it.  "Oh this is easy", I
  152. >say, before I realize there's no convenient place to put
  153. >A5 (as returned by CurrentA5()) so that the VBLTaskProc
  154. >can get at my applications globals.
  155.  
  156. You can stick it right next to the VBLTask struct itself (see below).
  157.  
  158. > What I end up doing
  159. >works, but is hideous
  160. > [self-modifying code deleted]
  161.  
  162. Ow!  You're not kidding!  If you _do_ do something like this, remember
  163. to flush the instruction cache.  But, fortunately, there's an easier
  164. way.
  165.  
  166. Just write something like:
  167.  
  168.  
  169. long gVBLCount = 0L;
  170.    /***********
  171.     * Keep these two declarations together!  The code depends on it!
  172.     */
  173. long gMyA5;
  174. VBLTask gTask;
  175.    /*
  176.     ***********/
  177.  
  178.  
  179.    /* See Tech Notes 180 and 208. */
  180.  
  181. long getVBLRec(void) = 0x2008; // move.l a0,d0
  182.  
  183. pascal void theVBLProc(void)
  184. {
  185.    long myVBLRecAddr;
  186.    unsigned long oldA5;
  187.    
  188.    myVBLRecAddr = getVBLRec();
  189.    oldA5 = SetA5( * ( (long*) myVBLRecAddr - 1 ) );
  190.    
  191.    // Do your ++gVBLCount, or whatever, here.  Globals are valid.
  192.    
  193.    SetA5(oldA5);
  194.  
  195. }
  196.  
  197. And, in your InitVBLTask procedure, be sure to assign your app's A5 to
  198. the global:
  199.  
  200. gMyA5 = SetCurrentA5();
  201. - -- 
  202.  Jamie McCarthy      Internet: k044477@kzoo.edu      AppleLink: j.mccarthy
  203.  "I don't think we should have to have them wandering the streets
  204.   frightening women and people."                            - Pat Buchanan
  205.  
  206. +++++++++++++++++++++++++++
  207.  
  208. From: time@ice.com (Tim Endres)
  209. Date: Wed, 14 Oct 92 15:46:29 EST
  210. Organization: ICE Engineering, Inc.
  211.  
  212.  
  213. In article <16947@pitt.UUCP> (comp.sys.mac.programmer), sherman@michael.nmr.upmc.edu (Sherman Uitzetter) writes:
  214. > blank.  Ok, I declare a global in my application,
  215. > gVBLCount, but I need a way for the VBLTaskProc to get
  216. > at this global to increment it.  "Oh this is easy", I
  217.  
  218. One way is to extend your VBLTask structure:
  219.  
  220. typedef struct {
  221.     VBLTask        gTask;
  222.     long        A5;
  223.     } MyVBLTask;
  224.  
  225. And then you can set this field in your app, and send a pointer to
  226. the extended struct into the VBL manager to install. Now, you get
  227. the extended structure in your VBL task. If you are paranoid about
  228. Apple later extending the VBLTask structure themselves, you might
  229. try:
  230.  
  231. typedef struct {
  232.     VBLTask        gTask;
  233.     char        paranoia[32];
  234.     long        A5;
  235.     } MyVBLTask;
  236.  
  237. or similar approaches to pad out your value...
  238.  
  239. tim.
  240.  
  241.  
  242. tim endres - time@ice.com  -or- tbomb!time
  243. ICE Engineering, Inc - 8840 Main St, Whitmore Lake, MI 48189
  244. Phone (313) 449 8288 - FAX (313) 449-9208
  245. USENET - a slow moving self parody... ph
  246.  
  247. +++++++++++++++++++++++++++
  248.  
  249. From: absurd@applelink.apple.com (Tim Dierks, software saboteur)
  250. Date: Wed, 14 Oct 1992 21:21:38 GMT
  251. Organization: MacDTS Marauders
  252.  
  253. In article <1CE00001.g3sal8@tbomb.ice.com>, time@ice.com (Tim Endres)
  254. wrote:
  255. > In article <16947@pitt.UUCP> (comp.sys.mac.programmer), sherman@michael.nmr.upmc.edu (Sherman Uitzetter) writes:
  256. > > blank.  Ok, I declare a global in my application,
  257. > > gVBLCount, but I need a way for the VBLTaskProc to get
  258. > > at this global to increment it.  "Oh this is easy", I
  259. > One way is to extend your VBLTask structure:
  260. > typedef struct {
  261. >     VBLTask        gTask;
  262. >     long        A5;
  263. >     } MyVBLTask;
  264. > And then you can set this field in your app, and send a pointer to
  265. > the extended struct into the VBL manager to install. Now, you get
  266. > the extended structure in your VBL task. If you are paranoid about
  267. > Apple later extending the VBLTask structure themselves, you might
  268. > try:
  269. > typedef struct {
  270. >     VBLTask        gTask;
  271. >     char        paranoia[32];
  272. >     long        A5;
  273. >     } MyVBLTask;
  274. > or similar approaches to pad out your value...
  275. > tim.
  276.  
  277. Wow!  You _are_ paranoid!  Allow me to say that there is no way Apple
  278. can make the VBLTask structure larger in this way; if we were to do
  279. so, we would break every single application that uses a VBLTask; they
  280. all allocate a standard amount of room, whether it is in a heap block,
  281. global data space, or on the stack; if Apple we suddenly to start writing
  282. to space after the end of this agreed-upon size, data would be trashed
  283. in virtually every application that uses a VBLTask variable.  Since
  284. we're unlikely to invalidate our entire application base just to avoid
  285. having to use something like an ExtendedVBLTask structure, I would say
  286. your extra 32 bytes of storage are entirely unneeded.  Other than that,
  287. the concept of storing values after a VBLTask so they can be used by
  288. the VBL is just great, and is an Officially Sanctioned Apple Solution (TM).
  289. In fact, if you didn't want to screw around with A5 and all that other
  290. garbage, you could just set up your structure like this:
  291.  
  292. typedef struct {
  293.     VBLTask     gTask;
  294.     long        gVBLCount;
  295. } MyVBLTask;
  296.  
  297. and mess with the variable directly; this way, you don't use globals,
  298. so you don't have to sweat the whole A5 thing.
  299.  
  300. Tim Dierks
  301. MacDTS, but why not?
  302.  
  303. +++++++++++++++++++++++++++
  304.  
  305. From: mhall@abc.comp.sys.mac.programmer (Matthew Hall)
  306. Date: 15 Oct 92 01:13:13 GMT
  307. Organization: Oberlin College Computer Science
  308.  
  309. Hello-
  310.     I asked this same question recently and got the same answer.
  311. However, that the address of the task is stored in A0 is not
  312. documented in any IM that I can find.  If it isn't documented, isn't
  313. it about as safe as self modifying code? (I know for revised and
  314. x-time tasks it is stored in reg A1, but it explicitly says in IMVI
  315. that for slot tasks you may need to use self modifying code)
  316.  
  317. Confused about what Apple is doing,
  318. - -matt hall
  319.  
  320. - --
  321. - -------------------------------------------------------------------------------
  322. Matt Hall.    mhall@occs.oberlin.edu  OR  SMH9666@OBERLIN.BITNET
  323.               (216)-775-6613 (That's a Cleveland Area code. Lucky Me)
  324.  
  325. "Life's good, but not fair at all"  -Lou Reed
  326.  
  327. +++++++++++++++++++++++++++
  328.  
  329. From: absurd@applelink.apple.com (Tim Dierks, software saboteur)
  330. Date: 15 Oct 92 01:55:43 GMT
  331. Organization: MacDTS Marauders
  332.  
  333. In article <MHALL.92Oct14201313@abc.comp.sys.mac.programmer>,
  334. mhall@abc.comp.sys.mac.programmer (Matthew Hall) wrote:
  335. > Hello-
  336. >     I asked this same question recently and got the same answer.
  337. > However, that the address of the task is stored in A0 is not
  338. > documented in any IM that I can find.  If it isn't documented, isn't
  339. > it about as safe as self modifying code? (I know for revised and
  340. > x-time tasks it is stored in reg A1, but it explicitly says in IMVI
  341. > that for slot tasks you may need to use self modifying code)
  342.  
  343. You're right- I can't find any place where this is documented.
  344. Nonetheless, it's far more compatible than self-modifying code
  345. if only because of the "safety in numbers" principle; Apple would
  346. just as soon not break a large number of applications when releasing
  347. a system release, if only because we always seem to take all the
  348. blame, so you're safer if you're doing something a lot of people
  349. are doing.
  350.  
  351. If you don't like it anyway, here's another way which isn't as
  352. preferable (to me) as the VBLTask method, but which is much better
  353. than your self-modifying code: if you're using MPW, put this in
  354. an assembly file:
  355.  
  356.     CASE ON
  357.  
  358. Stash   PROC EXPORT  ; Returns a pointer to the globals storage
  359.     LEA     Store,A0 ; Load the address of the storage
  360.     MOVE.L  A0,D0    ;  into D0
  361.     RTS
  362.  
  363. Store
  364.     DC.L    0        ; Where the globals pointer is stored
  365.  
  366.     ENDPROC
  367.  
  368. and add the following declaration to your C file:
  369.  
  370. extern long *Stash(void);
  371.  
  372. (If you're using Think, you'll need to translate this to inline assembly.)
  373.  
  374. Now, if you want to store a variable for later retrieval (your value of
  375. A5, a counter, or the price of lettuce in Zanzibar), you just do the
  376. following:
  377.  
  378.     *(Stash()) = myImportantValue;
  379.  
  380. When you want to get it back later, such as from your VBL, you can just
  381. do the following:
  382.  
  383.     superImportantNumber = *(Stash());
  384.  
  385. If you wanted to store something else, like a Handle, you would just change
  386. the prototype:
  387.  
  388. extern Handle *Stash(void);
  389.  
  390. This is safer because it doesn't actually modify code; thus, it won't run
  391. afoul of data or instruction caches; however, it does store data in code
  392. space, and therefore it would break if Apple ever decided to write-protect
  393. program code.
  394.  
  395. > Confused about what Apple is doing,
  396. > -matt hall
  397.  
  398. You think *you're* confused about what Apple is doing...
  399.  
  400. Tim Dierks
  401. MacDTS, but I speak for the little men in my head
  402.  
  403. +++++++++++++++++++++++++++
  404.  
  405. From: peirce@outpost.SF-Bay.org (Michael Peirce)
  406. Date: 15 Oct 92 17:20:11 GMT
  407. Organization: Peirce Software
  408.  
  409.  
  410. In article <absurd-141092140055@seuss.apple.com> (comp.sys.mac.programmer), absurd@applelink.apple.com (Tim Dierks, software saboteur) writes:
  411. > Wow!  You _are_ paranoid!  Allow me to say that there is no way Apple
  412. > can make the VBLTask structure larger in this way; if we were to do
  413. > so, we would break every single application that uses a VBLTask; they
  414. > all allocate a standard amount of room, whether it is in a heap block,
  415. > global data space, or on the stack; if Apple we suddenly to start writing
  416. > to space after the end of this agreed-upon size, data would be trashed
  417. > in virtually every application that uses a VBLTask variable.  Since
  418. > we're unlikely to invalidate our entire application base just to avoid
  419. > having to use something like an ExtendedVBLTask structure, I would say
  420. > your extra 32 bytes of storage are entirely unneeded.
  421.  
  422. What I do to advoid the whole subject is to store my value right before
  423. the parameter block.  This way, I can always access it using the same
  424. negative offset no matter which type of parameter block is used (be
  425. it for a time manager task, an ioCompletion, etc).
  426.  
  427. - --  Michael Peirce      --   peirce@outpost.SF-Bay.org
  428. - --  Peirce Software     --   Suite 301, 719 Hibiscus Place
  429. - --                      --   San Jose, California USA 95117
  430. - --  Makers of...        --   voice: (408) 244-6554 fax: (408) 244-6882
  431. - --            SMOOTHIE  --   AppleLink: peirce & America Online: AFC Peirce
  432.  
  433. +++++++++++++++++++++++++++
  434.  
  435. From: k044477@hobbes.kzoo.edu (Jamie R. McCarthy)
  436. Date: 15 Oct 92 21:11:47 GMT
  437. Organization: Kalamazoo College
  438.  
  439. mhall@abc.comp.sys.mac.programmer (Matthew Hall) writes:
  440. >However, that the address of the task is stored in A0 is not
  441. >documented in any IM that I can find.  If it isn't documented, isn't
  442. >it about as safe as self modifying code? (...it explicitly says in IMVI
  443. >that for slot tasks you may need to use self modifying code)
  444.  
  445. It _does_!?  Tsk, tsk, tsk.
  446.  
  447. To quote from a comment in TN 180 ("Multifinder Miscellanea"), p. 5:
  448.  
  449. "GetVBLRec() returns the address of the VBLRec associated with our VBL
  450. task.  This works because on entry into the VBL task, A0 points to the
  451. theVBLTask field in the VBLRec record, which is the first field in the
  452. record and that is the address we return.  Note that this method works
  453. whether the VBLRec is allocated globally, in the heap...or...on the
  454. stack.  ...  This trick allows us to get to the saved A5, but it could
  455. also be used to get to anything we wanted to store in the record."
  456.  
  457. Safety in numbers, as someone else pointed out--if Apple changes this
  458. little feature, hundreds of apps that trusted this Tech Note will die
  459. horrible deaths.  Apple won't want that to happen any more than you
  460. will.
  461.  
  462. (And self-modifying code is never a good idea unless every cycle counts,
  463. and that's not the case here.  I imagine the PowerPC will write-protect
  464. code segments and--who knows?--may compile 680x0 code into its native
  465. language and cache it, in which case self-modifying programs will be
  466. very unhappy.)
  467. - -- 
  468.  Jamie McCarthy      Internet: k044477@kzoo.edu      AppleLink: j.mccarthy
  469.  I suppose ya don't think I was run over by a streetcar!
  470.  
  471. +++++++++++++++++++++++++++
  472.  
  473. From: kpm@well.sf.ca.us (Keith Morgan)
  474. Date: 24 Oct 92 01:00:06 GMT
  475. Organization: Whole Earth 'Lectronic Link
  476.  
  477. In article <16947@pitt.UUCP> sherman@michael.nmr.upmc.edu (Sherman Uitzetter) writes:
  478. >
  479. >
  480. >    Hi, I want to synchronize some animation to the
  481. >vertical blanking of the main monitor (to avoid tearing
  482. >and flickering).  I figure I create a VBLTask and use
  483. >SlotVInstall so I know when a vertical blank occurs.
  484. >The thing is, I need the VBLTaskProc to update some
  485. >variable that my program can poll to wait for a vertical
  486. >blank.  Ok, I declare a global in my application,
  487. >gVBLCount, but I need a way for the VBLTaskProc to get
  488. >at this global to increment it.  "Oh this is easy", I
  489. >say, before I realize there's no convenient place to put
  490. >A5 (as returned by CurrentA5()) so that the VBLTaskProc
  491. >can get at my applications globals.  What I end up doing
  492. >works, but is hideous (see code below).  I know there
  493. >are game-writers out there who have done this. Here's
  494. >your chance to spread your knowledge - how did you do
  495. >it?
  496.  
  497. I use a modified vbltask struct that has two extra fields.
  498. One holds a pointer to a clock variable that is incremented every
  499. vbl period; the other holds the app A5. (Code follows.)  One problem
  500. I have had is that if the appl crashes, the vbl task wants to continue
  501. running, and since the application is dead, the entire machine is
  502. hosed. I have installed a signal handler for errors that I catch
  503. inside the app, but this doesn't work otherwise, making it necessary
  504. to restart. Ideas  anyone?
  505.  
  506. Keith Morgan | kpm@well.sf.ca.us
  507.  
  508. ******** VTask.h:
  509.  
  510. #include <Retrace.h>
  511.  
  512. typedef unsigned long VClock, *VClockPtr;
  513.  
  514. struct VTask;
  515.  
  516. typedef void (*VTaskProcPtr) (struct VTask *theVTaskPtr);
  517.  
  518. typedef struct VTask
  519. {
  520.     VBLTask     vblTask;
  521.     long        applA5;
  522.     VClockPtr    vblClockPtr;
  523. } VTask, *VTaskPtr;
  524.     
  525. void InstallVTask (VClockPtr theClockPtr, short count,
  526.            short theSlot, VTaskPtr theVTaskPtr);
  527.         
  528. void RemoveVTask (VTaskPtr theVTaskPtr, short theSlot);
  529.  
  530. short GetMainVideoSlot (void);
  531.  
  532. extern VClock gMainVideoVBLTicks;
  533.  
  534. void InitializeMainVideoVBLClock (void);
  535.  
  536. void CleanupMainVideoVBLClock (void);
  537.  
  538. void WaitForMainVideoVBL (void);
  539.  
  540. ******** VTask.c
  541.  
  542. #include <stddef.h>
  543. #include "VTask.h"
  544. #include "Start.h"
  545.  
  546. /*
  547. VBL task implementation that allows access to global variables.
  548. */    
  549.  
  550. pascal void RunVTask (void);
  551.  
  552. void InstallVTask (VClockPtr theClockPtr, short count, 
  553.            short theSlot, VTaskPtr theVTaskPtr)
  554. {
  555.     theVTaskPtr->vblTask.qType = vType;
  556.     theVTaskPtr->vblTask.vblAddr = RunVTask;
  557.     theVTaskPtr->vblTask.vblCount = count;
  558.     theVTaskPtr->vblTask.vblPhase = 0; /* do it now */
  559.     theVTaskPtr->applA5 = SetCurrentA5();
  560.     theVTaskPtr->vblClockPtr = theClockPtr;
  561.     SlotVInstall((QElemPtr)theVTaskPtr, theSlot);
  562. }
  563.  
  564. /* 
  565. While not documented in IM, Tech Note180 states that
  566. the current task is in A0 when task exection is begun
  567. */
  568.  
  569. pascal void RunVTask ()
  570. {
  571. asm 
  572.     move.l a5, -(sp)                ; push A5 on stack
  573.     move.l offsetof(VTask,applA5)(a0), a5        ; load applA5 -> A5
  574.     move.l offsetof(VTask,vblClockPtr)(a0), a1    ; load clock pointer -> A1
  575.     addq.l #1, (a1)                    ; increment clock
  576.     move.w #1, offsetof(VBLTask,vblCount)(a0)    ; store vblCount = 1
  577.     move.l (sp)+, A5                ; restore A5
  578. }
  579. }
  580.  
  581. void RemoveVTask (VTaskPtr theVTaskPtr, short theSlot)
  582. {
  583.     SlotVRemove((QElemPtr)theVTaskPtr, theSlot);
  584. }
  585.  
  586.  
  587. /* main video VBL clock */
  588. short gMainVideoSlot;
  589. VClock gMainVideoVBLTicks;
  590. VTask gMainVideoVBLTask;
  591.  
  592. short GetMainVideoSlot ()
  593. {
  594.     DefVideoRec defvidrec;
  595.     GetVideoDefault(&defvidrec);
  596.     return defvidrec.sdSlot;
  597. }
  598.  
  599. void InitializeMainVideoVBLClock ()
  600. {
  601.     gMainVideoSlot = GetMainVideoSlot();
  602.     gMainVideoVBLTicks = 0;
  603.     InstallVTask(&gMainVideoVBLTicks, 1, gMainVideoSlot, &gMainVideoVBLTask);
  604. }
  605.  
  606. void CleanupMainVideoVBLClock ()
  607. {
  608.     RemoveVTask(&gMainVideoVBLTask, gMainVideoSlot);
  609. }
  610.  
  611. void WaitForMainVideoVBL ()
  612. {
  613.     VClock oldMainVideoVBLTicks = gMainVideoVBLTicks;
  614.     while (oldMainVideoVBLTicks == gMainVideoVBLTicks)
  615.         /* do nothing */;
  616. }
  617.  
  618. ---------------------------
  619.  
  620. From: mssmith@afterlife.ncsc.mil (M. Scott Smith)
  621. Subject: How to flip images?
  622. Date: 17 Oct 92 17:32:16 GMT
  623. Organization: The Great Beyond
  624.  
  625. Hello..
  626.  
  627.    I'm still learning how to do animation..  [ugh.]
  628.  
  629.    Here's my latest question:
  630.  
  631.    I've got pictures of a fish swimming to the right that I want to animate.
  632. No problem.  Now, I need pictures of the fish swimming left as well.  As I
  633. was about to load up my paint program to flip these images and save
  634. "fish_swimming_left," it occurred to me this wasn't a good idea, and that
  635. there must be a way to flip an image within QuickDraw.
  636.  
  637.    And so I submersed myself in the IM's but didn't find anything.  (Surprised?)
  638.  
  639.    So, is there an easy way to do this?
  640.  
  641.    Right now I'm drawing the "sprites" in offscreen GWorlds and CopyBitting
  642. them to a window on the screen.
  643.  
  644.    In theory, I know how to write my own "flip" routine, but I'm still so new
  645. to GWorlds and etc. that I wouldn't know how to get down to the actual data.
  646. (I want to keep this as high-level as possible until I begin to understand it
  647. more.)
  648.  
  649.    Any pointers, sample code, etc. would be greatly appreciated.
  650.  
  651. Thanks!
  652.  
  653. M. Scott Smith
  654.   (mssmith@afterlife.ncsc.mil)
  655.  
  656. +++++++++++++++++++++++++++
  657.  
  658. From: d88-jwa@alv.nada.kth.se (Jon Wtte)
  659. Date: 18 Oct 92 14:00:44 GMT
  660. Organization: Royal Institute of Technology, Stockholm, Sweden
  661.  
  662. > mssmith@afterlife.ncsc.mil (M. Scott Smith) writes:
  663.  
  664.    was about to load up my paint program to flip these images and save
  665.    "fish_swimming_left," it occurred to me this wasn't a good idea, and that
  666.    there must be a way to flip an image within QuickDraw.
  667.  
  668.       So, is there an easy way to do this?
  669.  
  670. Yes, draw into an offscreen GWorld. Do a LockPixels on its
  671. PixMap. Do a GetPixBaseAddress. Now, with the Rect of your
  672. fish, and rowBytes, and the size of a pixel, you have all you
  673. need to write a flipping routine; walking from left to middle
  674. and flipping the rightmost/leftmost pixels. Excercise for the
  675. interested reader: what will happen if you walk from left to
  676. extreme right and flip pixels? :-)
  677.  
  678. UnlockPixels afterwards.
  679.  
  680. - -- 
  681.  -- Jon W{tte, h+@nada.kth.se, Mac Hacker Suedoise (not french speaking) --
  682.  
  683.    There's no problem that can't be solved using brute-force algorithms
  684.    and a sufficiently fast computer. Ergo, buy more hardware.
  685.  
  686. +++++++++++++++++++++++++++
  687.  
  688. From: Bruce@hoult.actrix.gen.nz
  689. Date: Mon, 19 Oct 92 19:00:41 +1300 (NZDT)
  690.  
  691. References: <1992Oct17.173216.6223@afterlife.ncsc.mil>
  692. Subject: Re: How to flip images?
  693.  
  694. M. Scott Smith asks:
  695.  
  696. >    I've got pictures of a fish swimming to the right that I want to animate.
  697. > No problem.  Now, I need pictures of the fish swimming left as well.  As I
  698. > was about to load up my paint program to flip these images and save
  699. > "fish_swimming_left," it occurred to me this wasn't a good idea, and that
  700. > there must be a way to flip an image within QuickDraw.
  701.  
  702. >    In theory, I know how to write my own "flip" routine, but I'm still so new
  703. > to GWorlds and etc. that I wouldn't know how to get down to the actual data.
  704. > (I want to keep this as high-level as possible until I begin to understand it
  705. > more.)
  706.  
  707.  
  708. You're right -- there's no way to flip images directly in QuickDraw, but you 
  709. don't
  710. need to get down and dirty yourself either.  Well, not *too* dirty...
  711.  
  712. Here's what comes to mind immediately:
  713.  
  714. Method 1)  use CopyBits to copy a column of pixels at a time: copy the first
  715. column of the source to the last column of the dest, then copy the second
  716. column of the source to the next-to-last column of the dest etc etc.  This
  717. is real easy to write, but will call CopyBits once for every column in the
  718. image.
  719.  
  720. Mathod 2)  use CopyBits and several temporary bitmaps of different shapes:
  721.  
  722. abcdefgh
  723.  
  724. ...becomes...
  725.  
  726. abcd
  727. efgh
  728.  
  729. ...becomes...
  730.  
  731. ab
  732. ef
  733. cd
  734. gh
  735.  
  736. ...becomes...
  737.  
  738. a
  739. e
  740. c
  741. g
  742. b
  743. f
  744. d
  745. h
  746.  
  747. ...becomes...
  748.  
  749. ba
  750. fe
  751. dc
  752. hg
  753.  
  754. ...becomes...
  755.  
  756. dcba
  757. hgfe
  758.  
  759. ...becomes...
  760.  
  761. hgfedcba
  762.  
  763. This will call copybits a number of times proportional to Log2(#columns), but
  764. has the disadvantage of needing very tall intermediate bitmaps that are very
  765. inefficient to store (because bitmaps are always a multiple of 16 wide)
  766.  
  767.  
  768. Method 3) combine methods 1 and 2.  Use 2 until the temp bitmap is 16 or 32 
  769. pixels
  770. wide, then switch to 1, then back to 2.
  771.  
  772.  
  773. Method 4)  use CopyMask to do much the same thing, but without needing the
  774. different shaped intermediate bitmaps.  Creating the mask regions will be a 
  775. pain.
  776. I.e. first use a region like this "XXXXXXXX        " to swap the left and right
  777.  halves, then one like "XXXX    XXXX    " then one like "XX  XX  XX  XX  ", 
  778. then
  779. finally one like "X X X X X X X X ".
  780.  
  781. Mathod 5) there is no method five right now :-)
  782.  
  783.  
  784. - -- Bruce
  785.  
  786. +++++++++++++++++++++++++++
  787.  
  788. From: ivanski@world.std.com (Ivan M CaveroBelaunde)
  789. Date: 21 Oct 92 01:10:22 GMT
  790. Organization: The World Public Access UNIX, Brookline, MA
  791.  
  792. mssmith@afterlife.ncsc.mil (M. Scott Smith) writes:
  793. >   I've got pictures of a fish swimming to the right that I want to animate.
  794. >No problem.  Now, I need pictures of the fish swimming left as well.  As I
  795. >was about to load up my paint program to flip these images and save
  796. >"fish_swimming_left," it occurred to me this wasn't a good idea, and that
  797. >there must be a way to flip an image within QuickDraw.
  798.  
  799. >   So, is there an easy way to do this?
  800.  
  801. Here's some sample code:
  802.  
  803.  
  804. pascal void FixPC()
  805.        = {0x41FA, 0x000A,  /*   LEA        *+$000C,A0                     */
  806.           0x2008,          /*   MOVE.L     A0,D0                          */
  807.           0xA055,          /*   _StripAddress                             */
  808.           0x2040,          /*   MOVEA.L    D0,A0                          */
  809.           0x4ED0};         /*   JMP        (A0) ;jmps to next instruction */
  810.  
  811.  
  812. void HorizontalFlipPixMap (PixMapHandle src)
  813. /*
  814.     Assumes pixmaps are locked, depth is 32 bpp.
  815.     Depth is determined via the type of srcPtr and dstPtr in
  816.     the inner loop (long*: 32bpp, short*:16bpp,char*:8bpp).
  817. */
  818. {
  819.     Rect    theRect;
  820.     short    width,height,hcount;
  821.     register short    rowBytes;
  822.     unsigned char *srcBase, *dstBase;
  823.     char    mmuMode;
  824.     
  825.     mmuMode = GetMMUMode();
  826.     if ((PixMap32Bit(src)) && !mmuMode) {
  827.         mmuMode = true32b;
  828.         FixPC();
  829.         SwapMMUMode (&mmuMode);
  830.     }
  831.     theRect = (**src).bounds;
  832.     height = theRect.bottom - theRect.top;
  833.     width = theRect.right - theRect.left;
  834.     rowBytes = (**src).rowBytes&0x3fff;
  835.     hcount = height;
  836.     srcBase = GetPixBaseAddr(src) +
  837.         (rowBytes * theRect.top)
  838.         + (((**src).pixelSize/8)*theRect.left);
  839.     dstBase = srcBase + (((**src).pixelSize/8) * (width-1));
  840.     while (hcount) {
  841.         register unsigned long *srcPtr, *dstPtr;
  842.         register short wcount;
  843.                 
  844.         srcPtr = (unsigned long*)srcBase;
  845.         dstPtr = (unsigned long*)dstBase;
  846.         wcount = width/2;
  847.         while (wcount) {
  848.             *(dstPtr--) = *(srcPtr++);
  849.             --wcount;
  850.         }
  851.         srcBase += rowBytes;
  852.         dstBase += rowBytes;
  853.         --hcount;
  854.     }
  855.     SwapMMUMode(&mmuMode);
  856. }
  857.  
  858.  
  859. Hope this helps,
  860.  
  861. - -Ivan
  862.  
  863. - ---
  864. Ivan Cavero Belaunde
  865. DiVA Corporation
  866.  
  867. +++++++++++++++++++++++++++
  868.  
  869. From: Roger.W.Brown@dartmouth.edu (Roger W. Brown)
  870. Date: 21 Oct 92 15:52:27 GMT
  871. Organization: Dartmouth College, Hanover, NH
  872.  
  873. In article <1992Oct17.173216.6223@afterlife.ncsc.mil>
  874. mssmith@afterlife.ncsc.mil (M. Scott Smith) writes:
  875.  
  876. >    I've got pictures of a fish swimming to the right that I want to animate.
  877. > No problem.  Now, I need pictures of the fish swimming left as well.  As I
  878. > was about to load up my paint program to flip these images and save
  879. > "fish_swimming_left," it occurred to me this wasn't a good idea, and that
  880. > there must be a way to flip an image within QuickDraw.
  881.  
  882. Just a thought...
  883.  
  884. If you are being a perfectionist and/or the look of the image is really
  885. important in some way, then flipping an image in code won't give you
  886. the right result if your image has shading. The shadows will then
  887. appear on the wrong side. Better to use the paint program and have two
  888. different images.
  889.  
  890. - --------------------------------------------------------------------
  891. Roger Brown                                roger.brown@dartmouth.edu
  892. Software Development
  893. Kiewit Computation Center, Dartmouth College, Hanover, NH
  894.  
  895. +++++++++++++++++++++++++++
  896.  
  897. From: James_Zuchelli@qm.claris.com (James Zuchelli)
  898. Date: 24 Oct 92 00:18:03 GMT
  899. Organization: Claris Corp
  900.  
  901. In article <1992Oct21.155227.27633@dartvax.dartmouth.edu>, Roger.W.Brown@dartmouth.edu (Roger W. Brown) writes:
  902. > In article <1992Oct17.173216.6223@afterlife.ncsc.mil>
  903. > mssmith@afterlife.ncsc.mil (M. Scott Smith) writes:
  904. > >    I've got pictures of a fish swimming to the right that I want to animate.
  905. > > No problem.  Now, I need pictures of the fish swimming left as well.  As I
  906. > > was about to load up my paint program to flip these images and save
  907. > > "fish_swimming_left," it occurred to me this wasn't a good idea, and that
  908. > > there must be a way to flip an image within QuickDraw.
  909. (deleted)
  910.  
  911. There wasa program out there called Aquarium, that featured a fish that
  912. kept swimming from side to side, and off screen.  The application and source
  913. code were available.  You could take a look at how the programmer did it there.
  914. It's probably availabe on Compuserv of AOL.  The version I had wasn't compatible
  915. with system 7 and maybe not with Multifinder.  But the fish still worked.
  916.  
  917. James Zuchelli
  918. - ----------------------
  919. The above is my personal opinion and not that of Claris Corp or my Employer.
  920.  
  921. ---------------------------
  922.  
  923. Subject: Genetic Algorithems
  924. From: $stephan@sasb (Stephan Fassmann, Software)
  925. Date: 19 Oct 92 16:36:40 -0700
  926. Organization: BYU
  927.  
  928. Does anyone know where to get information about Genetic Algorithems. I have 
  929. heard several things about it but nothing concrete.
  930. Thanks.
  931. _____________________________________________________________________
  932. |Stephan Fassmann  Internet: $stephan@sasb.byu.edu  GEnie: S.FASSMANN
  933. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  934. |The Random Acts of Kindness will Continue until Morale Deteriorates|
  935. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  936.  
  937. +++++++++++++++++++++++++++
  938.  
  939. From: ewright@convex.com (Edward V. Wright)
  940. Date: 20 Oct 92 19:12:53 GMT
  941. Organization: Engineering, CONVEX Computer Corp., Richardson, Tx., USA
  942.  
  943. In <$stephan.268.0@sasb> $stephan@sasb (Stephan Fassmann, Software) writes:
  944.  
  945. >Does anyone know where to get information about Genetic Algorithems. I have 
  946. >heard several things about it but nothing concrete.
  947. >Thanks.
  948.  
  949. Well, this may be a dumb answer, but there's a textbook called
  950. Genetic Algorithms.  Don't know the author, but I've seen in at
  951. the local technical bookstore.  Kind of expensive, though, as I 
  952. recall.
  953.  
  954. +++++++++++++++++++++++++++
  955.  
  956. From: peter@sysnext.library.upenn.edu (Peter C. Gorman)
  957. Date: 21 Oct 92 13:00:02 GMT
  958.  
  959. Hello -
  960.  
  961. Here's the citation for Goldberg's excellent intro to GAs:
  962.  
  963.  Author:         Goldberg, David E. 1953-
  964.  Title:          Genetic algorithms in search, optimization, and machine
  965.                    learning / David E. Goldberg.
  966.  Edition:        Repr. with corrections.
  967.  Published:      Reading, Mass. : Addison-Wesley Pub. Co., 1989.
  968.  Description:    xiii, 412 p. : ill. ; 25 cm.
  969.  
  970. There are also conference proceedings published annually; check Goldberg's  
  971. bibliography.
  972.  
  973. - --
  974. Peter Gorman
  975. University of Pennsylvania
  976. Library Systems Office
  977. peter@sysnext.library.upenn.edu
  978.  
  979. +++++++++++++++++++++++++++
  980.  
  981. From: cfhacc@ritvax.isc.rit.edu (Christopher Haupt)
  982. Date: 22 Oct 92 17:28:42 GMT
  983. Organization: Rochester Institute of Technology
  984.  
  985. In article <ewright.719608373@convex.convex.com>, ewright@convex.com
  986. (Edward V. Wright) wrote:
  987. > In <$stephan.268.0@sasb> $stephan@sasb (Stephan Fassmann, Software) writes:
  988. > >Does anyone know where to get information about Genetic Algorithems. I have 
  989. > >heard several things about it but nothing concrete.
  990. > >Thanks.
  991. > Well, this may be a dumb answer, but there's a textbook called
  992. > Genetic Algorithms.  Don't know the author, but I've seen in at
  993. > the local technical bookstore.  Kind of expensive, though, as I 
  994. > recall.
  995.  
  996. David Goldberg's "Genetic Algorithms in search, optimization and
  997. machine learning" (Addison-Wesley)
  998.  
  999. Goldberg may have earlier work as well.
  1000.  
  1001. I think there is a "Handbook of Genetic Algorithms", but am not sure of 
  1002. the exact publishing info.
  1003.  
  1004. There are also a number of precedings, etc.
  1005.  
  1006. Most of the above are available in decent University libraries if
  1007. you access to such, otherwise technical bookstores.
  1008.  
  1009. There are some pd and freeware software floating around 
  1010. the net as well.
  1011.  
  1012. Christopher Haupt--RIT/ISC/ACUS Soft.Supp. & RIT/CS (716) 475 5342         
  1013.  
  1014. RIT;ISC;One Lomb Memorial Dr.;PO BOX 9887;Rochester NY 14623-0887 USA
  1015. cfhacc@ritvax.isc.rit.edu -or- cfhacc@ritvax.bitnet [AppleLink:HAUPT.C]
  1016. "Looking for reality in a virtual world..."
  1017.  
  1018. +++++++++++++++++++++++++++
  1019.  
  1020. From: mpc@biosys.apldbio.COM (Morgan Conrad)
  1021. Date: 22 Oct 92 23:58:49 GMT
  1022. Organization: Applied Biosystems Inc, Foster City, CA
  1023.  
  1024. There is a Genetic Algorithms mailing list and ftp site.
  1025.  
  1026. mailing list:  GA-List-Request@AIC.NRL.NAVY.MIL
  1027. anonymous ftp archive: FTP.AIC.NRL.NAVY.MIL (see v6n5 for details)
  1028.  
  1029. A recent (last half year or so) Scientific American had an intro
  1030. article about genetic algorithms.
  1031.  
  1032.  
  1033. Morgan Conrad              Cthulu for President!
  1034. Applied Biosystems
  1035. mpc@apldbio.com            Why settle for
  1036. 415-570-6667               the lesser evil?
  1037.  
  1038. +++++++++++++++++++++++++++
  1039.  
  1040. From: mrn@eplunix.UUCP (Mark R. Nilsen)
  1041. Date: 22 Oct 92 23:08:13 GMT
  1042. Organization: Eaton-Peabody Lab, Boston, MA
  1043.  
  1044. In article <ewright.719608373@convex.convex.com>,
  1045. ewright@convex.com (Edward V. Wright) writes:
  1046. > Nntp-Posting-Host: bach.convex.com
  1047. > X-Disclaimer: This message was written by a user at CONVEX Computer
  1048. >               Corp. The opinions expressed are those of the user and
  1049. >               not necessarily those of CONVEX.
  1050. >In<$stephan.268.0@sasb> $stephan@sasb (Stephan Fassmann, Software) writes:
  1051. >Does anyone know where to get information about Genetic Algorithems. I have 
  1052. >heard several things about it but nothing concrete.
  1053. >Thanks.
  1054.  
  1055.  
  1056. An excellent book is "Handbook of Genetic Algorythms".  I think it's
  1057. by Davis.  It is an excellent introduction and survey of real-world
  1058. applications.  
  1059.  
  1060.  
  1061.  
  1062. - --Mark.
  1063. - -- 
  1064. Mark Nilsen.  == mrn%eplunix.UUCP@eddie.mit.edu
  1065. The world has changed and we can no longer think that logic is going to
  1066. prevail ... 
  1067.    - A. Scott Crossfield, Proceeding of X-15 first flight anniversary. 
  1068.  
  1069. ---------------------------
  1070.  
  1071. From: resnick@cogsci.uiuc.edu (Pete Resnick)
  1072. Subject: Scratch area low-memory globals
  1073. Organization: University of Illinois at Urbana
  1074. Date: Mon, 19 Oct 1992 01:41:42 GMT
  1075.  
  1076. A gross situation, but something that would make my life *much*
  1077. easier:
  1078.  
  1079. Way back in IM 1, several scratch areas are mentioned, including
  1080. Scratch20, Scratch8, ToolScratch, and ApplScratch. I would like to use
  1081. one of them to throw two instructions into and JMP to it. (The two
  1082. instructions being _DisposePtr and RTS; i.e. I want to dispose of the
  1083. block containing the code I am currently executing.) Questions:
  1084.  
  1085. 1. Are these scratch areas still "supported"? I assume the *real*
  1086. answer is "no, low-memory globals are not supported", but I would like
  1087. to know if these will work as documented through System 7.1.
  1088.  
  1089. 2. IM 1 says "Scratch20, Scratch8, and ToolScratch will not be
  1090. preserved across calls to the routines in the Macintosh ROM." Is this
  1091. true of _HwPriv? If I'm gonna put some code in there, I'm gonna have
  1092. to flush the cache.
  1093.  
  1094. 3. Anyone got a better way to do this? Someone once suggested that I
  1095. could use the autopop bit, but of course DisposePtr is not a Toolbox
  1096. call, and therefore doesn't allow autopop.
  1097.  
  1098. Sorry for such an icky question.
  1099.  
  1100. pr
  1101. - -- 
  1102. Pete Resnick             (...so what is a mojo, and why would one be rising?)
  1103. Graduate assistant - Philosophy Department, Gregory Hall, UIUC
  1104. System manager - Cognitive Science Group, Beckman Institute, UIUC
  1105. Internet: resnick@cogsci.uiuc.edu
  1106.  
  1107. +++++++++++++++++++++++++++
  1108.  
  1109. From: keith@taligent.com (Keith Rollin)
  1110. Date: 19 Oct 92 12:23:12 GMT
  1111. Organization: Taligent
  1112.  
  1113. In article <BwCI1J.BG8@news.cso.uiuc.edu>, resnick@cogsci.uiuc.edu (Pete
  1114. Resnick) wrote:
  1115. > A gross situation, but something that would make my life *much*
  1116. > easier:
  1117. > Way back in IM 1, several scratch areas are mentioned, including
  1118. > Scratch20, Scratch8, ToolScratch, and ApplScratch. I would like to use
  1119. > one of them to throw two instructions into and JMP to it. (The two
  1120. > instructions being _DisposePtr and RTS; i.e. I want to dispose of the
  1121. > block containing the code I am currently executing.) Questions:
  1122. > 1. Are these scratch areas still "supported"? I assume the *real*
  1123. > answer is "no, low-memory globals are not supported", but I would like
  1124. > to know if these will work as documented through System 7.1.
  1125. > 2. IM 1 says "Scratch20, Scratch8, and ToolScratch will not be
  1126. > preserved across calls to the routines in the Macintosh ROM." Is this
  1127. > true of _HwPriv? If I'm gonna put some code in there, I'm gonna have
  1128. > to flush the cache.
  1129. > 3. Anyone got a better way to do this? Someone once suggested that I
  1130. > could use the autopop bit, but of course DisposePtr is not a Toolbox
  1131. > call, and therefore doesn't allow autopop.
  1132.  
  1133. I don't think there's anything wrong with calling DisposePtr on yourself
  1134. while you're executing. Nothing will happen to your code block between the
  1135. call to DisposePtr and the RTS. It's not like there's a DisposePtrClear
  1136. call or anything. I could imagine that with all the work Apple is doing
  1137. with Power PC and such that they might come out with a funky new OS where
  1138. DiposePtr would act differently, but I can't see that happening for a LONG
  1139. time, if at all.
  1140.  
  1141. That answers your specific question, but I suggest you take a look at what
  1142. you are doing and see if you can't take a different approach. I've been
  1143. trying to think of what you might be doing where you'd want to do what you
  1144. ask above. The only thing I can think of is that you are writing a trap
  1145. patch that wants to remove itself after it's completed its task. If that's
  1146. so, keep in mind that removing a trap patch is not simple. If anyone over
  1147. patched you, they'll have a pointer to your code. If you remove that code,
  1148. you'll crash the system.
  1149.  
  1150. - -----
  1151. Keith Rollin
  1152. Phantom Programmer
  1153. Taligent, Inc.
  1154.  
  1155. +++++++++++++++++++++++++++
  1156.  
  1157. From: resnick@cogsci.uiuc.edu (Pete Resnick)
  1158. Date: 19 Oct 92 14:38:06 GMT
  1159. Organization: University of Illinois at Urbana
  1160.  
  1161. keith@taligent.com (Keith Rollin) writes:
  1162.  
  1163. >I don't think there's anything wrong with calling DisposePtr on yourself
  1164. >while you're executing. Nothing will happen to your code block between the
  1165. >call to DisposePtr and the RTS.
  1166.  
  1167. Yikes. I will go on this if noone comes up with anything better. I have
  1168. a great deal of faith in your comments Keith.  Pretty darn frightening
  1169. though.
  1170.  
  1171. >That answers your specific question, but I suggest you take a look at what
  1172. >you are doing and see if you can't take a different approach. I've been
  1173. >trying to think of what you might be doing where you'd want to do what you
  1174. >ask above.
  1175.  
  1176. Actually, I am trying to post a notification using the Notification
  1177. Manager that is self deleting. If I want to post a notification from an
  1178. INIT (or other places I wont get back to), it's nice to have it delete
  1179. itself including its response code so that nothing is left lying
  1180. around. Joe Holt had some code to do this posted to the net a while
  1181. back where he put the _DisposPtr and RTS instructions into ToolScratch
  1182. and then jumped to it. Of course, this was highly Quadra incompatible
  1183. (as some of my users have let me know) because ToolScratch is not
  1184. updated in the instruction cache correctly.
  1185.  
  1186. Any more ideas?
  1187.  
  1188. pr
  1189. - -- 
  1190. Pete Resnick             (...so what is a mojo, and why would one be rising?)
  1191. Graduate assistant - Philosophy Department, Gregory Hall, UIUC
  1192. System manager - Cognitive Science Group, Beckman Institute, UIUC
  1193. Internet: resnick@cogsci.uiuc.edu
  1194.  
  1195. +++++++++++++++++++++++++++
  1196.  
  1197. From: phils@chaos.cs.brandeis.edu (Phil Shapiro)
  1198. Date: 19 Oct 92 15:23:00 GMT
  1199. Organization: Symantec Corp.
  1200.  
  1201. >>>>> On Mon, 19 Oct 1992 12:23:12 GMT, keith@taligent.com (Keith Rollin) said:
  1202.  
  1203. In article <keith-191092051243@kip-55.taligent.com> keith@taligent.com (Keith Rollin) writes:
  1204.  
  1205.  > In article <BwCI1J.BG8@news.cso.uiuc.edu>, resnick@cogsci.uiuc.edu (Pete
  1206.  > Resnick) wrote:
  1207. >> 
  1208. >> Way back in IM 1, several scratch areas are mentioned, including
  1209. >> Scratch20, Scratch8, ToolScratch, and ApplScratch. I would like to
  1210. >> use one of them to throw two instructions into and JMP to it. (The
  1211. >> two instructions being _DisposePtr and RTS; i.e. I want to dispose
  1212. >> of the block containing the code I am currently executing.)
  1213.  
  1214.  > I don't think there's anything wrong with calling DisposePtr on
  1215.  > yourself while you're executing. Nothing will happen to your code
  1216.  > block between the call to DisposePtr and the RTS. It's not like
  1217.  > there's a DisposePtrClear call or anything. I could imagine that
  1218.  > with all the work Apple is doing with Power PC and such that they
  1219.  > might come out with a funky new OS where DiposePtr would act
  1220.  > differently, but I can't see that happening for a LONG time, if at
  1221.  > all.
  1222.  
  1223. I have some code that installs a patch on the fly, and then needs to
  1224. remove itself later. My original version copied itself to a handle,
  1225. and then ended with DisposHandle, RTS; when I changed it to use a
  1226. pointer, this technique no longer worked. I changed it to put the
  1227. DisposPtr and RTS in ToolScratch, and it works fine. (_HwPriv doesn't
  1228. affect ToolScratch.)
  1229.  
  1230. I don't know why DisposPtr, RTS didn't work, but that's what happened.
  1231. Besides, what if a user is running your hack with a debugging utility
  1232. that *does* overwrite disposed blocks, like HeapQC or the Zap Handles
  1233. dcmd?
  1234.  
  1235.  > That answers your specific question, but I suggest you take a look
  1236.  > at what you are doing and see if you can't take a different
  1237.  > approach. I've been trying to think of what you might be doing
  1238.  > where you'd want to do what you ask above. The only thing I can
  1239.  > think of is that you are writing a trap patch that wants to remove
  1240.  > itself after it's completed its task. If that's so, keep in mind
  1241.  > that removing a trap patch is not simple. If anyone over patched
  1242.  > you, they'll have a pointer to your code. If you remove that code,
  1243.  > you'll crash the system.
  1244.  
  1245. In my case I have an FKEY (project printer) that installs a couple
  1246. patches on the current application's heap. It has a very brief
  1247. lifetime, and I've never heard of any problems with it and other
  1248. patches.
  1249.  
  1250.     -phil
  1251. - --
  1252.    Phil Shapiro                                   Software Engineer
  1253.    Language Products Group                     Symantec Corporation
  1254.            Internet: phils@cs.brandeis.edu
  1255.  
  1256. +++++++++++++++++++++++++++
  1257.  
  1258. From: resnick@cogsci.uiuc.edu (Pete Resnick)
  1259. Date: 20 Oct 92 06:38:59 GMT
  1260. Organization: University of Illinois at Urbana
  1261.  
  1262. ldo@waikato.ac.nz (Lawrence D'Oliveiro, Waikato University) writes:
  1263.  
  1264. >OK, if it makes you happier, how about putting the DisposePtr/RTS sequence
  1265. >on the stack:
  1266.  
  1267. Actually, someone else recommended this to me in e-mail, and
  1268. personally I like it the best, except of course it won't run on a
  1269. 68000. I think I am going to use the ToolScratch method if I am on a
  1270. 68000 and use the stack method otherwise. Though I trust everyone that
  1271. _HwPriv doesn't change ToolScratch, why push my luck, right? Who knows
  1272. what will happen in the next version of the OS? At least as far as
  1273. stuff is documented now, this solution will work across the board.
  1274.  
  1275. Thanks to everyone for some great (and some *amusing*) suggestions!
  1276.  
  1277. pr
  1278. - -- 
  1279. Pete Resnick             (...so what is a mojo, and why would one be rising?)
  1280. Graduate assistant - Philosophy Department, Gregory Hall, UIUC
  1281. System manager - Cognitive Science Group, Beckman Institute, UIUC
  1282. Internet: resnick@cogsci.uiuc.edu
  1283.  
  1284. +++++++++++++++++++++++++++
  1285.  
  1286. From: ivanski@world.std.com (Ivan M CaveroBelaunde)
  1287. Date: 20 Oct 92 07:37:40 GMT
  1288. Organization: The World Public Access UNIX, Brookline, MA
  1289.  
  1290. ldo@waikato.ac.nz (Lawrence D'Oliveiro, Waikato University) writes:
  1291.  
  1292. >In article <BwDHzJ.9xM@news.cso.uiuc.edu>, resnick@cogsci.uiuc.edu (Pete Resnick) writes:
  1293. >> keith@taligent.com (Keith Rollin) writes:
  1294. >>
  1295. >>>I don't think there's anything wrong with calling DisposePtr on yourself
  1296. >>>while you're executing. Nothing will happen to your code block between the
  1297. >>>call to DisposePtr and the RTS.
  1298. >>
  1299. >> Yikes. I will go on this if noone comes up with anything better. I have
  1300. >> a great deal of faith in your comments Keith.  Pretty darn frightening
  1301. >> though.
  1302.  
  1303. >OK, if it makes you happier, how about putting the DisposePtr/RTS sequence
  1304. >on the stack:
  1305.  
  1306. [ code deleted]
  1307.  
  1308. Am I totally missing something in this discussion?  The first thing
  1309. that popped into my head was to do GetTrapAddress on DisposePtr
  1310. and do a JMP to it. Or will that break because of something else
  1311. regarding the context in which this code is executing?
  1312.  
  1313. - -Ivan
  1314.  
  1315. - ---
  1316. Ivan Cavero Belaunde
  1317. DiVA Corporation
  1318.  
  1319. +++++++++++++++++++++++++++
  1320.  
  1321. From: resnick@cogsci.uiuc.edu (Pete Resnick)
  1322. Date: 20 Oct 92 18:20:42 GMT
  1323. Organization: University of Illinois at Urbana
  1324.  
  1325. ivanski@world.std.com (Ivan M CaveroBelaunde) writes:
  1326.  
  1327. >Am I totally missing something in this discussion?  The first thing
  1328. >that popped into my head was to do GetTrapAddress on DisposePtr
  1329. >and do a JMP to it. Or will that break because of something else
  1330. >regarding the context in which this code is executing?
  1331.  
  1332. I discussed this with someone in e-mail a little. Though it would
  1333. probably work for my purposes, jumping to the trap address is a really
  1334. bad idea, especially for memory management stuff, just in term of
  1335. future compatibility. Remember, when the processor hits the trap in
  1336. code, it generates an unimplemented instruction trap, meaning that the
  1337. processor will go from user mode to supervisor mode.  If there is any
  1338. hope of having fancy stuff go on in future releases of the OS, the trap
  1339. mechanism is going to be a sure fire way of getting the machine to
  1340. switch modes. If anything is going to have to be in supervisor mode
  1341. when executing, the Memory Manager will.
  1342.  
  1343. All in all, the best answer seems to be avoid jumping directly to
  1344. traps if you can.
  1345.  
  1346. pr
  1347. - -- 
  1348. Pete Resnick             (...so what is a mojo, and why would one be rising?)
  1349. Graduate assistant - Philosophy Department, Gregory Hall, UIUC
  1350. System manager - Cognitive Science Group, Beckman Institute, UIUC
  1351. Internet: resnick@cogsci.uiuc.edu
  1352.  
  1353. +++++++++++++++++++++++++++
  1354.  
  1355. From: ldo@waikato.ac.nz (Lawrence D'Oliveiro, Waikato University)
  1356. Date: 20 Oct 92 17:58:10 +1300
  1357. Organization: University of Waikato, Hamilton, New Zealand
  1358.  
  1359. In article <BwDHzJ.9xM@news.cso.uiuc.edu>, resnick@cogsci.uiuc.edu (Pete Resnick) writes:
  1360. > keith@taligent.com (Keith Rollin) writes:
  1361. >
  1362. >>I don't think there's anything wrong with calling DisposePtr on yourself
  1363. >>while you're executing. Nothing will happen to your code block between the
  1364. >>call to DisposePtr and the RTS.
  1365. >
  1366. > Yikes. I will go on this if noone comes up with anything better. I have
  1367. > a great deal of faith in your comments Keith.  Pretty darn frightening
  1368. > though.
  1369.  
  1370. OK, if it makes you happier, how about putting the DisposePtr/RTS sequence
  1371. on the stack:
  1372.  
  1373.     subq.l    #6, sp        ; make room for return-and-deallocate sequence
  1374.     move.l    6(sp), (sp)    ; move up return address
  1375.     move.w    #$A01F, 4(sp)    ; _DisposePtr
  1376.     move.w    #$4E74, 6(sp)    ; RTD #n
  1377.     move.w    #$0006, 8(sp)    ; value of n for RTD
  1378.     _FlushInstructionCache    ; for brain-damaged processors that can't
  1379.     _FlushDataCache        ;  keep their own caches straight
  1380.     lea    myself, a0    ; pointer to block to be freed
  1381.     jmp    4(sp)        ; enter deallocate-and-return sequence
  1382.  
  1383. > Joe Holt had some code to do this posted to the net a while
  1384. > back where he put the _DisposPtr and RTS instructions into ToolScratch
  1385. > and then jumped to it. Of course, this was highly Quadra incompatible
  1386. > (as some of my users have let me know) because ToolScratch is not
  1387. > updated in the instruction cache correctly.
  1388.  
  1389. So, flush the caches (as I did above). Any code that's running an event loop
  1390. that allows notifications to get in isn't going to lose much more speed
  1391. anyway. :-)
  1392.  
  1393. Lawrence D'Oliveiro                       fone: +64-7-856-2889
  1394. Computer Services Dept                     fax: +64-7-838-4066
  1395. University of Waikato            electric mail: ldo@waikato.ac.nz
  1396. Hamilton, New Zealand    37^ 47' 26" S, 175^ 19' 7" E, GMT+13:00
  1397. Moderate positions are so sensible, somehow, although I've never taken one
  1398. myself.                        -- Bruce Tognazzini, "Tog on Interface"
  1399.  
  1400. +++++++++++++++++++++++++++
  1401.  
  1402. From: ldo@waikato.ac.nz (Lawrence D'Oliveiro, Waikato University)
  1403. Date: 21 Oct 92 16:52:40 +1300
  1404. Organization: University of Waikato, Hamilton, New Zealand
  1405.  
  1406. In article <BwEqGz.6yr@news.cso.uiuc.edu>, resnick@cogsci.uiuc.edu (Pete Resnick) writes:
  1407. > ldo@waikato.ac.nz (Lawrence D'Oliveiro, Waikato University) writes:
  1408. >
  1409. >>OK, if it makes you happier, how about putting the DisposePtr/RTS sequence
  1410. >>on the stack:
  1411. >
  1412. > Actually, someone else recommended this to me in e-mail, and
  1413. > personally I like it the best, except of course it won't run on a
  1414. > 68000.
  1415.  
  1416. The only thing that won't work on a 68000 is the cache-flushing calls. So
  1417. check if the _HWPriv trap is implemented, and don't call it if not. The rest
  1418. should run fine on a 68000.
  1419.  
  1420. Further thought: to be 107.5% safe, you should flush the data cache BEFORE
  1421. flushing the instruction cache.
  1422.  
  1423. Lawrence D'Oliveiro                       fone: +64-7-856-2889
  1424. Computer Services Dept                     fax: +64-7-838-4066
  1425. University of Waikato            electric mail: ldo@waikato.ac.nz
  1426. Hamilton, New Zealand    37^ 47' 26" S, 175^ 19' 7" E, GMT+13:00
  1427. Multiprocessing and multitasking offer the opportunity for change, but only if
  1428. we take it. We have the ability to design the overpasses that will lead our
  1429. users to the superhighways of computing. Let us not get bogged down in
  1430. designing high-speed traffic lights because we're still thinking in terms of
  1431. intersections.                    -- Bruce Tognazzini, "Tog on Interface"
  1432.  
  1433. +++++++++++++++++++++++++++
  1434.  
  1435. From: resnick@cogsci.uiuc.edu (Pete Resnick)
  1436. Organization: University of Illinois at Urbana
  1437. Date: Wed, 21 Oct 1992 16:17:00 GMT
  1438.  
  1439. ldo@waikato.ac.nz (Lawrence D'Oliveiro, Waikato University) writes:
  1440.  
  1441. >>>OK, if it makes you happier, how about putting the DisposePtr/RTS sequence
  1442. >>>on the stack:
  1443. >>
  1444. >> Actually, someone else recommended this to me in e-mail, and
  1445. >> personally I like it the best, except of course it won't run on a
  1446. >> 68000.
  1447.  
  1448. >The only thing that won't work on a 68000 is the cache-flushing calls. So
  1449. >check if the _HWPriv trap is implemented, and don't call it if not. The rest
  1450. >should run fine on a 68000.
  1451.  
  1452. No, the thing that won't work on the 68000 is the RTD instruction;
  1453. there ain't no such thing on the 68000 processor, only the 68010 and
  1454. up. So I am stuck with the ToolScratch solution on the 68000.
  1455.  
  1456. >Further thought: to be 107.5% safe, you should flush the data cache BEFORE
  1457. >flushing the instruction cache.
  1458.  
  1459. Doesn't the _FlushInstructionCache (_HwPriv selector 1) flush both the
  1460. data and instruction caches? It was somewhere in Tech Note 261.
  1461.  
  1462. pr
  1463. - -- 
  1464. Pete Resnick             (...so what is a mojo, and why would one be rising?)
  1465. Graduate assistant - Philosophy Department, Gregory Hall, UIUC
  1466. System manager - Cognitive Science Group, Beckman Institute, UIUC
  1467. Internet: resnick@cogsci.uiuc.edu
  1468.  
  1469. +++++++++++++++++++++++++++
  1470.  
  1471. From: resnick@cogsci.uiuc.edu (Pete Resnick)
  1472. Date: 22 Oct 92 08:51:35 GMT
  1473. Organization: University of Illinois at Urbana
  1474.  
  1475. ldo@waikato.ac.nz (Lawrence D'Oliveiro, Waikato University) writes:
  1476.  
  1477. >In article <BwFMyK.7tv@news.cso.uiuc.edu>, resnick@cogsci.uiuc.edu (Pete Resnick) writes:
  1478.  
  1479. >> All in all, the best answer seems to be avoid jumping directly to
  1480. >> traps if you can.
  1481.  
  1482. >Says who? Has an official pronouncement come down from DTS that this is a
  1483. >Bad Idea?
  1484.  
  1485. I take the fifth on this one; actually, avoiding direct calls to traps
  1486. is something I think I picked up at last year's MacWorld, though I
  1487. can't tell you for sure what session we were talking about this in
  1488. (perhaps it was the INIT session). There is also a line in Tech Note
  1489. 212 in reference to patching traps where it says, "Do not bypass the
  1490. Trap Dispatcher to call traps directly. The performance gains are
  1491. small, and there may be serious side effects." Aside from possible
  1492. reasons I gave in my posting, I think the reason for this had to do
  1493. with the way Apple deals with tail patching.
  1494.  
  1495. But again, I plead the fifth here; I'm not totally sure I have my
  1496. facts straight now that you press me.
  1497.  
  1498. >Traps are dumb way of calling OS services, anyway. Think of the overhead and
  1499. >of that dispatcher bottleneck, not to mention the inflexibility of a single,
  1500. >limited space of trap IDs. A direct procedure call is much simpler.
  1501.  
  1502. I can think of at least three advantages to traps as opposed to
  1503. procedure calls:
  1504.  
  1505. 1. Easily updateable system routines. There are obvious problems with
  1506. statically linked libraries (you have to re-link with the new libraries
  1507. every time they are updated), and even dynamically linked libraries are
  1508. not pure joy (anyone upgraded SunOS lately?). With traps, all you need
  1509. to do is update the trap table, and you can deal with whether the
  1510. routine is now in RAM, ROM, or in some on-disk code resource that you
  1511. have to load up. Very flexible. (There are, BTW, almost 4100 possible
  1512. traps, plus the possibility of trap selectors; running out is not going
  1513. to be a problem.)
  1514.  
  1515. 2. You get the switch up to supervisor mode if you are currently in
  1516. user mode. Again, this hasn't been taken advantage of by the MacOS to
  1517. date, but it could be.
  1518.  
  1519. 3. Saves code size. Procedure calls will take 2-3 instructions, traps
  1520. are 1 word. This was probably important back when the original Mac came
  1521. out, though it is obviously less so today.
  1522.  
  1523. pr
  1524. - -- 
  1525. Pete Resnick             (...so what is a mojo, and why would one be rising?)
  1526. Graduate assistant - Philosophy Department, Gregory Hall, UIUC
  1527. System manager - Cognitive Science Group, Beckman Institute, UIUC
  1528. Internet: resnick@cogsci.uiuc.edu
  1529.  
  1530. +++++++++++++++++++++++++++
  1531.  
  1532. Date: 22 Oct 92 14:13:19 GMT
  1533. Organization: Royal Institute of Technology, Stockholm, Sweden
  1534.  
  1535. resnick@cogsci.uiuc.edu (Pete Resnick) writes:
  1536.  
  1537.    >> All in all, the best answer seems to be avoid jumping directly to
  1538.    >> traps if you can.
  1539.  
  1540.    >Says who? Has an official pronouncement come down from DTS that this is a
  1541.    >Bad Idea?
  1542.  
  1543.    212 in reference to patching traps where it says, "Do not bypass the
  1544.    Trap Dispatcher to call traps directly. The performance gains are
  1545.    small, and there may be serious side effects." Aside from possible
  1546.    reasons I gave in my posting, I think the reason for this had to do
  1547.    with the way Apple deals with tail patching.
  1548.  
  1549. Imagine something that does not run plain Motorola 68k code,
  1550. but intead interpreted it like SoftAT interprets 286 code.
  1551. Now, imagine a cool way to speed it up: as soon as you hit
  1552. an A trap, just call the corresponding NATIVE code, which will
  1553. run much faster than an emulated ROM routine would.
  1554.  
  1555. Now, what platform due in '93 would this apply to? :-)
  1556.  
  1557. - -- 
  1558.  -- Jon W{tte, h+@nada.kth.se, Mac Hacker Suedoise (not french speaking) --
  1559.   _/~|   Yellow
  1560.  / * \_  Shark                      (This signature has won the "Worst ASCII
  1561.  ~~~~\/  Software                    Logo of the Year" award)
  1562.  
  1563. +++++++++++++++++++++++++++
  1564.  
  1565. From: buckeye@spf.trw.com (John Wallace)
  1566. Organization: TRW Sensor Data Processing Center, Redondo Beach, CA
  1567. Date: Thu, 22 Oct 92 21:31:30 GMT
  1568.  
  1569. In article <1992Oct22.165524.11609@waikato.ac.nz> ldo@waikato.ac.nz (Lawrence D'Oliveiro, Waikato University) writes:
  1570. >>
  1571. >> All in all, the best answer seems to be avoid jumping directly to
  1572. >> traps if you can.
  1573. >
  1574. >Says who? Has an official pronouncement come down from DTS that this is a
  1575. >Bad Idea?
  1576. >
  1577. >Traps are dumb way of calling OS services, anyway. Think of the overhead and
  1578. >of that dispatcher bottleneck, not to mention the inflexibility of a single,
  1579. >limited space of trap IDs. A direct procedure call is much simpler. 
  1580.  
  1581. The problem with jumping directly to a trap's code is that someone may 
  1582. have patched the trap _after_ you got the address to call.  In that 
  1583. case you will miss their patch and their code won't work.  The trap
  1584. dispatch takes very little time to execute (about 10 instructions) and does 
  1585. not add significantly to the time it takes your code to execute.  The
  1586. one level of indirection that traps provide gives lot of flexibility to 
  1587. the Mac's OS and most system extensions rely on them.  Seeing all of the 
  1588. ingenious additions to the Mac OS written by third parties validates 
  1589. Apple's patchable trap mechanism.  
  1590.  
  1591. Realize that the trap dispatcher does more than call the trap's code.
  1592. It also saves and restores registers that are used by the OS.  If
  1593. you jump directly to the trap's code then you become responsible for
  1594. saving these registers.  Because of this you won't achieve the huge
  1595. speed improvement that you might expect.
  1596.  
  1597. The bottom line: don't save the address of a trap and then jump
  1598. directly to it unless you have extremely tight performance problems.  
  1599. The nominal performance you gain isn't worth the compatibility problems 
  1600. that may crop up.
  1601.  
  1602. Cheers!
  1603. John
  1604. - ------------
  1605. John Wallace          buckeye@spf.trw.com
  1606.  
  1607.  
  1608. +++++++++++++++++++++++++++
  1609.  
  1610. From: bwilliam@iat.holonet.net (Bill Williams)
  1611. Date: 24 Oct 92 05:30:57 GMT
  1612. Organization: HoloNet (BBS: 510-704-1058)
  1613.  
  1614. keith@taligent.com (Keith Rollin) writes:
  1615.  
  1616. >I don't think there's anything wrong with calling DisposePtr on yourself
  1617. >while you're executing. Nothing will happen to your code block between the
  1618. >call to DisposePtr and the RTS.
  1619.  
  1620.  
  1621.  
  1622. Arrrgg!! I run a set of debugging aids from Apple called to check if bad
  1623. programmers accidentally dispose of resources as normal handles and
  1624. another one that clears handles that are disposed of (called "DoubleTrouble")
  1625.  
  1626.  
  1627. "DoubleTrouble" is GREAT. I run it all the time despite the slight
  1628. overhead of immediately corrupting contents of all deleted memory structures.
  1629.  
  1630. It of course would KILL a program that deletes the handle it is in before
  1631. it ever gets a chance to return!!!
  1632.  
  1633. Only ONE INIT out of tons that I use seems to have a problem and thought
  1634. that it was an innocent act to do.
  1635.  
  1636. A Good scratch area for jumping back would be a simple solution, or a
  1637. stack based trick that works.
  1638.  
  1639.  
  1640. Bill Williams
  1641.  
  1642. ---------------------------
  1643.  
  1644. End of C.S.M.P. Digest
  1645. **********************
  1646.