[comp.sys.mac.programmer] Breaking the rules

dvlmfs@afrodite.cs.umu.se (Michael Forselius) (10/30/90)

    [I have read your articles Marc, Tom]

    I suppose  that most of you people haven't  read my previous
    article  ("Re: More Mac Wierdness  (Memory Mangler..")  were
    I gave you a  brief  description on how about  calling traps
    during an autovectored  interrupt, IPL0-IPL2, this  could be
    a VBL task or something like that.  Then I told you  to tell
    me if you could find anything wrong with my assumptions. Now
    I have made a simple program that uses this technique and it
    looks  like it's  working,  the program follow my ravings...
    
    For those of you that haven't read  the first article here's
    the theory:
    
    ... First of all we  need to allocate a block of memory that
    will be resident.  We would call InitZone(growZoneProc, ...) 
    to make this memory block a heap zone.
    
    When we are  called  during some  autovectored  interrupt we
    would save the current zone, set the current zone to the one
    we've made. After this we should (theoretically)  be able to
    call any trap even if they could cause the heap to be purged
    scrambled or compacted due to fact  that the  memory mangler
    MUST work WITHIN the current heap zone. This is a truth with
    some modification since you could  call NewPtrSys but that's
    no problem since YOU are supposed to know what you're doing.
    
    When we are  done we simply restore the current zone and the
    next interrupt handler (if any) can continue?

    That's the theory, now what about the real world? What  kind
    of problems could one expect if this were to be used?
    
    Well, you have this about reentrancy and partially completed
    operations on the  application heap as Peter Lewis at Curtin
    University in Australia pointed out.  Since yesterday I have
    been looking through my  ROM listings and have found that it
    looks like Apple for once  doesn't use any scratch areas, or
    break too many rules so it SHOULD be pretty safe to do this,
    i.e. the trap routines seems to  handle reentrancy well - as
    far as I have seen...    Although the  5 ton elephant called
	Apple often changes it's direction (S. Jasik quoted).

    Also, this will break if you can't run it when the Mac is in
    supervisor mode (can I be thinking of TRUE multitasking ???)
    but that's no problem  since all but MPW and just about copy
    protected program then will break as well. This is the price
    you'll have  to pay when  you  don't have the  guts  to do a
    complete rewrite of the system.

    Tell me what you think of all this, no flames or stupid
    answers, only constructive criticism will be considered.

    I liked your answers Marc, Tom.  I said that you had to know
    what you were doing, this  provides some  help to "go beyond
    the limit".

    Hey Waldemar, I want TMON Professional !!!

    I'm also looking for a nice well-paid job - anyone...

    END /* Ravings */
    
    This is how the system heap could be arranged after
	execution.
    
        +--System Heap Zone-+
        |                   |
        |                   |
        |                   |
        |                   |
        +--Our Heap Zone----+   <-- Heap Zone header (52 bytes)
        |*******************|   <-- Interrupt handler (xxx bytes)
        |                   |   <-- Free (xxx bytes)
        |                   |
        +-End Our Heap Zone-+
        +--System Heap Zone-+
        |                   |
        |                   |
        .....................
        +-End Sys.Heap Zone-+
    
    Here comes the program, written in assembler - no language
	gives this much control. (Don't comment this PLEASE)
    
---------------------------------------------------

#define ZONESIZE    4096L
#define heapData    OFFSET(Zone,heapData)

void    Twiddle(void);

void
main(void)
{
asm {
;
;
;Create our heap zone
;
;

;
;save some registers:
;   D3 - size of interrupt code
;   D4 - start address in our heap zone to interrupt code
;   D5 - start address of our heap zone
;
        MOVEM.L D3-D5,-(SP)             ;save a couple of working registers
;
;try to get some space in the system heap for our zone
;remember that this can also be done at startup by moving BufPtr downwards
;
        MOVE.L  #ZONESIZE,D0            ;alloc this many bytes for our zone
        _NewPtr SYS                     ;in system heap
        MOVE.L  A0,D5                   ;save for later use
        BEQ     @xit                    ;if not enough heap space -> quit

;
;create our heap zone, we're allocating the parameter block on stack
;InitZone(growZoneProc, numMasters, limit, start) /
;    A0-block(start,limit,numMasters,growZoneProc)
;
        MOVE.L  TheZone,-(SP)           ;save current zone
        
        SUBI.L  #14,SP                  ;parameter block for _InitZone
        MOVE.L  A0,(SP)                 ;start of our zone
        LEA     ZONESIZE(A0),A1         ;end of our zone
        MOVE.L  A1,4(SP)                ;- // -
        MOVE.W  #16,8(SP)               ;alloc 16 master every time
        CLR.L   10(SP)                  ;no growZoneProc
        MOVE.L  SP,A0                   ;ptr to start of block
        _InitZone                       ;create zone
        ADDI.L  #14,SP                  ;return stack space, pop args
        
        MOVE.L  (SP)+,TheZone           ;must do this since _InitZone
                                        ;has set the current zone to the one
                                        ;just created
;
;
;Install IPL0 routine
;
;

;
;save original address to autovector 0
;also initialize our variables
;
        LEA     @next,A0                ;start of variable storage
        MOVE.L  0x0064,(A0)+            ;save current IPL0 routine
        MOVE.L  D5,(A0)                 ;our THz, ptr to start of system block

;
;calculate size of our patch/interrupt code and get ptr
;to first usable byte in our heap zone, we're moving the
;interrupt code to the beginning of our heap zone - convienent isn't it?
;
        LEA     @CSTART,A0              ;must do this since TC is a
        LEA     @CEND,A1                ;simple one-pass assembler
        SUBA.L  A0,A1                   ;calculate size of patch code
        MOVE.L  A1,D3                   ;save size 

        MOVEA.L D5,A0                   ;ptr to start of our sysheap block
        LEA     heapData(A0),A0         ;first usable byte in zone,past header
        MOVE.L  A0,D4                   ;save this address
;
;move our interrupt handler into system heap (our heap zone),
;it doesn't have to be there but this probably will be in an INIT/cdev ...
;
        LEA     @CSTART,A1              ;start of interrupt routine
        EXG.L   A0,A1                   ;EXchanGe register contents
        MOVE.L  D3,D0                   ;size of interrupt code
        _BlockMove                      ;move code into position

;
;remember to disable interrupts while we "connect" our simple patch
;
        ORI.W   #0x0300,SR              ;disable IPL0-IPL1 interrupts
        MOVE.L  D4,0x0064               ;install our interrupt handler
        MOVE.W  #0x2000,SR              ;enable ALL interrupts

;
;do something, test of our code
;if this were to be a "real" program we could have been finished here with
;patching and installing but this is a skeleton prog so we continue
;and "twiddles" a while before we remove the patch/interrupt handlers
;
        JSR     Twiddle

;
;restore the original interrupt handler, "disconnect" our code
;       
        ORI.W   #0x0300,SR              ;disable IPL0-IPL1 interrupts
        MOVE.L  @next,0x0064            ;connect original interrupt handler
        MOVE.W  #0x2000,SR              ;enable ALL interrupts

@noSpace
;
;free the memory held in system zone by our heap zone
;
        MOVEA.L D5,A0
        _DisposPtr

@xit        
        MOVEM.L (SP)+,D3-D5             ;restore working registers

        return

CSTART:
        MOVE.W  SR,-(SP)                ;save status register
        ORI.W   #0x0300,SR              ;disable IPL0-IPL1 interrupts
        MOVE.L  TheZone,-(SP)           ;save current heap zone
        MOVEM.L D0-D2/A0-A1,-(SP)       ;save these since we're thrashing them

        MOVE.L  ScrnBase,A0             ;flip topleft bit of screen
        EOR.B   #0xC0,(A0)              ;will this work on color Macs !?!

        MOVE.L  @ourZone,TheZone        ;install our zone

        MOVE.L  #maxSize,D0             ;let's cause some relocation!
        _NewPtr
        MOVE.L  #maxSize,D0
        _NewHandle
        MOVE.L  #maxSize,D0
        _PurgeMem
        MOVE.L  #maxSize,D0
        _CompactMem
        
        MOVEM.L (SP)+,D0-D2/A0-A1       ;restore registers
        MOVE.L  (SP)+,TheZone           ;restore original heap zone
        MOVE.W  (SP)+,SR                ;restore status register
        MOVE.L  @next,-(SP)             ;next IPL0 routine, autovector 0
        RTS                             ;"chain" routine

@next
        DC.L    0                       ;address to next interrupt handler
@ourZone
        DC.L    0                       ;our heap zone ptr
CEND:
}
}

void
Twiddle(void)
{
    EventRecord     theEvent;

    InitGraf(&thePort);
    InitFonts();
    InitWindows();
    
    FlushEvents(everyEvent, 0);

    while (!GetNextEvent(keyDownMask, &theEvent));
}

+---------------------------------------------------------+
| Michael Forselius    Internet: dvlmfs@cs.umu.se         |	
|---------------------------------------------------------|
| Ever heard of 'Pirate Purgatory' !?! - tough one though |
+---------------------------------------------------------+