home *** CD-ROM | disk | FTP | other *** search
Text File | 1995-09-17 | 3.1 KB | 80 lines | [TEXT/R*ch] |
- // Debugging 'orphan' code
- // (C) 1995 Stuart Cheshire <cheshire@cs.stanford.edu>
-
- Did you get stuck trying to write your first trap-patching INIT?
- Did you put in DebugStr calls which just printed garbage in MacsBug?
- Well, I did, and it made the legendary Macintosh learning curve even
- steeper.
-
- Applications on the Mac usually have register A5 set up to point to
- their global variables. Standalone code resources often use register
- A4 for a similar purpose. 'Orphan' code is code that doesn't have
- either A4 or A5 set up to point at it's globals. Trap patches,
- completion routines and AppleTalk socket listeners are all 'orphan'
- code that cannot access their global variables until they manually
- set up the correct base address value in the register.
-
- The problem is that even innocent-looking things such as strings for
- DebugStr calls count as global variables and require the base register
- to be set up correctly. Even in a standalone code resource, Think C
- references all static data as an offset from a base register. Why
- it doesn't use PC-relative references is a mystery. (A PC-relative
- reference is where the compiler uses instructions that say things
- like "the address 112 bytes before the address of this instruction
- in memory".) No matter where a standalone code resource is loaded in
- memory, a PC-relative reference will always be correct. Think C uses
- references relative to register A4, which means that you have to make
- sure register A4 is set up before you do anything which depends on it.
-
- The catch-22 for learning programmers trying to debug their code to find
- out why register A4 is not set up correctly is that in Think C the DebugStr
- call doesn't work unless you already have register A4 set up correctly.
-
- Even for experienced programmers, sometimes you want to be able to put
- in a DebugStr breakpoint without having to go to all the effort of
- setting up A4 and then restoring it afterwards. That's why I wrote the
- FixAddress routine:
-
- // Fixes an (erroneous) A4 relative address on top of stack
- static void FixAddress()
- {
- asm { movem.l d0/a4, -(sp) ; save registers
- move.l a4, d0
- sub.l d0, 12(sp) ; subtract incorrect base from argument
- jsr __GetA4
- move.l (a4), d0 ; get correct a4 value
- add.l d0, 12(sp) ; add correct base address to argument
- movem.l (sp)+, d0/a4
- }
- }
-
- The way you use it is like this:
-
- pea "\pThis is a Breakpoint"
- bsr FixAddress
- _DebugStr
-
- The pea instruction pushes the address of the string on the stack, as a
- relative offset from the address in register A4 (which may be incorrect).
- The call to FixAddress then repairs this address, if it was wrong, so
- that the following DebugStr call will work correctly.
-
- This assumes that __GetA4 is defined as:
- static void __GetA4(void)
- {
- asm { bsr.s @1
- dc.l 0 ; store A4 here
- @1 move.l (sp)+, a4 ; A4 contains the address of my A4 value
- }
- }
-
- If the __GetA4 routine your code uses returns the address in a different
- register (eg. a1), then you must change FixAddress to use that register,
- eg.
- move.l (a1), d0 ; get correct a4 value
-
- Obviously for application code, you should change this to use register A5
- instead of A4.
-
- Share and enjoy.
-