Jan 90 Letters
Volume Number: | | 6
|
Issue Number: | | 1
|
Column Tag: | | Letters
|
INITs First
By David E. Smith, Editor & Publisher, MacTutor
INITs in Pascal or C
Steve Brecher
Sunnyvale, CA
There are some problems in the advice and code provided in the October 1989 issues Pascal Procedures and C Workshop, which discuss writing INITs in the respective languages.
The VBL task shown in Pascal Procedures calls GetGDevice without checking whether Color QuickDraw is available. It assumes that the current GDevice is the monitor the mouse is on. Multiple-monitor users may be surprised to find the mouse jumping from the middle of one monitor to the opposite edge of another when they press Option for some application purpose. The code also assumes, in its reference to QuickDraw globals through CurrentA5, that QuickDraw has been initialized for the currently executing application. This is not necessarily true.
The following comments apply to the C Workshop article.
The author does not understand the function of the ioNamePtr field in File Manager parameter blocks. This field contains either a pointer to a caller- allocated string buffer or nil if the caller chooses not to pass or receive a filename string. The File Manager does not allocate a string buffer for the caller.
INIT 31 does not necessarily expand the system heap by the amount specified in your sysz 0 resource, and it certainly does not collapse the heap to undo any previous expansions--CompactMem collects free blocks together; it does not shrink a heap. What INIT 31 does is expand the system heap if necessary to assure that a contiguous block of the size specified in the sysz 0 resource is available at the time the first (and usually only) INIT resource in an INIT/
cdev/RDEV is executed. Any space gained by heap expansion that is not used by the INIT will remain free when the INIT exits--but may be sopped up by subsequent INITs or system activity.
The author unduly discounts the dangers of tail patching. A tail patch is a trap patch that calls (JSRs to) the original patch instead of JMPing to it. A tail patch may nullify an Apple-supplied ROM code patch. Such Apple patches operate by comparing their return address on the stack with certain hard-coded ROM addresses. By JSRing to the original trap code, one assures that such a comparison will always fail since the Apple patch will see ones own return address rather than that of the original trap caller. In some cases tail patching is necessary; my own products do it. But one should at least check the latest System release to make sure that ones tail patches do not nullify ROM code patches. And in some cases one can eat ones cake and have it too by JSRing to the original trap code, doing necessary post-processing, and then JMPing to the original code. The original code will be executed twice, and the second execution will invoke any ROM patch code that may be installed. The JMP requires assembly language.
The example INIT ignores the fact that Resource Manager traps preserve all registers except D0/A0 (except LoadResource, which preserves all including D0/A0)--IM I-113. It also uses the Dialog Manager with no assurance that it has been intialized, not to mention QuickDraw, etc.
Writing Inits in Pascal
Roland van Straten
Dutch Macintosh Developer
Perhaps this is not the right way to inform you about bugs, but sending a letter takes a while and it has no way to send real program listings. So if this is not the way you prefer, let me know.
Ive read the article about Writing INITs in Pascal, MacTutor Oct 89, Page 51, and after some study of the source code I noticed two bugs. Maybe Steve Kienes system and/or compiler do something special or he forgot to test it, I dont know.
1. The MTemp location has a counterpart: RawMouse. Its the next LongInt in Low Memory. If RawMouse is equal to MTemp the mouse handler will think the move has to be made absolute. Otherwise the move will be treated as relative (Steve Kienes program).
2. The CrsrNew location is a Byte value. Steve Kienes program declares a pointer to a Byte (^Byte). This is a two byte value. Look at IM vol1, pag 86. In order to fix this I used a standard type Ptr (^SignedByte) and exchanged $FF for -1.
Apart from these little bugs I enjoyed the article very much and found it useful. The article appeared at the time I was writing a VBL task that moves the mouse outside the screen (dont ask me for the reason of this).
UNIT CursorWrap;
{MacTutor ©1989, issue October 1989, page 51, Writing INITs in Pascal
by Steve Kiene}
{MPW 3.0 version - October 1989 - ROLAnd van Straten - ALink HOL0027}
INTERFACE
USES
MemTypes,QuickDraw,OSIntf,ToolIntf;
PROCEDURE SETUPVBL;
PROCEDURE Wrap;
IMPLEMENTATION
PROCEDURE SETUPVBL;
VAR
theVBL : VBLTask;
myQElem: QElemPtr;
myErr : OSErr;
SaveZone : THz;
SizeNeeded:LongInt;
PatchPtr : Ptr;
theCode: Handle;
thePtr : ^LongInt;
BEGIN
theCode:= Get1Resource(INIT,1);
SaveZone := GetZone;
SetZone(SystemZone);
SizeNeeded := SizeResource(theCode) - (LongInt(@SetUpVBL) - LongInt(theCode^))
+ SizeOf(QElem);
ResrvMem(SizeNeeded);
IF MemError <> NoErr THEN BEGIN
SysBeep(1); SetZone(SaveZone); Exit(SetUpVBL); END;
PatchPtr := NewPtr(SizeNeeded + 4) ;{get ptr for our code}
BlockMove(@Wrap, Pointer(ORD(PatchPtr)+4), SizeNeeded);
myQElem := QElemPtr(NewPtr(SizeOf(QElem)));
SetZone(SaveZone);
{put vbl task ptr addr into ptr where our patch will be}
thePtr := Pointer(PatchPtr);
thePtr^ := LongInt(myQElem);
WITH theVBL DO BEGIN
qType := ORD(vType);
vblAddr := Pointer(ORD(PatchPtr)+4);
vblCount := 6;
vblPhase := 0;
END;
myQElem^.vblQElem := theVBL;
myErr := VInstall(myQElem);
END;
PROCEDURE Wrap;
{Wrap cursor when option key is down
MTemp has latest mouse value
RawMouse has the un-jerked mouse value
CrsrNew must be set to -1 to indentify moved
If MTemp and RawMouse are different there only will be a relative change
of the mouse position. This looks like unstable moves on the screen.
Fixed this bug by moving RawMouse into the arena.
CrsrNew is a byte. The used pointer type was a ^Byte. This is in fact
a two byte value (WRONG). Look in IM.1 pag 68 for details on this. So
in order to fix this use the standard Ptr type and put the value -1 into
the addressed byte.
}
CONST
CurrentA5 = $904;
MTemp = $828; {Low-level interrupt mouse location [long]}
RawMouse = $82C; {un-jerked mouse coordinates [long]}
CrsrNew= $8CE; {Cursor changed? [byte]}
OptionKey= 58;
TYPE
LPtr = ^LongInt;
VAR
myQElem: QElemPtr;
currGDevice: GDHandle;
theMap : KeyMap;
changed: Boolean;
myRectPtr: ^Rect;
mouseRect, myRect : Rect;
myPtr,myPtr2 : LPtr;
PPtr,P2Ptr : ^Point;
BEGIN
GetKeys(theMap);
IF theMap[OptionKey] THEN BEGIN
changed := FALSE;
currGDevice :=GetGDevice;
IF currGDevice <> NIL THEN
mouseRect := currGDevice^^.gdRect
ELSE
BEGIN {use A5 to get offset to screenbits.bounds}
myPtr := Pointer(CurrentA5);
myPtr2 := Pointer(myPtr^);
myRectPtr := Pointer(myPtr2^-116);
mouseRect := myRectPtr^;
END;
InSetRect(mouseRect,1,1);
PPtr := Pointer(MTemp); P2Ptr := Pointer(RawMouse);
IF PPtr^.v <= mouseRect.top THEN BEGIN {check cursor pos for wrap}
PPtr^.v := mouseRect.bottom -1;
P2Ptr^.v := mouseRect.bottom -1;
changed := TRUE; END
ELSE
IF PPtr^.v >= mouseRect.bottom THEN BEGIN
PPtr^.v := mouseRect.top +1;
P2Ptr^.v := mouseRect.top +1;
changed := TRUE; END
ELSE
IF PPtr^.h <= mouseRect.left THEN BEGIN
PPtr^.h := mouseRect.right -1;
P2Ptr^.h := mouseRect.right -1;
changed := TRUE; END
ELSE
IF PPtr^.h >= mouseRect.right THEN BEGIN
PPtr^.h := mouseRect.left +1;
P2Ptr^.h := mouseRect.left +1;
changed:= TRUE; END;
IF changed THEN
Ptr(CrsrNew)^ := -1;{cursor has changed}
END;
myQElem := QElemPtr(LPtr(Pointer(ORD(@Wrap)-4))^); {get ptr to
vbl task, and reset it}
myQElem^.vblQElem.vblCount := 6;
END;
END.
INIT Inhibition
David Dunham
Seattle, WA
Just a quick note about Peter Hoddies article on INITs. He suggests that INITs not install themselves when the mouse button is down.
In fact, there is a fairly standard method of inhibiting INITs: the shift key. Not all INITs use this, but probably half the ones Im familiar with do. My own INITs also disable themselves when their initial letter (e.g. f for Findswell) is held at boot time.
The problem with using the mouse button is that it instructs the Mac to eject the internal disk at boot time. Disabling INITs shouldnt be a matter of timing just when to click the mouse.