[comp.sys.mac.programmer] Register saving conventions

joachim@iraul1.ira.uka.de (Joachim Lindenberg) (07/30/88)

Apple,

while writing some small patches to toolbox traps, I noticed that
Multifinder doesn't conform to the register saving conventions stated
in Inside Macintosh. Some routines apparently assume, that registers
are not changed by the trap. The traps I patched are GetResource and
DrawMenuBar. Both of them work only if I save registers D0-D2/A0-A1.

I know that you did a great job implementing MultiFinder, but fixing
problems like that will ease the life of your developers.

Joachim Lindenberg, Dept. of Computer Science, University of Karlsruhe
Sommerstrasse 4, 7500 Karlsruhe 1, Federal Republic of Germany

bob@eecs.nwu.edu (Bob Hablutzel) (08/01/88)

>while writing some small patches to toolbox traps, I noticed that
>Multifinder doesn't conform to the register saving conventions stated
>in Inside Macintosh. Some routines apparently assume, that registers
>are not changed by the trap. The traps I patched are GetResource and
>DrawMenuBar. Both of them work only if I save registers D0-D2/A0-A1.

Are you attempting to make tail patches? (A tail patch is one which 
processes AFTER the ROM code). MultiFinder is making tail patches very
hard to impliment, as the MultiFinder code often checks to see where
it comes from, or it can depend on the ROM code leaving certain registers
in specific states. I think Apple is quietly saying that tail patches
are no longer valid, although I have not heard this verbatum from Apple.

Isn't programming a moving target fun?? (severe sarcasm)

Bob Hablutzel
BOB@NUACC.ACNS.NWU.EDU
AppleLink:	A0173
CompuServe:	76474,154

dee@cca.CCA.COM (Donald Eastlake) (08/01/88)

Don't know about DrawMenuBar but as I recall Inside Macintosh
specifically says that GetResource saves ALL registers.
-- 
	+1 617-969-9570		Donald E. Eastlake, III
	ARPA: dee@CCA.CCA.COM	usenet:	{cbosg,decvax,linus}!cca!dee
	P. O. Box N, MIT Branch P. O., Cambridge, MA 02139-0903 USA

mkg@lzsc.ATT.COM (Marsh Gosnell) (08/02/88)

When I patch traps I make it a point to save/restore everything (D0-D7/A0-A4).
No telling what assumptions the ROM makes or, worse yet, what assumptions
some other programmer who patches the same trap as you made (or forgot to).
  Marsh Gosnell

darin@Apple.COM (Darin Adler) (08/02/88)

In article <664@iraun1.ira.uka.de> joachim@iraul1.ira.uka.de (Joachim Lindenberg) writes:
> While writing some small patches to toolbox traps, I noticed that
> MultiFinder doesn't conform to the register saving conventions stated
> in Inside Macintosh. Some routines apparently assume, that registers
> are not changed by the trap. The traps I patched are GetResource and
> DrawMenuBar. Both of them work only if I save registers D0-D2/A0-A1.

If you read page I-113 of Inside Macintosh, you will see the following note:

       "Assembly-language note: Except for LoadResource, all Resource
	Manager routine preserve all registers except A0 and D0.
	LoadResource preserves A0 and D0 as well."

Thus, the Resource Manager is an exception to the normal rules for Toolbox
register saving. If you patch a Resource Manager trap, you have to keep this
in mind.

I don't think that this is clear enough in Inside Macintosh, but it is
definitely a documentation problem rather than a MultiFinder problem.
--
Darin Adler					       AppleLink: Adler4
UUCP: {sun,voder,nsc,mtxinu,dual}!apple!darin	  CSNET: darin@Apple.com

t-benw@microsoft.UUCP (Benjamin Waldmin) (08/03/88)

In article <10050005@eecs.nwu.edu> bob@eecs.nwu.edu (Bob Hablutzel) writes:
>Are you attempting to make tail patches? (A tail patch is one which 
>processes AFTER the ROM code). MultiFinder is making tail patches very
>hard to impliment, as the MultiFinder code often checks to see where
>it comes from, or it can depend on the ROM code leaving certain registers
>in specific states. I think Apple is quietly saying that tail patches
>are no longer valid, although I have not heard this verbatum from Apple.
>
In the Macintosh supplement in the August Byte, there's an article by
Phil Goldman, one of the authors of MultiFinder.  Goldman writes
"... it's illegal for an application to have a patch that does post-
processing--that is, that has code after the call to the old routine."

Ben Waldman
Software Design Engineer,
Microsoft Corp.

uw-beaver!microsoft!t-benw

Disclaimer 1:  No, I usually don't read Byte; I find it useless.  But
when I saw the words Macintosh Supplement on the cover I though I'd
give it a try.  Unfortunately, there's a disgusting article on animation,
where the author directly calls the Color Manager rather than working
through the Palette Manager. Shame !!!

Disclaimer 2:  My thoughts, opinions, and writings are purely my own
and are no related in any way to those of my employer.

lsr@Apple.COM (Larry Rosenstein) (08/03/88)

In article <10050005@eecs.nwu.edu> bob@eecs.nwu.edu (Bob Hablutzel) writes:
>processes AFTER the ROM code). MultiFinder is making tail patches very
>hard to impliment, as the MultiFinder code often checks to see where
>it comes from, or it can depend on the ROM code leaving certain registers
>in specific states. I think Apple is quietly saying that tail patches
>are no longer valid, although I have not heard this verbatum from Apple.

Tail patches have always been questionable, even before MultiFinder.  Bugs
in the ROM are often fixed using the technique of looking at return
addresses (known as "come-from patches").  

Instead of replacing the entire buggy routine (which might be large), a
patch is made at a trap called just before the bug.  The patch looks at the
return address to see if it really is just before the bug.  If so, it fixes
the bug and jumps back into ROM.

The recommended way to patch a trap is to call the original routine with the
stack in the same state as you found it (ie, with the same return address).

		 Larry Rosenstein,  Object Specialist
 Apple Computer, Inc.  20525 Mariani Ave, MS 46-B  Cupertino, CA 95014
	    AppleLink:Rosenstein1    domain:lsr@Apple.COM
		UUCP:{sun,voder,nsc,decwrl}!apple!lsr

jln@eecs.nwu.edu (John Norstad) (08/04/88)

In article <10050005@eecs.nwu.edu> bob@eecs.nwu.edu (my friend Bob 
Hablutzel) writes:

>processes AFTER the ROM code). MultiFinder is making tail patches very
>hard to impliment, as the MultiFinder code often checks to see where
>it comes from, or it can depend on the ROM code leaving certain registers
>in specific states. I think Apple is quietly saying that tail patches
>are no longer valid, although I have not heard this verbatum from Apple.

Sorry Bob, but Inside Mac does warn against these "tail patches".  
On page II-383 IM says 

     "a number of ROM routines have been patched with corrected 
     versions in RAM; if you intercept a patched routine, you must 
     not do any processing after the existing patch, and you must be 
     sure to preserve the registers and the stack (or the system won't 
     work properly)."  
     
This doesn't make it clear exactly why tail patches should be avoided, 
but it does warn against them.

Thanks to Larry Rosenstein for explaining "come-from patches".  This is a
neat idea, and if you think about it for a while it becomes clear why
"tail patches" are taboo.

If you want to write a tail patch despite all these warnings, you should at
least check to make sure that a come-from patch hasn't already been 
written for the same trap.  You also, of course, run the risk of having 
your code break with new system releases.

There's another problem to watch out for when writing patches.  I recently
patched GetNextEvent, and noticed that under MultiFinder it wasn't working
quite perfectly.  Under MF, when a desktop icon (disk or trash) was 
uncovered after a window drag, it wasn't being redrawn.  After much 
investigation I discovered that in this particular circumstance MF was 
expecting GetNextEvent to return a full zero word if the function result 
was false.  This is much stricter than the usual rule for Boolean values 
on the stack, which says that only the low-order bit of the high-order 
byte of the word is significant.  The ROM version of GetNextEvent does 
indeed always return a full zero word if the function result is false, 
but my patch, which was written in a high-level language, was only setting 
the high-order byte of the function result.  I added some in-line assembly
language code to clear the low-order byte, and this fixed the problem.

So beware - sometimes parts of the system rely on traps behaving in very
particular, undocumented ways.  Your patch must perfectly imitate the trap 
in every way.

jmunkki@santra.HUT.FI (Juri Munkki) (08/06/88)

In article <1133@lzsc.ATT.COM> mkg@lzsc.ATT.COM (Marsh Gosnell) writes:
>When I patch traps I make it a point to save/restore everything (D0-D7/A0-A4).
>No telling what assumptions the ROM makes or, worse yet, what assumptions
>some other programmer who patches the same trap as you made (or forgot to).
>  Marsh Gosnell

Ithink we should program according to apple documentation. Since register-
saving is one of the most time-consuming things that can be done on a 68000,
it should avoided when possible.

Maybe someone could write a "register scramble" function for TMON. It would
trash any registers that can be theoretically trashed when a trap is called.
All apple code _should_ be able to survive this mode (probably wouldn't at
first).

Juri Munkki
jmunkki@santra.hut.fi
jmunkki@fingate.bitnet

P.S. When is there going to be a 020 and 881 compatible TMON??

imp@crayview.msi.umn.edu (Chuck Lukaszewski) (08/09/88)

In article <15055@santra.UUCP>, jmunkki@santra.HUT.FI (Juri Munkki) writes:
> In article <1133@lzsc.ATT.COM> mkg@lzsc.ATT.COM (Marsh Gosnell) writes:
> >When I patch traps I make it a point to save/restore everything (D0-D7/A0-A4)

There really is no need for this.  Juri is right about the cycle consumption
of register saving.  At a minimum, you can ignore D0-D2 and A0-A1 because Apple
says you can trash them.  Indeed, the ROM code makes no assumptions about these
five registers.

In point of fact, given the above about D0-D2/A0-A1, you need no register store
code at all because the trap dispatcher handles the other registers for you, so
you're just wasting stack space and CPU cycles.

---===---===---===---===--/* Chuck Lukaszewski */--===---===---===---===---
ARPAnet/NSFnet/MRnet:      AppleLink:  SnailMail:              Ma Bell:
imp@crayview.msi.umn.edu   UG0138      Minneapolis MN 55418    612/789-0931

carlton@ji.Berkeley.EDU (Mike Carlton) (08/10/88)

In article <6600@umn-cs.cs.umn.edu> imp@crayview.msi.umn.edu (Chuck Lukaszewski) writes:
>In article <15055@santra.UUCP>, jmunkki@santra.HUT.FI (Juri Munkki) writes:
>> In article <1133@lzsc.ATT.COM> mkg@lzsc.ATT.COM (Marsh Gosnell) writes:
>> >When I patch traps I make it a point to save/restore everything (D0-D7/A0-A4)
>
>There really is no need for this.  Juri is right about the cycle consumption
>of register saving.  At a minimum, you can ignore D0-D2 and A0-A1 because Apple
>says you can trash them.  Indeed, the ROM code makes no assumptions about these
>five registers.
>
>In point of fact, given the above about D0-D2/A0-A1, you need no register store
>code at all because the trap dispatcher handles the other registers for you, so
>you're just wasting stack space and CPU cycles.

Be careful here.  Many traps pass args in D0, A0 and even A1.  Many return
results in A0 and D0.  Presumably you would see this in the calling sequence 
and allow for it.  

More subtle problems could occur with traps that use bits in the trap word
to pass paramaters (such as _UprString which uses bits for case sensitive
and diactritical sensitive).  The trap dispatcher stuffs the trap number in
D2 and the trap word in D1 (I may have these backwords).  The code for the
trap will then check one of these words and branch accordingly.  So you
better not munge D1 or D2 in these cases.

Another potential problem could be tail patches on register based traps.
(I know, tail patches are frowned upon, but they're useful sometimes).  If
a result is returned in D0 the dispatcher tests D0 before returning to set 
the condition codes.  I don't know if any code branches immediately, but to 
be safe you'd have to retest D0 to reset the condition codes if you've 
changed them.

I agree, save only what is necessary.  However, there are some gotchas.


Ease of use != ease of programming.  Ease of use == !ease of programming.

regards,
mike  (carlton@ji.berkeley.edu    ...!ucbvax!ji!carlton)

schoaff@gefion.cs.cornell.edu (Peter Schoaff) (08/10/88)

Excuse this basic question, but could someone explain to me what is
meant by a tail patch on a trap.  

Thanks

| P. Chris Schoaff | We are the coffee generation, we can't afford cocaine  |
|                  | We need a healthy dose to make it through the day      |
| schoaff@         | Don't Care about nuclear war or poverty or pain        |
|   cs.cornell.edu | We are the coffee generation and life is just a game.  |

darin@Apple.COM (Darin Adler) (08/10/88)

In <6600@umn-cs.cs.umn.edu> imp@crayview.msi.umn.edu (Chuck Lukaszewski) writes:
> In article <15055@santra.UUCP>, jmunkki@santra.HUT.FI (Juri Munkki) writes:
> > In article <1133@lzsc.ATT.COM> mkg@lzsc.ATT.COM (Marsh Gosnell) writes:
> > >When I patch traps I make it a point to save/restore ... (D0-D7/A0-A4)
> 
>In point of fact, given the above about D0-D2/A0-A1, you need no register store
>code at all because the trap dispatcher handles the other registers for you, so
>you're just wasting stack space and CPU cycles.

Wrong!

Here is what happens:

OS traps
--------
Callers:	expect result in D0 (and possibly A0), nothing else trashed
Dispatcher:	saves D1-D2/A1 and sometimes A0, depending on the trap word
Traps:		can trash D0-D2/A0-A1, must preserve D3-D7/A2-A6

Toolbox traps (except Resource Manager)
---------------------------------------
Callers:	expect D0-D2/A0-A1 trashed
Dispatcher:	saves nothing
Traps:		can trash D0-D2/A0-A1, must preserve D3-D7/A2-A6

Resource Manager traps (except LoadResource)
--------------------------------------------
Callers:	expect D0/A0 trashed
Dispatcher:	saves nothing
Traps:		can trash D0/A0, must preserve D1-D7/A1-A6

LoadResource
------------
Callers:	expect nothing trashed
Dispatcher:	saves nothing
Traps:		must preserve D0-D7/A0-A6

You MUST save registers D2-D7/A2-A6 if you use them in a routine. Usually, you
can't use A5, since most routines (QuickDraw, the Toolbox) requires that it
point at QuickDraw globals. Also, the LINK and UNLK instructions save the
specified register, so it is not usually necessary to save A6. If the patch that
you are writing is not called often (or not in time-critical places), saving
all the registers is probably an OK idea, since it's almost always safe, and
prevents the subtle errors that can occur otherwise.

By the way, I agree with Juri about the need for a "register scramble" function
to test applications and system software with. There are currently a number of
places in the ROM and System that do not follow the above rules. For example,
there are a number of places in the ROM that rely on SetPort changing only A0!
--
Darin Adler					       AppleLink: Adler4
UUCP: {sun,voder,nsc,mtxinu,dual}!apple!darin	  CSNET: darin@Apple.com

imp@crayview.msi.umn.edu (Chuck Lukaszewski) (08/10/88)

Mike Carlton writes in response to my article on register saving:
> and diactritical sensitive).  The trap dispatcher stuffs the trap number in
> D2 and the trap word in D1 (I may have these backwords).  The code for the
> trap will then check one of these words and jranch accordingly.  So you
> better not munge D1 or D2 in these cases.
> 
> Another potential problem could be tail patches on register based traps.
> (I know, tail patches are frowned upon, but they're useful sometimes).  If
> a result is returned in D0 the dispatcher tests D0 before returning to set 

But Mike, you haven't given any reason to save any registers in a trap patch.
In your first example, you are discussing operating system traps, which take
it upon themselves to save D1 and D2 in addition to the usual D3-D7/A2-A4!
Also, you can touch them all you want if you are providing a complete patch.
If you are doing a 'header' patch, then you need to avoid them, but you do
not need to save them.

As far as trailer patches go, D0 is never saved anyways.  Implicit in the idea
of a 'patch' is the test that you talk about, or you haven't properly emulated
the routine you are replacing.

So, as I see it, there is still no reason to save any registers in replacement
traps.

---===---===---===---===--/* Chuck Lukaszewski */--===---===---===---===---
ARPAnet/NSFnet/MRnet:      AppleLink:  SnailMail:              Ma Bell:
imp@crayview.msi.umn.edu   UG0138      Minneapolis MN 55418    612/789-0931

imp@crayview.msi.umn.edu (Chuck Lukaszewski) (08/10/88)

Darin is absolutely right...I goofed.  I just peeked at my handy Mac ROM
disassembly, where I should have looked to begin with, instead of at some
quick patches I've written which don't use 'nontrashable' regs.

Actually all of this makes sense if you realize that traps RTS back to the
location that invoked the trap + 2, not to the trap dispatcher.

I guess I've got a lot on my mind or something.

---===---===---===---===--/* Chuck Lukaszewski */--===---===---===---===---
ARPAnet/NSFnet/MRnet:      AppleLink:  SnailMail:              Ma Bell:
imp@crayview.msi.umn.edu   UG0138      Minneapolis MN 55418    612/789-0931

kent@lloyd.camex.uucp (Kent Borg) (08/11/88)

>From: schoaff@gefion.cs.cornell.edu (Peter Schoaff)

>Excuse this basic question, but could someone explain to me what is
>meant by a tail patch on a trap.  

If you want to change the behavior of one the routines in the
Macintosh ROM, you can install your own routine that gets called
first.  You work your special custom magic and then possibly jump back
to the original routine to do what it does.  Because you jumped to the
original routine, when it returns it will return back to the original
calling code, not to your patch.  If, on the misbehaving other hand,
you arrange to have the original routine return control to your patch,
you have installed a tail patch.

I can't remember why tail patches are bad (I've never patched the
Macintosh), but I think it has to do with interference between
multiple patches (and Apple's tail patches?).

Kent Borg
kent@lloyd.uucp
or
hscfvax!lloyd!kent

jwhitnell@cup.portal.com (08/11/88)

mike  (carlton@ji.berkeley.edu    ...!ucbvax!ji!carlton) writes...
|(I know, tail patches are frowned upon, but they're useful sometimes).

They are not just frowned upon, they may also break current or future system
upgrades!  Using tail patches is a good recipe for guraenteeing your code
is not compatible with future system releases.

Jerry

--
Jerry Whitnell
jwhitnell@cup.portal.com
..!sun!cup.portal.com!jwhitnell

tedj@hpcilzb.HP.COM (Ted Johnson) (08/11/88)

>Excuse this basic question, but could someone explain to me what is
>meant by a tail patch on a trap.  

A tail patch is a patch that gets called *after* the normal trap is done.
The other kind of a patch is a "head patch"  ;-)   A head patch
gets called *before* the normal trap gets called.

-Ted

jmunkki@santra.HUT.FI (Juri Munkki) (08/14/88)

In article <130@lloyd.camex.uucp> kent@lloyd.UUCP (Kent Borg) writes:
>I can't remember why tail patches are bad (I've never patched the
>Macintosh), but I think it has to do with interference between
>multiple patches (and Apple's tail patches?).

They are bad because Apple does something very stupid in order to save
a few bytes (well maybe KB) when patching traps. Since Apple installed
patches look for the return address from a trap, using a jsr instead of
a jmp in your code fools the patch into thinking that it is called from
a normal program instead of a buggy ROM routine and the Apple patch is
never actually applied. If it fixes a bad problem, your program will
probably "cause" a crash in some cituations...if it is something less
obvious, you might get away with your code.

***** FLAME <<ON>> *****

Why is it stupid for Apple to make patches this way?

I can think of a few reasons:
1) It slows down some traps that are actually ok. Since you usually end
   up patching a simple and very often called trap to fix an obscure
   problem in a very complex trap, you slow down the execution of innocent
   trap and the buggy trap.

2) You can't write programs that monitor and change the results from traps.

3) These patches are much harder to document and explain to new innocent system
   systems programmers that arrive at Apple. The system should be kept as simple
   as possible, since we already have lost the elegance and speed of the
   theoretical perfect Mac ROM.

So: Keep it simple, keep it simple to document, keep it simple to understand
    and don't forget that there are people out there who would never think
    that something as stupid as this can be done at Apple...

**** FLAME <<OFF>> *****

Ok, when will be the totally rewritten Mac system be ready? I don't think
that the future system releases are going to be any better than 6.0 until
this thing is ready. I can only hope that Apple has some good people hired
to do this thing. The original Mac team did a good job. Can anyone repeat it?

I also hope that it won't be built to be compatible with Mac OS. Launching
Mac applications should start an emulation mode. OS/2 is good in this respect.

Juri Munkki
jmunkki@santra.hut.fi
jmunkki@fingate.bitnet


P.S.
     I only like newborn computers...the Mac is getting too old for me.