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
issue’s 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 one’s 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 one’s tail patches do not
nullify ROM code patches. And in some cases one can eat one’s 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.
I’ve 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 Kiene’s system
and/or compiler do something special or he forgot to test it, I don’t know.
1. The MTemp location has a counterpart: RawMouse. It’s 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
Kiene’s program).
2. The CrsrNew location is a Byte value. Steve Kiene’s 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 (don’t 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 Hoddie’s 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 I’m 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 shouldn’t be a matter of timing just when to
click the mouse.